changed email creation

This commit is contained in:
2025-10-20 01:50:12 +02:00
parent 9393de5239
commit e80bb81b5d
9 changed files with 308 additions and 98 deletions

View File

@@ -32,13 +32,19 @@ export interface JWTPayload {
} }
export interface RegisterRequest { export interface RegisterRequest {
email: string; // REMOVED: email - will be auto-generated
password: string; password: string;
firstname: String; firstname: string;
lastname: String; lastname: string;
role?: string; role?: string;
} }
function generateEmail(firstname: string, lastname: string): string {
const cleanFirstname = firstname.toLowerCase().replace(/[^a-z0-9]/g, '');
const cleanLastname = lastname.toLowerCase().replace(/[^a-z0-9]/g, '');
return `${cleanFirstname}.${cleanLastname}@sp.de`;
}
export const login = async (req: Request, res: Response) => { export const login = async (req: Request, res: Response) => {
try { try {
const { email, password } = req.body as LoginRequest; const { email, password } = req.body as LoginRequest;
@@ -162,16 +168,19 @@ export const validateToken = async (req: Request, res: Response) => {
export const register = async (req: Request, res: Response) => { export const register = async (req: Request, res: Response) => {
try { try {
const { email, password, firstname, lastname, role = 'user' } = req.body as RegisterRequest; const { password, firstname, lastname, role = 'user' } = req.body as RegisterRequest;
// Validate required fields // Validate required fields - REMOVED email
if (!email || !password || !firstname || !lastname) { if (!password || !firstname || !lastname) {
return res.status(400).json({ return res.status(400).json({
error: 'E-Mail, Passwort und Name sind erforderlich' error: 'Password, firstname und lastname sind erforderlich'
}); });
} }
// Check if email already exists // Generate email automatically
const email = generateEmail(firstname, lastname);
// Check if generated email already exists
const existingUser = await db.get<Employee>( const existingUser = await db.get<Employee>(
'SELECT id FROM employees WHERE email = ?', 'SELECT id FROM employees WHERE email = ?',
[email] [email]
@@ -179,19 +188,19 @@ export const register = async (req: Request, res: Response) => {
if (existingUser) { if (existingUser) {
return res.status(400).json({ return res.status(400).json({
error: 'Ein Benutzer mit dieser E-Mail existiert bereits' error: `Ein Benutzer mit der E-Mail ${email} existiert bereits`
}); });
} }
// Hash password // Hash password and create user
const hashedPassword = await bcrypt.hash(password, 10); const hashedPassword = await bcrypt.hash(password, 10);
// Insert user // Insert user with generated email
const result = await db.run( const result = await db.run(
`INSERT INTO employees (id, email, password, firstname, lastname, role, employee_type, contract_type, can_work_alone, is_active) `INSERT INTO employees (id, email, password, firstname, lastname, role, employee_type, contract_type, can_work_alone, is_active)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[uuidv4(), email, hashedPassword, firstname, lastname, role, 'experienced', 'small', false, 1] [uuidv4(), email, hashedPassword, firstname, lastname, role, 'experienced', 'small', false, 1]
); );
if (!result.lastID) { if (!result.lastID) {
throw new Error('Benutzer konnte nicht erstellt werden'); throw new Error('Benutzer konnte nicht erstellt werden');
@@ -199,7 +208,7 @@ export const register = async (req: Request, res: Response) => {
// Get created user // Get created user
const newUser = await db.get<Employee>( const newUser = await db.get<Employee>(
'SELECT id, email, name, role FROM employees WHERE id = ?', 'SELECT id, email, firstname, lastname, role FROM employees WHERE id = ?',
[result.lastID] [result.lastID]
); );

View File

@@ -6,6 +6,13 @@ import { db } from '../services/databaseService.js';
import { AuthRequest } from '../middleware/auth.js'; import { AuthRequest } from '../middleware/auth.js';
import { CreateEmployeeRequest } from '../models/Employee.js'; import { CreateEmployeeRequest } from '../models/Employee.js';
function generateEmail(firstname: string, lastname: string): string {
// Remove special characters and convert to lowercase
const cleanFirstname = firstname.toLowerCase().replace(/[^a-z0-9]/g, '');
const cleanLastname = lastname.toLowerCase().replace(/[^a-z0-9]/g, '');
return `${cleanFirstname}.${cleanLastname}@sp.de`;
}
export const getEmployees = async (req: AuthRequest, res: Response): Promise<void> => { export const getEmployees = async (req: AuthRequest, res: Response): Promise<void> => {
try { try {
console.log('🔍 Fetching employees - User:', req.user); console.log('🔍 Fetching employees - User:', req.user);
@@ -76,41 +83,40 @@ export const createEmployee = async (req: AuthRequest, res: Response): Promise<v
}); });
const { const {
email,
password, password,
firstname, firstname,
lastname, lastname,
role, role,
employeeType, employeeType,
contractType, contractType,
canWorkAlone canWorkAlone
} = req.body as CreateEmployeeRequest; } = req.body as CreateEmployeeRequest;
// Validierung // Validation - REMOVED email check
if (!email || !password || !firstname || !lastname || !role || !employeeType || !contractType) { if (!password || !firstname || !lastname || !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, employeeType und contractType sind erforderlich' error: 'Password, firstname, lastname, role, employeeType und contractType sind erforderlich'
}); });
return; return;
} }
if (password.length < 6) { // Generate email automatically
console.log('❌ Validation failed: Password too short'); const email = generateEmail(firstname, lastname);
res.status(400).json({ error: 'Password must be at least 6 characters long' }); console.log('📧 Generated email:', email);
return;
}
// Check if email already exists // Check if generated email already exists
const existingActiveUser = await db.get<any>('SELECT id FROM employees WHERE email = ? AND is_active = 1', [email]); const existingUser = await db.get<any>('SELECT id FROM employees WHERE email = ? AND is_active = 1', [email]);
if (existingActiveUser) { if (existingUser) {
console.log('❌ Email exists for active user:', existingActiveUser); console.log('❌ Generated email already exists:', email);
res.status(409).json({ error: 'Email already exists' }); res.status(409).json({
error: `Employee with email ${email} already exists. Please use different firstname/lastname.`
});
return; return;
} }
// Hash password // Hash password and create employee
const hashedPassword = await bcrypt.hash(password, 10); const hashedPassword = await bcrypt.hash(password, 10);
const employeeId = uuidv4(); const employeeId = uuidv4();
@@ -123,8 +129,8 @@ export const createEmployee = async (req: AuthRequest, res: Response): Promise<v
employeeId, employeeId,
email, email,
hashedPassword, hashedPassword,
firstname, // Changed from name firstname,
lastname, // Added lastname,
role, role,
employeeType, employeeType,
contractType, contractType,
@@ -133,10 +139,10 @@ export const createEmployee = async (req: AuthRequest, res: Response): Promise<v
] ]
); );
// Return created employee // Return created employee with generated email
const newEmployee = await db.get<any>(` const newEmployee = await db.get<any>(`
SELECT SELECT
id, email, name, role, is_active as isActive, id, email, firstname, lastname, role, is_active as isActive,
employee_type as employeeType, employee_type as employeeType,
contract_type as contractType, contract_type as contractType,
can_work_alone as canWorkAlone, can_work_alone as canWorkAlone,
@@ -157,35 +163,58 @@ export const updateEmployee = async (req: AuthRequest, res: Response): Promise<v
try { try {
const { id } = req.params; const { id } = req.params;
const { firstname, lastname, role, isActive, employeeType, contractType, canWorkAlone } = req.body; const { firstname, lastname, role, isActive, employeeType, contractType, canWorkAlone } = req.body;
console.log('📝 Update Employee Request:', { id, name, role, isActive, employeeType, contractType, canWorkAlone });
// Check if employee exists console.log('📝 Update Employee Request:', { id, firstname, lastname, role, isActive, employeeType, contractType, canWorkAlone });
const existingEmployee = await db.get('SELECT * FROM employees WHERE id = ?', [id]);
// Check if employee exists and get current data
const existingEmployee = await db.get<any>('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;
} }
// Update employee // Generate new email if firstname or lastname changed
let email = existingEmployee.email;
if (firstname || lastname) {
const newFirstname = firstname || existingEmployee.firstname;
const newLastname = lastname || existingEmployee.lastname;
email = generateEmail(newFirstname, newLastname);
// Check if new email already exists (for another employee)
const emailExists = await db.get<any>(
'SELECT id FROM employees WHERE email = ? AND id != ? AND is_active = 1',
[email, id]
);
if (emailExists) {
res.status(409).json({
error: `Cannot update name - email ${email} already exists for another employee`
});
return;
}
}
// Update employee with potentially new email
await db.run( await db.run(
`UPDATE employees `UPDATE employees
SET firstname = COALESCE(?, firstname), SET firstname = COALESCE(?, firstname),
lastname = COALESCE(?, lastname), lastname = COALESCE(?, lastname),
role = COALESCE(?, role), email = ?,
is_active = COALESCE(?, is_active), role = COALESCE(?, role),
employee_type = COALESCE(?, employee_type), is_active = COALESCE(?, is_active),
contract_type = COALESCE(?, contract_type), employee_type = COALESCE(?, employee_type),
can_work_alone = COALESCE(?, can_work_alone) contract_type = COALESCE(?, contract_type),
WHERE id = ?`, can_work_alone = COALESCE(?, can_work_alone)
[firstname, lastname, role, isActive, employeeType, contractType, canWorkAlone, id] WHERE id = ?`,
[firstname, lastname, email, role, isActive, employeeType, contractType, canWorkAlone, id]
); );
console.log('✅ Employee updated successfully'); console.log('✅ Employee updated successfully with email:', email);
// Return updated employee // Return updated employee
const updatedEmployee = await db.get<any>(` const updatedEmployee = await db.get<any>(`
SELECT SELECT
id, email, name, role, is_active as isActive, id, email, firstname, lastname, role, is_active as isActive,
employee_type as employeeType, employee_type as employeeType,
contract_type as contractType, contract_type as contractType,
can_work_alone as canWorkAlone, can_work_alone as canWorkAlone,

View File

@@ -5,6 +5,25 @@ import { v4 as uuidv4 } from 'uuid';
import { randomUUID } from 'crypto'; import { randomUUID } from 'crypto';
import { db } from '../services/databaseService.js'; import { db } from '../services/databaseService.js';
// Add the same email generation function
function generateEmail(firstname: string, lastname: string): string {
// Convert German umlauts to their expanded forms
const convertUmlauts = (str: string): string => {
return str
.toLowerCase()
.replace(/ü/g, 'ue')
.replace(/ö/g, 'oe')
.replace(/ä/g, 'ae')
.replace(/ß/g, 'ss');
};
// Remove any remaining special characters and convert to lowercase
const cleanFirstname = convertUmlauts(firstname).replace(/[^a-z0-9]/g, '');
const cleanLastname = convertUmlauts(lastname).replace(/[^a-z0-9]/g, '');
return `${cleanFirstname}.${cleanLastname}@sp.de`;
}
export const checkSetupStatus = async (req: Request, res: Response): Promise<void> => { export const checkSetupStatus = async (req: Request, res: Response): Promise<void> => {
try { try {
const adminExists = await db.get<{ 'COUNT(*)': number }>( const adminExists = await db.get<{ 'COUNT(*)': number }>(
@@ -43,12 +62,11 @@ export const setupAdmin = async (req: Request, res: Response): Promise<void> =>
return; return;
} }
const { password, firstname, lastname } = req.body; const { password, firstname, lastname } = req.body; // Changed from name to firstname/lastname
const email = 'admin@instandhaltung.de';
console.log('👤 Creating admin with data:', { name, email }); console.log('👤 Creating admin with data:', { firstname, lastname });
// Validation // Validation - updated for firstname/lastname
if (!password || !firstname || !lastname) { if (!password || !firstname || !lastname) {
res.status(400).json({ error: 'Passwort, Vorname und Nachname sind erforderlich' }); res.status(400).json({ error: 'Passwort, Vorname und Nachname sind erforderlich' });
return; return;
@@ -60,6 +78,10 @@ export const setupAdmin = async (req: Request, res: Response): Promise<void> =>
return; return;
} }
// Generate email automatically using the same pattern
const email = generateEmail(firstname, lastname);
console.log('📧 Generated admin email:', email);
// Hash password // Hash password
const hashedPassword = await bcrypt.hash(password, 10); const hashedPassword = await bcrypt.hash(password, 10);
const adminId = randomUUID(); const adminId = randomUUID();
@@ -70,18 +92,14 @@ export const setupAdmin = async (req: Request, res: Response): Promise<void> =>
await db.run('BEGIN TRANSACTION'); await db.run('BEGIN TRANSACTION');
try { try {
// Create admin user // Create admin user with generated email
await db.run( await db.run(
`INSERT INTO employees (id, email, password, firstname, lastname, role, employee_type, contract_type, can_work_alone, is_active) `INSERT INTO employees (id, email, password, firstname, lastname, role, employee_type, contract_type, can_work_alone, is_active)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[adminId, email, hashedPassword, firstname, lastname, 'admin', 'manager', 'large', true, 1] [adminId, email, hashedPassword, firstname, lastname, 'admin', 'manager', 'large', true, 1]
); );
console.log('✅ Admin user created successfully'); console.log('✅ Admin user created successfully with email:', email);
// Initialize default templates WITHOUT starting a new transaction
//console.log('🔄 Initialisiere Standard-Vorlagen...');
//await initializeDefaultTemplates(adminId, false);
// Commit the entire setup transaction // Commit the entire setup transaction
await db.run('COMMIT'); await db.run('COMMIT');
@@ -91,7 +109,9 @@ export const setupAdmin = async (req: Request, res: Response): Promise<void> =>
res.status(201).json({ res.status(201).json({
success: true, success: true,
message: 'Admin erfolgreich erstellt', message: 'Admin erfolgreich erstellt',
email: email email: email,
firstname: firstname,
lastname: lastname
}); });
} catch (dbError) { } catch (dbError) {

View File

@@ -14,7 +14,6 @@ export interface Employee {
} }
export interface CreateEmployeeRequest { export interface CreateEmployeeRequest {
email: string;
password: string; password: string;
firstname: string; firstname: string;
lastname: string; lastname: string;

View File

@@ -3,15 +3,15 @@ export interface ShiftPlan {
id: string; id: string;
name: string; name: string;
description?: string; description?: string;
startDate?: string; // Optional for templates startDate?: string;
endDate?: string; // Optional for templates endDate?: string;
isTemplate: boolean; isTemplate: boolean;
status: 'draft' | 'published' | 'archived' | 'template'; status: 'draft' | 'published' | 'archived' | 'template';
createdBy: string; createdBy: string;
createdAt: string; createdAt: string;
timeSlots: TimeSlot[]; timeSlots: TimeSlot[];
shifts: Shift[]; shifts: Shift[];
scheduledShifts?: ScheduledShift[]; // Only for non-template plans with dates scheduledShifts?: ScheduledShift[];
} }
export interface TimeSlot { export interface TimeSlot {
@@ -85,7 +85,6 @@ export interface AssignEmployeeRequest {
scheduledShiftId: string; scheduledShiftId: string;
} }
export interface UpdateRequiredEmployeesRequest { export interface UpdateRequiredEmployeesRequest {
requiredEmployees: number; requiredEmployees: number;
} }

View File

@@ -2,15 +2,20 @@
import { Employee } from './Employee.js'; import { Employee } from './Employee.js';
import { ShiftPlan } from './ShiftPlan.js'; import { ShiftPlan } from './ShiftPlan.js';
// Add the missing type definitions // Updated Availability interface to match new schema
export interface Availability { export interface Availability {
id: string; id: string;
employeeId: string; employeeId: string;
planId: string; planId: string;
dayOfWeek: number; // 1=Monday, 7=Sunday shiftId: string; // Now references shift_id instead of time_slot_id + day_of_week
timeSlotId: string;
preferenceLevel: 1 | 2 | 3; // 1:preferred, 2:available, 3:unavailable preferenceLevel: 1 | 2 | 3; // 1:preferred, 2:available, 3:unavailable
notes?: string; notes?: string;
// Optional convenience fields (can be joined from shifts and time_slots tables)
dayOfWeek?: number;
timeSlotId?: string;
timeSlotName?: string;
startTime?: string;
endTime?: string;
} }
export interface Constraint { export interface Constraint {
@@ -120,4 +125,77 @@ export interface ShiftRequirement {
maxEmployees: number; maxEmployees: number;
assignedEmployees: string[]; assignedEmployees: string[];
isPriority: boolean; isPriority: boolean;
}
// New types for the updated schema
export interface EmployeeWithRoles extends Employee {
roles: string[];
}
export interface ShiftWithDetails {
id: string;
planId: string;
timeSlotId: string;
dayOfWeek: number;
requiredEmployees: number;
color?: string;
timeSlot: {
id: string;
name: string;
startTime: string;
endTime: string;
description?: string;
};
}
export interface AvailabilityWithDetails extends Availability {
employee?: {
id: string;
firstname: string;
lastname: string;
employeeType: 'manager' | 'trainee' | 'experienced';
canWorkAlone: boolean;
};
shift?: {
dayOfWeek: number;
timeSlotId: string;
timeSlot?: {
name: string;
startTime: string;
endTime: string;
};
};
}
// Types for scheduling algorithm input
export interface SchedulingInput {
planId: string;
startDate: string;
endDate: string;
constraints: Constraint[];
options?: SolverOptions;
}
// Types for scheduling results with enhanced information
export interface EnhancedAssignment extends Assignment {
employeeName: string;
shiftDetails: {
date: string;
dayOfWeek: number;
timeSlotName: string;
startTime: string;
endTime: string;
};
preferenceLevel: number;
}
export interface SchedulingStatistics {
totalShifts: number;
totalAssignments: number;
coverageRate: number;
preferenceSatisfaction: number;
constraintViolations: number;
hardConstraintViolations: number;
softConstraintViolations: number;
processingTime: number;
} }

View File

@@ -2,7 +2,8 @@
export interface Employee { export interface Employee {
id: string; id: string;
email: string; email: string;
name: string; firstname: string;
lastname: string;
role: 'admin' | 'maintenance' | 'user'; role: 'admin' | 'maintenance' | 'user';
employeeType: 'manager' | 'trainee' | 'experienced'; employeeType: 'manager' | 'trainee' | 'experienced';
contractType: 'small' | 'large'; contractType: 'small' | 'large';
@@ -13,9 +14,9 @@ export interface Employee {
} }
export interface CreateEmployeeRequest { export interface CreateEmployeeRequest {
email: string;
password: string; password: string;
name: string; firstname: string;
lastname: string;
role: 'admin' | 'maintenance' | 'user'; role: 'admin' | 'maintenance' | 'user';
employeeType: 'manager' | 'trainee' | 'experienced'; employeeType: 'manager' | 'trainee' | 'experienced';
contractType: 'small' | 'large'; contractType: 'small' | 'large';
@@ -23,7 +24,8 @@ export interface CreateEmployeeRequest {
} }
export interface UpdateEmployeeRequest { export interface UpdateEmployeeRequest {
name?: string; firstname?: string;
lastname?: string;
role?: 'admin' | 'maintenance' | 'user'; role?: 'admin' | 'maintenance' | 'user';
employeeType?: 'manager' | 'trainee' | 'experienced'; employeeType?: 'manager' | 'trainee' | 'experienced';
contractType?: 'small' | 'large'; contractType?: 'small' | 'large';
@@ -39,8 +41,7 @@ export interface EmployeeAvailability {
id: string; id: string;
employeeId: string; employeeId: string;
planId: string; planId: string;
dayOfWeek: number; // 1=Monday, 7=Sunday shiftId: string; // Now references shift_id instead of time_slot_id + day_of_week
timeSlotId: string;
preferenceLevel: 1 | 2 | 3; // 1:preferred, 2:available, 3:unavailable preferenceLevel: 1 | 2 | 3; // 1:preferred, 2:available, 3:unavailable
notes?: string; notes?: string;
} }
@@ -72,4 +73,14 @@ export interface ManagerSelfAssignmentRequest {
export interface EmployeeWithAvailabilities extends Employee { export interface EmployeeWithAvailabilities extends Employee {
availabilities: EmployeeAvailability[]; availabilities: EmployeeAvailability[];
}
// Additional types for the new roles system
export interface Role {
role: 'admin' | 'user' | 'maintenance';
}
export interface EmployeeRole {
employeeId: string;
role: 'admin' | 'user' | 'maintenance';
} }

View File

@@ -3,14 +3,15 @@ export interface ShiftPlan {
id: string; id: string;
name: string; name: string;
description?: string; description?: string;
startDate?: string; // Optional for templates startDate?: string;
endDate?: string; // Optional for templates endDate?: string;
isTemplate: boolean; isTemplate: boolean;
status: 'draft' | 'published' | 'archived' | 'template'; status: 'draft' | 'published' | 'archived' | 'template';
createdBy: string; createdBy: string;
createdAt: string; createdAt: string;
timeSlots: TimeSlot[]; timeSlots: TimeSlot[];
shifts: Shift[]; shifts: Shift[];
scheduledShifts?: ScheduledShift[];
} }
export interface TimeSlot { export interface TimeSlot {
@@ -49,16 +50,6 @@ export interface ShiftAssignment {
assignedBy: string; assignedBy: string;
} }
export interface EmployeeAvailability {
id: string;
employeeId: string;
planId: string;
dayOfWeek: number;
timeSlotId: string;
preferenceLevel: 1 | 2 | 3; // 1:preferred, 2:available, 3:unavailable
notes?: string;
}
// Request/Response DTOs // Request/Response DTOs
export interface CreateShiftPlanRequest { export interface CreateShiftPlanRequest {
name: string; name: string;
@@ -68,7 +59,6 @@ export interface CreateShiftPlanRequest {
isTemplate: boolean; isTemplate: boolean;
timeSlots: Omit<TimeSlot, 'id' | 'planId'>[]; timeSlots: Omit<TimeSlot, 'id' | 'planId'>[];
shifts: Omit<Shift, 'id' | 'planId'>[]; shifts: Omit<Shift, 'id' | 'planId'>[];
templateId?: string;
} }
export interface UpdateShiftPlanRequest { export interface UpdateShiftPlanRequest {
@@ -95,10 +85,6 @@ export interface AssignEmployeeRequest {
scheduledShiftId: string; scheduledShiftId: string;
} }
export interface UpdateAvailabilityRequest {
planId: string;
availabilities: Omit<EmployeeAvailability, 'id' | 'employeeId'>[];
}
export interface UpdateRequiredEmployeesRequest { export interface UpdateRequiredEmployeesRequest {
requiredEmployees: number; requiredEmployees: number;

View File

@@ -2,15 +2,20 @@
import { Employee } from './Employee.js'; import { Employee } from './Employee.js';
import { ShiftPlan } from './ShiftPlan.js'; import { ShiftPlan } from './ShiftPlan.js';
// Add the missing type definitions // Updated Availability interface to match new schema
export interface Availability { export interface Availability {
id: string; id: string;
employeeId: string; employeeId: string;
planId: string; planId: string;
dayOfWeek: number; // 1=Monday, 7=Sunday shiftId: string; // Now references shift_id instead of time_slot_id + day_of_week
timeSlotId: string;
preferenceLevel: 1 | 2 | 3; // 1:preferred, 2:available, 3:unavailable preferenceLevel: 1 | 2 | 3; // 1:preferred, 2:available, 3:unavailable
notes?: string; notes?: string;
// Optional convenience fields (can be joined from shifts and time_slots tables)
dayOfWeek?: number;
timeSlotId?: string;
timeSlotName?: string;
startTime?: string;
endTime?: string;
} }
export interface Constraint { export interface Constraint {
@@ -75,6 +80,7 @@ export interface Solution {
variablesCreated: number; variablesCreated: number;
optimal: boolean; optimal: boolean;
}; };
variables?: { [key: string]: number };
} }
// Additional helper types for the scheduling system // Additional helper types for the scheduling system
@@ -119,4 +125,77 @@ export interface ShiftRequirement {
maxEmployees: number; maxEmployees: number;
assignedEmployees: string[]; assignedEmployees: string[];
isPriority: boolean; isPriority: boolean;
}
// New types for the updated schema
export interface EmployeeWithRoles extends Employee {
roles: string[];
}
export interface ShiftWithDetails {
id: string;
planId: string;
timeSlotId: string;
dayOfWeek: number;
requiredEmployees: number;
color?: string;
timeSlot: {
id: string;
name: string;
startTime: string;
endTime: string;
description?: string;
};
}
export interface AvailabilityWithDetails extends Availability {
employee?: {
id: string;
firstname: string;
lastname: string;
employeeType: 'manager' | 'trainee' | 'experienced';
canWorkAlone: boolean;
};
shift?: {
dayOfWeek: number;
timeSlotId: string;
timeSlot?: {
name: string;
startTime: string;
endTime: string;
};
};
}
// Types for scheduling algorithm input
export interface SchedulingInput {
planId: string;
startDate: string;
endDate: string;
constraints: Constraint[];
options?: SolverOptions;
}
// Types for scheduling results with enhanced information
export interface EnhancedAssignment extends Assignment {
employeeName: string;
shiftDetails: {
date: string;
dayOfWeek: number;
timeSlotName: string;
startTime: string;
endTime: string;
};
preferenceLevel: number;
}
export interface SchedulingStatistics {
totalShifts: number;
totalAssignments: number;
coverageRate: number;
preferenceSatisfaction: number;
constraintViolations: number;
hardConstraintViolations: number;
softConstraintViolations: number;
processingTime: number;
} }