Commit c8dc0a75 by sawit

ตารางปฏิทิน

parent 1e4426e9
......@@ -151,32 +151,69 @@
<!-- Schedule View -->
<div class="schedule-section">
<div class="schedule-header">
<div class="schedule-title">
<h2><mat-icon>schedule</mat-icon> ปฏิทินการจองห้องประชุม</h2>
<p>ดูและจัดการการจองในรูปแบบปฏิทินแบบเต็มรูปแบบ</p>
<div class="schedule-header-top">
<div class="schedule-title">
<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>
<p class="schedule-subtitle">ดูและจัดการการจองในรูปแบบปฏิทินแบบเต็มรูปแบบ</p>
</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>
<!-- 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>
<mat-form-field appearance="outline" class="control-field">
<mat-label>มุมมอง</mat-label>
<mat-select [(value)]="currentView" (selectionChange)="onViewChange($event)">
<mat-option value="Day">รายวัน</mat-option>
<mat-option value="Week">รายสัปดาห์</mat-option>
<mat-option value="WorkWeek">วันทำงาน</mat-option>
<mat-option value="Month">รายเดือน</mat-option>
<mat-option value="Agenda">วาระการประชุม</mat-option>
</mat-select>
</mat-form-field>
<!-- 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>
</div>
</div>
</div>
</div>
</div>
</div>
......@@ -189,6 +226,7 @@
[eventSettings]="eventSettings"
[showQuickInfo]="showQuickInfo"
[allowDragAndDrop]="allowDragAndDrop"
[showHeaderBar]="false"
(eventClick)="onEventClick($event)"
(eventCreate)="onEventCreate($event)"
(eventUpdate)="onEventUpdate($event)"
......
......@@ -176,26 +176,35 @@
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
border-bottom: 1px solid #dee2e6;
.schedule-title {
margin-bottom: 1.5rem;
.schedule-header-top {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 0.5rem;
h2 {
margin: 0 0 0.5rem 0;
font-size: 1.8rem;
font-weight: 700;
color: #495057;
display: flex;
align-items: center;
gap: 0.5rem;
.schedule-title {
h2 {
margin: 0;
font-size: 1.8rem;
font-weight: 700;
color: #495057;
display: flex;
align-items: center;
gap: 0.5rem;
}
}
p {
margin: 0;
color: #6c757d;
font-size: 1rem;
.schedule-controls {
margin-left: auto;
}
}
.schedule-subtitle {
margin: 0;
color: #6c757d;
font-size: 1rem;
}
.schedule-controls {
display: flex;
gap: 1rem;
......@@ -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 {
margin-bottom: 2rem;
......
......@@ -100,6 +100,9 @@ export class MeetingBookingComponent implements OnInit {
// UI State properties
public showBookingForm: boolean = false;
// Monthly calendar grid state
public monthWeeks: Date[][] = [];
constructor(
private meetingBookingService: MeetingBookingService,
private fb: FormBuilder,
......@@ -128,6 +131,7 @@ export class MeetingBookingComponent implements OnInit {
this.loadTimeSlots();
this.loadScheduleData();
this.loadSampleData(); // เพิ่มข้อมูลตัวอย่าง
this.buildMonthWeeks(this.selectedDate_schedule);
}
onDateChange(): void {
......@@ -295,6 +299,7 @@ export class MeetingBookingComponent implements OnInit {
};
console.log('Schedule data loaded:', this.scheduleData);
this.buildMonthWeeks(this.selectedDate_schedule);
});
} else {
// ถ้าไม่มี bookings$ observable ให้ใช้ข้อมูลตัวอย่าง
......@@ -356,6 +361,7 @@ export class MeetingBookingComponent implements OnInit {
onDateChange_schedule(args: any): void {
console.log('Date changed:', args);
this.selectedDate_schedule = args.selectedDate;
this.buildMonthWeeks(this.selectedDate_schedule);
}
// ฟังก์ชันสำหรับโหลดข้อมูลตัวอย่าง
......@@ -414,6 +420,7 @@ export class MeetingBookingComponent implements OnInit {
};
console.log('Sample data loaded:', this.scheduleData);
this.buildMonthWeeks(this.selectedDate_schedule);
}
// Statistics methods
......@@ -429,4 +436,84 @@ export class MeetingBookingComponent implements OnInit {
const uniqueRooms = new Set(this.scheduleData.map(booking => booking.Location));
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()}`;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment