From 211fe2cbb845a86b6801296663551e10d5d2506f Mon Sep 17 00:00:00 2001 From: veenm Date: Sun, 20 Apr 2025 15:23:59 +0200 Subject: [PATCH] Work in progress betreft het tonen van afspraken naast elkaar --- package-lock.json | 24 +++- .../appointment/appointment.component.html | 28 +++++ .../appointment/appointment.component.scss | 38 +++++++ .../appointment/appointment.component.ts | 46 ++++++++ .../datepicker/datepicker.component.html | 25 +++++ .../datepicker/datepicker.component.scss | 99 ++++++++++++++++ .../datepicker/datepicker.component.ts | 98 ++++++++++++++++ .../dropdown-content.component.html | 16 ++- .../dropdown-content.component.ts | 31 +++-- .../components/new-item/new-item.component.ts | 2 +- .../week-view/week-view.component.html | 34 ++++++ .../week-view/week-view.component.scss | 87 ++++++++++++++ .../week-view/week-view.component.ts | 106 ++++++++++++++++++ src/app/models/appointment-dto.ts | 17 ++- .../agenda-invite.component.html | 9 ++ .../agenda-invite.component.scss | 10 ++ .../agenda-invite/agenda-invite.component.ts | 7 +- src/app/pages/agenda/agenda.component.html | 51 +++++++-- src/app/pages/agenda/agenda.component.ts | 61 ++++++++-- src/app/services/appointment.service.ts | 14 ++- 20 files changed, 769 insertions(+), 34 deletions(-) create mode 100644 src/app/components/appointment/appointment.component.html create mode 100644 src/app/components/appointment/appointment.component.scss create mode 100644 src/app/components/appointment/appointment.component.ts create mode 100644 src/app/components/datepicker/datepicker.component.html create mode 100644 src/app/components/datepicker/datepicker.component.scss create mode 100644 src/app/components/datepicker/datepicker.component.ts create mode 100644 src/app/components/week-view/week-view.component.html create mode 100644 src/app/components/week-view/week-view.component.scss create mode 100644 src/app/components/week-view/week-view.component.ts diff --git a/package-lock.json b/package-lock.json index 699a5c2..01dff96 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pay-point", - "version": "0.0.21", + "version": "0.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pay-point", - "version": "0.0.21", + "version": "0.0.3", "dependencies": { "@angular/animations": "^19.0.0", "@angular/cdk": "^19.0.0", @@ -5974,6 +5974,20 @@ "@angular/core": ">=15.0.0" } }, + "node_modules/angularx-flatpickr": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/angularx-flatpickr/-/angularx-flatpickr-8.1.0.tgz", + "integrity": "sha512-U+WXMUXGEiQbdMGSPk7T+HehmAFdVWKu3XlCXFM8mYCCB/fWHW8sbHstxxZgOymD5Q1kfLaHNob1MxhWUgv1hg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/core": ">=17.0.0", + "@angular/forms": ">=17.0.0", + "flatpickr": "^4.5.0" + } + }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -8343,6 +8357,12 @@ "flat": "cli.js" } }, + "node_modules/flatpickr": { + "version": "4.6.13", + "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.13.tgz", + "integrity": "sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw==", + "license": "MIT" + }, "node_modules/flatted": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", diff --git a/src/app/components/appointment/appointment.component.html b/src/app/components/appointment/appointment.component.html new file mode 100644 index 0000000..815a808 --- /dev/null +++ b/src/app/components/appointment/appointment.component.html @@ -0,0 +1,28 @@ +
+ + {{ appointment.title }} + + + {{ appointment.customer.firstName }} {{ appointment.customer.lastName }} + + + {{ getFormattedTime(appointment.startHour, appointment.startMinute) }} + - {{ getFormattedTime(appointment.endHour, appointment.endMinute) }} + +
+
+ + {{ appointment.title }} +
diff --git a/src/app/components/appointment/appointment.component.scss b/src/app/components/appointment/appointment.component.scss new file mode 100644 index 0000000..37abd17 --- /dev/null +++ b/src/app/components/appointment/appointment.component.scss @@ -0,0 +1,38 @@ +.appointment { + position: absolute; + //width: calc(100% - 100px); + background: #1e88e5; + color: white; + border-radius: 4px; + border-left: 5px solid #1565c0; + box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2); + + z-index: 10; + cursor: pointer; + display: grid; + grid-auto-flow: column; + align-items: center; + min-height: 20px; +} + +.appointment.large { + grid-auto-flow: row; + align-items: flex-start; +} + +.appointment-time { + margin-left: 8px; +} + +@media (max-width: 600px) { + .appointment { + //width: calc(100% - 80px); + font-size: 14px; + } + + .appointment-time { + font-size: 12px; + display: block; + } +} + diff --git a/src/app/components/appointment/appointment.component.ts b/src/app/components/appointment/appointment.component.ts new file mode 100644 index 0000000..e2e6226 --- /dev/null +++ b/src/app/components/appointment/appointment.component.ts @@ -0,0 +1,46 @@ +import {Component, EventEmitter, Input, Output} from '@angular/core'; +import {NgClass, NgIf, NgStyle} from '@angular/common'; +import {AppointmentDto} from '../../models/appointment-dto'; +import {TuiIcon} from '@taiga-ui/core'; + +@Component({ + selector: 'component-appointment', + imports: [ + NgClass, + NgStyle, + TuiIcon, + NgIf + ], + templateUrl: './appointment.component.html', + styleUrl: './appointment.component.scss' +}) +export class AppointmentComponent { + @Input() appointment: AppointmentDto; + @Input() size: 'large' | 'medium' | 'small' = 'medium'; + @Input() width: 'normal' | 'small' = 'normal'; + @Output() onClick = new EventEmitter(); + + selectAppointment(appointment: AppointmentDto) { + this.onClick.emit(appointment); + } + + getInlineStyles(appointment: AppointmentDto): { [key: string]: string } { + return { + '--duration': `${appointment.durationInMinutes}`, + '--start-minute': `${appointment.startMinute}`, + 'top': `${appointment.startMinute}px`, // Startpositie binnen het uur + 'height': `${appointment.durationInMinutes}px`, // Hoogte over meerdere uren + 'width': this.width == "normal"? '100px' : '50px', + }; + } + + getFormattedTime(hour: number, minute: number): string { + return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`; + } + + getAppointmentHeight(appointment: AppointmentDto): number { + const startInMinutes = (appointment.startHour * 60) + appointment.startMinute; + const endInMinutes = (appointment.endHour * 60) + appointment.endMinute; + return (endInMinutes - startInMinutes); // 50px per uur + } +} diff --git a/src/app/components/datepicker/datepicker.component.html b/src/app/components/datepicker/datepicker.component.html new file mode 100644 index 0000000..f691069 --- /dev/null +++ b/src/app/components/datepicker/datepicker.component.html @@ -0,0 +1,25 @@ +
+
+ +

{{ currentDate | date:'MMMM yyyy' }} - {{ secondDate | date:'MMMM yyyy' }}

+ +
+ +
+
+
Wk
+
{{ day }}
+ + +
{{ week.number }}
+
+ {{ day.getDate() }} +
+
+
+
+
diff --git a/src/app/components/datepicker/datepicker.component.scss b/src/app/components/datepicker/datepicker.component.scss new file mode 100644 index 0000000..989672f --- /dev/null +++ b/src/app/components/datepicker/datepicker.component.scss @@ -0,0 +1,99 @@ +$primary: #1976d2; +$hover-bg: #e3f2fd; +$selected-bg: #1565c0; +$bg-light: #f5f5f5; +$border-radius: 8px; + +.calendar { + max-width: 900px; + margin: 2rem auto; + padding: 1rem; + background: #fff; + border-radius: $border-radius; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + font-family: 'Segoe UI', sans-serif; + + .calendar-header { + display: flex; + justify-content: space-between; + justify-content: space-between; + align-items: center; + margin-bottom: 1.5rem; + + h3 { + margin: 0; + font-size: 1.3rem; + text-align: center; + } + + .nav-button { + background: none; + border: none; + font-size: 1.6rem; + color: $primary; + cursor: pointer; + padding: 0.4rem 0.6rem; + border-radius: 4px; + + &:hover { + background: $hover-bg; + } + } + } + + .calendar-pair { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 2rem; + } + + .calendar-grid { + display: grid; + grid-template-columns: repeat(8, 1fr); + gap: 6px; + + .day-header, + .week-label, + .week-number { + font-weight: bold; + background: $bg-light; + padding: 6px 0; + border-radius: $border-radius; + font-size: 0.9rem; + text-align: center; + height: 20px; + width: 28px; + } + + .day-cell { + padding: 8px 0; + text-align: center; + cursor: pointer; + border-radius: $border-radius; + transition: background-color 0.2s; + height: 20px; + width: 28px; + + &:hover { + background: $hover-bg; + } + + &.today { + background: $hover-bg; + font-weight: bold; + } + + &.selected { + background: $selected-bg; + color: #fff; + } + } + } + + .calendar-footer { + text-align: center; + margin-top: 1.5rem; + font-size: 0.95rem; + color: #555; + } +} diff --git a/src/app/components/datepicker/datepicker.component.ts b/src/app/components/datepicker/datepicker.component.ts new file mode 100644 index 0000000..f839c59 --- /dev/null +++ b/src/app/components/datepicker/datepicker.component.ts @@ -0,0 +1,98 @@ +import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; +import {DatePipe, NgForOf, NgIf} from '@angular/common'; + +@Component({ + selector: 'datepicker', + imports: [ + NgForOf, + DatePipe, + NgIf + ], + templateUrl: './datepicker.component.html', + styleUrl: './datepicker.component.scss' +}) +export class DatepickerComponent implements OnInit { + @Input() currentDate = new Date(); // startmaand + secondDate = new Date(); // tweede maand + + calendars: any[][] = [[], []]; + selectedDate: Date | null = null; + @Output() onSelectedDate: EventEmitter = new EventEmitter(); + + ngOnInit() { + this.updateSecondDate(); + this.generateCalendars(); + } + + updateSecondDate() { + this.secondDate = new Date(this.currentDate.getFullYear(), this.currentDate.getMonth() + 1, 1); + } + + generateCalendars() { + this.calendars[0] = this.generateCalendar(this.currentDate); + this.calendars[1] = this.generateCalendar(this.secondDate); + } + + generateCalendar(date: Date) { + const year = date.getFullYear(); + const month = date.getMonth(); + + const firstDay = new Date(year, month, 1); + const lastDay = new Date(year, month + 1, 0); + + let day = new Date(firstDay); + day.setDate(day.getDate() - ((day.getDay() + 6) % 7)); // maandag starten + + const calendar: any[] = []; + + while (day <= lastDay || day.getDay() !== 1) { + const week = { + number: this.getWeekNumber(day), + days: [] + }; + + for (let i = 0; i < 7; i++) { + week.days.push(new Date(day)); + day.setDate(day.getDate() + 1); + } + + calendar.push(week); + } + + return calendar; + } + + getWeekNumber(d: Date): number { + const date = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate())); + const dayNum = date.getUTCDay() || 7; + date.setUTCDate(date.getUTCDate() + 4 - dayNum); + const yearStart = new Date(Date.UTC(date.getUTCFullYear(), 0, 1)); + return Math.ceil((((+date - +yearStart) / 86400000) + 1) / 7); + } + + selectDate(date: Date) { + this.selectedDate = date; + this.onSelectedDate.emit(date); + } + + isToday(date: Date): boolean { + const today = new Date(); + return date.toDateString() === today.toDateString(); + } + + isSelected(date: Date): boolean { + return this.selectedDate?.toDateString() === date.toDateString(); + } + + goToPreviousMonth() { + this.currentDate = new Date(this.currentDate.getFullYear(), this.currentDate.getMonth() - 1, 1); + this.updateSecondDate(); + this.generateCalendars(); + } + + goToNextMonth() { + this.currentDate = new Date(this.currentDate.getFullYear(), this.currentDate.getMonth() + 1, 1); + this.updateSecondDate(); + this.generateCalendars(); + } +} diff --git a/src/app/components/dropdown-content/dropdown-content.component.html b/src/app/components/dropdown-content/dropdown-content.component.html index 9f8cb10..29005ef 100644 --- a/src/app/components/dropdown-content/dropdown-content.component.html +++ b/src/app/components/dropdown-content/dropdown-content.component.html @@ -8,7 +8,7 @@ [style.color]="'var(--tui-background-accent-1)'"/> Agenda delen - - diff --git a/src/app/components/dropdown-content/dropdown-content.component.ts b/src/app/components/dropdown-content/dropdown-content.component.ts index fb5c57a..293c3c2 100644 --- a/src/app/components/dropdown-content/dropdown-content.component.ts +++ b/src/app/components/dropdown-content/dropdown-content.component.ts @@ -1,24 +1,28 @@ -import {Component, EventEmitter, Output} from '@angular/core'; +import {Component, EventEmitter, Input, Output} from '@angular/core'; import {TuiDataListComponent, TuiIcon, TuiOption} from '@taiga-ui/core'; import {Router} from '@angular/router'; +import {NgIf} from '@angular/common'; @Component({ selector: 'dropdown-content', + standalone: true, imports: [ - TuiDataListComponent, + TuiIcon, TuiOption, - TuiIcon + TuiDataListComponent, + NgIf, ], templateUrl: './dropdown-content.component.html', - styleUrl: './dropdown-content.component.scss' + styleUrls: ['./dropdown-content.component.scss'] }) export class DropdownContentComponent { constructor(private router: Router) { } - currentView: 'day' | 'week' | 'year' = 'day' + @Input() currentView: string = 'day' @Output() close = new EventEmitter() + @Output() view = new EventEmitter() shareAgenda() { this.close.emit()/* open modal etc */ @@ -29,9 +33,22 @@ export class DropdownContentComponent { this.close.emit()/* toggle agenda */ } - setView(view: 'day' | 'week' | 'year') { - this.close.emit() + setView(view: 'day' | 'week' | 'month') { this.currentView = view; + this.view.emit(view) + this.close.emit() + } + + isDay() { + return this.currentView === 'day' + } + + isWeek() { + return this.currentView === 'week' + } + + isMonth() { + return this.currentView === 'month' } } diff --git a/src/app/components/new-item/new-item.component.ts b/src/app/components/new-item/new-item.component.ts index 33f8ecf..56f5adc 100644 --- a/src/app/components/new-item/new-item.component.ts +++ b/src/app/components/new-item/new-item.component.ts @@ -89,7 +89,7 @@ export class NewItemComponent implements OnInit { const customer = this.customerControl.value; - const appointment = new Appointment(title, description, startTime.hours, startTime.minutes, endTime.hours, endTime.minutes, correctDate, customer) + const appointment = new AppointmentDto(title, description, startTime.hours, startTime.minutes, endTime.hours, endTime.minutes, correctDate, customer) this.waiting = true this.appointmentService.addAppointment(appointment, this.userService.currentCompany.id).subscribe(() => { this.waiting = false diff --git a/src/app/components/week-view/week-view.component.html b/src/app/components/week-view/week-view.component.html new file mode 100644 index 0000000..b28db94 --- /dev/null +++ b/src/app/components/week-view/week-view.component.html @@ -0,0 +1,34 @@ +
+
+
+
+ {{ getDayLabel(day) }} +
+
+ +
+
+
{{ hour }}
+
+
+ + +
+
+
+ + + + +
+
+
+
+
+
diff --git a/src/app/components/week-view/week-view.component.scss b/src/app/components/week-view/week-view.component.scss new file mode 100644 index 0000000..c1a0c9f --- /dev/null +++ b/src/app/components/week-view/week-view.component.scss @@ -0,0 +1,87 @@ +.calendar-container { + display: flex; + flex-direction: column; + border: 1px solid #ccc; + font-family: Arial, sans-serif; + height: 100%; + overflow: hidden; +} + +.calendar-header { + display: flex; + background-color: #f5f5f5; + border-bottom: 1px solid #ccc; +} + +.time-column-header { + width: 60px; +} + +.day-header { + flex: 1; + text-align: center; + padding: 10px 0; + font-weight: bold; + border-left: 1px solid #ccc; +} + +.calendar-body { + display: flex; + flex: 1; + overflow-y: auto; +} + +.time-column { + width: 60px; + border-right: 1px solid #ccc; +} + +.hour-label { + height: 60px; + line-height: 60px; + text-align: right; + padding-right: 5px; + font-size: 12px; + color: #666; +} + +.week-grid { + display: flex; + flex: 1; +} + +.day-column { + flex: 1; + display: flex; + flex-direction: column; + border-left: 1px solid #eee; +} + +.hour-cell { + height: 60px; + border-bottom: 1px solid #eee; + position: relative; +} + +.appointments { + display: flex; +} + +.appointments-wrapper { + display: flex; + flex-direction: row; + gap: 4px; // ruimte tussen de afspraken + height: 100%; + width: 100%; + + component-appointment { + flex: 1 1 0; + min-width: 0; + max-width: 100%; + } + + &.multiple component-appointment { + flex: 1 1 0; + } +} + diff --git a/src/app/components/week-view/week-view.component.ts b/src/app/components/week-view/week-view.component.ts new file mode 100644 index 0000000..f769f6a --- /dev/null +++ b/src/app/components/week-view/week-view.component.ts @@ -0,0 +1,106 @@ +import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; +import {NgClass, NgForOf, NgIf} from '@angular/common'; +import {AppointmentDto} from '../../models/appointment-dto'; +import {AppointmentService} from '../../services/appointment.service'; +import {AppointmentComponent} from '../appointment/appointment.component'; + +@Component({ + selector: 'app-week-view', + templateUrl: './week-view.component.html', + imports: [ + NgForOf, + AppointmentComponent, + NgIf, + NgClass + ], + styleUrls: ['./week-view.component.scss'] +}) +export class WeekViewComponent implements OnInit { + hours: string[] = []; + days: Date[] = []; + @Input() selectedDate: Date; + appointments: AppointmentDto[] = []; + @Output() appointmentSelected = new EventEmitter(); + + + constructor(private appointmentService: AppointmentService) { + } + + ngOnInit(): void { + this.generateHours(); + this.getWeekDates(); + this.getAppointments() + } + + generateHours() { + for (let i = 0; i < 24; i++) { + const hour = i.toString().padStart(2, '0') + ':00'; + this.hours.push(hour); + } + } + + getAppointments() { + console.log(this.days[0], this.days[6]) + this.appointmentService.getAppointmentsByWeek(this.days[0], this.days[6]).subscribe(appointments => { + this.appointments = appointments + console.log(appointments); + }); + } + + generateWeek() { + const today = new Date(); + const startOfWeek = today.getDate() - today.getDay() + 1; // maandag + console.log(today.getDate() - today.getDay()); + for (let i = 0; i < 7; i++) { + const date = new Date(today); + date.setDate(startOfWeek + i); + this.days.push(date); + } + } + + getDayLabel(date: Date): string { + return date.toLocaleDateString('nl-NL', {weekday: 'short', day: 'numeric', month: 'short'}); + } + + setSelectedDate(date: Date): void { + this.selectedDate = date; + } + + getWeekDates() { + const startDate = new Date(this.selectedDate); // Maak een kopie van de ingevoerde datum + const dayOfWeek = startDate.getDay(); // Verkrijg de dag van de week (0 = zondag, 1 = maandag, etc.) + + // Pas de datum aan zodat het de maandag van dezelfde week wordt + const diff = startDate.getDate() - dayOfWeek + (dayOfWeek == 0 ? -6 : 1); // Als het zondag is (dayOfWeek == 0), gaan we naar vorige maandag + startDate.setDate(diff); + + // Genereer de datums voor de volledige week + const weekDates: Date[] = []; + for (let i = 0; i < 7; i++) { + const currentDay = new Date(startDate); // Maak een kopie van de startdatum + currentDay.setDate(startDate.getDate() + i); // Voeg de dagen van de week toe + weekDates.push(currentDay); + } + + this.days = weekDates; + } + + searchAppointments(day: Date, hour: string) { + return this.appointments.filter(appointment => + appointment.startHour === Number(hour.substring(0, 2)) && + appointment.startDate.toString().startsWith(this.formatDateToISO(day)) + ); + } + + formatDateToISO(date: Date): string { + const year = date.getFullYear(); + const month = (date.getMonth() + 1).toString().padStart(2, '0'); // maand is 0-based + const day = date.getDate().toString().padStart(2, '0'); + + return `${year}-${month}-${day}`; + } + + emitAppointment($event: AppointmentDto) { + this.appointmentSelected.emit($event); + } +} diff --git a/src/app/models/appointment-dto.ts b/src/app/models/appointment-dto.ts index 17fdbf4..4c39ff2 100644 --- a/src/app/models/appointment-dto.ts +++ b/src/app/models/appointment-dto.ts @@ -5,7 +5,7 @@ export class AppointmentDto { id: number; title: string; description: string; - startDate: Date; // ISO 8601 string (LocalDateTime in Java) + startDate: string | Date; startHour: number; startMinute: number; endHour: number; @@ -13,4 +13,19 @@ export class AppointmentDto { durationInMinutes: number; customer: Customer; company: CompanyDTO; + + + constructor(title: string, description: string, startHour: number, startMinute: number, endHour: number, endMinute: number, date: Date, customer: Customer) { + this.title = title; + this.description = description; + this.startHour = startHour; + this.startMinute = startMinute; + this.endHour = endHour; + this.endMinute = endMinute; + this.startDate = date; + this.customer = customer; + + // Bereken de totale duur in minuten + this.durationInMinutes = (endHour * 60 + endMinute) - (startHour * 60 + startMinute); + } } diff --git a/src/app/pages/agenda-invite/agenda-invite.component.html b/src/app/pages/agenda-invite/agenda-invite.component.html index ab29bfb..dfa6f38 100644 --- a/src/app/pages/agenda-invite/agenda-invite.component.html +++ b/src/app/pages/agenda-invite/agenda-invite.component.html @@ -20,3 +20,12 @@ Uitnodiging accepteren +
+

Agenda-uitnodiging verlopen

+ +

+ Deze uitnodiging is niet meer geldig. Mogelijk is de geldigheidsduur verstreken of is de uitnodiging ingetrokken. +

+ +
+ diff --git a/src/app/pages/agenda-invite/agenda-invite.component.scss b/src/app/pages/agenda-invite/agenda-invite.component.scss index f91f949..c0a68d5 100644 --- a/src/app/pages/agenda-invite/agenda-invite.component.scss +++ b/src/app/pages/agenda-invite/agenda-invite.component.scss @@ -52,3 +52,13 @@ transform: translateY(0); } } + +.error-message { + color: #d9534f; + background-color: #fcebea; + padding: 12px; + border-radius: 6px; + margin-bottom: 1rem; + font-weight: 500; +} + diff --git a/src/app/pages/agenda-invite/agenda-invite.component.ts b/src/app/pages/agenda-invite/agenda-invite.component.ts index 8f3a09b..33ab687 100644 --- a/src/app/pages/agenda-invite/agenda-invite.component.ts +++ b/src/app/pages/agenda-invite/agenda-invite.component.ts @@ -23,6 +23,7 @@ export class AgendaInviteComponent implements OnInit { agendaName: string = '...'; company: CompanyDTO; inviteEntity: InviteEntity; + inviteExpired: boolean = false; constructor(private route: ActivatedRoute, private router: Router, private companyService: CompanyService, private agendaService: AgendaService, private userService: UserService, private alerts: TuiAlertService) { } @@ -36,7 +37,11 @@ export class AgendaInviteComponent implements OnInit { this.company = companyResponse; }) }, - error: (error) => console.log(error), + error: (error) => { + if (error.status === 410) { + this.inviteExpired = true; + } + }, }); } diff --git a/src/app/pages/agenda/agenda.component.html b/src/app/pages/agenda/agenda.component.html index 577b254..d4107a4 100644 --- a/src/app/pages/agenda/agenda.component.html +++ b/src/app/pages/agenda/agenda.component.html @@ -1,6 +1,7 @@
-

{{ getDate() }} + +

Week {{ getWeek() }} + + +

+

{{ getDate() }}

+
- + + + + + +
-
+
{{ hour.toString().padStart(2, '0') }}:00 @@ -104,6 +137,8 @@
+ +
diff --git a/src/app/pages/agenda/agenda.component.ts b/src/app/pages/agenda/agenda.component.ts index 55eff34..b8763cf 100644 --- a/src/app/pages/agenda/agenda.component.ts +++ b/src/app/pages/agenda/agenda.component.ts @@ -1,6 +1,6 @@ -import {Component, inject, OnInit} from '@angular/core'; +import {Component, inject, Input, OnInit, ViewChild} from '@angular/core'; import {CommonModule, NgFor, NgIf} from '@angular/common'; -import {TuiAlertService, TuiButton, TuiCalendar, tuiDateFormatProvider, TuiIcon} from '@taiga-ui/core'; +import {TuiAlertService, TuiButton, tuiDateFormatProvider, TuiIcon} from '@taiga-ui/core'; import {ModalComponent} from '../../components/modal/modal.component'; import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms'; import { @@ -19,13 +19,15 @@ import {NewItemComponent} from '../../components/new-item/new-item.component'; import {timeAfterStartValidator} from '../../models/validators/time-after-start-validator'; import {DropdownContentComponent} from '../../components/dropdown-content/dropdown-content.component'; import {AppointmentDto} from '../../models/appointment-dto'; +import {WeekViewComponent} from '../../components/week-view/week-view.component'; +import {DatepickerComponent} from '../../components/datepicker/datepicker.component'; @Component({ selector: 'app-agenda', imports: [NgFor, TuiButton, CommonModule, NgIf, ModalComponent, ReactiveFormsModule, TuiInputTimeModule, TuiTextfieldControllerModule, - TuiInputModule, TuiTextareaModule, TuiInputDateModule, TuiIcon, DetailsComponent, TuiCalendar, NewItemComponent, DropdownContentComponent], + TuiInputModule, TuiTextareaModule, TuiInputDateModule, TuiIcon, DetailsComponent, NewItemComponent, DropdownContentComponent, WeekViewComponent, DatepickerComponent], templateUrl: './agenda.component.html', providers: [tuiDateFormatProvider({separator: '-'}), AppointmentService], styleUrl: './agenda.component.scss' @@ -49,6 +51,8 @@ export class AgendaComponent implements OnInit { protected value: TuiDay | null = null; showCalendar: boolean = false; open = false + @Input() view: 'day' | 'week' | 'month' = 'day' + @ViewChild(WeekViewComponent) childComponent!: WeekViewComponent; private readonly alerts = inject(TuiAlertService); protected appointmentForm = new FormGroup({ @@ -110,6 +114,18 @@ export class AgendaComponent implements OnInit { this.appointmentForm.get('date').setValue(new TuiDay(this.selectedDate.getFullYear(), this.selectedDate.getMonth(), this.selectedDate.getDate())) } + nextWeek() { + this.selectedDate.setDate(this.selectedDate.getDate() + 7); + this.childComponent.getWeekDates() + this.childComponent.getAppointments() + } + + previousWeek() { + this.selectedDate.setDate(this.selectedDate.getDate() - 7); + this.childComponent.getWeekDates() + this.childComponent.getAppointments() + } + getHours() { let hours = this.today.getHours() if (hours > 23) { @@ -138,11 +154,20 @@ export class AgendaComponent implements OnInit { return endTime; } + setView(view: 'day' | 'week' | 'month') { + this.view = view + } setToday() { this.selectedDate = new Date() - this.getAppointmentsByDate(this.selectedDate); - this.appointmentForm.get('date').setValue(new TuiDay(this.selectedDate.getFullYear(), this.selectedDate.getMonth(), this.selectedDate.getDate())) + if (this.view === 'day') { + this.getAppointmentsByDate(this.selectedDate); + this.appointmentForm.get('date').setValue(new TuiDay(this.selectedDate.getFullYear(), this.selectedDate.getMonth(), this.selectedDate.getDate())) + } else if (this.view === 'week') { + this.childComponent.setSelectedDate(this.selectedDate); + this.childComponent.getWeekDates() + this.childComponent.getAppointments() + } } getAppointmentsByDate(date: Date) { @@ -176,12 +201,17 @@ export class AgendaComponent implements OnInit { protected readonly DateFormatter = DateFormatter; - onDayClick(day: TuiDay) { - this.value = day; - this.selectedDate = new Date(day.year, day.month, day.day); - this.getAppointmentsByDate(this.selectedDate); - this.appointmentForm.get('date').setValue(new TuiDay(this.selectedDate.getFullYear(), this.selectedDate.getMonth(), this.selectedDate.getDate())) + onDayClick(day: Date) { + this.selectedDate = day + this.toggleCalendar() + if (this.view === 'day') { + this.getAppointmentsByDate(this.selectedDate); + this.appointmentForm.get('date').setValue(new TuiDay(this.selectedDate.getFullYear(), this.selectedDate.getMonth(), this.selectedDate.getDate())) + + } else if (this.view === 'week') { + this.childComponent.getWeekDates() + } } toggleCalendar() { @@ -234,5 +264,16 @@ export class AgendaComponent implements OnInit { openSettings() { this.open = !this.open } + + getWeek() { + + const tempDate = new Date(this.selectedDate.getTime()); + tempDate.setHours(0, 0, 0, 0); + + // Zet de datum naar de donderdag van de week + tempDate.setDate(tempDate.getDate() + (4 - (tempDate.getDay() || 7)));// getDay() kan 0-6 zijn, waarbij 0 zondag is + const yearStart = new Date(tempDate.getFullYear(), 0, 1); + return Math.ceil((((tempDate.getTime() - yearStart.getTime()) / 86400000) + 1) / 7); + } } diff --git a/src/app/services/appointment.service.ts b/src/app/services/appointment.service.ts index 8a041f8..3fbea2c 100644 --- a/src/app/services/appointment.service.ts +++ b/src/app/services/appointment.service.ts @@ -15,7 +15,7 @@ export class AppointmentService { } getAllAppointments() { - return this.http.get(`${this.baseApi}`); + return this.http.get(`${this.baseApi}`); } getAppointmentsByDate(date: Date) { @@ -25,7 +25,17 @@ export class AppointmentService { return this.http.get(`${this.baseApi}/date?start=${year}-${month}-${day}`, {}); } - addAppointment(appointment: Appointment, companyId: number) { + getAppointmentsByWeek(date: Date, endDate: Date) { + const day = date.getDate().toString().padStart(2, '0') + const month = (date.getMonth() + 1).toString().padStart(2, '0') + const year = date.getFullYear(); + const endDay = endDate.getDate().toString().padStart(2, '0') + const endMonth = (endDate.getMonth() + 1).toString().padStart(2, '0') + const endYear = endDate.getFullYear(); + return this.http.get(`${this.baseApi}/date/week?start=${year}-${month}-${day}&end=${endYear}-${endMonth}-${endDay}`, {}); + } + + addAppointment(appointment: AppointmentDto, companyId: number) { return this.http.post(`${this.baseApi}/${companyId}`, appointment); }