diff --git a/dashboard/package-lock.json b/dashboard/package-lock.json index eed01bd..ccf4804 100644 --- a/dashboard/package-lock.json +++ b/dashboard/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@syncfusion/ej2-react-buttons": "^30.1.37", "@syncfusion/ej2-react-calendars": "^30.1.37", + "@syncfusion/ej2-react-dropdowns": "^30.1.37", "@syncfusion/ej2-react-grids": "^30.1.37", "@syncfusion/ej2-react-kanban": "^30.1.37", "@syncfusion/ej2-react-notifications": "^30.1.37", @@ -1216,6 +1217,17 @@ "@syncfusion/ej2-react-base": "~30.1.37" } }, + "node_modules/@syncfusion/ej2-react-dropdowns": { + "version": "30.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-react-dropdowns/-/ej2-react-dropdowns-30.1.37.tgz", + "integrity": "sha512-4mgqlsC8T/9YVMCo1LZIZVUfVY3llv4hndm5MlYIOSaKeVqpfbu9iUvTOZdg4NIEruaK04BGnEemO0TZkMi4Rg==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~30.1.37", + "@syncfusion/ej2-dropdowns": "30.1.37", + "@syncfusion/ej2-react-base": "~30.1.37" + } + }, "node_modules/@syncfusion/ej2-react-grids": { "version": "30.1.37", "resolved": "https://registry.npmjs.org/@syncfusion/ej2-react-grids/-/ej2-react-grids-30.1.37.tgz", diff --git a/dashboard/package.json b/dashboard/package.json index 8972d2e..8b1ad18 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -12,6 +12,7 @@ "dependencies": { "@syncfusion/ej2-react-buttons": "^30.1.37", "@syncfusion/ej2-react-calendars": "^30.1.37", + "@syncfusion/ej2-react-dropdowns": "^30.1.37", "@syncfusion/ej2-react-grids": "^30.1.37", "@syncfusion/ej2-react-kanban": "^30.1.37", "@syncfusion/ej2-react-notifications": "^30.1.37", diff --git a/dashboard/src/App.tsx b/dashboard/src/App.tsx index 2b019fa..f527813 100644 --- a/dashboard/src/App.tsx +++ b/dashboard/src/App.tsx @@ -21,8 +21,8 @@ const sidebarItems = [ { name: 'Dashboard', path: '/', icon: LayoutDashboard }, { name: 'Termine', path: '/termine', icon: Calendar }, { name: 'Ressourcen', path: '/ressourcen', icon: Boxes }, + { name: 'Raumgruppen', path: '/infoscr_groups', icon: MonitorDotIcon }, { name: 'Infoscreens', path: '/Infoscreens', icon: Monitor }, - { name: 'Gruppen', path: '/infoscr_groups', icon: MonitorDotIcon }, { name: 'Medien', path: '/medien', icon: Image }, { name: 'Benutzer', path: '/benutzer', icon: User }, { name: 'Einstellungen', path: '/einstellungen', icon: Settings }, diff --git a/dashboard/src/apiEvents.ts b/dashboard/src/apiEvents.ts index 981dd92..16ce32f 100644 --- a/dashboard/src/apiEvents.ts +++ b/dashboard/src/apiEvents.ts @@ -8,10 +8,9 @@ export interface Event { extendedProps: Record; } -export async function fetchEvents(): Promise { - const response = await fetch('/api/events'); - if (!response.ok) { - throw new Error('Fehler beim Laden der Events'); - } - return await response.json(); +export async function fetchEvents(groupId: string) { + const res = await fetch(`/api/events?group_id=${encodeURIComponent(groupId)}`); + const data = await res.json(); + if (!res.ok || data.error) throw new Error(data.error || 'Fehler beim Laden der Termine'); + return data; } diff --git a/dashboard/src/appointments.tsx b/dashboard/src/appointments.tsx index 781d31f..a9f3354 100644 --- a/dashboard/src/appointments.tsx +++ b/dashboard/src/appointments.tsx @@ -10,9 +10,36 @@ import { ViewsDirective, ViewDirective, } from '@syncfusion/ej2-react-schedule'; +import { DropDownListComponent } from '@syncfusion/ej2-react-dropdowns'; import { L10n, loadCldr, setCulture } from '@syncfusion/ej2-base'; +import type { EventRenderedArgs } from '@syncfusion/ej2-react-schedule'; import { fetchEvents } from './apiEvents'; -import type { Event } from './apiEvents'; +import { fetchGroups } from './apiGroups'; +import { getGroupColor } from './groupColors'; + +// Typ für Gruppe ergänzen +type Group = { + id: string; + name: string; +}; + +// Typ für Event hinzufügen +type Event = { + Id: string; + Subject: string; + StartTime: Date; + EndTime: Date; + IsAllDay: boolean; +}; + +type RawEvent = { + Id: string; + Subject: string; + StartTime: string; + EndTime: string; + IsAllDay: boolean; +}; + import * as de from 'cldr-data/main/de/ca-gregorian.json'; import * as numbers from 'cldr-data/main/de/numbers.json'; import * as timeZoneNames from 'cldr-data/main/de/timeZoneNames.json'; @@ -45,29 +72,97 @@ L10n.load({ setCulture('de'); const Appointments: React.FC = () => { + const [groups, setGroups] = useState([]); + const [selectedGroupId, setSelectedGroupId] = useState(null); const [events, setEvents] = useState([]); + // Gruppen laden useEffect(() => { - fetchEvents().then(setEvents).catch(console.error); + fetchGroups() + .then(data => { + // Nur Gruppen mit id berücksichtigen (nicht zugeordnet ignorieren) + const filtered = Array.isArray(data) + ? data.filter(g => g.id && g.name && g.name !== 'nicht zugeordnet') + : []; + setGroups(filtered); + if (filtered.length > 0) setSelectedGroupId(filtered[0].id); + }) + .catch(console.error); }, []); + // Termine für die ausgewählte Gruppe laden + useEffect(() => { + if (selectedGroupId) { + fetchEvents(selectedGroupId) + .then((data: RawEvent[]) => { + const mapped: Event[] = data.map(e => ({ + Id: e.Id, + Subject: e.Subject, + StartTime: new Date(e.StartTime), + EndTime: new Date(e.EndTime), + IsAllDay: e.IsAllDay, + })); + setEvents(mapped); + }) + .catch(console.error); + } else { + setEvents([]); + } + }, [selectedGroupId]); + + console.log('Aktuelle Events:', events); + return ( - - - - - - - - - - +
+

