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": {
|
||||
"@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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -8,10 +8,9 @@ export interface Event {
|
||||
extendedProps: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export async function fetchEvents(): Promise<Event[]> {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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,19 +72,86 @@ L10n.load({
|
||||
setCulture('de');
|
||||
|
||||
const Appointments: React.FC = () => {
|
||||
const [groups, setGroups] = useState<Group[]>([]);
|
||||
const [selectedGroupId, setSelectedGroupId] = useState<string | null>(null);
|
||||
const [events, setEvents] = useState<Event[]>([]);
|
||||
|
||||
// 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 (
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold mb-4">Terminmanagement</h1>
|
||||
<div
|
||||
style={{ marginBottom: 16, maxWidth: 500, display: 'flex', alignItems: 'center', gap: 12 }}
|
||||
>
|
||||
<label htmlFor="groupDropdown" className="mb-0 mr-2" style={{ whiteSpace: 'nowrap' }}>
|
||||
Raumgruppe auswählen:
|
||||
</label>
|
||||
<DropDownListComponent
|
||||
id="groupDropdown"
|
||||
dataSource={groups}
|
||||
fields={{ text: 'name', value: 'id' }}
|
||||
placeholder="Gruppe auswählen"
|
||||
value={selectedGroupId}
|
||||
change={e => {
|
||||
setEvents([]); // Events sofort leeren
|
||||
setSelectedGroupId(e.value as string);
|
||||
}}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
</div>
|
||||
<ScheduleComponent
|
||||
height="650px"
|
||||
key={selectedGroupId} // <- erzwingt komplettes Neurendern bei Gruppenwechsel
|
||||
height="750px"
|
||||
locale="de"
|
||||
currentView="Week"
|
||||
eventSettings={{ dataSource: events }}
|
||||
firstDayOfWeek={1} // Montag als ersten Wochentag setzen
|
||||
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" />
|
||||
@@ -68,6 +162,7 @@ const Appointments: React.FC = () => {
|
||||
</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