diff --git a/admin/src/app/features/calendar/components/calendar-view/calendar-view.ts b/admin/src/app/features/calendar/components/calendar-view/calendar-view.ts index 7b7690b..3bd0d38 100644 --- a/admin/src/app/features/calendar/components/calendar-view/calendar-view.ts +++ b/admin/src/app/features/calendar/components/calendar-view/calendar-view.ts @@ -24,8 +24,8 @@ import { SingleEventDashboardEventDelete, } from './single-event-dashboard-event-delete/single-event-dashboard-event-delete'; import { - SingleEventDashboardEventCancel, -} from './single-event-dashboard-event-cancel/single-event-dashboard-event-cancel'; + SingleEventDashboardEventActivation, +} from './single-event-dashboard-event-activation/single-event-dashboard-event-activation.component'; import { SingleEventDashboardEventEdit } from './single-event-dashboard-event-edit/single-event-dashboard-event-edit'; @Component({ @@ -81,7 +81,6 @@ export class CalendarView { { component: SingleEventDashboardEventDelete, isRendered: () => this.workflow() == 'event-delete', - // isRendered: () => true, closeClick: () => this.closeDialog(), modalBoxStyleClass: 'max-w-none w-2xl', componentInputs: () => { @@ -94,13 +93,26 @@ export class CalendarView { }, { - component: SingleEventDashboardEventCancel, + component: SingleEventDashboardEventActivation, isRendered: () => this.workflow() == 'event-cancel', - // isRendered: () => true, closeClick: () => this.closeDialog(), modalBoxStyleClass: 'max-w-none w-2xl', componentInputs: () => { return { + 'mode': 'cancel', + 'event': this.selectedEvent(), + 'onAction': this.handleAction, + }; + }, + }, + { + component: SingleEventDashboardEventActivation, + isRendered: () => this.workflow() == 'event_activate', + closeClick: () => this.closeDialog(), + modalBoxStyleClass: 'max-w-none w-2xl', + componentInputs: () => { + return { + 'mode': 'activate', 'event': this.selectedEvent(), 'onAction': this.handleAction, }; @@ -141,6 +153,12 @@ export class CalendarView { extendedProps: { event: model, }, + editable: !model.isCancelled, + // 2. Add a class for styling + classNames: model.isCancelled ? ['disabled-event'] : [], + // Optional: Force a gray color here if not using CSS + backgroundColor: '#d3d3d3', + borderColor: '#d3d3d3' }; if (model.eventType) { calendarEvent.borderColor = model.eventType.color; @@ -197,6 +215,9 @@ export class CalendarView { case 'event_edit': this.workflow.set('event-edit'); break; + default: + this.workflow.set(action); + break; } } @@ -209,6 +230,7 @@ export class CalendarView { } else if ( msg == 'save-event-success'){ this.closeDialog(); this.calendarComponent?.getApi().refetchEvents(); + }else{ } }; diff --git a/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard-event-cancel/single-event-dashboard-event-cancel.css b/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard-event-activation/single-event-dashboard-event-activation.component.css similarity index 100% rename from admin/src/app/features/calendar/components/calendar-view/single-event-dashboard-event-cancel/single-event-dashboard-event-cancel.css rename to admin/src/app/features/calendar/components/calendar-view/single-event-dashboard-event-activation/single-event-dashboard-event-activation.component.css diff --git a/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard-event-activation/single-event-dashboard-event-activation.component.html b/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard-event-activation/single-event-dashboard-event-activation.component.html new file mode 100644 index 0000000..f128b86 --- /dev/null +++ b/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard-event-activation/single-event-dashboard-event-activation.component.html @@ -0,0 +1,27 @@ +

+ @if (mode() == 'cancel') { + Esemény lemondása + } @else { + Esemény aktiválása + } +

+ +
+ + @if (mode() == 'cancel') { + + + Lemondás + + } + @if (mode() == 'activate') { + + + Aktiválás + + } + + + Mégsem + +
diff --git a/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard-event-cancel/single-event-dashboard-event-cancel.spec.ts b/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard-event-activation/single-event-dashboard-event-activation.component.spec.ts similarity index 51% rename from admin/src/app/features/calendar/components/calendar-view/single-event-dashboard-event-cancel/single-event-dashboard-event-cancel.spec.ts rename to admin/src/app/features/calendar/components/calendar-view/single-event-dashboard-event-activation/single-event-dashboard-event-activation.component.spec.ts index 4c35e21..6a7ce00 100644 --- a/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard-event-cancel/single-event-dashboard-event-cancel.spec.ts +++ b/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard-event-activation/single-event-dashboard-event-activation.component.spec.ts @@ -1,18 +1,18 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { SingleEventDashboardEventCancel } from './single-event-dashboard-event-cancel'; +import { SingleEventDashboardEventActivation } from './single-event-dashboard-event-activation.component'; describe('SingleEventDashboardEventCancel', () => { - let component: SingleEventDashboardEventCancel; - let fixture: ComponentFixture; + let component: SingleEventDashboardEventActivation; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [SingleEventDashboardEventCancel] + imports: [SingleEventDashboardEventActivation] }) .compileComponents(); - fixture = TestBed.createComponent(SingleEventDashboardEventCancel); + fixture = TestBed.createComponent(SingleEventDashboardEventActivation); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard-event-activation/single-event-dashboard-event-activation.component.ts b/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard-event-activation/single-event-dashboard-event-activation.component.ts new file mode 100644 index 0000000..be42096 --- /dev/null +++ b/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard-event-activation/single-event-dashboard-event-activation.component.ts @@ -0,0 +1,63 @@ +import { Component, inject, input } from '@angular/core'; +import { CalendarEventDto } from '../../../models/events-in-range-dto.model'; +import { + SingleEventDashboardEventDetailsView +} from '../single-event-dashboard-event-details-view/single-event-dashboard-event-details-view'; +import { Button } from '@rschneider/ng-daisyui'; +import { SvgIcons } from '../../../../../svg-icons'; +import { SafeHtmlPipe } from '../../../../../pipes/safe-html-pipe'; +import { CalendarService } from '../../../services/calendar.service'; + +export type ACTIVATION_TYPE = 'cancel' | 'activate'; + +@Component({ + selector: 'app-single-event-dashboard-event-cancel', + imports: [ + SingleEventDashboardEventDetailsView, + Button, + SafeHtmlPipe, + ], + templateUrl: './single-event-dashboard-event-activation.component.html', + styleUrl: './single-event-dashboard-event-activation.component.css', +}) +export class SingleEventDashboardEventActivation { + + mode = input('cancel'); + calendarService = inject(CalendarService); + event = input(); + onAction = input.required<(msg: string) => void>(); + + protected readonly SvgIcons = SvgIcons; + + + protected setEventOccurrenceActivation(activated: boolean) { + + const eventId =this.event()?.id!; + const startTime = this.event()?.startTime!; + + this.calendarService.applyException(eventId,{ + originalStartTime: new Date(startTime), + isCancelled: !activated, + }).subscribe( + { + next: () => { + this.onAction()('close'); + }, + error: err => { + alert("Failed to change event"); + } + } + ) + } + + protected cancelEventOccurrence() { +this.setEventOccurrenceActivation(false); + } + protected activateEventOccurrence() { + this.setEventOccurrenceActivation(true); + } + protected closeDialog() { + this.onAction()('close') + } + +} diff --git a/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard-event-cancel/single-event-dashboard-event-cancel.html b/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard-event-cancel/single-event-dashboard-event-cancel.html deleted file mode 100644 index fcc499e..0000000 --- a/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard-event-cancel/single-event-dashboard-event-cancel.html +++ /dev/null @@ -1,12 +0,0 @@ -

Esemény lemondása

- -
- - - Törlés - - - - Mégsem - -
diff --git a/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard-event-cancel/single-event-dashboard-event-cancel.ts b/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard-event-cancel/single-event-dashboard-event-cancel.ts deleted file mode 100644 index e14c4fc..0000000 --- a/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard-event-cancel/single-event-dashboard-event-cancel.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Component, input } from '@angular/core'; -import { CalendarEventDto } from '../../../models/events-in-range-dto.model'; -import { - SingleEventDashboardEventDetailsView -} from '../single-event-dashboard-event-details-view/single-event-dashboard-event-details-view'; -import { Button } from '@rschneider/ng-daisyui'; -import { SvgIcons } from '../../../../../svg-icons'; -import { SafeHtmlPipe } from '../../../../../pipes/safe-html-pipe'; - -@Component({ - selector: 'app-single-event-dashboard-event-cancel', - imports: [ - SingleEventDashboardEventDetailsView, - Button, - SafeHtmlPipe, - ], - templateUrl: './single-event-dashboard-event-cancel.html', - styleUrl: './single-event-dashboard-event-cancel.css', -}) -export class SingleEventDashboardEventCancel { - - event = input(); - onAction = input.required<(msg: string) => void>(); - - protected readonly SvgIcons = SvgIcons; - - protected triggerAction() { - - this.onAction()('close') - } -} diff --git a/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard/single-event-dashboard.html b/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard/single-event-dashboard.html index 3fca90d..1879aab 100644 --- a/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard/single-event-dashboard.html +++ b/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard/single-event-dashboard.html @@ -1,5 +1,9 @@ -

Esemény

- +@if (event()?.isRecurring) { +

Esemény sorozat

+} +@if (!event()?.isRecurring) { +

Esemény

+} @if (config) { - @for (card of cards; let i = $index; track i) { + @for (card of cards(); let i = $index; track i) { (); config: DetailViewConfig | undefined; - cards: CardConfig[] = []; + cards = signal([]); constructor() { effect(() => { + console.info("dashboard", this.event()); this.config = { data: this.event()!, @@ -50,8 +51,8 @@ export class SingleEventDashboard { }, ], }; - }); - this.cards = [ + + this.cards.set( [ { buttonTitle: 'Szerkesztés', title: 'Szerkesztés', @@ -59,11 +60,21 @@ export class SingleEventDashboard { description: 'Az esemény módosítása', action: 'event_edit', }, + + this.event()?.isCancelled ? + + { + buttonTitle: 'Aktiválás', + title: 'Előfordulás aktiválása', + svgIcon: SvgIcons.heroPlay, + description: 'Az esemény ezen előfordulásának aktiválása', + action: 'event_activate', + } : { buttonTitle: 'Lemondás', - title: 'Esemény lemondása', + title: 'Előfordulás lemondása', svgIcon: SvgIcons.heroXcircle, - description: 'Az esemény lemondása', + description: 'Az esemény ezen előfordulásának lemondása', action: 'event_cancel', }, { @@ -94,7 +105,8 @@ export class SingleEventDashboard { description: 'Az időpont lemondása', action: 'user_cancel', }, - ]; + ]); + }); } onCardAction (action: string|undefined) { diff --git a/admin/src/app/features/calendar/models/event-exception.model.ts b/admin/src/app/features/calendar/models/event-exception.model.ts new file mode 100644 index 0000000..bd7d085 --- /dev/null +++ b/admin/src/app/features/calendar/models/event-exception.model.ts @@ -0,0 +1,8 @@ +export interface CreateExceptionDto { + originalStartTime: Date; // The start time of the instance to modify/cancel + isCancelled?: boolean; + newStartTime?: Date; + newEndTime?: Date; + title?: string; + description?: string; +} diff --git a/admin/src/app/features/calendar/models/events-in-range-dto.model.ts b/admin/src/app/features/calendar/models/events-in-range-dto.model.ts index 21a1b2d..818fa3a 100644 --- a/admin/src/app/features/calendar/models/events-in-range-dto.model.ts +++ b/admin/src/app/features/calendar/models/events-in-range-dto.model.ts @@ -25,6 +25,8 @@ export type CalendarEventDto = { description: string, isModified?: boolean; eventBookings: BookingWithUserDto[]; + isCancelled: boolean; + isRecurring: boolean; eventType: EventType }; diff --git a/admin/src/app/features/calendar/services/calendar.service.ts b/admin/src/app/features/calendar/services/calendar.service.ts index 91d4463..d69c161 100644 --- a/admin/src/app/features/calendar/services/calendar.service.ts +++ b/admin/src/app/features/calendar/services/calendar.service.ts @@ -5,6 +5,7 @@ import { ConfigurationService } from '../../../services/configuration.service'; import { EventFormDTO, UpdateEventFormDTO } from '../models/event-form-dto.model'; import { Event } from '../../events/models/event.model'; import { CalendarEventDto, EventsInRangeDTO } from '../models/events-in-range-dto.model'; +import { CreateExceptionDto } from '../models/event-exception.model'; @Injectable({ @@ -40,4 +41,7 @@ export class CalendarService { return this.http.patch(this.apiUrl+'/events/'+id, data); } + public applyException(eventId: number, eventException: CreateExceptionDto){ + return this.http.post(this.apiUrl+`/events/${eventId}/exceptions`, eventException); + } } diff --git a/admin/src/app/svg-icons.ts b/admin/src/app/svg-icons.ts index 534080d..334c2c0 100644 --- a/admin/src/app/svg-icons.ts +++ b/admin/src/app/svg-icons.ts @@ -39,5 +39,9 @@ export class SvgIcons { ` + public static heroPlay = ` + + +`; } diff --git a/admin/src/styles.css b/admin/src/styles.css index 06f2182..4e3435d 100644 --- a/admin/src/styles.css +++ b/admin/src/styles.css @@ -4,3 +4,14 @@ @import "./styles/grid.css"; @source "../projects/rschneider/ng-daisyui/src"; @plugin "daisyui"; + +.disabled-event { + /* Make it look faded */ + opacity: 0.5; + + /* Make it un-clickable (stops hover effects and click events) */ + /*pointer-events: none;*/ + + /* Optional: Change cursor to indicate it's not interactive */ + /*cursor: not-allowed;*/ +} diff --git a/server/src/calendar/calendar.service.ts b/server/src/calendar/calendar.service.ts index 29e3fb6..6dee62f 100644 --- a/server/src/calendar/calendar.service.ts +++ b/server/src/calendar/calendar.service.ts @@ -51,6 +51,7 @@ type BookingWithUserDto = { // The final shape of a calendar event occurrence export type CalendarEventDto = Omit & { isModified?: boolean; + isCancelled?: boolean; eventBookings: BookingWithUserDto[]; }; @@ -170,16 +171,20 @@ export class CalendarService { ); if (exception) { - if (exception.isCancelled) continue; + // if (exception.isCancelled) continue; // This is a MODIFIED occurrence - const key = `${event.id}-${exception.newStartTime.getTime()}`; + const key = `${event.id}-${exception.newStartTime?.getTime() || occurrenceDate.getTime()}`; recurringOccurrences.push({ ...event, - startTime: exception.newStartTime, - endTime: exception.newEndTime, + // startTime: exception.newStartTime || occurrenceDate, + startTime: exception.newStartTime || occurrenceDate, + endTime: + exception.newEndTime || + new Date(occurrenceDate.getTime() + duration), isModified: true, eventBookings: bookingMap.get(key) || [], + isCancelled: !!exception.isCancelled, }); } else { // This is a REGULAR occurrence diff --git a/server/src/calendar/dto/create-exception.dto.ts b/server/src/calendar/dto/create-exception.dto.ts index ae03334..ec41448 100644 --- a/server/src/calendar/dto/create-exception.dto.ts +++ b/server/src/calendar/dto/create-exception.dto.ts @@ -1,4 +1,10 @@ -import { IsDate, IsNotEmpty, IsOptional, IsString, IsBoolean } from 'class-validator'; +import { + IsDate, + IsNotEmpty, + IsOptional, + IsString, + IsBoolean, +} from 'class-validator'; import { Type } from 'class-transformer'; export class CreateExceptionDto { @@ -28,4 +34,4 @@ export class CreateExceptionDto { @IsOptional() @IsString() description?: string; -} \ No newline at end of file +}