add fullcalendar example

This commit is contained in:
Roland Schneider 2025-11-21 08:10:49 +01:00
parent 02cad3dbcd
commit dfc3afd4a9
7 changed files with 209 additions and 14 deletions

View File

@ -14,6 +14,12 @@
"@angular/forms": "^20.3.0", "@angular/forms": "^20.3.0",
"@angular/platform-browser": "^20.3.0", "@angular/platform-browser": "^20.3.0",
"@angular/router": "^20.3.0", "@angular/router": "^20.3.0",
"@fullcalendar/angular": "^6.1.19",
"@fullcalendar/core": "^6.1.19",
"@fullcalendar/daygrid": "^6.1.19",
"@fullcalendar/interaction": "^6.1.19",
"@fullcalendar/list": "^6.1.19",
"@fullcalendar/timegrid": "^6.1.19",
"@tailwindcss/postcss": "^4.1.17", "@tailwindcss/postcss": "^4.1.17",
"daisyui": "^5.4.5", "daisyui": "^5.4.5",
"jwt-decode": "^4.0.0", "jwt-decode": "^4.0.0",
@ -1358,6 +1364,68 @@
"node": ">=18" "node": ">=18"
} }
}, },
"node_modules/@fullcalendar/angular": {
"version": "6.1.19",
"resolved": "https://registry.npmjs.org/@fullcalendar/angular/-/angular-6.1.19.tgz",
"integrity": "sha512-a3TmjKnF8xprH1aNgFn9zYehEhM4GBAyh+91SJymno2j1cE8D8z0+W1HNwtDekKWwJt/5YoinCvDTHydmF/kKw==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/common": "12 - 20",
"@angular/core": "12 - 20",
"@fullcalendar/core": "~6.1.19"
}
},
"node_modules/@fullcalendar/core": {
"version": "6.1.19",
"resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.19.tgz",
"integrity": "sha512-z0aVlO5e4Wah6p6mouM0UEqtRf1MZZPt4mwzEyU6kusaNL+dlWQgAasF2cK23hwT4cmxkEmr4inULXgpyeExdQ==",
"license": "MIT",
"dependencies": {
"preact": "~10.12.1"
}
},
"node_modules/@fullcalendar/daygrid": {
"version": "6.1.19",
"resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-6.1.19.tgz",
"integrity": "sha512-IAAfnMICnVWPjpT4zi87i3FEw0xxSza0avqY/HedKEz+l5MTBYvCDPOWDATpzXoLut3aACsjktIyw9thvIcRYQ==",
"license": "MIT",
"peerDependencies": {
"@fullcalendar/core": "~6.1.19"
}
},
"node_modules/@fullcalendar/interaction": {
"version": "6.1.19",
"resolved": "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-6.1.19.tgz",
"integrity": "sha512-GOciy79xe8JMVp+1evAU3ytdwN/7tv35t5i1vFkifiuWcQMLC/JnLg/RA2s4sYmQwoYhTw/p4GLcP0gO5B3X5w==",
"license": "MIT",
"peerDependencies": {
"@fullcalendar/core": "~6.1.19"
}
},
"node_modules/@fullcalendar/list": {
"version": "6.1.19",
"resolved": "https://registry.npmjs.org/@fullcalendar/list/-/list-6.1.19.tgz",
"integrity": "sha512-knZHpAVF0LbzZpSJSUmLUUzF0XlU/MRGK+Py2s0/mP93bCtno1k2L3XPs/kzh528hSjehwLm89RgKTSfW1P6cA==",
"license": "MIT",
"peerDependencies": {
"@fullcalendar/core": "~6.1.19"
}
},
"node_modules/@fullcalendar/timegrid": {
"version": "6.1.19",
"resolved": "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-6.1.19.tgz",
"integrity": "sha512-OuzpUueyO9wB5OZ8rs7TWIoqvu4v3yEqdDxZ2VcsMldCpYJRiOe7yHWKr4ap5Tb0fs7Rjbserc/b6Nt7ol6BRg==",
"license": "MIT",
"dependencies": {
"@fullcalendar/daygrid": "~6.1.19"
},
"peerDependencies": {
"@fullcalendar/core": "~6.1.19"
}
},
"node_modules/@inquirer/ansi": { "node_modules/@inquirer/ansi": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.1.tgz",
@ -8805,6 +8873,16 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/preact": {
"version": "10.12.1",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz",
"integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
}
},
"node_modules/proc-log": { "node_modules/proc-log": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz",

View File

@ -28,6 +28,12 @@
"@angular/forms": "^20.3.0", "@angular/forms": "^20.3.0",
"@angular/platform-browser": "^20.3.0", "@angular/platform-browser": "^20.3.0",
"@angular/router": "^20.3.0", "@angular/router": "^20.3.0",
"@fullcalendar/angular": "^6.1.19",
"@fullcalendar/core": "^6.1.19",
"@fullcalendar/daygrid": "^6.1.19",
"@fullcalendar/interaction": "^6.1.19",
"@fullcalendar/list": "^6.1.19",
"@fullcalendar/timegrid": "^6.1.19",
"@tailwindcss/postcss": "^4.1.17", "@tailwindcss/postcss": "^4.1.17",
"daisyui": "^5.4.5", "daisyui": "^5.4.5",
"jwt-decode": "^4.0.0", "jwt-decode": "^4.0.0",

View File

@ -37,8 +37,19 @@ import { BookingFormComponent } from "./features/bookings/components/booking-for
import { BookingDetailsComponent } from "./features/bookings/components/booking-details/booking-details.component"; import { BookingDetailsComponent } from "./features/bookings/components/booking-details/booking-details.component";
import { BookingTableComponent } from "./features/bookings/components/booking-table/booking-table.component"; import { BookingTableComponent } from "./features/bookings/components/booking-table/booking-table.component";
import { BookingListComponent } from "./features/bookings/components/booking-list/booking-list.component"; import { BookingListComponent } from "./features/bookings/components/booking-list/booking-list.component";
import { CalendarView } from './features/calendar/components/calendar-view/calendar-view';
export const routes: Routes = [ export const routes: Routes = [
{
path: 'calendar',
component: CalendarView,
canActivate: [AuthGuard],
data: {
roles: ['admin'],
},
},
{ {
path: 'bookings/new', path: 'bookings/new',
component: BookingFormComponent, component: BookingFormComponent,

View File

@ -3,18 +3,17 @@ import { Router, RouterOutlet } from '@angular/router';
import { AuthService } from './auth/auth.service'; import { AuthService } from './auth/auth.service';
import { AdminLayoutRs1 } from '../../projects/rschneider/ng-daisyui/src/lib/layout'; import { AdminLayoutRs1 } from '../../projects/rschneider/ng-daisyui/src/lib/layout';
import { Menu, MenuItem } from './components/menu/menu'; import { Menu, MenuItem } from './components/menu/menu';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
imports: [RouterOutlet, AdminLayoutRs1, Menu], imports: [RouterOutlet, AdminLayoutRs1, Menu],
templateUrl: './app.html', templateUrl: './app.html',
styleUrl: './app.css', styleUrl: './app.css',
}) })
export class App { export class App {
protected readonly title = signal('admin'); protected readonly title = signal('admin');
protected menuConfig: MenuItem[] = [ protected menuConfig: MenuItem[] = [];
];
protected currentUserRoles: string[] = ['admin']; protected currentUserRoles: string[] = ['admin'];
protected menuTitle: string | undefined = 'Menü'; protected menuTitle: string | undefined = 'Menü';
@ -27,7 +26,7 @@ export class App {
svgIcon: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6"> svgIcon: `<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="M6.429 9.75 2.25 12l4.179 2.25m0-4.5 5.571 3 5.571-3m-11.142 0L2.25 7.5 12 2.25l9.75 5.25-4.179 2.25m0 0L21.75 12l-4.179 2.25m0 0 4.179 2.25L12 21.75 2.25 16.5l4.179-2.25m11.142 0-5.571 3-5.571-3" /> <path stroke-linecap="round" stroke-linejoin="round" d="M6.429 9.75 2.25 12l4.179 2.25m0-4.5 5.571 3 5.571-3m-11.142 0L2.25 7.5 12 2.25l9.75 5.25-4.179 2.25m0 0L21.75 12l-4.179 2.25m0 0 4.179 2.25L12 21.75 2.25 16.5l4.179-2.25m11.142 0-5.571 3-5.571-3" />
</svg> </svg>
` `,
}, },
{ {
menuText: 'Események', menuText: 'Események',
@ -35,15 +34,23 @@ export class App {
svgIcon: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6"> svgIcon: `<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="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5" /> <path stroke-linecap="round" stroke-linejoin="round" d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5" />
</svg> </svg>
` `,
}, },
{ {
menuText: 'Naptár',
targetUrl: '/calendar',
svgIcon: `<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="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5m-9-6h.008v.008H12v-.008ZM12 15h.008v.008H12V15Zm0 2.25h.008v.008H12v-.008ZM9.75 15h.008v.008H9.75V15Zm0 2.25h.008v.008H9.75v-.008ZM7.5 15h.008v.008H7.5V15Zm0 2.25h.008v.008H7.5v-.008Zm6.75-4.5h.008v.008h-.008v-.008Zm0 2.25h.008v.008h-.008V15Zm0 2.25h.008v.008h-.008v-.008Zm2.25-4.5h.008v.008H16.5v-.008Zm0 2.25h.008v.008H16.5V15Z" />
</svg>
`,
},
{
menuText: 'Felhasználók', menuText: 'Felhasználók',
targetUrl: '/user/table', targetUrl: '/user/table',
svgIcon: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6"> svgIcon: `<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 19.128a9.38 9.38 0 0 0 2.625.372 9.337 9.337 0 0 0 4.121-.952 4.125 4.125 0 0 0-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 0 1 8.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0 1 11.964-3.07M12 6.375a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0Zm8.25 2.25a2.625 2.625 0 1 1-5.25 0 2.625 2.625 0 0 1 5.25 0Z" /> <path stroke-linecap="round" stroke-linejoin="round" d="M15 19.128a9.38 9.38 0 0 0 2.625.372 9.337 9.337 0 0 0 4.121-.952 4.125 4.125 0 0 0-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 0 1 8.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0 1 11.964-3.07M12 6.375a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0Zm8.25 2.25a2.625 2.625 0 1 1-5.25 0 2.625 2.625 0 0 1 5.25 0Z" />
</svg> </svg>
` `,
}, },
{ {
menuText: 'Felhasználó Csoport', menuText: 'Felhasználó Csoport',
@ -51,19 +58,19 @@ export class App {
svgIcon: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6"> svgIcon: `<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="M18 18.72a9.094 9.094 0 0 0 3.741-.479 3 3 0 0 0-4.682-2.72m.94 3.198.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0 1 12 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 0 1 6 18.719m12 0a5.971 5.971 0 0 0-.941-3.197m0 0A5.995 5.995 0 0 0 12 12.75a5.995 5.995 0 0 0-5.058 2.772m0 0a3 3 0 0 0-4.681 2.72 8.986 8.986 0 0 0 3.74.477m.94-3.197a5.971 5.971 0 0 0-.94 3.197M15 6.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm6 3a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Zm-13.5 0a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Z" /> <path stroke-linecap="round" stroke-linejoin="round" d="M18 18.72a9.094 9.094 0 0 0 3.741-.479 3 3 0 0 0-4.682-2.72m.94 3.198.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0 1 12 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 0 1 6 18.719m12 0a5.971 5.971 0 0 0-.941-3.197m0 0A5.995 5.995 0 0 0 12 12.75a5.995 5.995 0 0 0-5.058 2.772m0 0a3 3 0 0 0-4.681 2.72 8.986 8.986 0 0 0 3.74.477m.94-3.197a5.971 5.971 0 0 0-.94 3.197M15 6.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm6 3a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Zm-13.5 0a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Z" />
</svg> </svg>
` `,
}, },
{ {
menuText: 'Felhasználó Szerepek', menuText: 'Felhasználó Szerepek',
targetUrl: '/user-role/table', targetUrl: '/user-role/table',
svgIcon: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6"> svgIcon: `<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.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 0 1-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 0 1 1.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 0 0-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 0 1-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 0 0-3.375-3.375h-1.5a1.125 1.125 0 0 1-1.125-1.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H9.75" /> <path stroke-linecap="round" stroke-linejoin="round" d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 0 1-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 0 1 1.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 0 0-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 0 1-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 0 0-3.375-3.375h-1.5a1.125 1.125 0 0 1-1.125-1.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H9.75" />
</svg> </svg>
` `,
}, },
] ];
} }
logout(): void { logout(): void {
@ -81,8 +88,8 @@ export class App {
}); });
} }
loggedIn(){ loggedIn() {
return this.authService.isLoggedIn() return this.authService.isLoggedIn();
} }
} }

