Dummydaten und Sprache angepasst
Dieser Commit ist enthalten in:
@ -1,11 +1,11 @@
|
||||
# SkillMate - Mitarbeiter-Skills-Management für Sicherheitsbehörden
|
||||
# SkillMate - Skill-Management für Sicherheitsbehörden
|
||||
|
||||
SkillMate ist eine spezialisierte Anwendung zur Verwaltung von Mitarbeiterfähigkeiten und -kompetenzen in Sicherheitsbehörden. Die Anwendung läuft lokal bei jedem Nutzer und kann über ein Admin-Panel zentral verwaltet werden.
|
||||
SkillMate ist eine spezialisierte Anwendung zur Verwaltung von Fähigkeiten und Kompetenzen der Mitarbeitenden in Sicherheitsbehörden. Die Anwendung läuft lokal bei jedem Nutzer und kann über ein Admin-Panel zentral verwaltet werden.
|
||||
|
||||
## Features
|
||||
|
||||
- 🔍 **Skill-basierte Suche**: Finden Sie Mitarbeiter anhand ihrer Fähigkeiten
|
||||
- 👥 **Mitarbeiterverwaltung**: Umfassende Profile mit Skills, Sprachen und Spezialisierungen
|
||||
- 🔍 **Skill-basierte Suche**: Finden Sie Mitarbeitende anhand ihrer Fähigkeiten
|
||||
- 👥 **Mitarbeitendenverwaltung**: Umfassende Profile mit Skills, Sprachen und Spezialisierungen
|
||||
- 🔐 **Sicherheitsüberprüfungen**: Verwaltung von Ü2/Ü3-Clearances
|
||||
- 🌓 **Dark/Light Mode**: Anpassbare Benutzeroberfläche
|
||||
- 🔄 **Synchronisation**: Zentrale Datenverwaltung über Admin-Panel
|
||||
|
||||
@ -46,9 +46,9 @@ export default function CreateEmployee() {
|
||||
case 'stellvertreter':
|
||||
return 'Stellvertretung'
|
||||
case 'beauftragter':
|
||||
return 'Beauftragte:r'
|
||||
return 'Beauftragte Person'
|
||||
default:
|
||||
return 'Mitarbeiter:in'
|
||||
return 'Teammitglied'
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,9 +73,9 @@ export default function CreateEmployee() {
|
||||
|
||||
if (response.data.data?.temporaryPassword) {
|
||||
setCreatedUser({ password: response.data.data.temporaryPassword })
|
||||
setSuccess(`Mitarbeiter und Benutzerkonto erfolgreich erstellt!`)
|
||||
setSuccess(`Profil und Benutzerkonto erfolgreich erstellt!`)
|
||||
} else {
|
||||
setSuccess('Mitarbeiter erfolgreich erstellt!')
|
||||
setSuccess('Profil erfolgreich erstellt!')
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error('Error:', err.response?.data)
|
||||
@ -96,8 +96,8 @@ export default function CreateEmployee() {
|
||||
const getRoleDescription = (role: UserRole): string => {
|
||||
const descriptions = {
|
||||
admin: 'Vollzugriff auf alle Funktionen inklusive Admin Panel und Benutzerverwaltung',
|
||||
superuser: 'Kann Mitarbeiter anlegen und verwalten, aber kein Zugriff auf Admin Panel',
|
||||
user: 'Kann nur das eigene Profil bearbeiten und Mitarbeiter durchsuchen'
|
||||
superuser: 'Kann Mitarbeitende anlegen und verwalten, aber kein Zugriff auf Admin Panel',
|
||||
user: 'Kann nur das eigene Profil bearbeiten und Mitarbeitende durchsuchen'
|
||||
}
|
||||
return descriptions[role]
|
||||
}
|
||||
@ -112,10 +112,10 @@ export default function CreateEmployee() {
|
||||
← Zurück zur Benutzerverwaltung
|
||||
</button>
|
||||
<h1 className="text-title-lg font-poppins font-bold text-primary">
|
||||
Neuen Mitarbeiter & Benutzer anlegen
|
||||
Neues Mitarbeitendenprofil & Benutzer anlegen
|
||||
</h1>
|
||||
<p className="text-body text-secondary mt-2">
|
||||
Erstellen Sie einen neuen Mitarbeiter-Datensatz und optional ein Benutzerkonto
|
||||
Erstellen Sie ein neues Profil und optional ein Benutzerkonto
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -141,7 +141,7 @@ export default function CreateEmployee() {
|
||||
{createdUser.password}
|
||||
</code>
|
||||
<p className="text-sm mt-2 text-green-700">
|
||||
⚠️ Bitte notieren Sie dieses Passwort und geben Sie es sicher an den Mitarbeiter weiter.
|
||||
⚠️ Bitte notieren Sie dieses Passwort und geben Sie es sicher an die betreffende Person weiter.
|
||||
Das Passwort muss beim ersten Login geändert werden.
|
||||
</p>
|
||||
<div className="mt-4 flex justify-end">
|
||||
@ -160,7 +160,7 @@ export default function CreateEmployee() {
|
||||
|
||||
<div className="card mb-6">
|
||||
<h2 className="text-title-card font-poppins font-semibold text-primary mb-6">
|
||||
Mitarbeiter-Grunddaten
|
||||
Grunddaten des Profils
|
||||
</h2>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
@ -251,7 +251,7 @@ export default function CreateEmployee() {
|
||||
}}
|
||||
/>
|
||||
<p className="text-sm text-secondary-light mt-2">
|
||||
Wählen Sie die primäre Organisationseinheit für den neuen Mitarbeiter. Die Abteilung wird automatisch anhand der Auswahl gesetzt.
|
||||
Wählen Sie die primäre Organisationseinheit für die neue Person. Die Abteilung wird automatisch anhand der Auswahl gesetzt.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -265,10 +265,10 @@ export default function CreateEmployee() {
|
||||
onChange={event => setSelectedUnitRole(event.target.value as EmployeeUnitRole)}
|
||||
className="input-field w-full"
|
||||
>
|
||||
<option value="leiter">Leiter:in</option>
|
||||
<option value="leiter">Leitung</option>
|
||||
<option value="stellvertreter">Stellvertretung</option>
|
||||
<option value="mitarbeiter">Mitarbeiter:in</option>
|
||||
<option value="beauftragter">Beauftragte:r</option>
|
||||
<option value="mitarbeiter">Teammitglied</option>
|
||||
<option value="beauftragter">Beauftragte Person</option>
|
||||
</select>
|
||||
<p className="text-sm text-secondary-light mt-2">
|
||||
Diese Rolle wird für Vertretungs- und Organigramm-Funktionen verwendet.
|
||||
@ -296,7 +296,7 @@ export default function CreateEmployee() {
|
||||
</span>
|
||||
</label>
|
||||
<p className="text-sm text-secondary-light mt-2 ml-7">
|
||||
Erstellt ein Benutzerkonto, mit dem sich der Mitarbeiter im System anmelden kann.
|
||||
Erstellt ein Benutzerkonto, mit dem sich die angelegte Person im System anmelden kann.
|
||||
Ein sicheres temporäres Passwort wird automatisch generiert.
|
||||
</p>
|
||||
</div>
|
||||
@ -327,10 +327,10 @@ export default function CreateEmployee() {
|
||||
📋 Was passiert als Nächstes?
|
||||
</h3>
|
||||
<ul className="space-y-2 text-body text-secondary">
|
||||
<li>• Der Mitarbeiter wird mit Grunddaten angelegt (Position: "Mitarbeiter", Telefon: "Nicht angegeben")</li>
|
||||
<li>• Die Person wird mit Grunddaten angelegt (Position: "Teammitglied", Telefon: "Nicht angegeben")</li>
|
||||
<li>• {watchCreateUser ? 'Ein Benutzerkonto wird erstellt und ein temporäres Passwort generiert' : 'Kein Benutzerkonto wird erstellt'}</li>
|
||||
<li>• {selectedUnitId ? `Die Organisationseinheit ${selectedUnitName} (${getUnitRoleLabel(selectedUnitRole)}) wird als primäre Zuordnung hinterlegt` : 'Organisationseinheit kann später im Organigramm zugewiesen werden'}</li>
|
||||
<li>• Der Mitarbeiter kann später im Frontend seine Profildaten vervollständigen</li>
|
||||
<li>• Die Person kann später im Frontend das Profil vervollständigen</li>
|
||||
<li>• Alle Daten werden verschlüsselt in der Datenbank gespeichert</li>
|
||||
</ul>
|
||||
</div>
|
||||
@ -349,7 +349,7 @@ export default function CreateEmployee() {
|
||||
className="btn-primary"
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? 'Erstelle...' : 'Mitarbeiter erstellen'}
|
||||
{loading ? 'Erstelle...' : 'Profil erstellen'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@ -1,19 +1,15 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { api } from '../services/api'
|
||||
import SyncStatus from '../components/SyncStatus'
|
||||
|
||||
interface DashboardStats {
|
||||
totalEmployees: number
|
||||
totalSkills: number
|
||||
totalUsers: number
|
||||
lastSync?: {
|
||||
timestamp: string
|
||||
success: boolean
|
||||
itemsSynced: number
|
||||
}
|
||||
}
|
||||
|
||||
export default function Dashboard() {
|
||||
const navigate = useNavigate()
|
||||
const [stats, setStats] = useState<DashboardStats>({
|
||||
totalEmployees: 0,
|
||||
totalSkills: 0,
|
||||
@ -65,23 +61,19 @@ export default function Dashboard() {
|
||||
|
||||
const statsCards = [
|
||||
{
|
||||
title: 'Mitarbeiter',
|
||||
title: 'Mitarbeitende',
|
||||
value: stats.totalEmployees,
|
||||
color: 'text-primary-blue',
|
||||
bgColor: 'bg-bg-accent',
|
||||
path: '/users'
|
||||
},
|
||||
{
|
||||
title: 'Skills',
|
||||
value: stats.totalSkills,
|
||||
color: 'text-success',
|
||||
bgColor: 'bg-success-bg',
|
||||
},
|
||||
{
|
||||
title: 'Benutzer',
|
||||
value: stats.totalUsers,
|
||||
color: 'text-info',
|
||||
bgColor: 'bg-info-bg',
|
||||
},
|
||||
path: '/skills'
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
@ -90,9 +82,14 @@ export default function Dashboard() {
|
||||
Dashboard
|
||||
</h1>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
|
||||
{statsCards.map((stat, index) => (
|
||||
<div key={index} className="card">
|
||||
<button
|
||||
key={index}
|
||||
type="button"
|
||||
onClick={() => navigate(stat.path)}
|
||||
className="card text-left transition hover:shadow-lg focus:outline-none focus:ring-2 focus:ring-primary-blue"
|
||||
>
|
||||
<div className={`w-16 h-16 rounded-card ${stat.bgColor} flex items-center justify-center mb-4`}>
|
||||
<span className={`text-2xl font-bold ${stat.color}`}>
|
||||
{stat.value}
|
||||
@ -101,60 +98,11 @@ export default function Dashboard() {
|
||||
<h3 className="text-body font-medium text-tertiary">
|
||||
{stat.title}
|
||||
</h3>
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
|
||||
<div className="card">
|
||||
<h2 className="text-title-card font-poppins font-semibold text-primary mb-4">
|
||||
Letzte Synchronisation
|
||||
</h2>
|
||||
{stats.lastSync ? (
|
||||
<div className="space-y-2">
|
||||
<p className="text-body text-secondary">
|
||||
<span className="font-medium">Zeitpunkt:</span>{' '}
|
||||
{new Date(stats.lastSync.timestamp).toLocaleString('de-DE')}
|
||||
</p>
|
||||
<p className="text-body text-secondary">
|
||||
<span className="font-medium">Status:</span>{' '}
|
||||
<span className={stats.lastSync.success ? 'text-success' : 'text-error'}>
|
||||
{stats.lastSync.success ? 'Erfolgreich' : 'Fehlgeschlagen'}
|
||||
</span>
|
||||
</p>
|
||||
<p className="text-body text-secondary">
|
||||
<span className="font-medium">Synchronisierte Elemente:</span>{' '}
|
||||
{stats.lastSync.itemsSynced}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-body text-tertiary">
|
||||
Noch keine Synchronisation durchgeführt
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="card">
|
||||
<h2 className="text-title-card font-poppins font-semibold text-primary mb-4">
|
||||
Systemstatus
|
||||
</h2>
|
||||
<div className="space-y-2">
|
||||
<p className="text-body text-secondary">
|
||||
<span className="font-medium">Backend:</span>{' '}
|
||||
<span className="text-success">Online</span>
|
||||
</p>
|
||||
<p className="text-body text-secondary">
|
||||
<span className="font-medium">Datenbank:</span>{' '}
|
||||
<span className="text-success">Verbunden</span>
|
||||
</p>
|
||||
<p className="text-body text-secondary">
|
||||
<span className="font-medium">Version:</span> 1.0.0
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SyncStatus />
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ export default function EmployeeForm() {
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch employee:', error)
|
||||
setError('Mitarbeiter konnte nicht geladen werden')
|
||||
setError('Profil konnte nicht geladen werden')
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,8 +80,8 @@ export default function EmployeeForm() {
|
||||
const getRoleDescription = (role: UserRole): string => {
|
||||
const descriptions = {
|
||||
admin: 'Vollzugriff auf alle Funktionen inklusive Admin Panel und Benutzerverwaltung',
|
||||
superuser: 'Kann Mitarbeiter anlegen und verwalten, aber kein Zugriff auf Admin Panel',
|
||||
user: 'Kann nur das eigene Profil bearbeiten und Mitarbeiter durchsuchen'
|
||||
superuser: 'Kann Mitarbeitende anlegen und verwalten, aber kein Zugriff auf Admin Panel',
|
||||
user: 'Kann nur das eigene Profil bearbeiten und Mitarbeitende durchsuchen'
|
||||
}
|
||||
return descriptions[role]
|
||||
}
|
||||
@ -96,7 +96,7 @@ export default function EmployeeForm() {
|
||||
← Zurück zur Übersicht
|
||||
</button>
|
||||
<h1 className="text-title-lg font-poppins font-bold text-primary">
|
||||
{isEdit ? 'Mitarbeiter bearbeiten' : 'Neuer Mitarbeiter'}
|
||||
{isEdit ? 'Mitarbeitendenprofil bearbeiten' : 'Neues Mitarbeitendenprofil'}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
@ -109,7 +109,7 @@ export default function EmployeeForm() {
|
||||
|
||||
<div className="card mb-6">
|
||||
<h2 className="text-title-card font-poppins font-semibold text-primary mb-6">
|
||||
Mitarbeiterdaten
|
||||
Profilinformationen
|
||||
</h2>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
@ -192,7 +192,7 @@ export default function EmployeeForm() {
|
||||
className="w-5 h-5 rounded border-border-input text-primary-blue focus:ring-primary-blue"
|
||||
/>
|
||||
<span className="text-body text-secondary">
|
||||
Benutzerkonto für diesen Mitarbeiter erstellen
|
||||
Benutzerkonto für diese Person erstellen
|
||||
</span>
|
||||
</label>
|
||||
<p className="text-small text-tertiary mt-1">
|
||||
@ -242,10 +242,10 @@ export default function EmployeeForm() {
|
||||
disabled={loading}
|
||||
className="btn-primary disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{loading ? 'Speichern...' : (isEdit ? 'Änderungen speichern' : 'Mitarbeiter anlegen')}
|
||||
{loading ? 'Speichern...' : (isEdit ? 'Änderungen speichern' : 'Profil anlegen')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ export default function EmployeeFormComplete() {
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch employee:', error)
|
||||
setError('Mitarbeiter konnte nicht geladen werden')
|
||||
setError('Profil konnte nicht geladen werden')
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,15 +97,15 @@ export default function EmployeeFormComplete() {
|
||||
let response
|
||||
if (isEdit) {
|
||||
response = await api.put(`/employees/${id}`, payload)
|
||||
setSuccess('Mitarbeiter erfolgreich aktualisiert!')
|
||||
setSuccess('Profil erfolgreich aktualisiert!')
|
||||
} else {
|
||||
response = await api.post('/employees', payload)
|
||||
|
||||
if (response.data.data.temporaryPassword) {
|
||||
setCreatedUser({ password: response.data.data.temporaryPassword })
|
||||
setSuccess(`Mitarbeiter erfolgreich erstellt! Temporäres Passwort: ${response.data.data.temporaryPassword}`)
|
||||
setSuccess(`Profil erfolgreich erstellt! Temporäres Passwort: ${response.data.data.temporaryPassword}`)
|
||||
} else {
|
||||
setSuccess('Mitarbeiter erfolgreich erstellt!')
|
||||
setSuccess('Profil erfolgreich erstellt!')
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,8 +131,8 @@ export default function EmployeeFormComplete() {
|
||||
const getRoleDescription = (role: UserRole): string => {
|
||||
const descriptions = {
|
||||
admin: 'Vollzugriff auf alle Funktionen inklusive Admin Panel und Benutzerverwaltung',
|
||||
superuser: 'Kann Mitarbeiter anlegen und verwalten, aber kein Zugriff auf Admin Panel',
|
||||
user: 'Kann nur das eigene Profil bearbeiten und Mitarbeiter durchsuchen'
|
||||
superuser: 'Kann Mitarbeitende anlegen und verwalten, aber kein Zugriff auf Admin Panel',
|
||||
user: 'Kann nur das eigene Profil bearbeiten und Mitarbeitende durchsuchen'
|
||||
}
|
||||
return descriptions[role]
|
||||
}
|
||||
@ -147,7 +147,7 @@ export default function EmployeeFormComplete() {
|
||||
← Zurück zur Übersicht
|
||||
</button>
|
||||
<h1 className="text-title-lg font-poppins font-bold text-primary">
|
||||
{isEdit ? 'Mitarbeiter bearbeiten' : 'Neuer Mitarbeiter'}
|
||||
{isEdit ? 'Mitarbeitendenprofil bearbeiten' : 'Neues Mitarbeitendenprofil'}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
@ -207,10 +207,10 @@ export default function EmployeeFormComplete() {
|
||||
|
||||
<div>
|
||||
<label className="block text-body font-medium text-secondary mb-2">
|
||||
Mitarbeiternummer *
|
||||
Personalnummer *
|
||||
</label>
|
||||
<input
|
||||
{...register('employeeNumber', { required: 'Mitarbeiternummer ist erforderlich' })}
|
||||
{...register('employeeNumber', { required: 'Personalnummer ist erforderlich' })}
|
||||
className="input-field w-full"
|
||||
placeholder="EMP001"
|
||||
/>
|
||||
@ -346,7 +346,7 @@ export default function EmployeeFormComplete() {
|
||||
className="w-5 h-5 rounded border-border-input text-primary-blue focus:ring-primary-blue"
|
||||
/>
|
||||
<span className="text-body font-medium text-secondary">
|
||||
Benutzerkonto für diesen Mitarbeiter erstellen
|
||||
Benutzerkonto für diese Person erstellen
|
||||
</span>
|
||||
</label>
|
||||
<p className="text-sm text-secondary-light mt-1 ml-7">
|
||||
@ -395,4 +395,4 @@ export default function EmployeeFormComplete() {
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ export default function EmployeeManagement() {
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<p className="text-tertiary">Mitarbeiter werden geladen...</p>
|
||||
<p className="text-tertiary">Mitarbeitende werden geladen...</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -42,13 +42,13 @@ export default function EmployeeManagement() {
|
||||
<div>
|
||||
<div className="flex justify-between items-center mb-8">
|
||||
<h1 className="text-title-lg font-poppins font-bold text-primary">
|
||||
Mitarbeiterverwaltung
|
||||
Mitarbeitendenverwaltung
|
||||
</h1>
|
||||
<button
|
||||
onClick={() => navigate('/employees/new')}
|
||||
className="btn-primary"
|
||||
>
|
||||
Neuer Mitarbeiter
|
||||
Neues Mitarbeitendenprofil
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -110,10 +110,10 @@ export default function EmployeeManagement() {
|
||||
|
||||
{filteredEmployees.length === 0 && (
|
||||
<div className="text-center py-8">
|
||||
<p className="text-tertiary">Keine Mitarbeiter gefunden</p>
|
||||
<p className="text-tertiary">Keine Mitarbeitenden gefunden</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ const OrganizationNode = ({ data }: { data: any }) => {
|
||||
</h4>
|
||||
{data.employeeCount !== undefined && (
|
||||
<p className="text-[11px] text-slate-600 mt-1">
|
||||
👥 {data.employeeCount} Mitarbeiter
|
||||
👥 {data.employeeCount} Mitarbeitende
|
||||
</p>
|
||||
)}
|
||||
{data.hasFuehrungsstelle && (
|
||||
|
||||
@ -454,7 +454,7 @@ export default function SyncSettings() {
|
||||
checked={syncSettings.syncEmployees}
|
||||
onChange={(e) => setSyncSettings({ ...syncSettings, syncEmployees: e.target.checked })}
|
||||
/>
|
||||
<span className="text-sm">Mitarbeiterdaten</span>
|
||||
<span className="text-sm">Daten der Mitarbeitenden</span>
|
||||
</label>
|
||||
<label className="flex items-center">
|
||||
<input
|
||||
@ -514,4 +514,4 @@ export default function SyncSettings() {
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -277,7 +277,7 @@ export default function UserManagement() {
|
||||
onClick={() => navigate('/users/create-employee')}
|
||||
className="btn-primary"
|
||||
>
|
||||
+ Neuen Mitarbeiter anlegen
|
||||
+ Neues Mitarbeitendenprofil anlegen
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -303,7 +303,7 @@ export default function UserManagement() {
|
||||
Benutzer
|
||||
</th>
|
||||
<th className="text-left py-3 px-4 font-poppins font-medium text-secondary">
|
||||
Mitarbeiter
|
||||
Mitarbeitende
|
||||
</th>
|
||||
<th className="text-left py-3 px-4 font-poppins font-medium text-secondary">
|
||||
Rolle
|
||||
@ -483,9 +483,9 @@ export default function UserManagement() {
|
||||
</h3>
|
||||
<ul className="space-y-2 text-body text-secondary">
|
||||
<li>• <strong>Administrator:</strong> Vollzugriff auf alle Funktionen und Einstellungen</li>
|
||||
<li>• <strong>Poweruser:</strong> Kann Mitarbeiter und Skills verwalten, aber keine Systemeinstellungen ändern</li>
|
||||
<li>• <strong>Poweruser:</strong> Kann Mitarbeitende und Skills verwalten, aber keine Systemeinstellungen ändern</li>
|
||||
<li>• <strong>Benutzer:</strong> Kann nur eigenes Profil bearbeiten und Daten einsehen</li>
|
||||
<li>• Neue Benutzer können über den Import oder die Mitarbeiterverwaltung angelegt werden</li>
|
||||
<li>• Neue Benutzer können über den Import oder die Mitarbeitendenverwaltung angelegt werden</li>
|
||||
<li>• Der Admin-Benutzer kann nicht gelöscht werden</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@ -442,4 +442,4 @@ db.transaction(() => {
|
||||
}
|
||||
})()
|
||||
|
||||
console.log(`✅ ${SAMPLE_EMPLOYEES.length} Demo-Mitarbeiter mit Passwort "${PASSWORD}" angelegt.`)
|
||||
console.log(`✅ ${SAMPLE_EMPLOYEES.length} Demo-Mitarbeitende mit Passwort "${PASSWORD}" angelegt.`)
|
||||
|
||||
@ -165,7 +165,7 @@ function buildEmployeeWithDetails(emp: any): Employee {
|
||||
export function createEmployee(input: EmployeeInput, actorUserId: string): { id: string } {
|
||||
const now = new Date().toISOString()
|
||||
const employeeId = uuidv4()
|
||||
const position = input.position || 'Mitarbeiter'
|
||||
const position = input.position || 'Teammitglied'
|
||||
const phone = input.phone || 'Nicht angegeben'
|
||||
const availability = input.availability || 'available'
|
||||
const employeeNumber = input.employeeNumber || `EMP${Date.now()}`
|
||||
@ -256,7 +256,7 @@ export function updateEmployee(id: string, input: EmployeeInput, actorUserId: st
|
||||
updated_at = ?, updated_by = ?
|
||||
WHERE id = ?
|
||||
`).run(
|
||||
input.firstName, input.lastName, input.position || 'Mitarbeiter', input.department,
|
||||
input.firstName, input.lastName, input.position || 'Teammitglied', input.department,
|
||||
// encryption handled in secure db layer caller; here store encrypted values already in input? Route prepares with FieldEncryption
|
||||
input.email, // already encrypted by route layer
|
||||
null, // email_hash set by route if needed
|
||||
|
||||
@ -264,7 +264,7 @@ router.post('/',
|
||||
lastName,
|
||||
employeeNumber || null,
|
||||
photo || null,
|
||||
position || 'Mitarbeiter', // Default position
|
||||
position || 'Teammitglied', // Default position
|
||||
resolvedDepartment,
|
||||
email,
|
||||
phone || 'Nicht angegeben', // Default phone
|
||||
@ -339,7 +339,7 @@ router.post('/',
|
||||
lastName,
|
||||
employeeNumber: employeeNumber || null,
|
||||
photo: photo || null,
|
||||
position: position || 'Mitarbeiter',
|
||||
position: position || 'Teammitglied',
|
||||
department: resolvedDepartment,
|
||||
email,
|
||||
phone: phone || 'Nicht angegeben',
|
||||
|
||||
@ -354,7 +354,7 @@ router.post('/',
|
||||
const now = new Date().toISOString()
|
||||
|
||||
const {
|
||||
firstName, lastName, employeeNumber, photo, position = 'Mitarbeiter',
|
||||
firstName, lastName, employeeNumber, photo, position = 'Teammitglied',
|
||||
department, email, phone = 'Nicht angegeben', mobile, office, availability = 'available',
|
||||
clearance, skills = [], languages = [], specializations = [], userRole, createUser,
|
||||
primaryUnitId, assignmentRole
|
||||
@ -612,7 +612,7 @@ router.put('/:id',
|
||||
const now = new Date().toISOString()
|
||||
|
||||
const {
|
||||
firstName, lastName, position = 'Mitarbeiter', department, email, phone = 'Nicht angegeben',
|
||||
firstName, lastName, position = 'Teammitglied', department, email, phone = 'Nicht angegeben',
|
||||
mobile, office, availability = 'available', clearance, skills, languages, specializations,
|
||||
employeeNumber
|
||||
} = req.body
|
||||
|
||||
@ -39,7 +39,7 @@ export async function createEmployeeUC(req: Request, body: any, actorUserId: str
|
||||
lastName: body.lastName,
|
||||
employeeNumber: body.employeeNumber || null,
|
||||
photo: body.photo || null,
|
||||
position: body.position || 'Mitarbeiter',
|
||||
position: body.position || 'Teammitglied',
|
||||
department: body.department,
|
||||
email: body.email,
|
||||
phone: body.phone || 'Nicht angegeben',
|
||||
|
||||
@ -55,8 +55,8 @@
|
||||
"shortcutName": "SkillMate",
|
||||
"runAfterFinish": true,
|
||||
"menuCategory": true,
|
||||
"description": "Mitarbeiter-Skills-Management für Sicherheitsbehörden",
|
||||
"branding": "SkillMate - Professionelle Mitarbeiterverwaltung",
|
||||
"description": "Skill-Management für Sicherheitsbehörden",
|
||||
"branding": "SkillMate - Professionelles Skill-Management",
|
||||
"vendor": "SkillMate Development",
|
||||
"installerHeaderIcon": "build/icon.ico",
|
||||
"ui": {
|
||||
@ -81,11 +81,11 @@
|
||||
"createStartMenuShortcut": true,
|
||||
"shortcutName": "SkillMate",
|
||||
"menuCategory": true,
|
||||
"description": "Mitarbeiter-Skills-Management für Sicherheitsbehörden",
|
||||
"description": "Skill-Management für Sicherheitsbehörden",
|
||||
"language": "1031",
|
||||
"multiLanguageInstaller": false,
|
||||
"installerHeader": "build/installer-header.bmp",
|
||||
"installerSidebar": "build/installer-sidebar.bmp"
|
||||
},
|
||||
"publish": null
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>SkillMate - Mitarbeiter-Skills-Management</title>
|
||||
<title>SkillMate - Skill-Management</title>
|
||||
<script>
|
||||
// Define process for Electron renderer
|
||||
window.process = {
|
||||
@ -17,4 +17,4 @@
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@ -4,10 +4,10 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>SkillMate - Mitarbeiter-Skills-Management</title>
|
||||
<title>SkillMate - Skill-Management</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@ -31,7 +31,7 @@ UninstallDisplayName={#MyAppName}
|
||||
UninstallDisplayIcon={app}\{#MyAppExeName}
|
||||
VersionInfoVersion={#MyAppVersion}
|
||||
VersionInfoCompany={#MyAppPublisher}
|
||||
VersionInfoDescription=Mitarbeiter-Skills-Management für Sicherheitsbehörden
|
||||
VersionInfoDescription=Skill-Management für Sicherheitsbehörden
|
||||
VersionInfoCopyright=Copyright (C) 2024 {#MyAppPublisher}
|
||||
VersionInfoProductName={#MyAppName}
|
||||
VersionInfoProductVersion={#MyAppVersion}
|
||||
@ -76,4 +76,4 @@ begin
|
||||
end;
|
||||
|
||||
[UninstallDelete]
|
||||
Type: filesandordirs; Name: "{userappdata}\SkillMate"
|
||||
Type: filesandordirs; Name: "{userappdata}\SkillMate"
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@skillmate/frontend",
|
||||
"version": "1.0.0",
|
||||
"description": "SkillMate - Mitarbeiter-Skills-Management für Sicherheitsbehörden",
|
||||
"description": "SkillMate - Skill-Management für Sicherheitsbehörden",
|
||||
"author": "SkillMate Development",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@ -80,7 +80,7 @@ export default function DeputyManagement() {
|
||||
}
|
||||
|
||||
const handleDelegate = async (assignmentId: string) => {
|
||||
const toDeputyId = prompt('Bitte geben Sie die Mitarbeiter-ID des neuen Vertreters ein:')
|
||||
const toDeputyId = prompt('Bitte geben Sie die Personen-ID des neuen Vertretenden ein:')
|
||||
if (!toDeputyId) return
|
||||
|
||||
const reason = prompt('Grund für die Weitergabe (optional):')
|
||||
|
||||
@ -426,7 +426,7 @@ export default function OfficeMap3D({ targetEmployeeId, targetRoom, currentUserR
|
||||
<p className="text-sm text-gray-400">{roomDetails.floorName}</p>
|
||||
{roomDetails.occupantNames && roomDetails.occupantNames.length > 0 && (
|
||||
<div className="mt-2">
|
||||
<p className="text-sm text-gray-400">Mitarbeiter:</p>
|
||||
<p className="text-sm text-gray-400">Mitarbeitende:</p>
|
||||
{roomDetails.occupantNames.map((name, i) => (
|
||||
<p key={i} className="text-sm">• {name}</p>
|
||||
))}
|
||||
@ -562,4 +562,4 @@ export default function OfficeMap3D({ targetEmployeeId, targetRoom, currentUserR
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ export default function OfficeMapModal({
|
||||
<div className="flex justify-between items-center p-4 border-b border-gray-700">
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold text-white">
|
||||
Wegbeschreibung zu {employeeName || 'Mitarbeiter'}
|
||||
Wegbeschreibung zu {employeeName || 'dieser Person'}
|
||||
</h2>
|
||||
<p className="text-sm text-gray-400 mt-1">
|
||||
<span className="text-green-400">● Start:</span> Ihr Büro
|
||||
@ -95,4 +95,4 @@ export default function OfficeMapModal({
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -321,7 +321,7 @@ export default function OrganizationChart({ onClose }: OrganizationChartProps) {
|
||||
<div className="border-b border-gray-200 dark:border-gray-700 mb-4">
|
||||
<div className="flex gap-4">
|
||||
<button className="pb-2 border-b-2 border-blue-500 dark:text-white">
|
||||
Mitarbeiter
|
||||
Mitarbeitende
|
||||
</button>
|
||||
<button className="pb-2 dark:text-gray-400">
|
||||
Skills
|
||||
@ -353,7 +353,7 @@ export default function OrganizationChart({ onClose }: OrganizationChartProps) {
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
{emp.role === 'leiter' && '🔑 Leitung'}
|
||||
{emp.role === 'stellvertreter' && '↔️ Stellvertretung'}
|
||||
{emp.role === 'mitarbeiter' && 'Mitarbeiter'}
|
||||
{emp.role === 'mitarbeiter' && 'Mitarbeitende Person'}
|
||||
{emp.role === 'beauftragter' && '🎖️ Beauftragter'}
|
||||
</p>
|
||||
</div>
|
||||
@ -361,7 +361,7 @@ export default function OrganizationChart({ onClose }: OrganizationChartProps) {
|
||||
</div>
|
||||
))}
|
||||
{unitEmployees.length === 0 && (
|
||||
<p className="text-gray-500 dark:text-gray-400">Keine Mitarbeiter zugeordnet</p>
|
||||
<p className="text-gray-500 dark:text-gray-400">Keine Mitarbeitenden zugeordnet</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ import { useAuthStore } from '../stores/authStore'
|
||||
const navigation = [
|
||||
{ name: 'Dashboard', href: '/', icon: HomeIcon },
|
||||
{ name: 'Mein Profil', href: '/profile', icon: UsersIcon },
|
||||
{ name: 'Mitarbeiter', href: '/employees', icon: UsersIcon },
|
||||
{ name: 'Mitarbeitende', href: '/employees', icon: UsersIcon },
|
||||
{ name: 'Skill-Suche', href: '/search', icon: SearchIcon },
|
||||
{ name: 'Team-Zusammenstellung', href: '/team', icon: UsersIcon },
|
||||
{ name: 'Einstellungen', href: '/settings', icon: SettingsIcon },
|
||||
|
||||
@ -61,7 +61,7 @@ export default function Dashboard() {
|
||||
|
||||
const statsCards = [
|
||||
{
|
||||
title: 'Mitarbeiter gesamt',
|
||||
title: 'Mitarbeitende gesamt',
|
||||
value: stats.totalEmployees,
|
||||
color: 'text-primary-blue',
|
||||
bgColor: 'bg-bg-accent',
|
||||
@ -200,7 +200,7 @@ export default function Dashboard() {
|
||||
<div className="card">
|
||||
<h2 className="text-title-card font-poppins font-semibold text-primary mb-4">Profilqualität</h2>
|
||||
{!user?.employeeId ? (
|
||||
<p className="text-tertiary">Kein Mitarbeiterprofil mit Ihrem Nutzer verknüpft.</p>
|
||||
<p className="text-tertiary">Kein Mitarbeitendenprofil mit Ihrem Nutzer verknüpft.</p>
|
||||
) : (
|
||||
<>
|
||||
<div className="w-full h-3 bg-bg-gray dark:bg-dark-primary rounded-input overflow-hidden">
|
||||
@ -237,7 +237,7 @@ export default function Dashboard() {
|
||||
onClick={() => navigate('/employees')}
|
||||
className="w-full btn-secondary"
|
||||
>
|
||||
Mitarbeiter verwalten
|
||||
Mitarbeitende verwalten
|
||||
</button>
|
||||
<button
|
||||
onClick={() => navigate('/search')}
|
||||
|
||||
@ -84,12 +84,12 @@ export default function EmployeeDetail() {
|
||||
if (!employee) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<p className="text-tertiary">Mitarbeiter wird geladen...</p>
|
||||
<p className="text-tertiary">Profil wird geladen...</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Hinweis: Verfügbarkeits-Badge wird im Mitarbeiter-Detail nicht angezeigt
|
||||
// Hinweis: Verfügbarkeits-Badge wird im Mitarbeitenden-Detail nicht angezeigt
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
@ -175,7 +175,7 @@ export default function EmployeeForm() {
|
||||
|
||||
if (!formData.firstName.trim()) errors.firstName = 'Vorname ist erforderlich'
|
||||
if (!formData.lastName.trim()) errors.lastName = 'Nachname ist erforderlich'
|
||||
if (!formData.employeeNumber.trim()) errors.employeeNumber = 'Mitarbeiternummer ist erforderlich'
|
||||
if (!formData.employeeNumber.trim()) errors.employeeNumber = 'Personalnummer ist erforderlich'
|
||||
if (!formData.position.trim()) errors.position = 'Position ist erforderlich'
|
||||
if (!formData.department.trim()) errors.department = 'Abteilung ist erforderlich'
|
||||
if (!formData.email.trim()) errors.email = 'E-Mail ist erforderlich'
|
||||
@ -250,7 +250,7 @@ export default function EmployeeForm() {
|
||||
if (err.response?.status === 401) {
|
||||
setError('Ihre Session ist abgelaufen. Bitte melden Sie sich erneut an.')
|
||||
} else {
|
||||
setError(err.response?.data?.message || 'Fehler beim Erstellen des Mitarbeiters')
|
||||
setError(err.response?.data?.message || 'Fehler beim Erstellen des Profils')
|
||||
}
|
||||
} finally {
|
||||
setLoading(false)
|
||||
@ -261,7 +261,7 @@ export default function EmployeeForm() {
|
||||
<div>
|
||||
<div className="flex justify-between items-center mb-8">
|
||||
<h1 className="text-title-lg font-poppins font-bold text-primary">
|
||||
Neuer Mitarbeiter
|
||||
Neues Mitarbeitendenprofil
|
||||
</h1>
|
||||
<button
|
||||
onClick={() => navigate('/employees')}
|
||||
@ -288,7 +288,7 @@ export default function EmployeeForm() {
|
||||
<div className="md:col-span-2 flex justify-center mb-6">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-secondary mb-2 text-center">
|
||||
Mitarbeiterfoto
|
||||
Profilfoto
|
||||
</label>
|
||||
<PhotoPreview
|
||||
currentPhoto={employeePhoto || undefined}
|
||||
@ -696,7 +696,7 @@ export default function EmployeeForm() {
|
||||
disabled={loading}
|
||||
className="btn-primary"
|
||||
>
|
||||
{loading ? 'Wird gespeichert...' : 'Mitarbeiter erstellen'}
|
||||
{loading ? 'Wird gespeichert...' : 'Profil erstellen'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@ -87,17 +87,17 @@ export default function EmployeeList() {
|
||||
<div>
|
||||
<div className="flex justify-between items-center mb-8">
|
||||
<h1 className="text-3xl font-bold text-gray-900 dark:text-white">
|
||||
Mitarbeiter & Expert:innen
|
||||
Mitarbeitende & Expertise
|
||||
</h1>
|
||||
<p className="text-gray-600 dark:text-gray-400 mt-1">
|
||||
{filteredEmployees.length} von {employees.length} Mitarbeitern
|
||||
{filteredEmployees.length} von {employees.length} Mitarbeitenden
|
||||
</p>
|
||||
{canCreateEmployee() && (
|
||||
<button
|
||||
onClick={() => navigate('/employees/new')}
|
||||
className="btn-primary"
|
||||
>
|
||||
Mitarbeiter hinzufügen
|
||||
Mitarbeitende hinzufügen
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
@ -189,7 +189,7 @@ export default function EmployeeList() {
|
||||
|
||||
{filteredEmployees.length === 0 && (
|
||||
<div className="text-center py-12">
|
||||
<p className="text-tertiary">Keine Mitarbeiter gefunden</p>
|
||||
<p className="text-tertiary">Keine Mitarbeitenden gefunden</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -142,7 +142,7 @@ export default function MyProfile() {
|
||||
const payload = {
|
||||
firstName: form.firstName,
|
||||
lastName: form.lastName,
|
||||
position: form.position || 'Mitarbeiter',
|
||||
position: form.position || 'Teammitglied',
|
||||
department: form.department || '',
|
||||
employeeNumber: form.employeeNumber || undefined,
|
||||
email: user?.email || form.email,
|
||||
@ -178,7 +178,7 @@ export default function MyProfile() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-title-lg font-poppins font-bold text-primary mb-4">Mein Profil</h1>
|
||||
<p className="text-tertiary">Kein Mitarbeiterprofil mit Ihrem Nutzer verknüpft.</p>
|
||||
<p className="text-tertiary">Kein Mitarbeitendenprofil mit Ihrem Nutzer verknüpft.</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -117,8 +117,7 @@ export default function Settings() {
|
||||
<span className="font-medium">Entwickelt für:</span> Sicherheitsbehörden
|
||||
</p>
|
||||
<p className="text-small text-tertiary mt-4">
|
||||
SkillMate ist eine spezialisierte Anwendung zur Verwaltung von
|
||||
Mitarbeiterfähigkeiten und -kompetenzen in Sicherheitsbehörden.
|
||||
SkillMate ist eine spezialisierte Anwendung zur Verwaltung von Fähigkeiten und Kompetenzen der Mitarbeitenden in Sicherheitsbehörden.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -396,7 +396,7 @@ export default function SkillSearch() {
|
||||
)}
|
||||
</div>
|
||||
<span className="text-xs text-tertiary ml-auto">
|
||||
{skill.userCount} Mitarbeiter
|
||||
{skill.userCount} Mitarbeitende
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
@ -530,7 +530,7 @@ export default function SkillSearch() {
|
||||
<div className="lg:col-span-1">
|
||||
<div className="card">
|
||||
<h2 className="text-title-card font-poppins font-semibold text-primary mb-4">
|
||||
Mitarbeiter
|
||||
Mitarbeitende
|
||||
{filteredEmployees.length > 0 && (
|
||||
<span className="ml-2 text-body font-normal text-tertiary">
|
||||
({filteredEmployees.length})
|
||||
@ -547,7 +547,7 @@ export default function SkillSearch() {
|
||||
) : filteredEmployees.length === 0 ? (
|
||||
<div className="text-center py-8">
|
||||
<p className="text-tertiary text-sm">
|
||||
Keine Mitarbeiter gefunden
|
||||
Keine Mitarbeitenden gefunden
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@ -418,7 +418,7 @@ export default function TeamZusammenstellung() {
|
||||
Team-Zusammenstellung
|
||||
</h1>
|
||||
<p className="text-tertiary">
|
||||
Wählen Sie Skills oder nutzen Sie die Filter, um passende Mitarbeiter zu finden und Ihrem Team zuzuweisen.
|
||||
Wählen Sie Skills oder nutzen Sie die Filter, um passende Mitarbeitende zu finden und Ihrem Team zuzuweisen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -629,8 +629,8 @@ export default function TeamZusammenstellung() {
|
||||
<div className="card">
|
||||
<h2 className="text-title-card font-poppins font-semibold text-primary mb-4">
|
||||
{selectedSkills.size > 0 || searchTerm || selectedDepartment || selectedPosition
|
||||
? `Gefilterte Mitarbeiter (${filteredEmployees.length})`
|
||||
: `Mitarbeiter`
|
||||
? `Gefilterte Mitarbeitende (${filteredEmployees.length})`
|
||||
: `Mitarbeitende`
|
||||
}
|
||||
</h2>
|
||||
|
||||
@ -641,12 +641,12 @@ export default function TeamZusammenstellung() {
|
||||
Nutzen Sie die Filter oder wählen Sie Skills aus
|
||||
</p>
|
||||
<p className="text-sm text-tertiary">
|
||||
Die passenden Mitarbeiter werden dann hier angezeigt
|
||||
Die passenden Mitarbeitenden werden dann hier angezeigt
|
||||
</p>
|
||||
</div>
|
||||
) : filteredEmployees.length === 0 ? (
|
||||
<p className="text-center text-tertiary py-8">
|
||||
Keine passenden Mitarbeiter gefunden
|
||||
Keine passenden Mitarbeitenden gefunden
|
||||
</p>
|
||||
) : (
|
||||
filteredEmployees.map((employee: any) => {
|
||||
@ -912,7 +912,7 @@ export default function TeamZusammenstellung() {
|
||||
</button>
|
||||
) : (
|
||||
<div className="py-2 text-center text-xs text-gray-400">
|
||||
Wählen Sie einen Mitarbeiter aus
|
||||
Wählen Sie eine Person aus
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren