mirror of
https://github.com/donpat1to/Schichtenplaner.git
synced 2025-12-01 06:55:45 +01:00
added changing password frontend backend
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
// frontend/src/contexts/AuthContext.tsx
|
||||
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
|
||||
import { Employee } from '../models/Employee';
|
||||
|
||||
@@ -15,6 +16,7 @@ interface AuthContextType {
|
||||
refreshUser: () => void;
|
||||
needsSetup: boolean;
|
||||
checkSetupStatus: () => Promise<void>;
|
||||
updateUser: (userData: Employee) => void; // Add this line
|
||||
}
|
||||
|
||||
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
||||
@@ -26,7 +28,7 @@ interface AuthProviderProps {
|
||||
export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
|
||||
const [user, setUser] = useState<Employee | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [needsSetup, setNeedsSetup] = useState<boolean | null>(null); // ← Start mit null
|
||||
const [needsSetup, setNeedsSetup] = useState<boolean | null>(null);
|
||||
|
||||
// Token aus localStorage laden
|
||||
const getStoredToken = (): string | null => {
|
||||
@@ -55,7 +57,7 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
|
||||
setNeedsSetup(data.needsSetup === true);
|
||||
} catch (error) {
|
||||
console.error('❌ Error checking setup status:', error);
|
||||
setNeedsSetup(true); // Bei Fehler Setup annehmen
|
||||
setNeedsSetup(true);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -92,6 +94,12 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Add the updateUser function
|
||||
const updateUser = (userData: Employee) => {
|
||||
console.log('🔄 Updating user in auth context:', userData);
|
||||
setUser(userData);
|
||||
};
|
||||
|
||||
const login = async (credentials: LoginRequest): Promise<void> => {
|
||||
try {
|
||||
console.log('🔐 Attempting login for:', credentials.email);
|
||||
@@ -112,7 +120,6 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
|
||||
const data = await response.json();
|
||||
console.log('✅ Login successful, storing token');
|
||||
|
||||
// Token persistent speichern
|
||||
setStoredToken(data.token);
|
||||
setUser(data.user);
|
||||
} catch (error) {
|
||||
@@ -156,8 +163,9 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
|
||||
hasRole,
|
||||
loading,
|
||||
refreshUser,
|
||||
needsSetup: needsSetup === null ? true : needsSetup, // Falls null, true annehmen
|
||||
needsSetup: needsSetup === null ? true : needsSetup,
|
||||
checkSetupStatus,
|
||||
updateUser, // Add this to the context value
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,26 +1,541 @@
|
||||
// frontend/src/pages/Settings/Settings.tsx
|
||||
import React from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useAuth } from '../../contexts/AuthContext';
|
||||
import { employeeService } from '../../services/employeeService';
|
||||
import { useNotification } from '../../contexts/NotificationContext';
|
||||
import AvailabilityManager from '../Employees/components/AvailabilityManager';
|
||||
import { Employee } from '../../models/Employee';
|
||||
|
||||
const Settings: React.FC = () => {
|
||||
const { user: currentUser, updateUser } = useAuth();
|
||||
const { showNotification } = useNotification();
|
||||
const [activeTab, setActiveTab] = useState<'profile' | 'password' | 'availability'>('profile');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [showAvailabilityManager, setShowAvailabilityManager] = useState(false);
|
||||
|
||||
// Profile form state
|
||||
const [profileForm, setProfileForm] = useState({
|
||||
name: currentUser?.name || ''
|
||||
});
|
||||
|
||||
// Password form state
|
||||
const [passwordForm, setPasswordForm] = useState({
|
||||
currentPassword: '',
|
||||
newPassword: '',
|
||||
confirmPassword: ''
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (currentUser) {
|
||||
setProfileForm({
|
||||
name: currentUser.name
|
||||
});
|
||||
}
|
||||
}, [currentUser]);
|
||||
|
||||
const handleProfileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { name, value } = e.target;
|
||||
setProfileForm(prev => ({
|
||||
...prev,
|
||||
[name]: value
|
||||
}));
|
||||
};
|
||||
|
||||
const handlePasswordChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { name, value } = e.target;
|
||||
setPasswordForm(prev => ({
|
||||
...prev,
|
||||
[name]: value
|
||||
}));
|
||||
};
|
||||
|
||||
const handleProfileUpdate = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!currentUser) return;
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
await employeeService.updateEmployee(currentUser.id, {
|
||||
name: profileForm.name.trim()
|
||||
});
|
||||
|
||||
// Update the auth context with new user data
|
||||
const updatedUser = await employeeService.getEmployee(currentUser.id);
|
||||
updateUser(updatedUser);
|
||||
|
||||
showNotification({
|
||||
type: 'success',
|
||||
title: 'Erfolg',
|
||||
message: 'Profil erfolgreich aktualisiert'
|
||||
});
|
||||
} catch (error: any) {
|
||||
showNotification({
|
||||
type: 'error',
|
||||
title: 'Fehler',
|
||||
message: error.message || 'Profil konnte nicht aktualisiert werden'
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePasswordUpdate = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!currentUser) return;
|
||||
|
||||
// Validation
|
||||
if (passwordForm.newPassword.length < 6) {
|
||||
showNotification({
|
||||
type: 'error',
|
||||
title: 'Fehler',
|
||||
message: 'Das neue Passwort muss mindestens 6 Zeichen lang sein'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (passwordForm.newPassword !== passwordForm.confirmPassword) {
|
||||
showNotification({
|
||||
type: 'error',
|
||||
title: 'Fehler',
|
||||
message: 'Die Passwörter stimmen nicht überein'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
// Use the actual password change endpoint
|
||||
await employeeService.changePassword(currentUser.id, {
|
||||
currentPassword: passwordForm.currentPassword,
|
||||
newPassword: passwordForm.newPassword
|
||||
});
|
||||
|
||||
showNotification({
|
||||
type: 'success',
|
||||
title: 'Erfolg',
|
||||
message: 'Passwort erfolgreich geändert'
|
||||
});
|
||||
|
||||
// Clear password form
|
||||
setPasswordForm({
|
||||
currentPassword: '',
|
||||
newPassword: '',
|
||||
confirmPassword: ''
|
||||
});
|
||||
} catch (error: any) {
|
||||
showNotification({
|
||||
type: 'error',
|
||||
title: 'Fehler',
|
||||
message: error.message || 'Passwort konnte nicht geändert werden'
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAvailabilitySave = () => {
|
||||
setShowAvailabilityManager(false);
|
||||
showNotification({
|
||||
type: 'success',
|
||||
title: 'Erfolg',
|
||||
message: 'Verfügbarkeit erfolgreich gespeichert'
|
||||
});
|
||||
};
|
||||
|
||||
const handleAvailabilityCancel = () => {
|
||||
setShowAvailabilityManager(false);
|
||||
};
|
||||
|
||||
if (!currentUser) {
|
||||
return <div>Nicht eingeloggt</div>;
|
||||
}
|
||||
|
||||
if (showAvailabilityManager) {
|
||||
return (
|
||||
<AvailabilityManager
|
||||
employee={currentUser as Employee}
|
||||
onSave={handleAvailabilitySave}
|
||||
onCancel={handleAvailabilityCancel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={{ padding: '20px', maxWidth: '800px', margin: '0 auto' }}>
|
||||
<h1>⚙️ Einstellungen</h1>
|
||||
|
||||
{/* Tab Navigation */}
|
||||
<div style={{
|
||||
padding: '40px',
|
||||
textAlign: 'center',
|
||||
backgroundColor: '#f8f9fa',
|
||||
borderRadius: '8px',
|
||||
border: '2px dashed #dee2e6',
|
||||
marginTop: '20px'
|
||||
display: 'flex',
|
||||
borderBottom: '1px solid #e0e0e0',
|
||||
marginBottom: '30px'
|
||||
}}>
|
||||
<div style={{ fontSize: '48px', marginBottom: '20px' }}>⚙️</div>
|
||||
<h3>System Einstellungen</h3>
|
||||
<p>Hier können Sie Systemweite Einstellungen vornehmen.</p>
|
||||
<p style={{ fontSize: '14px', color: '#6c757d' }}>
|
||||
Diese Seite wird demnächst mit Funktionen gefüllt.
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setActiveTab('profile')}
|
||||
style={{
|
||||
padding: '12px 24px',
|
||||
backgroundColor: activeTab === 'profile' ? '#3498db' : 'transparent',
|
||||
color: activeTab === 'profile' ? 'white' : '#333',
|
||||
border: 'none',
|
||||
borderBottom: activeTab === 'profile' ? '3px solid #3498db' : 'none',
|
||||
cursor: 'pointer',
|
||||
fontWeight: 'bold'
|
||||
}}
|
||||
>
|
||||
👤 Profil
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('password')}
|
||||
style={{
|
||||
padding: '12px 24px',
|
||||
backgroundColor: activeTab === 'password' ? '#3498db' : 'transparent',
|
||||
color: activeTab === 'password' ? 'white' : '#333',
|
||||
border: 'none',
|
||||
borderBottom: activeTab === 'password' ? '3px solid #3498db' : 'none',
|
||||
cursor: 'pointer',
|
||||
fontWeight: 'bold'
|
||||
}}
|
||||
>
|
||||
🔒 Passwort
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('availability')}
|
||||
style={{
|
||||
padding: '12px 24px',
|
||||
backgroundColor: activeTab === 'availability' ? '#3498db' : 'transparent',
|
||||
color: activeTab === 'availability' ? 'white' : '#333',
|
||||
border: 'none',
|
||||
borderBottom: activeTab === 'availability' ? '3px solid #3498db' : 'none',
|
||||
cursor: 'pointer',
|
||||
fontWeight: 'bold'
|
||||
}}
|
||||
>
|
||||
📅 Verfügbarkeit
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Profile Tab */}
|
||||
{activeTab === 'profile' && (
|
||||
<div style={{
|
||||
backgroundColor: 'white',
|
||||
padding: '30px',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid #e0e0e0',
|
||||
boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
|
||||
}}>
|
||||
<h2 style={{ marginTop: 0, color: '#2c3e50' }}>Profilinformationen</h2>
|
||||
|
||||
<form onSubmit={handleProfileUpdate}>
|
||||
<div style={{ display: 'grid', gap: '20px' }}>
|
||||
{/* Read-only information */}
|
||||
<div style={{
|
||||
padding: '15px',
|
||||
backgroundColor: '#f8f9fa',
|
||||
borderRadius: '6px',
|
||||
border: '1px solid #e9ecef'
|
||||
}}>
|
||||
<h4 style={{ margin: '0 0 15px 0', color: '#495057' }}>Systeminformationen</h4>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '15px' }}>
|
||||
<div>
|
||||
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold', color: '#2c3e50' }}>
|
||||
E-Mail
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
value={currentUser.email}
|
||||
disabled
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '10px',
|
||||
border: '1px solid #ddd',
|
||||
borderRadius: '4px',
|
||||
backgroundColor: '#f8f9fa',
|
||||
color: '#666'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold', color: '#2c3e50' }}>
|
||||
Rolle
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={currentUser.role}
|
||||
disabled
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '10px',
|
||||
border: '1px solid #ddd',
|
||||
borderRadius: '4px',
|
||||
backgroundColor: '#f8f9fa',
|
||||
color: '#666'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '15px', marginTop: '15px' }}>
|
||||
<div>
|
||||
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold', color: '#2c3e50' }}>
|
||||
Mitarbeiter Typ
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={currentUser.employeeType}
|
||||
disabled
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '10px',
|
||||
border: '1px solid #ddd',
|
||||
borderRadius: '4px',
|
||||
backgroundColor: '#f8f9fa',
|
||||
color: '#666'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold', color: '#2c3e50' }}>
|
||||
Vertragstyp
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={currentUser.contractType}
|
||||
disabled
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '10px',
|
||||
border: '1px solid #ddd',
|
||||
borderRadius: '4px',
|
||||
backgroundColor: '#f8f9fa',
|
||||
color: '#666'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Editable name */}
|
||||
<div>
|
||||
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 'bold', color: '#2c3e50' }}>
|
||||
Vollständiger Name *
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
value={profileForm.name}
|
||||
onChange={handleProfileChange}
|
||||
required
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '10px',
|
||||
border: '1px solid #ddd',
|
||||
borderRadius: '4px',
|
||||
fontSize: '16px'
|
||||
}}
|
||||
placeholder="Ihr vollständiger Name"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
gap: '15px',
|
||||
justifyContent: 'flex-end',
|
||||
marginTop: '30px',
|
||||
paddingTop: '20px',
|
||||
borderTop: '1px solid #f0f0f0'
|
||||
}}>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading || !profileForm.name.trim()}
|
||||
style={{
|
||||
padding: '12px 24px',
|
||||
backgroundColor: loading ? '#bdc3c7' : (!profileForm.name.trim() ? '#95a5a6' : '#27ae60'),
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: '6px',
|
||||
cursor: (loading || !profileForm.name.trim()) ? 'not-allowed' : 'pointer',
|
||||
fontWeight: 'bold'
|
||||
}}
|
||||
>
|
||||
{loading ? '⏳ Wird gespeichert...' : 'Profil aktualisieren'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Password Tab */}
|
||||
{activeTab === 'password' && (
|
||||
<div style={{
|
||||
backgroundColor: 'white',
|
||||
padding: '30px',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid #e0e0e0',
|
||||
boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
|
||||
}}>
|
||||
<h2 style={{ marginTop: 0, color: '#2c3e50' }}>Passwort ändern</h2>
|
||||
|
||||
<form onSubmit={handlePasswordUpdate}>
|
||||
<div style={{ display: 'grid', gap: '20px', maxWidth: '400px' }}>
|
||||
<div>
|
||||
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 'bold', color: '#2c3e50' }}>
|
||||
Aktuelles Passwort *
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
name="currentPassword"
|
||||
value={passwordForm.currentPassword}
|
||||
onChange={handlePasswordChange}
|
||||
required
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '10px',
|
||||
border: '1px solid #ddd',
|
||||
borderRadius: '4px',
|
||||
fontSize: '16px'
|
||||
}}
|
||||
placeholder="Aktuelles Passwort"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 'bold', color: '#2c3e50' }}>
|
||||
Neues Passwort *
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
name="newPassword"
|
||||
value={passwordForm.newPassword}
|
||||
onChange={handlePasswordChange}
|
||||
required
|
||||
minLength={6}
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '10px',
|
||||
border: '1px solid #ddd',
|
||||
borderRadius: '4px',
|
||||
fontSize: '16px'
|
||||
}}
|
||||
placeholder="Mindestens 6 Zeichen"
|
||||
/>
|
||||
<div style={{ fontSize: '12px', color: '#7f8c8d', marginTop: '5px' }}>
|
||||
Das Passwort muss mindestens 6 Zeichen lang sein.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 'bold', color: '#2c3e50' }}>
|
||||
Neues Passwort bestätigen *
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
name="confirmPassword"
|
||||
value={passwordForm.confirmPassword}
|
||||
onChange={handlePasswordChange}
|
||||
required
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '10px',
|
||||
border: '1px solid #ddd',
|
||||
borderRadius: '4px',
|
||||
fontSize: '16px'
|
||||
}}
|
||||
placeholder="Passwort wiederholen"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
gap: '15px',
|
||||
justifyContent: 'flex-end',
|
||||
marginTop: '30px',
|
||||
paddingTop: '20px',
|
||||
borderTop: '1px solid #f0f0f0'
|
||||
}}>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading || !passwordForm.currentPassword || !passwordForm.newPassword || !passwordForm.confirmPassword}
|
||||
style={{
|
||||
padding: '12px 24px',
|
||||
backgroundColor: loading ? '#bdc3c7' : (!passwordForm.currentPassword || !passwordForm.newPassword || !passwordForm.confirmPassword ? '#95a5a6' : '#3498db'),
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: '6px',
|
||||
cursor: (loading || !passwordForm.currentPassword || !passwordForm.newPassword || !passwordForm.confirmPassword) ? 'not-allowed' : 'pointer',
|
||||
fontWeight: 'bold'
|
||||
}}
|
||||
>
|
||||
{loading ? '⏳ Wird geändert...' : 'Passwort ändern'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Availability Tab */}
|
||||
{activeTab === 'availability' && (
|
||||
<div style={{
|
||||
backgroundColor: 'white',
|
||||
padding: '30px',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid #e0e0e0',
|
||||
boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
|
||||
}}>
|
||||
<h2 style={{ marginTop: 0, color: '#2c3e50' }}>Meine Verfügbarkeit</h2>
|
||||
|
||||
<div style={{
|
||||
padding: '30px',
|
||||
textAlign: 'center',
|
||||
backgroundColor: '#f8f9fa',
|
||||
borderRadius: '8px',
|
||||
border: '2px dashed #dee2e6'
|
||||
}}>
|
||||
<div style={{ fontSize: '48px', marginBottom: '20px' }}>📅</div>
|
||||
<h3 style={{ color: '#2c3e50' }}>Verfügbarkeit verwalten</h3>
|
||||
<p style={{ color: '#6c757d', marginBottom: '25px' }}>
|
||||
Hier können Sie Ihre persönliche Verfügbarkeit für Schichtpläne festlegen.
|
||||
Legen Sie für jeden Tag und jede Schicht fest, ob Sie bevorzugt, möglicherweise
|
||||
oder nicht verfügbar sind.
|
||||
</p>
|
||||
|
||||
<button
|
||||
onClick={() => setShowAvailabilityManager(true)}
|
||||
style={{
|
||||
padding: '12px 24px',
|
||||
backgroundColor: '#3498db',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: '6px',
|
||||
cursor: 'pointer',
|
||||
fontWeight: 'bold',
|
||||
fontSize: '16px'
|
||||
}}
|
||||
>
|
||||
Verfügbarkeit bearbeiten
|
||||
</button>
|
||||
|
||||
<div style={{
|
||||
marginTop: '20px',
|
||||
padding: '15px',
|
||||
backgroundColor: '#e8f4fd',
|
||||
border: '1px solid #b6d7e8',
|
||||
borderRadius: '6px',
|
||||
fontSize: '14px',
|
||||
color: '#2c3e50',
|
||||
textAlign: 'left'
|
||||
}}>
|
||||
<strong>💡 Informationen:</strong>
|
||||
<ul style={{ margin: '8px 0 0 20px', padding: 0 }}>
|
||||
<li><strong>Bevorzugt:</strong> Sie möchten diese Schicht arbeiten</li>
|
||||
<li><strong>Möglich:</strong> Sie können diese Schicht arbeiten</li>
|
||||
<li><strong>Nicht möglich:</strong> Sie können diese Schicht nicht arbeiten</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -117,6 +117,19 @@ export class EmployeeService {
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async changePassword(id: string, data: { currentPassword: string, newPassword: string }): Promise<void> {
|
||||
const response = await fetch(`${API_BASE_URL}/employees/${id}/password`, {
|
||||
method: 'PUT',
|
||||
headers: getAuthHeaders(),
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.error || 'Failed to change password');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const employeeService = new EmployeeService();
|
||||
@@ -138,14 +138,14 @@ export const shiftPlanService = {
|
||||
},
|
||||
|
||||
// Create plan from template
|
||||
createFromTemplate: async (data: CreateShiftFromTemplateRequest): Promise<ShiftPlan> => {
|
||||
/*createFromTemplate: async (data: CreateShiftFromTemplateRequest): Promise<ShiftPlan> => {
|
||||
const response = await fetch(`${API_BASE}/from-template`, {
|
||||
method: 'POST',
|
||||
headers: getAuthHeaders(),
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return handleResponse(response);
|
||||
},
|
||||
},*/
|
||||
|
||||
// Create new plan
|
||||
createPlan: async (data: CreateShiftPlanRequest): Promise<ShiftPlan> => {
|
||||
|
||||
Reference in New Issue
Block a user