diff --git a/backend/package.json b/backend/package.json index 6aa51cb..6ef4947 100644 --- a/backend/package.json +++ b/backend/package.json @@ -3,9 +3,8 @@ "version": "1.0.0", "type": "module", "scripts": { - "dev": "node --loader ts-node/esm src/server.ts", - "simple": "node src/server.ts", - "build": "tsc", + "dev": "npx tsx src/server.ts", + "build": "tsc", "start": "node dist/server.js" }, "dependencies": { diff --git a/backend/src/controllers/authController.ts b/backend/src/controllers/authController.ts index a25fc9a..b0529dd 100644 --- a/backend/src/controllers/authController.ts +++ b/backend/src/controllers/authController.ts @@ -42,29 +42,38 @@ export const login = async (req: Request, res: Response) => { try { const { email, password } = req.body as LoginRequest; + console.log('🔐 Login attempt for email:', email); + if (!email || !password) { + console.log('❌ Missing email or password'); return res.status(400).json({ error: 'E-Mail und Passwort sind erforderlich' }); } // Get user from database const user = await db.get( - 'SELECT id, email, password, name, role, phone, department FROM users WHERE email = ?', + 'SELECT id, email, password, name, role, phone, department FROM users WHERE email = ? AND is_active = 1', [email] ); + console.log('🔍 User found:', user ? 'Yes' : 'No'); + if (!user) { + console.log('❌ No user found with email:', email); return res.status(401).json({ error: 'Ungültige Anmeldedaten' }); } // Verify password const validPassword = await bcrypt.compare(password, user.password); + console.log('🔑 Password valid:', validPassword); + if (!validPassword) { + console.log('❌ Invalid password for user:', email); return res.status(401).json({ error: 'Ungültige Anmeldedaten' }); } - // Create token payload + // Create token payload - ID als STRING verwenden const tokenPayload: JWTPayload = { - id: user.id.toString(), // ← Sicherstellen dass es string ist + id: user.id.toString(), // ← WICHTIG: Als string email: user.email, role: user.role }; @@ -79,6 +88,8 @@ export const login = async (req: Request, res: Response) => { // Remove password from user object const { password: _, ...userWithoutPassword } = user; + console.log('✅ Login successful for:', user.email); + res.json({ user: userWithoutPassword, token @@ -92,19 +103,26 @@ export const login = async (req: Request, res: Response) => { export const getCurrentUser = async (req: Request, res: Response) => { try { const jwtUser = (req as any).user as JWTPayload; + console.log('🔍 Getting current user for ID:', jwtUser?.id); + if (!jwtUser?.id) { + console.log('❌ No user ID in JWT'); return res.status(401).json({ error: 'Nicht authentifiziert' }); } const user = await db.get( - 'SELECT id, email, name, role, phone, department FROM users WHERE id = ?', + 'SELECT id, email, name, role, phone, department FROM users WHERE id = ? AND is_active = 1', [jwtUser.id] ); + console.log('🔍 User found in database:', user ? 'Yes' : 'No'); + if (!user) { + console.log('❌ User not found in database for ID:', jwtUser.id); return res.status(404).json({ error: 'Benutzer nicht gefunden' }); } + console.log('✅ Returning user:', user.email); res.json({ user }); } catch (error) { console.error('Get current user error:', error); diff --git a/backend/src/controllers/employeeController.ts b/backend/src/controllers/employeeController.ts index 481bcba..999c8ef 100644 --- a/backend/src/controllers/employeeController.ts +++ b/backend/src/controllers/employeeController.ts @@ -5,6 +5,25 @@ import bcrypt from 'bcryptjs'; import { db } from '../services/databaseService.js'; import { AuthRequest } from '../middleware/auth.js'; +export const getEmployees = async (req: AuthRequest, res: Response): Promise => { + try { + const employees = await db.all(` + SELECT + id, email, name, role, is_active as isActive, + phone, department, created_at as createdAt, + last_login as lastLogin + FROM users + WHERE is_active = 1 + ORDER BY name + `); + + res.json(employees); + } catch (error) { + console.error('Error fetching employees:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}; + export const getEmployee = async (req: AuthRequest, res: Response): Promise => { try { const { id } = req.params; diff --git a/backend/src/controllers/setupController.ts b/backend/src/controllers/setupController.ts index f4aa7ac..8acf032 100644 --- a/backend/src/controllers/setupController.ts +++ b/backend/src/controllers/setupController.ts @@ -1,18 +1,24 @@ // backend/src/controllers/setupController.ts import { Request, Response } from 'express'; import bcrypt from 'bcrypt'; +import { v4 as uuidv4 } from 'uuid'; import { randomUUID } from 'crypto'; import { db } from '../services/databaseService.js'; export const checkSetupStatus = async (req: Request, res: Response): Promise => { try { const adminExists = await db.get<{ 'COUNT(*)': number }>( - 'SELECT COUNT(*) FROM users WHERE role = ?', + 'SELECT COUNT(*) FROM users WHERE role = ? AND is_active = 1', ['admin'] ); + console.log('Admin exists check:', adminExists); + + // Korrekte Rückgabe - needsSetup sollte true sein wenn KEIN Admin existiert + const needsSetup = !adminExists || adminExists['COUNT(*)'] === 0; + res.json({ - needsSetup: !adminExists || adminExists['COUNT(*)'] === 0 + needsSetup: needsSetup }); } catch (error) { console.error('Error checking setup status:', error); @@ -26,11 +32,14 @@ export const setupAdmin = async (req: Request, res: Response): Promise => try { // Check if admin already exists const adminExists = await db.get<{ 'COUNT(*)': number }>( - 'SELECT COUNT(*) FROM users WHERE role = ?', + 'SELECT COUNT(*) FROM users WHERE role = ? AND is_active = 1', ['admin'] ); + console.log('🔍 Admin exists check:', adminExists); + if (adminExists && adminExists['COUNT(*)'] > 0) { + console.log('❌ Admin already exists'); res.status(400).json({ error: 'Admin existiert bereits' }); return; } @@ -38,6 +47,8 @@ export const setupAdmin = async (req: Request, res: Response): Promise => const { password, name, phone, department } = req.body; const email = 'admin@instandhaltung.de'; // Fixed admin email + console.log('👤 Creating admin with data:', { name, email, phone, department }); + // Validation if (!password || !name) { res.status(400).json({ error: 'Passwort und Name sind erforderlich' }); @@ -52,29 +63,33 @@ export const setupAdmin = async (req: Request, res: Response): Promise => // Hash password const hashedPassword = await bcrypt.hash(password, 10); - const adminId = randomUUID(); + const adminId = uuidv4(); + + 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, true] + [adminId, email, hashedPassword, name, 'admin', phone || null, department || null, 1] ); + console.log('✅ Admin user created successfully'); + res.status(201).json({ success: true, message: 'Admin erfolgreich erstellt', email: email }); } catch (dbError) { - console.error('Database error during admin creation:', dbError); + console.error('❌ Database error during admin creation:', dbError); res.status(500).json({ error: 'Fehler beim Erstellen des Admin-Accounts' }); } } catch (error) { - console.error('Error in setup:', error); + console.error('❌ Error in setup:', error); res.status(500).json({ error: 'Ein unerwarteter Fehler ist aufgetreten' }); diff --git a/backend/src/middleware/auth.ts b/backend/src/middleware/auth.ts index 1a46d37..ef7e987 100644 --- a/backend/src/middleware/auth.ts +++ b/backend/src/middleware/auth.ts @@ -1,7 +1,6 @@ // backend/src/middleware/auth.ts import { Request, Response, NextFunction } from 'express'; import jwt from 'jsonwebtoken'; -import { JWTPayload } from '../controllers/authController.js'; const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key'; @@ -14,15 +13,21 @@ export interface AuthRequest extends Request { } export const authMiddleware = (req: AuthRequest, res: Response, next: NextFunction): void => { - const token = req.header('Authorization')?.replace('Bearer ', ''); + const authHeader = req.header('Authorization'); + console.log('🔐 Auth middleware - Authorization header:', authHeader); + + const token = authHeader?.replace('Bearer ', ''); if (!token) { + console.log('❌ No token provided'); res.status(401).json({ error: 'Access denied. No token provided.' }); return; } try { - const decoded = jwt.verify(token, JWT_SECRET) as JWTPayload; + const decoded = jwt.verify(token, JWT_SECRET) as any; + console.log('✅ Token valid for user:', decoded.email); + req.user = { userId: decoded.id, email: decoded.email, @@ -30,6 +35,7 @@ export const authMiddleware = (req: AuthRequest, res: Response, next: NextFuncti }; next(); } catch (error) { + console.error('❌ Invalid token:', error); res.status(400).json({ error: 'Invalid token.' }); } }; @@ -37,6 +43,7 @@ export const authMiddleware = (req: AuthRequest, res: Response, next: NextFuncti export const requireRole = (roles: string[]) => { return (req: AuthRequest, res: Response, next: NextFunction): void => { if (!req.user || !roles.includes(req.user.role)) { + console.log('❌ Insufficient permissions for user:', req.user?.email); res.status(403).json({ error: 'Access denied. Insufficient permissions.' }); return; } diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 7396026..4df72dc 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -14,7 +14,7 @@ import Settings from './pages/Settings/Settings'; import Help from './pages/Help/Help'; import Setup from './pages/Setup/Setup'; -// Protected Route Component direkt in App.tsx +// Protected Route Component const ProtectedRoute: React.FC<{ children: React.ReactNode; roles?: string[] }> = ({ children, roles = ['admin', 'instandhalter', 'user'] @@ -47,6 +47,89 @@ 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(); + + if (loading) { + return ( +
+
⏳ Lade Anwendung...
+
+ ); + } + + console.log('AppContent - needsSetup:', needsSetup, 'user:', user); + + // Wenn Setup benötigt wird → Setup zeigen (mit Router) + if (needsSetup) { + return ; + } + + // Wenn kein User eingeloggt ist → Login zeigen (mit Router) + if (!user) { + return ; + } + + // Wenn User eingeloggt ist → Geschützte Routen zeigen + return ( + + + + + + + } /> + + + + } /> + + + + } /> + + + + } /> + + + + } /> + + + + } /> + } /> + + + ); +}; + function App() { return ( @@ -57,61 +140,4 @@ function App() { ); } -function AppContent() { - const { loading, needsSetup } = useAuth(); - - if (loading) { - return ( -
-
⏳ Lade Anwendung...
-
- ); - } - - return ( - - - - {needsSetup ? ( - } /> - ) : ( - <> - } /> - - - - } /> - - - - } /> - - - - } /> - - - - } /> - - - - } /> - - - - } /> - - )} - - - ); -} - export default App; \ No newline at end of file diff --git a/frontend/src/contexts/AuthContext.tsx b/frontend/src/contexts/AuthContext.tsx index 6edbaf8..b93951a 100644 --- a/frontend/src/contexts/AuthContext.tsx +++ b/frontend/src/contexts/AuthContext.tsx @@ -1,9 +1,14 @@ // frontend/src/contexts/AuthContext.tsx -import React, { createContext, useContext, useState, useEffect } from 'react'; -import { authService, User, LoginRequest } from '../services/authService'; +import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react'; +import { Employee } from '../types/employee'; + +interface LoginRequest { + email: string; + password: string; +} interface AuthContextType { - user: User | null; + user: Employee | null; login: (credentials: LoginRequest) => Promise; logout: () => void; hasRole: (roles: string[]) => boolean; @@ -15,102 +20,130 @@ interface AuthContextType { const AuthContext = createContext(undefined); -export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { - const [user, setUser] = useState(null); +interface AuthProviderProps { + children: ReactNode; +} + +export const AuthProvider: React.FC = ({ children }) => { + const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); const [needsSetup, setNeedsSetup] = useState(false); - const [refreshTrigger, setRefreshTrigger] = useState(0); - const checkSetupStatus = async () => { + // Token aus localStorage laden + const getStoredToken = (): string | null => { + return localStorage.getItem('token'); + }; + + // Token in localStorage speichern + const setStoredToken = (token: string) => { + localStorage.setItem('token', token); + }; + + // Token aus localStorage entfernen + const removeStoredToken = () => { + localStorage.removeItem('token'); + }; + + const checkSetupStatus = async (): Promise => { try { - const response = await fetch('/api/setup/status'); + const response = await fetch('http://localhost:3002/api/setup/status'); if (!response.ok) { - throw new Error('Failed to check setup status'); + throw new Error('Setup status check failed'); } const data = await response.json(); - setNeedsSetup(data.needsSetup); + console.log('Setup status response:', data); + setNeedsSetup(data.needsSetup === true); } catch (error) { console.error('Error checking setup status:', error); - // If we can't reach the server, assume setup is needed - setNeedsSetup(true); + setNeedsSetup(false); } }; - // Check setup status and load user on mount - useEffect(() => { - const initializeApp = async () => { - await checkSetupStatus(); - - // Only try to load user if setup is not needed - if (!needsSetup) { - const savedUser = authService.getCurrentUser(); - if (savedUser) { - setUser(savedUser); - console.log('✅ User from localStorage:', savedUser.email); - } - } - - setLoading(false); - }; - initializeApp(); - }, []); - - // Update needsSetup when it changes - useEffect(() => { - if (!needsSetup && !user) { - // If setup is complete but no user is loaded, try to load from localStorage - const savedUser = authService.getCurrentUser(); - if (savedUser) { - setUser(savedUser); - } - } - }, [needsSetup, user]); - - // User vom Server laden wenn nötig - useEffect(() => { - if (refreshTrigger > 0 && !needsSetup) { - const loadUserFromServer = async () => { - const serverUser = await authService.fetchCurrentUser(); - if (serverUser) { - setUser(serverUser); - console.log('✅ User from server:', serverUser.email); - } - }; - loadUserFromServer(); - } - }, [refreshTrigger, needsSetup]); - - const login = async (credentials: LoginRequest) => { + const refreshUser = async () => { try { - console.log('🔄 Attempting login...'); - const response = await authService.login(credentials); - setUser(response.user); - console.log('✅ Login successful, user set:', response.user.email); - - // Force refresh der App - setRefreshTrigger(prev => prev + 1); + const token = getStoredToken(); + console.log('🔄 Refreshing user, token exists:', !!token); + if (!token) { + setLoading(false); + return; + } + + const response = await fetch('http://localhost:3002/api/auth/me', { + headers: { + 'Authorization': `Bearer ${token}` + } + }); + + if (response.ok) { + const data = await response.json(); + console.log('✅ User refreshed:', data.user); + setUser(data.user); + } else { + console.log('❌ Token invalid, removing from storage'); + removeStoredToken(); + setUser(null); + } } catch (error) { - console.error('❌ Login failed:', error); + console.error('Error refreshing user:', error); + removeStoredToken(); + setUser(null); + } finally { + setLoading(false); + } + }; + + const login = async (credentials: LoginRequest): Promise => { + try { + console.log('🔐 Attempting login for:', credentials.email); + + const response = await fetch('http://localhost:3002/api/auth/login', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(credentials), + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.error || 'Login failed'); + } + + const data = await response.json(); + console.log('✅ Login successful, storing token'); + + // Token persistent speichern + setStoredToken(data.token); + setUser(data.user); + } catch (error) { + console.error('Login error:', error); throw error; } }; const logout = () => { - authService.logout(); + console.log('🚪 Logging out user'); + removeStoredToken(); setUser(null); - console.log('✅ Logout completed'); }; - const refreshUser = () => { - setRefreshTrigger(prev => prev + 1); + const hasRole = (roles: string[]): boolean => { + if (!user) return false; + return roles.includes(user.role); }; - const hasRole = (roles: string[]) => { - return user ? roles.includes(user.role) : false; - }; + useEffect(() => { + const initializeAuth = async () => { + console.log('🚀 Initializing authentication...'); + await checkSetupStatus(); + await refreshUser(); + }; - const value = { + initializeAuth(); + }, []); + + const value: AuthContextType = { user, login, logout, @@ -118,9 +151,18 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children loading, refreshUser, needsSetup, - checkSetupStatus + checkSetupStatus, }; + useEffect(() => { + console.log('🔄 Auth state changed - user:', user, 'loading:', loading); + }, [user, loading]); + + useEffect(() => { + const token = getStoredToken(); + console.log('💾 Stored token on mount:', token ? 'Exists' : 'None'); + }, []); + return ( {children} @@ -128,7 +170,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children ); }; -export const useAuth = () => { +export const useAuth = (): AuthContextType => { const context = useContext(AuthContext); if (context === undefined) { throw new Error('useAuth must be used within an AuthProvider'); diff --git a/frontend/src/pages/Setup/Setup.tsx b/frontend/src/pages/Setup/Setup.tsx index e2f246b..bc8af1b 100644 --- a/frontend/src/pages/Setup/Setup.tsx +++ b/frontend/src/pages/Setup/Setup.tsx @@ -64,7 +64,6 @@ const Setup: React.FC = () => { setLoading(true); setError(''); - // Create the request payload const payload = { password: formData.password, name: formData.name, @@ -72,7 +71,7 @@ const Setup: React.FC = () => { ...(formData.department ? { department: formData.department } : {}) }; - console.log('Sending setup request with payload:', payload); + console.log('🚀 Sending setup request with payload:', payload); const response = await fetch('http://localhost:3002/api/setup/admin', { method: 'POST', @@ -82,41 +81,38 @@ const Setup: React.FC = () => { body: JSON.stringify(payload), }); + const responseText = await response.text(); + console.log('📨 Setup response:', responseText); + + let result; + try { + result = JSON.parse(responseText); + } catch (parseError) { + console.error('❌ Failed to parse response as JSON:', responseText); + throw new Error('Invalid server response'); + } + if (!response.ok) { - const data = await response.json(); - throw new Error(data.error || 'Setup fehlgeschlagen'); + throw new Error(result.error || 'Setup fehlgeschlagen'); } - // Check response format - const contentType = response.headers.get('content-type'); - if (!contentType || !contentType.includes('application/json')) { - const text = await response.text(); - console.error('Non-JSON response:', text); - throw new Error('Server returned non-JSON response'); - } + console.log('✅ Setup successful:', result); - const result = await response.json(); - console.log('Setup response:', result); - - // Re-check setup status after successful setup + // WICHTIG: Setup Status neu prüfen und dann zu Login navigieren await checkSetupStatus(); - // Automatically log in after setup - await login({ - email: 'admin@instandhaltung.de', - password: formData.password - }); - - navigate('/'); + // Kurze Verzögerung damit der State aktualisiert werden kann + setTimeout(() => { + navigate('/login'); + }, 100); } catch (err: any) { - console.error('Setup error:', err); + console.error('❌ Setup error:', err); setError(typeof err === 'string' ? err : err.message || 'Ein unerwarteter Fehler ist aufgetreten'); } finally { setLoading(false); } }; - return (
diff --git a/frontend/src/services/employeeService.ts b/frontend/src/services/employeeService.ts index 63039d9..77cb313 100644 --- a/frontend/src/services/employeeService.ts +++ b/frontend/src/services/employeeService.ts @@ -1,127 +1,110 @@ // frontend/src/services/employeeService.ts -import { Employee, Availability, CreateEmployeeRequest, UpdateEmployeeRequest } from '../types/employee'; -import { authService } from './authService'; +import { Employee, CreateEmployeeRequest, UpdateEmployeeRequest, Availability } from '../types/employee'; -const API_BASE = 'http://localhost:3002/api/employees'; +const API_BASE_URL = 'http://localhost:3002/api'; + +const getAuthHeaders = () => { + const token = localStorage.getItem('token'); + return { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + }; +}; export class EmployeeService { - // Alle Mitarbeiter abrufen async getEmployees(): Promise { - const response = await fetch(`${API_BASE}?_=${Date.now()}`, { - headers: { - 'Content-Type': 'application/json', - 'Cache-Control': 'no-cache', - 'Pragma': 'no-cache', - ...authService.getAuthHeaders() - } + const response = await fetch(`${API_BASE_URL}/employees`, { + headers: getAuthHeaders(), }); if (!response.ok) { - throw new Error('Fehler beim Laden der Mitarbeiter'); + throw new Error('Failed to fetch employees'); } return response.json(); } - // Einzelnen Mitarbeiter abrufen async getEmployee(id: string): Promise { - const response = await fetch(`${API_BASE}/${id}`, { - headers: { - 'Content-Type': 'application/json', - ...authService.getAuthHeaders() - } + const response = await fetch(`${API_BASE_URL}/employees/${id}`, { + headers: getAuthHeaders(), }); if (!response.ok) { - throw new Error('Mitarbeiter nicht gefunden'); + throw new Error('Failed to fetch employee'); } return response.json(); } - // Neuen Mitarbeiter erstellen - async createEmployee(employeeData: CreateEmployeeRequest): Promise { - const response = await fetch(API_BASE, { + async createEmployee(employee: CreateEmployeeRequest): Promise { + const response = await fetch(`${API_BASE_URL}/employees`, { method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...authService.getAuthHeaders() - }, - body: JSON.stringify(employeeData) + headers: getAuthHeaders(), + body: JSON.stringify(employee), }); if (!response.ok) { const error = await response.json(); - throw new Error(error.error || 'Fehler beim Erstellen des Mitarbeiters'); + throw new Error(error.error || 'Failed to create employee'); } return response.json(); } - // Mitarbeiter aktualisieren - async updateEmployee(id: string, updates: UpdateEmployeeRequest): Promise { - const response = await fetch(`${API_BASE}/${id}`, { + async updateEmployee(id: string, employee: UpdateEmployeeRequest): Promise { + const response = await fetch(`${API_BASE_URL}/employees/${id}`, { method: 'PUT', - headers: { - 'Content-Type': 'application/json', - ...authService.getAuthHeaders() - }, - body: JSON.stringify(updates) + headers: getAuthHeaders(), + body: JSON.stringify(employee), }); if (!response.ok) { - throw new Error('Fehler beim Aktualisieren des Mitarbeiters'); + const error = await response.json(); + throw new Error(error.error || 'Failed to update employee'); } return response.json(); } - // Mitarbeiter permanent löschen async deleteEmployee(id: string): Promise { - const response = await fetch(`${API_BASE}/${id}`, { + const response = await fetch(`${API_BASE_URL}/employees/${id}`, { method: 'DELETE', - headers: { - ...authService.getAuthHeaders() - } + headers: getAuthHeaders(), }); if (!response.ok) { - const error = await response.json().catch(() => ({ error: 'Fehler beim Löschen des Mitarbeiters' })); - throw new Error(error.error || 'Fehler beim Löschen des Mitarbeiters'); + const error = await response.json(); + throw new Error(error.error || 'Failed to delete employee'); } } - // Verfügbarkeiten abrufen async getAvailabilities(employeeId: string): Promise { - const response = await fetch(`${API_BASE}/${employeeId}/availabilities`, { - headers: { - 'Content-Type': 'application/json', - ...authService.getAuthHeaders() - } + const response = await fetch(`${API_BASE_URL}/employees/${employeeId}/availabilities`, { + headers: getAuthHeaders(), }); if (!response.ok) { - throw new Error('Fehler beim Laden der Verfügbarkeiten'); + throw new Error('Failed to fetch availabilities'); } return response.json(); } - // Verfügbarkeiten aktualisieren async updateAvailabilities(employeeId: string, availabilities: Availability[]): Promise { - const response = await fetch(`${API_BASE}/${employeeId}/availabilities`, { + const response = await fetch(`${API_BASE_URL}/employees/${employeeId}/availabilities`, { method: 'PUT', - headers: { - 'Content-Type': 'application/json', - ...authService.getAuthHeaders() - }, - body: JSON.stringify(availabilities) + headers: getAuthHeaders(), + body: JSON.stringify(availabilities), }); if (!response.ok) { - throw new Error('Fehler beim Aktualisieren der Verfügbarkeiten'); + const error = await response.json(); + throw new Error(error.error || 'Failed to update availabilities'); } return response.json(); } -}; \ No newline at end of file +} + +// ✅ Exportiere eine Instanz der Klasse +export const employeeService = new EmployeeService(); \ No newline at end of file diff --git a/frontend/src/types/user.ts b/frontend/src/types/user.ts new file mode 100644 index 0000000..4bcf5bb --- /dev/null +++ b/frontend/src/types/user.ts @@ -0,0 +1,15 @@ +// frontend/src/types/user.ts +export interface User { + id: string; + email: string; + name: string; + role: 'admin' | 'instandhalter' | 'user'; + phone?: string; + department?: string; + lastLogin?: string; +} + +export interface LoginRequest { + email: string; + password: string; +} \ No newline at end of file