@for (card of cards(); let i = $index; track i) {
@@ -19,7 +16,7 @@
[description]="card.description"
[buttonTitle]="card.buttonTitle"
[styleClass]="'flex-1'"
- (onAction)="onCardAction(card.action)"
+ (onAction)="onCardAction(card.workflow)"
>
}
diff --git a/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard/single-event-dashboard.ts b/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard/single-event-dashboard.ts
index 27f8307..9546bc7 100644
--- a/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard/single-event-dashboard.ts
+++ b/admin/src/app/features/calendar/components/calendar-view/single-event-dashboard/single-event-dashboard.ts
@@ -3,12 +3,17 @@ import { CalendarEventDto } from '../../../models/events-in-range-dto.model';
import { DetailView, DetailViewConfig } from '../../../../../components/detail-view/detail-view';
import { SvgIcons } from '../../../../../svg-icons';
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({
selector: 'app-single-event-dashboard',
imports: [
DetailView,
SingleEventDashboardCard,
+ SingleEventDashboardEventDetailsView,
],
templateUrl: './single-event-dashboard.html',
styleUrl: './single-event-dashboard.css',
@@ -16,49 +21,20 @@ import { SingleEventDashboardCard } from '../single-event-dashboard-card/single-
export class SingleEventDashboard {
event = input
();
- action = output();
- config: DetailViewConfig | undefined;
-
+ action = output();
cards = signal([]);
constructor() {
-
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( [
{
buttonTitle: 'Szerkesztés',
title: 'Szerkesztés',
svgIcon: SvgIcons.heorPencilSquare,
description: 'Az esemény módosítása',
- action: 'event_edit',
+ workflow: 'event_edit',
},
this.event()?.isCancelled ?
@@ -68,48 +44,48 @@ export class SingleEventDashboard {
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',
+ workflow: 'event_activate',
} :
{
buttonTitle: 'Lemondás',
title: 'Előfordulás lemondása',
svgIcon: SvgIcons.heroXcircle,
description: 'Az esemény ezen előfordulásának lemondása',
- action: 'event_cancel',
+ workflow: 'event_cancel',
},
{
buttonTitle: 'Törlés',
title: 'Esemény törlése',
svgIcon: SvgIcons.heroTrash,
description: 'Az esemény törlése',
- action: 'event_delete',
+ workflow: 'event_delete',
},
{
buttonTitle: 'Megnézem',
title: 'Foglalások',
svgIcon: SvgIcons.heroUserGroup,
description: 'Foglalások megtekintése',
- action: 'event_bookings',
+ workflow: 'booking_list',
},
{
buttonTitle: 'Bejelentkezés',
title: 'Időpont foglalás',
svgIcon: SvgIcons.heroUserPlus,
description: 'Időpont foglalása eseményre',
- action: 'user_booking',
+ workflow: 'booking_create',
},
{
buttonTitle: 'Lemondás',
title: 'Lemondás',
svgIcon: SvgIcons.heroUserMinus,
description: 'Az időpont lemondása',
- action: 'user_cancel',
+ workflow: 'booking_cancel',
},
]);
});
}
- onCardAction (action: string|undefined) {
+ onCardAction (action: WORKFLOW_TYPE) {
if ( action ){
this.action.emit(action);
}
@@ -125,5 +101,5 @@ export interface CardConfig {
description?: string;
buttonTitle?: string;
svgIcon?: string;
- action?: string;
+ workflow: WORKFLOW_TYPE;
}
diff --git a/admin/src/app/features/calendar/components/create-event-form/create-event-form.ts b/admin/src/app/features/calendar/components/create-event-form/create-event-form.ts
index e6d45be..1e56379 100644
--- a/admin/src/app/features/calendar/components/create-event-form/create-event-form.ts
+++ b/admin/src/app/features/calendar/components/create-event-form/create-event-form.ts
@@ -16,6 +16,7 @@ export type FrequencyOption = {
frequency: Frequency,
label: string;
}
+export type ReadyType = 'close' | 'save-event-success' | 'save-event-failed';
@Component({
selector: 'app-create-event-form',
@@ -28,7 +29,7 @@ export type FrequencyOption = {
export class CreateEventForm implements OnInit {
form: FormGroup;
isEditMode = false;
- ready = output();
+ ready = output();
id = input();
date = input();
eventTypes = signal([]);
diff --git a/admin/src/app/features/calendar/services/calendar.service.ts b/admin/src/app/features/calendar/services/calendar.service.ts
index 1868342..1eb75a0 100644
--- a/admin/src/app/features/calendar/services/calendar.service.ts
+++ b/admin/src/app/features/calendar/services/calendar.service.ts
@@ -9,18 +9,17 @@ import { CreateExceptionDto } from '../models/event-exception.model';
@Injectable({
- providedIn: 'root',
+ providedIn: 'root'
})
export class CalendarService {
private readonly apiUrl: string;
constructor(
private http: HttpClient,
- private configService: ConfigurationService,
+ private configService: ConfigurationService
) {
this.apiUrl = `${this.configService.getApiUrl()}/calendar`;
}
-
/**
* get events in range
*/
@@ -28,26 +27,24 @@ export class CalendarService {
const params = new HttpParams()
.set('startDate', eventsInRangeDto.startTime!.toISOString())
.set('endDate', eventsInRangeDto.endTime!.toISOString());
- return this.http.get(this.apiUrl + '', { params });
+ return this.http.get(this.apiUrl+'', { params });
}
/**
* Create a new record.
*/
public create(data: EventFormDTO): Observable {
- return this.http.post(this.apiUrl + '/events', data);
+ return this.http.post(this.apiUrl+'/events', data);
}
- public update(id: number, data: UpdateEventFormDTO): Observable {
- return this.http.patch(this.apiUrl + '/events/' + id, data);
+ public update(id: number,data: UpdateEventFormDTO): Observable {
+ 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);
+ public applyException(eventId: number, eventException: CreateExceptionDto){
+ return this.http.post(this.apiUrl+`/events/${eventId}/exceptions`, eventException);
}
-
- public deleteEvent(id: number): Observable {
- return this.http.delete(this.apiUrl + '/events/' + id);
+ public delete(eventId: number){
+ return this.http.delete(this.apiUrl+`/events/${eventId}` );
}
-
}
diff --git a/admin/src/app/services/event-bus.service.ts b/admin/src/app/services/event-bus.service.ts
new file mode 100644
index 0000000..22a03ca
--- /dev/null
+++ b/admin/src/app/services/event-bus.service.ts
@@ -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();
+
+ /**
+ * Publish an event to the bus
+ */
+ emit(type: K, payload: EventMap[K]): void {
+ this.subject$.next({ type, payload });
+ }
+
+ /**
+ * Subscribe to specific event types
+ * Returns an Observable of the payload
+ */
+ on(type: K): Observable {
+ return this.subject$.pipe(
+ filter(e => e.type === type),
+ map(e => e.payload)
+ );
+ }
+}
diff --git a/admin/src/types.ts b/admin/src/types.ts
index a46ce60..af49ce6 100644
--- a/admin/src/types.ts
+++ b/admin/src/types.ts
@@ -14,3 +14,24 @@ export interface PaginatedResponse {
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 {
+ type: EventType;
+ payload?: T;
+}
diff --git a/server/package-lock.json b/server/package-lock.json
index 46af3b7..ff253f3 100644
--- a/server/package-lock.json
+++ b/server/package-lock.json
@@ -16,7 +16,7 @@
"@nestjs/mapped-types": "^2.1.0",
"@nestjs/passport": "^11.0.5",
"@nestjs/platform-express": "^11.0.1",
- "@nestjs/swagger": "^11.2.1",
+ "@nestjs/swagger": "^11.2.3",
"@nestjs/typeorm": "^11.0.0",
"bcrypt": "^5.1.1",
"class-transformer": "^0.5.1",
@@ -2109,9 +2109,9 @@
}
},
"node_modules/@microsoft/tsdoc": {
- "version": "0.15.1",
- "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz",
- "integrity": "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==",
+ "version": "0.16.0",
+ "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.16.0.tgz",
+ "integrity": "sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==",
"license": "MIT"
},
"node_modules/@napi-rs/wasm-runtime": {
@@ -2620,17 +2620,17 @@
}
},
"node_modules/@nestjs/swagger": {
- "version": "11.2.1",
- "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-11.2.1.tgz",
- "integrity": "sha512-1MS7xf0pzc1mofG53xrrtrurnziafPUHkqzRm4YUVPA/egeiMaSerQBD/feiAeQ2BnX0WiLsTX4HQFO0icvOjQ==",
+ "version": "11.2.3",
+ "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-11.2.3.tgz",
+ "integrity": "sha512-a0xFfjeqk69uHIUpP8u0ryn4cKuHdra2Ug96L858i0N200Hxho+n3j+TlQXyOF4EstLSGjTfxI1Xb2E1lUxeNg==",
"license": "MIT",
"dependencies": {
- "@microsoft/tsdoc": "0.15.1",
+ "@microsoft/tsdoc": "0.16.0",
"@nestjs/mapped-types": "2.1.0",
- "js-yaml": "4.1.0",
+ "js-yaml": "4.1.1",
"lodash": "4.17.21",
"path-to-regexp": "8.3.0",
- "swagger-ui-dist": "5.29.4"
+ "swagger-ui-dist": "5.30.2"
},
"peerDependencies": {
"@fastify/static": "^8.0.0",
@@ -7814,9 +7814,9 @@
"license": "MIT"
},
"node_modules/js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
@@ -10202,9 +10202,9 @@
}
},
"node_modules/swagger-ui-dist": {
- "version": "5.29.4",
- "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.29.4.tgz",
- "integrity": "sha512-gJFDz/gyLOCQtWwAgqs6Rk78z9ONnqTnlW11gimG9nLap8drKa3AJBKpzIQMIjl5PD2Ix+Tn+mc/tfoT2tgsng==",
+ "version": "5.30.2",
+ "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.30.2.tgz",
+ "integrity": "sha512-HWCg1DTNE/Nmapt+0m2EPXFwNKNeKK4PwMjkwveN/zn1cV2Kxi9SURd+m0SpdcSgWEK/O64sf8bzXdtUhigtHA==",
"license": "Apache-2.0",
"dependencies": {
"@scarf/scarf": "=1.4.0"
diff --git a/server/package.json b/server/package.json
index cbb8e45..9c2cba3 100644
--- a/server/package.json
+++ b/server/package.json
@@ -30,7 +30,7 @@
"@nestjs/mapped-types": "^2.1.0",
"@nestjs/passport": "^11.0.5",
"@nestjs/platform-express": "^11.0.1",
- "@nestjs/swagger": "^11.2.1",
+ "@nestjs/swagger": "^11.2.3",
"@nestjs/typeorm": "^11.0.0",
"bcrypt": "^5.1.1",
"class-transformer": "^0.5.1",
diff --git a/server/src/calendar/calendar.controller.ts b/server/src/calendar/calendar.controller.ts
index e49c820..52b1569 100644
--- a/server/src/calendar/calendar.controller.ts
+++ b/server/src/calendar/calendar.controller.ts
@@ -9,6 +9,7 @@ import {
Query,
ParseIntPipe,
UseGuards,
+ ValidationPipe,
} from '@nestjs/common';
import { CalendarService } from './calendar.service';
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 { Roles } from '../auth/roles.decorator';
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 { ApiBody } from '@nestjs/swagger';
@Controller('calendar')
@UseGuards(JwtAuthGuard, RolesGuard)
@@ -73,9 +75,10 @@ export class CalendarController {
// Create a booking for a specific event occurrence
@Post('events/:id/bookings')
+ @ApiBody({ type: CalendarCreateBookingDto })
createBooking(
@Param('id', ParseIntPipe) eventId: number,
- @Body() createBookingDto: CreateBookingDto,
+ @Body(new ValidationPipe()) createBookingDto: CalendarCreateBookingDto,
) {
return this.calendarService.createBooking(eventId, createBookingDto);
}
diff --git a/server/src/calendar/calendar.service.ts b/server/src/calendar/calendar.service.ts
index 8fc2550..ec61cbc 100644
--- a/server/src/calendar/calendar.service.ts
+++ b/server/src/calendar/calendar.service.ts
@@ -14,7 +14,7 @@ import { CreateEventDto } from './dto/create-event.dto';
import { CreateExceptionDto } from './dto/create-exception.dto';
import { Booking } from '../entity/booking.entity';
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';
// --- Type-Safe Maps ---
@@ -520,7 +520,7 @@ export class CalendarService {
async createBooking(
eventId: number,
- createBookingDto: CreateBookingDto,
+ createBookingDto: CalendarCreateBookingDto,
): Promise {
const { occurrenceStartTime, userId } = createBookingDto;
diff --git a/server/src/calendar/dto/create-booking.dto.ts b/server/src/calendar/dto/create-booking.dto.ts
index e68ee4b..260b3f4 100644
--- a/server/src/calendar/dto/create-booking.dto.ts
+++ b/server/src/calendar/dto/create-booking.dto.ts
@@ -7,23 +7,28 @@ import {
Min,
} from 'class-validator';
import { Type } from 'class-transformer';
+import { ApiProperty } from '@nestjs/swagger';
-export class CreateBookingDto {
+export class CalendarCreateBookingDto {
@IsNotEmpty()
@Type(() => Date)
@IsDate()
+ @ApiProperty()
occurrenceStartTime: Date;
@IsNotEmpty()
@IsInt()
+ @ApiProperty()
userId: number; // Replaces userName/userEmail
@IsOptional()
@IsInt()
@Min(1)
+ @ApiProperty()
reservedSeatsCount?: number = 1;
@IsOptional()
@IsString()
+ @ApiProperty()
notes?: string;
}