improve calendar event creation

This commit is contained in:
Roland Schneider 2025-11-26 16:48:20 +01:00
parent 2934e099b1
commit 71a8e267dc
8 changed files with 88 additions and 34 deletions

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="dev:gui" type="ShConfigurationType"> <configuration default="false" name="dev:gui" type="ShConfigurationType">
<option name="SCRIPT_TEXT" value="cd dvbooking &amp;&amp; npm run start" /> <option name="SCRIPT_TEXT" value="cd admin &amp;&amp; npm run start" />
<option name="INDEPENDENT_SCRIPT_PATH" value="true" /> <option name="INDEPENDENT_SCRIPT_PATH" value="true" />
<option name="SCRIPT_PATH" value="" /> <option name="SCRIPT_PATH" value="" />
<option name="SCRIPT_OPTIONS" value="" /> <option name="SCRIPT_OPTIONS" value="" />

View File

@ -7,7 +7,7 @@
<full-calendar #calendar [options]="calendarOptions"></full-calendar> <full-calendar #calendar [options]="calendarOptions"></full-calendar>
</div> </div>
<rs-daisy-modal [isOpen]="isOpen()" (closeClick)="closeDialog()" > <rs-daisy-modal [isOpen]="isOpen()" (closeClick)="closeDialog()" >
@if (isOpen()){ @if (isOpen()){
<app-create-event-form (ready)="closeDialog()" [date]="selectedDate()" [id]="selectedEventId()"></app-create-event-form> <app-create-event-form (ready)="closeDialog()" [date]="selectedDate()" [id]="selectedEventId()"></app-create-event-form>
} }

View File

