refactor admin to use eventbus
This commit is contained in:
@@ -1,23 +1,19 @@
|
|||||||
<div>
|
<div>
|
||||||
<h1>Naptár</h1>
|
<h1>Naptár</h1>
|
||||||
<!-- <div>-->
|
|
||||||
<!-- <input type="text" name="startHour" id="starthour" #startHour>-->
|
|
||||||
<!-- <a class="btn" (click)="addEvent($event)">add</a>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
<full-calendar #calendar [options]="calendarOptions"></full-calendar>
|
<full-calendar #calendar [options]="calendarOptions"></full-calendar>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (workflow() == 'event-create') {
|
@if (workflow() == 'event_create') {
|
||||||
<rs-daisy-modal [isOpen]="true" (closeClick)="closeDialog()" [modalBoxStyleClass]="'max-w-none w-2xl'">
|
<rs-daisy-modal [isOpen]="true" (closeClick)="closeDialog()" [modalBoxStyleClass]="'max-w-none w-2xl'">
|
||||||
<app-create-event-form (ready)="closeDialog()" [date]="selectedDate()"
|
<app-create-event-form (ready)="closeDialog()" [date]="selectedDate()"
|
||||||
[id]="undefined"></app-create-event-form>
|
[id]="undefined"></app-create-event-form>
|
||||||
</rs-daisy-modal>
|
</rs-daisy-modal>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (workflow() == 'event-dashboard' && selectedEvent()) {
|
@if (workflow() == 'event_dashboard' && selectedEvent()) {
|
||||||
<rs-daisy-modal [isOpen]="true" (closeClick)="closeDialog()" [modalBoxStyleClass]="'max-w-none w-2xl'">
|
<rs-daisy-modal [isOpen]="true" (closeClick)="closeDialog()" [modalBoxStyleClass]="'max-w-none w-2xl'">
|
||||||
<app-single-event-dashboard [event]="selectedEvent()"
|
<app-single-event-dashboard [event]="selectedEvent()"
|
||||||
(action)="onDashboardAction($event)"
|
(action)="setWorkFlow($event)"
|
||||||
></app-single-event-dashboard>
|
></app-single-event-dashboard>
|
||||||
</rs-daisy-modal>
|
</rs-daisy-modal>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,24 @@ import {
|
|||||||
SingleEventDashboardEventActivation,
|
SingleEventDashboardEventActivation,
|
||||||
} from './single-event-dashboard-event-activation/single-event-dashboard-event-activation.component';
|
} 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';
|
import { SingleEventDashboardEventEdit } from './single-event-dashboard-event-edit/single-event-dashboard-event-edit';
|
||||||
|
import { EventBusService } from '../../../../services/event-bus.service';
|
||||||
|
import { EventType } from '../../../../../types';
|
||||||
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
|
import { SingleEventBookingCreate } from './single-event-booking-create/single-event-booking-create';
|
||||||
|
import { SingleEventBookingList } from './single-event-booking-list/single-event-booking-list';
|
||||||
|
|
||||||
|
|
||||||
|
export type WORKFLOW_TYPE = 'NO_DIALOG'
|
||||||
|
| 'event_delete'
|
||||||
|
| 'event_cancel'
|
||||||
|
| 'event_dashboard'
|
||||||
|
| 'event_create'
|
||||||
|
| 'event_activate'
|
||||||
|
| 'event_edit'
|
||||||
|
| 'booking_create'
|
||||||
|
| 'booking_list'
|
||||||
|
| 'booking_cancel'
|
||||||
|
;
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-calendar-view',
|
selector: 'app-calendar-view',
|
||||||
@@ -39,9 +57,11 @@ export class CalendarView {
|
|||||||
@ViewChild('startHour') startHour!: ElementRef;
|
@ViewChild('startHour') startHour!: ElementRef;
|
||||||
@ViewChild('calendar') calendarComponent: FullCalendarComponent | undefined;
|
@ViewChild('calendar') calendarComponent: FullCalendarComponent | undefined;
|
||||||
|
|
||||||
|
private eventBus = inject(EventBusService);
|
||||||
|
|
||||||
calendarService = inject(CalendarService);
|
calendarService = inject(CalendarService);
|
||||||
|
|
||||||
workflow = signal<string>('');
|
workflow = signal<WORKFLOW_TYPE>('NO_DIALOG');
|
||||||
selectedDate = signal<Date | undefined>(undefined);
|
selectedDate = signal<Date | undefined>(undefined);
|
||||||
selectedEvent = signal<CalendarEventDto | undefined>(undefined);
|
selectedEvent = signal<CalendarEventDto | undefined>(undefined);
|
||||||
|
|
||||||
@@ -50,6 +70,24 @@ export class CalendarView {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
||||||
|
this.eventBus.on(EventType.CALENDAR_VIEW_EVENT_SAVED)
|
||||||
|
.pipe(takeUntilDestroyed())
|
||||||
|
.subscribe({
|
||||||
|
next: (_) => {
|
||||||
|
this.closeDialog();
|
||||||
|
this.calendarComponent?.getApi().refetchEvents();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.eventBus.on(EventType.CALENDAR_VIEW_DIALOG_CLOSED)
|
||||||
|
.pipe(takeUntilDestroyed())
|
||||||
|
.subscribe({
|
||||||
|
next: (_) => {
|
||||||
|
this.closeDialog();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
this.calendarOptions = {
|
this.calendarOptions = {
|
||||||
plugins: [dayGridPlugin, timeGridPlugin, listPlugin, interactionPlugin],
|
plugins: [dayGridPlugin, timeGridPlugin, listPlugin, interactionPlugin],
|
||||||
initialView: 'dayGridMonth',
|
initialView: 'dayGridMonth',
|
||||||
@@ -65,12 +103,12 @@ export class CalendarView {
|
|||||||
|
|
||||||
eventClick: (info) => {
|
eventClick: (info) => {
|
||||||
this.selectedEvent.set(info.event.extendedProps['event']);
|
this.selectedEvent.set(info.event.extendedProps['event']);
|
||||||
this.workflow.set('event-dashboard');
|
this.workflow.set('event_dashboard');
|
||||||
this.selectedDate.set(undefined);
|
this.selectedDate.set(undefined);
|
||||||
},
|
},
|
||||||
|
|
||||||
dateClick: (info) => {
|
dateClick: (info) => {
|
||||||
this.workflow.set('event-create');
|
this.workflow.set('event_create');
|
||||||
this.selectedDate.set(info.date);
|
this.selectedDate.set(info.date);
|
||||||
this.selectedEvent.set(undefined);
|
this.selectedEvent.set(undefined);
|
||||||
},
|
},
|
||||||
@@ -80,13 +118,12 @@ export class CalendarView {
|
|||||||
this.dialogs = [
|
this.dialogs = [
|
||||||
{
|
{
|
||||||
component: SingleEventDashboardEventDelete,
|
component: SingleEventDashboardEventDelete,
|
||||||
isRendered: () => this.workflow() == 'event-delete',
|
isRendered: () => this.workflow() == 'event_delete',
|
||||||
closeClick: () => this.closeDialog(),
|
closeClick: () => this.closeDialog(),
|
||||||
modalBoxStyleClass: 'max-w-none w-2xl',
|
modalBoxStyleClass: 'max-w-none w-2xl',
|
||||||
componentInputs: () => {
|
componentInputs: () => {
|
||||||
return {
|
return {
|
||||||
'event': this.selectedEvent(),
|
'event': this.selectedEvent(),
|
||||||
'onAction': this.handleAction,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -94,14 +131,13 @@ export class CalendarView {
|
|||||||
|
|
||||||
{
|
{
|
||||||
component: SingleEventDashboardEventActivation,
|
component: SingleEventDashboardEventActivation,
|
||||||
isRendered: () => this.workflow() == 'event-cancel',
|
isRendered: () => this.workflow() == 'event_cancel',
|
||||||
closeClick: () => this.closeDialog(),
|
closeClick: () => this.closeDialog(),
|
||||||
modalBoxStyleClass: 'max-w-none w-2xl',
|
modalBoxStyleClass: 'max-w-none w-2xl',
|
||||||
componentInputs: () => {
|
componentInputs: () => {
|
||||||
return {
|
return {
|
||||||
'mode': 'cancel',
|
'mode': 'cancel',
|
||||||
'event': this.selectedEvent(),
|
'event': this.selectedEvent(),
|
||||||
'onAction': this.handleAction,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -114,26 +150,61 @@ export class CalendarView {
|
|||||||
return {
|
return {
|
||||||
'mode': 'activate',
|
'mode': 'activate',
|
||||||
'event': this.selectedEvent(),
|
'event': this.selectedEvent(),
|
||||||
'onAction': this.handleAction,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: SingleEventDashboardEventEdit,
|
component: SingleEventDashboardEventEdit,
|
||||||
isRendered: () => this.workflow() == 'event-edit',
|
isRendered: () => this.workflow() == 'event_edit',
|
||||||
|
// isRendered: () => true,
|
||||||
|
closeClick: () => this.closeDialog(),
|
||||||
|
modalBoxStyleClass: 'max-w-none w-2xl',
|
||||||
|
componentInputs: () => {
|
||||||
|
return {
|
||||||
|
'event': this.selectedEvent(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: SingleEventBookingCreate,
|
||||||
|
isRendered: () => this.workflow() == 'booking_create',
|
||||||
|
// isRendered: () => true,
|
||||||
|
closeClick: () => this.closeDialog(),
|
||||||
|
modalBoxStyleClass: 'max-w-none w-2xl',
|
||||||
|
componentInputs: () => {
|
||||||
|
return {
|
||||||
|
'event': this.selectedEvent(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: SingleEventBookingList,
|
||||||
|
isRendered: () => this.workflow() == 'booking_list',
|
||||||
|
// isRendered: () => true,
|
||||||
|
closeClick: () => this.closeDialog(),
|
||||||
|
modalBoxStyleClass: 'max-w-none w-2xl',
|
||||||
|
componentInputs: () => {
|
||||||
|
return {
|
||||||
|
'event': this.selectedEvent(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: SingleEventBookingList,
|
||||||
|
isRendered: () => this.workflow() == 'booking_cancel',
|
||||||
// isRendered: () => true,
|
// isRendered: () => true,
|
||||||
closeClick: () => this.closeDialog(),
|
closeClick: () => this.closeDialog(),
|
||||||
modalBoxStyleClass: 'max-w-none w-2xl',
|
modalBoxStyleClass: 'max-w-none w-2xl',
|
||||||
componentInputs: () => {
|
componentInputs: () => {
|
||||||
return {
|
return {
|
||||||
'event': this.selectedEvent(),
|
'event': this.selectedEvent(),
|
||||||
'onAction': this.handleAction,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fetchEvents(fetchInfo: any, successCallback: (events: EventInput[]) => void, failureCallback: (error: any) => void): void {
|
fetchEvents(fetchInfo: any, successCallback: (events: EventInput[]) => void, failureCallback: (error: any) => void): void {
|
||||||
|
|
||||||
console.info('fetching events', fetchInfo);
|
console.info('fetching events', fetchInfo);
|
||||||
@@ -183,58 +254,21 @@ export class CalendarView {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// protected addEvent($event: PointerEvent) {
|
|
||||||
// let hourStr = this.startHour.nativeElement.value;
|
|
||||||
// const hour = parseInt(hourStr, 10);
|
|
||||||
// const date = new Date();
|
|
||||||
// const start = new Date(date.getTime());
|
|
||||||
// start.setHours(hour, 0, 0);
|
|
||||||
// const end = new Date(date.getTime());
|
|
||||||
// // end.setHours(3,0,0)
|
|
||||||
//
|
|
||||||
// this.calendarComponent?.getApi().addEvent({
|
|
||||||
// title: 'Event at ' + hour,
|
|
||||||
// start,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
closeDialog() {
|
closeDialog() {
|
||||||
this.workflow.set('');
|
this.workflow.set('NO_DIALOG');
|
||||||
}
|
}
|
||||||
|
|
||||||
onDashboardAction(action: string) {
|
/**
|
||||||
console.info('dashboard event', action);
|
* Set dashboard workflow
|
||||||
switch (action) {
|
* @param workflowType
|
||||||
case 'event_delete':
|
*/
|
||||||
this.workflow.set('event-delete');
|
setWorkFlow(workflowType: WORKFLOW_TYPE) {
|
||||||
break;
|
console.info('set workflow to', workflowType);
|
||||||
|
this.workflow.set(workflowType);
|
||||||
case 'event_cancel':
|
|
||||||
this.workflow.set('event-cancel');
|
|
||||||
break;
|
|
||||||
case 'event_edit':
|
|
||||||
this.workflow.set('event-edit');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this.workflow.set(action);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// This function is passed into the child
|
|
||||||
handleAction = (msg: string) => {
|
|
||||||
console.log('Parent received:', msg);
|
|
||||||
if (msg == 'close') {
|
|
||||||
this.closeDialog();
|
|
||||||
} else if ( msg == 'save-event-success'){
|
|
||||||
this.closeDialog();
|
|
||||||
this.calendarComponent?.getApi().refetchEvents();
|
|
||||||
}else{
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DialogConfig {
|
export interface DialogConfig {
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
<p>single-event-booking-cancel works!</p>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { Component, inject, input } from '@angular/core';
|
||||||
|
import { EventBusService } from '../../../../../services/event-bus.service';
|
||||||
|
import { CalendarEventDto } from '../../../models/events-in-range-dto.model';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-single-event-booking-cancel',
|
||||||
|
imports: [],
|
||||||
|
templateUrl: './single-event-booking-cancel.html',
|
||||||
|
styleUrl: './single-event-booking-cancel.css',
|
||||||
|
})
|
||||||
|
export class SingleEventBookingCancel {
|
||||||
|
eventBus = inject(EventBusService);
|
||||||
|
event = input<CalendarEventDto>();
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<p>single-event-booking-create works!</p>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { Component, inject, input } from '@angular/core';
|
||||||
|
import { CalendarEventDto } from '../../../models/events-in-range-dto.model';
|
||||||
|
import { EventBusService } from '../../../../../services/event-bus.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-single-event-booking-create',
|
||||||
|
imports: [],
|
||||||
|
templateUrl: './single-event-booking-create.html',
|
||||||
|
styleUrl: './single-event-booking-create.css',
|
||||||
|
})
|
||||||
|
export class SingleEventBookingCreate {
|
||||||
|
eventBus = inject(EventBusService);
|
||||||
|
event = input<CalendarEventDto>();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<p>single-event-booking-list works!</p>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { Component, inject, input } from '@angular/core';
|
||||||
|
import { EventBusService } from '../../../../../services/event-bus.service';
|
||||||
|
import { CalendarEventDto } from '../../../models/events-in-range-dto.model';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-single-event-booking-list',
|
||||||
|
imports: [],
|
||||||
|
templateUrl: './single-event-booking-list.html',
|
||||||
|
styleUrl: './single-event-booking-list.css',
|
||||||
|
})
|
||||||
|
export class SingleEventBookingList {
|
||||||
|
eventBus = inject(EventBusService);
|
||||||
|
event = input<CalendarEventDto>();
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Component, inject, input } from '@angular/core';
|
import { Component, inject, input } from '@angular/core';
|
||||||
import { CalendarEventDto, EventExceptionDto } from '../../../models/events-in-range-dto.model';
|
import { CalendarEventDto } from '../../../models/events-in-range-dto.model';
|
||||||
import {
|
import {
|
||||||
SingleEventDashboardEventDetailsView,
|
SingleEventDashboardEventDetailsView,
|
||||||
} from '../single-event-dashboard-event-details-view/single-event-dashboard-event-details-view';
|
} from '../single-event-dashboard-event-details-view/single-event-dashboard-event-details-view';
|
||||||
@@ -8,6 +8,8 @@ import { SvgIcons } from '../../../../../svg-icons';
|
|||||||
import { SafeHtmlPipe } from '../../../../../pipes/safe-html-pipe';
|
import { SafeHtmlPipe } from '../../../../../pipes/safe-html-pipe';
|
||||||
import { CalendarService } from '../../../services/calendar.service';
|
import { CalendarService } from '../../../services/calendar.service';
|
||||||
import { CreateExceptionDto } from '../../../models/event-exception.model';
|
import { CreateExceptionDto } from '../../../models/event-exception.model';
|
||||||
|
import { EventBusService } from '../../../../../services/event-bus.service';
|
||||||
|
import { EventType } from '../../../../../../types';
|
||||||
|
|
||||||
export type ACTIVATION_TYPE = 'cancel' | 'activate';
|
export type ACTIVATION_TYPE = 'cancel' | 'activate';
|
||||||
|
|
||||||
@@ -23,11 +25,10 @@ export type ACTIVATION_TYPE = 'cancel' | 'activate';
|
|||||||
})
|
})
|
||||||
export class SingleEventDashboardEventActivation {
|
export class SingleEventDashboardEventActivation {
|
||||||
|
|
||||||
|
eventBus = inject(EventBusService);
|
||||||
mode = input<ACTIVATION_TYPE>('cancel');
|
mode = input<ACTIVATION_TYPE>('cancel');
|
||||||
calendarService = inject(CalendarService);
|
calendarService = inject(CalendarService);
|
||||||
event = input<CalendarEventDto>();
|
event = input<CalendarEventDto>();
|
||||||
onAction = input.required<(msg: string) => void>();
|
|
||||||
|
|
||||||
protected readonly SvgIcons = SvgIcons;
|
protected readonly SvgIcons = SvgIcons;
|
||||||
|
|
||||||
|
|
||||||
@@ -37,7 +38,7 @@ export class SingleEventDashboardEventActivation {
|
|||||||
console.info('setEventOccurrenceActivation', event);
|
console.info('setEventOccurrenceActivation', event);
|
||||||
const eventId = this.event()?.id!;
|
const eventId = this.event()?.id!;
|
||||||
const startTime = this.event()?.startTime!;
|
const startTime = this.event()?.startTime!;
|
||||||
let payload: CreateExceptionDto | undefined = undefined;
|
let payload: CreateExceptionDto | undefined;
|
||||||
const eventException = event?.exceptions?.length ? event.exceptions[0] : undefined;
|
const eventException = event?.exceptions?.length ? event.exceptions[0] : undefined;
|
||||||
if (eventException) {
|
if (eventException) {
|
||||||
payload = {
|
payload = {
|
||||||
@@ -57,9 +58,10 @@ export class SingleEventDashboardEventActivation {
|
|||||||
this.calendarService.applyException(eventId, payload ).subscribe(
|
this.calendarService.applyException(eventId, payload ).subscribe(
|
||||||
{
|
{
|
||||||
next: () => {
|
next: () => {
|
||||||
this.onAction()('save-event-success');
|
this.eventBus.emit(EventType.CALENDAR_VIEW_EVENT_SAVED, 'Event saved')
|
||||||
},
|
},
|
||||||
error: err => {
|
error: err => {
|
||||||
|
console.error(err);
|
||||||
alert('Failed to change event');
|
alert('Failed to change event');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -75,7 +77,7 @@ export class SingleEventDashboardEventActivation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected closeDialog() {
|
protected closeDialog() {
|
||||||
this.onAction()('close');
|
this.eventBus.emit(EventType.CALENDAR_VIEW_DIALOG_CLOSED, 'Event saved')
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
<h2 class="text-2xl">Törlés</h2>
|
<h2 class="text-2xl">Törlés</h2>
|
||||||
<p>Esemény és az egész széria törlése</p>
|
<p>Esemény és az egész széria törlése</p>
|
||||||
@if (config) {
|
<app-single-event-dashboard-event-details-view [event]="event()">
|
||||||
<app-detail-view
|
</app-single-event-dashboard-event-details-view>
|
||||||
[config]="config"
|
|
||||||
></app-detail-view>
|
|
||||||
}
|
|
||||||
|
|
||||||
<div class="flex gap-2 mt-3">
|
<div class="flex gap-2 mt-3">
|
||||||
<rs-daisy-button variant="error" (click)="doDelete()">
|
<rs-daisy-button variant="error" (click)="deleteEventTemplate()">
|
||||||
<span [outerHTML]="SvgIcons.heroTrash | safeHtml"></span>
|
<span [outerHTML]="SvgIcons.heroTrash | safeHtml"></span>
|
||||||
Törlés
|
Törlés
|
||||||
</rs-daisy-button>
|
</rs-daisy-button>
|
||||||
<rs-daisy-button variant="primary" (click)="triggerAction()">
|
<rs-daisy-button variant="primary" (click)="closeDialog()">
|
||||||
<span [outerHTML]="SvgIcons.heroXcircle | safeHtml"></span>
|
<span [outerHTML]="SvgIcons.heroXcircle | safeHtml"></span>
|
||||||
Mégsem
|
Mégsem
|
||||||
</rs-daisy-button>
|
</rs-daisy-button>
|
||||||
|
|||||||
@@ -1,76 +1,41 @@
|
|||||||
import { Component, effect, inject, input, output } from '@angular/core';
|
import { Component, inject, input } from '@angular/core';
|
||||||
import { CalendarEventDto } from '../../../models/events-in-range-dto.model';
|
import { CalendarEventDto } from '../../../models/events-in-range-dto.model';
|
||||||
import { DetailView, DetailViewConfig } from '../../../../../components/detail-view/detail-view';
|
|
||||||
import { SvgIcons } from '../../../../../svg-icons';
|
import { SvgIcons } from '../../../../../svg-icons';
|
||||||
import { SafeHtmlPipe } from '../../../../../pipes/safe-html-pipe';
|
import { SafeHtmlPipe } from '../../../../../pipes/safe-html-pipe';
|
||||||
import { Button } from '@rschneider/ng-daisyui';
|
import { Button } from '@rschneider/ng-daisyui';
|
||||||
import { CalendarService } from '../../../services/calendar.service';
|
import { CalendarService } from '../../../services/calendar.service';
|
||||||
|
import { EventBusService } from '../../../../../services/event-bus.service';
|
||||||
|
import {
|
||||||
|
SingleEventDashboardEventDetailsView
|
||||||
|
} from '../single-event-dashboard-event-details-view/single-event-dashboard-event-details-view';
|
||||||
|
import { EventType } from '../../../../../../types';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-single-event-dashboard-event-delete',
|
selector: 'app-single-event-dashboard-event-delete',
|
||||||
imports: [
|
imports: [
|
||||||
|
|
||||||
DetailView,
|
|
||||||
SafeHtmlPipe,
|
SafeHtmlPipe,
|
||||||
Button,
|
Button,
|
||||||
|
SingleEventDashboardEventDetailsView,
|
||||||
],
|
],
|
||||||
templateUrl: './single-event-dashboard-event-delete.html',
|
templateUrl: './single-event-dashboard-event-delete.html',
|
||||||
styleUrl: './single-event-dashboard-event-delete.css',
|
styleUrl: './single-event-dashboard-event-delete.css',
|
||||||
})
|
})
|
||||||
export class SingleEventDashboardEventDelete {
|
export class SingleEventDashboardEventDelete {
|
||||||
|
|
||||||
|
eventBus = inject(EventBusService);
|
||||||
calendarService = inject(CalendarService);
|
calendarService = inject(CalendarService);
|
||||||
event = input<CalendarEventDto>();
|
event = input<CalendarEventDto>();
|
||||||
// Define an input that expects a function
|
|
||||||
onAction = input.required<(msg: string) => void>();
|
|
||||||
config: DetailViewConfig<CalendarEventDto> | undefined;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
effect(() => {
|
|
||||||
|
|
||||||
this.config = {
|
|
||||||
data: this.event()!,
|
|
||||||
|
|
||||||
rows: [{
|
|
||||||
attribute: 'id',
|
|
||||||
getTitle: 'Esemény azonosító',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
attribute: 'title',
|
|
||||||
getTitle: 'Esemény neve',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
attribute: 'startTime',
|
|
||||||
getTitle: 'Kezdési időpont',
|
|
||||||
format: 'datetime',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
attribute: 'eventType',
|
|
||||||
getTitle: 'Esemény típusa',
|
|
||||||
getValue: obj => obj.eventType.name,
|
|
||||||
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
triggerAction() {
|
|
||||||
// Call the function passed from the parent
|
|
||||||
this.onAction()('close');
|
|
||||||
}
|
|
||||||
|
|
||||||
doDelete() {
|
|
||||||
this.calendarService.deleteEvent(this.event()!.id).subscribe(
|
|
||||||
{
|
|
||||||
next: ( ) => {
|
|
||||||
this.onAction()('save-event-success');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
// Call the function passed from the parent
|
|
||||||
}
|
|
||||||
|
|
||||||
protected readonly SvgIcons = SvgIcons;
|
protected readonly SvgIcons = SvgIcons;
|
||||||
|
|
||||||
|
closeDialog() {
|
||||||
|
// Call the function passed from the parent
|
||||||
|
this.eventBus.emit(EventType.CALENDAR_VIEW_DIALOG_CLOSED, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected deleteEventTemplate() {
|
||||||
|
this.calendarService.delete(this.event()!.id).subscribe(() => this.eventBus.emit(EventType.CALENDAR_VIEW_EVENT_SAVED, 'Event saved'))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
<app-create-event-form (ready)="triggerAction($event)" [date]="selectedDate()"
|
<app-create-event-form
|
||||||
|
(ready)="triggerAction($event)"
|
||||||
[id]="event()?.id"></app-create-event-form>
|
[date]="selectedDate()"
|
||||||
|
[id]="event()?.id">
|
||||||
|
</app-create-event-form>
|
||||||
|
|||||||
@@ -1,31 +1,33 @@
|
|||||||
import { Component, input, signal } from '@angular/core';
|
import { Component, inject, input, signal } from '@angular/core';
|
||||||
import { CalendarEventDto } from '../../../models/events-in-range-dto.model';
|
import { CalendarEventDto } from '../../../models/events-in-range-dto.model';
|
||||||
import { SvgIcons } from '../../../../../svg-icons';
|
import { CreateEventForm, ReadyType } from '../../create-event-form/create-event-form';
|
||||||
import { CreateEventForm } from '../../create-event-form/create-event-form';
|
import { EventBusService } from '../../../../../services/event-bus.service';
|
||||||
import { Modal } from '@rschneider/ng-daisyui';
|
import { EventType } from '../../../../../../types';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-single-event-dashboard-event-edit',
|
selector: 'app-single-event-dashboard-event-edit',
|
||||||
imports: [
|
imports: [
|
||||||
CreateEventForm,
|
CreateEventForm,
|
||||||
Modal,
|
|
||||||
],
|
],
|
||||||
templateUrl: './single-event-dashboard-event-edit.html',
|
templateUrl: './single-event-dashboard-event-edit.html',
|
||||||
styleUrl: './single-event-dashboard-event-edit.css',
|
styleUrl: './single-event-dashboard-event-edit.css',
|
||||||
})
|
})
|
||||||
export class SingleEventDashboardEventEdit {
|
export class SingleEventDashboardEventEdit {
|
||||||
|
|
||||||
|
eventBus = inject(EventBusService);
|
||||||
selectedDate = signal<Date | undefined>(undefined);
|
selectedDate = signal<Date | undefined>(undefined);
|
||||||
event = input<CalendarEventDto>();
|
event = input<CalendarEventDto>();
|
||||||
onAction = input.required<(msg: string) => void>();
|
|
||||||
|
|
||||||
protected readonly SvgIcons = SvgIcons;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* proxy to ready event from form to parent
|
* proxy to ready event from form to parent
|
||||||
*/
|
*/
|
||||||
protected triggerAction(action: string) {
|
protected triggerAction(action: ReadyType) {
|
||||||
console.info("event details dashboard", action)
|
if ( action == 'save-event-success'){
|
||||||
this.onAction()(action)
|
this.eventBus.emit(EventType.CALENDAR_VIEW_EVENT_SAVED,'');
|
||||||
|
}else if ( action == 'save-event-failed'){
|
||||||
|
this.eventBus.emit(EventType.CALENDAR_VIEW_DIALOG_CLOSED,'');
|
||||||
|
}else{
|
||||||
|
this.eventBus.emit(EventType.CALENDAR_VIEW_DIALOG_CLOSED,'');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,8 @@
|
|||||||
@if (!event()?.isRecurring) {
|
@if (!event()?.isRecurring) {
|
||||||
<h2 class="text-xl">Esemény </h2>
|
<h2 class="text-xl">Esemény </h2>
|
||||||
}
|
}
|
||||||
@if (config) {
|
<app-single-event-dashboard-event-details-view [event]="event()">
|
||||||
<app-detail-view
|
</app-single-event-dashboard-event-details-view>
|
||||||
[config]="config"
|
|
||||||
></app-detail-view>
|
|
||||||
}
|
|
||||||
|
|
||||||
<div class="flex mt-3 gap-2 flex-wrap content-stretch ">
|
<div class="flex mt-3 gap-2 flex-wrap content-stretch ">
|
||||||
@for (card of cards(); let i = $index; track i) {
|
@for (card of cards(); let i = $index; track i) {
|
||||||
@@ -19,7 +16,7 @@
|
|||||||
[description]="card.description"
|
[description]="card.description"
|
||||||
[buttonTitle]="card.buttonTitle"
|
[buttonTitle]="card.buttonTitle"
|
||||||
[styleClass]="'flex-1'"
|
[styleClass]="'flex-1'"
|
||||||
(onAction)="onCardAction(card.action)"
|
(onAction)="onCardAction(card.workflow)"
|
||||||
|
|
||||||
></app-single-event-dashboard-card>
|
></app-single-event-dashboard-card>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,17 @@ import { CalendarEventDto } from '../../../models/events-in-range-dto.model';
|
|||||||
import { DetailView, DetailViewConfig } from '../../../../../components/detail-view/detail-view';
|
import { DetailView, DetailViewConfig } from '../../../../../components/detail-view/detail-view';
|
||||||
import { SvgIcons } from '../../../../../svg-icons';
|
import { SvgIcons } from '../../../../../svg-icons';
|
||||||
import { SingleEventDashboardCard } from '../single-event-dashboard-card/single-event-dashboard-card';
|
import { SingleEventDashboardCard } from '../single-event-dashboard-card/single-event-dashboard-card';
|
||||||
|
import { WORKFLOW_TYPE } from '../calendar-view';
|
||||||
|
import {
|
||||||
|
SingleEventDashboardEventDetailsView
|
||||||
|
} from '../single-event-dashboard-event-details-view/single-event-dashboard-event-details-view';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-single-event-dashboard',
|
selector: 'app-single-event-dashboard',
|
||||||
imports: [
|
imports: [
|
||||||
DetailView,
|
DetailView,
|
||||||
SingleEventDashboardCard,
|
SingleEventDashboardCard,
|
||||||
|
SingleEventDashboardEventDetailsView,
|
||||||
],
|
],
|
||||||
templateUrl: './single-event-dashboard.html',
|
templateUrl: './single-event-dashboard.html',
|
||||||
styleUrl: './single-event-dashboard.css',
|
styleUrl: './single-event-dashboard.css',
|
||||||
@@ -16,49 +21,20 @@ import { SingleEventDashboardCard } from '../single-event-dashboard-card/single-
|
|||||||
export class SingleEventDashboard {
|
export class SingleEventDashboard {
|
||||||
|
|
||||||
event = input<CalendarEventDto>();
|
event = input<CalendarEventDto>();
|
||||||
action = output<string>();
|
action = output<WORKFLOW_TYPE>();
|
||||||
config: DetailViewConfig<CalendarEventDto> | undefined;
|
|
||||||
|
|
||||||
cards = signal<CardConfig[]>([]);
|
cards = signal<CardConfig[]>([]);
|
||||||
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
||||||
|
|
||||||
effect(() => {
|
effect(() => {
|
||||||
console.info("dashboard", this.event());
|
|
||||||
this.config = {
|
|
||||||
data: this.event()!,
|
|
||||||
|
|
||||||
rows: [{
|
|
||||||
attribute: 'id',
|
|
||||||
getTitle: 'Esemény azonosító',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
attribute: 'title',
|
|
||||||
getTitle: 'Esemény neve',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
attribute: 'startTime',
|
|
||||||
getTitle: 'Kezdési időpont',
|
|
||||||
format: 'datetime',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
attribute: 'eventType',
|
|
||||||
getTitle: 'Esemény típusa',
|
|
||||||
getValue: obj => obj.eventType.name,
|
|
||||||
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
this.cards.set( [
|
this.cards.set( [
|
||||||
{
|
{
|
||||||
buttonTitle: 'Szerkesztés',
|
buttonTitle: 'Szerkesztés',
|
||||||
title: 'Szerkesztés',
|
title: 'Szerkesztés',
|
||||||
svgIcon: SvgIcons.heorPencilSquare,
|
svgIcon: SvgIcons.heorPencilSquare,
|
||||||
description: 'Az esemény módosítása',
|
description: 'Az esemény módosítása',
|
||||||
action: 'event_edit',
|
workflow: 'event_edit',
|
||||||
},
|
},
|
||||||
|
|
||||||
this.event()?.isCancelled ?
|
this.event()?.isCancelled ?
|
||||||
@@ -68,48 +44,48 @@ export class SingleEventDashboard {
|
|||||||
title: 'Előfordulás aktiválása',
|
title: 'Előfordulás aktiválása',
|
||||||
svgIcon: SvgIcons.heroPlay,
|
svgIcon: SvgIcons.heroPlay,
|
||||||
description: 'Az esemény ezen előfordulásának aktiválása',
|
description: 'Az esemény ezen előfordulásának aktiválása',
|
||||||
action: 'event_activate',
|
workflow: 'event_activate',
|
||||||
} :
|
} :
|
||||||
{
|
{
|
||||||
buttonTitle: 'Lemondás',
|
buttonTitle: 'Lemondás',
|
||||||
title: 'Előfordulás lemondása',
|
title: 'Előfordulás lemondása',
|
||||||
svgIcon: SvgIcons.heroXcircle,
|
svgIcon: SvgIcons.heroXcircle,
|
||||||
description: 'Az esemény ezen előfordulásának lemondása',
|
description: 'Az esemény ezen előfordulásának lemondása',
|
||||||
action: 'event_cancel',
|
workflow: 'event_cancel',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
buttonTitle: 'Törlés',
|
buttonTitle: 'Törlés',
|
||||||
title: 'Esemény törlése',
|
title: 'Esemény törlése',
|
||||||
svgIcon: SvgIcons.heroTrash,
|
svgIcon: SvgIcons.heroTrash,
|
||||||
description: 'Az esemény törlése',
|
description: 'Az esemény törlése',
|
||||||
action: 'event_delete',
|
workflow: 'event_delete',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
buttonTitle: 'Megnézem',
|
buttonTitle: 'Megnézem',
|
||||||
title: 'Foglalások',
|
title: 'Foglalások',
|
||||||
svgIcon: SvgIcons.heroUserGroup,
|
svgIcon: SvgIcons.heroUserGroup,
|
||||||
description: 'Foglalások megtekintése',
|
description: 'Foglalások megtekintése',
|
||||||
action: 'event_bookings',
|
workflow: 'booking_list',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
buttonTitle: 'Bejelentkezés',
|
buttonTitle: 'Bejelentkezés',
|
||||||
title: 'Időpont foglalás',
|
title: 'Időpont foglalás',
|
||||||
svgIcon: SvgIcons.heroUserPlus,
|
svgIcon: SvgIcons.heroUserPlus,
|
||||||
description: 'Időpont foglalása eseményre',
|
description: 'Időpont foglalása eseményre',
|
||||||
action: 'user_booking',
|
workflow: 'booking_create',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
buttonTitle: 'Lemondás',
|
buttonTitle: 'Lemondás',
|
||||||
title: 'Lemondás',
|
title: 'Lemondás',
|
||||||
svgIcon: SvgIcons.heroUserMinus,
|
svgIcon: SvgIcons.heroUserMinus,
|
||||||
description: 'Az időpont lemondása',
|
description: 'Az időpont lemondása',
|
||||||
action: 'user_cancel',
|
workflow: 'booking_cancel',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onCardAction (action: string|undefined) {
|
onCardAction (action: WORKFLOW_TYPE) {
|
||||||
if ( action ){
|
if ( action ){
|
||||||
this.action.emit(action);
|
this.action.emit(action);
|
||||||
}
|
}
|
||||||
@@ -125,5 +101,5 @@ export interface CardConfig {
|
|||||||
description?: string;
|
description?: string;
|
||||||
buttonTitle?: string;
|
buttonTitle?: string;
|
||||||
svgIcon?: string;
|
svgIcon?: string;
|
||||||
action?: string;
|
workflow: WORKFLOW_TYPE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export type FrequencyOption = {
|
|||||||
frequency: Frequency,
|
frequency: Frequency,
|
||||||
label: string;
|
label: string;
|
||||||
}
|
}
|
||||||
|
export type ReadyType = 'close' | 'save-event-success' | 'save-event-failed';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-create-event-form',
|
selector: 'app-create-event-form',
|
||||||
@@ -28,7 +29,7 @@ export type FrequencyOption = {
|
|||||||
export class CreateEventForm implements OnInit {
|
export class CreateEventForm implements OnInit {
|
||||||
form: FormGroup;
|
form: FormGroup;
|
||||||
isEditMode = false;
|
isEditMode = false;
|
||||||
ready = output<string>();
|
ready = output<ReadyType>();
|
||||||
id = input<number | undefined>();
|
id = input<number | undefined>();
|
||||||
date = input<Date | undefined>();
|
date = input<Date | undefined>();
|
||||||
eventTypes = signal<EventType[]>([]);
|
eventTypes = signal<EventType[]>([]);
|
||||||
|
|||||||
@@ -9,18 +9,17 @@ import { CreateExceptionDto } from '../models/event-exception.model';
|
|||||||
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class CalendarService {
|
export class CalendarService {
|
||||||
private readonly apiUrl: string;
|
private readonly apiUrl: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private http: HttpClient,
|
private http: HttpClient,
|
||||||
private configService: ConfigurationService,
|
private configService: ConfigurationService
|
||||||
) {
|
) {
|
||||||
this.apiUrl = `${this.configService.getApiUrl()}/calendar`;
|
this.apiUrl = `${this.configService.getApiUrl()}/calendar`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get events in range
|
* get events in range
|
||||||
*/
|
*/
|
||||||
@@ -28,26 +27,24 @@ export class CalendarService {
|
|||||||
const params = new HttpParams()
|
const params = new HttpParams()
|
||||||
.set('startDate', eventsInRangeDto.startTime!.toISOString())
|
.set('startDate', eventsInRangeDto.startTime!.toISOString())
|
||||||
.set('endDate', eventsInRangeDto.endTime!.toISOString());
|
.set('endDate', eventsInRangeDto.endTime!.toISOString());
|
||||||
return this.http.get<CalendarEventDto[]>(this.apiUrl + '', { params });
|
return this.http.get<CalendarEventDto[]>(this.apiUrl+'', { params });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new record.
|
* Create a new record.
|
||||||
*/
|
*/
|
||||||
public create(data: EventFormDTO): Observable<Event> {
|
public create(data: EventFormDTO): Observable<Event> {
|
||||||
return this.http.post<Event>(this.apiUrl + '/events', data);
|
return this.http.post<Event>(this.apiUrl+'/events', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public update(id: number, data: UpdateEventFormDTO): Observable<Event> {
|
public update(id: number,data: UpdateEventFormDTO): Observable<Event> {
|
||||||
return this.http.patch<Event>(this.apiUrl + '/events/' + id, data);
|
return this.http.patch<Event>(this.apiUrl+'/events/'+id, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public applyException(eventId: number, eventException: CreateExceptionDto) {
|
public applyException(eventId: number, eventException: CreateExceptionDto){
|
||||||
return this.http.post(this.apiUrl + `/events/${eventId}/exceptions`, eventException);
|
return this.http.post(this.apiUrl+`/events/${eventId}/exceptions`, eventException);
|
||||||
}
|
}
|
||||||
|
public delete(eventId: number){
|
||||||
public deleteEvent(id: number): Observable<void> {
|
return this.http.delete(this.apiUrl+`/events/${eventId}` );
|
||||||
return this.http.delete<void>(this.apiUrl + '/events/' + id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
29
admin/src/app/services/event-bus.service.ts
Normal file
29
admin/src/app/services/event-bus.service.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Subject, Observable } from 'rxjs';
|
||||||
|
import { filter, map } from 'rxjs/operators';
|
||||||
|
import { AppEvent, EventMap } from '../../types';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root' // Makes this a singleton available everywhere
|
||||||
|
})
|
||||||
|
export class EventBusService {
|
||||||
|
private subject$ = new Subject<AppEvent>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Publish an event to the bus
|
||||||
|
*/
|
||||||
|
emit<K extends keyof EventMap>(type: K, payload: EventMap[K]): void {
|
||||||
|
this.subject$.next({ type, payload });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribe to specific event types
|
||||||
|
* Returns an Observable of the payload
|
||||||
|
*/
|
||||||
|
on<K extends keyof EventMap>(type: K): Observable<EventMap[K]> {
|
||||||
|
return this.subject$.pipe(
|
||||||
|
filter(e => e.type === type),
|
||||||
|
map(e => e.payload)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,3 +14,24 @@ export interface PaginatedResponse<T> {
|
|||||||
currentPage: number;
|
currentPage: number;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
export enum EventType {
|
||||||
|
USER_LOGGED_IN = 'USER_LOGGED_IN',
|
||||||
|
ORDER_CREATED = 'ORDER_CREATED',
|
||||||
|
THEME_CHANGED = 'THEME_CHANGED',
|
||||||
|
CALENDAR_VIEW_EVENT_SAVED = 'CALENDAR_VIEW_EVENT_SAVED',
|
||||||
|
CALENDAR_VIEW_DIALOG_CLOSED = 'CALENDAR_VIEW_DIALOG_CLOSED'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface EventMap {
|
||||||
|
[EventType.USER_LOGGED_IN]: { id: number; name: string };
|
||||||
|
[EventType.THEME_CHANGED]: string;
|
||||||
|
[EventType.ORDER_CREATED]: { orderId: number };
|
||||||
|
[EventType.CALENDAR_VIEW_EVENT_SAVED]: string;
|
||||||
|
[EventType.CALENDAR_VIEW_DIALOG_CLOSED]: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AppEvent<T = any> {
|
||||||
|
type: EventType;
|
||||||
|
payload?: T;
|
||||||
|
}
|
||||||
|
|||||||
32
server/package-lock.json
generated
32
server/package-lock.json
generated
@@ -16,7 +16,7 @@
|
|||||||
"@nestjs/mapped-types": "^2.1.0",
|
"@nestjs/mapped-types": "^2.1.0",
|
||||||
"@nestjs/passport": "^11.0.5",
|
"@nestjs/passport": "^11.0.5",
|
||||||
"@nestjs/platform-express": "^11.0.1",
|
"@nestjs/platform-express": "^11.0.1",
|
||||||
"@nestjs/swagger": "^11.2.1",
|
"@nestjs/swagger": "^11.2.3",
|
||||||
"@nestjs/typeorm": "^11.0.0",
|
"@nestjs/typeorm": "^11.0.0",
|
||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
@@ -2109,9 +2109,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@microsoft/tsdoc": {
|
"node_modules/@microsoft/tsdoc": {
|
||||||
"version": "0.15.1",
|
"version": "0.16.0",
|
||||||
"resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz",
|
"resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.16.0.tgz",
|
||||||
"integrity": "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==",
|
"integrity": "sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@napi-rs/wasm-runtime": {
|
"node_modules/@napi-rs/wasm-runtime": {
|
||||||
@@ -2620,17 +2620,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@nestjs/swagger": {
|
"node_modules/@nestjs/swagger": {
|
||||||
"version": "11.2.1",
|
"version": "11.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-11.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-11.2.3.tgz",
|
||||||
"integrity": "sha512-1MS7xf0pzc1mofG53xrrtrurnziafPUHkqzRm4YUVPA/egeiMaSerQBD/feiAeQ2BnX0WiLsTX4HQFO0icvOjQ==",
|
"integrity": "sha512-a0xFfjeqk69uHIUpP8u0ryn4cKuHdra2Ug96L858i0N200Hxho+n3j+TlQXyOF4EstLSGjTfxI1Xb2E1lUxeNg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@microsoft/tsdoc": "0.15.1",
|
"@microsoft/tsdoc": "0.16.0",
|
||||||
"@nestjs/mapped-types": "2.1.0",
|
"@nestjs/mapped-types": "2.1.0",
|
||||||
"js-yaml": "4.1.0",
|
"js-yaml": "4.1.1",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
"path-to-regexp": "8.3.0",
|
"path-to-regexp": "8.3.0",
|
||||||
"swagger-ui-dist": "5.29.4"
|
"swagger-ui-dist": "5.30.2"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@fastify/static": "^8.0.0",
|
"@fastify/static": "^8.0.0",
|
||||||
@@ -7814,9 +7814,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/js-yaml": {
|
"node_modules/js-yaml": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"argparse": "^2.0.1"
|
"argparse": "^2.0.1"
|
||||||
@@ -10202,9 +10202,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/swagger-ui-dist": {
|
"node_modules/swagger-ui-dist": {
|
||||||
"version": "5.29.4",
|
"version": "5.30.2",
|
||||||
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.29.4.tgz",
|
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.30.2.tgz",
|
||||||
"integrity": "sha512-gJFDz/gyLOCQtWwAgqs6Rk78z9ONnqTnlW11gimG9nLap8drKa3AJBKpzIQMIjl5PD2Ix+Tn+mc/tfoT2tgsng==",
|
"integrity": "sha512-HWCg1DTNE/Nmapt+0m2EPXFwNKNeKK4PwMjkwveN/zn1cV2Kxi9SURd+m0SpdcSgWEK/O64sf8bzXdtUhigtHA==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@scarf/scarf": "=1.4.0"
|
"@scarf/scarf": "=1.4.0"
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
"@nestjs/mapped-types": "^2.1.0",
|
"@nestjs/mapped-types": "^2.1.0",
|
||||||
"@nestjs/passport": "^11.0.5",
|
"@nestjs/passport": "^11.0.5",
|
||||||
"@nestjs/platform-express": "^11.0.1",
|
"@nestjs/platform-express": "^11.0.1",
|
||||||
"@nestjs/swagger": "^11.2.1",
|
"@nestjs/swagger": "^11.2.3",
|
||||||
"@nestjs/typeorm": "^11.0.0",
|
"@nestjs/typeorm": "^11.0.0",
|
||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
Query,
|
Query,
|
||||||
ParseIntPipe,
|
ParseIntPipe,
|
||||||
UseGuards,
|
UseGuards,
|
||||||
|
ValidationPipe,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { CalendarService } from './calendar.service';
|
import { CalendarService } from './calendar.service';
|
||||||
import { GetCalendarDto } from './dto/get-calendar.dto';
|
import { GetCalendarDto } from './dto/get-calendar.dto';
|
||||||
@@ -18,8 +19,9 @@ import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
|||||||
import { RolesGuard } from '../auth/roles.guard';
|
import { RolesGuard } from '../auth/roles.guard';
|
||||||
import { Roles } from '../auth/roles.decorator';
|
import { Roles } from '../auth/roles.decorator';
|
||||||
import { Role } from '../auth/role.enum';
|
import { Role } from '../auth/role.enum';
|
||||||
import { CreateBookingDto } from './dto/create-booking.dto';
|
import { CalendarCreateBookingDto } from './dto/create-booking.dto';
|
||||||
import { CancelBookingDto } from './dto/cancel-booking.dto';
|
import { CancelBookingDto } from './dto/cancel-booking.dto';
|
||||||
|
import { ApiBody } from '@nestjs/swagger';
|
||||||
|
|
||||||
@Controller('calendar')
|
@Controller('calendar')
|
||||||
@UseGuards(JwtAuthGuard, RolesGuard)
|
@UseGuards(JwtAuthGuard, RolesGuard)
|
||||||
@@ -73,9 +75,10 @@ export class CalendarController {
|
|||||||
|
|
||||||
// Create a booking for a specific event occurrence
|
// Create a booking for a specific event occurrence
|
||||||
@Post('events/:id/bookings')
|
@Post('events/:id/bookings')
|
||||||
|
@ApiBody({ type: CalendarCreateBookingDto })
|
||||||
createBooking(
|
createBooking(
|
||||||
@Param('id', ParseIntPipe) eventId: number,
|
@Param('id', ParseIntPipe) eventId: number,
|
||||||
@Body() createBookingDto: CreateBookingDto,
|
@Body(new ValidationPipe()) createBookingDto: CalendarCreateBookingDto,
|
||||||
) {
|
) {
|
||||||
return this.calendarService.createBooking(eventId, createBookingDto);
|
return this.calendarService.createBooking(eventId, createBookingDto);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { CreateEventDto } from './dto/create-event.dto';
|
|||||||
import { CreateExceptionDto } from './dto/create-exception.dto';
|
import { CreateExceptionDto } from './dto/create-exception.dto';
|
||||||
import { Booking } from '../entity/booking.entity';
|
import { Booking } from '../entity/booking.entity';
|
||||||
import { CancelBookingDto } from './dto/cancel-booking.dto';
|
import { CancelBookingDto } from './dto/cancel-booking.dto';
|
||||||
import { CreateBookingDto } from './dto/create-booking.dto';
|
import { CalendarCreateBookingDto } from './dto/create-booking.dto';
|
||||||
import { EventType } from '../entity/event-type.entity';
|
import { EventType } from '../entity/event-type.entity';
|
||||||
|
|
||||||
// --- Type-Safe Maps ---
|
// --- Type-Safe Maps ---
|
||||||
@@ -520,7 +520,7 @@ export class CalendarService {
|
|||||||
|
|
||||||
async createBooking(
|
async createBooking(
|
||||||
eventId: number,
|
eventId: number,
|
||||||
createBookingDto: CreateBookingDto,
|
createBookingDto: CalendarCreateBookingDto,
|
||||||
): Promise<Booking> {
|
): Promise<Booking> {
|
||||||
const { occurrenceStartTime, userId } = createBookingDto;
|
const { occurrenceStartTime, userId } = createBookingDto;
|
||||||
|
|
||||||
|
|||||||
@@ -7,23 +7,28 @@ import {
|
|||||||
Min,
|
Min,
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
|
||||||
export class CreateBookingDto {
|
export class CalendarCreateBookingDto {
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@Type(() => Date)
|
@Type(() => Date)
|
||||||
@IsDate()
|
@IsDate()
|
||||||
|
@ApiProperty()
|
||||||
occurrenceStartTime: Date;
|
occurrenceStartTime: Date;
|
||||||
|
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@IsInt()
|
@IsInt()
|
||||||
|
@ApiProperty()
|
||||||
userId: number; // Replaces userName/userEmail
|
userId: number; // Replaces userName/userEmail
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(1)
|
@Min(1)
|
||||||
|
@ApiProperty()
|
||||||
reservedSeatsCount?: number = 1;
|
reservedSeatsCount?: number = 1;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
|
@ApiProperty()
|
||||||
notes?: string;
|
notes?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user