backend working

This commit is contained in:
2025-10-19 00:15:17 +02:00
parent 372b9b2f20
commit 970651c021
11 changed files with 1060 additions and 162 deletions

View File

@@ -1,43 +1,141 @@
// src/components/Scheduler.tsx
import React from 'react';
import { useScheduling } from '../hooks/useScheduling';
import { useScheduling } from '../services/scheduling/useScheduling';
import { ScheduleRequest } from '../models/scheduling';
interface Props {
interface SchedulerProps {
scheduleRequest: ScheduleRequest;
onScheduleGenerated?: (result: any) => void;
}
export const Scheduler: React.FC<Props> = ({ scheduleRequest }) => {
export const Scheduler: React.FC<SchedulerProps> = ({
scheduleRequest,
onScheduleGenerated
}) => {
const { generateSchedule, loading, error, result } = useScheduling();
const handleGenerateSchedule = async () => {
try {
await generateSchedule(scheduleRequest);
const scheduleResult = await generateSchedule(scheduleRequest);
if (onScheduleGenerated) {
onScheduleGenerated(scheduleResult);
}
} catch (err) {
// Error handling
console.error('Scheduling failed:', err);
}
};
return (
<div>
<div style={{ padding: '20px', border: '1px solid #e0e0e0', borderRadius: '8px' }}>
<h3>Automatic Schedule Generation</h3>
<button
onClick={handleGenerateSchedule}
disabled={loading}
style={{
padding: '12px 24px',
backgroundColor: loading ? '#95a5a6' : '#3498db',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: loading ? 'not-allowed' : 'pointer',
fontSize: '16px',
fontWeight: 'bold'
}}
>
{loading ? 'Generating Schedule...' : 'Generate Optimal Schedule'}
{loading ? '🔄 Generating Schedule...' : '🚀 Generate Optimal Schedule'}
</button>
{loading && (
<div>
<progress max="100" value="70" />
<p>Optimizing schedule... (max 2 minutes)</p>
<div style={{ marginTop: '15px' }}>
<div style={{
width: '100%',
height: '8px',
backgroundColor: '#ecf0f1',
borderRadius: '4px',
overflow: 'hidden'
}}>
<div style={{
width: '70%',
height: '100%',
backgroundColor: '#3498db',
animation: 'pulse 2s infinite',
borderRadius: '4px'
}} />
</div>
<p style={{ color: '#7f8c8d', fontSize: '14px', marginTop: '8px' }}>
Optimizing schedule... (max 2 minutes)
</p>
</div>
)}
{error && <div className="error">{error}</div>}
{error && (
<div style={{
marginTop: '15px',
padding: '12px',
backgroundColor: '#f8d7da',
border: '1px solid #f5c6cb',
borderRadius: '4px',
color: '#721c24'
}}>
<strong>Error:</strong> {error}
</div>
)}
{result && (
<ScheduleResultView result={result} />
<div style={{ marginTop: '20px' }}>
<ScheduleResultView result={result} />
</div>
)}
</div>
);
};
};
const ScheduleResultView: React.FC<{ result: any }> = ({ result }) => {
return (
<div style={{
padding: '15px',
backgroundColor: result.success ? '#d4edda' : '#f8d7da',
border: `1px solid ${result.success ? '#c3e6cb' : '#f5c6cb'}`,
borderRadius: '4px'
}}>
<h4 style={{
color: result.success ? '#155724' : '#721c24',
marginTop: 0
}}>
{result.success ? '✅ Schedule Generated Successfully' : '❌ Schedule Generation Failed'}
</h4>
<div style={{ marginBottom: '10px' }}>
<strong>Assignments:</strong> {Object.keys(result.assignments || {}).length} shifts assigned
</div>
<div style={{ marginBottom: '10px' }}>
<strong>Violations:</strong> {result.violations?.length || 0}
</div>
{result.resolution_report && result.resolution_report.length > 0 && (
<details style={{ marginTop: '10px' }}>
<summary style={{ cursor: 'pointer', fontWeight: 'bold' }}>
Resolution Report
</summary>
<div style={{
marginTop: '10px',
maxHeight: '200px',
overflow: 'auto',
fontSize: '12px',
fontFamily: 'monospace',
backgroundColor: 'rgba(0,0,0,0.05)',
padding: '10px',
borderRadius: '4px'
}}>
{result.resolution_report.map((line: string, index: number) => (
<div key={index} style={{ marginBottom: '2px' }}>{line}</div>
))}
</div>
</details>
)}
</div>
);
};
export default Scheduler;

View File

@@ -1,37 +0,0 @@
// frontend/src/services/scheduling/scheduling.ts
import { useState, useCallback } from 'react';
import { ScheduleRequest, ScheduleResult } from '../types/scheduling';
export const useScheduling = () => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [result, setResult] = useState<ScheduleResult | null>(null);
const generateSchedule = useCallback(async (request: ScheduleRequest) => {
setLoading(true);
setError(null);
try {
const response = await fetch('/api/scheduling/generate-schedule', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request)
});
if (!response.ok) throw new Error('Scheduling request failed');
const data: ScheduleResult = await response.json();
setResult(data);
return data;
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
setError(errorMessage);
throw err;
} finally {
setLoading(false);
}
}, []);
return { generateSchedule, loading, error, result };
};

View File

