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
ca26828b
Commit
ca26828b
authored
Oct 07, 2025
by
sawit
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ตารางปฏิทิน และทะเบียนห้องประชุม
parent
66b5f4ce
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
634 additions
and
120 deletions
+634
-120
meeting-booking.component.html
...tal-manage/meeting-booking/meeting-booking.component.html
+13
-52
meeting-booking.component.ts
...ortal-manage/meeting-booking/meeting-booking.component.ts
+30
-66
meeting-room.component.css
...e/meeting-booking/meeting-room/meeting-room.component.css
+0
-0
meeting-room.component.html
.../meeting-booking/meeting-room/meeting-room.component.html
+132
-0
meeting-room.component.spec.ts
...eting-booking/meeting-room/meeting-room.component.spec.ts
+28
-0
meeting-room.component.ts
...ge/meeting-booking/meeting-room/meeting-room.component.ts
+137
-0
portal-manage.routes.ts
src/app/portal-manage/portal-manage.routes.ts
+8
-0
sidebar.component.ts
src/app/shared/components/sidebar/sidebar.component.ts
+5
-1
nav.service.ts
src/app/shared/services/nav.service.ts
+1
-0
main.ts
src/main.ts
+78
-1
styles.scss
src/styles.scss
+202
-0
No files found.
src/app/portal-manage/meeting-booking/meeting-booking.component.html
View file @
ca26828b
...
@@ -200,53 +200,6 @@
...
@@ -200,53 +200,6 @@
<p
class=
"schedule-subtitle"
>
ดูและจัดการการจองในรูปแบบปฏิทินแบบเต็มรูปแบบ
</p>
<p
class=
"schedule-subtitle"
>
ดูและจัดการการจองในรูปแบบปฏิทินแบบเต็มรูปแบบ
</p>
</div>
</div>
<!-- Monthly Calendar Grid -->
<div
class=
"month-calendar"
>
<div
class=
"month-toolbar"
>
<button
mat-icon-button
(
click
)="
prevMonth
()"
aria-label=
"Previous Month"
>
<mat-icon>
chevron_left
</mat-icon>
</button>
<div
class=
"month-title"
>
{{ monthLabel() }}
</div>
<button
mat-icon-button
(
click
)="
nextMonth
()"
aria-label=
"Next Month"
>
<mat-icon>
chevron_right
</mat-icon>
</button>
<button
mat-button
(
click
)="
today
()"
>
วันนี้
</button>
</div>
<!-- Weekday headers -->
<div
class=
"calendar-grid calendar-header"
[
ngStyle
]="{
display:
'
grid
','
grid-template-columns
'
:
'
repeat
(
7
,
1fr
)',
gap:
'
4px
'}"
>
<div
class=
"calendar-cell"
>
อา
</div>
<div
class=
"calendar-cell"
>
จ
</div>
<div
class=
"calendar-cell"
>
อ
</div>
<div
class=
"calendar-cell"
>
พ
</div>
<div
class=
"calendar-cell"
>
พฤ
</div>
<div
class=
"calendar-cell"
>
ศ
</div>
<div
class=
"calendar-cell"
>
ส
</div>
</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>
<span
class=
"count"
*
ngIf=
"e.AttendeesCount"
>
({{ e.AttendeesCount }})
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Syncfusion Schedule Component -->
<!-- Syncfusion Schedule Component -->
<div
class=
"schedule-wrapper"
>
<div
class=
"schedule-wrapper"
>
...
@@ -257,16 +210,24 @@
...
@@ -257,16 +210,24 @@
[
eventSettings
]="
eventSettings
"
[
eventSettings
]="
eventSettings
"
[
showQuickInfo
]="
showQuickInfo
"
[
showQuickInfo
]="
showQuickInfo
"
[
allowDragAndDrop
]="
allowDragAndDrop
"
[
allowDragAndDrop
]="
allowDragAndDrop
"
[
showHeaderBar
]="
false
"
[
showHeaderBar
]="
true
"
[
views
]="
scheduleViews
"
[
locale
]="
locale
"
[
enableRtl
]="
enableRtl
"
[
firstDayOfWeek
]="
firstDayOfWeek
"
[
timeFormat
]="
timeFormat
"
[
dateFormat
]="
dateFormat
"
[
workDays
]="
workDays
"
[
workHours
]="
workHours
"
[
height
]="
height
"
[
width
]="
width
"
[
cssClass
]="
cssClass
"
(
eventClick
)="
onEventClick
($
event
)"
(
eventClick
)="
onEventClick
($
event
)"
(
eventCreate
)="
onEventCreate
($
event
)"
(
eventCreate
)="
onEventCreate
($
event
)"
(
eventUpdate
)="
onEventUpdate
($
event
)"
(
eventUpdate
)="
onEventUpdate
($
event
)"
(
eventDelete
)="
onEventDelete
($
event
)"
(
eventDelete
)="
onEventDelete
($
event
)"
(
viewChange
)="
onViewChange
($
event
)"
(
viewChange
)="
onViewChange
($
event
)"
(
dateChange
)="
onDateChange_schedule
($
event
)"
(
dateChange
)="
onDateChange_schedule
($
event
)"
>
height=
"700px"
width=
"100%"
cssClass=
"custom-schedule"
>
<!-- Event Template -->
<!-- Event Template -->
<ng-template
#
eventTemplate
let-data
>
<ng-template
#
eventTemplate
let-data
>
...
...
src/app/portal-manage/meeting-booking/meeting-booking.component.ts
View file @
ca26828b
...
@@ -18,6 +18,7 @@ import { MatDialogModule, MatDialog } from '@angular/material/dialog';
...
@@ -18,6 +18,7 @@ import { MatDialogModule, MatDialog } from '@angular/material/dialog';
import
{
MatTabsModule
}
from
'@angular/material/tabs'
;
import
{
MatTabsModule
}
from
'@angular/material/tabs'
;
import
{
MatSnackBarModule
,
MatSnackBar
}
from
'@angular/material/snack-bar'
;
import
{
MatSnackBarModule
,
MatSnackBar
}
from
'@angular/material/snack-bar'
;
import
{
MatProgressSpinnerModule
}
from
'@angular/material/progress-spinner'
;
import
{
MatProgressSpinnerModule
}
from
'@angular/material/progress-spinner'
;
import
{
MatButtonToggleModule
}
from
'@angular/material/button-toggle'
;
import
{
TranslateModule
}
from
'@ngx-translate/core'
;
import
{
TranslateModule
}
from
'@ngx-translate/core'
;
import
{
Observable
}
from
'rxjs'
;
import
{
Observable
}
from
'rxjs'
;
...
@@ -25,6 +26,7 @@ import { Observable } from 'rxjs';
...
@@ -25,6 +26,7 @@ import { Observable } from 'rxjs';
import
{
ScheduleModule
,
View
,
EventSettingsModel
,
DayService
,
WeekService
,
WorkWeekService
,
MonthService
,
AgendaService
,
ResizeService
,
DragAndDropService
}
from
'@syncfusion/ej2-angular-schedule'
;
import
{
ScheduleModule
,
View
,
EventSettingsModel
,
DayService
,
WeekService
,
WorkWeekService
,
MonthService
,
AgendaService
,
ResizeService
,
DragAndDropService
}
from
'@syncfusion/ej2-angular-schedule'
;
import
{
DateTimePickerModule
}
from
'@syncfusion/ej2-angular-calendars'
;
import
{
DateTimePickerModule
}
from
'@syncfusion/ej2-angular-calendars'
;
import
{
DropDownListModule
}
from
'@syncfusion/ej2-angular-dropdowns'
;
import
{
DropDownListModule
}
from
'@syncfusion/ej2-angular-dropdowns'
;
import
{
L10n
,
setCulture
}
from
'@syncfusion/ej2-base'
;
import
{
MeetingBookingService
}
from
'../services/meeting-booking.service'
;
import
{
MeetingBookingService
}
from
'../services/meeting-booking.service'
;
import
{
MeetingRoom
,
MeetingBooking
,
BookingTimeSlot
,
BookingStatistics
}
from
'../models/meeting-booking.model'
;
import
{
MeetingRoom
,
MeetingBooking
,
BookingTimeSlot
,
BookingStatistics
}
from
'../models/meeting-booking.model'
;
...
@@ -52,6 +54,7 @@ import { MeetingRoom, MeetingBooking, BookingTimeSlot, BookingStatistics } from
...
@@ -52,6 +54,7 @@ import { MeetingRoom, MeetingBooking, BookingTimeSlot, BookingStatistics } from
MatTabsModule
,
MatTabsModule
,
MatSnackBarModule
,
MatSnackBarModule
,
MatProgressSpinnerModule
,
MatProgressSpinnerModule
,
MatButtonToggleModule
,
TranslateModule
,
TranslateModule
,
// Syncfusion modules
// Syncfusion modules
ScheduleModule
,
ScheduleModule
,
...
@@ -91,6 +94,20 @@ export class MeetingBookingComponent implements OnInit {
...
@@ -91,6 +94,20 @@ export class MeetingBookingComponent implements OnInit {
// Syncfusion Schedule properties
// Syncfusion Schedule properties
public
selectedDate_schedule
:
Date
=
new
Date
();
public
selectedDate_schedule
:
Date
=
new
Date
();
public
currentView
:
View
=
'Month'
;
public
currentView
:
View
=
'Month'
;
public
scheduleViews
:
View
[]
=
[
'Month'
,
'Week'
,
'Day'
,
'Agenda'
];
public
locale
:
string
=
'en'
;
public
enableRtl
:
boolean
=
false
;
public
firstDayOfWeek
:
number
=
0
;
// 0 = Sunday
public
timeFormat
:
string
=
'HH:mm'
;
public
dateFormat
:
string
=
'dd/MM/yyyy'
;
public
workDays
:
number
[]
=
[
0
,
1
,
2
,
3
,
4
,
5
,
6
];
// All days
public
workHours
:
any
=
{
start
:
'08:00'
,
end
:
'18:00'
};
public
height
:
string
=
'700px'
;
public
width
:
string
=
'100%'
;
public
cssClass
:
string
=
'custom-schedule'
;
public
eventSettings
:
EventSettingsModel
=
{
public
eventSettings
:
EventSettingsModel
=
{
dataSource
:
[]
dataSource
:
[]
};
};
...
@@ -101,9 +118,6 @@ export class MeetingBookingComponent implements OnInit {
...
@@ -101,9 +118,6 @@ 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
,
...
@@ -131,10 +145,21 @@ export class MeetingBookingComponent implements OnInit {
...
@@ -131,10 +145,21 @@ export class MeetingBookingComponent implements OnInit {
}
}
ngOnInit
():
void
{
ngOnInit
():
void
{
this
.
setupLocale
();
this
.
loadTimeSlots
();
this
.
loadTimeSlots
();
this
.
loadScheduleData
();
this
.
loadScheduleData
();
this
.
loadSampleData
();
// เพิ่มข้อมูลตัวอย่าง
this
.
loadSampleData
();
// เพิ่มข้อมูลตัวอย่าง
this
.
buildMonthWeeks
(
this
.
selectedDate_schedule
);
}
private
setupLocale
():
void
{
// Ensure locale is set before Schedule initializes
try
{
// Try to set English locale first to avoid the error
setCulture
(
'en'
);
console
.
log
(
'Locale setup completed successfully with English'
);
}
catch
(
error
)
{
console
.
error
(
'Error setting up locale:'
,
error
);
}
}
}
onDateChange
():
void
{
onDateChange
():
void
{
...
@@ -304,7 +329,6 @@ export class MeetingBookingComponent implements OnInit {
...
@@ -304,7 +329,6 @@ 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 ให้ใช้ข้อมูลตัวอย่าง
...
@@ -366,7 +390,6 @@ export class MeetingBookingComponent implements OnInit {
...
@@ -366,7 +390,6 @@ 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
);
}
}
// ฟังก์ชันสำหรับโหลดข้อมูลตัวอย่าง
// ฟังก์ชันสำหรับโหลดข้อมูลตัวอย่าง
...
@@ -431,7 +454,6 @@ export class MeetingBookingComponent implements OnInit {
...
@@ -431,7 +454,6 @@ 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
...
@@ -448,79 +470,21 @@ export class MeetingBookingComponent implements OnInit {
...
@@ -448,79 +470,21 @@ export class MeetingBookingComponent implements OnInit {
return
uniqueRooms
.
size
;
return
uniqueRooms
.
size
;
}
}
// ===== Monthly Calendar Helpers =====
// ===== Month Navigation 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
{
public
prevMonth
():
void
{
const
d
=
new
Date
(
this
.
selectedDate_schedule
);
const
d
=
new
Date
(
this
.
selectedDate_schedule
);
d
.
setMonth
(
d
.
getMonth
()
-
1
);
d
.
setMonth
(
d
.
getMonth
()
-
1
);
this
.
selectedDate_schedule
=
d
;
this
.
selectedDate_schedule
=
d
;
this
.
buildMonthWeeks
(
this
.
selectedDate_schedule
);
}
}
public
nextMonth
():
void
{
public
nextMonth
():
void
{
const
d
=
new
Date
(
this
.
selectedDate_schedule
);
const
d
=
new
Date
(
this
.
selectedDate_schedule
);
d
.
setMonth
(
d
.
getMonth
()
+
1
);
d
.
setMonth
(
d
.
getMonth
()
+
1
);
this
.
selectedDate_schedule
=
d
;
this
.
selectedDate_schedule
=
d
;
this
.
buildMonthWeeks
(
this
.
selectedDate_schedule
);
}
}
public
today
():
void
{
public
today
():
void
{
this
.
selectedDate_schedule
=
new
Date
();
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
getRoomImageByName
(
roomName
:
string
):
string
|
undefined
{
public
getRoomImageByName
(
roomName
:
string
):
string
|
undefined
{
...
...
src/app/portal-manage/meeting-booking/meeting-room/meeting-room.component.css
0 → 100644
View file @
ca26828b
src/app/portal-manage/meeting-booking/meeting-room/meeting-room.component.html
0 → 100644
View file @
ca26828b
<app-page-header
[
title
]="'ทะเบียนห้องประชุม'"
[
activeTitle
]="'ผู้ดูแลระบบ'"
[
title1
]="'ทะเบียนห้องประชุม'"
></app-page-header>
<div
class=
"grid grid-cols-12 gap-6"
>
<div
class=
"xl:col-span-12 col-span-12"
>
<div
class=
"box"
>
<div
class=
"box-header justify-between"
>
<div
class=
"box-title flex items-center gap-2"
>
<mat-icon>
meeting_room
</mat-icon>
ทะเบียนห้องประชุม
<span
class=
"badge bg-light text-default rounded-full ms-1 text-[0.75rem] align-middle"
>
{{ rooms.length }}
</span>
</div>
<div
class=
"flex flex-wrap gap-2"
>
<a
href=
"javascript:void(0);"
class=
"hs-dropdown-toggle ti-btn ti-btn-primary-full me-2"
(
click
)="
startCreate
()"
data-hs-overlay=
"#room-modal"
>
<i
class=
"ri-add-line font-semibold align-middle"
></i>
เพิ่มห้อง
</a>
<div>
<input
class=
"form-control form-control"
type=
"text"
placeholder=
"ค้นหาห้อง/อาคาร"
[(
ngModel
)]="
searchTerm
"
>
</div>
</div>
</div>
<div
class=
"box-body"
>
<div
class=
"table-responsive"
>
<table
class=
"table whitespace-nowrap min-w-full ti-custom-table-hover"
>
<thead>
<tr
class=
"border-b border-defaultborder"
>
<th
scope=
"col"
class=
"text-start"
>
ห้อง
</th>
<th
scope=
"col"
class=
"text-start"
>
อาคาร/ชั้น
</th>
<th
scope=
"col"
class=
"text-start"
>
ความจุ
</th>
<th
scope=
"col"
class=
"text-start"
>
สถานะ
</th>
<th
scope=
"col"
class=
"text-start"
>
Action
</th>
</tr>
</thead>
<tbody>
<tr
class=
"border border-defaultborder dark:border-defaultborder/10"
*
ngFor=
"let r of filteredRooms"
>
<td>
<div
class=
"flex items-center"
>
<span
class=
"avatar avatar-sm p-1 me-1 bg-light !rounded-full"
>
<img
[
src
]="
r
.
imageUrl
||
'
assets
/
images
/
media
/
backgrounds
/
1
.
png
'"
alt=
""
/>
</span>
<div
class=
"ms-2"
>
<p
class=
"font-semibold mb-0 text-primary"
>
{{ r.name }}
</p>
</div>
</div>
</td>
<td>
{{ r.building }} / {{ r.floor }}
</td>
<td>
{{ r.capacity }} คน
</td>
<td>
<span
class=
"badge"
[
ngClass
]="
r
.
isActive
?
'
bg-primary
'
:
'
bg-warning
'"
>
{{ r.isActive ? 'Active' : 'Inactive' }}
</span>
</td>
<td>
<div
class=
"flex flex-row items-center !gap-2 "
>
<a
aria-label=
"anchor"
(
click
)="
edit
(
r
)"
data-hs-overlay=
"#room-modal"
class=
"ti-btn ti-btn-wave !gap-0 !m-0 bg-info/10 text-info hover:bg-info hover:text-white hover:border-info"
><i
class=
"ri-pencil-line"
></i></a>
<a
aria-label=
"anchor"
href=
"javascript:void(0);"
(
click
)="
remove
(
r
)"
class=
"ti-btn ti-btn-wave product-btn !gap-0 !m-0 bg-danger/10 text-danger hover:bg-danger hover:text-white hover:border-danger"
><i
class=
"ri-delete-bin-line"
></i></a>
</div>
</td>
</tr>
<tr
*
ngIf=
"filteredRooms.length === 0"
>
<td
[
attr
.
colspan
]="
5
"
class=
"text-center py-4"
>
ไม่พบข้อมูล...
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Modal ฟอร์มห้องประชุม -->
<div
id=
"room-modal"
class=
"hs-overlay hidden ti-modal [--overlay-backdrop:static]"
>
<div
class=
"hs-overlay-open:mt-7 ti-modal-box mt-0 ease-out"
>
<div
class=
"ti-modal-content"
>
<div
class=
"ti-modal-header"
>
<h6
class=
"modal-title text-[1rem] font-semibold text-defaulttextcolor"
>
{{ form.value.id ? 'แก้ไขห้อง' : 'เพิ่มห้อง' }}
</h6>
<button
type=
"button"
class=
"hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
data-hs-overlay=
"#room-modal"
>
<span
class=
"sr-only"
>
Close
</span>
<i
class=
"ri-close-line"
></i>
</button>
</div>
<div
class=
"ti-modal-body px-4"
>
<form
[
formGroup
]="
form
"
(
ngSubmit
)="
save
()"
class=
"grid grid-cols-12 gap-4"
>
<div
class=
"xl:col-span-12 col-span-12"
>
<div
class=
"mb-0 text-center"
>
<span
class=
"avatar avatar-xxl avatar-rounded"
>
<img
[
src
]="
imagePreview
||
form
.
value
.
imageUrl
||
'
assets
/
images
/
media
/
backgrounds
/
1
.
png
'"
alt=
""
id=
"room-img"
>
<span
class=
"badge rounded-full bg-primary avatar-badge"
>
<input
type=
"file"
accept=
"image/*"
class=
"absolute w-full h-full opacity-[0]"
(
change
)="
onImageFileChange
($
event
)"
>
<i
class=
"fe fe-camera text-[.625rem]"
></i>
</span>
</span>
</div>
</div>
<div
class=
"xl:col-span-12 col-span-12"
>
<label
class=
"form-label"
>
ชื่อห้อง
</label>
<input
type=
"text"
class=
"form-control"
formControlName=
"name"
>
</div>
<div
class=
"xl:col-span-6 col-span-12"
>
<label
class=
"form-label"
>
ความจุ (คน)
</label>
<input
type=
"number"
class=
"form-control"
formControlName=
"capacity"
>
</div>
<div
class=
"xl:col-span-6 col-span-12"
>
<label
class=
"form-label"
>
อาคาร
</label>
<input
type=
"text"
class=
"form-control"
formControlName=
"building"
>
</div>
<div
class=
"xl:col-span-6 col-span-12"
>
<label
class=
"form-label"
>
ชั้น
</label>
<input
type=
"text"
class=
"form-control"
formControlName=
"floor"
>
</div>
<div
class=
"xl:col-span-6 col-span-12"
>
<label
class=
"form-label"
>
สถานะ
</label>
<select
class=
"form-control"
formControlName=
"isActive"
>
<option
[
ngValue
]="
true
"
>
Active
</option>
<option
[
ngValue
]="
false
"
>
Inactive
</option>
</select>
</div>
<div
class=
"xl:col-span-12 col-span-12"
>
<label
class=
"form-label"
>
รายละเอียด
</label>
<input
type=
"text"
class=
"form-control"
formControlName=
"amenitiesText"
>
</div>
<div
class=
"xl:col-span-12 col-span-12"
>
<label
class=
"form-label"
>
หมายเหตุ
</label>
<input
type=
"text"
class=
"form-control"
formControlName=
"remarksText"
>
</div>
</form>
</div>
<div
class=
"ti-modal-footer"
>
<button
type=
"button"
class=
"hs-dropdown-toggle ti-btn ti-btn-light align-middle"
data-hs-overlay=
"#room-modal"
>
ยกเลิก
</button>
<button
type=
"button"
class=
"ti-btn bg-primary text-white !font-medium"
(
click
)="
save
()"
[
class
.
ti-btn-disabled
]="
form
.
invalid
"
[
disabled
]="
form
.
invalid
"
>
บันทึก
</button>
</div>
</div>
</div>
</div>
src/app/portal-manage/meeting-booking/meeting-room/meeting-room.component.spec.ts
0 → 100644
View file @
ca26828b
/* tslint:disable:no-unused-variable */
import
{
async
,
ComponentFixture
,
TestBed
}
from
'@angular/core/testing'
;
import
{
By
}
from
'@angular/platform-browser'
;
import
{
DebugElement
}
from
'@angular/core'
;
import
{
MeetingRoomComponent
}
from
'./meeting-room.component'
;
describe
(
'MeetingRoomComponent'
,
()
=>
{
let
component
:
MeetingRoomComponent
;
let
fixture
:
ComponentFixture
<
MeetingRoomComponent
>
;
beforeEach
(
async
(()
=>
{
TestBed
.
configureTestingModule
({
declarations
:
[
MeetingRoomComponent
]
})
.
compileComponents
();
}));
beforeEach
(()
=>
{
fixture
=
TestBed
.
createComponent
(
MeetingRoomComponent
);
component
=
fixture
.
componentInstance
;
fixture
.
detectChanges
();
});
it
(
'should create'
,
()
=>
{
expect
(
component
).
toBeTruthy
();
});
});
src/app/portal-manage/meeting-booking/meeting-room/meeting-room.component.ts
0 → 100644
View file @
ca26828b
import
{
Component
,
OnInit
}
from
'@angular/core'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
FormsModule
,
ReactiveFormsModule
,
FormBuilder
,
Validators
}
from
'@angular/forms'
;
import
{
MatTableModule
}
from
'@angular/material/table'
;
import
{
MatButtonModule
}
from
'@angular/material/button'
;
import
{
MatIconModule
}
from
'@angular/material/icon'
;
import
{
MatFormFieldModule
}
from
'@angular/material/form-field'
;
import
{
MatInputModule
}
from
'@angular/material/input'
;
import
{
MatSelectModule
}
from
'@angular/material/select'
;
import
{
MatCardModule
}
from
'@angular/material/card'
;
import
{
MatChipsModule
}
from
'@angular/material/chips'
;
import
{
MatSnackBar
,
MatSnackBarModule
}
from
'@angular/material/snack-bar'
;
import
{
MeetingRoom
}
from
'../../models/meeting-booking.model'
;
import
{
SharedModule
}
from
'../../../shared/shared.module'
;
@
Component
({
selector
:
'app-meeting-room'
,
templateUrl
:
'./meeting-room.component.html'
,
styleUrls
:
[
'./meeting-room.component.css'
],
standalone
:
true
,
imports
:
[
CommonModule
,
FormsModule
,
ReactiveFormsModule
,
SharedModule
,
MatTableModule
,
MatButtonModule
,
MatIconModule
,
MatFormFieldModule
,
MatInputModule
,
MatSelectModule
,
MatCardModule
,
MatChipsModule
,
MatSnackBarModule
],
})
export
class
MeetingRoomComponent
implements
OnInit
{
displayedColumns
=
[
'name'
,
'building'
,
'capacity'
,
'actions'
];
rooms
:
MeetingRoom
[]
=
[];
searchTerm
:
string
=
''
;
imagePreview
:
string
=
''
;
form
=
this
.
fb
.
group
({
id
:
[
''
],
name
:
[
''
,
Validators
.
required
],
capacity
:
[
0
],
building
:
[
''
],
floor
:
[
''
],
amenitiesText
:
[
''
],
imageUrl
:
[
''
],
isActive
:
[
true
]
});
constructor
(
private
fb
:
FormBuilder
,
private
snack
:
MatSnackBar
){
// seed sample
this
.
rooms
=
[
{
id
:
'A101'
,
name
:
'ห้องประชุม A101'
,
capacity
:
12
,
location
:
'โซนตะวันออก'
,
floor
:
'10'
,
building
:
'อาคาร A'
,
amenities
:[
'Display 75"'
,
'HDMI'
],
isActive
:
true
,
imageUrl
:
''
},
{
id
:
'B201'
,
name
:
'ห้องประชุม B201'
,
capacity
:
8
,
location
:
'โซนเหนือ'
,
floor
:
'2'
,
building
:
'อาคาร B'
,
amenities
:[
'TV'
,
'Whiteboard'
],
isActive
:
true
,
imageUrl
:
''
}
];
}
ngOnInit
():
void
{
throw
new
Error
(
'Method not implemented.'
);
}
get
filteredRooms
():
MeetingRoom
[]
{
const
q
=
(
this
.
searchTerm
||
''
).
toLowerCase
();
if
(
!
q
)
return
this
.
rooms
;
return
this
.
rooms
.
filter
(
r
=>
r
.
name
.
toLowerCase
().
includes
(
q
)
||
(
r
.
building
||
''
).
toLowerCase
().
includes
(
q
)
||
(
r
.
floor
||
''
).
toLowerCase
().
includes
(
q
)
);
}
// overlay handled by HS overlay via data-hs-overlay attributes
startCreate
(){
this
.
form
.
reset
({
id
:
''
,
name
:
''
,
capacity
:
0
,
building
:
''
,
floor
:
''
,
amenitiesText
:
''
,
imageUrl
:
''
,
isActive
:
true
});
}
edit
(
r
:
MeetingRoom
){
this
.
form
.
setValue
({
id
:
r
.
id
,
name
:
r
.
name
,
capacity
:
r
.
capacity
,
building
:
r
.
building
,
floor
:
r
.
floor
,
amenitiesText
:
r
.
amenities
?.
join
(
', '
)
||
''
,
imageUrl
:
r
.
imageUrl
||
''
,
isActive
:
r
.
isActive
});
this
.
imagePreview
=
r
.
imageUrl
||
''
;
}
cancel
(){
this
.
form
.
reset
();
}
save
(){
const
v
=
this
.
form
.
getRawValue
();
if
(
!
v
)
return
;
const
idx
=
this
.
rooms
.
findIndex
(
x
=>
x
.
id
===
v
.
id
&&
v
.
id
);
const
data
:
MeetingRoom
=
{
id
:
v
.
id
||
this
.
genId
(),
name
:
v
.
name
||
''
,
capacity
:
Number
(
v
.
capacity
)
||
0
,
building
:
v
.
building
||
''
,
floor
:
v
.
floor
||
''
,
location
:
''
,
amenities
:
(
v
.
amenitiesText
||
''
).
split
(
','
).
map
(
s
=>
s
.
trim
()).
filter
(
Boolean
),
isActive
:
!!
v
.
isActive
,
imageUrl
:
this
.
imagePreview
||
v
.
imageUrl
||
''
};
if
(
idx
>=
0
){
this
.
rooms
[
idx
]
=
data
;
}
else
{
this
.
rooms
=
[...
this
.
rooms
,
data
];
}
this
.
snack
.
open
(
'บันทึกห้องประชุมแล้ว'
,
'ปิด'
,
{
duration
:
2000
});
this
.
form
.
reset
();
}
remove
(
r
:
MeetingRoom
){
this
.
rooms
=
this
.
rooms
.
filter
(
x
=>
x
.
id
!==
r
.
id
);
this
.
snack
.
open
(
'ลบห้องประชุมแล้ว'
,
'ปิด'
,
{
duration
:
2000
});
}
private
genId
(){
return
'RM-'
+
Math
.
random
().
toString
(
36
).
slice
(
2
,
8
).
toUpperCase
();
}
onImageFileChange
(
evt
:
any
){
const
file
:
File
|
undefined
=
evt
?.
target
?.
files
?.[
0
];
if
(
!
file
)
return
;
const
reader
=
new
FileReader
();
reader
.
onload
=
()
=>
{
this
.
imagePreview
=
String
(
reader
.
result
||
''
);
};
reader
.
readAsDataURL
(
file
);
}
}
src/app/portal-manage/portal-manage.routes.ts
View file @
ca26828b
...
@@ -2,6 +2,7 @@ import { Routes } from '@angular/router';
...
@@ -2,6 +2,7 @@ import { Routes } from '@angular/router';
import
{
moduleAccessGuard
}
from
'../core/guards/module-access.guard'
;
import
{
moduleAccessGuard
}
from
'../core/guards/module-access.guard'
;
import
{
HomeComponent
}
from
'./home/home.component'
;
import
{
HomeComponent
}
from
'./home/home.component'
;
import
{
MeetingBookingComponent
}
from
'./meeting-booking/meeting-booking.component'
;
import
{
MeetingBookingComponent
}
from
'./meeting-booking/meeting-booking.component'
;
import
{
MeetingRoomComponent
}
from
'../portal-manage/meeting-booking/meeting-room/meeting-room.component'
;
import
{
MenuPermissionManagementComponent
}
from
'./menu-permission-management/menu-permission-management.component'
;
import
{
MenuPermissionManagementComponent
}
from
'./menu-permission-management/menu-permission-management.component'
;
// import { CompanyManagementComponent } from './company-management/company-management.component';
// import { CompanyManagementComponent } from './company-management/company-management.component';
...
@@ -80,6 +81,13 @@ export const portalManageRoutes: Routes = [
...
@@ -80,6 +81,13 @@ export const portalManageRoutes: Routes = [
canActivate
:
[
moduleAccessGuard
]
canActivate
:
[
moduleAccessGuard
]
},
},
// Meeting Room Registry
{
path
:
'meeting-rooms'
,
component
:
MeetingRoomComponent
,
canActivate
:
[
moduleAccessGuard
]
},
// === การตั้งค่าระบบ ===
// === การตั้งค่าระบบ ===
// Permission Management
// Permission Management
...
...
src/app/shared/components/sidebar/sidebar.component.ts
View file @
ca26828b
...
@@ -81,6 +81,7 @@ export class SidebarComponent {
...
@@ -81,6 +81,7 @@ export class SidebarComponent {
isPermissionManagementRoute
:
boolean
=
false
;
isPermissionManagementRoute
:
boolean
=
false
;
isMenuPermissionManagementRoute
:
boolean
=
false
;
isMenuPermissionManagementRoute
:
boolean
=
false
;
isMeetingBookingRoute
:
boolean
=
false
;
isMeetingBookingRoute
:
boolean
=
false
;
isMeetingRoomRegistryRoute
:
boolean
=
false
;
isWidgetWarehouseRoute
:
boolean
=
false
;
isWidgetWarehouseRoute
:
boolean
=
false
;
isWidgetLinkerRoute
:
boolean
=
false
;
isWidgetLinkerRoute
:
boolean
=
false
;
previousUrl
:
string
=
''
;
previousUrl
:
string
=
''
;
...
@@ -133,6 +134,7 @@ export class SidebarComponent {
...
@@ -133,6 +134,7 @@ export class SidebarComponent {
this
.
isPermissionManagementRoute
=
this
.
currentUrl
.
includes
(
'/permission-management'
);
this
.
isPermissionManagementRoute
=
this
.
currentUrl
.
includes
(
'/permission-management'
);
this
.
isMenuPermissionManagementRoute
=
this
.
currentUrl
.
includes
(
'/menu-permission-management'
);
this
.
isMenuPermissionManagementRoute
=
this
.
currentUrl
.
includes
(
'/menu-permission-management'
);
this
.
isMeetingBookingRoute
=
this
.
currentUrl
.
includes
(
'/meeting-booking'
);
this
.
isMeetingBookingRoute
=
this
.
currentUrl
.
includes
(
'/meeting-booking'
);
this
.
isMeetingRoomRegistryRoute
=
this
.
currentUrl
.
includes
(
'/meeting-rooms'
);
this
.
isWidgetWarehouseRoute
=
this
.
currentUrl
.
includes
(
'/widget-warehouse'
);
this
.
isWidgetWarehouseRoute
=
this
.
currentUrl
.
includes
(
'/widget-warehouse'
);
this
.
isWidgetLinkerRoute
=
this
.
currentUrl
.
includes
(
'/widget-linker'
);
this
.
isWidgetLinkerRoute
=
this
.
currentUrl
.
includes
(
'/widget-linker'
);
this
.
menuitemsSubscribe$
=
this
.
navServices
.
items
.
subscribe
((
items
)
=>
{
this
.
menuitemsSubscribe$
=
this
.
navServices
.
items
.
subscribe
((
items
)
=>
{
...
@@ -175,6 +177,7 @@ export class SidebarComponent {
...
@@ -175,6 +177,7 @@ export class SidebarComponent {
this
.
isPermissionManagementRoute
=
this
.
currentUrl
.
includes
(
'/permission-management'
);
this
.
isPermissionManagementRoute
=
this
.
currentUrl
.
includes
(
'/permission-management'
);
this
.
isMenuPermissionManagementRoute
=
this
.
currentUrl
.
includes
(
'/menu-permission-management'
);
this
.
isMenuPermissionManagementRoute
=
this
.
currentUrl
.
includes
(
'/menu-permission-management'
);
this
.
isMeetingBookingRoute
=
this
.
currentUrl
.
includes
(
'/meeting-booking'
);
this
.
isMeetingBookingRoute
=
this
.
currentUrl
.
includes
(
'/meeting-booking'
);
this
.
isMeetingRoomRegistryRoute
=
this
.
currentUrl
.
includes
(
'/meeting-rooms'
);
this
.
isWidgetWarehouseRoute
=
this
.
currentUrl
.
includes
(
'/widget-warehouse'
);
this
.
isWidgetWarehouseRoute
=
this
.
currentUrl
.
includes
(
'/widget-warehouse'
);
this
.
isWidgetLinkerRoute
=
this
.
currentUrl
.
includes
(
'/widget-linker'
);
this
.
isWidgetLinkerRoute
=
this
.
currentUrl
.
includes
(
'/widget-linker'
);
this
.
checkUrlChanges
()
this
.
checkUrlChanges
()
...
@@ -237,6 +240,7 @@ export class SidebarComponent {
...
@@ -237,6 +240,7 @@ export class SidebarComponent {
this
.
isPermissionManagementRoute
=
this
.
currentUrl
.
includes
(
'/permission-management'
);
this
.
isPermissionManagementRoute
=
this
.
currentUrl
.
includes
(
'/permission-management'
);
this
.
isMenuPermissionManagementRoute
=
this
.
currentUrl
.
includes
(
'/menu-permission-management'
);
this
.
isMenuPermissionManagementRoute
=
this
.
currentUrl
.
includes
(
'/menu-permission-management'
);
this
.
isMeetingBookingRoute
=
this
.
currentUrl
.
includes
(
'/meeting-booking'
);
this
.
isMeetingBookingRoute
=
this
.
currentUrl
.
includes
(
'/meeting-booking'
);
this
.
isMeetingRoomRegistryRoute
=
this
.
currentUrl
.
includes
(
'/meeting-rooms'
);
this
.
isWidgetWarehouseRoute
=
this
.
currentUrl
.
includes
(
'/widget-warehouse'
);
this
.
isWidgetWarehouseRoute
=
this
.
currentUrl
.
includes
(
'/widget-warehouse'
);
this
.
isWidgetLinkerRoute
=
this
.
currentUrl
.
includes
(
'/widget-linker'
);
this
.
isWidgetLinkerRoute
=
this
.
currentUrl
.
includes
(
'/widget-linker'
);
...
@@ -292,7 +296,7 @@ export class SidebarComponent {
...
@@ -292,7 +296,7 @@ export class SidebarComponent {
this
.
menuItems
=
this
.
navServices
.
getSystemManagementMenu
();
this
.
menuItems
=
this
.
navServices
.
getSystemManagementMenu
();
}
else
if
(
this
.
isMenuPermissionManagementRoute
){
}
else
if
(
this
.
isMenuPermissionManagementRoute
){
this
.
menuItems
=
this
.
navServices
.
getSystemManagementMenu
();
this
.
menuItems
=
this
.
navServices
.
getSystemManagementMenu
();
}
else
if
(
this
.
isMeetingBookingRoute
){
}
else
if
(
this
.
isMeetingBookingRoute
||
this
.
isMeetingRoomRegistryRoute
){
this
.
menuItems
=
this
.
navServices
.
getMeetingBookingMenu
();
this
.
menuItems
=
this
.
navServices
.
getMeetingBookingMenu
();
}
else
if
(
this
.
isWidgetWarehouseRoute
||
this
.
isWidgetLinkerRoute
){
}
else
if
(
this
.
isWidgetWarehouseRoute
||
this
.
isWidgetLinkerRoute
){
// สำหรับ widget-warehouse และ widget-linker ใช้เมนูตามแอปที่อยู่ใน URL
// สำหรับ widget-warehouse และ widget-linker ใช้เมนูตามแอปที่อยู่ใน URL
...
...
src/app/shared/services/nav.service.ts
View file @
ca26828b
...
@@ -486,6 +486,7 @@ export class NavService implements OnDestroy {
...
@@ -486,6 +486,7 @@ export class NavService implements OnDestroy {
type
:
'sub'
,
type
:
'sub'
,
active
:
false
,
active
:
false
,
children
:
[
children
:
[
{
path
:
'/portal-manage/meeting-rooms'
,
title
:
'ทะเบียนห้องประชุม'
,
type
:
'link'
},
{
path
:
'/portal-manage/meeting-booking'
,
title
:
'จองห้องประชุม'
,
type
:
'link'
},
{
path
:
'/portal-manage/meeting-booking'
,
title
:
'จองห้องประชุม'
,
type
:
'link'
},
],
],
},
},
...
...
src/main.ts
View file @
ca26828b
...
@@ -3,9 +3,86 @@
...
@@ -3,9 +3,86 @@
import
{
bootstrapApplication
}
from
'@angular/platform-browser'
;
import
{
bootstrapApplication
}
from
'@angular/platform-browser'
;
import
{
appConfig
}
from
'./app/app.config'
;
import
{
appConfig
}
from
'./app/app.config'
;
import
{
AppComponent
}
from
'./app/app.component'
;
import
{
AppComponent
}
from
'./app/app.component'
;
import
{
registerLicense
}
from
'@syncfusion/ej2-base'
;
import
{
registerLicense
,
L10n
,
setCulture
}
from
'@syncfusion/ej2-base'
;
registerLicense
(
'ORg4AjUWIQA/Gnt2XFhhQlJHfV5AQmBIYVp/TGpJfl96cVxMZVVBJAtUQF1hTH5WdUVjWXtXdHNdRWFbWkdx'
);
registerLicense
(
'ORg4AjUWIQA/Gnt2XFhhQlJHfV5AQmBIYVp/TGpJfl96cVxMZVVBJAtUQF1hTH5WdUVjWXtXdHNdRWFbWkdx'
);
// Set Thai locale for Syncfusion components
L10n
.
load
({
'th'
:
{
'schedule'
:
{
'dayNames'
:
[
'อาทิตย์'
,
'จันทร์'
,
'อังคาร'
,
'พุธ'
,
'พฤหัสบดี'
,
'ศุกร์'
,
'เสาร์'
],
'dayNamesShort'
:
[
'อา'
,
'จ'
,
'อ'
,
'พ'
,
'พฤ'
,
'ศ'
,
'ส'
],
'dayNamesMin'
:
[
'อา'
,
'จ'
,
'อ'
,
'พ'
,
'พฤ'
,
'ศ'
,
'ส'
],
'monthNames'
:
[
'มกราคม'
,
'กุมภาพันธ์'
,
'มีนาคม'
,
'เมษายน'
,
'พฤษภาคม'
,
'มิถุนายน'
,
'กรกฎาคม'
,
'สิงหาคม'
,
'กันยายน'
,
'ตุลาคม'
,
'พฤศจิกายน'
,
'ธันวาคม'
],
'monthNamesShort'
:
[
'ม.ค.'
,
'ก.พ.'
,
'มี.ค.'
,
'เม.ย.'
,
'พ.ค.'
,
'มิ.ย.'
,
'ก.ค.'
,
'ส.ค.'
,
'ก.ย.'
,
'ต.ค.'
,
'พ.ย.'
,
'ธ.ค.'
],
'day'
:
'วัน'
,
'week'
:
'สัปดาห์'
,
'workWeek'
:
'สัปดาห์ทำงาน'
,
'month'
:
'เดือน'
,
'agenda'
:
'รายการ'
,
'today'
:
'วันนี้'
,
'noEvents'
:
'ไม่มีกิจกรรม'
,
'emptyContainer'
:
'ไม่มีกิจกรรมที่กำหนดไว้ในวันที่นี้'
,
'allDay'
:
'ทั้งวัน'
,
'start'
:
'เริ่มต้น'
,
'end'
:
'สิ้นสุด'
,
'more'
:
'เพิ่มเติม'
,
'close'
:
'ปิด'
,
'cancel'
:
'ยกเลิก'
,
'noTitle'
:
'(ไม่มีชื่อ)'
,
'delete'
:
'ลบ'
,
'deleteEvent'
:
'ลบกิจกรรม'
,
'selected'
:
'เลือกแล้ว'
,
'occurrence'
:
'การเกิดขึ้น'
,
'series'
:
'ชุด'
,
'previous'
:
'ก่อนหน้า'
,
'next'
:
'ถัดไป'
,
'edit'
:
'แก้ไข'
,
'editEvent'
:
'แก้ไขกิจกรรม'
,
'create'
:
'สร้าง'
,
'newEvent'
:
'กิจกรรมใหม่'
,
'save'
:
'บันทึก'
,
'subject'
:
'หัวข้อ'
,
'title'
:
'ชื่อ'
,
'startTime'
:
'เวลาเริ่มต้น'
,
'endTime'
:
'เวลาสิ้นสุด'
,
'repeat'
:
'ทำซ้ำ'
,
'location'
:
'สถานที่'
,
'description'
:
'รายละเอียด'
,
'timezone'
:
'เขตเวลา'
,
'none'
:
'ไม่มี'
,
'daily'
:
'รายวัน'
,
'weekly'
:
'รายสัปดาห์'
,
'monthly'
:
'รายเดือน'
,
'yearly'
:
'รายปี'
,
'never'
:
'ไม่เคย'
,
'until'
:
'จนถึง'
,
'count'
:
'จำนวน'
,
'first'
:
'แรก'
,
'second'
:
'สอง'
,
'third'
:
'สาม'
,
'fourth'
:
'สี่'
,
'last'
:
'สุดท้าย'
}
}
});
// Set culture to Thai
setCulture
(
'th'
);
// Additional locale setup for Schedule
L10n
.
load
({
'th-TH'
:
{
'schedule'
:
{
'dayNames'
:
[
'อาทิตย์'
,
'จันทร์'
,
'อังคาร'
,
'พุธ'
,
'พฤหัสบดี'
,
'ศุกร์'
,
'เสาร์'
],
'dayNamesShort'
:
[
'อา'
,
'จ'
,
'อ'
,
'พ'
,
'พฤ'
,
'ศ'
,
'ส'
],
'dayNamesMin'
:
[
'อา'
,
'จ'
,
'อ'
,
'พ'
,
'พฤ'
,
'ศ'
,
'ส'
],
'monthNames'
:
[
'มกราคม'
,
'กุมภาพันธ์'
,
'มีนาคม'
,
'เมษายน'
,
'พฤษภาคม'
,
'มิถุนายน'
,
'กรกฎาคม'
,
'สิงหาคม'
,
'กันยายน'
,
'ตุลาคม'
,
'พฤศจิกายน'
,
'ธันวาคม'
],
'monthNamesShort'
:
[
'ม.ค.'
,
'ก.พ.'
,
'มี.ค.'
,
'เม.ย.'
,
'พ.ค.'
,
'มิ.ย.'
,
'ก.ค.'
,
'ส.ค.'
,
'ก.ย.'
,
'ต.ค.'
,
'พ.ย.'
,
'ธ.ค.'
]
}
}
});
bootstrapApplication
(
AppComponent
,
appConfig
)
bootstrapApplication
(
AppComponent
,
appConfig
)
.
catch
((
err
)
=>
console
.
error
(
err
));
.
catch
((
err
)
=>
console
.
error
(
err
));
src/styles.scss
View file @
ca26828b
...
@@ -49,6 +49,7 @@
...
@@ -49,6 +49,7 @@
@import
"../node_modules/@syncfusion/ej2-splitbuttons/styles/tailwind.css"
;
@import
"../node_modules/@syncfusion/ej2-splitbuttons/styles/tailwind.css"
;
@import
'../node_modules/@syncfusion/ej2-angular-pivotview/styles/tailwind.css'
;
@import
'../node_modules/@syncfusion/ej2-angular-pivotview/styles/tailwind.css'
;
@import
"../node_modules/@syncfusion/ej2-angular-layouts/styles/tailwind.css"
;
@import
"../node_modules/@syncfusion/ej2-angular-layouts/styles/tailwind.css"
;
@import
'../node_modules/@syncfusion/ej2-angular-schedule/styles/tailwind.css'
;
// @import "../node_modules/angular-calendar/scss/angular-calendar.scss";
// @import "../node_modules/angular-calendar/scss/angular-calendar.scss";
//swiperjs
//swiperjs
...
@@ -109,3 +110,204 @@
...
@@ -109,3 +110,204 @@
.e-grid
td
.e-selectionbackground
{
.e-grid
td
.e-selectionbackground
{
background-color
:
#aec2ec
!
important
;
background-color
:
#aec2ec
!
important
;
}
}
// ===== Syncfusion Schedule Custom Styles =====
.custom-schedule
{
.e-schedule
{
border
:
1px
solid
#e2e8f0
;
border-radius
:
8px
;
box-shadow
:
0
1px
3px
0
rgba
(
0
,
0
,
0
,
0
.1
);
}
.e-header-cells
{
background-color
:
#f8fafc
;
color
:
#475569
;
font-weight
:
600
;
border-bottom
:
2px
solid
#e2e8f0
;
}
.e-date-header
{
background-color
:
#f1f5f9
;
color
:
#334155
;
font-weight
:
500
;
}
.e-work-cells
{
border-right
:
1px
solid
#e2e8f0
;
border-bottom
:
1px
solid
#e2e8f0
;
}
.e-appointment
{
border-radius
:
4px
;
border
:
none
;
box-shadow
:
0
1px
2px
0
rgba
(
0
,
0
,
0
,
0
.1
);
}
.e-appointment
.e-subject
{
font-weight
:
500
;
color
:
#1e293b
;
}
.e-appointment
.e-location
{
font-size
:
0
.75rem
;
color
:
#64748b
;
}
.e-appointment
.e-time
{
font-size
:
0
.75rem
;
color
:
#64748b
;
}
// Status colors
.e-appointment.status-confirmed
{
background-color
:
#dcfce7
;
border-left
:
4px
solid
#22c55e
;
}
.e-appointment.status-pending
{
background-color
:
#fef3c7
;
border-left
:
4px
solid
#f59e0b
;
}
.e-appointment.status-cancelled
{
background-color
:
#fee2e2
;
border-left
:
4px
solid
#ef4444
;
}
.e-appointment.status-completed
{
background-color
:
#e0f2fe
;
border-left
:
4px
solid
#0ea5e9
;
}
// Month view specific styles
.e-month-view
.e-date-header
{
padding
:
8px
4px
;
text-align
:
center
;
}
.e-month-view
.e-work-cells
{
height
:
100px
;
vertical-align
:
top
;
}
.e-month-view
.e-appointment
{
margin
:
1px
2px
;
padding
:
2px
4px
;
font-size
:
0
.75rem
;
}
// Week view specific styles
.e-week-view
.e-work-cells
{
height
:
60px
;
}
.e-week-view
.e-appointment
{
margin
:
1px
;
padding
:
4px
6px
;
}
// Day view specific styles
.e-day-view
.e-work-cells
{
height
:
40px
;
}
.e-day-view
.e-appointment
{
margin
:
1px
;
padding
:
6px
8px
;
}
// Agenda view specific styles
.e-agenda-view
.e-appointment
{
border
:
1px
solid
#e2e8f0
;
border-radius
:
6px
;
margin-bottom
:
8px
;
padding
:
12px
;
}
.e-agenda-view
.e-subject
{
font-size
:
1rem
;
font-weight
:
600
;
color
:
#1e293b
;
}
.e-agenda-view
.e-location
{
font-size
:
0
.875rem
;
color
:
#64748b
;
margin-top
:
4px
;
}
.e-agenda-view
.e-time
{
font-size
:
0
.875rem
;
color
:
#64748b
;
margin-top
:
2px
;
}
}
// View switcher styles
.view-switcher
{
margin-bottom
:
16px
;
display
:
flex
;
justify-content
:
center
;
.mat-button-toggle-group
{
border-radius
:
8px
;
overflow
:
hidden
;
box-shadow
:
0
1px
3px
0
rgba
(
0
,
0
,
0
,
0
.1
);
}
.mat-button-toggle
{
border
:
none
;
background-color
:
#f8fafc
;
color
:
#64748b
;
font-weight
:
500
;
min-width
:
100px
;
&
.mat-button-toggle-checked
{
background-color
:
#3b82f6
;
color
:
white
;
}
&
:hover:not
(
.mat-button-toggle-checked
)
{
background-color
:
#e2e8f0
;
}
.mat-icon
{
margin-right
:
4px
;
font-size
:
18px
;
}
}
}
// Month controls styles
.month-controls
{
margin-bottom
:
16px
;
display
:
flex
;
justify-content
:
center
;
.month-toolbar
{
display
:
flex
;
align-items
:
center
;
gap
:
16px
;
background-color
:
#f8fafc
;
padding
:
8px
16px
;
border-radius
:
8px
;
box-shadow
:
0
1px
3px
0
rgba
(
0
,
0
,
0
,
0
.1
);
.month-title
{
font-size
:
1
.25rem
;
font-weight
:
600
;
color
:
#1e293b
;
min-width
:
200px
;
text-align
:
center
;
}
button
{
color
:
#64748b
;
&
:hover
{
color
:
#3b82f6
;
background-color
:
#e2e8f0
;
}
}
}
}
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