different colors for different groups
This commit is contained in:
12
dashboard/package-lock.json
generated
12
dashboard/package-lock.json
generated
@@ -10,6 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@syncfusion/ej2-react-buttons": "^30.1.37",
|
"@syncfusion/ej2-react-buttons": "^30.1.37",
|
||||||
"@syncfusion/ej2-react-calendars": "^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-grids": "^30.1.37",
|
||||||
"@syncfusion/ej2-react-kanban": "^30.1.37",
|
"@syncfusion/ej2-react-kanban": "^30.1.37",
|
||||||
"@syncfusion/ej2-react-notifications": "^30.1.37",
|
"@syncfusion/ej2-react-notifications": "^30.1.37",
|
||||||
@@ -1216,6 +1217,17 @@
|
|||||||
"@syncfusion/ej2-react-base": "~30.1.37"
|
"@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": {
|
"node_modules/@syncfusion/ej2-react-grids": {
|
||||||
"version": "30.1.37",
|
"version": "30.1.37",
|
||||||
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-react-grids/-/ej2-react-grids-30.1.37.tgz",
|
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-react-grids/-/ej2-react-grids-30.1.37.tgz",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@syncfusion/ej2-react-buttons": "^30.1.37",
|
"@syncfusion/ej2-react-buttons": "^30.1.37",
|
||||||
"@syncfusion/ej2-react-calendars": "^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-grids": "^30.1.37",
|
||||||
"@syncfusion/ej2-react-kanban": "^30.1.37",
|
"@syncfusion/ej2-react-kanban": "^30.1.37",
|
||||||
"@syncfusion/ej2-react-notifications": "^30.1.37",
|
"@syncfusion/ej2-react-notifications": "^30.1.37",
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ const sidebarItems = [
|
|||||||
{ name: 'Dashboard', path: '/', icon: LayoutDashboard },
|
{ name: 'Dashboard', path: '/', icon: LayoutDashboard },
|
||||||
{ name: 'Termine', path: '/termine', icon: Calendar },
|
{ name: 'Termine', path: '/termine', icon: Calendar },
|
||||||
{ name: 'Ressourcen', path: '/ressourcen', icon: Boxes },
|
{ name: 'Ressourcen', path: '/ressourcen', icon: Boxes },
|
||||||
|
{ name: 'Raumgruppen', path: '/infoscr_groups', icon: MonitorDotIcon },
|
||||||
{ name: 'Infoscreens', path: '/Infoscreens', icon: Monitor },
|
{ name: 'Infoscreens', path: '/Infoscreens', icon: Monitor },
|
||||||
{ name: 'Gruppen', path: '/infoscr_groups', icon: MonitorDotIcon },
|
|
||||||
{ name: 'Medien', path: '/medien', icon: Image },
|
{ name: 'Medien', path: '/medien', icon: Image },
|
||||||
{ name: 'Benutzer', path: '/benutzer', icon: User },
|
{ name: 'Benutzer', path: '/benutzer', icon: User },
|
||||||
{ name: 'Einstellungen', path: '/einstellungen', icon: Settings },
|
{ name: 'Einstellungen', path: '/einstellungen', icon: Settings },
|
||||||
|
|||||||
@@ -8,10 +8,9 @@ export interface Event {
|
|||||||
extendedProps: Record<string, unknown>;
|
extendedProps: Record<string, unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchEvents(): Promise<Event[]> {
|
export async function fetchEvents(groupId: string) {
|
||||||
const response = await fetch('/api/events');
|
const res = await fetch(`/api/events?group_id=${encodeURIComponent(groupId)}`);
|
||||||
if (!response.ok) {
|
const data = await res.json();
|
||||||
throw new Error('Fehler beim Laden der Events');
|
if (!res.ok || data.error) throw new Error(data.error || 'Fehler beim Laden der Termine');
|
||||||
}
|
return data;
|
||||||
return await response.json();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,36 @@ import {
|
|||||||
ViewsDirective,
|
ViewsDirective,
|
||||||
ViewDirective,
|
ViewDirective,
|
||||||
} from '@syncfusion/ej2-react-schedule';
|
} from '@syncfusion/ej2-react-schedule';
|
||||||
|
import { DropDownListComponent } from '@syncfusion/ej2-react-dropdowns';
|
||||||
import { L10n, loadCldr, setCulture } from '@syncfusion/ej2-base';
|
import { L10n, loadCldr, setCulture } from '@syncfusion/ej2-base';
|
||||||
|
import type { EventRenderedArgs } from '@syncfusion/ej2-react-schedule';
|
||||||
import { fetchEvents } from './apiEvents';
|
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 de from 'cldr-data/main/de/ca-gregorian.json';
|
||||||
import * as numbers from 'cldr-data/main/de/numbers.json';
|
import * as numbers from 'cldr-data/main/de/numbers.json';
|
||||||
import * as timeZoneNames from 'cldr-data/main/de/timeZoneNames.json';
|
import * as timeZoneNames from 'cldr-data/main/de/timeZoneNames.json';
|
||||||
@@ -45,29 +72,97 @@ L10n.load({
|
|||||||
setCulture('de');
|
setCulture('de');
|
||||||
|
|
||||||
const Appointments: React.FC = () => {
|
const Appointments: React.FC = () => {
|
||||||
|
const [groups, setGroups] = useState<Group[]>([]);
|
||||||
|
const [selectedGroupId, setSelectedGroupId] = useState<string | null>(null);
|
||||||
const [events, setEvents] = useState<Event[]>([]);
|
const [events, setEvents] = useState<Event[]>([]);
|
||||||
|
|
||||||
|
// Gruppen laden
|
||||||
useEffect(() => {
|
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 (
|
return (
|
||||||
<ScheduleComponent
|
<div>
|
||||||
height="650px"
|
<h1 className="text-2xl font-bold mb-4">Terminmanagement</h1>
|
||||||
locale="de"
|
<div
|
||||||
currentView="Week"
|
style={{ marginBottom: 16, maxWidth: 500, display: 'flex', alignItems: 'center', gap: 12 }}
|
||||||
eventSettings={{ dataSource: events }}
|
>
|
||||||
firstDayOfWeek={1} // Montag als ersten Wochentag setzen
|
<label htmlFor="groupDropdown" className="mb-0 mr-2" style={{ whiteSpace: 'nowrap' }}>
|
||||||
>
|
Raumgruppe auswählen:
|
||||||
<ViewsDirective>
|
</label>
|
||||||
<ViewDirective option="Day" />
|
<DropDownListComponent
|
||||||
<ViewDirective option="Week" />
|
id="groupDropdown"
|
||||||
<ViewDirective option="WorkWeek" />
|
dataSource={groups}
|
||||||
<ViewDirective option="Month" />
|
fields={{ text: 'name', value: 'id' }}
|
||||||
<ViewDirective option="Agenda" />
|
placeholder="Gruppe auswählen"
|
||||||
</ViewsDirective>
|
value={selectedGroupId}
|
||||||
<Inject services={[Day, Week, WorkWeek, Month, Agenda]} />
|
change={e => {
|
||||||
</ScheduleComponent>
|
setEvents([]); // Events sofort leeren
|
||||||
|
setSelectedGroupId(e.value as string);
|
||||||
|
}}
|
||||||
|
style={{ flex: 1 }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<ScheduleComponent
|
||||||
|
key={selectedGroupId} // <- erzwingt komplettes Neurendern bei Gruppenwechsel
|
||||||
|
height="750px"
|
||||||
|
locale="de"
|
||||||
|
currentView="Week"
|
||||||
|
eventSettings={{
|
||||||
|
dataSource: events,
|
||||||
|
}}
|
||||||
|
eventRendered={(args: EventRenderedArgs) => {
|
||||||
|
// 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}
|
||||||
|
>
|
||||||
|
<ViewsDirective>
|
||||||
|
<ViewDirective option="Day" />
|
||||||
|
<ViewDirective option="Week" />
|
||||||
|
<ViewDirective option="WorkWeek" />
|
||||||
|
<ViewDirective option="Month" />
|
||||||
|
<ViewDirective option="Agenda" />
|
||||||
|
</ViewsDirective>
|
||||||
|
<Inject services={[Day, Week, WorkWeek, Month, Agenda]} />
|
||||||
|
</ScheduleComponent>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
52
dashboard/src/groupColors.ts
Normal file
52
dashboard/src/groupColors.ts
Normal file
@@ -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];
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user