feat: Implement event dashboard with activation, cancellation, editing, and booking management functionalities.
This commit is contained in:
66
.vscode/launch.json
vendored
Normal file
66
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Launch Program",
|
||||||
|
"skipFiles": [
|
||||||
|
"<node_internals>/**"
|
||||||
|
],
|
||||||
|
"program": "${file}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "NPM Start Admin",
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"cwd": "${workspaceFolder}/admin",
|
||||||
|
"runtimeExecutable": "npm",
|
||||||
|
"runtimeArgs": [
|
||||||
|
"run",
|
||||||
|
"start"
|
||||||
|
],
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "NPM Start LIB",
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"cwd": "${workspaceFolder}/admin",
|
||||||
|
"runtimeExecutable": "ng",
|
||||||
|
"runtimeArgs": [
|
||||||
|
"build",
|
||||||
|
"@rschneider/ng-daisyui",
|
||||||
|
"--watch"
|
||||||
|
],
|
||||||
|
"console": "integratedTerminal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "NPM Start REST",
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"cwd": "${workspaceFolder}/server",
|
||||||
|
"runtimeExecutable": "npm",
|
||||||
|
"runtimeArgs": [
|
||||||
|
"run",
|
||||||
|
"start:dev"
|
||||||
|
],
|
||||||
|
"preLaunchTask": "Docker Up (Environment)",
|
||||||
|
"console": "integratedTerminal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"compounds": [
|
||||||
|
{
|
||||||
|
"name": "Full Stack: Docker + App + GUI",
|
||||||
|
"configurations": [
|
||||||
|
"NPM Start LIB",
|
||||||
|
"NPM Start Admin",
|
||||||
|
"NPM Start REST"
|
||||||
|
],
|
||||||
|
"stopAll": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"editor.fontFamily": "'FiraCode Nerd Font Mono',Menlo, Monaco, 'Courier New', monospace",
|
||||||
|
"terminal.integrated.fontFamily": "'FiraCode Nerd Font Mono'",
|
||||||
|
"markdown.preview.fontFamily": "'FiraCode Nerd Font Mono',-apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', system-ui, 'Ubuntu', 'Droid Sans', sans-serif"
|
||||||
|
}
|
||||||
18
.vscode/tasks.json
vendored
Normal file
18
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "Docker Up (Environment)",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "docker compose up -d",
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}/environment/dev"
|
||||||
|
},
|
||||||
|
"presentation": {
|
||||||
|
"reveal": "always",
|
||||||
|
"panel": "new"
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -78,6 +78,7 @@ export interface CalendarCreateBookingDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface CancelBookingDto {
|
export interface CancelBookingDto {
|
||||||
|
canceledReason: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserResponseDto {
|
export interface UserResponseDto {
|
||||||
@@ -94,6 +95,7 @@ export interface BookingResponseDto {
|
|||||||
canceledAt: string | null;
|
canceledAt: string | null;
|
||||||
createdAt: string | null;
|
createdAt: string | null;
|
||||||
user: UserResponseDto;
|
user: UserResponseDto;
|
||||||
|
status?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PaginationResponseMetaDto {
|
export interface PaginationResponseMetaDto {
|
||||||
|
|||||||
@@ -137,9 +137,9 @@ export class CalendarService {
|
|||||||
return this.httpClient.post(url, calendarCreateBookingDto, requestOptions);
|
return this.httpClient.post(url, calendarCreateBookingDto, requestOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
calendarControllerCancelBooking(bookingId: number, cancelBookingDto: CancelBookingDto, observe?: 'body', options?: RequestOptions<'json'>): Observable<any>;
|
calendarControllerCancelBooking(bookingId: number, cancelBookingDto: CancelBookingDto, observe?: 'body', options?: RequestOptions<'json'>): Observable<CalenderControllerGetBookingResponse>;
|
||||||
calendarControllerCancelBooking(bookingId: number, cancelBookingDto: CancelBookingDto, observe?: 'response', options?: RequestOptions<'json'>): Observable<HttpResponse<any>>;
|
calendarControllerCancelBooking(bookingId: number, cancelBookingDto: CancelBookingDto, observe?: 'response', options?: RequestOptions<'json'>): Observable<HttpResponse<CalenderControllerGetBookingResponse>>;
|
||||||
calendarControllerCancelBooking(bookingId: number, cancelBookingDto: CancelBookingDto, observe?: 'events', options?: RequestOptions<'json'>): Observable<HttpEvent<any>>;
|
calendarControllerCancelBooking(bookingId: number, cancelBookingDto: CancelBookingDto, observe?: 'events', options?: RequestOptions<'json'>): Observable<HttpEvent<CalenderControllerGetBookingResponse>>;
|
||||||
calendarControllerCancelBooking(bookingId: number, cancelBookingDto: CancelBookingDto, observe?: 'body' | 'events' | 'response', options?: RequestOptions<'arraybuffer' | 'blob' | 'json' | 'text'>): Observable<any> {
|
calendarControllerCancelBooking(bookingId: number, cancelBookingDto: CancelBookingDto, observe?: 'body' | 'events' | 'response', options?: RequestOptions<'arraybuffer' | 'blob' | 'json' | 'text'>): Observable<any> {
|
||||||
const url = `${this.basePath}/api/calendar/bookings/${bookingId}/cancel`;
|
const url = `${this.basePath}/api/calendar/bookings/${bookingId}/cancel`;
|
||||||
|
|
||||||
|
|||||||
@@ -5,26 +5,22 @@
|
|||||||
|
|
||||||
@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)="setWorkFlow($event)"></app-single-event-dashboard>
|
||||||
(action)="setWorkFlow($event)"
|
|
||||||
></app-single-event-dashboard>
|
|
||||||
</rs-daisy-modal>
|
</rs-daisy-modal>
|
||||||
}
|
}
|
||||||
|
|
||||||
@for (dialogDefinition of dialogs; track dialogDefinition) {
|
@for (dialogDefinition of dialogs; track dialogDefinition) {
|
||||||
|
|
||||||
@if (dialogDefinition.isRendered()) {
|
@if (dialogDefinition.isRendered()) {
|
||||||
<rs-daisy-modal [isOpen]="true" (closeClick)="closeDialog()" [modalBoxStyleClass]="'max-w-none w-2xl'">
|
<rs-daisy-modal [isOpen]="true" (closeClick)="openDashboard()" [modalBoxStyleClass]="'max-w-none w-2xl'">
|
||||||
<ng-container
|
<ng-container
|
||||||
*ngComponentOutlet="dialogDefinition.component; inputs: dialogDefinition.componentInputs ? dialogDefinition.componentInputs() : {}; "
|
*ngComponentOutlet="dialogDefinition.component; inputs: dialogDefinition.componentInputs ? dialogDefinition.componentInputs() : {}; "></ng-container>
|
||||||
></ng-container>
|
|
||||||
</rs-daisy-modal>
|
</rs-daisy-modal>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,14 +79,24 @@ export class CalendarView {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.eventBus.on(EventType.CALENDAR_VIEW_DIALOG_CLOSED)
|
this.eventBus.on(EventType.CALENDAR_VIEW_CLOSE_DIALOG_AND_RELOAD)
|
||||||
.pipe(takeUntilDestroyed())
|
.pipe(takeUntilDestroyed())
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: (_) => {
|
next: (_) => {
|
||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
|
this.calendarComponent?.getApi().refetchEvents();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.eventBus.on(EventType.CALENDAR_VIEW_EVENT_DASHBOARD)
|
||||||
|
.pipe(takeUntilDestroyed())
|
||||||
|
.subscribe({
|
||||||
|
next: (_) => {
|
||||||
|
this.openDashboard();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this.calendarOptions = {
|
this.calendarOptions = {
|
||||||
plugins: [dayGridPlugin, timeGridPlugin, listPlugin, interactionPlugin],
|
plugins: [dayGridPlugin, timeGridPlugin, listPlugin, interactionPlugin],
|
||||||
@@ -254,7 +264,9 @@ export class CalendarView {
|
|||||||
closeDialog() {
|
closeDialog() {
|
||||||
this.workflow.set('NO_DIALOG');
|
this.workflow.set('NO_DIALOG');
|
||||||
}
|
}
|
||||||
|
openDashboard() {
|
||||||
|
this.workflow.set('event_dashboard');
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Set dashboard workflow
|
* Set dashboard workflow
|
||||||
* @param workflowType
|
* @param workflowType
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Foglalás dátuma</th>
|
<th>Foglalás dátuma</th>
|
||||||
<th>Foglalt helyek száma</th>
|
<th>Foglalt helyek száma</th>
|
||||||
|
<th>Státusz</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -20,9 +21,23 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>{{formatDateTime(booking.createdAt)}}</td>
|
<td>{{formatDateTime(booking.createdAt)}}</td>
|
||||||
<td>{{booking.reservedSeatsCount}}</td>
|
<td>{{booking.reservedSeatsCount}}</td>
|
||||||
<td><rs-daisy-button [variant]="'error'">
|
<td>
|
||||||
|
@switch (booking.status){
|
||||||
|
@case ('active'){
|
||||||
|
<span [outerHTML]="SvgIcons.heroCheckCircle | safeHtml"></span>
|
||||||
|
}
|
||||||
|
@case ('customer_cancelled'){
|
||||||
|
<span [outerHTML]="SvgIcons.heroMinusCircle | safeHtml"></span>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
@if (booking.status == 'active') {
|
||||||
|
<rs-daisy-button [variant]="'error'" (clickEvent)="cancelBooking(booking)">
|
||||||
Lemondás
|
Lemondás
|
||||||
</rs-daisy-button></td>
|
</rs-daisy-button>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ import { delay, of, throwError } from 'rxjs';
|
|||||||
import { Button, Pagination } from '@rschneider/ng-daisyui';
|
import { Button, Pagination } from '@rschneider/ng-daisyui';
|
||||||
import { Heading } from '../../../../../components/heading/heading';
|
import { Heading } from '../../../../../components/heading/heading';
|
||||||
import { format } from 'date-fns';
|
import { format } from 'date-fns';
|
||||||
|
import { EventType } from '../../../../../../types';
|
||||||
|
import { SvgIcons } from '../../../../../svg-icons';
|
||||||
|
import { SafeHtmlPipe } from "../../../../../pipes/safe-html-pipe";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-single-event-booking-list',
|
selector: 'app-single-event-booking-list',
|
||||||
@@ -14,6 +17,7 @@ import { format } from 'date-fns';
|
|||||||
Pagination,
|
Pagination,
|
||||||
Heading,
|
Heading,
|
||||||
Button,
|
Button,
|
||||||
|
SafeHtmlPipe
|
||||||
],
|
],
|
||||||
templateUrl: './single-event-booking-list.html',
|
templateUrl: './single-event-booking-list.html',
|
||||||
styleUrl: './single-event-booking-list.css',
|
styleUrl: './single-event-booking-list.css',
|
||||||
@@ -30,57 +34,64 @@ export class SingleEventBookingList {
|
|||||||
pageCount = computed(() => {
|
pageCount = computed(() => {
|
||||||
// return this.bookings.value()?.pageCount || 1;
|
// return this.bookings.value()?.pageCount || 1;
|
||||||
return 1;
|
return 1;
|
||||||
})
|
});
|
||||||
|
|
||||||
bookings = rxResource(
|
bookings = rxResource(
|
||||||
{
|
{
|
||||||
params: () => ({
|
params: () => ({
|
||||||
page: this.activePage()
|
page: this.activePage(),
|
||||||
}),
|
}),
|
||||||
stream: ({ params }) => {
|
stream: ({ params }) => {
|
||||||
|
|
||||||
// console.info("loading resource", params);
|
|
||||||
//
|
|
||||||
// const allData = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t"]
|
|
||||||
//
|
|
||||||
// let pageCount = Math.floor( allData.length / this.pageSize());
|
|
||||||
// if ( (allData.length % this.pageSize()) > 0){
|
|
||||||
// pageCount += 1;
|
|
||||||
// }
|
|
||||||
// pageCount = Math.max(pageCount ,1);
|
|
||||||
//
|
|
||||||
// const pageData = allData.slice( ((this.activePage()-1) * this.pageSize()),this.activePage()*this.pageSize());
|
|
||||||
// console.info("booking page data",pageData);
|
|
||||||
// return of({
|
|
||||||
// items: pageData,
|
|
||||||
// pageCount
|
|
||||||
// }).pipe( delay(1000))
|
|
||||||
if (!this.event()) {
|
if (!this.event()) {
|
||||||
return throwError( () => new Error("no event"))
|
return throwError(() => new Error('no event'));
|
||||||
}
|
}
|
||||||
const event = this.event()!;
|
const event = this.event()!;
|
||||||
return this.calendarService.calendarControllerGetBookings(
|
return this.calendarService.calendarControllerGetBookings(
|
||||||
event.id,
|
event.id,
|
||||||
new Date(event.startTime).toISOString()
|
new Date(event.startTime).toISOString(),
|
||||||
)
|
params.page,
|
||||||
|
this.pageSize(),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
}
|
},
|
||||||
)
|
);
|
||||||
|
|
||||||
|
|
||||||
protected paginate($event: number) {
|
protected paginate($event: number) {
|
||||||
console.info("paginated to ", $event)
|
console.info('paginated to ', $event);
|
||||||
this.activePage.set($event);
|
this.activePage.set($event);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public cancelBooking(booking: BookingResponseDto) {
|
||||||
|
this.calendarService.calendarControllerCancelBooking(
|
||||||
|
booking.id,
|
||||||
|
{
|
||||||
|
canceledReason: 'customer_cancelled',
|
||||||
|
},
|
||||||
|
).subscribe({
|
||||||
|
next: () => {
|
||||||
|
// this.eventBus.emit(EventType.CALENDAR_VIEW_BOOKING_CANCELLED, '');
|
||||||
|
this.bookings.reload();
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
// todo: handle error
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
formatDateTime(dateStr?: string | Date | null) {
|
formatDateTime(dateStr?: string | Date | null) {
|
||||||
if (!dateStr) {
|
if (!dateStr) {
|
||||||
return "";
|
return '';
|
||||||
}
|
}
|
||||||
return format(new Date(dateStr), 'yyyy-MM-dd HH:mm');
|
return format(new Date(dateStr), 'yyyy-MM-dd HH:mm');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected readonly format = format;
|
protected readonly format = format;
|
||||||
|
|
||||||
|
protected readonly SvgIcons = SvgIcons;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ export class SingleEventDashboardEventActivation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected closeDialog() {
|
protected closeDialog() {
|
||||||
this.eventBus.emit(EventType.CALENDAR_VIEW_DIALOG_CLOSED, 'Event saved')
|
this.eventBus.emit(EventType.CALENDAR_VIEW_EVENT_DASHBOARD, 'Event saved')
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export class SingleEventDashboardEventEdit {
|
|||||||
} else if (action == 'save-event-failed') {
|
} else if (action == 'save-event-failed') {
|
||||||
this.eventBus.emit(EventType.CALENDAR_VIEW_DIALOG_CLOSED, '');
|
this.eventBus.emit(EventType.CALENDAR_VIEW_DIALOG_CLOSED, '');
|
||||||
} else {
|
} else {
|
||||||
this.eventBus.emit(EventType.CALENDAR_VIEW_DIALOG_CLOSED,'');
|
this.eventBus.emit(EventType.CALENDAR_VIEW_EVENT_DASHBOARD, '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,14 +7,13 @@
|
|||||||
<form [formGroup]="form" (ngSubmit)="onSubmit()" class="space-y-4 mt-4">
|
<form [formGroup]="form" (ngSubmit)="onSubmit()" class="space-y-4 mt-4">
|
||||||
|
|
||||||
<div class="form-control"><label class="label"><span class="label-text">Megnevezés</span></label>
|
<div class="form-control"><label class="label"><span class="label-text">Megnevezés</span></label>
|
||||||
<input [class.input-error]="title?.invalid && title?.touched" type="text" formControlName="title" class="input input-bordered w-full" />
|
<input [class.input-error]="title?.invalid && title?.touched" type="text" formControlName="title"
|
||||||
|
class="input input-bordered w-full" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-control"><label class="label"><span class="label-text">Esemény típus</span></label>
|
<div class="form-control"><label class="label"><span class="label-text">Esemény típus</span></label>
|
||||||
<select class="select w-full"
|
<select class="select w-full" formControlName="eventTypeId"
|
||||||
formControlName="eventTypeId"
|
[class.input-error]="eventType?.invalid && eventType?.touched">
|
||||||
[class.input-error]="eventType?.invalid && eventType?.touched"
|
|
||||||
>
|
|
||||||
<option disabled selected>Pick a color</option>
|
<option disabled selected>Pick a color</option>
|
||||||
@for (eventType of eventTypes(); track eventType.id) {
|
@for (eventType of eventTypes(); track eventType.id) {
|
||||||
<option [value]="eventType.id">{{ eventType.name }}</option>
|
<option [value]="eventType.id">{{ eventType.name }}</option>
|
||||||
@@ -23,13 +22,19 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-control"><label class="label"><span class="label-text">Leírás</span></label>
|
<div class="form-control"><label class="label"><span class="label-text">Leírás</span></label>
|
||||||
<input [class.input-error]="description?.invalid && description?.touched" type="text" formControlName="description" class="input input-bordered w-full" /></div>
|
<input [class.input-error]="description?.invalid && description?.touched" type="text"
|
||||||
|
formControlName="description" class="input input-bordered w-full" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-control"><label class="label"><span class="label-text">Kezdő időpont</span></label>
|
<div class="form-control"><label class="label"><span class="label-text">Kezdő időpont</span></label>
|
||||||
<input [class.input-error]="startTime?.invalid && startTime?.touched" type="datetime-local" formControlName="startTime" class="input input-bordered w-full" /></div>
|
<input [class.input-error]="startTime?.invalid && startTime?.touched" type="datetime-local"
|
||||||
|
formControlName="startTime" class="input input-bordered w-full" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-control"><label class="label"><span class="label-text">Befejezési időpont</span></label>
|
<div class="form-control"><label class="label"><span class="label-text">Befejezési időpont</span></label>
|
||||||
<input [class.input-error]="endTime?.invalid && endTime?.touched" type="datetime-local" formControlName="endTime" class="input input-bordered w-full" /></div>
|
<input [class.input-error]="endTime?.invalid && endTime?.touched" type="datetime-local" formControlName="endTime"
|
||||||
|
class="input input-bordered w-full" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-control"><label class="label cursor-pointer justify-start gap-4">
|
<div class="form-control"><label class="label cursor-pointer justify-start gap-4">
|
||||||
<span class="label-text">Ismétlődő</span>
|
<span class="label-text">Ismétlődő</span>
|
||||||
@@ -42,8 +47,7 @@
|
|||||||
|
|
||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
<label class="label"><span class="label-text">Gyakoriság</span></label>
|
<label class="label"><span class="label-text">Gyakoriság</span></label>
|
||||||
<select class="select w-full"
|
<select class="select w-full" formControlName="frequency"
|
||||||
formControlName="frequency"
|
|
||||||
[class.select-error]="frequency?.invalid && frequency?.touched">
|
[class.select-error]="frequency?.invalid && frequency?.touched">
|
||||||
<option disabled selected>Válassz gyakoriságot</option>
|
<option disabled selected>Válassz gyakoriságot</option>
|
||||||
@for (frequencyOption of frequencyOptions; track frequencyOption) {
|
@for (frequencyOption of frequencyOptions; track frequencyOption) {
|
||||||
@@ -54,9 +58,7 @@
|
|||||||
|
|
||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
<label class="label"><span class="label-text">Intervallum</span></label>
|
<label class="label"><span class="label-text">Intervallum</span></label>
|
||||||
<input type="number"
|
<input type="number" formControlName="interval" class="input input-bordered w-full"
|
||||||
formControlName="interval"
|
|
||||||
class="input input-bordered w-full"
|
|
||||||
[class.input-error]="interval?.invalid && interval?.touched" />
|
[class.input-error]="interval?.invalid && interval?.touched" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -65,11 +67,8 @@
|
|||||||
<div class="flex flex-wrap gap-4">
|
<div class="flex flex-wrap gap-4">
|
||||||
@for (day of weekDayOptions; track day.value) {
|
@for (day of weekDayOptions; track day.value) {
|
||||||
<label class="label cursor-pointer justify-start gap-2">
|
<label class="label cursor-pointer justify-start gap-2">
|
||||||
<input type="checkbox"
|
<input type="checkbox" [value]="day.value" [checked]="isDayChecked(day.value)"
|
||||||
[value]="day.value"
|
(change)="onByDayChange($event)" class="checkbox" />
|
||||||
[checked]="isDayChecked(day.value)"
|
|
||||||
(change)="onByDayChange($event)"
|
|
||||||
class="checkbox" />
|
|
||||||
<span class="label-text">{{ day.label }}</span>
|
<span class="label-text">{{ day.label }}</span>
|
||||||
</label>
|
</label>
|
||||||
}
|
}
|
||||||
@@ -78,9 +77,7 @@
|
|||||||
|
|
||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
<label class="label"><span class="label-text">Hónap napja</span></label>
|
<label class="label"><span class="label-text">Hónap napja</span></label>
|
||||||
<input type="text"
|
<input type="text" formControlName="byMonthDay" class="input input-bordered w-full" />
|
||||||
formControlName="byMonthDay"
|
|
||||||
class="input input-bordered w-full" />
|
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<span class="label-text-alt">Vesszővel elválasztott lista (pl. 1,15,31)</span>
|
<span class="label-text-alt">Vesszővel elválasztott lista (pl. 1,15,31)</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -88,9 +85,7 @@
|
|||||||
|
|
||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
<label class="label"><span class="label-text">Hónap</span></label>
|
<label class="label"><span class="label-text">Hónap</span></label>
|
||||||
<input type="text"
|
<input type="text" formControlName="byMonth" class="input input-bordered w-full" />
|
||||||
formControlName="byMonth"
|
|
||||||
class="input input-bordered w-full" />
|
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<span class="label-text-alt">Vesszővel elválasztott lista (pl. 1,2,3)</span>
|
<span class="label-text-alt">Vesszővel elválasztott lista (pl. 1,2,3)</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -98,18 +93,16 @@
|
|||||||
|
|
||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
<label class="label"><span class="label-text">Ismétlődés vége</span></label>
|
<label class="label"><span class="label-text">Ismétlődés vége</span></label>
|
||||||
<input type="date"
|
<input type="date" formControlName="endDate" class="input input-bordered w-full" />
|
||||||
formControlName="endDate"
|
|
||||||
class="input input-bordered w-full" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="card-actions justify-end mt-6">
|
<div class="card-actions justify-end mt-6">
|
||||||
<a class="btn btn-ghost" (click)="doReady()">Mégse</a>
|
|
||||||
<button type="submit" class="btn btn-primary" [disabled]="form.invalid">
|
<button type="submit" class="btn btn-primary" [disabled]="form.invalid">
|
||||||
{{ isEditMode ? 'Mentés' : 'Létrezhozás' }}
|
{{ isEditMode ? 'Mentés' : 'Létrezhozás' }}
|
||||||
</button>
|
</button>
|
||||||
|
<a class="btn btn-primary" (click)="doReady()">Mégse</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -44,4 +44,19 @@ export class SvgIcons {
|
|||||||
</svg>
|
</svg>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
public static heroMinusCircle = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
||||||
|
</svg>
|
||||||
|
`;
|
||||||
|
|
||||||
|
public static heroBadgeCircle = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12c0 1.268-.63 2.39-1.593 3.068a3.745 3.745 0 0 1-1.043 3.296 3.745 3.745 0 0 1-3.296 1.043A3.745 3.745 0 0 1 12 21c-1.268 0-2.39-.63-3.068-1.593a3.746 3.746 0 0 1-3.296-1.043 3.745 3.745 0 0 1-1.043-3.296A3.745 3.745 0 0 1 3 12c0-1.268.63-2.39 1.593-3.068a3.745 3.745 0 0 1 1.043-3.296 3.746 3.746 0 0 1 3.296-1.043A3.746 3.746 0 0 1 12 3c1.268 0 2.39.63 3.068 1.593a3.746 3.746 0 0 1 3.296 1.043 3.746 3.746 0 0 1 1.043 3.296Z" />
|
||||||
|
</svg>
|
||||||
|
`;
|
||||||
|
|
||||||
|
public static heroCheckCircle = `
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
||||||
|
</svg>
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,11 @@ export enum EventType {
|
|||||||
USER_LOGGED_IN = 'USER_LOGGED_IN',
|
USER_LOGGED_IN = 'USER_LOGGED_IN',
|
||||||
ORDER_CREATED = 'ORDER_CREATED',
|
ORDER_CREATED = 'ORDER_CREATED',
|
||||||
THEME_CHANGED = 'THEME_CHANGED',
|
THEME_CHANGED = 'THEME_CHANGED',
|
||||||
|
CALENDAR_VIEW_EVENT_DASHBOARD = 'CALENDAR_VIEW_EVENT_DASHBOARD',
|
||||||
CALENDAR_VIEW_EVENT_SAVED = 'CALENDAR_VIEW_EVENT_SAVED',
|
CALENDAR_VIEW_EVENT_SAVED = 'CALENDAR_VIEW_EVENT_SAVED',
|
||||||
CALENDAR_VIEW_DIALOG_CLOSED = 'CALENDAR_VIEW_DIALOG_CLOSED'
|
CALENDAR_VIEW_DIALOG_CLOSED = 'CALENDAR_VIEW_DIALOG_CLOSED',
|
||||||
|
CALENDAR_VIEW_BOOKING_CANCELLED = 'CALENDAR_VIEW_BOOKING_CANCELLED',
|
||||||
|
CALENDAR_VIEW_CLOSE_DIALOG_AND_RELOAD = 'CALENDAR_VIEW_CLOSE_DIALOG_AND_RELOAD'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -30,6 +33,10 @@ export interface EventMap {
|
|||||||
[EventType.ORDER_CREATED]: { orderId: number };
|
[EventType.ORDER_CREATED]: { orderId: number };
|
||||||
[EventType.CALENDAR_VIEW_EVENT_SAVED]: string;
|
[EventType.CALENDAR_VIEW_EVENT_SAVED]: string;
|
||||||
[EventType.CALENDAR_VIEW_DIALOG_CLOSED]: string;
|
[EventType.CALENDAR_VIEW_DIALOG_CLOSED]: string;
|
||||||
|
[EventType.CALENDAR_VIEW_DIALOG_CLOSED]: string;
|
||||||
|
[EventType.CALENDAR_VIEW_BOOKING_CANCELLED]: string;
|
||||||
|
[EventType.CALENDAR_VIEW_CLOSE_DIALOG_AND_RELOAD]: string;
|
||||||
|
[EventType.CALENDAR_VIEW_EVENT_DASHBOARD]: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AppEvent<T = any> {
|
export interface AppEvent<T = any> {
|
||||||
|
|||||||
8
dius.txt
Normal file
8
dius.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
NAiP
|
||||||
|
file:///Users/rschneider/Library/Fonts/FiraCodeNerdFontMono-Light.ttf#postscript-name=FiraCodeNFM-Light
|
||||||
|
file:///Users/rschneider/Library/Fonts/FiraCodeNerdFontMono-Regular.ttf#postscript-name=FiraCodeNFM-Reg
|
||||||
|
file:///Users/rschneider/Library/Fonts/FiraCodeNerdFontMono-Retina.ttf#postscript-name=FiraCodeNFM-Ret
|
||||||
|
file:///Users/rschneider/Library/Fonts/FiraCodeNerdFontMono-Medium.ttf#postscript-name=FiraCodeNFM-Med
|
||||||
|
file:///Users/rschneider/Library/Fonts/FiraCodeNerdFontMono-SemiBold.ttf#postscript-name=FiraCodeNFM-SemBd
|
||||||
|
file:///Users/rschneider/Library/Fonts/FiraCodeNerdFontMono-Bold.ttf#postscript-name=FiraCodeNFM-Bold
|
||||||
@@ -110,6 +110,7 @@ export class CalendarController {
|
|||||||
@ApiCreatedResponse({ type: CalenderControllerGetBookingResponse }) // Removed <Booking> generic for cleaner syntax unless defined elsewhere
|
@ApiCreatedResponse({ type: CalenderControllerGetBookingResponse }) // Removed <Booking> generic for cleaner syntax unless defined elsewhere
|
||||||
// Ensure ValidationPipe is enabled with transform: true (Global or Method scoped)
|
// Ensure ValidationPipe is enabled with transform: true (Global or Method scoped)
|
||||||
// @UsePipes(new ValidationPipe({ transform: true }))
|
// @UsePipes(new ValidationPipe({ transform: true }))
|
||||||
|
@Roles(Role.Admin, Role.User)
|
||||||
async getBookings(
|
async getBookings(
|
||||||
@User() user: types.AppUser,
|
@User() user: types.AppUser,
|
||||||
@Param('eventId', ParseIntPipe) eventId: number,
|
@Param('eventId', ParseIntPipe) eventId: number,
|
||||||
@@ -117,7 +118,7 @@ export class CalendarController {
|
|||||||
@Query() calendarGetBookingDto: CalendarGetBookingDto,
|
@Query() calendarGetBookingDto: CalendarGetBookingDto,
|
||||||
) {
|
) {
|
||||||
return this.calendarService.getBookings(
|
return this.calendarService.getBookings(
|
||||||
user.user!.userId,
|
user,
|
||||||
eventId,
|
eventId,
|
||||||
calendarGetBookingDto,
|
calendarGetBookingDto,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ import {
|
|||||||
BookingResponseDto,
|
BookingResponseDto,
|
||||||
CalenderControllerGetBookingResponse,
|
CalenderControllerGetBookingResponse,
|
||||||
} from './dto/booking.response.dto';
|
} from './dto/booking.response.dto';
|
||||||
|
import { AppUser } from 'src/types';
|
||||||
|
import { Role } from '../auth/role.enum';
|
||||||
|
|
||||||
// --- Type-Safe Maps ---
|
// --- Type-Safe Maps ---
|
||||||
const frequencyMap: Record<string, RRule.Frequency> = {
|
const frequencyMap: Record<string, RRule.Frequency> = {
|
||||||
@@ -606,7 +608,7 @@ export class CalendarService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getBookings(
|
async getBookings(
|
||||||
userId: number,
|
user: AppUser,
|
||||||
eventId: number,
|
eventId: number,
|
||||||
queryParams: CalendarGetBookingDto,
|
queryParams: CalendarGetBookingDto,
|
||||||
): Promise<CalenderControllerGetBookingResponse> {
|
): Promise<CalenderControllerGetBookingResponse> {
|
||||||
@@ -621,6 +623,13 @@ export class CalendarService {
|
|||||||
['createdAt']: order!,
|
['createdAt']: order!,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!user.user?.roles.includes(Role.Admin)) {
|
||||||
|
findOptions.where = {
|
||||||
|
...findOptions.where,
|
||||||
|
userId: user.user!.userId,
|
||||||
|
};
|
||||||
|
}
|
||||||
const paginated = limit > 0;
|
const paginated = limit > 0;
|
||||||
const [data, totalItems] =
|
const [data, totalItems] =
|
||||||
await this.bookingRepository.findAndCount(findOptions);
|
await this.bookingRepository.findAndCount(findOptions);
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ export class BookingResponseDto {
|
|||||||
})
|
})
|
||||||
user?: UserResponseDto;
|
user?: UserResponseDto;
|
||||||
|
|
||||||
|
@ApiProperty({ required: false })
|
||||||
|
status?: string;
|
||||||
|
|
||||||
constructor(booking: Booking) {
|
constructor(booking: Booking) {
|
||||||
this.id = Number(booking.id); // Handle BigInt conversion
|
this.id = Number(booking.id); // Handle BigInt conversion
|
||||||
this.eventId = Number(booking.eventId);
|
this.eventId = Number(booking.eventId);
|
||||||
@@ -46,6 +49,14 @@ export class BookingResponseDto {
|
|||||||
// Assuming User entity has firstName/lastName
|
// Assuming User entity has firstName/lastName
|
||||||
this.user = new UserResponseDto(booking.user);
|
this.user = new UserResponseDto(booking.user);
|
||||||
}
|
}
|
||||||
|
this.status = 'active';
|
||||||
|
if (booking.canceledAt) {
|
||||||
|
if (booking.canceledReason == 'customer_cancelled') {
|
||||||
|
this.status = 'customer_cancelled';
|
||||||
|
} else {
|
||||||
|
this.status = 'cancelled';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
IsInt,
|
|
||||||
IsNotEmpty,
|
|
||||||
IsOptional,
|
IsOptional,
|
||||||
IsString,
|
IsString,
|
||||||
MaxLength,
|
MaxLength,
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
|
||||||
export class CancelBookingDto {
|
export class CancelBookingDto {
|
||||||
|
@ApiProperty()
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
@MaxLength(50)
|
@MaxLength(50)
|
||||||
|
|||||||
Reference in New Issue
Block a user