From 9cc0778384fec4be3e8dbbb09ab69e4adf59a371 Mon Sep 17 00:00:00 2001 From: donpat1to Date: Fri, 10 Oct 2025 19:49:49 +0200 Subject: [PATCH] added listing shift plans --- .../src/controllers/shiftPlanController.ts | 10 +- .../migrations/003_add_shift_name.sql | 2 + backend/src/database/schema.sql | 4 +- .../src/pages/ShiftPlans/ShiftPlanList.tsx | 183 ++++++++++++++++-- frontend/src/services/shiftPlanService.ts | 1 + 5 files changed, 176 insertions(+), 24 deletions(-) create mode 100644 backend/src/database/migrations/003_add_shift_name.sql diff --git a/backend/src/controllers/shiftPlanController.ts b/backend/src/controllers/shiftPlanController.ts index bc5ce57..5728984 100644 --- a/backend/src/controllers/shiftPlanController.ts +++ b/backend/src/controllers/shiftPlanController.ts @@ -96,6 +96,7 @@ export const getShiftPlan = async (req: AuthRequest, res: Response): Promise ({ id: shift.id, date: shift.date, + name: shift.name, startTime: shift.start_time, endTime: shift.end_time, requiredEmployees: shift.required_employees, @@ -163,6 +164,7 @@ export const createShiftPlan = async (req: AuthRequest, res: Response): Promise< shifts: assignedShifts.map(shift => ({ id: shift.id, date: shift.date, + name: shift.name, startTime: shift.start_time, endTime: shift.end_time, requiredEmployees: shift.required_employees, @@ -308,7 +310,8 @@ async function generateShiftsFromTemplate(shiftPlanId: string, templateId: strin // Generate shifts for each day in the date range for (let date = new Date(start); date <= end; date.setDate(date.getDate() + 1)) { - const dayOfWeek = date.getDay(); // 0 = Sunday, 1 = Monday, etc. + // Convert JS day (0=Sunday) to our format (1=Monday, 7=Sunday) + const dayOfWeek = date.getDay() === 0 ? 7 : date.getDay(); // Find template shifts for this day of week const shiftsForDay = templateShifts.filter(shift => shift.day_of_week === dayOfWeek); @@ -317,12 +320,13 @@ async function generateShiftsFromTemplate(shiftPlanId: string, templateId: strin const shiftId = uuidv4(); await db.run( - `INSERT INTO assigned_shifts (id, shift_plan_id, date, start_time, end_time, required_employees, assigned_employees) - VALUES (?, ?, ?, ?, ?, ?, ?)`, + `INSERT INTO assigned_shifts (id, shift_plan_id, date, name, start_time, end_time, required_employees, assigned_employees) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [ shiftId, shiftPlanId, date.toISOString().split('T')[0], + templateShift.name, templateShift.start_time, templateShift.end_time, templateShift.required_employees, diff --git a/backend/src/database/migrations/003_add_shift_name.sql b/backend/src/database/migrations/003_add_shift_name.sql new file mode 100644 index 0000000..f555732 --- /dev/null +++ b/backend/src/database/migrations/003_add_shift_name.sql @@ -0,0 +1,2 @@ +-- backend/src/database/migrations/003_add_shift_name.sql +ALTER TABLE assigned_shifts ADD COLUMN name TEXT; \ No newline at end of file diff --git a/backend/src/database/schema.sql b/backend/src/database/schema.sql index cad6333..f60efe0 100644 --- a/backend/src/database/schema.sql +++ b/backend/src/database/schema.sql @@ -27,7 +27,7 @@ CREATE TABLE IF NOT EXISTS shift_templates ( CREATE TABLE IF NOT EXISTS template_shifts ( id TEXT PRIMARY KEY, template_id TEXT NOT NULL, - day_of_week INTEGER NOT NULL CHECK (day_of_week >= 1 AND day_of_week <= 7), + day_of_week INTEGER NOT NULL CHECK (day_of_week >= 1 AND day_of_week <= 5), name TEXT NOT NULL, start_time TEXT NOT NULL, end_time TEXT NOT NULL, @@ -64,7 +64,7 @@ CREATE TABLE IF NOT EXISTS assigned_shifts ( CREATE TABLE IF NOT EXISTS employee_availabilities ( id TEXT PRIMARY KEY, employee_id TEXT NOT NULL, - day_of_week INTEGER NOT NULL CHECK (day_of_week >= 0 AND day_of_week <= 6), + day_of_week INTEGER NOT NULL CHECK (day_of_week >= 0 AND day_of_week <= 4), start_time TEXT NOT NULL, end_time TEXT NOT NULL, is_available BOOLEAN DEFAULT FALSE, diff --git a/frontend/src/pages/ShiftPlans/ShiftPlanList.tsx b/frontend/src/pages/ShiftPlans/ShiftPlanList.tsx index 3a192a7..193e287 100644 --- a/frontend/src/pages/ShiftPlans/ShiftPlanList.tsx +++ b/frontend/src/pages/ShiftPlans/ShiftPlanList.tsx @@ -1,14 +1,80 @@ // frontend/src/pages/ShiftPlans/ShiftPlanList.tsx -import React from 'react'; -import { Link } from 'react-router-dom'; +import React, { useState, useEffect } from 'react'; +import { Link, useNavigate } from 'react-router-dom'; import { useAuth } from '../../contexts/AuthContext'; +import { shiftPlanService, ShiftPlan } from '../../services/shiftPlanService'; +import { useNotification } from '../../contexts/NotificationContext'; const ShiftPlanList: React.FC = () => { const { hasRole } = useAuth(); + const navigate = useNavigate(); + const { showNotification } = useNotification(); + const [shiftPlans, setShiftPlans] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + loadShiftPlans(); + }, []); + + const loadShiftPlans = async () => { + try { + const plans = await shiftPlanService.getShiftPlans(); + setShiftPlans(plans); + } catch (error) { + console.error('Error loading shift plans:', error); + showNotification({ + type: 'error', + title: 'Fehler', + message: 'Die Schichtpläne konnten nicht geladen werden.' + }); + } finally { + setLoading(false); + } + }; + + const handleDelete = async (id: string) => { + if (!window.confirm('Möchten Sie diesen Schichtplan wirklich löschen?')) { + return; + } + + try { + await shiftPlanService.deleteShiftPlan(id); + showNotification({ + type: 'success', + title: 'Erfolg', + message: 'Der Schichtplan wurde erfolgreich gelöscht.' + }); + loadShiftPlans(); + } catch (error) { + console.error('Error deleting shift plan:', error); + showNotification({ + type: 'error', + title: 'Fehler', + message: 'Der Schichtplan konnte nicht gelöscht werden.' + }); + } + }; + + const formatDate = (dateString: string) => { + return new Date(dateString).toLocaleDateString('de-DE', { + day: '2-digit', + month: '2-digit', + year: 'numeric' + }); + }; + + if (loading) { + return
Lade Schichtpläne...
; + } return (
-
+

📅 Schichtpläne

{hasRole(['admin', 'instandhalter']) && ( @@ -17,28 +83,107 @@ const ShiftPlanList: React.FC = () => { backgroundColor: '#3498db', color: 'white', border: 'none', - borderRadius: '4px' + borderRadius: '4px', + cursor: 'pointer' }}> + Neuen Plan )}
- -
-
📋
-

Schichtpläne Übersicht

-

Hier werden alle Schichtpläne angezeigt.

-

- Diese Seite wird demnächst mit Funktionen gefüllt. -

-
+ + {shiftPlans.length === 0 ? ( +
+
📋
+

Keine Schichtpläne vorhanden

+

Erstellen Sie Ihren ersten Schichtplan!

+
+ ) : ( +
+ {shiftPlans.map(plan => ( +
+
+

{plan.name}

+
+

+ Zeitraum: {formatDate(plan.startDate)} - {formatDate(plan.endDate)} +

+

+ Status: + {plan.status === 'published' ? 'Veröffentlicht' : 'Entwurf'} + +

+
+
+
+ + {hasRole(['admin', 'instandhalter']) && ( + <> + + + + )} +
+
+ ))} +
+ )}
); }; diff --git a/frontend/src/services/shiftPlanService.ts b/frontend/src/services/shiftPlanService.ts index 1c260ff..562a93f 100644 --- a/frontend/src/services/shiftPlanService.ts +++ b/frontend/src/services/shiftPlanService.ts @@ -26,6 +26,7 @@ export interface ShiftPlanShift { id: string; shiftPlanId: string; date: string; + name: string; startTime: string; endTime: string; requiredEmployees: number;