Dummydaten und Sprache angepasst

Dieser Commit ist enthalten in:
Claude Project Manager
2025-09-29 00:35:31 +02:00
Ursprung b462f69281
Commit e275b8fcd6
32 geänderte Dateien mit 122 neuen und 175 gelöschten Zeilen

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -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 && (

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -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.`)

Datei anzeigen

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

Datei anzeigen

@ -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',

Datei anzeigen

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

Datei anzeigen

@ -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',

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -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"

Datei anzeigen

@ -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": {

Datei anzeigen

@ -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):')

Datei anzeigen

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

Datei anzeigen

@ -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>
</>
)
}
}

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -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')}

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -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>
) : (

Datei anzeigen

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