Terminmanagement

+
+ + { + setEvents([]); // Events sofort leeren + setSelectedGroupId(e.value as string); + }} + style={{ flex: 1 }} + /> +
+ { + // Farbe basierend auf der Gruppe setzen + if (selectedGroupId && args.data && args.data.Id) { + const groupColor = getGroupColor(selectedGroupId, groups); + if (groupColor) { + args.element.style.backgroundColor = groupColor; + } + } + }} + firstDayOfWeek={1} + > + + + + + + + + + +
); }; diff --git a/dashboard/src/groupColors.ts b/dashboard/src/groupColors.ts new file mode 100644 index 0000000..8c00bac --- /dev/null +++ b/dashboard/src/groupColors.ts @@ -0,0 +1,52 @@ +// 20 gut unterscheidbare Farben für Gruppen + +export const groupColorPalette: string[] = [ + '#1E90FF', // Blau + '#28A745', // Grün + '#FFC107', // Gelb + '#DC3545', // Rot + '#6F42C1', // Lila + '#20C997', // Türkis + '#FD7E14', // Orange + '#6610F2', // Violett + '#17A2B8', // Cyan + '#E83E8C', // Pink + '#FF5733', // Koralle + '#2ECC40', // Hellgrün + '#FFB300', // Dunkelgelb + '#00796B', // Petrol + '#C70039', // Dunkelrot + '#8D6E63', // Braun + '#607D8B', // Grau-Blau + '#00B8D4', // Türkisblau + '#FF6F00', // Dunkelorange + '#9C27B0', // Dunkellila +]; + +// Gibt für eine Gruppen-ID immer dieselbe Farbe zurück (Index basiert auf Gruppenliste) +export function getGroupColor(groupId: string, groups: { id: string }[]): string { + const colorPalette = [ + '#1E90FF', + '#28A745', + '#FFC107', + '#DC3545', + '#6F42C1', + '#20C997', + '#FD7E14', + '#6610F2', + '#17A2B8', + '#E83E8C', + '#FF5733', + '#2ECC40', + '#FFB300', + '#00796B', + '#C70039', + '#8D6E63', + '#607D8B', + '#00B8D4', + '#FF6F00', + '#9C27B0', + ]; + const idx = groups.findIndex(g => g.id === groupId); + return colorPalette[idx % colorPalette.length]; +}