Consolidates the previously separate omsorgapp and omsorgCore repos (each had their own nested .git with GitHub history) plus the old root-level website/mitarbeiter-app into a single monorepo, matching the structure already documented in the root CLAUDE.md. Also moves the PHP employee app aside as omsorgWeb/mitarbeiter-app-legacy/ to serve as a template for a ground-up rewrite. Fixes .gitignore in the same pass: the config-secrets/uploads/data patterns were unanchored (relative to repo root, not depth-agnostic), so they silently stopped matching once the app moved under omsorgWeb/. Patterns are now **/-prefixed and cover both mitarbeiter-app and mitarbeiter-app-legacy, keeping DB/SMTP credentials and uploaded employee documents out of version control. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
198 lines
5.4 KiB
React
198 lines
5.4 KiB
React
import { useEffect, useState } from "react";
|
|
import { Pencil } from "lucide-react";
|
|
|
|
import OmsorgCard from "../../components/OmsorgCard";
|
|
import OmsorgBadge from "../../components/ui/OmsorgBadge";
|
|
import OmsorgButton from "../../components/ui/OmsorgButton";
|
|
import { useAuth } from "../../app/AuthContext";
|
|
import EmployeeTabs from "./EmployeeTabs";
|
|
import EditEmployeeDialog from "./EditEmployeeDialog";
|
|
|
|
function getInitials(firstName = "", lastName = "") {
|
|
return `${firstName[0] ?? ""}${lastName[0] ?? ""}`.toUpperCase();
|
|
}
|
|
|
|
function formatDate(value) {
|
|
if (!value) return "—";
|
|
return new Date(value).toLocaleDateString("de-DE");
|
|
}
|
|
|
|
function formatAddress(employee) {
|
|
const line = [employee.postalCode, employee.city].filter(Boolean).join(" ");
|
|
const parts = [employee.street, line].filter(Boolean);
|
|
|
|
if (employee.country && employee.country !== "Deutschland") {
|
|
parts.push(employee.country);
|
|
}
|
|
|
|
return parts.length > 0 ? parts.join(", ") : "—";
|
|
}
|
|
|
|
export default function EmployeeDetailPanel({ employee, onUpdated }) {
|
|
const { hasPermission } = useAuth();
|
|
const canEdit = hasPermission("Employees", "Edit");
|
|
const [activeTab, setActiveTab] = useState("overview");
|
|
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false);
|
|
|
|
useEffect(() => {
|
|
setActiveTab("overview");
|
|
}, [employee?.id]);
|
|
|
|
if (!employee) {
|
|
return (
|
|
<OmsorgCard>
|
|
<div className="employee-detail-empty">
|
|
<h2>Keine Personalakte ausgewählt</h2>
|
|
<p>Wähle links einen Mitarbeiter aus.</p>
|
|
</div>
|
|
</OmsorgCard>
|
|
);
|
|
}
|
|
|
|
const renderTabContent = () => {
|
|
switch (activeTab) {
|
|
case "documents":
|
|
return (
|
|
<div className="employee-tab-content">
|
|
<h3>Dokumente</h3>
|
|
<p>
|
|
Hier werden später Arbeitsverträge,
|
|
Nachweise und weitere Dokumente angezeigt.
|
|
</p>
|
|
</div>
|
|
);
|
|
|
|
case "contracts":
|
|
return (
|
|
<div className="employee-tab-content">
|
|
<h3>Verträge</h3>
|
|
<p>
|
|
Hier erscheint später der Arbeitsvertrag
|
|
mit Beginn, Ende und Konditionen.
|
|
</p>
|
|
</div>
|
|
);
|
|
|
|
case "qualifications":
|
|
return (
|
|
<div className="employee-tab-content">
|
|
<h3>Qualifikationen</h3>
|
|
<p>
|
|
Hier erscheinen später Ausbildung,
|
|
Zertifikate und Fortbildungen.
|
|
</p>
|
|
</div>
|
|
);
|
|
|
|
case "assignments":
|
|
return (
|
|
<div className="employee-tab-content">
|
|
<h3>Einsätze</h3>
|
|
<p>
|
|
Hier wird später die komplette
|
|
Einsatzhistorie angezeigt.
|
|
</p>
|
|
</div>
|
|
);
|
|
|
|
case "overview":
|
|
default:
|
|
return (
|
|
<div className="employee-detail-grid">
|
|
<div>
|
|
<strong>Telefon</strong>
|
|
<p>{employee.phoneNumber ?? "—"}</p>
|
|
</div>
|
|
|
|
<div>
|
|
<strong>E-Mail</strong>
|
|
<p>{employee.email ?? "—"}</p>
|
|
</div>
|
|
|
|
<div>
|
|
<strong>Adresse</strong>
|
|
<p>{formatAddress(employee)}</p>
|
|
</div>
|
|
|
|
<div>
|
|
<strong>Geburtsdatum</strong>
|
|
<p>{formatDate(employee.dateOfBirth)}</p>
|
|
</div>
|
|
|
|
<div>
|
|
<strong>Eintritt</strong>
|
|
<p>{formatDate(employee.entryDate)}</p>
|
|
</div>
|
|
|
|
<div>
|
|
<strong>Austritt</strong>
|
|
<p>{formatDate(employee.exitDate)}</p>
|
|
</div>
|
|
|
|
<div>
|
|
<strong>Beschäftigungsart</strong>
|
|
<p>{employee.employmentType ?? "—"}</p>
|
|
</div>
|
|
|
|
<div>
|
|
<strong>Qualifikation</strong>
|
|
<p>{employee.qualification ?? "—"}</p>
|
|
</div>
|
|
|
|
<div>
|
|
<strong>Notfallkontakt</strong>
|
|
<p>
|
|
{employee.emergencyContactName
|
|
? `${employee.emergencyContactName}${
|
|
employee.emergencyContactRelation ? ` (${employee.emergencyContactRelation})` : ""
|
|
}${employee.emergencyContactPhone ? ` — ${employee.emergencyContactPhone}` : ""}`
|
|
: "—"}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<OmsorgCard>
|
|
<div className="employee-detail">
|
|
<div className="employee-detail-header">
|
|
<div className="employee-detail-avatar">
|
|
{getInitials(employee.firstName, employee.lastName)}
|
|
</div>
|
|
|
|
<div>
|
|
<h2>{`${employee.firstName} ${employee.lastName}`.trim()}</h2>
|
|
<OmsorgBadge status={employee.status?.toLowerCase()} />
|
|
</div>
|
|
|
|
{canEdit && (
|
|
<OmsorgButton variant="secondary" icon={Pencil} onClick={() => setIsEditDialogOpen(true)}>
|
|
Bearbeiten
|
|
</OmsorgButton>
|
|
)}
|
|
</div>
|
|
|
|
<EmployeeTabs
|
|
activeTab={activeTab}
|
|
onChange={setActiveTab}
|
|
/>
|
|
|
|
{renderTabContent()}
|
|
</div>
|
|
|
|
{isEditDialogOpen && (
|
|
<EditEmployeeDialog
|
|
employee={employee}
|
|
onClose={() => setIsEditDialogOpen(false)}
|
|
onUpdated={(updatedEmployee) => {
|
|
setIsEditDialogOpen(false);
|
|
onUpdated?.(updatedEmployee);
|
|
}}
|
|
/>
|
|
)}
|
|
</OmsorgCard>
|
|
);
|
|
}
|