reworked scheduling

This commit is contained in:
2025-10-18 01:55:04 +02:00
parent b86040dc04
commit f705a83cd4
26 changed files with 3222 additions and 3193 deletions

View File

@@ -5,6 +5,7 @@ import { employeeService } from '../../services/employeeService';
import { useNotification } from '../../contexts/NotificationContext';
import AvailabilityManager from '../Employees/components/AvailabilityManager';
import { Employee } from '../../models/Employee';
import { styles } from './type/SettingsType';
const Settings: React.FC = () => {
const { user: currentUser, updateUser } = useAuth();
@@ -148,7 +149,12 @@ const Settings: React.FC = () => {
};
if (!currentUser) {
return <div>Nicht eingeloggt</div>;
return <div style={{
textAlign: 'center',
padding: '3rem',
color: '#666',
fontSize: '1.1rem'
}}>Nicht eingeloggt</div>;
}
if (showAvailabilityManager) {
@@ -161,395 +167,390 @@ const Settings: React.FC = () => {
);
}
// Style constants for consistency
return (
<div style={{ padding: '20px', maxWidth: '800px', margin: '0 auto' }}>
<h1> Einstellungen</h1>
{/* Tab Navigation */}
<div style={{
display: 'flex',
borderBottom: '1px solid #e0e0e0',
marginBottom: '30px'
}}>
<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 style={styles.container}>
{/* Left Sidebar with Tabs */}
<div style={styles.sidebar}>
<div style={styles.header}>
<h1 style={styles.title}>Einstellungen</h1>
<div style={styles.subtitle}>Verwalten Sie Ihre Kontoeinstellungen und Präferenzen</div>
</div>
<div style={styles.tabs}>
<button
onClick={() => setActiveTab('profile')}
style={{
...styles.tab,
...(activeTab === 'profile' ? styles.tabActive : {})
}}
onMouseEnter={(e) => {
if (activeTab !== 'profile') {
e.currentTarget.style.background = styles.tabHover.background;
e.currentTarget.style.color = styles.tabHover.color;
e.currentTarget.style.transform = styles.tabHover.transform;
}
}}
onMouseLeave={(e) => {
if (activeTab !== 'profile') {
e.currentTarget.style.background = styles.tab.background;
e.currentTarget.style.color = styles.tab.color;
e.currentTarget.style.transform = 'none';
}
}}
>
<span style={{ color: '#cda8f0', fontSize: '24px' }}>{'\u{1F464}\u{FE0E}'}</span>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }}>
<span style={{ fontSize: '0.95rem', fontWeight: 500 }}>Profil</span>
<span style={{ fontSize: '0.8rem', opacity: 0.7, marginTop: '2px' }}>Persönliche Informationen</span>
</div>
</button>
<button
onClick={() => setActiveTab('password')}
style={{
...styles.tab,
...(activeTab === 'password' ? styles.tabActive : {})
}}
onMouseEnter={(e) => {
if (activeTab !== 'password') {
e.currentTarget.style.background = styles.tabHover.background;
e.currentTarget.style.color = styles.tabHover.color;
e.currentTarget.style.transform = styles.tabHover.transform;
}
}}
onMouseLeave={(e) => {
if (activeTab !== 'password') {
e.currentTarget.style.background = styles.tab.background;
e.currentTarget.style.color = styles.tab.color;
e.currentTarget.style.transform = 'none';
}
}}
>
<span style={{ fontSize: '1.2rem', width: '24px' }}>🔒</span>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }}>
<span style={{ fontSize: '0.95rem', fontWeight: 500 }}>Passwort</span>
<span style={{ fontSize: '0.8rem', opacity: 0.7, marginTop: '2px' }}>Sicherheitseinstellungen</span>
</div>
</button>
<button
onClick={() => setActiveTab('availability')}
style={{
...styles.tab,
...(activeTab === 'availability' ? styles.tabActive : {})
}}
onMouseEnter={(e) => {
if (activeTab !== 'availability') {
e.currentTarget.style.background = styles.tabHover.background;
e.currentTarget.style.color = styles.tabHover.color;
e.currentTarget.style.transform = styles.tabHover.transform;
}
}}
onMouseLeave={(e) => {
if (activeTab !== 'availability') {
e.currentTarget.style.background = styles.tab.background;
e.currentTarget.style.color = styles.tab.color;
e.currentTarget.style.transform = 'none';
}
}}
>
<span style={{ fontSize: '1.2rem', width: '24px' }}>📅</span>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }}>
<span style={{ fontSize: '0.95rem', fontWeight: 500 }}>Verfügbarkeit</span>
<span style={{ fontSize: '0.8rem', opacity: 0.7, marginTop: '2px' }}>Schichtplanung</span>
</div>
</button>
</div>
</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: '95%',
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: '95%',
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: '95%',
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: '95%',
padding: '10px',
border: '1px solid #ddd',
borderRadius: '4px',
backgroundColor: '#f8f9fa',
color: '#666'
}}
/>
</div>
</div>
</div>
{/* Right Content Area */}
<div style={styles.content}>
{/* Profile Tab */}
{activeTab === 'profile' && (
<>
<div style={styles.section}>
<h2 style={styles.sectionTitle}>Profilinformationen</h2>
<p style={styles.sectionDescription}>
Verwalten Sie Ihre persönlichen Informationen und Kontaktdaten
</p>
</div>
<div
style={{
marginTop: '10px',
padding: '15px',
backgroundColor: '#f8f9fa',
borderRadius: '6px',
border: '1px solid #e9ecef',
}}
>
<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: '97.5%',
padding: '10px',
border: '1px solid #ddd',
borderRadius: '4px',
fontSize: '16px',
}}
placeholder="Ihr vollständiger Name"
/>
</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>
<form onSubmit={handleProfileUpdate} style={{ marginTop: '2rem' }}>
<div style={styles.formGrid}>
{/* Read-only information */}
<div style={styles.infoCard}>
<h4 style={styles.infoCardTitle}>Systeminformationen</h4>
<div style={styles.infoGrid}>
<div style={styles.field}>
<label style={styles.fieldLabel}>
E-Mail
</label>
<input
type="email"
value={currentUser.email}
disabled
style={styles.fieldInputDisabled}
/>
</div>
<div style={styles.field}>
<label style={styles.fieldLabel}>
Rolle
</label>
<input
type="text"
value={currentUser.role}
disabled
style={styles.fieldInputDisabled}
/>
</div>
<div style={styles.field}>
<label style={styles.fieldLabel}>
Mitarbeiter Typ
</label>
<input
type="text"
value={currentUser.employeeType}
disabled
style={styles.fieldInputDisabled}
/>
</div>
<div style={styles.field}>
<label style={styles.fieldLabel}>
Vertragstyp
</label>
<input
type="text"
value={currentUser.contractType}
disabled
style={styles.fieldInputDisabled}
/>
</div>
</div>
</div>
<div style={styles.infoCard}>
{/* Editable name field */}
<div style={{ ...styles.field, marginTop: '1rem' }}>
<label style={styles.fieldLabel}>
Vollständiger Name *
</label>
<input
type="text"
name="name"
value={profileForm.name}
onChange={handleProfileChange}
required
style={{
...styles.fieldInput,
width: '95%'
}}
placeholder="Ihr vollständiger Name"
onFocus={(e) => {
e.target.style.borderColor = '#1a1325';
e.target.style.boxShadow = '0 0 0 3px rgba(26, 19, 37, 0.1)';
}}
onBlur={(e) => {
e.target.style.borderColor = '#e8e8e8';
e.target.style.boxShadow = 'none';
}}
/>
</div>
</div>
</div>
<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 style={styles.actions}>
<button
type="submit"
disabled={loading || !profileForm.name.trim()}
style={{
...styles.button,
...styles.buttonPrimary,
...((loading || !profileForm.name.trim()) ? styles.buttonDisabled : {})
}}
onMouseEnter={(e) => {
if (!loading && profileForm.name.trim()) {
e.currentTarget.style.background = styles.buttonPrimaryHover.background;
e.currentTarget.style.transform = styles.buttonPrimaryHover.transform;
e.currentTarget.style.boxShadow = styles.buttonPrimaryHover.boxShadow;
}
}}
onMouseLeave={(e) => {
if (!loading && profileForm.name.trim()) {
e.currentTarget.style.background = styles.buttonPrimary.background;
e.currentTarget.style.transform = 'none';
e.currentTarget.style.boxShadow = styles.buttonPrimary.boxShadow;
}
}}
>
{loading ? '⏳ Wird gespeichert...' : 'Profil aktualisieren'}
</button>
</div>
</form>
</>
)}
{/* Password Tab */}
{activeTab === 'password' && (
<>
<div style={styles.section}>
<h2 style={styles.sectionTitle}>Passwort ändern</h2>
<p style={styles.sectionDescription}>
Aktualisieren Sie Ihr Passwort für erhöhte Sicherheit
</p>
</div>
</div>
</div>
)}
<form onSubmit={handlePasswordUpdate} style={{ marginTop: '2rem' }}>
<div style={styles.formGridCompact}>
<div style={styles.field}>
<label style={styles.fieldLabel}>
Aktuelles Passwort *
</label>
<input
type="password"
name="currentPassword"
value={passwordForm.currentPassword}
onChange={handlePasswordChange}
required
style={styles.fieldInput}
placeholder="Aktuelles Passwort"
onFocus={(e) => {
e.target.style.borderColor = '#1a1325';
e.target.style.boxShadow = '0 0 0 3px rgba(26, 19, 37, 0.1)';
}}
onBlur={(e) => {
e.target.style.borderColor = '#e8e8e8';
e.target.style.boxShadow = 'none';
}}
/>
</div>
<div style={styles.field}>
<label style={styles.fieldLabel}>
Neues Passwort *
</label>
<input
type="password"
name="newPassword"
value={passwordForm.newPassword}
onChange={handlePasswordChange}
required
minLength={6}
style={styles.fieldInput}
placeholder="Mindestens 6 Zeichen"
onFocus={(e) => {
e.target.style.borderColor = '#1a1325';
e.target.style.boxShadow = '0 0 0 3px rgba(26, 19, 37, 0.1)';
}}
onBlur={(e) => {
e.target.style.borderColor = '#e8e8e8';
e.target.style.boxShadow = 'none';
}}
/>
<div style={styles.fieldHint}>
Das Passwort muss mindestens 6 Zeichen lang sein.
</div>
</div>
<div style={styles.field}>
<label style={styles.fieldLabel}>
Neues Passwort bestätigen *
</label>
<input
type="password"
name="confirmPassword"
value={passwordForm.confirmPassword}
onChange={handlePasswordChange}
required
style={styles.fieldInput}
placeholder="Passwort wiederholen"
onFocus={(e) => {
e.target.style.borderColor = '#1a1325';
e.target.style.boxShadow = '0 0 0 3px rgba(26, 19, 37, 0.1)';
}}
onBlur={(e) => {
e.target.style.borderColor = '#e8e8e8';
e.target.style.boxShadow = 'none';
}}
/>
</div>
</div>
<div style={styles.actions}>
<button
type="submit"
disabled={loading || !passwordForm.currentPassword || !passwordForm.newPassword || !passwordForm.confirmPassword}
style={{
...styles.button,
...styles.buttonPrimary,
...((loading || !passwordForm.currentPassword || !passwordForm.newPassword || !passwordForm.confirmPassword) ? styles.buttonDisabled : {})
}}
onMouseEnter={(e) => {
if (!loading && passwordForm.currentPassword && passwordForm.newPassword && passwordForm.confirmPassword) {
e.currentTarget.style.background = styles.buttonPrimaryHover.background;
e.currentTarget.style.transform = styles.buttonPrimaryHover.transform;
e.currentTarget.style.boxShadow = styles.buttonPrimaryHover.boxShadow;
}
}}
onMouseLeave={(e) => {
if (!loading && passwordForm.currentPassword && passwordForm.newPassword && passwordForm.confirmPassword) {
e.currentTarget.style.background = styles.buttonPrimary.background;
e.currentTarget.style.transform = 'none';
e.currentTarget.style.boxShadow = styles.buttonPrimary.boxShadow;
}
}}
>
{loading ? '⏳ Wird geändert...' : 'Passwort ändern'}
</button>
</div>
</form>
</>
)}
{/* Availability Tab */}
{activeTab === 'availability' && (
<>
<div style={styles.section}>
<h2 style={styles.sectionTitle}>Meine Verfügbarkeit</h2>
<p style={styles.sectionDescription}>
Legen Sie Ihre persönliche Verfügbarkeit für Schichtpläne fest
</p>
</div>
<div style={styles.availabilityCard}>
<div style={styles.availabilityIcon}>📅</div>
<h3 style={styles.availabilityTitle}>Verfügbarkeit verwalten</h3>
<p style={styles.availabilityDescription}>
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={{
...styles.button,
...styles.buttonPrimary,
marginBottom: '2rem'
}}
onMouseEnter={(e) => {
e.currentTarget.style.background = styles.buttonPrimaryHover.background;
e.currentTarget.style.transform = styles.buttonPrimaryHover.transform;
e.currentTarget.style.boxShadow = styles.buttonPrimaryHover.boxShadow;
}}
onMouseLeave={(e) => {
e.currentTarget.style.background = styles.buttonPrimary.background;
e.currentTarget.style.transform = 'none';
e.currentTarget.style.boxShadow = styles.buttonPrimary.boxShadow;
}}
>
Verfügbarkeit bearbeiten
</button>
</div>
</>
)}
</div>
</div>
);
};