fixed login

This commit is contained in:
2025-10-09 16:37:43 +02:00
parent adc47c2480
commit 4dcff0f70e
10 changed files with 354 additions and 234 deletions

View File

@@ -3,9 +3,8 @@
"version": "1.0.0", "version": "1.0.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "node --loader ts-node/esm src/server.ts", "dev": "npx tsx src/server.ts",
"simple": "node src/server.ts", "build": "tsc",
"build": "tsc",
"start": "node dist/server.js" "start": "node dist/server.js"
}, },
"dependencies": { "dependencies": {

View File

@@ -42,29 +42,38 @@ export const login = async (req: Request, res: Response) => {
try { try {
const { email, password } = req.body as LoginRequest; const { email, password } = req.body as LoginRequest;
console.log('🔐 Login attempt for email:', email);
if (!email || !password) { if (!email || !password) {
console.log('❌ Missing email or password');
return res.status(400).json({ error: 'E-Mail und Passwort sind erforderlich' }); return res.status(400).json({ error: 'E-Mail und Passwort sind erforderlich' });
} }
// Get user from database // Get user from database
const user = await db.get<UserWithPassword>( const user = await db.get<UserWithPassword>(
'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] [email]
); );
console.log('🔍 User found:', user ? 'Yes' : 'No');
if (!user) { if (!user) {
console.log('❌ No user found with email:', email);
return res.status(401).json({ error: 'Ungültige Anmeldedaten' }); return res.status(401).json({ error: 'Ungültige Anmeldedaten' });
} }
// Verify password // Verify password
const validPassword = await bcrypt.compare(password, user.password); const validPassword = await bcrypt.compare(password, user.password);
console.log('🔑 Password valid:', validPassword);
if (!validPassword) { if (!validPassword) {
console.log('❌ Invalid password for user:', email);
return res.status(401).json({ error: 'Ungültige Anmeldedaten' }); return res.status(401).json({ error: 'Ungültige Anmeldedaten' });
} }
// Create token payload // Create token payload - ID als STRING verwenden
const tokenPayload: JWTPayload = { const tokenPayload: JWTPayload = {
id: user.id.toString(), // ← Sicherstellen dass es string ist id: user.id.toString(), // ← WICHTIG: Als string
email: user.email, email: user.email,
role: user.role role: user.role
}; };
@@ -79,6 +88,8 @@ export const login = async (req: Request, res: Response) => {
// Remove password from user object // Remove password from user object
const { password: _, ...userWithoutPassword } = user; const { password: _, ...userWithoutPassword } = user;
console.log('✅ Login successful for:', user.email);
res.json({ res.json({
user: userWithoutPassword, user: userWithoutPassword,
token token
@@ -92,19 +103,26 @@ export const login = async (req: Request, res: Response) => {
export const getCurrentUser = async (req: Request, res: Response) => { export const getCurrentUser = async (req: Request, res: Response) => {
try { try {
const jwtUser = (req as any).user as JWTPayload; const jwtUser = (req as any).user as JWTPayload;
console.log('🔍 Getting current user for ID:', jwtUser?.id);
if (!jwtUser?.id) { if (!jwtUser?.id) {
console.log('❌ No user ID in JWT');
return res.status(401).json({ error: 'Nicht authentifiziert' }); return res.status(401).json({ error: 'Nicht authentifiziert' });
} }
const user = await db.get<User>( const user = await db.get<User>(
'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] [jwtUser.id]
); );
console.log('🔍 User found in database:', user ? 'Yes' : 'No');
if (!user) { if (!user) {
console.log('❌ User not found in database for ID:', jwtUser.id);
return res.status(404).json({ error: 'Benutzer nicht gefunden' }); return res.status(404).json({ error: 'Benutzer nicht gefunden' });
} }
console.log('✅ Returning user:', user.email);
res.json({ user }); res.json({ user });
} catch (error) { } catch (error) {
console.error('Get current user error:', error); console.error('Get current user error:', error);

View File

@@ -5,6 +5,25 @@ import bcrypt from 'bcryptjs';
import { db } from '../services/databaseService.js'; import { db } from '../services/databaseService.js';
import { AuthRequest } from '../middleware/auth.js'; import { AuthRequest } from '../middleware/auth.js';
export const getEmployees = async (req: AuthRequest, res: Response): Promise<void> => {
try {
const employees = await db.all<any>(`
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<void> => { export const getEmployee = async (req: AuthRequest, res: Response): Promise<void> => {
try { try {
const { id } = req.params; const { id } = req.params;

View File

@@ -1,18 +1,24 @@
// backend/src/controllers/setupController.ts // backend/src/controllers/setupController.ts
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import bcrypt from 'bcrypt'; import bcrypt from 'bcrypt';
import { v4 as uuidv4 } from 'uuid';
import { randomUUID } from 'crypto'; import { randomUUID } from 'crypto';
import { db } from '../services/databaseService.js'; import { db } from '../services/databaseService.js';
export const checkSetupStatus = async (req: Request, res: Response): Promise<void> => { export const checkSetupStatus = async (req: Request, res: Response): Promise<void> => {
try { try {
const adminExists = await db.get<{ 'COUNT(*)': number }>( const adminExists = await db.get<{ 'COUNT(*)': number }>(
'SELECT COUNT(*) FROM users WHERE role = ?', 'SELECT COUNT(*) FROM users WHERE role = ? AND is_active = 1',
['admin'] ['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({ res.json({
needsSetup: !adminExists || adminExists['COUNT(*)'] === 0 needsSetup: needsSetup
}); });
} catch (error) { } catch (error) {
console.error('Error checking setup status:', error); console.error('Error checking setup status:', error);
@@ -26,11 +32,14 @@ export const setupAdmin = async (req: Request, res: Response): Promise<void> =>
try { try {
// Check if admin already exists // Check if admin already exists
const adminExists = await db.get<{ 'COUNT(*)': number }>( const adminExists = await db.get<{ 'COUNT(*)': number }>(
'SELECT COUNT(*) FROM users WHERE role = ?', 'SELECT COUNT(*) FROM users WHERE role = ? AND is_active = 1',
['admin'] ['admin']
); );
console.log('🔍 Admin exists check:', adminExists);
if (adminExists && adminExists['COUNT(*)'] > 0) { if (adminExists && adminExists['COUNT(*)'] > 0) {
console.log('❌ Admin already exists');
res.status(400).json({ error: 'Admin existiert bereits' }); res.status(400).json({ error: 'Admin existiert bereits' });
return; return;
} }
@@ -38,6 +47,8 @@ export const setupAdmin = async (req: Request, res: Response): Promise<void> =>
const { password, name, phone, department } = req.body; const { password, name, phone, department } = req.body;
const email = 'admin@instandhaltung.de'; // Fixed admin email const email = 'admin@instandhaltung.de'; // Fixed admin email
console.log('👤 Creating admin with data:', { name, email, phone, department });
// Validation // Validation
if (!password || !name) { if (!password || !name) {
res.status(400).json({ error: 'Passwort und Name sind erforderlich' }); res.status(400).json({ error: 'Passwort und Name sind erforderlich' });
@@ -52,29 +63,33 @@ export const setupAdmin = async (req: Request, res: Response): Promise<void> =>
// Hash password // Hash password
const hashedPassword = await bcrypt.hash(password, 10); const hashedPassword = await bcrypt.hash(password, 10);
const adminId = randomUUID(); const adminId = uuidv4();
console.log('📝 Inserting admin user with ID:', adminId);
try { try {
// Create admin user // Create admin user
await db.run( await db.run(
`INSERT INTO users (id, email, password, name, role, phone, department, is_active) `INSERT INTO users (id, email, password, name, role, phone, department, is_active)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, 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({ res.status(201).json({
success: true, success: true,
message: 'Admin erfolgreich erstellt', message: 'Admin erfolgreich erstellt',
email: email email: email
}); });
} catch (dbError) { } catch (dbError) {
console.error('Database error during admin creation:', dbError); console.error('Database error during admin creation:', dbError);
res.status(500).json({ res.status(500).json({
error: 'Fehler beim Erstellen des Admin-Accounts' error: 'Fehler beim Erstellen des Admin-Accounts'
}); });
} }
} catch (error) { } catch (error) {
console.error('Error in setup:', error); console.error('Error in setup:', error);
res.status(500).json({ res.status(500).json({
error: 'Ein unerwarteter Fehler ist aufgetreten' error: 'Ein unerwarteter Fehler ist aufgetreten'
}); });

View File

@@ -1,7 +1,6 @@
// backend/src/middleware/auth.ts // backend/src/middleware/auth.ts
import { Request, Response, NextFunction } from 'express'; import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import { JWTPayload } from '../controllers/authController.js';
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key'; 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 => { 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) { if (!token) {
console.log('❌ No token provided');
res.status(401).json({ error: 'Access denied. No token provided.' }); res.status(401).json({ error: 'Access denied. No token provided.' });
return; return;
} }
try { 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 = { req.user = {
userId: decoded.id, userId: decoded.id,
email: decoded.email, email: decoded.email,
@@ -30,6 +35,7 @@ export const authMiddleware = (req: AuthRequest, res: Response, next: NextFuncti
}; };
next(); next();
} catch (error) { } catch (error) {
console.error('❌ Invalid token:', error);
res.status(400).json({ error: 'Invalid token.' }); 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[]) => { export const requireRole = (roles: string[]) => {
return (req: AuthRequest, res: Response, next: NextFunction): void => { return (req: AuthRequest, res: Response, next: NextFunction): void => {
if (!req.user || !roles.includes(req.user.role)) { 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.' }); res.status(403).json({ error: 'Access denied. Insufficient permissions.' });
return; return;
} }

View File

@@ -14,7 +14,7 @@ import Settings from './pages/Settings/Settings';
import Help from './pages/Help/Help'; import Help from './pages/Help/Help';
import Setup from './pages/Setup/Setup'; 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[] }> = ({ const ProtectedRoute: React.FC<{ children: React.ReactNode; roles?: string[] }> = ({
children, children,
roles = ['admin', 'instandhalter', 'user'] roles = ['admin', 'instandhalter', 'user']
@@ -47,6 +47,89 @@ const ProtectedRoute: React.FC<{ children: React.ReactNode; roles?: string[] }>
return <Layout>{children}</Layout>; return <Layout>{children}</Layout>;
}; };
// SetupWrapper Component
const SetupWrapper: React.FC = () => {
return (
<Router>
<Setup />
</Router>
);
};
// LoginWrapper Component
const LoginWrapper: React.FC = () => {
return (
<Router>
<Login />
</Router>
);
};
// Main App Content
const AppContent: React.FC = () => {
const { loading, needsSetup, user } = useAuth();
if (loading) {
return (
<div style={{ textAlign: 'center', padding: '40px' }}>
<div> Lade Anwendung...</div>
</div>
);
}
console.log('AppContent - needsSetup:', needsSetup, 'user:', user);
// Wenn Setup benötigt wird → Setup zeigen (mit Router)
if (needsSetup) {
return <SetupWrapper />;
}
// Wenn kein User eingeloggt ist → Login zeigen (mit Router)
if (!user) {
return <LoginWrapper />;
}
// Wenn User eingeloggt ist → Geschützte Routen zeigen
return (
<Router>
<NotificationContainer />
<Routes>
<Route path="/" element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
} />
<Route path="/shift-plans" element={
<ProtectedRoute>
<ShiftPlanList />
</ProtectedRoute>
} />
<Route path="/shift-plans/new" element={
<ProtectedRoute roles={['admin', 'instandhalter']}>
<ShiftPlanCreate />
</ProtectedRoute>
} />
<Route path="/employees" element={
<ProtectedRoute roles={['admin', 'instandhalter']}>
<EmployeeManagement />
</ProtectedRoute>
} />
<Route path="/settings" element={
<ProtectedRoute roles={['admin']}>
<Settings />
</ProtectedRoute>
} />
<Route path="/help" element={
<ProtectedRoute>
<Help />
</ProtectedRoute>
} />
<Route path="/login" element={<Login />} />
</Routes>
</Router>
);
};
function App() { function App() {
return ( return (
<NotificationProvider> <NotificationProvider>
@@ -57,61 +140,4 @@ function App() {
); );
} }
function AppContent() {
const { loading, needsSetup } = useAuth();
if (loading) {
return (
<div style={{ textAlign: 'center', padding: '40px' }}>
<div> Lade Anwendung...</div>
</div>
);
}
return (
<Router>
<NotificationContainer />
<Routes>
{needsSetup ? (
<Route path="*" element={<Setup />} />
) : (
<>
<Route path="/login" element={<Login />} />
<Route path="/" element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
} />
<Route path="/shift-plans" element={
<ProtectedRoute>
<ShiftPlanList />
</ProtectedRoute>
} />
<Route path="/shift-plans/new" element={
<ProtectedRoute roles={['admin', 'instandhalter']}>
<ShiftPlanCreate />
</ProtectedRoute>
} />
<Route path="/employees" element={
<ProtectedRoute roles={['admin', 'instandhalter']}>
<EmployeeManagement />
</ProtectedRoute>
} />
<Route path="/settings" element={
<ProtectedRoute roles={['admin']}>
<Settings />
</ProtectedRoute>
} />
<Route path="/help" element={
<ProtectedRoute>
<Help />
</ProtectedRoute>
} />
</>
)}
</Routes>
</Router>
);
}
export default App; export default App;

View File

@@ -1,9 +1,14 @@
// frontend/src/contexts/AuthContext.tsx // frontend/src/contexts/AuthContext.tsx
import React, { createContext, useContext, useState, useEffect } from 'react'; import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
import { authService, User, LoginRequest } from '../services/authService'; import { Employee } from '../types/employee';
interface LoginRequest {
email: string;
password: string;
}
interface AuthContextType { interface AuthContextType {
user: User | null; user: Employee | null;
login: (credentials: LoginRequest) => Promise<void>; login: (credentials: LoginRequest) => Promise<void>;
logout: () => void; logout: () => void;
hasRole: (roles: string[]) => boolean; hasRole: (roles: string[]) => boolean;
@@ -15,102 +20,130 @@ interface AuthContextType {
const AuthContext = createContext<AuthContextType | undefined>(undefined); const AuthContext = createContext<AuthContextType | undefined>(undefined);
export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { interface AuthProviderProps {
const [user, setUser] = useState<User | null>(null); children: ReactNode;
}
export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
const [user, setUser] = useState<Employee | null>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [needsSetup, setNeedsSetup] = useState(false); 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<void> => {
try { try {
const response = await fetch('/api/setup/status'); const response = await fetch('http://localhost:3002/api/setup/status');
if (!response.ok) { if (!response.ok) {
throw new Error('Failed to check setup status'); throw new Error('Setup status check failed');
} }
const data = await response.json(); const data = await response.json();
setNeedsSetup(data.needsSetup); console.log('Setup status response:', data);
setNeedsSetup(data.needsSetup === true);
} catch (error) { } catch (error) {
console.error('Error checking setup status:', error); console.error('Error checking setup status:', error);
// If we can't reach the server, assume setup is needed setNeedsSetup(false);
setNeedsSetup(true);
} }
}; };
// Check setup status and load user on mount const refreshUser = async () => {
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) => {
try { try {
console.log('🔄 Attempting login...'); const token = getStoredToken();
const response = await authService.login(credentials); console.log('🔄 Refreshing user, token exists:', !!token);
setUser(response.user);
console.log('✅ Login successful, user set:', response.user.email);
// Force refresh der App
setRefreshTrigger(prev => prev + 1);
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) { } 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<void> => {
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; throw error;
} }
}; };
const logout = () => { const logout = () => {
authService.logout(); console.log('🚪 Logging out user');
removeStoredToken();
setUser(null); setUser(null);
console.log('✅ Logout completed');
}; };
const refreshUser = () => { const hasRole = (roles: string[]): boolean => {
setRefreshTrigger(prev => prev + 1); if (!user) return false;
return roles.includes(user.role);
}; };
const hasRole = (roles: string[]) => { useEffect(() => {
return user ? roles.includes(user.role) : false; const initializeAuth = async () => {
}; console.log('🚀 Initializing authentication...');
await checkSetupStatus();
await refreshUser();
};
const value = { initializeAuth();
}, []);
const value: AuthContextType = {
user, user,
login, login,
logout, logout,
@@ -118,9 +151,18 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
loading, loading,
refreshUser, refreshUser,
needsSetup, 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 ( return (
<AuthContext.Provider value={value}> <AuthContext.Provider value={value}>
{children} {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); const context = useContext(AuthContext);
if (context === undefined) { if (context === undefined) {
throw new Error('useAuth must be used within an AuthProvider'); throw new Error('useAuth must be used within an AuthProvider');

View File

@@ -64,7 +64,6 @@ const Setup: React.FC = () => {
setLoading(true); setLoading(true);
setError(''); setError('');
// Create the request payload
const payload = { const payload = {
password: formData.password, password: formData.password,
name: formData.name, name: formData.name,
@@ -72,7 +71,7 @@ const Setup: React.FC = () => {
...(formData.department ? { department: formData.department } : {}) ...(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', { const response = await fetch('http://localhost:3002/api/setup/admin', {
method: 'POST', method: 'POST',
@@ -82,41 +81,38 @@ const Setup: React.FC = () => {
body: JSON.stringify(payload), 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) { if (!response.ok) {
const data = await response.json(); throw new Error(result.error || 'Setup fehlgeschlagen');
throw new Error(data.error || 'Setup fehlgeschlagen');
} }
// Check response format console.log('✅ Setup successful:', result);
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');
}
const result = await response.json(); // WICHTIG: Setup Status neu prüfen und dann zu Login navigieren
console.log('Setup response:', result);
// Re-check setup status after successful setup
await checkSetupStatus(); await checkSetupStatus();
// Automatically log in after setup // Kurze Verzögerung damit der State aktualisiert werden kann
await login({ setTimeout(() => {
email: 'admin@instandhaltung.de', navigate('/login');
password: formData.password }, 100);
});
navigate('/');
} catch (err: any) { } 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'); setError(typeof err === 'string' ? err : err.message || 'Ein unerwarteter Fehler ist aufgetreten');
} finally { } finally {
setLoading(false); setLoading(false);
} }
}; };
return ( return (
<div className="min-h-screen bg-gray-100 flex items-center justify-center"> <div className="min-h-screen bg-gray-100 flex items-center justify-center">
<div className="max-w-md w-full bg-white rounded-lg shadow-lg p-8"> <div className="max-w-md w-full bg-white rounded-lg shadow-lg p-8">

View File

@@ -1,127 +1,110 @@
// frontend/src/services/employeeService.ts // frontend/src/services/employeeService.ts
import { Employee, Availability, CreateEmployeeRequest, UpdateEmployeeRequest } from '../types/employee'; import { Employee, CreateEmployeeRequest, UpdateEmployeeRequest, Availability } from '../types/employee';
import { authService } from './authService';
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 { export class EmployeeService {
// Alle Mitarbeiter abrufen
async getEmployees(): Promise<Employee[]> { async getEmployees(): Promise<Employee[]> {
const response = await fetch(`${API_BASE}?_=${Date.now()}`, { const response = await fetch(`${API_BASE_URL}/employees`, {
headers: { headers: getAuthHeaders(),
'Content-Type': 'application/json',
'Cache-Control': 'no-cache',
'Pragma': 'no-cache',
...authService.getAuthHeaders()
}
}); });
if (!response.ok) { if (!response.ok) {
throw new Error('Fehler beim Laden der Mitarbeiter'); throw new Error('Failed to fetch employees');
} }
return response.json(); return response.json();
} }
// Einzelnen Mitarbeiter abrufen
async getEmployee(id: string): Promise<Employee> { async getEmployee(id: string): Promise<Employee> {
const response = await fetch(`${API_BASE}/${id}`, { const response = await fetch(`${API_BASE_URL}/employees/${id}`, {
headers: { headers: getAuthHeaders(),
'Content-Type': 'application/json',
...authService.getAuthHeaders()
}
}); });
if (!response.ok) { if (!response.ok) {
throw new Error('Mitarbeiter nicht gefunden'); throw new Error('Failed to fetch employee');
} }
return response.json(); return response.json();
} }
// Neuen Mitarbeiter erstellen async createEmployee(employee: CreateEmployeeRequest): Promise<Employee> {
async createEmployee(employeeData: CreateEmployeeRequest): Promise<Employee> { const response = await fetch(`${API_BASE_URL}/employees`, {
const response = await fetch(API_BASE, {
method: 'POST', method: 'POST',
headers: { headers: getAuthHeaders(),
'Content-Type': 'application/json', body: JSON.stringify(employee),
...authService.getAuthHeaders()
},
body: JSON.stringify(employeeData)
}); });
if (!response.ok) { if (!response.ok) {
const error = await response.json(); 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(); return response.json();
} }
// Mitarbeiter aktualisieren async updateEmployee(id: string, employee: UpdateEmployeeRequest): Promise<Employee> {
async updateEmployee(id: string, updates: UpdateEmployeeRequest): Promise<Employee> { const response = await fetch(`${API_BASE_URL}/employees/${id}`, {
const response = await fetch(`${API_BASE}/${id}`, {
method: 'PUT', method: 'PUT',
headers: { headers: getAuthHeaders(),
'Content-Type': 'application/json', body: JSON.stringify(employee),
...authService.getAuthHeaders()
},
body: JSON.stringify(updates)
}); });
if (!response.ok) { 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(); return response.json();
} }
// Mitarbeiter permanent löschen
async deleteEmployee(id: string): Promise<void> { async deleteEmployee(id: string): Promise<void> {
const response = await fetch(`${API_BASE}/${id}`, { const response = await fetch(`${API_BASE_URL}/employees/${id}`, {
method: 'DELETE', method: 'DELETE',
headers: { headers: getAuthHeaders(),
...authService.getAuthHeaders()
}
}); });
if (!response.ok) { if (!response.ok) {
const error = await response.json().catch(() => ({ error: 'Fehler beim Löschen des Mitarbeiters' })); const error = await response.json();
throw new Error(error.error || 'Fehler beim Löschen des Mitarbeiters'); throw new Error(error.error || 'Failed to delete employee');
} }
} }
// Verfügbarkeiten abrufen
async getAvailabilities(employeeId: string): Promise<Availability[]> { async getAvailabilities(employeeId: string): Promise<Availability[]> {
const response = await fetch(`${API_BASE}/${employeeId}/availabilities`, { const response = await fetch(`${API_BASE_URL}/employees/${employeeId}/availabilities`, {
headers: { headers: getAuthHeaders(),
'Content-Type': 'application/json',
...authService.getAuthHeaders()
}
}); });
if (!response.ok) { if (!response.ok) {
throw new Error('Fehler beim Laden der Verfügbarkeiten'); throw new Error('Failed to fetch availabilities');
} }
return response.json(); return response.json();
} }
// Verfügbarkeiten aktualisieren
async updateAvailabilities(employeeId: string, availabilities: Availability[]): Promise<Availability[]> { async updateAvailabilities(employeeId: string, availabilities: Availability[]): Promise<Availability[]> {
const response = await fetch(`${API_BASE}/${employeeId}/availabilities`, { const response = await fetch(`${API_BASE_URL}/employees/${employeeId}/availabilities`, {
method: 'PUT', method: 'PUT',
headers: { headers: getAuthHeaders(),
'Content-Type': 'application/json', body: JSON.stringify(availabilities),
...authService.getAuthHeaders()
},
body: JSON.stringify(availabilities)
}); });
if (!response.ok) { 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(); return response.json();
} }
}; }
// ✅ Exportiere eine Instanz der Klasse
export const employeeService = new EmployeeService();

View File

@@ -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;
}