Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
portal-apps-manage
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Registry
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
angular
portal-apps-manage
Commits
c8dc0a75
Commit
c8dc0a75
authored
Oct 06, 2025
by
sawit
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ตารางปฏิทิน
parent
1e4426e9
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
266 additions
and
37 deletions
+266
-37
meeting-booking.component.html
...tal-manage/meeting-booking/meeting-booking.component.html
+61
-23
meeting-booking.component.scss
...tal-manage/meeting-booking/meeting-booking.component.scss
+118
-14
meeting-booking.component.ts
...ortal-manage/meeting-booking/meeting-booking.component.ts
+87
-0
No files found.
src/app/portal-manage/meeting-booking/meeting-booking.component.html
View file @
c8dc0a75
...
@@ -151,32 +151,69 @@
...
@@ -151,32 +151,69 @@
<!-- Schedule View -->
<!-- Schedule View -->
<div
class=
"schedule-section"
>
<div
class=
"schedule-section"
>
<div
class=
"schedule-header"
>
<div
class=
"schedule-header"
>
<div
class=
"schedule-title"
>
<div
class=
"schedule-header-top"
>
<h2><mat-icon>
schedule
</mat-icon>
ปฏิทินการจองห้องประชุม
</h2>
<div
class=
"schedule-title"
>
<p>
ดูและจัดการการจองในรูปแบบปฏิทินแบบเต็มรูปแบบ
</p>
<h2><mat-icon>
schedule
</mat-icon>
ปฏิทินการจองห้องประชุม
</h2>
</div>
<div
class=
"schedule-controls"
>
<mat-form-field
appearance=
"outline"
class=
"control-field"
>
<mat-label>
กรองตามห้องประชุม
</mat-label>
<mat-select
[(
value
)]="
selectedRoom
"
(
selectionChange
)="
onRoomChange
()"
>
<mat-option
value=
""
>
ทั้งหมด
</mat-option>
<mat-option
*
ngFor=
"let room of rooms$ | async"
[
value
]="
room
.
id
"
>
{{ room.name }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
</div>
<p
class=
"schedule-subtitle"
>
ดูและจัดการการจองในรูปแบบปฏิทินแบบเต็มรูปแบบ
</p>
</div>
<div
class=
"schedule-controls"
>
<!-- Monthly Calendar Grid -->
<mat-form-field
appearance=
"outline"
class=
"control-field"
>
<div
class=
"month-calendar"
>
<mat-label>
กรองตามห้องประชุม
</mat-label>
<div
class=
"month-toolbar"
>
<mat-select
[(
value
)]="
selectedRoom
"
(
selectionChange
)="
onRoomChange
()"
>
<button
mat-icon-button
(
click
)="
prevMonth
()"
aria-label=
"Previous Month"
>
<mat-option
value=
""
>
ทั้งหมด
</mat-option>
<mat-icon>
chevron_left
</mat-icon>
<mat-option
*
ngFor=
"let room of rooms$ | async"
[
value
]="
room
.
id
"
>
</button>
{{ room.name }}
<div
class=
"month-title"
>
{{ monthLabel() }}
</div>
</mat-option>
<button
mat-icon-button
(
click
)="
nextMonth
()"
aria-label=
"Next Month"
>
</mat-select>
<mat-icon>
chevron_right
</mat-icon>
</mat-form-field>
</button>
<button
mat-button
(
click
)="
today
()"
>
วันนี้
</button>
</div>
<mat-form-field
appearance=
"outline"
class=
"control-field"
>
<!-- Weekday headers -->
<mat-label>
มุมมอง
</mat-label>
<div
class=
"calendar-grid calendar-header"
[
ngStyle
]="{
display:
'
grid
','
grid-template-columns
'
:
'
repeat
(
7
,
1fr
)',
gap:
'
4px
'}"
>
<mat-select
[(
value
)]="
currentView
"
(
selectionChange
)="
onViewChange
($
event
)"
>
<div
class=
"calendar-cell"
>
อา
</div>
<mat-option
value=
"Day"
>
รายวัน
</mat-option>
<div
class=
"calendar-cell"
>
จ
</div>
<mat-option
value=
"Week"
>
รายสัปดาห์
</mat-option>
<div
class=
"calendar-cell"
>
อ
</div>
<mat-option
value=
"WorkWeek"
>
วันทำงาน
</mat-option>
<div
class=
"calendar-cell"
>
พ
</div>
<mat-option
value=
"Month"
>
รายเดือน
</mat-option>
<div
class=
"calendar-cell"
>
พฤ
</div>
<mat-option
value=
"Agenda"
>
วาระการประชุม
</mat-option>
<div
class=
"calendar-cell"
>
ศ
</div>
</mat-select>
<div
class=
"calendar-cell"
>
ส
</div>
</mat-form-field>
</div>
<!-- Weeks -->
<div
class=
"calendar-week"
*
ngFor=
"let week of monthWeeks"
>
<div
class=
"calendar-grid"
[
ngStyle
]="{
display:
'
grid
','
grid-template-columns
'
:
'
repeat
(
7
,
1fr
)',
gap:
'
4px
'}"
>
<div
class=
"calendar-cell"
*
ngFor=
"let day of week"
[
class
.
outside-month
]="!
isCurrentMonth
(
day
)"
[
class
.
today
]="
isToday
(
day
)"
>
<div
class=
"cell-header"
>
<span
class=
"date-number"
>
{{ day.getDate() }}
</span>
</div>
<div
class=
"cell-events"
>
<div
class=
"event-chip status-{{ e.Status }}"
*
ngFor=
"let e of getBookingsForDate(day)"
(
click
)="
onEventClick
({
event:
e
})"
>
<span
class=
"time"
*
ngIf=
"e.StartTime && e.EndTime"
>
{{ e.StartTime | date:'HH:mm' }}-{{ e.EndTime | date:'HH:mm' }}
</span>
<span
class=
"title"
>
{{ e.Subject }}
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
...
@@ -189,6 +226,7 @@
...
@@ -189,6 +226,7 @@
[
eventSettings
]="
eventSettings
"
[
eventSettings
]="
eventSettings
"
[
showQuickInfo
]="
showQuickInfo
"
[
showQuickInfo
]="
showQuickInfo
"
[
allowDragAndDrop
]="
allowDragAndDrop
"
[
allowDragAndDrop
]="
allowDragAndDrop
"
[
showHeaderBar
]="
false
"
(
eventClick
)="
onEventClick
($
event
)"
(
eventClick
)="
onEventClick
($
event
)"
(
eventCreate
)="
onEventCreate
($
event
)"
(
eventCreate
)="
onEventCreate
($
event
)"
(
eventUpdate
)="
onEventUpdate
($
event
)"
(
eventUpdate
)="
onEventUpdate
($
event
)"
...
...
src/app/portal-manage/meeting-booking/meeting-booking.component.scss
View file @
c8dc0a75
...
@@ -176,26 +176,35 @@
...
@@ -176,26 +176,35 @@
background
:
linear-gradient
(
135deg
,
#f8f9fa
0%
,
#e9ecef
100%
);
background
:
linear-gradient
(
135deg
,
#f8f9fa
0%
,
#e9ecef
100%
);
border-bottom
:
1px
solid
#dee2e6
;
border-bottom
:
1px
solid
#dee2e6
;
.schedule-title
{
.schedule-header-top
{
margin-bottom
:
1
.5rem
;
display
:
flex
;
align-items
:
center
;
gap
:
1rem
;
margin-bottom
:
0
.5rem
;
h2
{
.schedule-title
{
margin
:
0
0
0
.5rem
0
;
h2
{
font-size
:
1
.8rem
;
margin
:
0
;
font-weight
:
700
;
font-size
:
1
.8rem
;
color
:
#495057
;
font-weight
:
700
;
display
:
flex
;
color
:
#495057
;
align-items
:
center
;
display
:
flex
;
gap
:
0
.5rem
;
align-items
:
center
;
gap
:
0
.5rem
;
}
}
}
p
{
.schedule-controls
{
margin
:
0
;
margin-left
:
auto
;
color
:
#6c757d
;
font-size
:
1rem
;
}
}
}
}
.schedule-subtitle
{
margin
:
0
;
color
:
#6c757d
;
font-size
:
1rem
;
}
.schedule-controls
{
.schedule-controls
{
display
:
flex
;
display
:
flex
;
gap
:
1rem
;
gap
:
1rem
;
...
@@ -212,6 +221,101 @@
...
@@ -212,6 +221,101 @@
}
}
}
}
// Monthly calendar grid styles
.month-calendar
{
padding
:
1rem
1rem
0
1rem
;
.month-toolbar
{
display
:
flex
;
align-items
:
center
;
gap
:
0
.5rem
;
margin-bottom
:
0
.75rem
;
.month-title
{
margin-inline
:
auto
;
font-weight
:
700
;
color
:
#495057
;
}
}
.calendar-grid
{
display
:
grid
;
grid-template-columns
:
repeat
(
7
,
1fr
);
gap
:
4px
;
}
.calendar-header
{
padding
:
0
2px
6px
2px
;
color
:
#6c757d
;
font-weight
:
700
;
text-align
:
center
;
}
.calendar-week
{
margin-bottom
:
4px
;
}
.calendar-cell
{
background
:
#fff
;
border
:
1px
solid
#e9ecef
;
border-radius
:
8px
;
min-height
:
110px
;
padding
:
6px
;
display
:
flex
;
flex-direction
:
column
;
}
.calendar-cell.outside-month
{
background
:
#fafbfc
;
color
:
#adb5bd
;
}
.calendar-cell.today
{
box-shadow
:
inset
0
0
0
2px
#667eea
;
}
.cell-header
{
display
:
flex
;
justify-content
:
flex-end
;
font-weight
:
700
;
color
:
#495057
;
}
.date-number
{
font-size
:
0
.9rem
;
}
.cell-events
{
margin-top
:
4px
;
display
:
flex
;
flex-direction
:
column
;
gap
:
4px
;
}
.event-chip
{
display
:
inline-flex
;
align-items
:
center
;
gap
:
6px
;
padding
:
2px
6px
;
border-radius
:
6px
;
font-size
:
12px
;
line-height
:
1
.2
;
color
:
#fff
;
cursor
:
pointer
;
user-select
:
none
;
white-space
:
nowrap
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
}
.event-chip
.time
{
opacity
:
0
.9
;
}
.event-chip.status-confirmed
{
background
:
#4caf50
;
}
.event-chip.status-pending
{
background
:
#ff9800
;
}
.event-chip.status-cancelled
{
background
:
#f44336
;
}
.event-chip.status-completed
{
background
:
#2196f3
;
}
}
// Statistics Section
// Statistics Section
.statistics-section
{
.statistics-section
{
margin-bottom
:
2rem
;
margin-bottom
:
2rem
;
...
...
src/app/portal-manage/meeting-booking/meeting-booking.component.ts
View file @
c8dc0a75
...
@@ -100,6 +100,9 @@ export class MeetingBookingComponent implements OnInit {
...
@@ -100,6 +100,9 @@ export class MeetingBookingComponent implements OnInit {
// UI State properties
// UI State properties
public
showBookingForm
:
boolean
=
false
;
public
showBookingForm
:
boolean
=
false
;
// Monthly calendar grid state
public
monthWeeks
:
Date
[][]
=
[];
constructor
(
constructor
(
private
meetingBookingService
:
MeetingBookingService
,
private
meetingBookingService
:
MeetingBookingService
,
private
fb
:
FormBuilder
,
private
fb
:
FormBuilder
,
...
@@ -128,6 +131,7 @@ export class MeetingBookingComponent implements OnInit {
...
@@ -128,6 +131,7 @@ export class MeetingBookingComponent implements OnInit {
this
.
loadTimeSlots
();
this
.
loadTimeSlots
();
this
.
loadScheduleData
();
this
.
loadScheduleData
();
this
.
loadSampleData
();
// เพิ่มข้อมูลตัวอย่าง
this
.
loadSampleData
();
// เพิ่มข้อมูลตัวอย่าง
this
.
buildMonthWeeks
(
this
.
selectedDate_schedule
);
}
}
onDateChange
():
void
{
onDateChange
():
void
{
...
@@ -295,6 +299,7 @@ export class MeetingBookingComponent implements OnInit {
...
@@ -295,6 +299,7 @@ export class MeetingBookingComponent implements OnInit {
};
};
console
.
log
(
'Schedule data loaded:'
,
this
.
scheduleData
);
console
.
log
(
'Schedule data loaded:'
,
this
.
scheduleData
);
this
.
buildMonthWeeks
(
this
.
selectedDate_schedule
);
});
});
}
else
{
}
else
{
// ถ้าไม่มี bookings$ observable ให้ใช้ข้อมูลตัวอย่าง
// ถ้าไม่มี bookings$ observable ให้ใช้ข้อมูลตัวอย่าง
...
@@ -356,6 +361,7 @@ export class MeetingBookingComponent implements OnInit {
...
@@ -356,6 +361,7 @@ export class MeetingBookingComponent implements OnInit {
onDateChange_schedule
(
args
:
any
):
void
{
onDateChange_schedule
(
args
:
any
):
void
{
console
.
log
(
'Date changed:'
,
args
);
console
.
log
(
'Date changed:'
,
args
);
this
.
selectedDate_schedule
=
args
.
selectedDate
;
this
.
selectedDate_schedule
=
args
.
selectedDate
;
this
.
buildMonthWeeks
(
this
.
selectedDate_schedule
);
}
}
// ฟังก์ชันสำหรับโหลดข้อมูลตัวอย่าง
// ฟังก์ชันสำหรับโหลดข้อมูลตัวอย่าง
...
@@ -414,6 +420,7 @@ export class MeetingBookingComponent implements OnInit {
...
@@ -414,6 +420,7 @@ export class MeetingBookingComponent implements OnInit {
};
};
console
.
log
(
'Sample data loaded:'
,
this
.
scheduleData
);
console
.
log
(
'Sample data loaded:'
,
this
.
scheduleData
);
this
.
buildMonthWeeks
(
this
.
selectedDate_schedule
);
}
}
// Statistics methods
// Statistics methods
...
@@ -429,4 +436,84 @@ export class MeetingBookingComponent implements OnInit {
...
@@ -429,4 +436,84 @@ export class MeetingBookingComponent implements OnInit {
const
uniqueRooms
=
new
Set
(
this
.
scheduleData
.
map
(
booking
=>
booking
.
Location
));
const
uniqueRooms
=
new
Set
(
this
.
scheduleData
.
map
(
booking
=>
booking
.
Location
));
return
uniqueRooms
.
size
;
return
uniqueRooms
.
size
;
}
}
// ===== Monthly Calendar Helpers =====
private
startOfWeek
(
date
:
Date
):
Date
{
const
d
=
new
Date
(
date
);
const
day
=
d
.
getDay
();
// 0 = Sun
d
.
setDate
(
d
.
getDate
()
-
day
);
d
.
setHours
(
0
,
0
,
0
,
0
);
return
d
;
}
private
endOfWeek
(
date
:
Date
):
Date
{
const
d
=
new
Date
(
date
);
const
day
=
d
.
getDay
();
d
.
setDate
(
d
.
getDate
()
+
(
6
-
day
));
d
.
setHours
(
23
,
59
,
59
,
999
);
return
d
;
}
public
buildMonthWeeks
(
baseDate
:
Date
):
void
{
const
firstOfMonth
=
new
Date
(
baseDate
.
getFullYear
(),
baseDate
.
getMonth
(),
1
);
const
lastOfMonth
=
new
Date
(
baseDate
.
getFullYear
(),
baseDate
.
getMonth
()
+
1
,
0
);
const
gridStart
=
this
.
startOfWeek
(
firstOfMonth
);
const
gridEnd
=
this
.
endOfWeek
(
lastOfMonth
);
const
weeks
:
Date
[][]
=
[];
const
cursor
=
new
Date
(
gridStart
);
while
(
cursor
<=
gridEnd
)
{
const
week
:
Date
[]
=
[];
for
(
let
i
=
0
;
i
<
7
;
i
++
)
{
week
.
push
(
new
Date
(
cursor
));
cursor
.
setDate
(
cursor
.
getDate
()
+
1
);
}
weeks
.
push
(
week
);
}
this
.
monthWeeks
=
weeks
;
}
public
prevMonth
():
void
{
const
d
=
new
Date
(
this
.
selectedDate_schedule
);
d
.
setMonth
(
d
.
getMonth
()
-
1
);
this
.
selectedDate_schedule
=
d
;
this
.
buildMonthWeeks
(
this
.
selectedDate_schedule
);
}
public
nextMonth
():
void
{
const
d
=
new
Date
(
this
.
selectedDate_schedule
);
d
.
setMonth
(
d
.
getMonth
()
+
1
);
this
.
selectedDate_schedule
=
d
;
this
.
buildMonthWeeks
(
this
.
selectedDate_schedule
);
}
public
today
():
void
{
this
.
selectedDate_schedule
=
new
Date
();
this
.
buildMonthWeeks
(
this
.
selectedDate_schedule
);
}
public
isSameDay
(
a
:
Date
,
b
:
Date
):
boolean
{
return
a
.
getFullYear
()
===
b
.
getFullYear
()
&&
a
.
getMonth
()
===
b
.
getMonth
()
&&
a
.
getDate
()
===
b
.
getDate
();
}
public
isCurrentMonth
(
date
:
Date
):
boolean
{
return
date
.
getMonth
()
===
this
.
selectedDate_schedule
.
getMonth
()
&&
date
.
getFullYear
()
===
this
.
selectedDate_schedule
.
getFullYear
();
}
public
isToday
(
date
:
Date
):
boolean
{
return
this
.
isSameDay
(
date
,
new
Date
());
}
public
getBookingsForDate
(
date
:
Date
):
any
[]
{
const
items
=
this
.
scheduleData
.
filter
(
e
=>
this
.
isSameDay
(
new
Date
(
e
.
StartTime
),
date
));
if
(
this
.
selectedRoom
)
{
return
items
.
filter
(
e
=>
e
.
Location
===
this
.
getRoomName
(
this
.
selectedRoom
));
}
return
items
;
}
public
monthLabel
():
string
{
const
d
=
this
.
selectedDate_schedule
;
return
`
${
d
.
toLocaleString
(
'th-TH'
,
{
month
:
'long'
})}
${
d
.
getFullYear
()}
`
;
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment