add calendarview event creation
This commit is contained in:
@@ -6,3 +6,8 @@
|
||||
</div>
|
||||
<full-calendar #calendar [options]="calendarOptions"></full-calendar>
|
||||
</div>
|
||||
|
||||
<rs-daisy-modal [isOpen]="isOpen()" (closeClick)="closeDialog()" >
|
||||
<app-create-event-form (ready)="closeDialog()"></app-create-event-form>
|
||||
</rs-daisy-modal>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, ElementRef, ViewChild } from '@angular/core';
|
||||
import { AfterViewInit, Component, effect, ElementRef, inject, OnInit, signal, ViewChild } from '@angular/core';
|
||||
import { FullCalendarComponent, FullCalendarModule } from '@fullcalendar/angular';
|
||||
import { CalendarOptions } from '@fullcalendar/core';
|
||||
|
||||
@@ -6,19 +6,30 @@ import dayGridPlugin from '@fullcalendar/daygrid';
|
||||
import timeGridPlugin from '@fullcalendar/timegrid';
|
||||
import listPlugin from '@fullcalendar/list';
|
||||
import interactionPlugin from '@fullcalendar/interaction';
|
||||
import { appConfig } from '../../../../app.config';
|
||||
import { Modal } from '@rschneider/ng-daisyui';
|
||||
import { CreateEventForm } from '../create-event-form/create-event-form';
|
||||
import { CalendarService } from '../../services/calendar.service';
|
||||
import { addDays, subDays } from 'date-fns';
|
||||
import { EventsInRangeDTO } from '../../models/events-in-range-dto.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-calendar-view',
|
||||
imports: [FullCalendarModule],
|
||||
imports: [FullCalendarModule, Modal, CreateEventForm],
|
||||
templateUrl: './calendar-view.html',
|
||||
styleUrl: './calendar-view.css',
|
||||
})
|
||||
export class CalendarView {
|
||||
export class CalendarView implements OnInit, AfterViewInit {
|
||||
|
||||
@ViewChild('startHour') startHour!: ElementRef;
|
||||
@ViewChild('calendar') calendarComponent: FullCalendarComponent | undefined;
|
||||
|
||||
calendarService = inject(CalendarService);
|
||||
|
||||
workflow = signal<string>("")
|
||||
isOpen = signal<boolean>(false);
|
||||
|
||||
events = signal<EventsInRangeDTO[]>([])
|
||||
|
||||
calendarOptions: CalendarOptions;
|
||||
|
||||
constructor() {
|
||||
@@ -27,6 +38,7 @@ export class CalendarView {
|
||||
const end = new Date();
|
||||
end.setHours(11,0,0)
|
||||
|
||||
|
||||
this.calendarOptions = {
|
||||
plugins: [dayGridPlugin, timeGridPlugin, listPlugin,interactionPlugin],
|
||||
initialView: 'dayGridMonth',
|
||||
@@ -41,7 +53,6 @@ export class CalendarView {
|
||||
events: [
|
||||
|
||||
|
||||
{ title: 'Meeting1 until'+end.toString(), start, end },
|
||||
],
|
||||
|
||||
eventClick: function(info) {
|
||||
@@ -51,21 +62,54 @@ export class CalendarView {
|
||||
info.el.style.borderColor = 'red';
|
||||
},
|
||||
|
||||
dateClick: function(info) {
|
||||
console.info('Date click on: ' , info);
|
||||
const calendarApi = info.view.calendar;
|
||||
const start = new Date(info.date.getTime())
|
||||
start.setHours(2,0,0)
|
||||
const end = new Date(info.date.getTime())
|
||||
end.setHours(3,0,0)
|
||||
calendarApi.addEvent({
|
||||
title: 'New Event',
|
||||
start,
|
||||
end,
|
||||
});
|
||||
dateClick: (info) => {
|
||||
console.info("setting day workflow");
|
||||
this.workflow.set("day");
|
||||
this.isOpen.set(true);
|
||||
// console.info('Date click on: ' , info);
|
||||
// const calendarApi = info.view.calendar;
|
||||
// const start = new Date(info.date.getTime())
|
||||
// start.setHours(2,0,0)
|
||||
// const end = new Date(info.date.getTime())
|
||||
// end.setHours(3,0,0)
|
||||
// calendarApi.addEvent({
|
||||
// title: 'New Event',
|
||||
// start,
|
||||
// end,
|
||||
// });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
effect(() => {
|
||||
// this.calendarOptions.events = this.events
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
|
||||
// this.calendarComponent?.getApi().
|
||||
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
const start = subDays(new Date(),14)
|
||||
const end = addDays(new Date(),14);
|
||||
this.calendarService.getEventsInRange({
|
||||
startTime: start,
|
||||
endTime: end
|
||||
}).subscribe(
|
||||
{
|
||||
'next': (events) => {
|
||||
console.info('events',events)
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
protected addEvent($event: PointerEvent) {
|
||||
@@ -82,4 +126,8 @@ export class CalendarView {
|
||||
start
|
||||
})
|
||||
}
|
||||
|
||||
closeDialog() {
|
||||
this.isOpen.set(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<!-- Generated by the CLI -->
|
||||
<div class="p-4 md:p-8">
|
||||
<div class="card bg-base-100 shadow-xl max-w-2xl mx-auto">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title text-3xl">
|
||||
Esemény {{ isEditMode ? 'szerkesztése' : 'létrehozása' }}
|
||||
</h2>
|
||||
|
||||
<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>
|
||||
<input [class.input-error]="title?.invalid && title?.touched" type="text" formControlName="title" class="input input-bordered w-full" />
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-control"><label class="label"><span class="label-text">Esemény típus</span></label>
|
||||
<select class="select w-full"
|
||||
formControlName="eventTypeId"
|
||||
[class.input-error]="eventType?.invalid && eventType?.touched"
|
||||
>
|
||||
<option disabled selected>Pick a color</option>
|
||||
@for (eventType of eventTypes(); track eventType.id) {
|
||||
<option [value]="eventType.id">{{ eventType.name }}</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<div class="form-control"><label class="label cursor-pointer justify-start gap-4">
|
||||
<span class="label-text">Ismétlődő</span>
|
||||
<input type="checkbox" formControlName="is_recurring" class="checkbox" />
|
||||
</label></div>
|
||||
|
||||
<div class="card-actions justify-end mt-6">
|
||||
<a routerLink="/events" class="btn btn-ghost">Mégse</a>
|
||||
<button type="submit" class="btn btn-primary" [disabled]="form.invalid">
|
||||
{{ isEditMode ? 'Mentés' : 'Létrezhozás' }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,146 @@
|
||||
import { Component, input, OnInit, output, signal } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { EventService } from '../../../events/services/event.service';
|
||||
import { mergeMap, switchMap, tap } from 'rxjs/operators';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { Event } from '../../../events/models/event.model';
|
||||
import { EventType } from '../../../event-type/models/event-type.model';
|
||||
import { EventTypeService } from '../../../event-type/services/event-type.service';
|
||||
import { CalendarService } from '../../services/calendar.service';
|
||||
import { EventFormDTO } from '../../models/event-form-dto.model';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-create-event-form',
|
||||
imports: [
|
||||
ReactiveFormsModule
|
||||
],
|
||||
templateUrl: './create-event-form.html',
|
||||
styleUrl: './create-event-form.css',
|
||||
})
|
||||
export class CreateEventForm implements OnInit {
|
||||
form: FormGroup;
|
||||
isEditMode = false;
|
||||
ready = output<void>();
|
||||
id= input<number | null>() ;
|
||||
|
||||
eventTypes = signal<EventType[]>([])
|
||||
|
||||
private numericFields = ["event_type_id"];
|
||||
|
||||
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private eventService: EventService,
|
||||
private calendarService: CalendarService,
|
||||
private eventTypeService: EventTypeService
|
||||
) {
|
||||
this.form = this.fb.group({
|
||||
eventTypeId: [null,Validators.required],
|
||||
title: [null,Validators.required],
|
||||
description: [null],
|
||||
startTime: [null,Validators.required],
|
||||
endTime: [null,Validators.required],
|
||||
is_recurring: [null],
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
of(this.id()).pipe(
|
||||
tap(id => {
|
||||
if (id) {
|
||||
this.isEditMode = true;
|
||||
}else{
|
||||
|
||||
const start = new Date();
|
||||
const end = new Date();
|
||||
start.setMinutes(0,0);
|
||||
end.setHours(start.getHours()+1,0,0);
|
||||
start.setMinutes(start.getMinutes() - start.getTimezoneOffset());
|
||||
const startString = start.toISOString().slice(0,16);
|
||||
|
||||
end.setMinutes(end.getMinutes() - end.getTimezoneOffset());
|
||||
const endTime = end.toISOString().slice(0,16);
|
||||
|
||||
console.info("Date start",start.toLocaleString("hu-HU", {timeStyle: 'short', dateStyle: 'short'}));
|
||||
this.form.patchValue({
|
||||
// start_time: new Date().getTime().toString(),
|
||||
startTime: startString,
|
||||
endTime: endTime
|
||||
})
|
||||
}
|
||||
}),
|
||||
mergeMap(() => {
|
||||
return this.eventTypeService.find({});
|
||||
}
|
||||
),
|
||||
tap(eventTypes => {
|
||||
this.eventTypes.set(eventTypes.data);
|
||||
}),
|
||||
switchMap(() => {
|
||||
if (this.isEditMode && this.id()) {
|
||||
return this.eventService.findOne(this.id()!);
|
||||
}
|
||||
return of(null);
|
||||
}),
|
||||
).subscribe(event => {
|
||||
if (event) {
|
||||
this.form.patchValue(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit(): void {
|
||||
if (this.form.invalid) {
|
||||
this.form.markAllAsTouched();
|
||||
return;
|
||||
}
|
||||
|
||||
const payload: EventFormDTO|any = { ...this.form.value };
|
||||
|
||||
for (const field of this.numericFields) {
|
||||
if (payload[field] != null && payload[field] !== '') {
|
||||
payload[field] = parseFloat(payload[field]);
|
||||
}
|
||||
}
|
||||
|
||||
let action$: Observable<Event>;
|
||||
|
||||
if (this.isEditMode && this.id()) {
|
||||
console.info("rong branch")
|
||||
// action$ = this.calendarService.update(this.id()!, payload);
|
||||
action$ = of(payload);
|
||||
} else {
|
||||
action$ = this.calendarService.create(payload);
|
||||
}
|
||||
|
||||
action$.subscribe({
|
||||
next: () => this.router.navigate(['/events']),
|
||||
error: (err) => console.error('Failed to save event', err)
|
||||
});
|
||||
}
|
||||
|
||||
get eventType(){
|
||||
return this.form.get('eventTypeId');
|
||||
}
|
||||
|
||||
get title(){
|
||||
return this.form.get('title');
|
||||
}
|
||||
get description(){
|
||||
return this.form.get('description');
|
||||
}
|
||||
get startTime(){
|
||||
return this.form.get('startTime');
|
||||
}
|
||||
get endTime(){
|
||||
return this.form.get('endTime');
|
||||
}
|
||||
|
||||
doReady(){
|
||||
this.ready.emit();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// dvbooking-cli/src/templates/angular/model.ts.tpl
|
||||
|
||||
// Generated by the CLI
|
||||
export interface EventFormDTO {
|
||||
id?: number;
|
||||
event_type_id: number;
|
||||
title: string;
|
||||
description?: string;
|
||||
start_time?: Date;
|
||||
end_time?: Date;
|
||||
is_recurring: boolean;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Event } from '../../events/models/event.model';
|
||||
export interface EventsInRangeDTO {
|
||||
startTime?: Date;
|
||||
endTime?: Date;
|
||||
}
|
||||
export type BookingUserDto = {
|
||||
id: number;
|
||||
name: string; // Assuming user has a 'name' property
|
||||
email: string; // Assuming user has a 'name' property
|
||||
};
|
||||
|
||||
// This represents a booking with nested user info
|
||||
export type BookingWithUserDto = {
|
||||
user: BookingUserDto | null;
|
||||
id: number;
|
||||
};
|
||||
|
||||
// The final shape of a calendar event occurrence
|
||||
export type CalendarEventDto = Omit<Event, 'bookings'> & {
|
||||
isModified?: boolean;
|
||||
eventBookings: BookingWithUserDto[];
|
||||
};
|
||||
39
admin/src/app/features/calendar/services/calendar.service.ts
Normal file
39
admin/src/app/features/calendar/services/calendar.service.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ConfigurationService } from '../../../services/configuration.service';
|
||||
import { EventFormDTO } from '../models/event-form-dto.model';
|
||||
import { Event } from '../../events/models/event.model';
|
||||
import { EventsInRangeDTO } from '../models/events-in-range-dto.model';
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class CalendarService {
|
||||
private readonly apiUrl: string;
|
||||
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
private configService: ConfigurationService
|
||||
) {
|
||||
this.apiUrl = `${this.configService.getApiUrl()}/calendar`;
|
||||
}
|
||||
/**
|
||||
* get events in range
|
||||
*/
|
||||
public getEventsInRange(eventsInRangeDto: EventsInRangeDTO): Observable<EventsInRangeDTO[]> {
|
||||
const params = new HttpParams()
|
||||
.set('startDate', eventsInRangeDto.startTime!.toISOString())
|
||||
.set('endDate', eventsInRangeDto.endTime!.toISOString());
|
||||
return this.http.get<EventsInRangeDTO[]>(this.apiUrl+'', { params });
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new record.
|
||||
*/
|
||||
public create(data: EventFormDTO): Observable<Event> {
|
||||
return this.http.post<Event>(this.apiUrl+'/events', data);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
<!-- dvbooking-cli/src/templates/angular/form.component.html.tpl -->
|
||||
|
||||
<!-- Generated by the CLI -->
|
||||
<div class="p-4 md:p-8">
|
||||
<div class="card bg-base-100 shadow-xl max-w-2xl mx-auto">
|
||||
@@ -29,7 +27,7 @@
|
||||
<input type="text" formControlName="timezone" class="input input-bordered w-full" /></div>
|
||||
|
||||
<div class="form-control"><label class="label cursor-pointer justify-start gap-4">
|
||||
<span class="label-text">is_recurring</span>
|
||||
<span class="label-text">is_recurring</span>
|
||||
<input type="checkbox" formControlName="is_recurring" class="checkbox" />
|
||||
</label></div>
|
||||
|
||||
@@ -48,4 +46,4 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user