@ -1,6 +1,6 @@
<!-- Generated by the CLI --> <!-- Generated by the CLI -->
<div class="p-4 md:p-8"> <div class="p-4 md:p-8">
<div class="card bg-base-100 shadow-xl max-w-2xl mx-auto"> <div class="card bg-base-100 shadow-xl mx-auto">
<div class="card-body"> <div class="card-body">
<h2 class="card-title text-3xl"> <h2 class="card-title text-3xl">
Esemény {{ isEditMode ? 'szerkesztése' : 'létrehozása' }} Esemény {{ isEditMode ? 'szerkesztése' : 'létrehozása' }}
@ -39,7 +39,7 @@
</label></div> </label></div>
@if (isRecurring?.value) { @if (isRecurring?.value) {
<div formGroupName="recurringRule" class="space-y-4 p-4 border border-base-300 rounded-lg"> <div formGroupName="recurrenceRule" class="space-y-4 p-4 border border-base-300 rounded-lg">
<h3 class="text-lg font-semibold">Ismétlődés</h3> <h3 class="text-lg font-semibold">Ismétlődés</h3>
<div class="form-control"> <div class="form-control">

View File

@ -1,4 +1,4 @@
import { Component, input, OnInit, output, signal } from '@angular/core'; import { Component, effect, input, OnInit, output, signal } from '@angular/core';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { EventService } from '../../../events/services/event.service'; import { EventService } from '../../../events/services/event.service';
@ -31,10 +31,9 @@ export class CreateEventForm implements OnInit {
ready = output<void>(); ready = output<void>();
id= input<number | undefined>() ; id= input<number | undefined>() ;
date = input<Date|undefined>(); date = input<Date|undefined>();
eventTypes = signal<EventType[]>([]) eventTypes = signal<EventType[]>([])
private numericFields = ["event_type_id"]; private numericFields: (keyof EventFormDTO)[] = ["event_type_id"];
frequencyOptions: FrequencyOption[] = [ frequencyOptions: FrequencyOption[] = [
{ {
@ -78,9 +77,9 @@ export class CreateEventForm implements OnInit {
startTime: [null,Validators.required], startTime: [null,Validators.required],
endTime: [null,Validators.required], endTime: [null,Validators.required],
is_recurring: [false], is_recurring: [false],
recurringRule: this.fb.group({ recurrenceRule: this.fb.group({
frequency: [null] ,//'DAILY' | 'WEEKLY' | 'MONTHLY' | 'YEARLY'; frequency: ['DAILY'] ,//'DAILY' | 'WEEKLY' | 'MONTHLY' | 'YEARLY';
interval: [null] ,//number interval: [1] ,//number
endDate: [null], // Date endDate: [null], // Date
count: [null], // number count: [null], // number
byDay: [null], // string byDay: [null], // string
@ -89,11 +88,18 @@ export class CreateEventForm implements OnInit {
}) })
}); });
effect(() => {
console.info("effect run")
});
} }
ngOnInit(): void { ngOnInit(): void {
console.info("oninit run")
this.isRecurring?.valueChanges.subscribe(isRecurring => { this.isRecurring?.valueChanges.subscribe(isRecurring => {
const recurringRule = this.form.get('recurringRule'); const recurringRule = this.form.get('recurrenceRule');
const frequency = recurringRule?.get('frequency'); const frequency = recurringRule?.get('frequency');
const interval = recurringRule?.get('interval'); const interval = recurringRule?.get('interval');
@ -183,24 +189,25 @@ export class CreateEventForm implements OnInit {
return; return;
} }
const payload: EventFormDTO|any = { ...this.form.value }; const payload: EventFormDTO = { ...this.form.value };
if (!payload.is_recurring) { if (!payload.is_recurring) {
delete payload.recurringRule; delete payload.recurrenceRule;
} }
for (const field of this.numericFields) { for (const field of this.numericFields) {
if (payload[field] != null && payload[field] !== '') { if (payload[field] != null && payload[field] !== '') {
payload[field] = parseFloat(payload[field]); if ( typeof payload[field] !== 'number'){
payload[field] = parseFloat(payload[field] as string) as never;
}
} }
} }
let action$: Observable<Event>; let action$: Observable<Event|undefined>;
if (this.isEditMode && this.id()) { if (this.isEditMode && this.id()) {
console.info("rong branch") action$ = this.calendarService.update(this.id()!, payload);
// action$ = this.calendarService.update(this.id()!, payload); // action$ = of(undefined);
action$ = of(payload);
} else { } else {
action$ = this.calendarService.create(payload); action$ = this.calendarService.create(payload);
} }
@ -233,31 +240,31 @@ export class CreateEventForm implements OnInit {
} }
get recurringRule(){ get recurringRule(){
return this.form.get('recurringRule'); return this.form.get('recurrenceRule');
} }
get frequency(){ get frequency(){
return this.form.get('recurringRule')?.get('frequency'); return this.form.get('recurrenceRule')?.get('frequency');
} }
get interval(){ get interval(){
return this.form.get('recurringRule')?.get('interval'); return this.form.get('recurrenceRule')?.get('interval');
} }
get endDate(){ get endDate(){
return this.form.get('recurringRule')?.get('endDate'); return this.form.get('recurrenceRule')?.get('endDate');
} }
get byDay() { get byDay() {
return this.form.get('recurringRule')?.get('byDay'); return this.form.get('recurrenceRule')?.get('byDay');
} }
get byMonthDay() { get byMonthDay() {
return this.form.get('recurringRule')?.get('byMonthDay'); return this.form.get('recurrenceRule')?.get('byMonthDay');
} }
get byMonth() { get byMonth() {
return this.form.get('recurringRule')?.get('byMonth'); return this.form.get('recurrenceRule')?.get('byMonth');
} }
doReady(){ doReady(){

View File

@ -1,5 +1,19 @@
// dvbooking-cli/src/templates/angular/model.ts.tpl // dvbooking-cli/src/templates/angular/model.ts.tpl
export interface RecurrenceRuleDto {
frequency: 'DAILY' | 'WEEKLY' | 'MONTHLY' | 'YEARLY';
interval: number;
byDay?: string; // e.g., 'MO,TU,WE,TH,FR'
endDate?: Date;
count?: number;
}
// Generated by the CLI // Generated by the CLI
export interface EventFormDTO { export interface EventFormDTO {
id?: number; id?: number;
@ -9,4 +23,7 @@ export interface EventFormDTO {
start_time?: Date; start_time?: Date;
end_time?: Date; end_time?: Date;
is_recurring: boolean; is_recurring: boolean;
recurrenceRule?: RecurrenceRuleDto;
} }
export interface UpdateEventFormDTO extends Partial<EventFormDTO>{}

View File

@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http'; import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { ConfigurationService } from '../../../services/configuration.service'; import { ConfigurationService } from '../../../services/configuration.service';
import { EventFormDTO } from '../models/event-form-dto.model'; import { EventFormDTO, UpdateEventFormDTO } from '../models/event-form-dto.model';
import { Event } from '../../events/models/event.model'; import { Event } from '../../events/models/event.model';
import { CalendarEventDto, EventsInRangeDTO } from '../models/events-in-range-dto.model'; import { CalendarEventDto, EventsInRangeDTO } from '../models/events-in-range-dto.model';
@ -36,4 +36,8 @@ export class CalendarService {
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> {
return this.http.put<Event>(this.apiUrl+'/events', data);
}
} }

View File

@ -148,10 +148,15 @@ export class CalendarService {
if (!freq) continue; if (!freq) continue;
let untilDate: Date|undefined = undefined;
// typeorm does not convert postgres type 'date' to javascript date object
if (event?.recurrenceRule?.endDate) {
untilDate = new Date(event.recurrenceRule.endDate);
}
const rrule = new RRule({ const rrule = new RRule({
freq, freq,
dtstart: event.startTime, dtstart: event.startTime,
until: event.recurrenceRule.endDate, until: untilDate,
count: event.recurrenceRule.count, count: event.recurrenceRule.count,
interval: event.recurrenceRule.interval, interval: event.recurrenceRule.interval,
byweekday: byweekday?.length > 0 ? byweekday : undefined, byweekday: byweekday?.length > 0 ? byweekday : undefined,
@ -199,7 +204,9 @@ export class CalendarService {
// --- Other service methods (createEvent, etc.) remain unchanged --- // --- Other service methods (createEvent, etc.) remain unchanged ---
async createEvent(createEventDto: CreateEventDto): Promise<Event> { async createEvent(createEventDto: CreateEventDto): Promise<Event> {
console.info('createEvent', createEventDto); console.log('[CalendarService] Entering createEvent method.');
console.log('[CalendarService] Received DTO:', JSON.stringify(createEventDto, null, 2));
const { recurrenceRule, ...eventData } = createEventDto; const { recurrenceRule, ...eventData } = createEventDto;
const newEvent: Omit< const newEvent: Omit<
@ -211,36 +218,52 @@ export class CalendarService {
updatedAt: new Date(), updatedAt: new Date(),
timezone: 'Europe/Budapest', timezone: 'Europe/Budapest',
eventType: undefined, eventType: undefined,
isRecurring: recurrenceRule ? true : false, isRecurring: !!recurrenceRule,
}; };
console.log('[CalendarService] Prepared base event object:', newEvent);
// check if event type exists // check if event type exists
if (eventData.eventTypeId) { if (eventData.eventTypeId) {
console.info('eventTypeId found'); console.log(`[CalendarService] Event has eventTypeId: ${eventData.eventTypeId}. Fetching event type...`);
const eventType = await this.eventTypeRepository.findOneBy({ const eventType = await this.eventTypeRepository.findOneBy({
id: eventData.eventTypeId, id: eventData.eventTypeId,
}); });
if (!eventType) { if (!eventType) {
console.error(`[CalendarService] Event type with ID ${eventData.eventTypeId} not found.`);
throw new BadRequestException( throw new BadRequestException(
{}, {},
'Event type not found ' + eventData.eventTypeId, 'Event type not found ' + eventData.eventTypeId,
); );
} }
newEvent.eventType = eventType; newEvent.eventType = eventType;
console.log('[CalendarService] Successfully attached event type:', eventType);
} else {
console.log('[CalendarService] No eventTypeId provided.');
} }
console.log('[CalendarService] Creating event entity...');
const event = this.eventRepository.create(newEvent); const event = this.eventRepository.create(newEvent);
const savedEvent = await this.eventRepository.save(event);
if (event.isRecurring && recurrenceRule) { console.log('[CalendarService] Saving event entity to the database...');
const savedEvent = await this.eventRepository.save(event);
console.log('[CalendarService] Event saved successfully. Saved event ID:', savedEvent.id);
if (savedEvent.isRecurring && recurrenceRule) {
console.log(`[CalendarService] Event is recurring. Creating recurrence rule...`);
const rule = this.recurrenceRuleRepository.create({ const rule = this.recurrenceRuleRepository.create({
...recurrenceRule, ...recurrenceRule,
event: savedEvent, event: savedEvent,
}); });
await this.recurrenceRuleRepository.save(rule); await this.recurrenceRuleRepository.save(rule);
console.log('[CalendarService] Recurrence rule saved successfully.');
} else {
console.log('[CalendarService] Event is not recurring or no recurrence rule provided.');
} }
return this.getEventById(savedEvent.id); console.log(`[CalendarService] Fetching final event by ID ${savedEvent.id} to return.`);
const finalEvent = await this.getEventById(savedEvent.id);
console.log('[CalendarService] Exiting createEvent method.');
return finalEvent;
} }
async createException( async createException(

View File

@ -52,7 +52,10 @@ export class EventsController {
} }
@Patch(':id') @Patch(':id')
update(@Param('id', ParseIntPipe) id: number, @Body() updateEventDto: UpdateEventDto) { update(
@Param('id', ParseIntPipe) id: number,
@Body() updateEventDto: UpdateEventDto,
) {
return this.eventsService.update(id, updateEventDto); return this.eventsService.update(id, updateEventDto);
} }