worker-calendar
Version:
A customizable, responsive Angular calendar component for scheduling worker appointments or shifts
269 lines (240 loc) • 10.8 kB
HTML
<div class="worker-calendar">
<!-- Calendar Header -->
<div class="calendar-header">
<div class="calendar-controls">
<button class="btn-control" (click)="onPrevious()">{{ language === 'fr' ? 'Précédent' : 'Previous' }}</button>
<button class="btn-control" (click)="onToday()">{{ language === 'fr' ? 'Aujourd\'hui' : 'Today' }}</button>
<button class="btn-control" (click)="onNext()">{{ language === 'fr' ? 'Suivant' : 'Next' }}</button>
</div>
<div class="calendar-title">
{{ displayDate }}
</div>
<div class="view-mode-selector">
<button
class="btn-view-mode"
[class.active]="viewMode === 'day'"
(click)="changeViewMode('day')">
{{ language === 'fr' ? 'Jour' : 'Day' }}
</button>
<button
class="btn-view-mode"
[class.active]="viewMode === 'week'"
(click)="changeViewMode('week')">
{{ language === 'fr' ? 'Semaine' : 'Week' }}
</button>
<button
class="btn-view-mode"
[class.active]="viewMode === 'month'"
(click)="changeViewMode('month')">
{{ language === 'fr' ? 'Mois' : 'Month' }}
</button>
</div>
</div>
<!-- Drag Preview Element -->
<div
class="drag-preview"
*ngIf="dragPreview.visible"
[ngStyle]="{
top: dragPreview.top,
left: dragPreview.left,
width: dragPreview.width,
height: dragPreview.height
}">
</div>
<!-- Day View -->
<div class="day-view" *ngIf="viewMode === 'day'" cdkDropListGroup>
<!-- Time Header -->
<div class="time-header">
<div class="worker-column-header">{{ language === 'fr' ? 'Employés' : 'Workers' }}</div>
<ng-container *ngFor="let hour of hours">
<div class="hour-column">{{ hour }}:00</div>
</ng-container>
</div>
<!-- Workers Rows -->
<div class="worker-row" *ngFor="let worker of workers">
<div class="worker-info">
<div class="worker-name">{{ worker.name }}</div>
<div class="worker-description" *ngIf="worker.description">{{ worker.description }}</div>
</div>
<ng-container *ngFor="let hour of hours">
<div
class="time-cell"
cdkDropList
[cdkDropListData]="{workerId: worker.id, hour: hour}"
(cdkDropListDropped)="handleDrop($event)"
(cdkDropListEntered)="handleDragEnter($event)"
(click)="handleDayCellClick(hour, worker.id)">
<!-- Show only events that start in this hour cell -->
<ng-container *ngFor="let event of getEventsForWorkerAndHour(worker.id, hour)">
<div
*ngIf="shouldRenderEvent(event, worker.id, hour)"
class="spanning-event"
[ngStyle]="getEventStyle(event, worker.id)"
cdkDrag
[cdkDragData]="event"
cdkDragBoundary=".day-view"
(cdkDragStarted)="handleDragStarted($event, event)"
(cdkDragEnded)="handleDragEnded($event)"
(click)="handleEventClick(event); $event.stopPropagation()">
<!-- Remove button -->
<div class="remove-button" (click)="handleEventRemove(event, $event)">×</div>
<!-- Left resize handle (hidden but still in DOM) -->
<div
class="resize-handle left"
(mousedown)="handleResizeStart($event, event, 'left')">
</div>
<div class="event-content">{{ event.title }}</div>
<!-- Right resize handle (visible) -->
<div
class="resize-handle right"
(mousedown)="handleResizeStart($event, event, 'right')">
</div>
<!-- Custom preview when dragging -->
<ng-template cdkDragPreview>
<div class="event-preview" [style.background-color]="event.color || '#4285f4'">
{{ event.title }}
</div>
</ng-template>
<!-- Custom placeholder when dragging -->
<div *cdkDragPlaceholder class="event-placeholder"></div>
</div>
</ng-container>
<!-- Drop preview element -->
<div
*ngIf="dropPreview.visible && dropPreview.workerId === worker.id && dropPreview.hour === hour"
class="drop-preview"
[ngStyle]="{
width: dropPreview.width,
height: dropPreview.height,
'background-color': dropPreview.color
}">
{{ dropPreview.title }}
</div>
</div>
</ng-container>
</div>
</div>
<!-- Week View -->
<div class="week-view" *ngIf="viewMode === 'week'" cdkDropListGroup>
<!-- Week Header -->
<div class="week-header">
<div class="time-column-header">{{ language === 'fr' ? 'Heure' : 'Time' }}</div>
<ng-container *ngFor="let day of weekDays">
<div
class="day-column-header"
[class.today]="day.isToday">
<span class="day-name">{{ day.dayName }}</span>
<span class="day-date">{{ day.dayNumber }}</span>
</div>
</ng-container>
</div>
<!-- Week Body -->
<div class="week-body">
<!-- Hours Column -->
<div class="hours-column">
<ng-container *ngFor="let hour of hours">
<div class="hour-cell">{{ hour }}:00</div>
</ng-container>
</div>
<!-- Day Columns -->
<ng-container *ngFor="let day of weekDays">
<div
class="day-column"
[class.today]="day.isToday">
<ng-container *ngFor="let hour of hours">
<div
class="hour-cell"
cdkDropList
[cdkDropListData]="{date: day.date, hour: hour}"
(cdkDropListDropped)="handleWeekDrop($event)"
(cdkDropListEntered)="handleWeekDragEnter($event)"
(click)="handleWeekCellClick(hour, day.date)">
<!-- Show only events that start in this hour cell -->
<ng-container *ngFor="let event of getEventsForHourAndDay(hour, day.date)">
<div
*ngIf="shouldRenderWeekEvent(event, day.date, hour)"
class="spanning-event"
[ngStyle]="getWeekEventStyle(event, day.date)"
cdkDrag
[cdkDragData]="event"
cdkDragBoundary=".week-view"
(cdkDragStarted)="handleDragStarted($event, event)"
(cdkDragEnded)="handleDragEnded($event)"
(click)="handleEventClick(event); $event.stopPropagation()">
<!-- Remove button -->
<div class="remove-button" (click)="handleEventRemove(event, $event)">×</div>
<!-- Left resize handle (hidden but still in DOM) -->
<div
class="resize-handle left"
(mousedown)="handleResizeStart($event, event, 'left')">
</div>
<div class="event-content">{{ event.title }}</div>
<!-- Right resize handle (visible) -->
<div
class="resize-handle right"
(mousedown)="handleResizeStart($event, event, 'right')">
</div>
<!-- Custom preview when dragging -->
<ng-template cdkDragPreview>
<div class="event-preview" [style.background-color]="event.color || '#4285f4'">
{{ event.title }}
</div>
</ng-template>
<!-- Custom placeholder when dragging -->
<div *cdkDragPlaceholder class="event-placeholder"></div>
</div>
</ng-container>
<!-- Drop preview element for week view -->
<div
*ngIf="dropPreview.visible && dropPreview.date && isSameDay(dropPreview.date, day.date) && dropPreview.hour === hour"
class="drop-preview"
[ngStyle]="{
width: dropPreview.width,
height: dropPreview.height,
'background-color': dropPreview.color
}">
{{ dropPreview.title }}
</div>
</div>
</ng-container>
</div>
</ng-container>
</div>
</div>
<!-- Month View -->
<div class="month-view" *ngIf="viewMode === 'month'">
<!-- Month Header -->
<div class="month-header">
<div class="day-name" *ngFor="let dayName of weekDayNames">
{{ dayName.slice(0, 3) }}
</div>
</div>
<!-- Month Grid -->
<div class="month-grid">
<div
class="month-day"
*ngFor="let day of monthDays"
[class.other-month]="!day.isCurrentMonth"
[class.today]="day.isToday"
(click)="handleMonthDayClick(day.date)">
<div class="date-number">{{ day.dayNumber }}</div>
<div class="month-events">
<ng-container *ngIf="getDisplayedEvents(day.date, 3) as displayData">
<div
class="month-event"
*ngFor="let event of displayData.events"
[style.background-color]="event.color || '#4285f4'"
(click)="handleEventClick(event); $event.stopPropagation()">
<span class="month-event-time">{{ formatMonthEventTime(event) }}</span>
<span class="month-event-title">{{ event.title }}</span>
<span class="month-event-remove" (click)="handleEventRemove(event, $event)">×</span>
</div>
<div class="more-events" *ngIf="displayData.overflow > 0">
{{ language === 'fr' ? '+' + displayData.overflow + ' autres' : '+' + displayData.overflow + ' more' }}
</div>
</ng-container>
</div>
</div>
</div>
</div>
</div>