mirror of
https://github.com/donpat1to/Schichtenplaner.git
synced 2025-12-01 06:55:45 +01:00
can add and delete shiftplans from pressets
This commit is contained in:
@@ -1,15 +1,14 @@
|
||||
// frontend/src/pages/ShiftPlans/ShiftPlanCreate.tsx
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { shiftTemplateService } from '../../services/shiftTemplateService';
|
||||
import { shiftPlanService } from '../../services/shiftPlanService';
|
||||
import styles from './ShiftPlanCreate.module.css';
|
||||
import { TimeSlot, Shift } from '../../models/ShiftPlan';
|
||||
|
||||
export interface TemplateShift {
|
||||
id: string;
|
||||
// Interface für Template Presets
|
||||
interface TemplatePreset {
|
||||
name: string;
|
||||
isDefault?: boolean;
|
||||
label: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const ShiftPlanCreate: React.FC = () => {
|
||||
@@ -19,37 +18,31 @@ const ShiftPlanCreate: React.FC = () => {
|
||||
const [planName, setPlanName] = useState('');
|
||||
const [startDate, setStartDate] = useState('');
|
||||
const [endDate, setEndDate] = useState('');
|
||||
const [selectedTemplate, setSelectedTemplate] = useState('');
|
||||
const [templates, setTemplates] = useState<TemplateShift[]>([]);
|
||||
const [selectedPreset, setSelectedPreset] = useState('');
|
||||
const [presets, setPresets] = useState<TemplatePreset[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [success, setSuccess] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
loadTemplates();
|
||||
loadTemplatePresets();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// Template aus URL-Parameter setzen, falls vorhanden
|
||||
const templateId = searchParams.get('template');
|
||||
if (templateId) {
|
||||
setSelectedTemplate(templateId);
|
||||
}
|
||||
}, [searchParams]);
|
||||
|
||||
const loadTemplates = async () => {
|
||||
const loadTemplatePresets = async () => {
|
||||
try {
|
||||
const data = await shiftTemplateService.getTemplates();
|
||||
setTemplates(data);
|
||||
console.log('🔄 Lade verfügbare Vorlagen-Presets...');
|
||||
const data = await shiftPlanService.getTemplatePresets();
|
||||
console.log('✅ Presets geladen:', data);
|
||||
|
||||
// Wenn keine Template-ID in der URL ist, setze die Standard-Vorlage
|
||||
if (!searchParams.get('template')) {
|
||||
if (!searchParams.get('template') && data.length > 0) {
|
||||
setSelectedTemplate(data[0].id);
|
||||
}
|
||||
setPresets(data);
|
||||
|
||||
// Setze das erste Preset als Standard, falls vorhanden
|
||||
if (data.length > 0) {
|
||||
setSelectedPreset(data[0].name);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Vorlagen:', error);
|
||||
setError('Vorlagen konnten nicht geladen werden');
|
||||
console.error('❌ Fehler beim Laden der Vorlagen-Presets:', error);
|
||||
setError('Vorlagen-Presets konnten nicht geladen werden');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -57,6 +50,7 @@ const ShiftPlanCreate: React.FC = () => {
|
||||
|
||||
const handleCreate = async () => {
|
||||
try {
|
||||
// Validierung
|
||||
if (!planName.trim()) {
|
||||
setError('Bitte geben Sie einen Namen für den Schichtplan ein');
|
||||
return;
|
||||
@@ -73,53 +67,53 @@ const ShiftPlanCreate: React.FC = () => {
|
||||
setError('Das Enddatum muss nach dem Startdatum liegen');
|
||||
return;
|
||||
}
|
||||
|
||||
let timeSlots: Omit<TimeSlot, 'id' | 'planId'>[] = [];
|
||||
let shifts: Omit<Shift, 'id' | 'planId'>[] = [];
|
||||
|
||||
// If a template is selected, load its data
|
||||
if (selectedTemplate) {
|
||||
try {
|
||||
const template = await shiftTemplateService.getTemplate(selectedTemplate);
|
||||
timeSlots = template.timeSlots.map(slot => ({
|
||||
name: slot.name,
|
||||
startTime: slot.startTime,
|
||||
endTime: slot.endTime,
|
||||
description: slot.description
|
||||
}));
|
||||
shifts = template.shifts.map(shift => ({
|
||||
timeSlotId: shift.timeSlotId,
|
||||
dayOfWeek: shift.dayOfWeek,
|
||||
requiredEmployees: shift.requiredEmployees,
|
||||
color: shift.color
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Vorlage:', error);
|
||||
setError('Die ausgewählte Vorlage konnte nicht geladen werden');
|
||||
return;
|
||||
}
|
||||
if (!selectedPreset) {
|
||||
setError('Bitte wählen Sie eine Vorlage aus');
|
||||
return;
|
||||
}
|
||||
|
||||
await shiftPlanService.createShiftPlan({
|
||||
console.log('🔄 Erstelle Schichtplan aus Preset...', {
|
||||
presetName: selectedPreset,
|
||||
name: planName,
|
||||
startDate,
|
||||
endDate,
|
||||
isTemplate: false,
|
||||
templateId: selectedTemplate || undefined,
|
||||
timeSlots,
|
||||
shifts
|
||||
endDate
|
||||
});
|
||||
|
||||
// Nach erfolgreicher Erstellung zur Liste der Schichtpläne navigieren
|
||||
navigate('/shift-plans');
|
||||
// Erstelle den Plan aus dem ausgewählten Preset
|
||||
const createdPlan = await shiftPlanService.createFromPreset({
|
||||
presetName: selectedPreset,
|
||||
name: planName,
|
||||
startDate: startDate,
|
||||
endDate: endDate,
|
||||
isTemplate: false
|
||||
});
|
||||
|
||||
console.log('✅ Plan erstellt:', createdPlan);
|
||||
|
||||
// Erfolgsmeldung und Weiterleitung
|
||||
setSuccess('Schichtplan erfolgreich erstellt!');
|
||||
setTimeout(() => {
|
||||
navigate(`/shift-plans/${createdPlan.id}`);
|
||||
}, 1500);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Erstellen des Schichtplans:', error);
|
||||
setError('Der Schichtplan konnte nicht erstellt werden. Bitte versuchen Sie es später erneut.');
|
||||
}
|
||||
const err = error as Error;
|
||||
console.error('❌ Fehler beim Erstellen des Plans:', err);
|
||||
setError(`Plan konnte nicht erstellt werden: ${err.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
const getSelectedPresetDescription = () => {
|
||||
const preset = presets.find(p => p.name === selectedPreset);
|
||||
return preset ? preset.description : '';
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return <div>Lade Vorlagen...</div>;
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.loading}>Lade Vorlagen...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -136,6 +130,12 @@ const ShiftPlanCreate: React.FC = () => {
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{success && (
|
||||
<div className={styles.success}>
|
||||
{success}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={styles.form}>
|
||||
<div className={styles.formGroup}>
|
||||
@@ -145,6 +145,7 @@ const ShiftPlanCreate: React.FC = () => {
|
||||
value={planName}
|
||||
onChange={(e) => setPlanName(e.target.value)}
|
||||
placeholder="z.B. KW 42 2025"
|
||||
className={styles.input}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -155,6 +156,7 @@ const ShiftPlanCreate: React.FC = () => {
|
||||
type="date"
|
||||
value={startDate}
|
||||
onChange={(e) => setStartDate(e.target.value)}
|
||||
className={styles.input}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -164,6 +166,7 @@ const ShiftPlanCreate: React.FC = () => {
|
||||
type="date"
|
||||
value={endDate}
|
||||
onChange={(e) => setEndDate(e.target.value)}
|
||||
className={styles.input}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -171,29 +174,37 @@ const ShiftPlanCreate: React.FC = () => {
|
||||
<div className={styles.formGroup}>
|
||||
<label>Vorlage verwenden:</label>
|
||||
<select
|
||||
value={selectedTemplate}
|
||||
onChange={(e) => setSelectedTemplate(e.target.value)}
|
||||
className={templates.length === 0 ? styles.empty : ''}
|
||||
value={selectedPreset}
|
||||
onChange={(e) => setSelectedPreset(e.target.value)}
|
||||
className={`${styles.select} ${presets.length === 0 ? styles.empty : ''}`}
|
||||
>
|
||||
<option value="">Keine Vorlage</option>
|
||||
{templates.map(template => (
|
||||
<option key={template.id} value={template.id}>
|
||||
{template.name} {template.isDefault ? '(Standard)' : ''}
|
||||
<option value="">Bitte wählen...</option>
|
||||
{presets.map(preset => (
|
||||
<option key={preset.name} value={preset.name}>
|
||||
{preset.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{templates.length === 0 && (
|
||||
|
||||
{selectedPreset && (
|
||||
<div className={styles.presetDescription}>
|
||||
{getSelectedPresetDescription()}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{presets.length === 0 && (
|
||||
<p className={styles.noTemplates}>
|
||||
Keine Vorlagen verfügbar.
|
||||
<button onClick={() => navigate('/shift-templates/new')} className={styles.linkButton}>
|
||||
Neue Vorlage erstellen
|
||||
</button>
|
||||
Keine Vorlagen verfügbar.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={styles.actions}>
|
||||
<button onClick={handleCreate} className={styles.createButton} disabled={!selectedTemplate}>
|
||||
<button
|
||||
onClick={handleCreate}
|
||||
className={styles.createButton}
|
||||
disabled={!selectedPreset || !planName.trim() || !startDate || !endDate}
|
||||
>
|
||||
Schichtplan erstellen
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@ import React, { useState, useEffect } from 'react';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { useAuth } from '../../contexts/AuthContext';
|
||||
import { shiftPlanService } from '../../services/shiftPlanService';
|
||||
import { ShiftPlan } from '../../../../backend/src/models/shiftPlan';
|
||||
import { ShiftPlan } from '../../models/ShiftPlan';
|
||||
import { useNotification } from '../../contexts/NotificationContext';
|
||||
import { formatDate } from '../../utils/foramatters';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user