mirror of
https://github.com/donpat1to/Schichtenplaner.git
synced 2025-11-30 22:45:46 +01:00
changed email creation
This commit is contained in:
@@ -32,13 +32,19 @@ export interface JWTPayload {
|
||||
}
|
||||
|
||||
export interface RegisterRequest {
|
||||
email: string;
|
||||
// REMOVED: email - will be auto-generated
|
||||
password: string;
|
||||
firstname: String;
|
||||
lastname: String;
|
||||
firstname: string;
|
||||
lastname: 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) => {
|
||||
try {
|
||||
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) => {
|
||||
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
|
||||
if (!email || !password || !firstname || !lastname) {
|
||||
// Validate required fields - REMOVED email
|
||||
if (!password || !firstname || !lastname) {
|
||||
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>(
|
||||
'SELECT id FROM employees WHERE email = ?',
|
||||
[email]
|
||||
@@ -179,19 +188,19 @@ export const register = async (req: Request, res: Response) => {
|
||||
|
||||
if (existingUser) {
|
||||
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);
|
||||
|
||||
// Insert user
|
||||
const result = await db.run(
|
||||
`INSERT INTO employees (id, email, password, firstname, lastname, role, employee_type, contract_type, can_work_alone, is_active)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[uuidv4(), email, hashedPassword, firstname, lastname, role, 'experienced', 'small', false, 1]
|
||||
);
|
||||
// Insert user with generated email
|
||||
const result = await db.run(
|
||||
`INSERT INTO employees (id, email, password, firstname, lastname, role, employee_type, contract_type, can_work_alone, is_active)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[uuidv4(), email, hashedPassword, firstname, lastname, role, 'experienced', 'small', false, 1]
|
||||
);
|
||||
|
||||
if (!result.lastID) {
|
||||
throw new Error('Benutzer konnte nicht erstellt werden');
|
||||
@@ -199,7 +208,7 @@ export const register = async (req: Request, res: Response) => {
|
||||
|
||||
// Get created user
|
||||
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]
|
||||
);
|
||||
|
||||
|
||||
@@ -6,6 +6,13 @@ import { db } from '../services/databaseService.js';
|
||||
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, '');
|
||||
return `${cleanFirstname}.${cleanLastname}@sp.de`;
|
||||
}
|
||||
|
||||
export const getEmployees = async (req: AuthRequest, res: Response): Promise<void> => {
|
||||
try {
|
||||
console.log('🔍 Fetching employees - User:', req.user);
|
||||
@@ -76,41 +83,40 @@ export const createEmployee = async (req: AuthRequest, res: Response): Promise<v
|
||||
});
|
||||
|
||||
const {
|
||||
email,
|
||||
password,
|
||||
firstname,
|
||||
lastname,
|
||||
firstname,
|
||||
lastname,
|
||||
role,
|
||||
employeeType,
|
||||
contractType,
|
||||
canWorkAlone
|
||||
} = req.body as CreateEmployeeRequest;
|
||||
|
||||
// Validierung
|
||||
if (!email || !password || !firstname || !lastname || !role || !employeeType || !contractType) {
|
||||
// Validation - REMOVED email check
|
||||
if (!password || !firstname || !lastname || !role || !employeeType || !contractType) {
|
||||
console.log('❌ Validation failed: Missing required fields');
|
||||
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;
|
||||
}
|
||||
|
||||
if (password.length < 6) {
|
||||
console.log('❌ Validation failed: Password too short');
|
||||
res.status(400).json({ error: 'Password must be at least 6 characters long' });
|
||||
return;
|
||||
}
|
||||
// Generate email automatically
|
||||
const email = generateEmail(firstname, lastname);
|
||||
console.log('📧 Generated email:', email);
|
||||
|
||||
// Check if email already exists
|
||||
const existingActiveUser = await db.get<any>('SELECT id FROM employees WHERE email = ? AND is_active = 1', [email]);
|
||||
// Check if generated email already exists
|
||||
const existingUser = await db.get<any>('SELECT id FROM employees WHERE email = ? AND is_active = 1', [email]);
|
||||
|
||||
if (existingActiveUser) {
|
||||
console.log('❌ Email exists for active user:', existingActiveUser);
|
||||
res.status(409).json({ error: 'Email already exists' });
|
||||
if (existingUser) {
|
||||
console.log('❌ Generated email already exists:', email);
|
||||
res.status(409).json({
|
||||
error: `Employee with email ${email} already exists. Please use different firstname/lastname.`
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Hash password
|
||||
// Hash password and create employee
|
||||
const hashedPassword = await bcrypt.hash(password, 10);
|
||||
const employeeId = uuidv4();
|
||||
|
||||
@@ -123,8 +129,8 @@ export const createEmployee = async (req: AuthRequest, res: Response): Promise<v
|
||||
employeeId,
|
||||
email,
|
||||
hashedPassword,
|
||||
firstname, // Changed from name
|
||||
lastname, // Added
|
||||
firstname,
|
||||
lastname,
|
||||
role,
|
||||
employeeType,
|
||||
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>(`
|
||||
SELECT
|
||||
id, email, name, role, is_active as isActive,
|
||||
id, email, firstname, lastname, role, is_active as isActive,
|
||||
employee_type as employeeType,
|
||||
contract_type as contractType,
|
||||
can_work_alone as canWorkAlone,
|
||||
@@ -157,35 +163,58 @@ export const updateEmployee = async (req: AuthRequest, res: Response): Promise<v
|
||||
try {
|
||||
const { id } = req.params;
|
||||
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
|
||||
const existingEmployee = await db.get('SELECT * FROM employees WHERE id = ?', [id]);
|
||||
console.log('📝 Update Employee Request:', { id, firstname, lastname, role, isActive, employeeType, contractType, canWorkAlone });
|
||||
|
||||
// Check if employee exists and get current data
|
||||
const existingEmployee = await db.get<any>('SELECT * FROM employees WHERE id = ?', [id]);
|
||||
if (!existingEmployee) {
|
||||
res.status(404).json({ error: 'Employee not found' });
|
||||
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(
|
||||
`UPDATE employees
|
||||
SET firstname = COALESCE(?, firstname),
|
||||
lastname = COALESCE(?, lastname),
|
||||
role = COALESCE(?, role),
|
||||
is_active = COALESCE(?, is_active),
|
||||
employee_type = COALESCE(?, employee_type),
|
||||
contract_type = COALESCE(?, contract_type),
|
||||
can_work_alone = COALESCE(?, can_work_alone)
|
||||
WHERE id = ?`,
|
||||
[firstname, lastname, role, isActive, employeeType, contractType, canWorkAlone, id]
|
||||
SET firstname = COALESCE(?, firstname),
|
||||
lastname = COALESCE(?, lastname),
|
||||
email = ?,
|
||||
role = COALESCE(?, role),
|
||||
is_active = COALESCE(?, is_active),
|
||||
employee_type = COALESCE(?, employee_type),
|
||||
contract_type = COALESCE(?, contract_type),
|
||||
can_work_alone = COALESCE(?, can_work_alone)
|
||||
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
|
||||
const updatedEmployee = await db.get<any>(`
|
||||
SELECT
|
||||
id, email, name, role, is_active as isActive,
|
||||
id, email, firstname, lastname, role, is_active as isActive,
|
||||
employee_type as employeeType,
|
||||
contract_type as contractType,
|
||||
can_work_alone as canWorkAlone,
|
||||
|
||||
@@ -5,6 +5,25 @@ import { v4 as uuidv4 } from 'uuid';
|
||||
import { randomUUID } from 'crypto';
|
||||
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> => {
|
||||
try {
|
||||
const adminExists = await db.get<{ 'COUNT(*)': number }>(
|
||||
@@ -43,12 +62,11 @@ export const setupAdmin = async (req: Request, res: Response): Promise<void> =>
|
||||
return;
|
||||
}
|
||||
|
||||
const { password, firstname, lastname } = req.body;
|
||||
const email = 'admin@instandhaltung.de';
|
||||
const { password, firstname, lastname } = req.body; // Changed from name to firstname/lastname
|
||||
|
||||
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) {
|
||||
res.status(400).json({ error: 'Passwort, Vorname und Nachname sind erforderlich' });
|
||||
return;
|
||||
@@ -60,6 +78,10 @@ export const setupAdmin = async (req: Request, res: Response): Promise<void> =>
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate email automatically using the same pattern
|
||||
const email = generateEmail(firstname, lastname);
|
||||
console.log('📧 Generated admin email:', email);
|
||||
|
||||
// Hash password
|
||||
const hashedPassword = await bcrypt.hash(password, 10);
|
||||
const adminId = randomUUID();
|
||||
@@ -70,18 +92,14 @@ export const setupAdmin = async (req: Request, res: Response): Promise<void> =>
|
||||
await db.run('BEGIN TRANSACTION');
|
||||
|
||||
try {
|
||||
// Create admin user
|
||||
// Create admin user with generated email
|
||||
await db.run(
|
||||
`INSERT INTO employees (id, email, password, firstname, lastname, role, employee_type, contract_type, can_work_alone, is_active)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[adminId, email, hashedPassword, firstname, lastname, 'admin', 'manager', 'large', true, 1]
|
||||
);
|
||||
|
||||
console.log('✅ Admin user created successfully');
|
||||
|
||||
// Initialize default templates WITHOUT starting a new transaction
|
||||
//console.log('🔄 Initialisiere Standard-Vorlagen...');
|
||||
//await initializeDefaultTemplates(adminId, false);
|
||||
console.log('✅ Admin user created successfully with email:', email);
|
||||
|
||||
// Commit the entire setup transaction
|
||||
await db.run('COMMIT');
|
||||
@@ -91,7 +109,9 @@ export const setupAdmin = async (req: Request, res: Response): Promise<void> =>
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
message: 'Admin erfolgreich erstellt',
|
||||
email: email
|
||||
email: email,
|
||||
firstname: firstname,
|
||||
lastname: lastname
|
||||
});
|
||||
|
||||
} catch (dbError) {
|
||||
|
||||
@@ -14,7 +14,6 @@ export interface Employee {
|
||||
}
|
||||
|
||||
export interface CreateEmployeeRequest {
|
||||
email: string;
|
||||
password: string;
|
||||
firstname: string;
|
||||
lastname: string;
|
||||
|
||||
@@ -3,15 +3,15 @@ export interface ShiftPlan {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
startDate?: string; // Optional for templates
|
||||
endDate?: string; // Optional for templates
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
isTemplate: boolean;
|
||||
status: 'draft' | 'published' | 'archived' | 'template';
|
||||
createdBy: string;
|
||||
createdAt: string;
|
||||
timeSlots: TimeSlot[];
|
||||
shifts: Shift[];
|
||||
scheduledShifts?: ScheduledShift[]; // Only for non-template plans with dates
|
||||
scheduledShifts?: ScheduledShift[];
|
||||
}
|
||||
|
||||
export interface TimeSlot {
|
||||
@@ -85,7 +85,6 @@ export interface AssignEmployeeRequest {
|
||||
scheduledShiftId: string;
|
||||
}
|
||||
|
||||
|
||||
export interface UpdateRequiredEmployeesRequest {
|
||||
requiredEmployees: number;
|
||||
}
|
||||
@@ -2,15 +2,20 @@
|
||||
import { Employee } from './Employee.js';
|
||||
import { ShiftPlan } from './ShiftPlan.js';
|
||||
|
||||
// Add the missing type definitions
|
||||
// Updated Availability interface to match new schema
|
||||
export interface Availability {
|
||||
id: string;
|
||||
employeeId: string;
|
||||
planId: string;
|
||||
dayOfWeek: number; // 1=Monday, 7=Sunday
|
||||
timeSlotId: string;
|
||||
shiftId: string; // Now references shift_id instead of time_slot_id + day_of_week
|
||||
preferenceLevel: 1 | 2 | 3; // 1:preferred, 2:available, 3:unavailable
|
||||
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 {
|
||||
@@ -120,4 +125,77 @@ export interface ShiftRequirement {
|
||||
maxEmployees: number;
|
||||
assignedEmployees: string[];
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user