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
23c40532
Commit
23c40532
authored
Oct 07, 2025
by
sawit
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
css จองห้องประชุม
parent
87b84b2e
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
381 additions
and
193 deletions
+381
-193
meeting-booking.component.html
...tal-manage/meeting-booking/meeting-booking.component.html
+375
-187
meeting-booking.component.ts
...ortal-manage/meeting-booking/meeting-booking.component.ts
+5
-5
meeting-room.component.html
.../meeting-booking/meeting-room/meeting-room.component.html
+1
-1
No files found.
src/app/portal-manage/meeting-booking/meeting-booking.component.html
View file @
23c40532
<!-- Meeting Room Booking System with Syncfusion Schedule -->
<!-- Meeting Room Booking System with Syncfusion Schedule -->
<div
class=
"m
eeting-booking-container
"
>
<div
class=
"m
in-h-screen bg-gradient-to-b from-gray-50 via-white to-gray-50
"
>
<!-- Header Section -->
<!-- Header Section -->
<div
class=
"page-header"
>
<div
class=
"bg-gradient-to-r from-indigo-600 via-violet-600 to-fuchsia-600"
>
<div
class=
"header-content"
>
<div
class=
"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8"
>
<div
class=
"header-title"
>
<div
class=
"flex flex-col lg:flex-row lg:items-center lg:justify-between"
>
<h1><i
class=
"fas fa-calendar-alt"
></i>
ระบบจองห้องประชุม
</h1>
<div
class=
"flex-1 min-w-0"
>
<p>
จัดการการจองห้องประชุมอย่างมีประสิทธิภาพด้วย Syncfusion Schedule
</p>
<div
class=
"flex items-center space-x-3"
>
</div>
<div
class=
"flex-shrink-0"
>
<div
class=
"header-actions"
>
<div
class=
"w-10 h-10 bg-white/20 rounded-lg flex items-center justify-center ring-1 ring-white/30"
>
<button
mat-raised-button
color=
"primary"
(
click
)="
showBookingForm =
true"
>
<i
class=
"ri-calendar-event-line text-white text-xl drop-shadow"
></i>
<mat-icon>
add
</mat-icon>
</div>
จองห้องประชุม
</div>
</button>
<div>
<button
mat-raised-button
color=
"accent"
(
click
)="
loadSampleData
()"
>
<h1
class=
"text-2xl font-bold text-white"
>
ระบบจองห้องประชุม
</h1>
<mat-icon>
refresh
</mat-icon>
<p
class=
"text-sm text-white/90 mt-1"
>
จัดการการจองห้องประชุมอย่างมีประสิทธิภาพด้วย Syncfusion Schedule
</p>
โหลดข้อมูลตัวอย่าง
</div>
</button>
</div>
</div>
<div
class=
"mt-4 lg:mt-0 lg:ml-4"
>
<div
class=
"flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-3"
>
<button
class=
"inline-flex items-center px-4 py-2 border border-white/30 text-sm font-medium rounded-lg text-white bg-white/10 hover:bg-white/20 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-white/40 backdrop-blur transition-colors duration-200"
(
click
)="
showBookingForm =
true"
>
<i
class=
"ri-add-line mr-2"
></i>
จองห้องประชุม
</button>
<!-- <button
class="inline-flex items-center px-4 py-2 border border-white/30 text-sm font-medium rounded-lg text-white bg-white/10 hover:bg-white/20 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-white/40 transition-colors duration-200"
(click)="loadSampleData()">
<i class="ri-refresh-line mr-2"></i>
โหลดข้อมูลตัวอย่าง
</button> -->
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Main Content -->
<!-- Main Content -->
<div
class=
"ma
in-content
"
>
<div
class=
"ma
x-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8
"
>
<!-- Booking Form Modal -->
<!-- Booking Form Modal -->
<div
class=
"booking-form-overlay"
*
ngIf=
"showBookingForm"
(
click
)="
showBookingForm =
false"
>
<div
class=
"fixed inset-0 z-50"
*
ngIf=
"showBookingForm"
(
click
)="
showBookingForm =
false"
>
<div
class=
"booking-form-container"
(
click
)="$
event
.
stopPropagation
()"
>
<div
class=
"flex items-end sm:items-center justify-center min-h-screen px-4 text-center sm:block sm:p-0"
>
<div
class=
"form-header"
>
<!-- Background overlay -->
<h2><mat-icon>
event_available
</mat-icon>
จองห้องประชุม
</h2>
<div
class=
"fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
aria-hidden=
"true"
></div>
<button
mat-icon-button
(
click
)="
showBookingForm =
false"
>
<mat-icon>
close
</mat-icon>
</button>
</div>
<form
[
formGroup
]="
bookingForm
"
(
ngSubmit
)="
createBooking
()"
class=
"booking-form"
>
<div
class=
"form-row"
>
<mat-form-field
appearance=
"outline"
class=
"form-field"
>
<mat-label>
ห้องประชุม
</mat-label>
<mat-select
formControlName=
"roomId"
(
selectionChange
)="
onRoomChange
()"
>
<mat-option
*
ngFor=
"let room of rooms$ | async"
[
value
]="
room
.
id
"
>
{{ room.name }} ({{ room.capacity }} ที่นั่ง) - {{ room.location }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field
appearance=
"outline"
class=
"form-field"
>
<!-- Modal panel -->
<mat-label>
หัวข้อการประชุม
</mat-label>
<div
class=
"inline-block align-bottom sm:align-middle bg-white w-full sm:max-w-4xl sm:my-8 text-left overflow-hidden shadow-xl transform transition-all rounded-none sm:rounded-lg max-h-screen"
(
click
)="$
event
.
stopPropagation
()"
>
<input
matInput
formControlName=
"title"
placeholder=
"ระบุหัวข้อการประชุม"
>
<!-- Modal header -->
</mat-form-field>
<div
class=
"bg-white px-6 py-4 border-b border-gray-200 sticky top-0 z-10"
>
<div
class=
"flex items-center justify-between"
>
<div
class=
"flex items-center space-x-3"
>
<div
class=
"w-8 h-8 bg-blue-600 rounded-lg flex items-center justify-center"
>
<i
class=
"ri-calendar-event-line text-white text-lg"
></i>
</div>
<h3
class=
"text-lg font-semibold text-gray-900"
>
จองห้องประชุม
</h3>
</div>
<button
class=
"text-gray-400 hover:text-gray-600 transition-colors duration-200"
(
click
)="
showBookingForm =
false"
>
<i
class=
"ri-close-line text-xl"
></i>
</button>
</div>
</div>
</div>
<!-- Room Preview -->
<!-- Modal body -->
<div
class=
"form-row"
*
ngIf=
"getSelectedRoomImage() as img"
>
<form
[
formGroup
]="
bookingForm
"
(
ngSubmit
)="
createBooking
()"
class=
"bg-white px-6 py-6 overflow-y-auto"
style=
"max-height: calc(100vh - 8rem);"
>
<img
[
src
]="
img
"
alt=
"room"
style=
"width:100%;max-height:180px;object-fit:cover;border-radius:8px;border:1px solid #e9ecef;"
/>
<div
class=
"space-y-6"
>
</div>
<!-- Room Selection -->
<div
class=
"space-y-2"
>
<label
class=
"block text-sm font-medium text-gray-700"
>
ห้องประชุม
</label>
<select
class=
"w-full px-3 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-colors duration-200"
formControlName=
"roomId"
(
change
)="
onRoomChange
()"
>
<option
value=
""
>
เลือกห้องประชุม
</option>
<option
*
ngFor=
"let room of rooms$ | async"
[
value
]="
room
.
id
"
>
{{ room.name }} ({{ room.capacity }} ที่นั่ง) - {{ room.location }}
</option>
</select>
</div>
<div
class=
"form-row"
>
<!-- Meeting Title -->
<mat-form-field
appearance=
"outline"
class=
"form-field"
>
<div
class=
"space-y-2"
>
<mat-label>
วันที่เริ่มต้น
</mat-label>
<label
class=
"block text-sm font-medium text-gray-700"
>
หัวข้อการประชุม
</label>
<input
matInput
[
matDatepicker
]="
startPicker
"
formControlName=
"startDateTime"
>
<input
<mat-datepicker-toggle
matSuffix
[
for
]="
startPicker
"
></mat-datepicker-toggle>
type=
"text"
<mat-datepicker
#
startPicker
></mat-datepicker>
class=
"w-full px-3 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-colors duration-200"
</mat-form-field>
formControlName=
"title"
placeholder=
"ระบุหัวข้อการประชุม"
>
</div>
<mat-form-field
appearance=
"outline"
class=
"form-field"
>
<!-- Room Preview -->
<mat-label>
วันที่สิ้นสุด
</mat-label>
<div
class=
"space-y-2"
*
ngIf=
"getSelectedRoomImage() as img"
>
<input
matInput
[
matDatepicker
]="
endPicker
"
formControlName=
"endDateTime"
>
<label
class=
"block text-sm font-medium text-gray-700"
>
ภาพห้องประชุม
</label>
<mat-datepicker-toggle
matSuffix
[
for
]="
endPicker
"
></mat-datepicker-toggle>
<div
class=
"relative"
>
<mat-datepicker
#
endPicker
></mat-datepicker>
<img
</mat-form-field>
[
src
]="
img
"
</div>
alt=
"room"
class=
"w-full h-48 object-cover rounded-lg border border-gray-200 shadow-sm"
>
<div
class=
"absolute inset-0 bg-black bg-opacity-0 hover:bg-opacity-10 transition-all duration-200 rounded-lg"
></div>
</div>
</div>
<div
class=
"form-row"
>
<!-- Date and Time Section -->
<mat-form-field
appearance=
"outline"
class=
"form-field"
>
<div
class=
"grid grid-cols-1 md:grid-cols-2 gap-6"
>
<mat-label>
เวลาเริ่มต้น
</mat-label>
<!-- Start Date -->
<input
matInput
type=
"time"
formControlName=
"startTime"
>
<div
class=
"space-y-2"
>
</mat-form-field>
<label
class=
"block text-sm font-medium text-gray-700"
>
วันที่เริ่มต้น
</label>
<div
class=
"relative"
>
<input
[
matDatepicker
]="
startPicker
"
formControlName=
"startDateTime"
class=
"w-full px-3 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-colors duration-200"
>
<mat-datepicker-toggle
matSuffix
[
for
]="
startPicker
"
class=
"absolute right-2 top-1/2 transform -translate-y-1/2"
></mat-datepicker-toggle>
<mat-datepicker
#
startPicker
></mat-datepicker>
</div>
</div>
<mat-form-field
appearance=
"outline"
class=
"form-field"
>
<!-- End Date -->
<mat-label>
เวลาสิ้นสุด
</mat-label>
<div
class=
"space-y-2"
>
<input
matInput
type=
"time"
formControlName=
"endTime"
>
<label
class=
"block text-sm font-medium text-gray-700"
>
วันที่สิ้นสุด
</label>
</mat-form-field>
<div
class=
"relative"
>
</div>
<input
[
matDatepicker
]="
endPicker
"
formControlName=
"endDateTime"
class=
"w-full px-3 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-colors duration-200"
>
<mat-datepicker-toggle
matSuffix
[
for
]="
endPicker
"
class=
"absolute right-2 top-1/2 transform -translate-y-1/2"
></mat-datepicker-toggle>
<mat-datepicker
#
endPicker
></mat-datepicker>
</div>
</div>
<!-- Attendees section -->
<!-- Start Time -->
<div
class=
"form-row"
>
<div
class=
"space-y-2"
>
<mat-form-field
appearance=
"outline"
class=
"form-field"
>
<label
class=
"block text-sm font-medium text-gray-700"
>
เวลาเริ่มต้น
</label>
<mat-label>
จำนวนผู้เข้าร่วม
</mat-label>
<input
<input
matInput
type=
"number"
min=
"0"
formControlName=
"attendeeCount"
[
value
]="
attendees
.
length
"
readonly
>
type=
"time"
</mat-form-field>
formControlName=
"startTime"
class=
"w-full px-3 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-colors duration-200"
>
</div>
<mat-form-field
appearance=
"outline"
class=
"form-field"
>
<!-- End Time -->
<mat-label>
เพิ่มผู้เข้าร่วม (พิมพ์อีเมลแล้วกด Enter)
</mat-label>
<div
class=
"space-y-2"
>
<mat-chip-grid
#
chipList
aria-label=
"Attendees"
>
<label
class=
"block text-sm font-medium text-gray-700"
>
เวลาสิ้นสุด
</label>
<mat-chip-row
*
ngFor=
"let a of attendees"
[
removable
]="
true
"
(
removed
)="
removeAttendee
(
a
)"
>
<input
{{ a }}
type=
"time"
<button
matChipRemove
aria-label=
"remove"
>
formControlName=
"endTime"
<mat-icon>
cancel
</mat-icon>
class=
"w-full px-3 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-colors duration-200"
>
</button>
</div>
</mat-chip-row>
</div>
<input
placeholder=
"เพิ่มอีเมลผู้เข้าร่วม"
[
matChipInputFor
]="
chipList
"
[
matChipInputSeparatorKeyCodes
]="
separatorKeysCodes
"
(
matChipInputTokenEnd
)="
addAttendee
($
event
)"
/>
</mat-chip-grid>
</mat-form-field>
</div>
<mat-form-field
appearance=
"outline"
class=
"form-field-full"
>
<!-- Attendees Section -->
<mat-label>
รายละเอียด
</mat-label>
<div
class=
"space-y-4"
>
<textarea
matInput
formControlName=
"description"
rows=
"3"
placeholder=
"รายละเอียดเพิ่มเติม (ไม่บังคับ)"
></textarea>
<div
class=
"grid grid-cols-1 md:grid-cols-2 gap-6"
>
</mat-form-field>
<!-- Attendee Count -->
<div
class=
"space-y-2"
>
<label
class=
"block text-sm font-medium text-gray-700"
>
จำนวนผู้เข้าร่วม
</label>
<input
type=
"number"
min=
"0"
formControlName=
"attendeeCount"
[
value
]="
attendees
.
length
"
class=
"w-full px-3 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-colors duration-200"
readonly
>
</div>
<div
class=
"form-actions"
>
<!-- Add Attendees -->
<button
mat-button
type=
"button"
(
click
)="
bookingForm
.
reset
()"
>
<div
class=
"space-y-2"
>
<mat-icon>
refresh
</mat-icon>
<label
class=
"block text-sm font-medium text-gray-700"
>
เพิ่มผู้เข้าร่วม
</label>
รีเซ็ต
<div
class=
"border border-gray-300 rounded-lg p-3 min-h-[42px]"
>
</button>
<mat-chip-grid
#
chipList
aria-label=
"Attendees"
class=
"w-full"
>
<button
mat-button
type=
"button"
(
click
)="
showBookingForm =
false"
>
<mat-chip-row
*
ngFor=
"let a of attendees"
[
removable
]="
true
"
(
removed
)="
removeAttendee
(
a
)"
<mat-icon>
close
</mat-icon>
class=
"bg-blue-100 text-blue-800 text-sm px-2 py-1 rounded-full mr-2 mb-2 inline-flex items-center"
>
ยกเลิก
{{ a }}
</button>
<button
matChipRemove
aria-label=
"remove"
class=
"ml-1 text-blue-600 hover:text-blue-800"
>
<button
mat-raised-button
color=
"primary"
type=
"submit"
[
disabled
]="!
bookingForm
.
valid
"
>
<i
class=
"ri-close-line text-sm"
></i>
<mat-icon>
event
</mat-icon>
</button>
จองห้องประชุม
</mat-chip-row>
</button>
<input
</div>
class=
"border-0 outline-none w-full text-sm"
</form>
placeholder=
"พิมพ์อีเมลแล้วกด Enter"
[
matChipInputFor
]="
chipList
"
[
matChipInputSeparatorKeyCodes
]="
separatorKeysCodes
"
(
matChipInputTokenEnd
)="
addAttendee
($
event
)"
/>
</mat-chip-grid>
</div>
</div>
</div>
</div>
<!-- Description -->
<div
class=
"space-y-2"
>
<label
class=
"block text-sm font-medium text-gray-700"
>
รายละเอียด
</label>
<textarea
formControlName=
"description"
rows=
"3"
placeholder=
"รายละเอียดเพิ่มเติม (ไม่บังคับ)"
class=
"w-full px-3 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-colors duration-200 resize-none"
></textarea>
</div>
</div>
<!-- Modal footer -->
<div
class=
"bg-gray-50 px-6 py-4 border-t border-gray-200 mt-6 -mx-6 -mb-6 sticky bottom-0"
>
<div
class=
"flex flex-col sm:flex-row sm:justify-end space-y-2 sm:space-y-0 sm:space-x-3"
>
<button
type=
"button"
class=
"w-full sm:w-auto inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-lg text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors duration-200"
(
click
)="
showBookingForm =
false"
>
<i
class=
"ri-close-line mr-2"
></i>
ยกเลิก
</button>
<button
type=
"button"
class=
"w-full sm:w-auto inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-lg text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors duration-200"
(
click
)="
bookingForm
.
reset
()"
>
<i
class=
"ri-refresh-line mr-2"
></i>
รีเซ็ต
</button>
<button
type=
"submit"
[
disabled
]="!
bookingForm
.
valid
"
class=
"w-full sm:w-auto inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-lg text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200"
>
<i
class=
"ri-calendar-event-line mr-2"
></i>
จองห้องประชุม
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- Statistics Cards -->
<!-- Statistics Cards -->
<div
class=
"statistics-section"
*
ngIf=
"scheduleData.length > 0"
>
<div
class=
"mb-8"
*
ngIf=
"scheduleData.length > 0; else emptyState"
>
<div
class=
"stats-title"
>
<div
class=
"mb-6"
>
<h3><mat-icon>
analytics
</mat-icon>
สถิติการจอง
</h3>
<h3
class=
"text-lg font-semibold text-gray-900 flex items-center"
>
<i
class=
"ri-bar-chart-line mr-2 text-blue-600"
></i>
สถิติการจอง
</h3>
</div>
</div>
<div
class=
"stats-cards"
>
<div
class=
"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6"
>
<div
class=
"stat-card"
>
<!-- Total Bookings -->
<div
class=
"stat-icon"
>
<div
class=
"relative rounded-2xl shadow-md p-6 hover:shadow-lg transition-all duration-200 text-white overflow-hidden"
style=
"background:linear-gradient(135deg,#3b82f6 0%,#4f46e5 100%);"
>
<mat-icon>
event
</mat-icon>
<div
class=
"absolute inset-0 opacity-0"
></div>
</div>
<div
class=
"relative z-10 flex items-center"
>
<div
class=
"stat-content"
>
<div
class=
"flex-shrink-0"
>
<div
class=
"stat-number"
>
{{ scheduleData.length }}
</div>
<div
class=
"w-12 h-12 bg-white/20 rounded-xl flex items-center justify-center ring-1 ring-white/30"
>
<div
class=
"stat-label"
>
การจองทั้งหมด
</div>
<i
class=
"ri-calendar-event-line text-white text-xl"
></i>
</div>
</div>
<div
class=
"ml-4"
>
<p
class=
"text-sm/5 font-medium opacity-90"
>
การจองทั้งหมด
</p>
<p
class=
"text-2xl font-extrabold"
>
{{ scheduleData.length }}
</p>
</div>
</div>
</div>
</div>
</div>
<div
class=
"stat-card"
>
<!-- Confirmed Bookings -->
<div
class=
"stat-icon"
>
<div
class=
"relative rounded-2xl shadow-md p-6 hover:shadow-lg transition-all duration-200 text-white overflow-hidden"
style=
"background:linear-gradient(135deg,#10b981 0%,#0d9488 100%);"
>
<mat-icon>
check_circle
</mat-icon>
<div
class=
"absolute inset-0 opacity-0"
></div>
</div>
<div
class=
"relative z-10 flex items-center"
>
<div
class=
"stat-content"
>
<div
class=
"flex-shrink-0"
>
<div
class=
"stat-number"
>
{{ getConfirmedCount() }}
</div>
<div
class=
"w-12 h-12 bg-white/20 rounded-xl flex items-center justify-center ring-1 ring-white/30"
>
<div
class=
"stat-label"
>
ยืนยันแล้ว
</div>
<i
class=
"ri-check-line text-white text-xl"
></i>
</div>
</div>
<div
class=
"ml-4"
>
<p
class=
"text-sm/5 font-medium opacity-90"
>
ยืนยันแล้ว
</p>
<p
class=
"text-2xl font-extrabold"
>
{{ getConfirmedCount() }}
</p>
</div>
</div>
</div>
</div>
</div>
<div
class=
"stat-card"
>
<!-- Pending Bookings -->
<div
class=
"stat-icon"
>
<div
class=
"relative rounded-2xl shadow-md p-6 hover:shadow-lg transition-all duration-200 text-white overflow-hidden"
style=
"background:linear-gradient(135deg,#f59e0b 0%,#f97316 100%);"
>
<mat-icon>
pending
</mat-icon>
<div
class=
"absolute inset-0 opacity-0"
></div>
</div>
<div
class=
"relative z-10 flex items-center"
>
<div
class=
"stat-content"
>
<div
class=
"flex-shrink-0"
>
<div
class=
"stat-number"
>
{{ getPendingCount() }}
</div>
<div
class=
"w-12 h-12 bg-white/20 rounded-xl flex items-center justify-center ring-1 ring-white/30"
>
<div
class=
"stat-label"
>
รอดำเนินการ
</div>
<i
class=
"ri-time-line text-white text-xl"
></i>
</div>
</div>
<div
class=
"ml-4"
>
<p
class=
"text-sm/5 font-medium opacity-90"
>
รอดำเนินการ
</p>
<p
class=
"text-2xl font-extrabold"
>
{{ getPendingCount() }}
</p>
</div>
</div>
</div>
</div>
</div>
<div
class=
"stat-card"
>
<!-- Active Rooms -->
<div
class=
"stat-icon"
>
<div
class=
"relative rounded-2xl shadow-md p-6 hover:shadow-lg transition-all duration-200 text-white overflow-hidden"
style=
"background:linear-gradient(135deg,#d946ef 0%,#9333ea 100%);"
>
<mat-icon>
room
</mat-icon>
<div
class=
"absolute inset-0 opacity-0"
></div>
</div>
<div
class=
"relative z-10 flex items-center"
>
<div
class=
"stat-content"
>
<div
class=
"flex-shrink-0"
>
<div
class=
"stat-number"
>
{{ getUniqueRoomsCount() }}
</div>
<div
class=
"w-12 h-12 bg-white/20 rounded-xl flex items-center justify-center ring-1 ring-white/30"
>
<div
class=
"stat-label"
>
ห้องที่ใช้งาน
</div>
<i
class=
"ri-building-line text-white text-xl"
></i>
</div>
</div>
<div
class=
"ml-4"
>
<p
class=
"text-sm/5 font-medium opacity-90"
>
ห้องที่ใช้งาน
</p>
<p
class=
"text-2xl font-extrabold"
>
{{ getUniqueRoomsCount() }}
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Empty State -->
<ng-template
#
emptyState
>
<div
class=
"bg-white/80 backdrop-blur-sm border border-dashed border-gray-300 rounded-2xl p-10 text-center text-gray-700"
>
<div
class=
"mx-auto w-16 h-16 rounded-2xl bg-gradient-to-br from-blue-50 to-blue-100 flex items-center justify-center ring-1 ring-blue-200 mb-4"
>
<i
class=
"ri-calendar-event-line text-blue-600 text-2xl"
></i>
</div>
<h4
class=
"text-lg font-semibold text-gray-900 mb-1"
>
ยังไม่มีการจอง
</h4>
<p
class=
"text-sm"
>
เริ่มต้นด้วยการสร้างการจองใหม่ โดยกดปุ่ม "จองห้องประชุม" ด้านบน
</p>
</div>
</ng-template>
<!-- Schedule View -->
<!-- Schedule View -->
<div
class=
"schedule-section"
>
<div
class=
"bg-white rounded-lg shadow-sm border border-gray-200"
>
<div
class=
"schedule-header"
>
<!-- Schedule Header -->
<div
class=
"schedule-header-top"
>
<div
class=
"px-6 py-4 border-b border-gray-200"
>
<div
class=
"schedule-title"
>
<div
class=
"flex flex-col lg:flex-row lg:items-center lg:justify-between"
>
<h2><mat-icon>
schedule
</mat-icon>
ปฏิทินการจองห้องประชุม
</h2>
<div
class=
"flex-1 min-w-0"
>
<div
class=
"flex items-center space-x-3"
>
<div
class=
"w-8 h-8 bg-blue-600 rounded-lg flex items-center justify-center"
>
<i
class=
"ri-calendar-line text-white text-lg"
></i>
</div>
<div>
<h2
class=
"text-xl font-semibold text-gray-900"
>
ปฏิทินการจองห้องประชุม
</h2>
<p
class=
"text-sm text-gray-500 mt-1"
>
ดูและจัดการการจองในรูปแบบปฏิทินแบบเต็มรูปแบบ
</p>
</div>
</div>
</div>
</div>
<div
class=
"schedule-controls"
>
<div
class=
"mt-4 lg:mt-0 lg:ml-4"
>
<mat-form-field
appearance=
"outline"
class=
"control-field"
>
<div
class=
"flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-3"
>
<mat-label>
กรองตามห้องประชุม
</mat-label>
<div
class=
"relative"
>
<mat-select
[(
value
)]="
selectedRoom
"
(
selectionChange
)="
onRoomChange
()"
>
<select
<mat-option
value=
""
>
ทั้งหมด
</mat-option>
[(
ngModel
)]="
selectedRoom
"
<mat-option
*
ngFor=
"let room of rooms$ | async"
[
value
]="
room
.
id
"
>
(
change
)="
onRoomChange
()"
{{ room.name }}
class=
"appearance-none bg-white border border-gray-300 rounded-lg px-3 py-2 pr-8 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
</mat-option>
<option
value=
""
>
ทั้งหมด
</option>
</mat-select>
<option
*
ngFor=
"let room of rooms$ | async"
[
value
]="
room
.
id
"
>
</mat-form-field>
{{ room.name }}
</option>
</select>
<div
class=
"absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none"
>
<i
class=
"ri-arrow-down-s-line text-gray-400"
></i>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<p
class=
"schedule-subtitle"
>
ดูและจัดการการจองในรูปแบบปฏิทินแบบเต็มรูปแบบ
</p>
</div>
</div>
<!-- Schedule Toolbar -->
<!-- <div class="px-6 py-4 flex flex-col md:flex-row md:items-center md:justify-between gap-3 bg-white/70 backdrop-blur-sm border-b border-gray-200">
<div class="flex items-center gap-2">
<button (click)="prevMonth()" class="inline-flex items-center px-3 py-2 rounded-lg border border-gray-300 text-sm text-gray-700 bg-white hover:bg-gray-50">
<i class="ri-arrow-left-s-line mr-1"></i>ก่อนหน้า
</button>
<button (click)="today()" class="inline-flex items-center px-3 py-2 rounded-lg border border-gray-300 text-sm text-gray-700 bg-white hover:bg-gray-50">
วันนี้
</button>
<button (click)="nextMonth()" class="inline-flex items-center px-3 py-2 rounded-lg border border-gray-300 text-sm text-gray-700 bg-white hover:bg-gray-50">
ถัดไป<i class="ri-arrow-right-s-line ml-1"></i>
</button>
</div>
<div class="inline-flex rounded-lg border border-gray-300 overflow-hidden">
<button type="button" (click)="currentView = 'Month'" [class.bg-blue-600]="currentView==='Month'" [class.text-white]="currentView==='Month'" class="px-3 py-2 text-sm hover:bg-gray-50">เดือน</button>
<button type="button" (click)="currentView = 'Week'" [class.bg-blue-600]="currentView==='Week'" [class.text-white]="currentView==='Week'" class="px-3 py-2 text-sm hover:bg-gray-50 border-l border-gray-300">สัปดาห์</button>
<button type="button" (click)="currentView = 'Day'" [class.bg-blue-600]="currentView==='Day'" [class.text-white]="currentView==='Day'" class="px-3 py-2 text-sm hover:bg-gray-50 border-l border-gray-300">วัน</button>
<button type="button" (click)="currentView = 'Agenda'" [class.bg-blue-600]="currentView==='Agenda'" [class.text-white]="currentView==='Agenda'" class="px-3 py-2 text-sm hover:bg-gray-50 border-l border-gray-300">กำหนดการ</button>
</div>
</div> -->
<!-- S
yncfusion S
chedule Component -->
<!-- Schedule Component -->
<div
class=
"
schedule-wrapper
"
>
<div
class=
"
p-6 pt-0 overflow-x-auto -mx-4 sm:mx-0
"
>
<ejs-schedule
<ejs-schedule
#
scheduleObj
#
scheduleObj
[
selectedDate
]="
selectedDate_schedule
"
[
selectedDate
]="
selectedDate_schedule
"
...
@@ -231,10 +405,10 @@
...
@@ -231,10 +405,10 @@
<!-- Event Template -->
<!-- Event Template -->
<ng-template
#
eventTemplate
let-data
>
<ng-template
#
eventTemplate
let-data
>
<div
class=
"event-template"
>
<div
class=
"event-template
bg-blue-50 border border-blue-200 rounded-lg p-2 text-xs
"
>
<div
class=
"
event-titl
e"
>
{{ data.Subject }}
</div>
<div
class=
"
font-semibold text-blue-900 truncat
e"
>
{{ data.Subject }}
</div>
<div
class=
"
event-location
"
>
{{ data.Location }}
</div>
<div
class=
"
text-blue-700 truncate
"
>
{{ data.Location }}
</div>
<div
class=
"
event-time
"
>
<div
class=
"
text-blue-600
"
>
{{ data.StartTime | date:'HH:mm' }} - {{ data.EndTime | date:'HH:mm' }}
{{ data.StartTime | date:'HH:mm' }} - {{ data.EndTime | date:'HH:mm' }}
</div>
</div>
</div>
</div>
...
@@ -242,49 +416,63 @@
...
@@ -242,49 +416,63 @@
<!-- Quick Info Template -->
<!-- Quick Info Template -->
<ng-template
#
quickInfoTemplate
let-data
>
<ng-template
#
quickInfoTemplate
let-data
>
<div
class=
"quick-info-template"
>
<div
class=
"bg-white rounded-lg shadow-lg border border-gray-200 p-4 max-w-sm"
>
<div
class=
"quick-info-header"
>
<div
class=
"flex items-center justify-between mb-3"
>
<h6>
{{ data.Subject }}
</h6>
<h6
class=
"text-lg font-semibold text-gray-900"
>
{{ data.Subject }}
</h6>
<span
class=
"status-badge status-{{ data.Status }}"
>
<span
class=
"px-2 py-1 text-xs font-medium rounded-full"
[
class
]="'
bg-
'
+
getStatusColor
(
data
.
Status
)
+
'
-100
text-
'
+
getStatusColor
(
data
.
Status
)
+
'
-800
'"
>
{{ getStatusText(data.Status) }}
{{ getStatusText(data.Status) }}
</span>
</span>
</div>
</div>
<div
class=
"quick-info-content"
>
<div
class=
"space-y-3"
>
<div
class=
"info-item"
*
ngIf=
"getRoomImageByName(data.Location)"
>
<div
class=
"info-item"
*
ngIf=
"getRoomImageByName(data.Location)"
>
<img
[
src
]="
getRoomImageByName
(
data
.
Location
)"
alt=
"room"
style=
"width:100%;max-height:140px;object-fit:cover;border-radius:8px;border:1px solid #e9ecef;"
/>
<img
[
src
]="
getRoomImageByName
(
data
.
Location
)"
alt=
"room"
class=
"w-full h-32 object-cover rounded-lg border border-gray-200"
/>
</div>
</div>
<div
class=
"info-item"
>
<mat-icon>
location_on
</mat-icon>
<div
class=
"flex items-center space-x-2 text-sm text-gray-600"
>
<i
class=
"ri-map-pin-line text-blue-500"
></i>
<span>
{{ data.Location }}
</span>
<span>
{{ data.Location }}
</span>
</div>
</div>
<div
class=
"info-item"
>
<mat-icon>
schedule
</mat-icon>
<div
class=
"flex items-center space-x-2 text-sm text-gray-600"
>
<i
class=
"ri-time-line text-blue-500"
></i>
<span>
{{ data.StartTime | date:'dd/MM/yyyy HH:mm' }} - {{ data.EndTime | date:'dd/MM/yyyy HH:mm' }}
</span>
<span>
{{ data.StartTime | date:'dd/MM/yyyy HH:mm' }} - {{ data.EndTime | date:'dd/MM/yyyy HH:mm' }}
</span>
</div>
</div>
<div
class=
"info-item"
*
ngIf=
"data.Description"
>
<mat-icon>
description
</mat-icon>
<div
class=
"flex items-center space-x-2 text-sm text-gray-600"
*
ngIf=
"data.Description"
>
<i
class=
"ri-file-text-line text-blue-500"
></i>
<span>
{{ data.Description }}
</span>
<span>
{{ data.Description }}
</span>
</div>
</div>
<div
class=
"info-item"
>
<mat-icon>
person
</mat-icon>
<div
class=
"flex items-center space-x-2 text-sm text-gray-600"
>
<i
class=
"ri-user-line text-blue-500"
></i>
<span>
{{ data.Organizer }}
</span>
<span>
{{ data.Organizer }}
</span>
</div>
</div>
<div
class=
"info-item"
*
ngIf=
"data.Attendees"
>
<mat-icon>
group
</mat-icon>
<div
class=
"flex items-center space-x-2 text-sm text-gray-600"
*
ngIf=
"data.Attendees"
>
<i
class=
"ri-group-line text-blue-500"
></i>
<span>
{{ data.Attendees }} ({{ data.AttendeesCount || (data.AttendeesList?.length || 0) }} คน)
</span>
<span>
{{ data.Attendees }} ({{ data.AttendeesCount || (data.AttendeesList?.length || 0) }} คน)
</span>
</div>
</div>
<div
class=
"info-item"
*
ngIf=
"data.AttendeesList?.length"
>
<mat-icon>
badge
</mat-icon>
<div
class=
"flex items-center space-x-2 text-sm text-gray-600"
*
ngIf=
"data.AttendeesList?.length"
>
<i
class=
"ri-user-settings-line text-blue-500"
></i>
<span>
{{ data.AttendeesList.join(', ') }}
</span>
<span>
{{ data.AttendeesList.join(', ') }}
</span>
</div>
</div>
</div>
</div>
<div
class=
"quick-info-actions"
>
<button
mat-button
color=
"primary"
(
click
)="
onEventClick
(
data
)"
>
<div
class=
"flex space-x-2 mt-4 pt-3 border-t border-gray-200"
>
<mat-icon>
visibility
</mat-icon>
<button
class=
"flex-1 inline-flex items-center justify-center px-3 py-2 border border-transparent text-sm font-medium rounded-lg text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors duration-200"
(
click
)="
onEventClick
(
data
)"
>
<i
class=
"ri-eye-line mr-1"
></i>
ดูรายละเอียด
ดูรายละเอียด
</button>
</button>
<button
mat-button
color=
"warn"
*
ngIf=
"data.Status === 'pending' || data.Status === 'confirmed'"
>
<button
<mat-icon>
cancel
</mat-icon>
class=
"flex-1 inline-flex items-center justify-center px-3 py-2 border border-red-300 text-sm font-medium rounded-lg text-red-700 bg-white hover:bg-red-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 transition-colors duration-200"
*
ngIf=
"data.Status === 'pending' || data.Status === 'confirmed'"
>
<i
class=
"ri-close-line mr-1"
></i>
ยกเลิก
ยกเลิก
</button>
</button>
</div>
</div>
...
...
src/app/portal-manage/meeting-booking/meeting-booking.component.ts
View file @
23c40532
...
@@ -270,11 +270,11 @@ export class MeetingBookingComponent implements OnInit {
...
@@ -270,11 +270,11 @@ export class MeetingBookingComponent implements OnInit {
getStatusColor
(
status
:
string
):
string
{
getStatusColor
(
status
:
string
):
string
{
switch
(
status
)
{
switch
(
status
)
{
case
'confirmed'
:
return
'
success
'
;
case
'confirmed'
:
return
'
green
'
;
case
'pending'
:
return
'
warning
'
;
case
'pending'
:
return
'
yellow
'
;
case
'cancelled'
:
return
'
danger
'
;
case
'cancelled'
:
return
'
red
'
;
case
'completed'
:
return
'
info
'
;
case
'completed'
:
return
'
blue
'
;
default
:
return
'
secondar
y'
;
default
:
return
'
gra
y'
;
}
}
}
}
...
...
src/app/portal-manage/meeting-booking/meeting-room/meeting-room.component.html
View file @
23c40532
...
@@ -95,7 +95,7 @@
...
@@ -95,7 +95,7 @@
</div>
</div>
<div
class=
"xl:col-span-6 col-span-12"
>
<div
class=
"xl:col-span-6 col-span-12"
>
<label
class=
"form-label"
>
ความจุ (คน)
</label>
<label
class=
"form-label"
>
ความจุ (คน)
</label>
<input
type=
"number"
class=
"form-control"
formControlName=
"capacity"
>
<input
type=
"number"
min=
"0"
class=
"form-control"
formControlName=
"capacity"
>
</div>
</div>
<div
class=
"xl:col-span-6 col-span-12"
>
<div
class=
"xl:col-span-6 col-span-12"
>
<label
class=
"form-label"
>
อาคาร
</label>
<label
class=
"form-label"
>
อาคาร
</label>
...
...
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