diff --git a/dashboard/src/App.tsx b/dashboard/src/App.tsx index d03e342..cd84a9a 100644 --- a/dashboard/src/App.tsx +++ b/dashboard/src/App.tsx @@ -8,19 +8,19 @@ import { LayoutDashboard, Calendar, Boxes, - Users, - UserSquare, Image, User, Settings, + Monitor, + MonitorDotIcon } from 'lucide-react'; const sidebarItems = [ { name: 'Dashboard', path: '/', icon: LayoutDashboard }, { name: 'Termine', path: '/termine', icon: Calendar }, { name: 'Ressourcen', path: '/ressourcen', icon: Boxes }, - { name: 'Infoscreens', path: '/Infoscreens', icon: Users }, - { name: 'Gruppen', path: '/gruppen', icon: UserSquare }, + { name: 'Infoscreens', path: '/Infoscreens', icon: Monitor }, + { name: 'Gruppen', path: '/gruppen', 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 new file mode 100644 index 0000000..981dd92 --- /dev/null +++ b/dashboard/src/apiEvents.ts @@ -0,0 +1,17 @@ +export interface Event { + id: string; + title: string; + start: string; + end: string; + allDay: boolean; + classNames: string[]; + 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(); +} diff --git a/dashboard/src/appointments.tsx b/dashboard/src/appointments.tsx index 52bdfad..1d700ab 100644 --- a/dashboard/src/appointments.tsx +++ b/dashboard/src/appointments.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { ScheduleComponent, Day, @@ -6,15 +6,13 @@ import { WorkWeek, Month, Agenda, - TimelineViews, - TimelineMonth, Inject, ViewsDirective, ViewDirective, - ResourcesDirective, - ResourceDirective, } from '@syncfusion/ej2-react-schedule'; import { L10n, loadCldr, setCulture } from '@syncfusion/ej2-base'; +import { fetchEvents } from './apiEvents'; +import type { Event } from './apiEvents'; 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'; @@ -46,61 +44,31 @@ L10n.load({ // Kultur setzen setCulture('de'); -// Ressourcen-Daten -const resources = [ - { text: 'Raum A', id: 1, color: '#1aaa55' }, - { text: 'Raum B', id: 2, color: '#357cd2' }, - { text: 'Raum C', id: 3, color: '#7fa900' }, -]; +const Appointments: React.FC = () => { + const [events, setEvents] = useState([]); -// Dummy-Termine generieren -const now = new Date(); -const appointments = Array.from({ length: 10 }).map((_, i) => { - const dayOffset = Math.floor(i * 1.4); // verteilt auf 14 Tage - const start = new Date(now); - start.setDate(now.getDate() + dayOffset); - start.setHours(9 + (i % 4), 0, 0, 0); - const end = new Date(start); - end.setHours(start.getHours() + 1); + useEffect(() => { + fetchEvents().then(setEvents).catch(console.error); + }, []); - return { - Id: i + 1, - Subject: `Termin ${i + 1}`, - StartTime: start, - EndTime: end, - ResourceId: (i % 3) + 1, - Location: resources[i % 3].text, - }; -}); - -const Appointments: React.FC = () => ( - - - - - - - - - - - - -); + return ( + + + + + + + + + + + ); +}; export default Appointments; diff --git a/server/dummy_events.py b/server/dummy_events.py index f1b1e4c..2eff9e5 100644 --- a/server/dummy_events.py +++ b/server/dummy_events.py @@ -4,6 +4,7 @@ from models import Event, EventMedia, EventType from dotenv import load_dotenv import os from datetime import datetime, timedelta +import random # .env laden load_dotenv() @@ -24,9 +25,11 @@ created_by = 1 # ID eines existierenden Users # --- Alte Testdaten löschen --- # Zuerst alle EventMedia zu den Events dieses Clients löschen -event_ids = [e.id for e in session.query(Event.id).filter_by(client_uuid=client_uuid).all()] +event_ids = [e.id for e in session.query( + Event.id).filter_by(client_uuid=client_uuid).all()] if event_ids: - session.query(EventMedia).filter(EventMedia.event_id.in_(event_ids)).delete(synchronize_session=False) + session.query(EventMedia).filter(EventMedia.event_id.in_( + event_ids)).delete(synchronize_session=False) # Dann alle Events dieses Clients löschen session.query(Event).filter_by(client_uuid=client_uuid).delete() session.commit() @@ -34,13 +37,23 @@ session.commit() now = datetime.now() + +def random_time_on_day(day_offset: int, duration_hours: int = 1): + """Erzeugt eine zufällige Start- und Endzeit zwischen 8 und 16 Uhr für einen Tag.""" + start_hour = random.randint(8, 15 - duration_hours + 1) + start = (now + timedelta(days=day_offset)).replace(hour=start_hour, + minute=0, second=0, microsecond=0) + end = start + timedelta(hours=duration_hours) + return start, end + + events = [ Event( client_uuid=client_uuid, title="Mathe Präsentation", description="Präsentation zum Thema Algebra.", - start=now + timedelta(days=1, hours=8), - end=now + timedelta(days=1, hours=9), + start=random_time_on_day(1)[0], + end=random_time_on_day(1)[1], event_type=EventType.presentation, created_by=created_by, updated_by=None, @@ -50,8 +63,8 @@ events = [ client_uuid=client_uuid, title="Schulwebsite Update", description="Neue Inhalte auf der Schulwebsite.", - start=now + timedelta(days=2, hours=10), - end=now + timedelta(days=2, hours=11), + start=random_time_on_day(2)[0], + end=random_time_on_day(2)[1], event_type=EventType.website, created_by=created_by, updated_by=None, @@ -61,8 +74,8 @@ events = [ client_uuid=client_uuid, title="Lehrvideo ansehen", description="Video zum Thema Photosynthese.", - start=now + timedelta(days=3, hours=9), - end=now + timedelta(days=3, hours=10), + start=random_time_on_day(3)[0], + end=random_time_on_day(3)[1], event_type=EventType.video, created_by=created_by, updated_by=None, @@ -72,8 +85,8 @@ events = [ client_uuid=client_uuid, title="Nachricht vom Lehrer", description="Wichtige Mitteilung zum Unterricht.", - start=now + timedelta(days=4, hours=13), - end=now + timedelta(days=4, hours=14), + start=random_time_on_day(4)[0], + end=random_time_on_day(4)[1], event_type=EventType.message, created_by=created_by, updated_by=None, @@ -83,8 +96,8 @@ events = [ client_uuid=client_uuid, title="Sonstiges Event", description="Allgemeines Event ohne Kategorie.", - start=now + timedelta(days=5, hours=11), - end=now + timedelta(days=5, hours=12), + start=random_time_on_day(5)[0], + end=random_time_on_day(5)[1], event_type=EventType.other, created_by=created_by, updated_by=None, @@ -94,8 +107,8 @@ events = [ client_uuid=client_uuid, title="WebUntis Termin", description="Termin aus WebUntis importiert.", - start=now + timedelta(days=6, hours=8), - end=now + timedelta(days=6, hours=9), + start=random_time_on_day(6)[0], + end=random_time_on_day(6)[1], event_type=EventType.webuntis, created_by=created_by, updated_by=None, @@ -105,8 +118,8 @@ events = [ client_uuid=client_uuid, title="Englisch Präsentation", description="Präsentation zu Shakespeare.", - start=now + timedelta(days=8, hours=10), - end=now + timedelta(days=8, hours=11), + start=random_time_on_day(8)[0], + end=random_time_on_day(8)[1], event_type=EventType.presentation, created_by=created_by, updated_by=None, @@ -116,8 +129,8 @@ events = [ client_uuid=client_uuid, title="Website Relaunch", description="Vorstellung der neuen Schulwebsite.", - start=now + timedelta(days=10, hours=9), - end=now + timedelta(days=10, hours=10), + start=random_time_on_day(10)[0], + end=random_time_on_day(10)[1], event_type=EventType.website, created_by=created_by, updated_by=None, @@ -127,8 +140,8 @@ events = [ client_uuid=client_uuid, title="Videoanalyse", description="Analyse eines Lehrvideos.", - start=now + timedelta(days=12, hours=14), - end=now + timedelta(days=12, hours=15), + start=random_time_on_day(12)[0], + end=random_time_on_day(12)[1], event_type=EventType.video, created_by=created_by, updated_by=None, @@ -138,8 +151,8 @@ events = [ client_uuid=client_uuid, title="WebUntis Info", description="Weitere Termine aus WebUntis.", - start=now + timedelta(days=14, hours=8), - end=now + timedelta(days=14, hours=9), + start=random_time_on_day(14)[0], + end=random_time_on_day(14)[1], event_type=EventType.webuntis, created_by=created_by, updated_by=None, @@ -219,4 +232,4 @@ event_media = [ for media in event_media: session.add(media) session.commit() -print("Test-Events und EventMedia wurden angelegt.") \ No newline at end of file +print("Test-Events und EventMedia wurden angelegt.") diff --git a/server/wsgi.py b/server/wsgi.py index cbd845f..515c910 100644 --- a/server/wsgi.py +++ b/server/wsgi.py @@ -18,16 +18,21 @@ Session = sessionmaker(bind=engine) app = Flask(__name__) + @app.route("/health") def health(): return jsonify(status="ok") # Optional: Test-Route + + @app.route("/") def index(): return "Hello from Infoscreen‐API!" # (Weitere Endpunkte, Blueprints, Datenbank-Initialisierung usw. kommen hierher) + + @app.route("/screenshots/") def get_screenshot(uuid): """Liefert den aktuellen Screenshot für die angegebene UUID zurück.""" @@ -44,6 +49,7 @@ def get_screenshot(uuid): print("Gefundene Dateien:", files) return send_from_directory("screenshots", filename) + @app.route("/api/clients") def get_clients(): # from models import Client # Import lokal, da im selben Container @@ -63,12 +69,9 @@ def get_clients(): session.close() return jsonify(result) + @app.route("/api/events") def get_events(): - """ - Liefert Events für einen Zeitraum (start, end) als FullCalendar-kompatible Objekte. - Query-Parameter: start, end (ISO-Format), optional: client_uuid - """ session = Session() start = request.args.get("start") end = request.args.get("end") @@ -82,22 +85,16 @@ def get_events(): result = [] for e in events: result.append({ - "id": str(e.id), - "title": e.title, - "start": e.start.isoformat() if e.start else None, - "end": e.end.isoformat() if e.end else None, - "allDay": False, - "classNames": [e.event_type.value] if e.event_type else [], - "extendedProps": { - "description": e.description, - "client_uuid": e.client_uuid, - "event_type": e.event_type.value if e.event_type else None, - "created_by": e.created_by, - "updated_by": e.updated_by, - } + "Id": str(e.id), + "Subject": e.title, + "StartTime": e.start.isoformat() if e.start else None, + "EndTime": e.end.isoformat() if e.end else None, + "IsAllDay": False, + # Optional: weitere Felder wie "Description", "Location", etc. }) session.close() return jsonify(result) + if __name__ == "__main__": - app.run(host="0.0.0.0", port=8000, debug=True) \ No newline at end of file + app.run(host="0.0.0.0", port=8000, debug=True)