mirror of
https://github.com/donpat1to/Schichtenplaner.git
synced 2025-11-30 22:45:46 +01:00
backend without errors
This commit is contained in:
@@ -14,17 +14,16 @@ export const getEmployees = async (req: AuthRequest, res: Response): Promise<voi
|
|||||||
SELECT
|
SELECT
|
||||||
id, email, name, role, is_active as isActive,
|
id, email, name, role, is_active as isActive,
|
||||||
employee_type as employeeType,
|
employee_type as employeeType,
|
||||||
is_sufficiently_independent as isSufficientlyIndependent,
|
contract_type as contractType,
|
||||||
|
can_work_alone as canWorkAlone,
|
||||||
created_at as createdAt,
|
created_at as createdAt,
|
||||||
last_login as lastLogin
|
last_login as lastLogin
|
||||||
FROM users
|
FROM employees
|
||||||
WHERE is_active = 1
|
WHERE is_active = 1
|
||||||
ORDER BY name
|
ORDER BY name
|
||||||
`);
|
`);
|
||||||
|
|
||||||
console.log('✅ Employees found:', employees.length);
|
console.log('✅ Employees found:', employees.length);
|
||||||
console.log('📋 Employees data:', employees);
|
|
||||||
|
|
||||||
res.json(employees);
|
res.json(employees);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Error fetching employees:', error);
|
console.error('❌ Error fetching employees:', error);
|
||||||
@@ -40,10 +39,11 @@ export const getEmployee = async (req: AuthRequest, res: Response): Promise<void
|
|||||||
SELECT
|
SELECT
|
||||||
id, email, name, role, is_active as isActive,
|
id, email, name, role, is_active as isActive,
|
||||||
employee_type as employeeType,
|
employee_type as employeeType,
|
||||||
is_sufficiently_independent as isSufficientlyIndependent,
|
contract_type as contractType,
|
||||||
|
can_work_alone as canWorkAlone,
|
||||||
created_at as createdAt,
|
created_at as createdAt,
|
||||||
last_login as lastLogin
|
last_login as lastLogin
|
||||||
FROM users
|
FROM employees
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
`, [id]);
|
`, [id]);
|
||||||
|
|
||||||
@@ -72,14 +72,15 @@ export const createEmployee = async (req: AuthRequest, res: Response): Promise<v
|
|||||||
name,
|
name,
|
||||||
role,
|
role,
|
||||||
employeeType,
|
employeeType,
|
||||||
isSufficientlyIndependent,
|
contractType,
|
||||||
|
canWorkAlone // Statt isSufficientlyIndependent
|
||||||
} = req.body as CreateEmployeeRequest;
|
} = req.body as CreateEmployeeRequest;
|
||||||
|
|
||||||
// Validierung
|
// Validierung
|
||||||
if (!email || !password || !name || !role || !employeeType) {
|
if (!email || !password || !name || !role || !employeeType || !contractType) {
|
||||||
console.log('❌ Validation failed: Missing required fields');
|
console.log('❌ Validation failed: Missing required fields');
|
||||||
res.status(400).json({
|
res.status(400).json({
|
||||||
error: 'Email, password, name, role und employeeType sind erforderlich'
|
error: 'Email, password, name, role, employeeType und contractType sind erforderlich'
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -91,7 +92,7 @@ export const createEmployee = async (req: AuthRequest, res: Response): Promise<v
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if email already exists
|
// Check if email already exists
|
||||||
const existingActiveUser = await db.get<any>('SELECT id FROM users WHERE email = ? AND is_active = 1', [email]);
|
const existingActiveUser = await db.get<any>('SELECT id FROM employees WHERE email = ? AND is_active = 1', [email]);
|
||||||
|
|
||||||
if (existingActiveUser) {
|
if (existingActiveUser) {
|
||||||
console.log('❌ Email exists for active user:', existingActiveUser);
|
console.log('❌ Email exists for active user:', existingActiveUser);
|
||||||
@@ -104,10 +105,10 @@ export const createEmployee = async (req: AuthRequest, res: Response): Promise<v
|
|||||||
const employeeId = uuidv4();
|
const employeeId = uuidv4();
|
||||||
|
|
||||||
await db.run(
|
await db.run(
|
||||||
`INSERT INTO users (
|
`INSERT INTO employees (
|
||||||
id, email, password, name, role, employee_type, is_sufficiently_independent,
|
id, email, password, name, role, employee_type, contract_type, can_work_alone,
|
||||||
is_active
|
is_active
|
||||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
[
|
[
|
||||||
employeeId,
|
employeeId,
|
||||||
email,
|
email,
|
||||||
@@ -115,7 +116,8 @@ export const createEmployee = async (req: AuthRequest, res: Response): Promise<v
|
|||||||
name,
|
name,
|
||||||
role,
|
role,
|
||||||
employeeType,
|
employeeType,
|
||||||
isSufficientlyIndependent ? 1 : 0,
|
contractType,
|
||||||
|
canWorkAlone ? 1 : 0, // Statt isSufficientlyIndependent
|
||||||
1
|
1
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
@@ -125,10 +127,11 @@ export const createEmployee = async (req: AuthRequest, res: Response): Promise<v
|
|||||||
SELECT
|
SELECT
|
||||||
id, email, name, role, is_active as isActive,
|
id, email, name, role, is_active as isActive,
|
||||||
employee_type as employeeType,
|
employee_type as employeeType,
|
||||||
is_sufficiently_independent as isSufficientlyIndependent,
|
contract_type as contractType,
|
||||||
|
can_work_alone as canWorkAlone,
|
||||||
created_at as createdAt,
|
created_at as createdAt,
|
||||||
last_login as lastLogin
|
last_login as lastLogin
|
||||||
FROM users
|
FROM employees
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
`, [employeeId]);
|
`, [employeeId]);
|
||||||
|
|
||||||
@@ -142,12 +145,12 @@ export const createEmployee = async (req: AuthRequest, res: Response): Promise<v
|
|||||||
export const updateEmployee = async (req: AuthRequest, res: Response): Promise<void> => {
|
export const updateEmployee = async (req: AuthRequest, res: Response): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const { name, role, isActive, employeeType, isSufficientlyIndependent } = req.body;
|
const { name, role, isActive, employeeType, contractType, canWorkAlone } = req.body; // Statt isSufficientlyIndependent
|
||||||
|
|
||||||
console.log('📝 Update Employee Request:', { id, name, role, isActive, employeeType, isSufficientlyIndependent });
|
console.log('📝 Update Employee Request:', { id, name, role, isActive, employeeType, contractType, canWorkAlone });
|
||||||
|
|
||||||
// Check if employee exists
|
// Check if employee exists
|
||||||
const existingEmployee = await db.get('SELECT * FROM users WHERE id = ?', [id]);
|
const existingEmployee = await db.get('SELECT * FROM employees WHERE id = ?', [id]);
|
||||||
if (!existingEmployee) {
|
if (!existingEmployee) {
|
||||||
res.status(404).json({ error: 'Employee not found' });
|
res.status(404).json({ error: 'Employee not found' });
|
||||||
return;
|
return;
|
||||||
@@ -155,14 +158,15 @@ export const updateEmployee = async (req: AuthRequest, res: Response): Promise<v
|
|||||||
|
|
||||||
// Update employee
|
// Update employee
|
||||||
await db.run(
|
await db.run(
|
||||||
`UPDATE users
|
`UPDATE employees
|
||||||
SET name = COALESCE(?, name),
|
SET name = COALESCE(?, name),
|
||||||
role = COALESCE(?, role),
|
role = COALESCE(?, role),
|
||||||
is_active = COALESCE(?, is_active),
|
is_active = COALESCE(?, is_active),
|
||||||
employee_type = COALESCE(?, employee_type),
|
employee_type = COALESCE(?, employee_type),
|
||||||
is_sufficiently_independent = COALESCE(?, is_sufficiently_independent)
|
contract_type = COALESCE(?, contract_type),
|
||||||
|
can_work_alone = COALESCE(?, can_work_alone)
|
||||||
WHERE id = ?`,
|
WHERE id = ?`,
|
||||||
[name, role, isActive, employeeType, isSufficientlyIndependent, id]
|
[name, role, isActive, employeeType, contractType, canWorkAlone, id]
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log('✅ Employee updated successfully');
|
console.log('✅ Employee updated successfully');
|
||||||
@@ -172,10 +176,11 @@ export const updateEmployee = async (req: AuthRequest, res: Response): Promise<v
|
|||||||
SELECT
|
SELECT
|
||||||
id, email, name, role, is_active as isActive,
|
id, email, name, role, is_active as isActive,
|
||||||
employee_type as employeeType,
|
employee_type as employeeType,
|
||||||
is_sufficiently_independent as isSufficientlyIndependent,
|
contract_type as contractType,
|
||||||
|
can_work_alone as canWorkAlone,
|
||||||
created_at as createdAt,
|
created_at as createdAt,
|
||||||
last_login as lastLogin
|
last_login as lastLogin
|
||||||
FROM users
|
FROM employees
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
`, [id]);
|
`, [id]);
|
||||||
|
|
||||||
@@ -194,7 +199,7 @@ export const deleteEmployee = async (req: AuthRequest, res: Response): Promise<v
|
|||||||
// Check if employee exists
|
// Check if employee exists
|
||||||
const existingEmployee = await db.get<any>(`
|
const existingEmployee = await db.get<any>(`
|
||||||
SELECT id, email, name, is_active, role
|
SELECT id, email, name, is_active, role
|
||||||
FROM users
|
FROM employees
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
`, [id]);
|
`, [id]);
|
||||||
|
|
||||||
@@ -211,7 +216,7 @@ export const deleteEmployee = async (req: AuthRequest, res: Response): Promise<v
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. Remove availabilities
|
// 1. Remove availabilities
|
||||||
await db.run('DELETE FROM employee_availabilities WHERE employee_id = ?', [id]);
|
await db.run('DELETE FROM employee_availability WHERE employee_id = ?', [id]);
|
||||||
|
|
||||||
// 2. Remove from assigned_shifts (JSON field cleanup)
|
// 2. Remove from assigned_shifts (JSON field cleanup)
|
||||||
interface AssignedShift {
|
interface AssignedShift {
|
||||||
@@ -220,7 +225,7 @@ export const deleteEmployee = async (req: AuthRequest, res: Response): Promise<v
|
|||||||
}
|
}
|
||||||
|
|
||||||
const assignedShifts = await db.all<AssignedShift>(
|
const assignedShifts = await db.all<AssignedShift>(
|
||||||
'SELECT id, assigned_employees FROM assigned_shifts WHERE json_extract(assigned_employees, "$") LIKE ?',
|
'SELECT id, assigned_employees FROM scheduled_shifts WHERE json_extract(assigned_employees, "$") LIKE ?',
|
||||||
[`%${id}%`]
|
[`%${id}%`]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -229,14 +234,13 @@ export const deleteEmployee = async (req: AuthRequest, res: Response): Promise<v
|
|||||||
const employeesArray: string[] = JSON.parse(shift.assigned_employees || '[]');
|
const employeesArray: string[] = JSON.parse(shift.assigned_employees || '[]');
|
||||||
const filteredEmployees = employeesArray.filter((empId: string) => empId !== id);
|
const filteredEmployees = employeesArray.filter((empId: string) => empId !== id);
|
||||||
await db.run(
|
await db.run(
|
||||||
'UPDATE assigned_shifts SET assigned_employees = ? WHERE id = ?',
|
'UPDATE scheduled_shifts SET assigned_employees = ? WHERE id = ?',
|
||||||
[JSON.stringify(filteredEmployees), shift.id]
|
[JSON.stringify(filteredEmployees), shift.id]
|
||||||
);
|
);
|
||||||
} catch (parseError) {
|
} catch (parseError) {
|
||||||
console.warn(`Could not parse assigned_employees for shift ${shift.id}:`, shift.assigned_employees);
|
console.warn(`Could not parse assigned_employees for shift ${shift.id}:`, shift.assigned_employees);
|
||||||
// Falls JSON parsing fehlschlägt, setze leeres Array
|
|
||||||
await db.run(
|
await db.run(
|
||||||
'UPDATE assigned_shifts SET assigned_employees = ? WHERE id = ?',
|
'UPDATE scheduled_shifts SET assigned_employees = ? WHERE id = ?',
|
||||||
[JSON.stringify([]), shift.id]
|
[JSON.stringify([]), shift.id]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -244,10 +248,9 @@ export const deleteEmployee = async (req: AuthRequest, res: Response): Promise<v
|
|||||||
|
|
||||||
// 3. Nullify created_by references
|
// 3. Nullify created_by references
|
||||||
await db.run('UPDATE shift_plans SET created_by = NULL WHERE created_by = ?', [id]);
|
await db.run('UPDATE shift_plans SET created_by = NULL WHERE created_by = ?', [id]);
|
||||||
await db.run('UPDATE shift_templates SET created_by = NULL WHERE created_by = ?', [id]);
|
|
||||||
|
|
||||||
// 4. Finally delete the user
|
// 4. Finally delete the employee
|
||||||
await db.run('DELETE FROM users WHERE id = ?', [id]);
|
await db.run('DELETE FROM employees WHERE id = ?', [id]);
|
||||||
|
|
||||||
await db.run('COMMIT');
|
await db.run('COMMIT');
|
||||||
console.log('✅ Successfully deleted employee:', existingEmployee.email);
|
console.log('✅ Successfully deleted employee:', existingEmployee.email);
|
||||||
@@ -271,25 +274,26 @@ export const getAvailabilities = async (req: AuthRequest, res: Response): Promis
|
|||||||
const { employeeId } = req.params;
|
const { employeeId } = req.params;
|
||||||
|
|
||||||
// Check if employee exists
|
// Check if employee exists
|
||||||
const existingEmployee = await db.get('SELECT id FROM users WHERE id = ?', [employeeId]);
|
const existingEmployee = await db.get('SELECT id FROM employees WHERE id = ?', [employeeId]);
|
||||||
if (!existingEmployee) {
|
if (!existingEmployee) {
|
||||||
res.status(404).json({ error: 'Employee not found' });
|
res.status(404).json({ error: 'Employee not found' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const availabilities = await db.all<any>(`
|
const availabilities = await db.all<any>(`
|
||||||
SELECT * FROM employee_availabilities
|
SELECT * FROM employee_availability
|
||||||
WHERE employee_id = ?
|
WHERE employee_id = ?
|
||||||
ORDER BY day_of_week, start_time
|
ORDER BY day_of_week, time_slot_id
|
||||||
`, [employeeId]);
|
`, [employeeId]);
|
||||||
|
|
||||||
res.json(availabilities.map(avail => ({
|
res.json(availabilities.map(avail => ({
|
||||||
id: avail.id,
|
id: avail.id,
|
||||||
employeeId: avail.employee_id,
|
employeeId: avail.employee_id,
|
||||||
|
planId: avail.plan_id,
|
||||||
dayOfWeek: avail.day_of_week,
|
dayOfWeek: avail.day_of_week,
|
||||||
startTime: avail.start_time,
|
timeSlotId: avail.time_slot_id,
|
||||||
endTime: avail.end_time,
|
preferenceLevel: avail.preference_level,
|
||||||
isAvailable: avail.is_available === 1
|
notes: avail.notes
|
||||||
})));
|
})));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching availabilities:', error);
|
console.error('Error fetching availabilities:', error);
|
||||||
@@ -300,17 +304,10 @@ export const getAvailabilities = async (req: AuthRequest, res: Response): Promis
|
|||||||
export const updateAvailabilities = async (req: AuthRequest, res: Response): Promise<void> => {
|
export const updateAvailabilities = async (req: AuthRequest, res: Response): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const { employeeId } = req.params;
|
const { employeeId } = req.params;
|
||||||
const availabilities = req.body as Array<{
|
const { planId, availabilities } = req.body;
|
||||||
id?: string;
|
|
||||||
employeeId: string;
|
|
||||||
dayOfWeek: number;
|
|
||||||
startTime: string;
|
|
||||||
endTime: string;
|
|
||||||
isAvailable: boolean;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
// Check if employee exists
|
// Check if employee exists
|
||||||
const existingEmployee = await db.get('SELECT id FROM users WHERE id = ?', [employeeId]);
|
const existingEmployee = await db.get('SELECT id FROM employees WHERE id = ?', [employeeId]);
|
||||||
if (!existingEmployee) {
|
if (!existingEmployee) {
|
||||||
res.status(404).json({ error: 'Employee not found' });
|
res.status(404).json({ error: 'Employee not found' });
|
||||||
return;
|
return;
|
||||||
@@ -319,22 +316,23 @@ export const updateAvailabilities = async (req: AuthRequest, res: Response): Pro
|
|||||||
await db.run('BEGIN TRANSACTION');
|
await db.run('BEGIN TRANSACTION');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Delete existing availabilities
|
// Delete existing availabilities for this plan
|
||||||
await db.run('DELETE FROM employee_availabilities WHERE employee_id = ?', [employeeId]);
|
await db.run('DELETE FROM employee_availability WHERE employee_id = ? AND plan_id = ?', [employeeId, planId]);
|
||||||
|
|
||||||
// Insert new availabilities
|
// Insert new availabilities
|
||||||
for (const availability of availabilities) {
|
for (const availability of availabilities) {
|
||||||
const availabilityId = uuidv4();
|
const availabilityId = uuidv4();
|
||||||
await db.run(
|
await db.run(
|
||||||
`INSERT INTO employee_availabilities (id, employee_id, day_of_week, start_time, end_time, is_available)
|
`INSERT INTO employee_availability (id, employee_id, plan_id, day_of_week, time_slot_id, preference_level, notes)
|
||||||
VALUES (?, ?, ?, ?, ?, ?)`,
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
||||||
[
|
[
|
||||||
availabilityId,
|
availabilityId,
|
||||||
employeeId,
|
employeeId,
|
||||||
|
planId,
|
||||||
availability.dayOfWeek,
|
availability.dayOfWeek,
|
||||||
availability.startTime,
|
availability.timeSlotId,
|
||||||
availability.endTime,
|
availability.preferenceLevel,
|
||||||
availability.isAvailable ? 1 : 0
|
availability.notes || null
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -343,18 +341,19 @@ export const updateAvailabilities = async (req: AuthRequest, res: Response): Pro
|
|||||||
|
|
||||||
// Return updated availabilities
|
// Return updated availabilities
|
||||||
const updatedAvailabilities = await db.all<any>(`
|
const updatedAvailabilities = await db.all<any>(`
|
||||||
SELECT * FROM employee_availabilities
|
SELECT * FROM employee_availability
|
||||||
WHERE employee_id = ?
|
WHERE employee_id = ? AND plan_id = ?
|
||||||
ORDER BY day_of_week, start_time
|
ORDER BY day_of_week, time_slot_id
|
||||||
`, [employeeId]);
|
`, [employeeId, planId]);
|
||||||
|
|
||||||
res.json(updatedAvailabilities.map(avail => ({
|
res.json(updatedAvailabilities.map(avail => ({
|
||||||
id: avail.id,
|
id: avail.id,
|
||||||
employeeId: avail.employee_id,
|
employeeId: avail.employee_id,
|
||||||
|
planId: avail.plan_id,
|
||||||
dayOfWeek: avail.day_of_week,
|
dayOfWeek: avail.day_of_week,
|
||||||
startTime: avail.start_time,
|
timeSlotId: avail.time_slot_id,
|
||||||
endTime: avail.end_time,
|
preferenceLevel: avail.preference_level,
|
||||||
isAvailable: avail.is_available === 1
|
notes: avail.notes
|
||||||
})));
|
})));
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export const setupAdmin = async (req: Request, res: Response): Promise<void> =>
|
|||||||
try {
|
try {
|
||||||
// Check if admin already exists
|
// Check if admin already exists
|
||||||
const adminExists = await db.get<{ 'COUNT(*)': number }>(
|
const adminExists = await db.get<{ 'COUNT(*)': number }>(
|
||||||
'SELECT COUNT(*) FROM users WHERE role = ? AND is_active = 1',
|
'SELECT COUNT(*) FROM employees WHERE role = ? AND is_active = 1',
|
||||||
['admin']
|
['admin']
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -70,8 +70,8 @@ export const setupAdmin = async (req: Request, res: Response): Promise<void> =>
|
|||||||
try {
|
try {
|
||||||
// Create admin user
|
// Create admin user
|
||||||
await db.run(
|
await db.run(
|
||||||
`INSERT INTO users (id, email, password, name, role, is_active)
|
`INSERT INTO employees (id, email, password, name, role, is_active)
|
||||||
VALUES (?, ?, ?, ?, ?, ?)`,
|
VALUES (?, ?, ?, ?, ?, ?)`,
|
||||||
[adminId, email, hashedPassword, name, 'admin', 1]
|
[adminId, email, hashedPassword, name, 'admin', 1]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ export interface TimeSlot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Shift {
|
export interface Shift {
|
||||||
timeSlot: any;
|
|
||||||
id: string;
|
id: string;
|
||||||
planId: string;
|
planId: string;
|
||||||
timeSlotId: string;
|
timeSlotId: string;
|
||||||
|
|||||||
@@ -1,19 +1,24 @@
|
|||||||
|
// backend/src/scripts/checkTemplates.ts
|
||||||
import { db } from '../services/databaseService.js';
|
import { db } from '../services/databaseService.js';
|
||||||
import { ShiftPlan } from '../models/ShiftPlan.js';
|
|
||||||
|
|
||||||
async function checkTemplates() {
|
async function checkTemplates() {
|
||||||
try {
|
try {
|
||||||
const templates = await db.all<ShiftPlan>(
|
// KORREKTUR: employees statt users verwenden
|
||||||
`SELECT sp.*, u.name as created_by_name
|
const templates = await db.all<any>(
|
||||||
|
`SELECT sp.*, e.name as created_by_name
|
||||||
FROM shift_plans sp
|
FROM shift_plans sp
|
||||||
LEFT JOIN users u ON sp.created_by = u.id`
|
LEFT JOIN employees e ON sp.created_by = e.id
|
||||||
|
WHERE sp.is_template = 1`
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log('Templates:', templates);
|
console.log('Templates:', templates);
|
||||||
|
|
||||||
for (const template of templates) {
|
for (const template of templates) {
|
||||||
const shifts = await db.all<any>(
|
const shifts = await db.all<any>(
|
||||||
`SELECT * FROM template_shifts WHERE template_id = ?`,
|
`SELECT s.*, ts.name as time_slot_name
|
||||||
|
FROM shifts s
|
||||||
|
LEFT JOIN time_slots ts ON s.time_slot_id = ts.id
|
||||||
|
WHERE s.plan_id = ?`,
|
||||||
[template.id]
|
[template.id]
|
||||||
);
|
);
|
||||||
console.log(`Shifts for template ${template.id}:`, shifts);
|
console.log(`Shifts for template ${template.id}:`, shifts);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// backend/src/scripts/setupDefaultTemplate.ts
|
// backend/src/scripts/setupDefaultTemplate.ts
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import { db } from '../services/databaseService.js';
|
import { db } from '../services/databaseService.js';
|
||||||
import { DEFAULT_ZEBRA_TIME_SLOTS, TemplateShift } from '../models/ShiftPlan.js';
|
import { DEFAULT_ZEBRA_TIME_SLOTS } from '../models/defaults/shiftPlanDefaults.js';
|
||||||
|
|
||||||
interface AdminUser {
|
interface AdminUser {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -13,9 +13,10 @@ interface AdminUser {
|
|||||||
*/
|
*/
|
||||||
export async function setupDefaultTemplate(): Promise<void> {
|
export async function setupDefaultTemplate(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
// Prüfen ob bereits eine Standard-Vorlage existiert
|
// Prüfen ob bereits eine Standard-Vorlage existiert - KORREKTUR: shift_plans verwenden
|
||||||
const existingDefault = await db.get(
|
const existingDefault = await db.get(
|
||||||
'SELECT * FROM shift_templates WHERE is_default = 1'
|
'SELECT * FROM shift_plans WHERE is_template = 1 AND name = ?',
|
||||||
|
['Standardwoche']
|
||||||
);
|
);
|
||||||
|
|
||||||
if (existingDefault) {
|
if (existingDefault) {
|
||||||
@@ -23,9 +24,9 @@ export async function setupDefaultTemplate(): Promise<void> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Admin-Benutzer für die Standard-Vorlage finden
|
// Admin-Benutzer für die Standard-Vorlage finden - KORREKTUR: employees verwenden
|
||||||
const adminUser = await db.get<AdminUser>(
|
const adminUser = await db.get<AdminUser>(
|
||||||
'SELECT id FROM users WHERE role = ?',
|
'SELECT id FROM employees WHERE role = ?',
|
||||||
['admin']
|
['admin']
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -40,70 +41,74 @@ export async function setupDefaultTemplate(): Promise<void> {
|
|||||||
// Transaktion starten
|
// Transaktion starten
|
||||||
await db.run('BEGIN TRANSACTION');
|
await db.run('BEGIN TRANSACTION');
|
||||||
|
|
||||||
const timeSlots = DEFAULT_TIME_SLOTS;
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Standard-Vorlage erstellen
|
// Standard-Vorlage erstellen - KORREKTUR: shift_plans verwenden
|
||||||
await db.run(
|
await db.run(
|
||||||
`INSERT INTO shift_templates (id, name, description, is_default, created_by)
|
`INSERT INTO shift_plans (id, name, description, is_template, status, created_by)
|
||||||
VALUES (?, ?, ?, ?, ?)`,
|
VALUES (?, ?, ?, ?, ?, ?)`,
|
||||||
[
|
[
|
||||||
templateId,
|
templateId,
|
||||||
'Standard Wochenplan',
|
'Standardwoche',
|
||||||
'Mo-Do: Vormittags- und Nachmittagsschicht, Fr: nur Vormittagsschicht',
|
'Mo-Do: Vormittags- und Nachmittagsschicht, Fr: nur Vormittagsschicht',
|
||||||
1,
|
1, // is_template = true
|
||||||
|
'template', // status = 'template'
|
||||||
adminUser.id
|
adminUser.id
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log('Standard-Vorlage erstellt:', templateId);
|
console.log('Standard-Vorlage erstellt:', templateId);
|
||||||
|
|
||||||
|
// Zeit-Slots erstellen - KORREKTUR: time_slots verwenden
|
||||||
|
const timeSlots = DEFAULT_ZEBRA_TIME_SLOTS.map(slot => ({
|
||||||
|
...slot,
|
||||||
|
id: uuidv4()
|
||||||
|
}));
|
||||||
|
|
||||||
for (const slot of timeSlots) {
|
for (const slot of timeSlots) {
|
||||||
await db.run(
|
await db.run(
|
||||||
`INSERT INTO template_time_slots (id, template_id, name, start_time, end_time)
|
`INSERT INTO time_slots (id, plan_id, name, start_time, end_time, description)
|
||||||
VALUES (?, ?, ?, ?, ?)`,
|
VALUES (?, ?, ?, ?, ?, ?)`,
|
||||||
[slot.id, templateId, slot.name, slot.startTime, slot.endTime]
|
[slot.id, templateId, slot.name, slot.startTime, slot.endTime, slot.description]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('✅ Zeit-Slots erstellt');
|
console.log('✅ Zeit-Slots erstellt');
|
||||||
|
|
||||||
// Schichten für Mo-Do
|
// Schichten für Mo-Do - KORREKTUR: shifts verwenden
|
||||||
for (let day = 1; day <= 4; day++) {
|
for (let day = 1; day <= 4; day++) {
|
||||||
// Vormittagsschicht
|
// Vormittagsschicht
|
||||||
await db.run(
|
await db.run(
|
||||||
`INSERT INTO template_shifts (id, template_id, day_of_week, time_slot_id, required_employees, color)
|
`INSERT INTO shifts (id, plan_id, day_of_week, time_slot_id, required_employees, color)
|
||||||
VALUES (?, ?, ?, ?, ?, ?)`,
|
VALUES (?, ?, ?, ?, ?, ?)`,
|
||||||
[uuidv4(), templateId, day, timeSlots[0].id, 1, '#3498db']
|
[uuidv4(), templateId, day, timeSlots[0].id, 2, '#3498db']
|
||||||
);
|
);
|
||||||
|
|
||||||
// Nachmittagsschicht
|
// Nachmittagsschicht
|
||||||
await db.run(
|
await db.run(
|
||||||
`INSERT INTO template_shifts (id, template_id, day_of_week, time_slot_id, required_employees, color)
|
`INSERT INTO shifts (id, plan_id, day_of_week, time_slot_id, required_employees, color)
|
||||||
VALUES (?, ?, ?, ?, ?, ?)`,
|
VALUES (?, ?, ?, ?, ?, ?)`,
|
||||||
[uuidv4(), templateId, day, timeSlots[1].id, 1, '#e74c3c']
|
[uuidv4(), templateId, day, timeSlots[1].id, 2, '#e74c3c']
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Freitag nur Vormittagsschicht
|
// Freitag nur Vormittagsschicht
|
||||||
await db.run(
|
await db.run(
|
||||||
`INSERT INTO template_shifts (id, template_id, day_of_week, time_slot_id, required_employees, color)
|
`INSERT INTO shifts (id, plan_id, day_of_week, time_slot_id, required_employees, color)
|
||||||
VALUES (?, ?, ?, ?, ?, ?)`,
|
VALUES (?, ?, ?, ?, ?, ?)`,
|
||||||
[uuidv4(), templateId, 5, timeSlots[0].id, 1, '#3498db']
|
[uuidv4(), templateId, 5, timeSlots[0].id, 2, '#3498db']
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log('✅ Schichten erstellt');
|
console.log('✅ Schichten erstellt');
|
||||||
|
|
||||||
// In der problematischen Stelle:
|
// In der problematischen Stelle: KORREKTUR: shift_plans verwenden
|
||||||
const createdTemplate = await db.get(
|
const createdTemplate = await db.get(
|
||||||
'SELECT * FROM shift_templates WHERE id = ?',
|
'SELECT * FROM shift_plans WHERE id = ?',
|
||||||
[templateId]
|
[templateId]
|
||||||
) as { name: string } | undefined;
|
) as { name: string } | undefined;
|
||||||
console.log('📋 Erstellte Vorlage:', createdTemplate?.name);
|
console.log('📋 Erstellte Vorlage:', createdTemplate?.name);
|
||||||
|
|
||||||
const shiftCount = await db.get(
|
const shiftCount = await db.get(
|
||||||
'SELECT COUNT(*) as count FROM template_shifts WHERE template_id = ?',
|
'SELECT COUNT(*) as count FROM shifts WHERE plan_id = ?',
|
||||||
[templateId]
|
[templateId]
|
||||||
) as { count: number } | undefined;
|
) as { count: number } | undefined;
|
||||||
console.log(`📊 Anzahl Schichten: ${shiftCount?.count}`);
|
console.log(`📊 Anzahl Schichten: ${shiftCount?.count}`);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
|
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
|
||||||
import { Employee } from '../types/employee';
|
import { Employee } from '../../../backend/src/models/employee';
|
||||||
|
|
||||||
interface LoginRequest {
|
interface LoginRequest {
|
||||||
email: string;
|
email: string;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// frontend/src/pages/Employees/EmployeeManagement.tsx
|
// frontend/src/pages/Employees/EmployeeManagement.tsx
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Employee } from '../../types/employee';
|
import { Employee } from '../../../../backend/src/models/employee';
|
||||||
import { employeeService } from '../../services/employeeService';
|
import { employeeService } from '../../services/employeeService';
|
||||||
import EmployeeList from './components/EmployeeList';
|
import EmployeeList from './components/EmployeeList';
|
||||||
import EmployeeForm from './components/EmployeeForm';
|
import EmployeeForm from './components/EmployeeForm';
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
// frontend/src/pages/Employees/components/AvailabilityManager.tsx - KORRIGIERT
|
// frontend/src/pages/Employees/components/AvailabilityManager.tsx
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Employee, Availability } from '../../../../../backend/src/models/employee';
|
import { Employee, EmployeeAvailability } from '../../../../../backend/src/models/employee';
|
||||||
import { employeeService } from '../../../services/employeeService';
|
import { employeeService } from '../../../services/employeeService';
|
||||||
import { shiftPlanService } from '../../../services/shiftPlanService';
|
import { shiftPlanService } from '../../../services/shiftPlanService';
|
||||||
import { ShiftPlan, TimeSlot } from '../../../../../backend/src/models/shiftPlan';
|
import { ShiftPlan, TimeSlot, Shift } from '../../../../../backend/src/models/shiftPlan';
|
||||||
import { shiftTemplateService } from '../../../services/shiftTemplateService';
|
|
||||||
import { time } from 'console';
|
|
||||||
|
|
||||||
interface AvailabilityManagerProps {
|
interface AvailabilityManagerProps {
|
||||||
employee: Employee;
|
employee: Employee;
|
||||||
@@ -13,19 +11,19 @@ interface AvailabilityManagerProps {
|
|||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Local interface extensions
|
||||||
|
interface ExtendedTimeSlot extends TimeSlot {
|
||||||
|
displayName?: string;
|
||||||
|
source?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Availability extends EmployeeAvailability {
|
||||||
|
isAvailable?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
// Verfügbarkeits-Level
|
// Verfügbarkeits-Level
|
||||||
export type AvailabilityLevel = 1 | 2 | 3;
|
export type AvailabilityLevel = 1 | 2 | 3;
|
||||||
|
|
||||||
// Interface für Zeit-Slots
|
|
||||||
interface TimeSlot {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
startTime: string;
|
|
||||||
endTime: string;
|
|
||||||
displayName: string;
|
|
||||||
source: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
||||||
employee,
|
employee,
|
||||||
onSave,
|
onSave,
|
||||||
@@ -35,7 +33,7 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
|||||||
const [shiftPlans, setShiftPlans] = useState<ShiftPlan[]>([]);
|
const [shiftPlans, setShiftPlans] = useState<ShiftPlan[]>([]);
|
||||||
const [selectedPlanId, setSelectedPlanId] = useState<string>('');
|
const [selectedPlanId, setSelectedPlanId] = useState<string>('');
|
||||||
const [selectedPlan, setSelectedPlan] = useState<ShiftPlan | null>(null);
|
const [selectedPlan, setSelectedPlan] = useState<ShiftPlan | null>(null);
|
||||||
const [timeSlots, setTimeSlots] = useState<TimeSlot[]>([]);
|
const [timeSlots, setTimeSlots] = useState<ExtendedTimeSlot[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
@@ -47,7 +45,7 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
|||||||
{ id: 4, name: 'Donnerstag' },
|
{ id: 4, name: 'Donnerstag' },
|
||||||
{ id: 5, name: 'Freitag' },
|
{ id: 5, name: 'Freitag' },
|
||||||
{ id: 6, name: 'Samstag' },
|
{ id: 6, name: 'Samstag' },
|
||||||
{ id: 0, name: 'Sonntag' }
|
{ id: 7, name: 'Sonntag' }
|
||||||
];
|
];
|
||||||
|
|
||||||
const availabilityLevels = [
|
const availabilityLevels = [
|
||||||
@@ -66,81 +64,51 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
|||||||
}
|
}
|
||||||
}, [selectedPlanId]);
|
}, [selectedPlanId]);
|
||||||
|
|
||||||
// NEU: Hole Zeit-Slots aus Schichtvorlagen als Hauptquelle
|
// Load time slots from shift plans
|
||||||
const loadTimeSlotsFromTemplates = async (): Promise<TimeSlot[]> => {
|
const extractTimeSlotsFromPlans = (plans: ShiftPlan[]): ExtendedTimeSlot[] => {
|
||||||
try {
|
|
||||||
console.log('🔄 LADE ZEIT-SLOTS AUS SCHICHTVORLAGEN...');
|
|
||||||
const shiftPlan = await shiftPlanService.getShiftPlans();
|
|
||||||
console.log('✅ SCHICHTVORLAGEN GELADEN:', shiftPlan);
|
|
||||||
|
|
||||||
const allTimeSlots = new Map<string, TimeSlot>();
|
|
||||||
|
|
||||||
shiftPlan.forEach(plan => {
|
|
||||||
console.log(`📋 VORLAGE: ${plan.name}`, plan);
|
|
||||||
|
|
||||||
// Extrahiere Zeit-Slots aus den Schicht-Zeitbereichen
|
|
||||||
if (plan.shifts && plan.shifts.length > 0) {
|
|
||||||
plan.shifts.forEach(shift => {
|
|
||||||
const key = `${shift.timeSlot.startTime}-${shift.timeSlot.endTime}`;
|
|
||||||
if (!allTimeSlots.has(key)) {
|
|
||||||
allTimeSlots.set(key, {
|
|
||||||
id: shift.id || `slot-${shift.timeSlot.startTime.replace(/:/g, '')}-${shift.timeSlot.endTime.replace(/:/g, '')}`,
|
|
||||||
name: shift.timeSlot.name || 'Schicht',
|
|
||||||
startTime: shift.timeSlot.startTime,
|
|
||||||
endTime: shift.timeSlot.endTime,
|
|
||||||
displayName: `${shift.timeSlot.name || 'Schicht'} (${formatTime(shift.timeSlot.startTime)}-${formatTime(shift.timeSlot.endTime)})`,
|
|
||||||
source: `Vorlage: ${plan.name}`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = Array.from(allTimeSlots.values()).sort((a, b) =>
|
|
||||||
a.startTime.localeCompare(b.startTime)
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log('✅ ZEIT-SLOTS AUS VORLAGEN:', result);
|
|
||||||
return result;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ FEHLER BEIM LADEN DER VORLAGEN:', error);
|
|
||||||
return getDefaultTimeSlots();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// NEU: Alternative Methode - Extrahiere aus Schichtplänen
|
|
||||||
const extractTimeSlotsFromPlans = (plans: ShiftPlan[]): TimeSlot[] => {
|
|
||||||
console.log('🔄 EXTRAHIERE ZEIT-SLOTS AUS SCHICHTPLÄNEN:', plans);
|
console.log('🔄 EXTRAHIERE ZEIT-SLOTS AUS SCHICHTPLÄNEN:', plans);
|
||||||
|
|
||||||
const allTimeSlots = new Map<string, TimeSlot>();
|
const allTimeSlots = new Map<string, ExtendedTimeSlot>();
|
||||||
|
|
||||||
plans.forEach(plan => {
|
plans.forEach(plan => {
|
||||||
console.log(`📋 ANALYSIERE PLAN: ${plan.name}`, {
|
console.log(`📋 ANALYSIERE PLAN: ${plan.name}`, {
|
||||||
id: plan.id,
|
id: plan.id,
|
||||||
|
timeSlots: plan.timeSlots,
|
||||||
shifts: plan.shifts
|
shifts: plan.shifts
|
||||||
});
|
});
|
||||||
|
|
||||||
// Prüfe ob Schichten existieren und ein Array sind
|
// Use timeSlots from plan if available
|
||||||
if (plan.shifts && Array.isArray(plan.shifts)) {
|
if (plan.timeSlots && Array.isArray(plan.timeSlots)) {
|
||||||
|
plan.timeSlots.forEach(timeSlot => {
|
||||||
|
console.log(` 🔍 ZEIT-SLOT:`, timeSlot);
|
||||||
|
const key = `${timeSlot.startTime}-${timeSlot.endTime}`;
|
||||||
|
if (!allTimeSlots.has(key)) {
|
||||||
|
allTimeSlots.set(key, {
|
||||||
|
...timeSlot,
|
||||||
|
displayName: `${timeSlot.name} (${formatTime(timeSlot.startTime)}-${formatTime(timeSlot.endTime)})`,
|
||||||
|
source: `Plan: ${plan.name}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also extract from shifts if timeSlots is empty
|
||||||
|
if ((!plan.timeSlots || plan.timeSlots.length === 0) && plan.shifts && Array.isArray(plan.shifts)) {
|
||||||
plan.shifts.forEach(shift => {
|
plan.shifts.forEach(shift => {
|
||||||
console.log(` 🔍 SCHICHT:`, shift);
|
console.log(` 🔍 SCHICHT:`, shift);
|
||||||
|
// For shifts, we need to find the corresponding time slot
|
||||||
if (shift.timeSlot.startTime && shift.timeSlot.endTime) {
|
const timeSlot = plan.timeSlots?.find(ts => ts.id === shift.timeSlotId);
|
||||||
const key = `${shift.timeSlot.startTime}-${shift.timeSlot.endTime}`;
|
if (timeSlot) {
|
||||||
|
const key = `${timeSlot.startTime}-${timeSlot.endTime}`;
|
||||||
if (!allTimeSlots.has(key)) {
|
if (!allTimeSlots.has(key)) {
|
||||||
allTimeSlots.set(key, {
|
allTimeSlots.set(key, {
|
||||||
id: `slot-${shift.timeSlot.startTime.replace(/:/g, '')}-${shift.timeSlot.endTime.replace(/:/g, '')}`,
|
...timeSlot,
|
||||||
name: shift.timeSlot.name || 'Schicht',
|
displayName: `${timeSlot.name} (${formatTime(timeSlot.startTime)}-${formatTime(timeSlot.endTime)})`,
|
||||||
startTime: shift.timeSlot.startTime,
|
|
||||||
endTime: shift.timeSlot.endTime,
|
|
||||||
displayName: `${shift.timeSlot.name || 'Schicht'} (${formatTime(shift.timeSlot.startTime)}-${formatTime(shift.timeSlot.endTime)})`,
|
|
||||||
source: `Plan: ${plan.name}`
|
source: `Plan: ${plan.name}`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
console.log(` ❌ KEINE SCHICHTEN IN PLAN ${plan.name} oder keine Array-Struktur`);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -152,7 +120,7 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDefaultTimeSlots = (): TimeSlot[] => {
|
/*const getDefaultTimeSlots = (): ExtendedTimeSlot[] => {
|
||||||
console.log('⚠️ VERWENDE STANDARD-ZEIT-SLOTS');
|
console.log('⚠️ VERWENDE STANDARD-ZEIT-SLOTS');
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -180,7 +148,7 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
|||||||
source: 'Standard'
|
source: 'Standard'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
};
|
};*/
|
||||||
|
|
||||||
const formatTime = (time: string): string => {
|
const formatTime = (time: string): string => {
|
||||||
return time.substring(0, 5);
|
return time.substring(0, 5);
|
||||||
@@ -191,30 +159,28 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
console.log('🔄 LADE DATEN FÜR MITARBEITER:', employee.id);
|
console.log('🔄 LADE DATEN FÜR MITARBEITER:', employee.id);
|
||||||
|
|
||||||
// 1. Lade Verfügbarkeiten
|
// 1. Load availabilities
|
||||||
let existingAvailabilities: Availability[] = [];
|
let existingAvailabilities: Availability[] = [];
|
||||||
try {
|
try {
|
||||||
existingAvailabilities = await employeeService.getAvailabilities(employee.id);
|
const availabilitiesData = await employeeService.getAvailabilities(employee.id);
|
||||||
|
existingAvailabilities = availabilitiesData.map(avail => ({
|
||||||
|
...avail,
|
||||||
|
isAvailable: avail.preferenceLevel !== 3
|
||||||
|
}));
|
||||||
console.log('✅ VERFÜGBARKEITEN GELADEN:', existingAvailabilities.length);
|
console.log('✅ VERFÜGBARKEITEN GELADEN:', existingAvailabilities.length);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('⚠️ KEINE VERFÜGBARKEITEN GEFUNDEN');
|
console.log('⚠️ KEINE VERFÜGBARKEITEN GEFUNDEN');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Lade Schichtpläne
|
// 2. Load shift plans
|
||||||
console.log('🔄 LADE SCHICHTPLÄNE...');
|
console.log('🔄 LADE SCHICHTPLÄNE...');
|
||||||
const plans = await shiftPlanService.getShiftPlans();
|
const plans = await shiftPlanService.getShiftPlans();
|
||||||
console.log('✅ SCHICHTPLÄNE GELADEN:', plans.length, plans);
|
console.log('✅ SCHICHTPLÄNE GELADEN:', plans.length, plans);
|
||||||
|
|
||||||
// 3. VERSUCH 1: Lade Zeit-Slots aus Schichtvorlagen (bessere Quelle)
|
// 3. Extract time slots from plans
|
||||||
let extractedTimeSlots = await loadTimeSlotsFromTemplates();
|
let extractedTimeSlots = extractTimeSlotsFromPlans(plans);
|
||||||
|
|
||||||
// VERSUCH 2: Falls keine Zeit-Slots aus Vorlagen, versuche es mit Schichtplänen
|
|
||||||
if (extractedTimeSlots.length === 0) {
|
|
||||||
console.log('⚠️ KEINE ZEIT-SLOTS AUS VORLAGEN, VERSUCHE SCHICHTPLÄNE...');
|
|
||||||
extractedTimeSlots = extractTimeSlotsFromPlans(plans);
|
|
||||||
}
|
|
||||||
|
|
||||||
// VERSUCH 3: Falls immer noch keine, verwende Standard-Slots
|
// 4. Fallback to default slots if none found
|
||||||
if (extractedTimeSlots.length === 0) {
|
if (extractedTimeSlots.length === 0) {
|
||||||
console.log('⚠️ KEINE ZEIT-SLOTS GEFUNDEN, VERWENDE STANDARD-SLOTS');
|
console.log('⚠️ KEINE ZEIT-SLOTS GEFUNDEN, VERWENDE STANDARD-SLOTS');
|
||||||
extractedTimeSlots = getDefaultTimeSlots();
|
extractedTimeSlots = getDefaultTimeSlots();
|
||||||
@@ -223,17 +189,17 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
|||||||
setTimeSlots(extractedTimeSlots);
|
setTimeSlots(extractedTimeSlots);
|
||||||
setShiftPlans(plans);
|
setShiftPlans(plans);
|
||||||
|
|
||||||
// 4. Erstelle Standard-Verfügbarkeiten falls nötig
|
// 5. Create default availabilities if needed
|
||||||
if (existingAvailabilities.length === 0) {
|
if (existingAvailabilities.length === 0) {
|
||||||
const defaultAvailabilities: Availability[] = daysOfWeek.flatMap(day =>
|
const defaultAvailabilities: Availability[] = daysOfWeek.flatMap(day =>
|
||||||
extractedTimeSlots.map(slot => ({
|
extractedTimeSlots.map(slot => ({
|
||||||
id: `temp-${day.id}-${slot.id}`,
|
id: `temp-${day.id}-${slot.id}`,
|
||||||
employeeId: employee.id,
|
employeeId: employee.id,
|
||||||
|
planId: '', // Will be set when saving
|
||||||
dayOfWeek: day.id,
|
dayOfWeek: day.id,
|
||||||
startTime: slot.startTime,
|
timeSlotId: slot.id,
|
||||||
endTime: slot.endTime,
|
preferenceLevel: 3 as AvailabilityLevel,
|
||||||
isAvailable: false,
|
isAvailable: false
|
||||||
availabilityLevel: 3 as AvailabilityLevel
|
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
setAvailabilities(defaultAvailabilities);
|
setAvailabilities(defaultAvailabilities);
|
||||||
@@ -242,7 +208,7 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
|||||||
setAvailabilities(existingAvailabilities);
|
setAvailabilities(existingAvailabilities);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Wähle ersten Plan aus
|
// 6. Select first plan
|
||||||
if (plans.length > 0) {
|
if (plans.length > 0) {
|
||||||
const publishedPlan = plans.find(plan => plan.status === 'published');
|
const publishedPlan = plans.find(plan => plan.status === 'published');
|
||||||
const firstPlan = publishedPlan || plans[0];
|
const firstPlan = publishedPlan || plans[0];
|
||||||
@@ -264,8 +230,8 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
|||||||
setSelectedPlan(plan);
|
setSelectedPlan(plan);
|
||||||
console.log('✅ SCHICHTPLAN GELADEN:', {
|
console.log('✅ SCHICHTPLAN GELADEN:', {
|
||||||
name: plan.name,
|
name: plan.name,
|
||||||
shiftsCount: plan.shifts?.length || 0,
|
timeSlotsCount: plan.timeSlots?.length || 0,
|
||||||
shifts: plan.shifts
|
shiftsCount: plan.shifts?.length || 0
|
||||||
});
|
});
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error('❌ FEHLER BEIM LADEN DES SCHICHTPLANS:', err);
|
console.error('❌ FEHLER BEIM LADEN DES SCHICHTPLANS:', err);
|
||||||
@@ -277,16 +243,9 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
|||||||
console.log(`🔄 ÄNDERE VERFÜGBARKEIT: Tag ${dayId}, Slot ${timeSlotId}, Level ${level}`);
|
console.log(`🔄 ÄNDERE VERFÜGBARKEIT: Tag ${dayId}, Slot ${timeSlotId}, Level ${level}`);
|
||||||
|
|
||||||
setAvailabilities(prev => {
|
setAvailabilities(prev => {
|
||||||
const timeSlot = timeSlots.find(s => s.id === timeSlotId);
|
|
||||||
if (!timeSlot) {
|
|
||||||
console.log('❌ ZEIT-SLOT NICHT GEFUNDEN:', timeSlotId);
|
|
||||||
return prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
const existingIndex = prev.findIndex(avail =>
|
const existingIndex = prev.findIndex(avail =>
|
||||||
avail.dayOfWeek === dayId &&
|
avail.dayOfWeek === dayId &&
|
||||||
avail.startTime === timeSlot.startTime &&
|
avail.timeSlotId === timeSlotId
|
||||||
avail.endTime === timeSlot.endTime
|
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(`🔍 EXISTIERENDE VERFÜGBARKEIT GEFUNDEN AN INDEX:`, existingIndex);
|
console.log(`🔍 EXISTIERENDE VERFÜGBARKEIT GEFUNDEN AN INDEX:`, existingIndex);
|
||||||
@@ -296,7 +255,7 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
|||||||
const updated = [...prev];
|
const updated = [...prev];
|
||||||
updated[existingIndex] = {
|
updated[existingIndex] = {
|
||||||
...updated[existingIndex],
|
...updated[existingIndex],
|
||||||
availabilityLevel: level,
|
preferenceLevel: level,
|
||||||
isAvailable: level !== 3
|
isAvailable: level !== 3
|
||||||
};
|
};
|
||||||
console.log('✅ VERFÜGBARKEIT AKTUALISIERT:', updated[existingIndex]);
|
console.log('✅ VERFÜGBARKEIT AKTUALISIERT:', updated[existingIndex]);
|
||||||
@@ -306,11 +265,11 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
|||||||
const newAvailability: Availability = {
|
const newAvailability: Availability = {
|
||||||
id: `temp-${dayId}-${timeSlotId}-${Date.now()}`,
|
id: `temp-${dayId}-${timeSlotId}-${Date.now()}`,
|
||||||
employeeId: employee.id,
|
employeeId: employee.id,
|
||||||
|
planId: selectedPlanId || '', // Use selected plan if available
|
||||||
dayOfWeek: dayId,
|
dayOfWeek: dayId,
|
||||||
startTime: timeSlot.startTime,
|
timeSlotId: timeSlotId,
|
||||||
endTime: timeSlot.endTime,
|
preferenceLevel: level,
|
||||||
isAvailable: level !== 3,
|
isAvailable: level !== 3
|
||||||
availabilityLevel: level
|
|
||||||
};
|
};
|
||||||
console.log('🆕 NEUE VERFÜGBARKEIT ERSTELLT:', newAvailability);
|
console.log('🆕 NEUE VERFÜGBARKEIT ERSTELLT:', newAvailability);
|
||||||
return [...prev, newAvailability];
|
return [...prev, newAvailability];
|
||||||
@@ -319,19 +278,12 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getAvailabilityForDayAndSlot = (dayId: number, timeSlotId: string): AvailabilityLevel => {
|
const getAvailabilityForDayAndSlot = (dayId: number, timeSlotId: string): AvailabilityLevel => {
|
||||||
const timeSlot = timeSlots.find(s => s.id === timeSlotId);
|
|
||||||
if (!timeSlot) {
|
|
||||||
console.log('❌ ZEIT-SLOT NICHT GEFUNDEN FÜR ABFRAGE:', timeSlotId);
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
const availability = availabilities.find(avail =>
|
const availability = availabilities.find(avail =>
|
||||||
avail.dayOfWeek === dayId &&
|
avail.dayOfWeek === dayId &&
|
||||||
avail.startTime === timeSlot.startTime &&
|
avail.timeSlotId === timeSlotId
|
||||||
avail.endTime === timeSlot.endTime
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = availability?.availabilityLevel || 3;
|
const result = availability?.preferenceLevel || 3;
|
||||||
console.log(`🔍 ABFRAGE VERFÜGBARKEIT: Tag ${dayId}, Slot ${timeSlotId} = Level ${result}`);
|
console.log(`🔍 ABFRAGE VERFÜGBARKEIT: Tag ${dayId}, Slot ${timeSlotId} = Level ${result}`);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -342,7 +294,18 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
|||||||
setSaving(true);
|
setSaving(true);
|
||||||
setError('');
|
setError('');
|
||||||
|
|
||||||
await employeeService.updateAvailabilities(employee.id, availabilities);
|
// Convert to EmployeeAvailability format for API
|
||||||
|
const availabilitiesToSave: EmployeeAvailability[] = availabilities.map(avail => ({
|
||||||
|
id: avail.id,
|
||||||
|
employeeId: avail.employeeId,
|
||||||
|
planId: avail.planId || selectedPlanId, // Use selected plan if planId is empty
|
||||||
|
dayOfWeek: avail.dayOfWeek,
|
||||||
|
timeSlotId: avail.timeSlotId,
|
||||||
|
preferenceLevel: avail.preferenceLevel,
|
||||||
|
notes: avail.notes
|
||||||
|
}));
|
||||||
|
|
||||||
|
await employeeService.updateAvailabilities(employee.id, availabilitiesToSave);
|
||||||
console.log('✅ VERFÜGBARKEITEN ERFOLGREICH GESPEICHERT');
|
console.log('✅ VERFÜGBARKEITEN ERFOLGREICH GESPEICHERT');
|
||||||
|
|
||||||
onSave();
|
onSave();
|
||||||
@@ -354,10 +317,6 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTimeSlotsForTimetable = (): TimeSlot[] => {
|
|
||||||
return timeSlots;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div style={{ textAlign: 'center', padding: '40px' }}>
|
<div style={{ textAlign: 'center', padding: '40px' }}>
|
||||||
@@ -366,8 +325,6 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const timetableTimeSlots = getTimeSlotsForTimetable();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{
|
<div style={{
|
||||||
maxWidth: '1400px',
|
maxWidth: '1400px',
|
||||||
@@ -419,7 +376,7 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Rest der Komponente... */}
|
{/* Employee Info */}
|
||||||
<div style={{ marginBottom: '20px' }}>
|
<div style={{ marginBottom: '20px' }}>
|
||||||
<h3 style={{ margin: '0 0 10px 0', color: '#34495e' }}>
|
<h3 style={{ margin: '0 0 10px 0', color: '#34495e' }}>
|
||||||
{employee.name}
|
{employee.name}
|
||||||
@@ -442,7 +399,7 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Verfügbarkeits-Legende */}
|
{/* Availability Legend */}
|
||||||
<div style={{
|
<div style={{
|
||||||
marginBottom: '30px',
|
marginBottom: '30px',
|
||||||
padding: '20px',
|
padding: '20px',
|
||||||
@@ -479,7 +436,7 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Schichtplan Auswahl */}
|
{/* Shift Plan Selection */}
|
||||||
<div style={{
|
<div style={{
|
||||||
marginBottom: '30px',
|
marginBottom: '30px',
|
||||||
padding: '20px',
|
padding: '20px',
|
||||||
@@ -509,7 +466,7 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
|||||||
<option value="">Bitte auswählen...</option>
|
<option value="">Bitte auswählen...</option>
|
||||||
{shiftPlans.map(plan => (
|
{shiftPlans.map(plan => (
|
||||||
<option key={plan.id} value={plan.id}>
|
<option key={plan.id} value={plan.id}>
|
||||||
{plan.name} ({plan.shifts?.length || 0} Schichten)
|
{plan.name} ({plan.timeSlots?.length || 0} Zeit-Slots)
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
@@ -517,8 +474,8 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Verfügbarkeits-Timetable */}
|
{/* Availability Timetable */}
|
||||||
{timetableTimeSlots.length > 0 ? (
|
{timeSlots.length > 0 ? (
|
||||||
<div style={{
|
<div style={{
|
||||||
marginBottom: '30px',
|
marginBottom: '30px',
|
||||||
border: '1px solid #e0e0e0',
|
border: '1px solid #e0e0e0',
|
||||||
@@ -533,7 +490,7 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
|||||||
}}>
|
}}>
|
||||||
Verfügbarkeit definieren
|
Verfügbarkeit definieren
|
||||||
<div style={{ fontSize: '14px', fontWeight: 'normal', marginTop: '5px' }}>
|
<div style={{ fontSize: '14px', fontWeight: 'normal', marginTop: '5px' }}>
|
||||||
{timetableTimeSlots.length} Schichttypen verfügbar
|
{timeSlots.length} Schichttypen verfügbar
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -568,8 +525,8 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{timetableTimeSlots.map((timeSlot, timeIndex) => (
|
{timeSlots.map((timeSlot, timeIndex) => (
|
||||||
<tr key={`timeSlot-${timeSlot.id}-timeIndex-${timeIndex}`} style={{
|
<tr key={timeSlot.id} style={{
|
||||||
backgroundColor: timeIndex % 2 === 0 ? 'white' : '#f8f9fa'
|
backgroundColor: timeIndex % 2 === 0 ? 'white' : '#f8f9fa'
|
||||||
}}>
|
}}>
|
||||||
<td style={{
|
<td style={{
|
||||||
@@ -643,9 +600,9 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
|
|||||||
}}>
|
}}>
|
||||||
<div style={{ fontSize: '48px', marginBottom: '20px' }}>❌</div>
|
<div style={{ fontSize: '48px', marginBottom: '20px' }}>❌</div>
|
||||||
<h4>Keine Schichttypen konfiguriert</h4>
|
<h4>Keine Schichttypen konfiguriert</h4>
|
||||||
<p>Es wurden keine Zeit-Slots in den Schichtvorlagen oder -plänen gefunden.</p>
|
<p>Es wurden keine Zeit-Slots in den Schichtplänen gefunden.</p>
|
||||||
<p style={{ fontSize: '14px', marginTop: '10px' }}>
|
<p style={{ fontSize: '14px', marginTop: '10px' }}>
|
||||||
Bitte erstellen Sie zuerst Schichtvorlagen mit Zeit-Slots.
|
Bitte erstellen Sie zuerst Schichtpläne mit Zeit-Slots.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// frontend/src/services/employeeService.ts
|
// frontend/src/services/employeeService.ts
|
||||||
import { Employee, CreateEmployeeRequest, UpdateEmployeeRequest, Availability } from '../types/employee';
|
import { Employee, CreateEmployeeRequest, UpdateEmployeeRequest, EmployeeAvailability } from '../../../backend/src/models/employee';
|
||||||
|
|
||||||
const API_BASE_URL = 'http://localhost:3002/api';
|
const API_BASE_URL = 'http://localhost:3002/api';
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ export class EmployeeService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAvailabilities(employeeId: string): Promise<Availability[]> {
|
async getAvailabilities(employeeId: string): Promise<EmployeeAvailability[]> {
|
||||||
const response = await fetch(`${API_BASE_URL}/employees/${employeeId}/availabilities`, {
|
const response = await fetch(`${API_BASE_URL}/employees/${employeeId}/availabilities`, {
|
||||||
headers: getAuthHeaders(),
|
headers: getAuthHeaders(),
|
||||||
});
|
});
|
||||||
@@ -102,7 +102,7 @@ export class EmployeeService {
|
|||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateAvailabilities(employeeId: string, availabilities: Availability[]): Promise<Availability[]> {
|
async updateAvailabilities(employeeId: string, availabilities: EmployeeAvailability[]): Promise<EmployeeAvailability[]> {
|
||||||
const response = await fetch(`${API_BASE_URL}/employees/${employeeId}/availabilities`, {
|
const response = await fetch(`${API_BASE_URL}/employees/${employeeId}/availabilities`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: getAuthHeaders(),
|
headers: getAuthHeaders(),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// frontend/src/services/shiftPlanService.ts
|
// frontend/src/services/shiftPlanService.ts
|
||||||
import { authService } from './authService';
|
import { authService } from './authService';
|
||||||
import { ShiftPlan, CreateShiftPlanRequest, ShiftSlot } from '../types/shiftPlan.js';
|
import { ShiftPlan, CreateShiftPlanRequest, Shift } from '../../../backend/src/models/shiftPlan.js';
|
||||||
|
|
||||||
const API_BASE = 'http://localhost:3002/api/shift-plans';
|
const API_BASE = 'http://localhost:3002/api/shift-plans';
|
||||||
|
|
||||||
@@ -21,20 +21,7 @@ export const shiftPlanService = {
|
|||||||
throw new Error('Fehler beim Laden der Schichtpläne');
|
throw new Error('Fehler beim Laden der Schichtpläne');
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
return await response.json();
|
||||||
|
|
||||||
// Convert snake_case to camelCase
|
|
||||||
return data.map((plan: any) => ({
|
|
||||||
id: plan.id,
|
|
||||||
name: plan.name,
|
|
||||||
startDate: plan.start_date, // Convert here
|
|
||||||
endDate: plan.end_date, // Convert here
|
|
||||||
templateId: plan.template_id,
|
|
||||||
status: plan.status,
|
|
||||||
createdBy: plan.created_by,
|
|
||||||
createdAt: plan.created_at,
|
|
||||||
shifts: plan.shifts || []
|
|
||||||
}));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async getShiftPlan(id: string): Promise<ShiftPlan> {
|
async getShiftPlan(id: string): Promise<ShiftPlan> {
|
||||||
@@ -53,20 +40,7 @@ export const shiftPlanService = {
|
|||||||
throw new Error('Schichtplan nicht gefunden');
|
throw new Error('Schichtplan nicht gefunden');
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
return await response.json();
|
||||||
|
|
||||||
// Convert snake_case to camelCase
|
|
||||||
return data.map((plan: any) => ({
|
|
||||||
id: plan.id,
|
|
||||||
name: plan.name,
|
|
||||||
startDate: plan.start_date,
|
|
||||||
endDate: plan.end_date,
|
|
||||||
templateId: plan.template_id,
|
|
||||||
status: plan.status,
|
|
||||||
createdBy: plan.created_by,
|
|
||||||
createdAt: plan.created_at,
|
|
||||||
shifts: plan.shifts || []
|
|
||||||
}));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async createShiftPlan(plan: CreateShiftPlanRequest): Promise<ShiftPlan> {
|
async createShiftPlan(plan: CreateShiftPlanRequest): Promise<ShiftPlan> {
|
||||||
@@ -127,60 +101,5 @@ export const shiftPlanService = {
|
|||||||
}
|
}
|
||||||
throw new Error('Fehler beim Löschen des Schichtplans');
|
throw new Error('Fehler beim Löschen des Schichtplans');
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
async updateShiftPlanShift(planId: string, shift: ShiftSlot): Promise<void> {
|
|
||||||
const response = await fetch(`${API_BASE}/${planId}/shifts/${shift.id}`, {
|
|
||||||
method: 'PUT',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
...authService.getAuthHeaders()
|
|
||||||
},
|
|
||||||
body: JSON.stringify(shift)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
if (response.status === 401) {
|
|
||||||
authService.logout();
|
|
||||||
throw new Error('Nicht authorisiert - bitte erneut anmelden');
|
|
||||||
}
|
|
||||||
throw new Error('Fehler beim Aktualisieren der Schicht');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async addShiftPlanShift(planId: string, shift: Omit<ShiftSlot, 'id' | 'shiftPlanId' | 'assignedEmployees'>): Promise<void> {
|
|
||||||
const response = await fetch(`${API_BASE}/${planId}/shifts`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
...authService.getAuthHeaders()
|
|
||||||
},
|
|
||||||
body: JSON.stringify(shift)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
if (response.status === 401) {
|
|
||||||
authService.logout();
|
|
||||||
throw new Error('Nicht authorisiert - bitte erneut anmelden');
|
|
||||||
}
|
|
||||||
throw new Error('Fehler beim Hinzufügen der Schicht');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async deleteShiftPlanShift(planId: string, shiftId: string): Promise<void> {
|
|
||||||
const response = await fetch(`${API_BASE}/${planId}/shifts/${shiftId}`, {
|
|
||||||
method: 'DELETE',
|
|
||||||
headers: {
|
|
||||||
...authService.getAuthHeaders()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
if (response.status === 401) {
|
|
||||||
authService.logout();
|
|
||||||
throw new Error('Nicht authorisiert - bitte erneut anmelden');
|
|
||||||
}
|
|
||||||
throw new Error('Fehler beim Löschen der Schicht');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// frontend/src/services/shiftTemplateService.ts
|
// frontend/src/services/shiftTemplateService.ts
|
||||||
import { ShiftPlan } from '../../../backend/src/models/shiftTemplate.js';
|
import { ShiftPlan } from '../../../backend/src/models/shiftPlan.js';
|
||||||
import { authService } from './authService';
|
import { authService } from './authService';
|
||||||
|
|
||||||
const API_BASE = 'http://localhost:3002/api/shift-templates';
|
const API_BASE = 'http://localhost:3002/api/shift-templates';
|
||||||
@@ -22,15 +22,10 @@ export const shiftTemplateService = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const templates = await response.json();
|
const templates = await response.json();
|
||||||
// Sortiere die Vorlagen so, dass die Standard-Vorlage immer zuerst kommt
|
return templates;
|
||||||
return templates.sort((a: TemplateShift, b: TemplateShift) => {
|
|
||||||
if (a.isDefault && !b.isDefault) return -1;
|
|
||||||
if (!a.isDefault && b.isDefault) return 1;
|
|
||||||
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async getTemplate(id: string): Promise<TemplateShift> {
|
async getTemplate(id: string): Promise<ShiftPlan> {
|
||||||
const response = await fetch(`${API_BASE}/${id}`, {
|
const response = await fetch(`${API_BASE}/${id}`, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -49,18 +44,7 @@ export const shiftTemplateService = {
|
|||||||
return response.json();
|
return response.json();
|
||||||
},
|
},
|
||||||
|
|
||||||
async createTemplate(template: Omit<TemplateShift, 'id' | 'createdAt' | 'createdBy'>): Promise<TemplateShift> {
|
async createTemplate(template: Omit<ShiftPlan, 'id' | 'createdAt' | 'createdBy'>): Promise<ShiftPlan> {
|
||||||
// Wenn diese Vorlage als Standard markiert ist,
|
|
||||||
// fragen wir den Benutzer, ob er wirklich die Standard-Vorlage ändern möchte
|
|
||||||
if (template.isDefault) {
|
|
||||||
const confirm = window.confirm(
|
|
||||||
'Diese Vorlage wird als neue Standard-Vorlage festgelegt. Die bisherige Standard-Vorlage wird dadurch zu einer normalen Vorlage. Möchten Sie fortfahren?'
|
|
||||||
);
|
|
||||||
if (!confirm) {
|
|
||||||
throw new Error('Operation abgebrochen');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await fetch(API_BASE, {
|
const response = await fetch(API_BASE, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@@ -81,7 +65,7 @@ export const shiftTemplateService = {
|
|||||||
return response.json();
|
return response.json();
|
||||||
},
|
},
|
||||||
|
|
||||||
async updateTemplate(id: string, template: Partial<TemplateShift>): Promise<TemplateShift> {
|
async updateTemplate(id: string, template: Partial<ShiftPlan>): Promise<ShiftPlan> {
|
||||||
const response = await fetch(`${API_BASE}/${id}`, {
|
const response = await fetch(`${API_BASE}/${id}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
Reference in New Issue
Block a user