add single calendar dialog
This commit is contained in:
parent
e1ae0a36d7
commit
364be9976a
@ -3,11 +3,12 @@ import { Router, RouterOutlet } from '@angular/router';
|
||||
import { AuthService } from './auth/auth.service';
|
||||
import { AdminLayoutRs1 } from '../../projects/rschneider/ng-daisyui/src/lib/layout';
|
||||
import { Menu, MenuItem } from './components/menu/menu';
|
||||
import { SingleEventDashboardCard } from './features/calendar/components/calendar-view/single-event-dashboard-card/single-event-dashboard-card';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
|
||||
imports: [RouterOutlet, AdminLayoutRs1, Menu],
|
||||
imports: [RouterOutlet, AdminLayoutRs1, Menu, SingleEventDashboardCard], // Import it here
|
||||
templateUrl: './app.html',
|
||||
styleUrl: './app.css',
|
||||
})
|
||||
@ -74,8 +75,6 @@ export class App {
|
||||
}
|
||||
|
||||
logout(): void {
|
||||
// With the interceptor fixed, this is now the correct and robust way.
|
||||
// The error from a failed server logout will propagate here.
|
||||
this.authService.serverSideLogout().subscribe({
|
||||
next: () => {
|
||||
console.log('Server-side logout successful.');
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
{{config().data}}
|
||||
@if (config().data){
|
||||
<div class="overflow-x-auto" [ngClass]="config().styleClass">
|
||||
<table class="table w-full">
|
||||
<table class="table w-full table-zebra">
|
||||
<tbody>
|
||||
@for (row of config().rows; track row.attribute) {
|
||||
<tr [ngClass]="row.getDetailClass ? row.getDetailClass(config().data) : ''">
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
|
||||
|
||||
@if (workflow() == 'event-dashboard' && selectedEvent()) {
|
||||
<rs-daisy-modal [isOpen]="true" (closeClick)="closeDialog()">
|
||||
<rs-daisy-modal [isOpen]="true" (closeClick)="closeDialog()" [modalBoxStyleClass]="'max-w-none w-2xl'">
|
||||
<app-single-event-dashboard [event]="selectedEvent()"></app-single-event-dashboard>
|
||||
</rs-daisy-modal>
|
||||
}
|
||||
|
||||
@ -12,10 +12,11 @@ import { CalendarService } from '../../services/calendar.service';
|
||||
import { CalendarEventDto, EventsInRangeDTO } from '../../models/events-in-range-dto.model';
|
||||
import { map } from 'rxjs';
|
||||
import { SingleEventDashboard } from './single-event-dashboard/single-event-dashboard';
|
||||
import { JsonPipe } from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector: 'app-calendar-view',
|
||||
imports: [FullCalendarModule, Modal, CreateEventForm, SingleEventDashboard],
|
||||
imports: [FullCalendarModule, Modal, CreateEventForm, SingleEventDashboard, JsonPipe],
|
||||
templateUrl: './calendar-view.html',
|
||||
styleUrl: './calendar-view.css',
|
||||
})
|
||||
@ -86,7 +87,7 @@ export class CalendarView implements OnInit, AfterViewInit {
|
||||
// end: model.end_time,
|
||||
title: model.title,
|
||||
extendedProps: {
|
||||
event: value
|
||||
event: model
|
||||
}
|
||||
};
|
||||
if ( model.eventType){
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
<div class="card bg-base-100 shadow-sm" [class]="styleClass()">
|
||||
<div class="card-body items-center ">
|
||||
<h2 class="card-title">{{ title() }}</h2>
|
||||
<p>{{ description() }}</p>
|
||||
<div class="card-actions justify-end">
|
||||
<button class="btn btn-primary">
|
||||
<!-- Using the new @if control flow -->
|
||||
@if (svgIcon()) {
|
||||
<span [innerHTML]="svgIcon() | safeHtml"></span>
|
||||
}
|
||||
{{ buttonTitle() }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SingleEventDashboardCard } from './single-event-dashboard-card';
|
||||
|
||||
describe('SingleEventDashboardCard', () => {
|
||||
let component: SingleEventDashboardCard;
|
||||
let fixture: ComponentFixture<SingleEventDashboardCard>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [SingleEventDashboardCard]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(SingleEventDashboardCard);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,44 @@
|
||||
import { Component, computed, HostBinding, input } from '@angular/core';
|
||||
import { SafeHtmlPipe } from '../../../../../pipes/safe-html-pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-single-event-dashboard-card',
|
||||
standalone: true,
|
||||
imports: [
|
||||
SafeHtmlPipe,
|
||||
], // CommonModule is no longer needed
|
||||
templateUrl: './single-event-dashboard-card.html',
|
||||
styleUrl: './single-event-dashboard-card.css',
|
||||
})
|
||||
export class SingleEventDashboardCard {
|
||||
title= input<string>();
|
||||
description= input<string>();
|
||||
buttonTitle= input<string>();
|
||||
svgIcon= input<string>();
|
||||
|
||||
// 1. Define the Input Signal to receive class names as a string or array
|
||||
// Example: 'grow-2 align-center'
|
||||
public customClasses = input<string | null>(null);
|
||||
|
||||
// 2. Create a computed signal that filters out any null/undefined and returns the class string
|
||||
private hostClasses = computed(() => {
|
||||
// We append a base class that should always be present (e.g., 'flex-item')
|
||||
const classes = ['flex-item'];
|
||||
|
||||
const custom = this.customClasses();
|
||||
if (custom) {
|
||||
// Split the input string and add the class names
|
||||
classes.push(...custom.trim().split(/\s+/));
|
||||
}
|
||||
|
||||
// Return a single space-separated string
|
||||
return classes.join(' ');
|
||||
});
|
||||
|
||||
// 3. Use @HostBinding to bind the computed string to the host element's 'class' attribute
|
||||
@HostBinding('class')
|
||||
get classAttribute(): string {
|
||||
return this.hostClasses();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,3 +1,23 @@
|
||||
<h3>Esemény</h3>
|
||||
|
||||
@if (config) {
|
||||
<app-detail-view
|
||||
[config]="config"
|
||||
></app-detail-view>
|
||||
}
|
||||
|
||||
<div class="flex mt-3 gap-2 flex-wrap ">
|
||||
@for (card of cards; let i = $index; track i) {
|
||||
|
||||
<app-single-event-dashboard-card
|
||||
[title]="card.title"
|
||||
[svgIcon]="card.svgIcon"
|
||||
[description]="card.description"
|
||||
[buttonTitle]="card.buttonTitle"
|
||||
[customClasses]="'flex-1'"
|
||||
|
||||
></app-single-event-dashboard-card>
|
||||
<!-- [customClasses]="'flex-[50_1_0] w-50' "-->
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
@ -1,11 +1,18 @@
|
||||
import { Component, input, signal } from '@angular/core';
|
||||
import { Component, effect, input, signal } from '@angular/core';
|
||||
import { CalendarEventDto, EventsInRangeDTO } from '../../../models/events-in-range-dto.model';
|
||||
import { DetailView, DetailViewConfig } from '../../../../../components/detail-view/detail-view';
|
||||
import { JsonPipe } from '@angular/common';
|
||||
import { SvgIcons } from '../../../../../svg-icons';
|
||||
import { SafeHtmlPipe } from '../../../../../pipes/safe-html-pipe';
|
||||
import { SingleEventDashboardCard } from '../single-event-dashboard-card/single-event-dashboard-card';
|
||||
|
||||
@Component({
|
||||
selector: 'app-single-event-dashboard',
|
||||
imports: [
|
||||
DetailView,
|
||||
JsonPipe,
|
||||
SafeHtmlPipe,
|
||||
SingleEventDashboardCard,
|
||||
],
|
||||
templateUrl: './single-event-dashboard.html',
|
||||
styleUrl: './single-event-dashboard.css',
|
||||
@ -13,16 +20,75 @@ import { DetailView, DetailViewConfig } from '../../../../../components/detail-v
|
||||
export class SingleEventDashboard {
|
||||
|
||||
event = input<CalendarEventDto>();
|
||||
config: DetailViewConfig<CalendarEventDto> | undefined;
|
||||
|
||||
cards: CardConfig[] = [
|
||||
{
|
||||
buttonTitle: 'Szerkesztés',
|
||||
title: 'Szerkesztés',
|
||||
svgIcon: SvgIcons.heorPencilSquare,
|
||||
description: 'Az esemény módosítása'
|
||||
},
|
||||
{
|
||||
buttonTitle: 'Esemény lemondása',
|
||||
title: 'Esemény lemondása',
|
||||
svgIcon: SvgIcons.heroXcircle,
|
||||
description: 'Az esemény lemondása'
|
||||
},
|
||||
{
|
||||
buttonTitle: 'Esemény törlése',
|
||||
title: 'Esemény törlése',
|
||||
svgIcon: SvgIcons.heroTrash,
|
||||
description: 'Az esemény törlése'
|
||||
},
|
||||
{
|
||||
buttonTitle: 'Bejelentkezés',
|
||||
title: 'Időpont foglalás',
|
||||
svgIcon: SvgIcons.heroTrash,
|
||||
description: 'Időpont foglalása eseményre'
|
||||
},
|
||||
{
|
||||
buttonTitle: 'Lemondás',
|
||||
title: 'Időpont lemondása',
|
||||
svgIcon: SvgIcons.heroTrash,
|
||||
description: 'Az időpont lemondása'
|
||||
}
|
||||
];
|
||||
|
||||
config: DetailViewConfig<CalendarEventDto>;
|
||||
constructor() {
|
||||
effect(() => {
|
||||
this.config = {
|
||||
data: this.event()!,
|
||||
|
||||
rows: [{
|
||||
attribute: 'id',
|
||||
getTitle: 'Esemény azonosító',
|
||||
},
|
||||
{
|
||||
attribute: 'title',
|
||||
getTitle: '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,
|
||||
|
||||
}
|
||||
],
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
protected readonly SvgIcons = SvgIcons;
|
||||
}
|
||||
export interface CardConfig{
|
||||
title?: string;
|
||||
description?: string;
|
||||
buttonTitle?: string;
|
||||
svgIcon?: string;
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ export type BookingWithUserDto = {
|
||||
|
||||
// The final shape of a calendar event occurrence
|
||||
export type CalendarEventDto = {
|
||||
id: number;
|
||||
title: string;
|
||||
startTime: string,
|
||||
endTime: string,
|
||||
|
||||
@ -12,6 +12,16 @@ export class SvgIcons {
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
|
||||
</svg>
|
||||
`;
|
||||
|
||||
public static heorPencilSquare = `<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="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10" />
|
||||
</svg>
|
||||
`;
|
||||
|
||||
public static heroXcircle = `<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.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
||||
</svg>
|
||||
`;
|
||||
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user