mirror of
https://github.com/donpat1to/Schichtenplaner.git
synced 2025-12-01 15:05:45 +01:00
frontend with ony errors
This commit is contained in:
@@ -3,9 +3,14 @@ import React, { useState, useEffect } from 'react';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { shiftTemplateService } from '../../services/shiftTemplateService';
|
||||
import { shiftPlanService } from '../../services/shiftPlanService';
|
||||
import { TemplateShift } from '../../types/shiftTemplate';
|
||||
import styles from './ShiftPlanCreate.module.css';
|
||||
|
||||
export interface TemplateShift {
|
||||
id: string;
|
||||
name: string;
|
||||
isDefault?: boolean;
|
||||
}
|
||||
|
||||
const ShiftPlanCreate: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// frontend/src/pages/ShiftPlans/ShiftPlanEdit.tsx
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { shiftPlanService, ShiftPlan, ShiftPlanShift } from '../../services/shiftPlanService';
|
||||
import { shiftPlanService } from '../../services/shiftPlanService';
|
||||
import { ShiftPlan, Shift } from '../../../../backend/src/models/shiftPlan';
|
||||
import { useNotification } from '../../contexts/NotificationContext';
|
||||
import { getTimeSlotById } from '../../models/helpers/shiftPlanHelpers';
|
||||
|
||||
const ShiftPlanEdit: React.FC = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
@@ -10,12 +12,10 @@ const ShiftPlanEdit: React.FC = () => {
|
||||
const { showNotification } = useNotification();
|
||||
const [shiftPlan, setShiftPlan] = useState<ShiftPlan | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [editingShift, setEditingShift] = useState<ShiftPlanShift | null>(null);
|
||||
const [newShift, setNewShift] = useState<Partial<ShiftPlanShift>>({
|
||||
const [editingShift, setEditingShift] = useState<Shift | null>(null);
|
||||
const [newShift, setNewShift] = useState<Partial<ScheduledShift>>({
|
||||
date: '',
|
||||
name: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
timeSlotId: '',
|
||||
requiredEmployees: 1
|
||||
});
|
||||
|
||||
@@ -41,16 +41,10 @@ const ShiftPlanEdit: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateShift = async (shift: ShiftPlanShift) => {
|
||||
const handleUpdateShift = async (shift: Shift) => {
|
||||
if (!shiftPlan || !id) return;
|
||||
|
||||
try {
|
||||
await shiftPlanService.updateShiftPlanShift(id, shift);
|
||||
showNotification({
|
||||
type: 'success',
|
||||
title: 'Erfolg',
|
||||
message: 'Schicht wurde aktualisiert.'
|
||||
});
|
||||
loadShiftPlan();
|
||||
setEditingShift(null);
|
||||
} catch (error) {
|
||||
@@ -66,23 +60,16 @@ const ShiftPlanEdit: React.FC = () => {
|
||||
const handleAddShift = async () => {
|
||||
if (!shiftPlan || !id) return;
|
||||
|
||||
if (!newShift.date || !newShift.name || !newShift.startTime || !newShift.endTime || !newShift.requiredEmployees) {
|
||||
if (!getTimeSlotById(shiftPlan, newShift.timeSlotId?) || !newShift.name || !newShift.startTime || !newShift.endTime || !newShift.requiredEmployees) {
|
||||
showNotification({
|
||||
type: 'error',
|
||||
title: 'Fehler',
|
||||
message: 'Bitte füllen Sie alle Pflichtfelder aus.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await shiftPlanService.addShiftPlanShift(id, {
|
||||
date: newShift.date,
|
||||
name: newShift.name,
|
||||
startTime: newShift.startTime,
|
||||
endTime: newShift.endTime,
|
||||
requiredEmployees: Number(newShift.requiredEmployees)
|
||||
});
|
||||
showNotification({
|
||||
type: 'success',
|
||||
title: 'Erfolg',
|
||||
@@ -112,12 +99,6 @@ const ShiftPlanEdit: React.FC = () => {
|
||||
}
|
||||
|
||||
try {
|
||||
await shiftPlanService.deleteShiftPlanShift(id!, shiftId);
|
||||
showNotification({
|
||||
type: 'success',
|
||||
title: 'Erfolg',
|
||||
message: 'Schicht wurde gelöscht.'
|
||||
});
|
||||
loadShiftPlan();
|
||||
} catch (error) {
|
||||
console.error('Error deleting shift:', error);
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
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 { shiftPlanService } from '../../services/shiftPlanService';
|
||||
import { ShiftPlan } from '../../../../backend/src/models/shiftPlan';
|
||||
import { useNotification } from '../../contexts/NotificationContext';
|
||||
import { formatDate } from '../../utils/foramatters';
|
||||
|
||||
const ShiftPlanList: React.FC = () => {
|
||||
const { hasRole } = useAuth();
|
||||
@@ -55,14 +57,6 @@ const ShiftPlanList: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
return new Date(dateString).toLocaleDateString('de-DE', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric'
|
||||
});
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return <div>Lade Schichtpläne...</div>;
|
||||
}
|
||||
|
||||
@@ -3,8 +3,10 @@ import React, { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { useAuth } from '../../contexts/AuthContext';
|
||||
import { shiftPlanService } from '../../services/shiftPlanService';
|
||||
import { ShiftPlan, Shift, TimeSlot } from '../../../../backend/src/models/shiftPlan.js';
|
||||
import { getTimeSlotById } from '../../models/helpers/shiftPlanHelpers';
|
||||
import { ShiftPlan, TimeSlot } from '../../models/ShiftPlan';
|
||||
import { useNotification } from '../../contexts/NotificationContext';
|
||||
import { formatDate, formatTime } from '../../utils/foramatters';
|
||||
|
||||
const ShiftPlanView: React.FC = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
@@ -36,94 +38,44 @@ const ShiftPlanView: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const formatDate = (dateString: string | undefined): string => {
|
||||
if (!dateString) return 'Kein Datum';
|
||||
|
||||
const date = new Date(dateString);
|
||||
|
||||
if (isNaN(date.getTime())) {
|
||||
return 'Ungültiges Datum';
|
||||
}
|
||||
|
||||
return date.toLocaleDateString('de-DE', {
|
||||
weekday: 'long',
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric'
|
||||
});
|
||||
};
|
||||
|
||||
const formatTime = (timeString: string) => {
|
||||
return timeString.substring(0, 5);
|
||||
};
|
||||
|
||||
// Get unique shift types and their staffing per weekday
|
||||
// Simplified timetable data generation
|
||||
const getTimetableData = () => {
|
||||
if (!shiftPlan) return { shifts: [], weekdays: [] };
|
||||
|
||||
// Get all unique shift types (name + time combination)
|
||||
const shiftTypes = Array.from(new Set(
|
||||
shiftPlan.shifts.map(shift =>
|
||||
`${shift.timeSlot.name}|${shift.timeSlot.startTime}|${shift.timeSlot.endTime}`
|
||||
)
|
||||
)).map(shiftKey => {
|
||||
const [name, startTime, endTime] = shiftKey.split('|');
|
||||
return { name, startTime, endTime };
|
||||
});
|
||||
|
||||
// Weekdays (1=Monday, 7=Sunday)
|
||||
const weekdays = [1, 2, 3, 4, 5, 6, 7];
|
||||
|
||||
// For each shift type and weekday, calculate staffing
|
||||
const timetableShifts = shiftTypes.map(shiftType => {
|
||||
// Use timeSlots directly since shifts reference them
|
||||
const timetableShifts = shiftPlan.timeSlots.map(timeSlot => {
|
||||
const weekdayData: Record<number, string> = {};
|
||||
|
||||
weekdays.forEach(weekday => {
|
||||
// Find all shifts of this type on this weekday
|
||||
const shiftsOnDay = shiftPlan.shifts.filter(shift => {
|
||||
const date = new Date(shift.date);
|
||||
const dayOfWeek = date.getDay() === 0 ? 7 : date.getDay(); // Convert to 1-7 (Mon-Sun)
|
||||
return dayOfWeek === weekday &&
|
||||
shift.timeSlot.name === shiftType.name &&
|
||||
shift.timeSlot.startTime === shiftType.startTime &&
|
||||
shift.timeSlot.endTime === shiftType.endTime;
|
||||
});
|
||||
const shiftsOnDay = shiftPlan.shifts.filter(shift =>
|
||||
shift.dayOfWeek === weekday.id &&
|
||||
shift.timeSlotId === timeSlot.id
|
||||
);
|
||||
|
||||
if (shiftsOnDay.length === 0) {
|
||||
weekdayData[weekday] = '';
|
||||
weekdayData[weekday.id] = '';
|
||||
} else {
|
||||
const totalAssigned = shiftsOnDay.reduce((sum, shift) => sum + shift.timeSlot.assignedEmployees.length, 0);
|
||||
const totalRequired = shiftsOnDay.reduce((sum, shift) => sum + shift.requiredEmployees, 0);
|
||||
weekdayData[weekday] = `${totalAssigned}/${totalRequired}`;
|
||||
const totalRequired = shiftsOnDay.reduce((sum, shift) =>
|
||||
sum + shift.requiredEmployees, 0);
|
||||
// For now, show required count since we don't have assigned employees in Shift
|
||||
weekdayData[weekday.id] = `0/${totalRequired}`;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
...shiftType,
|
||||
displayName: `${shiftType.name} (${formatTime(shiftType.startTime)}–${formatTime(shiftType.endTime)})`,
|
||||
...timeSlot,
|
||||
displayName: `${timeSlot.name} (${formatTime(timeSlot.startTime)}–${formatTime(timeSlot.endTime)})`,
|
||||
weekdayData
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
shifts: timetableShifts,
|
||||
weekdays: weekdays.map(day => ({
|
||||
id: day,
|
||||
name: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'][day === 7 ? 0 : day]
|
||||
}))
|
||||
};
|
||||
return { shifts: timetableShifts, weekdays };
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return <div>Lade Schichtplan...</div>;
|
||||
}
|
||||
|
||||
if (!shiftPlan) {
|
||||
return <div>Schichtplan nicht gefunden</div>;
|
||||
}
|
||||
if (loading) return <div>Lade Schichtplan...</div>;
|
||||
if (!shiftPlan) return <div>Schichtplan nicht gefunden</div>;
|
||||
|
||||
const timetableData = getTimetableData();
|
||||
|
||||
return (
|
||||
<div style={{ padding: '20px' }}>
|
||||
<div style={{
|
||||
|
||||
Reference in New Issue
Block a user