From e1e435a81155e3ea71852686fdac4ccfe62171af Mon Sep 17 00:00:00 2001 From: donpat1to Date: Fri, 10 Oct 2025 18:22:13 +0200 Subject: [PATCH] removed phone and departement as user attribute --- backend/src/controllers/authController.ts | 19 +- backend/src/controllers/employeeController.ts | 79 +-- backend/src/controllers/setupController.ts | 12 +- .../migrations/002_add_employee_fields.sql | 3 + backend/src/database/schema.sql | 4 +- backend/src/models/Employee.ts | 29 +- backend/src/scripts/applyMigration.ts | 43 ++ backend/src/scripts/seedData.ts | 92 ---- backend/src/server.ts | 7 + frontend/src/App.tsx | 117 ++--- frontend/src/components/Layout/Footer.tsx | 251 ++++++--- .../src/components/Layout/Layout.module.css | 220 ++++++++ frontend/src/components/Layout/Layout.tsx | 38 +- frontend/src/components/Layout/Navigation.tsx | 353 +++++++------ frontend/src/contexts/AuthContext.tsx | 54 +- frontend/src/pages/Auth/Login.tsx | 137 +++-- .../Employees/components/EmployeeForm.tsx | 491 +++++++++++------- .../Employees/components/EmployeeList.tsx | 150 ++++-- frontend/src/pages/Setup/Setup.tsx | 255 +++++---- frontend/src/types/employee.ts | 33 +- frontend/src/types/user.ts | 9 +- 21 files changed, 1508 insertions(+), 888 deletions(-) create mode 100644 backend/src/database/migrations/002_add_employee_fields.sql create mode 100644 backend/src/scripts/applyMigration.ts delete mode 100644 backend/src/scripts/seedData.ts create mode 100644 frontend/src/components/Layout/Layout.module.css diff --git a/backend/src/controllers/authController.ts b/backend/src/controllers/authController.ts index d9575a4..9e8efa7 100644 --- a/backend/src/controllers/authController.ts +++ b/backend/src/controllers/authController.ts @@ -1,3 +1,4 @@ +// backend/src/controllers/authController.ts import { Request, Response } from 'express'; import jwt from 'jsonwebtoken'; import bcrypt from 'bcrypt'; @@ -9,8 +10,6 @@ export interface User { email: string; name: string; role: string; - phone?: string; - department?: string; } export interface UserWithPassword extends User { @@ -34,8 +33,8 @@ export interface RegisterRequest { email: string; password: string; name: string; - phone?: string; - department?: string; + //employee_type?: string; + //is_sufficiently_independent?: string; role?: string; } @@ -52,7 +51,7 @@ export const login = async (req: Request, res: Response) => { // Get user from database const user = await db.get( - 'SELECT id, email, password, name, role, phone, department FROM users WHERE email = ? AND is_active = 1', + 'SELECT id, email, password, name, role FROM users WHERE email = ? AND is_active = 1', [email] ); @@ -116,7 +115,7 @@ export const getCurrentUser = async (req: Request, res: Response) => { } const user = await db.get( - 'SELECT id, email, name, role, phone, department FROM users WHERE id = ? AND is_active = 1', + 'SELECT id, email, name, role FROM users WHERE id = ? AND is_active = 1', [jwtUser.userId] // ← HIER: userId verwenden ); @@ -162,7 +161,7 @@ export const validateToken = async (req: Request, res: Response) => { export const register = async (req: Request, res: Response) => { try { - const { email, password, name, phone, department, role = 'user' } = req.body as RegisterRequest; + const { email, password, name, role = 'user' } = req.body as RegisterRequest; // Validate required fields if (!email || !password || !name) { @@ -188,9 +187,9 @@ export const register = async (req: Request, res: Response) => { // Insert user const result = await db.run( - `INSERT INTO users (email, password, name, role, phone, department) - VALUES (?, ?, ?, ?, ?, ?)`, - [email, hashedPassword, name, role, phone, department] + `INSERT INTO users (email, password, name, role) + VALUES (?, ?, ?, ?)`, + [email, hashedPassword, name, role] ); if (!result.lastID) { diff --git a/backend/src/controllers/employeeController.ts b/backend/src/controllers/employeeController.ts index b22d3e7..6a0abec 100644 --- a/backend/src/controllers/employeeController.ts +++ b/backend/src/controllers/employeeController.ts @@ -4,6 +4,7 @@ import { v4 as uuidv4 } from 'uuid'; import bcrypt from 'bcryptjs'; import { db } from '../services/databaseService.js'; import { AuthRequest } from '../middleware/auth.js'; +import { CreateEmployeeRequest } from '../models/Employee.js'; export const getEmployees = async (req: AuthRequest, res: Response): Promise => { try { @@ -12,7 +13,9 @@ export const getEmployees = async (req: AuthRequest, res: Response): Promise(` SELECT id, email, name, role, is_active as isActive, - phone, department, created_at as createdAt, + employee_type as employeeType, + is_sufficiently_independent as isSufficientlyIndependent, + created_at as createdAt, last_login as lastLogin FROM users WHERE is_active = 1 @@ -36,7 +39,9 @@ export const getEmployee = async (req: AuthRequest, res: Response): Promise(` SELECT id, email, name, role, is_active as isActive, - phone, department, created_at as createdAt, + employee_type as employeeType, + is_sufficiently_independent as isSufficientlyIndependent, + created_at as createdAt, last_login as lastLogin FROM users WHERE id = ? @@ -61,19 +66,22 @@ export const createEmployee = async (req: AuthRequest, res: Response): Promise('SELECT id, email, is_active FROM users WHERE email = ?', [email]); - console.log('🔍 Found existing users with this email:', allUsersWithEmail); - - // Check if email already exists among active users only - const existingActiveUser = await db.get('SELECT id, is_active FROM users WHERE email = ? AND is_active = 1', [email]); - console.log('🔍 Checking active users with this email:', existingActiveUser); + // Check if email already exists + const existingActiveUser = await db.get('SELECT id FROM users WHERE email = ? AND is_active = 1', [email]); if (existingActiveUser) { console.log('❌ Email exists for active user:', existingActiveUser); @@ -102,16 +105,30 @@ export const createEmployee = async (req: AuthRequest, res: Response): Promise(` SELECT - id, email, name, role, is_active as isActive, - phone, department, created_at as createdAt, + id, email, name, role, is_active as isActive, + employee_type as employeeType, + is_sufficiently_independent as isSufficientlyIndependent, + notes, created_at as createdAt, last_login as lastLogin FROM users WHERE id = ? @@ -127,7 +144,7 @@ export const createEmployee = async (req: AuthRequest, res: Response): Promise => { try { const { id } = req.params; - const { name, role, isActive, phone, department } = req.body; + const { name, role, isActive, employeeType, isSufficientlyIndependent } = req.body; // Check if employee exists const existingEmployee = await db.get('SELECT * FROM users WHERE id = ?', [id]); @@ -142,17 +159,19 @@ export const updateEmployee = async (req: AuthRequest, res: Response): Promise(` SELECT id, email, name, role, is_active as isActive, - phone, department, created_at as createdAt, + employee_type as employeeType, + is_sufficiently_independent as isSufficientlyIndependent, + created_at as createdAt, last_login as lastLogin FROM users WHERE id = ? diff --git a/backend/src/controllers/setupController.ts b/backend/src/controllers/setupController.ts index 8acf032..9557451 100644 --- a/backend/src/controllers/setupController.ts +++ b/backend/src/controllers/setupController.ts @@ -44,10 +44,10 @@ export const setupAdmin = async (req: Request, res: Response): Promise => return; } - const { password, name, phone, department } = req.body; + const { password, name } = req.body; const email = 'admin@instandhaltung.de'; // Fixed admin email - console.log('👤 Creating admin with data:', { name, email, phone, department }); + console.log('👤 Creating admin with data:', { name, email }); // Validation if (!password || !name) { @@ -63,16 +63,16 @@ export const setupAdmin = async (req: Request, res: Response): Promise => // Hash password const hashedPassword = await bcrypt.hash(password, 10); - const adminId = uuidv4(); + const adminId = randomUUID(); console.log('📝 Inserting admin user with ID:', adminId); try { // Create admin user await db.run( - `INSERT INTO users (id, email, password, name, role, phone, department, is_active) - VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, - [adminId, email, hashedPassword, name, 'admin', phone || null, department || null, 1] + `INSERT INTO users (id, email, password, name, role, is_active) + VALUES (?, ?, ?, ?, ?, ?)`, + [adminId, email, hashedPassword, name, 'admin', 1] ); console.log('✅ Admin user created successfully'); diff --git a/backend/src/database/migrations/002_add_employee_fields.sql b/backend/src/database/migrations/002_add_employee_fields.sql new file mode 100644 index 0000000..ecffe0b --- /dev/null +++ b/backend/src/database/migrations/002_add_employee_fields.sql @@ -0,0 +1,3 @@ +-- Add employee fields +ALTER TABLE users ADD COLUMN employee_type TEXT CHECK(employee_type IN ('chef', 'neuling', 'erfahren')); +ALTER TABLE users ADD COLUMN is_sufficiently_independent BOOLEAN DEFAULT FALSE; \ No newline at end of file diff --git a/backend/src/database/schema.sql b/backend/src/database/schema.sql index a764d67..19234c9 100644 --- a/backend/src/database/schema.sql +++ b/backend/src/database/schema.sql @@ -5,8 +5,8 @@ CREATE TABLE IF NOT EXISTS users ( password TEXT NOT NULL, name TEXT NOT NULL, role TEXT CHECK(role IN ('admin', 'user', 'instandhalter')) NOT NULL, - phone TEXT, - department TEXT, + employee_type TEXT CHECK(employee_type IN ('chef', 'neuling', 'erfahren')), + is_sufficiently_independent BOOLEAN DEFAULT FALSE, is_active BOOLEAN DEFAULT TRUE, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); diff --git a/backend/src/models/Employee.ts b/backend/src/models/Employee.ts index 0916b13..afbc869 100644 --- a/backend/src/models/Employee.ts +++ b/backend/src/models/Employee.ts @@ -2,38 +2,35 @@ export interface Employee { id: string; email: string; - password: string; name: string; role: 'admin' | 'instandhalter' | 'user'; + employeeType: 'chef' | 'neuling' | 'erfahren'; + isSufficientlyIndependent: boolean; isActive: boolean; - phone?: string; - department?: string; + notes?: string; createdAt: string; lastLogin?: string | null; } -export interface Availability { - id: string; - employeeId: string; - dayOfWeek: number; - startTime: string; - endTime: string; - isAvailable: boolean; -} - export interface CreateEmployeeRequest { email: string; password: string; name: string; role: 'admin' | 'instandhalter' | 'user'; - phone?: string; - department?: string; + employeeType: 'chef' | 'neuling' | 'erfahren'; + isSufficientlyIndependent: boolean; + notes?: string; } export interface UpdateEmployeeRequest { name?: string; role?: 'admin' | 'instandhalter' | 'user'; + employeeType?: 'chef' | 'neuling' | 'erfahren'; + isSufficientlyIndependent?: boolean; isActive?: boolean; - phone?: string; - department?: string; + notes?: string; +} + +export interface EmployeeWithPassword extends Employee { + password: string; } \ No newline at end of file diff --git a/backend/src/scripts/applyMigration.ts b/backend/src/scripts/applyMigration.ts new file mode 100644 index 0000000..a13f186 --- /dev/null +++ b/backend/src/scripts/applyMigration.ts @@ -0,0 +1,43 @@ +import { db } from '../services/databaseService.js'; +import { readFile } from 'fs/promises'; +import { fileURLToPath } from 'url'; +import { dirname, join } from 'path'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +export async function applyMigration() { + try { + console.log('📦 Starting database migration...'); + + // Read the migration file + const migrationPath = join(__dirname, '../database/migrations/002_add_employee_fields.sql'); + const migrationSQL = await readFile(migrationPath, 'utf-8'); + + // Split into individual statements + const statements = migrationSQL + .split(';') + .map(s => s.trim()) + .filter(s => s.length > 0); + + // Execute each statement + for (const statement of statements) { + try { + await db.exec(statement); + console.log('✅ Executed:', statement.slice(0, 50) + '...'); + } catch (error) { + const err = error as { code: string; message: string }; + if (err.code === 'SQLITE_ERROR' && err.message.includes('duplicate column name')) { + console.log('ℹ️ Column already exists, skipping...'); + continue; + } + throw error; + } + } + + console.log('✅ Migration completed successfully'); + } catch (error) { + console.error('❌ Migration failed:', error); + throw error; + } +} \ No newline at end of file diff --git a/backend/src/scripts/seedData.ts b/backend/src/scripts/seedData.ts deleted file mode 100644 index c02bb28..0000000 --- a/backend/src/scripts/seedData.ts +++ /dev/null @@ -1,92 +0,0 @@ -// backend/src/scripts/seedData.ts - Erweitert -import { db } from '../services/databaseService.js'; -import { v4 as uuidv4 } from 'uuid'; -import bcrypt from 'bcryptjs'; - -export const seedData = async () => { - try { - console.log('Starting database seeding...'); - - // Admin User erstellen - const adminId = uuidv4(); - const hashedPassword = await bcrypt.hash('admin123', 10); - - await db.run( - `INSERT OR IGNORE INTO users (id, email, password, name, role, phone, department, is_active) - VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, - [adminId, 'admin@schichtplan.de', hashedPassword, 'System Administrator', 'admin', '+49 123 456789', 'IT', 1] - ); - - // Test-Mitarbeiter erstellen - const testUsers = [ - { - id: uuidv4(), - email: 'instandhalter@schichtplan.de', - password: await bcrypt.hash('instandhalter123', 10), - name: 'Max Instandhalter', - role: 'instandhalter', - phone: '+49 123 456790', - department: 'Produktion' - }, - { - id: uuidv4(), - email: 'mitarbeiter1@schichtplan.de', - password: await bcrypt.hash('user123', 10), - name: 'Anna Müller', - role: 'user', - phone: '+49 123 456791', - department: 'Logistik' - }, - { - id: uuidv4(), - email: 'mitarbeiter2@schichtplan.de', - password: await bcrypt.hash('user123', 10), - name: 'Tom Schmidt', - role: 'user', - phone: '+49 123 456792', - department: 'Produktion' - } - ]; - - for (const user of testUsers) { - await db.run( - `INSERT OR IGNORE INTO users (id, email, password, name, role, phone, department, is_active) - VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, - [user.id, user.email, user.password, user.name, user.role, user.phone, user.department, 1] - ); - } - - // Standard Vorlage erstellen - const templateId = uuidv4(); - await db.run( - `INSERT OR IGNORE INTO shift_templates (id, name, description, is_default, created_by) - VALUES (?, ?, ?, ?, ?)`, - [templateId, 'Standard Woche', 'Standard Schichtplan für Montag bis Freitag', 1, adminId] - ); - - // Standard Schichten - const shifts = [ - { day: 1, name: 'Vormittag', start: '08:00', end: '12:00', employees: 2 }, - { day: 1, name: 'Nachmittag', start: '11:30', end: '15:30', employees: 2 }, - { day: 2, name: 'Vormittag', start: '08:00', end: '12:00', employees: 2 }, - { day: 2, name: 'Nachmittag', start: '11:30', end: '15:30', employees: 2 }, - { day: 3, name: 'Vormittag', start: '08:00', end: '12:00', employees: 2 }, - { day: 3, name: 'Nachmittag', start: '11:30', end: '15:30', employees: 2 }, - { day: 4, name: 'Vormittag', start: '08:00', end: '12:00', employees: 2 }, - { day: 4, name: 'Nachmittag', start: '11:30', end: '15:30', employees: 2 }, - { day: 5, name: 'Vormittag', start: '08:00', end: '12:00', employees: 2 } - ]; - - for (const shift of shifts) { - await db.run( - `INSERT OR IGNORE INTO template_shifts (id, template_id, day_of_week, name, start_time, end_time, required_employees) - VALUES (?, ?, ?, ?, ?, ?, ?)`, - [uuidv4(), templateId, shift.day, shift.name, shift.start, shift.end, shift.employees] - ); - } - - console.log('✅ Test data seeded successfully'); - } catch (error) { - console.error('❌ Error seeding test data:', error); - } -}; \ No newline at end of file diff --git a/backend/src/server.ts b/backend/src/server.ts index cc9b682..4a43468 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -59,9 +59,16 @@ app.get('/api/initial-setup', async (req: any, res: any) => { // Initialize the application const initializeApp = async () => { try { + // Initialize database with base schema await initializeDatabase(); console.log('✅ Database initialized successfully'); + // Apply any pending migrations + const { applyMigration } = await import('./scripts/applyMigration.js'); + await applyMigration(); + console.log('✅ Database migrations applied'); + + // Setup default template await setupDefaultTemplate(); console.log('✅ Default template checked/created'); diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 4df72dc..1327787 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,4 +1,4 @@ -// frontend/src/App.tsx - KORRIGIERTE VERSION +// frontend/src/App.tsx - KORRIGIERT MIT LAYOUT import React from 'react'; import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; import { AuthProvider, useAuth } from './contexts/AuthContext'; @@ -47,28 +47,13 @@ const ProtectedRoute: React.FC<{ children: React.ReactNode; roles?: string[] }> return {children}; }; -// SetupWrapper Component -const SetupWrapper: React.FC = () => { - return ( - - - - ); -}; - -// LoginWrapper Component -const LoginWrapper: React.FC = () => { - return ( - - - - ); -}; - // Main App Content const AppContent: React.FC = () => { const { loading, needsSetup, user } = useAuth(); + console.log('🏠 AppContent rendering - loading:', loading, 'needsSetup:', needsSetup, 'user:', user); + + // Während des Ladens if (loading) { return (
@@ -77,56 +62,59 @@ const AppContent: React.FC = () => { ); } - console.log('AppContent - needsSetup:', needsSetup, 'user:', user); - - // Wenn Setup benötigt wird → Setup zeigen (mit Router) + // Setup benötigt if (needsSetup) { - return ; + console.log('🔧 Showing setup page'); + return ; } - // Wenn kein User eingeloggt ist → Login zeigen (mit Router) + // Kein User eingeloggt if (!user) { - return ; + console.log('🔐 Showing login page'); + return ; } - // Wenn User eingeloggt ist → Geschützte Routen zeigen + // User eingeloggt - Geschützte Routen + console.log('✅ Showing protected routes for user:', user.email); return ( - - - - - - - } /> - - - - } /> - - - - } /> - - - - } /> - - - - } /> - - - - } /> - } /> - - + + + + + } /> + + + + } /> + + + + } /> + + + + } /> + + + + } /> + + + + } /> + } /> + + + + } /> + ); }; @@ -134,7 +122,10 @@ function App() { return ( - + + + + ); diff --git a/frontend/src/components/Layout/Footer.tsx b/frontend/src/components/Layout/Footer.tsx index 612d1c9..043d17b 100644 --- a/frontend/src/components/Layout/Footer.tsx +++ b/frontend/src/components/Layout/Footer.tsx @@ -1,95 +1,186 @@ // frontend/src/components/Layout/Footer.tsx import React from 'react'; -import { Link } from 'react-router-dom'; const Footer: React.FC = () => { - return ( -