@@ -0,0 +1,122 @@
// backend/src/models/scheduling.ts
import { Employee } from './Employee.js';
import { ShiftPlan } from './ShiftPlan.js';
// Add the missing type definitions
export interface Availability {
id: string;
employeeId: string;
planId: string;
dayOfWeek: number; // 1=Monday, 7=Sunday
timeSlotId: string;
preferenceLevel: 1 | 2 | 3; // 1:preferred, 2:available, 3:unavailable
notes?: string;
}
export interface Constraint {
type: string;
severity: 'hard' | 'soft';
parameters: {
maxShiftsPerDay?: number;
minEmployeesPerShift?: number;
maxEmployeesPerShift?: number;
enforceTraineeSupervision?: boolean;
contractHoursLimit?: boolean;
maxHoursPerWeek?: number;
[key: string]: any;
};
weight?: number; // For soft constraints
}
export interface ScheduleRequest {
shiftPlan: ShiftPlan;
employees: Employee[];
availabilities: Availability[];
constraints: Constraint[];
}
export interface ScheduleResult {
assignments: Assignment[];
violations: Violation[];
success: boolean;
resolutionReport: string[];
processingTime: number;
}
export interface Assignment {
shiftId: string;
employeeId: string;
assignedAt: Date;
score: number; // Qualität der Zuweisung (1-100)
}
export interface Violation {
type: string;
severity: 'critical' | 'warning';
message: string;
involvedEmployees?: string[];
shiftId?: string;
details?: any;
}
export interface SolverOptions {
maxTimeInSeconds: number;
numSearchWorkers: number;
logSearchProgress: boolean;
}
export interface Solution {
assignments: Assignment[];
violations: Violation[];
success: boolean;
metadata: {
solveTime: number;
constraintsAdded: number;
variablesCreated: number;
optimal: boolean;
};
}
// Additional helper types for the scheduling system
export interface SchedulingConfig {
maxRepairAttempts: number;
targetEmployeesPerShift: number;
enforceNoTraineeAlone: boolean;
enforceExperiencedWithChef: boolean;
preferEmployeePreferences: boolean;
}
export interface AssignmentResult {
assignments: { [shiftId: string]: string[] }; // shiftId -> employeeIds
violations: string[];
resolutionReport: string[];
success: boolean;
statistics?: {
totalAssignments: number;
preferredAssignments: number;
availableAssignments: number;
coverageRate: number;
violationCount: number;
};
}
export interface EmployeeAvailabilitySummary {
employeeId: string;
employeeName: string;
preferredSlots: number;
availableSlots: number;
unavailableSlots: number;
totalSlots: number;
}
export interface ShiftRequirement {
shiftId: string;
timeSlotId: string;
dayOfWeek: number;
date?: string;
requiredEmployees: number;
minEmployees: number;
maxEmployees: number;
assignedEmployees: string[];
isPriority: boolean;
}

View File

@@ -5,7 +5,7 @@ import { useAuth } from '../../contexts/AuthContext';
import { shiftPlanService } from '../../services/shiftPlanService';
import { employeeService } from '../../services/employeeService';
import { shiftAssignmentService, ShiftAssignmentService } from '../../services/shiftAssignmentService';
import { IntelligentShiftScheduler, SchedulingResult, AssignmentResult } from '../../hooks/useScheduling';
import { IntelligentShiftScheduler, SchedulingResult, AssignmentResult } from '../../services/scheduling/useScheduling';
import { ShiftPlan, TimeSlot, ScheduledShift } from '../../models/ShiftPlan';
import { Employee, EmployeeAvailability } from '../../models/Employee';
import { useNotification } from '../../contexts/NotificationContext';

View File

@@ -0,0 +1,71 @@
import { useState, useCallback } from 'react';
import { ScheduleRequest, ScheduleResult } from '../../models/scheduling';
export const useScheduling = () => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [result, setResult] = useState<ScheduleResult | null>(null);
const generateSchedule = useCallback(async (request: ScheduleRequest) => {
setLoading(true);
setError(null);
try {
console.log('📤 Sending scheduling request:', {
shiftPlan: request.shiftPlan.name,
employees: request.employees.length,
availabilities: request.availabilities.length,
constraints: request.constraints.length
});
const response = await fetch('/api/scheduling/generate-schedule', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(request)
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Scheduling request failed: ${response.status} ${errorText}`);
}
const data: ScheduleResult = await response.json();
console.log('📥 Received scheduling result:', {
success: data.success,
assignments: Object.keys(data.assignments).length,
violations: data.violations.length,
processingTime: data.processingTime
});
setResult(data);
return data;
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Unknown scheduling error';
console.error('❌ Scheduling error:', errorMessage);
setError(errorMessage);
throw err;
} finally {
setLoading(false);
}
}, []);
const reset = useCallback(() => {
setLoading(false);
setError(null);
setResult(null);
}, []);
return {
generateSchedule,
loading,
error,
result,
reset
};
};
// Export for backward compatibility
export default useScheduling;

View File

@@ -2,7 +2,7 @@
import { ShiftPlan, ScheduledShift } from '../models/ShiftPlan';
import { Employee, EmployeeAvailability } from '../models/Employee';
import { authService } from './authService';
import { IntelligentShiftScheduler, AssignmentResult, WeeklyPattern } from '../hooks/useScheduling';
//import { IntelligentShiftScheduler, AssignmentResult, WeeklyPattern } from './scheduling/useScheduling';
import { isScheduledShift } from '../models/helpers';
const API_BASE_URL = 'http://localhost:3002/api/scheduled-shifts';