View File

@ -0,0 +1,8 @@
<div>
<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>
</div>

View File

@ -0,0 +1,85 @@
import { Component, ElementRef, ViewChild } from '@angular/core';
import { FullCalendarComponent, FullCalendarModule } from '@fullcalendar/angular';
import { CalendarOptions } from '@fullcalendar/core';
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';
@Component({
selector: 'app-calendar-view',
imports: [FullCalendarModule],
templateUrl: './calendar-view.html',
styleUrl: './calendar-view.css',
})
export class CalendarView {
@ViewChild('startHour') startHour!: ElementRef;
@ViewChild('calendar') calendarComponent: FullCalendarComponent | undefined;
calendarOptions: CalendarOptions;
constructor() {
const start = new Date();
start.setHours(10,0,0)
const end = new Date();
end.setHours(11,0,0)
this.calendarOptions = {
plugins: [dayGridPlugin, timeGridPlugin, listPlugin,interactionPlugin],
initialView: 'dayGridMonth',
weekends: true,
headerToolbar: {
left: 'prev,next,today',
center: 'title',
// right: 'dayGridMonth,dayGridWeek,dayGridDay' // user can switch between the two
right: 'dayGridMonth,dayGridWeek,dayGridDay,timeGridWeek,timeGridDay,listWeek',
},
events: [
{ title: 'Meeting1 until'+end.toString(), start, end },
],
eventClick: function(info) {
console.info('Event info: ' , info);
// change the border color just for fun
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,
});
}
};
}
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
})
}
}