mirror of
https://github.com/donpat1to/Schichtenplaner.git
synced 2025-11-30 22:45:46 +01:00
fixed settings
This commit is contained in:
@@ -7,9 +7,20 @@ import { AuthRequest } from '../middleware/auth.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, '');
|
||||
// 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`;
|
||||
}
|
||||
|
||||
|
||||
@@ -104,11 +104,14 @@ CREATE TABLE IF NOT EXISTS employee_availability (
|
||||
UNIQUE(employee_id, plan_id, shift_id)
|
||||
);
|
||||
|
||||
-- Performance indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_employees_role_active ON employees(role, is_active);
|
||||
-- Performance indexes (UPDATED - removed role index from employees)
|
||||
CREATE INDEX IF NOT EXISTS idx_employees_email_active ON employees(email, is_active);
|
||||
CREATE INDEX IF NOT EXISTS idx_employees_type_active ON employees(employee_type, is_active);
|
||||
|
||||
-- Index for employee_roles table (NEW)
|
||||
CREATE INDEX IF NOT EXISTS idx_employee_roles_employee ON employee_roles(employee_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_employee_roles_role ON employee_roles(role);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_shift_plans_status_date ON shift_plans(status, start_date, end_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_shift_plans_created_by ON shift_plans(created_by);
|
||||
CREATE INDEX IF NOT EXISTS idx_shift_plans_template ON shift_plans(is_template, status);
|
||||
|
||||
@@ -14,7 +14,7 @@ export const EMPLOYEE_DEFAULTS = {
|
||||
export const MANAGER_DEFAULTS = {
|
||||
role: 'admin' as const,
|
||||
employeeType: 'manager' as const,
|
||||
contractType: 'large' as const, // Not really used but required by DB
|
||||
contractType: 'large' as const,
|
||||
canWorkAlone: true,
|
||||
isActive: true
|
||||
};
|
||||
@@ -64,26 +64,25 @@ export const AVAILABILITY_PREFERENCES = {
|
||||
} as const;
|
||||
|
||||
// Default availability for new employees (all shifts unavailable as level 3)
|
||||
export function createDefaultAvailabilities(employeeId: string, planId: string, timeSlotIds: string[]): Omit<EmployeeAvailability, 'id'>[] {
|
||||
// UPDATED: Now uses shiftId instead of timeSlotId + dayOfWeek
|
||||
export function createDefaultAvailabilities(employeeId: string, planId: string, shiftIds: string[]): Omit<EmployeeAvailability, 'id'>[] {
|
||||
const availabilities: Omit<EmployeeAvailability, 'id'>[] = [];
|
||||
|
||||
// Monday to Friday (1-5)
|
||||
for (let day = 1; day <= 5; day++) {
|
||||
for (const timeSlotId of timeSlotIds) {
|
||||
availabilities.push({
|
||||
employeeId,
|
||||
planId,
|
||||
dayOfWeek: day,
|
||||
timeSlotId,
|
||||
preferenceLevel: 3 // Default to "unavailable" - employees must explicitly set availability
|
||||
});
|
||||
}
|
||||
// Create one availability entry per shift
|
||||
for (const shiftId of shiftIds) {
|
||||
availabilities.push({
|
||||
employeeId,
|
||||
planId,
|
||||
shiftId,
|
||||
preferenceLevel: 3 // Default to "unavailable" - employees must explicitly set availability
|
||||
});
|
||||
}
|
||||
|
||||
return availabilities;
|
||||
}
|
||||
|
||||
// Create complete manager availability for all days (default: only Mon-Tue available)
|
||||
// NOTE: This function might need revision based on new schema requirements
|
||||
export function createManagerDefaultSchedule(managerId: string, planId: string, timeSlotIds: string[]): Omit<ManagerAvailability, 'id'>[] {
|
||||
const assignments: Omit<ManagerAvailability, 'id'>[] = [];
|
||||
|
||||
|
||||
@@ -1,25 +1,51 @@
|
||||
// backend/src/models/helpers/employeeHelpers.ts
|
||||
import { Employee, CreateEmployeeRequest, EmployeeAvailability } from '../Employee.js';
|
||||
|
||||
// Simplified validation - use schema validation instead
|
||||
// Email generation function (same as in controllers)
|
||||
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`;
|
||||
}
|
||||
|
||||
// UPDATED: Validation for new employee model
|
||||
export function validateEmployeeData(employee: CreateEmployeeRequest): string[] {
|
||||
const errors: string[] = [];
|
||||
|
||||
if (!employee.email?.includes('@')) {
|
||||
errors.push('Valid email is required');
|
||||
}
|
||||
|
||||
// Email is now auto-generated, so no email validation needed
|
||||
|
||||
if (employee.password?.length < 6) {
|
||||
errors.push('Password must be at least 6 characters long');
|
||||
}
|
||||
|
||||
if (!employee.name?.trim() || employee.name.trim().length < 2) {
|
||||
errors.push('Name is required and must be at least 2 characters long');
|
||||
if (!employee.firstname?.trim() || employee.firstname.trim().length < 2) {
|
||||
errors.push('First name is required and must be at least 2 characters long');
|
||||
}
|
||||
|
||||
if (!employee.lastname?.trim() || employee.lastname.trim().length < 2) {
|
||||
errors.push('Last name is required and must be at least 2 characters long');
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
// Generate email for employee (new helper function)
|
||||
export function generateEmployeeEmail(firstname: string, lastname: string): string {
|
||||
return generateEmail(firstname, lastname);
|
||||
}
|
||||
|
||||
// Simplified business logic helpers
|
||||
export const isManager = (employee: Employee): boolean =>
|
||||
employee.employeeType === 'manager';
|
||||
@@ -38,3 +64,26 @@ export const canEmployeeWorkAlone = (employee: Employee): boolean =>
|
||||
|
||||
export const getEmployeeWorkHours = (employee: Employee): number =>
|
||||
isManager(employee) ? 999 : (employee.contractType === 'small' ? 1 : 2);
|
||||
|
||||
// New helper for full name display
|
||||
export const getFullName = (employee: { firstname: string; lastname: string }): string =>
|
||||
`${employee.firstname} ${employee.lastname}`;
|
||||
|
||||
// Helper for availability validation
|
||||
export function validateAvailabilityData(availability: Omit<EmployeeAvailability, 'id' | 'employeeId'>): string[] {
|
||||
const errors: string[] = [];
|
||||
|
||||
if (!availability.planId) {
|
||||
errors.push('Plan ID is required');
|
||||
}
|
||||
|
||||
if (!availability.shiftId) {
|
||||
errors.push('Shift ID is required');
|
||||
}
|
||||
|
||||
if (![1, 2, 3].includes(availability.preferenceLevel)) {
|
||||
errors.push('Preference level must be 1, 2, or 3');
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
@@ -78,7 +78,8 @@ export function calculateTotalRequiredEmployees(plan: ShiftPlan): number {
|
||||
return plan.shifts.reduce((total, shift) => total + shift.requiredEmployees, 0);
|
||||
}
|
||||
|
||||
/*export function getScheduledShiftByDateAndTime(
|
||||
// UPDATED: Get scheduled shift by date and time slot
|
||||
export function getScheduledShiftByDateAndTime(
|
||||
plan: ShiftPlan,
|
||||
date: string,
|
||||
timeSlotId: string
|
||||
@@ -86,7 +87,7 @@ export function calculateTotalRequiredEmployees(plan: ShiftPlan): number {
|
||||
return plan.scheduledShifts?.find(shift =>
|
||||
shift.date === date && shift.timeSlotId === timeSlotId
|
||||
);
|
||||
}*/
|
||||
}
|
||||
|
||||
export function canPublishPlan(plan: ShiftPlan): { canPublish: boolean; errors: string[] } {
|
||||
const errors: string[] = [];
|
||||
@@ -115,4 +116,14 @@ export function canPublishPlan(plan: ShiftPlan): { canPublish: boolean; errors:
|
||||
canPublish: errors.length === 0,
|
||||
errors
|
||||
};
|
||||
}
|
||||
|
||||
// NEW: Helper for shift generation
|
||||
export function generateShiftId(): string {
|
||||
return `shift_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
}
|
||||
|
||||
// NEW: Helper for time slot generation
|
||||
export function generateTimeSlotId(): string {
|
||||
return `timeslot_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
}
|
||||
Reference in New Issue
Block a user