mirror of
https://github.com/donpat1to/Schichtenplaner.git
synced 2025-12-01 06:55:45 +01:00
updated employee and shift structure
This commit is contained in:
128
backend/src/models/helpers/employeeHelpers.ts
Normal file
128
backend/src/models/helpers/employeeHelpers.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
// backend/src/models/helpers/employeeHelpers.ts
|
||||
import { Employee, CreateEmployeeRequest, EmployeeAvailability, ManagerAvailability } from '../Employee.js';
|
||||
|
||||
// Validation helpers
|
||||
export function validateEmployee(employee: CreateEmployeeRequest): string[] {
|
||||
const errors: string[] = [];
|
||||
|
||||
if (!employee.email || !employee.email.includes('@')) {
|
||||
errors.push('Valid email is required');
|
||||
}
|
||||
|
||||
if (!employee.password || employee.password.length < 6) {
|
||||
errors.push('Password must be at least 6 characters long');
|
||||
}
|
||||
|
||||
if (!employee.name || employee.name.trim().length < 2) {
|
||||
errors.push('Name is required and must be at least 2 characters long');
|
||||
}
|
||||
|
||||
if (!employee.contractType) {
|
||||
errors.push('Contract type is required');
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
export function validateAvailability(availability: Omit<EmployeeAvailability, 'id' | 'employeeId'>): string[] {
|
||||
const errors: string[] = [];
|
||||
|
||||
if (availability.dayOfWeek < 1 || availability.dayOfWeek > 7) {
|
||||
errors.push('Day of week must be between 1 and 7');
|
||||
}
|
||||
|
||||
if (![1, 2, 3].includes(availability.preferenceLevel)) {
|
||||
errors.push('Preference level must be 1, 2, or 3');
|
||||
}
|
||||
|
||||
if (!availability.timeSlotId) {
|
||||
errors.push('Time slot ID is required');
|
||||
}
|
||||
|
||||
if (!availability.planId) {
|
||||
errors.push('Plan ID is required');
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
// Employee type guards
|
||||
export function isManager(employee: Employee): boolean {
|
||||
return employee.employeeType === 'manager';
|
||||
}
|
||||
|
||||
export function isTrainee(employee: Employee): boolean {
|
||||
return employee.employeeType === 'trainee';
|
||||
}
|
||||
|
||||
export function isExperienced(employee: Employee): boolean {
|
||||
return employee.employeeType === 'experienced';
|
||||
}
|
||||
|
||||
export function isAdmin(employee: Employee): boolean {
|
||||
return employee.role === 'admin';
|
||||
}
|
||||
|
||||
// Business logic helpers
|
||||
export function canEmployeeWorkAlone(employee: Employee): boolean {
|
||||
return employee.canWorkAlone && employee.employeeType === 'experienced';
|
||||
}
|
||||
|
||||
export function getEmployeeWorkHours(employee: Employee): number {
|
||||
// Manager: no contract limit, others: small=1, large=2 shifts per week
|
||||
return isManager(employee) ? 999 : (employee.contractType === 'small' ? 1 : 2);
|
||||
}
|
||||
|
||||
export function requiresAvailabilityPreference(employee: Employee): boolean {
|
||||
// Only non-managers use the preference system
|
||||
return !isManager(employee);
|
||||
}
|
||||
|
||||
export function canSetOwnAvailability(employee: Employee): boolean {
|
||||
// Manager can set their own specific shift assignments
|
||||
return isManager(employee);
|
||||
}
|
||||
|
||||
// Manager availability helpers
|
||||
export function isManagerAvailable(
|
||||
managerAssignments: ManagerAvailability[],
|
||||
dayOfWeek: number,
|
||||
timeSlotId: string
|
||||
): boolean {
|
||||
const assignment = managerAssignments.find(assignment =>
|
||||
assignment.dayOfWeek === dayOfWeek &&
|
||||
assignment.timeSlotId === timeSlotId
|
||||
);
|
||||
|
||||
return assignment ? assignment.isAvailable : false;
|
||||
}
|
||||
|
||||
export function getManagerAvailableShifts(managerAssignments: ManagerAvailability[]): ManagerAvailability[] {
|
||||
return managerAssignments.filter(assignment => assignment.isAvailable);
|
||||
}
|
||||
|
||||
export function updateManagerAvailability(
|
||||
assignments: ManagerAvailability[],
|
||||
dayOfWeek: number,
|
||||
timeSlotId: string,
|
||||
isAvailable: boolean
|
||||
): ManagerAvailability[] {
|
||||
return assignments.map(assignment =>
|
||||
assignment.dayOfWeek === dayOfWeek && assignment.timeSlotId === timeSlotId
|
||||
? { ...assignment, isAvailable }
|
||||
: assignment
|
||||
);
|
||||
}
|
||||
|
||||
export function validateManagerMinimumAvailability(managerAssignments: ManagerAvailability[]): boolean {
|
||||
const requiredShifts = [
|
||||
{ dayOfWeek: 1, timeSlotId: 'morning' },
|
||||
{ dayOfWeek: 1, timeSlotId: 'afternoon' },
|
||||
{ dayOfWeek: 2, timeSlotId: 'morning' },
|
||||
{ dayOfWeek: 2, timeSlotId: 'afternoon' }
|
||||
];
|
||||
|
||||
return requiredShifts.every(required =>
|
||||
isManagerAvailable(managerAssignments, required.dayOfWeek, required.timeSlotId)
|
||||
);
|
||||
}
|
||||
3
backend/src/models/helpers/index.ts
Normal file
3
backend/src/models/helpers/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
// backend/src/models/helpers/index.ts
|
||||
export * from './employeeHelpers.js';
|
||||
export * from './shiftPlanHelpers.js';
|
||||
118
backend/src/models/helpers/shiftPlanHelpers.ts
Normal file
118
backend/src/models/helpers/shiftPlanHelpers.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
// backend/src/models/helpers/shiftPlanHelpers.ts
|
||||
import { ShiftPlan, Shift, ScheduledShift, TimeSlot } from '../ShiftPlan.js';
|
||||
|
||||
// Validation helpers
|
||||
export function validateRequiredEmployees(shift: Shift | ScheduledShift): string[] {
|
||||
const errors: string[] = [];
|
||||
|
||||
if (shift.requiredEmployees < 1) {
|
||||
errors.push('Required employees must be at least 1');
|
||||
}
|
||||
|
||||
if (shift.requiredEmployees > 10) {
|
||||
errors.push('Required employees cannot exceed 10');
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
export function isTemplate(plan: ShiftPlan): boolean {
|
||||
return plan.isTemplate || plan.status === 'template';
|
||||
}
|
||||
|
||||
export function hasDateRange(plan: ShiftPlan): boolean {
|
||||
return !isTemplate(plan) && !!plan.startDate && !!plan.endDate;
|
||||
}
|
||||
|
||||
export function validatePlanDates(plan: ShiftPlan): string[] {
|
||||
const errors: string[] = [];
|
||||
|
||||
if (!isTemplate(plan)) {
|
||||
if (!plan.startDate) errors.push('Start date is required for non-template plans');
|
||||
if (!plan.endDate) errors.push('End date is required for non-template plans');
|
||||
if (plan.startDate && plan.endDate && plan.startDate > plan.endDate) {
|
||||
errors.push('Start date must be before end date');
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
export function validateTimeSlot(timeSlot: { startTime: string; endTime: string }): string[] {
|
||||
const errors: string[] = [];
|
||||
|
||||
if (!timeSlot.startTime || !timeSlot.endTime) {
|
||||
errors.push('Start time and end time are required');
|
||||
return errors;
|
||||
}
|
||||
|
||||
const start = new Date(`2000-01-01T${timeSlot.startTime}`);
|
||||
const end = new Date(`2000-01-01T${timeSlot.endTime}`);
|
||||
|
||||
if (start >= end) {
|
||||
errors.push('Start time must be before end time');
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
// Type guards
|
||||
export function isScheduledShift(shift: Shift | ScheduledShift): shift is ScheduledShift {
|
||||
return 'date' in shift;
|
||||
}
|
||||
|
||||
export function isTemplateShift(shift: Shift | ScheduledShift): shift is Shift {
|
||||
return 'dayOfWeek' in shift && !('date' in shift);
|
||||
}
|
||||
|
||||
// Business logic helpers
|
||||
export function getShiftsForDay(plan: ShiftPlan, dayOfWeek: number): Shift[] {
|
||||
return plan.shifts.filter(shift => shift.dayOfWeek === dayOfWeek);
|
||||
}
|
||||
|
||||
export function getTimeSlotById(plan: ShiftPlan, timeSlotId: string): TimeSlot | undefined {
|
||||
return plan.timeSlots.find(slot => slot.id === timeSlotId);
|
||||
}
|
||||
|
||||
export function calculateTotalRequiredEmployees(plan: ShiftPlan): number {
|
||||
return plan.shifts.reduce((total, shift) => total + shift.requiredEmployees, 0);
|
||||
}
|
||||
|
||||
export function getScheduledShiftByDateAndTime(
|
||||
plan: ShiftPlan,
|
||||
date: string,
|
||||
timeSlotId: string
|
||||
): ScheduledShift | undefined {
|
||||
return plan.scheduledShifts?.find(shift =>
|
||||
shift.date === date && shift.timeSlotId === timeSlotId
|
||||
);
|
||||
}
|
||||
|
||||
export function canPublishPlan(plan: ShiftPlan): { canPublish: boolean; errors: string[] } {
|
||||
const errors: string[] = [];
|
||||
|
||||
if (!hasDateRange(plan)) {
|
||||
errors.push('Plan must have a date range to be published');
|
||||
}
|
||||
|
||||
if (plan.shifts.length === 0) {
|
||||
errors.push('Plan must have at least one shift');
|
||||
}
|
||||
|
||||
if (plan.timeSlots.length === 0) {
|
||||
errors.push('Plan must have at least one time slot');
|
||||
}
|
||||
|
||||
// Validate all shifts
|
||||
plan.shifts.forEach((shift, index) => {
|
||||
const shiftErrors = validateRequiredEmployees(shift);
|
||||
if (shiftErrors.length > 0) {
|
||||
errors.push(`Shift ${index + 1}: ${shiftErrors.join(', ')}`);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
canPublish: errors.length === 0,
|
||||
errors
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user