Kuma/src/modules/employee/ui/components/EmployeeListItem.tsx
2026-07-14 18:36:53 +02:00

171 lines
6.3 KiB
TypeScript

/**
* EmployeeListItem - Mitarbeiter-Karten-Komponente
*
* Diese Komponente zeigt einen einzelnen Mitarbeiter als Karte an.
* Sie wird in der Mitarbeiterliste verwendet und zeigt alle wichtigen Mitarbeiterinformationen
* inklusive Rollen, Onboarding-Status und Bearbeitungsoption.
*/
import { Pen, Trash2, Mail, Send, Loader2 } from "lucide-react";
import { Tooltip, TooltipTrigger, TooltipContent } from "@/src/components/ui/tooltip";
import { Avatar, AvatarFallback, AvatarImage } from "@/src/components/ui/avatar";
/**
* Props für die EmployeeListItem Komponente
*/
interface EmployeeListItemProps {
employeeId: string; // ID des Mitarbeiters
employeeName: string; // Name des Mitarbeiters (oder E-Mail wenn Name nicht vorhanden)
email: string; // E-Mail-Adresse
pbSrc?: string | null; // URL des Profilbilds
roles: string[]; // Rollen des Mitarbeiters
status: string; // Status ("draft" oder "published")
isOnboarded: boolean; // Ob der Mitarbeiter das Onboarding abgeschlossen hat
onEdit?: () => void; // Callback-Funktion beim Klick auf Bearbeiten-Button
onDelete?: () => void; // Callback-Funktion beim Klick auf Löschen-Button
onResendEmail?: () => void; // Callback-Funktion für erneutes Senden der Onboarding-Mail
isResendingEmail?: boolean; // Ob die Mail gerade versendet wird
showDeleteIcon?: boolean; // Ob das Löschen-Icon angezeigt werden soll
}
const getInitials = (name: string) => {
if (!name) return "";
const parts = name.trim().split(" ");
if (parts.length === 1) return parts[0][0].toUpperCase();
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
};
/**
* Mitarbeiter-Karten-Komponente
* Zeigt Mitarbeiterinformationen in einem visuell ansprechenden Karten-Layout
*/
const EmployeeListItem: React.FC<EmployeeListItemProps> = ({
employeeName,
email,
pbSrc,
roles,
status,
isOnboarded,
onEdit,
onDelete,
onResendEmail,
isResendingEmail,
showDeleteIcon,
}) => {
const canResend = status === "published" && !isOnboarded && onResendEmail;
return (
<div className="border border-white/60 bg-white/55 backdrop-blur-xl rounded-xl p-5 shadow-sm hover:shadow-md hover:bg-white/70 transition flex flex-col gap-3 relative">
{/* Avatar + Name/Rollen + Status Badges */}
<div className="flex justify-between items-start gap-3">
<div className="flex items-start gap-3 min-w-0">
<Avatar className="size-12 shrink-0">
{pbSrc && <AvatarImage src={pbSrc} alt={employeeName} />}
<AvatarFallback className="bg-blue-50 text-blue-700 font-semibold">
{getInitials(employeeName)}
</AvatarFallback>
</Avatar>
<div className="min-w-0">
<h2 className="text-xl font-semibold text-gray-900 truncate">{employeeName}</h2>
{roles.length > 0 && (
<div className="flex flex-wrap gap-1.5 mt-1">
{roles.map((role, index) => (
<span
key={index}
className="w-fit text-xs font-semibold px-2 py-1 rounded bg-blue-50 text-blue-700"
>
{role}
</span>
))}
</div>
)}
</div>
</div>
<div className="flex flex-col items-end gap-1 shrink-0">
<span
className={`w-fit text-xs font-semibold px-2 py-1 rounded ${
status === "draft" ? "bg-yellow-100 text-yellow-700" : "bg-green-100 text-green-700"
}`}
>
{status === "draft" ? "Entwurf" : "Veröffentlicht"}
</span>
<span
className={`w-fit text-xs font-semibold px-2 py-1 rounded ${
isOnboarded ? "bg-blue-100 text-blue-700" : "bg-gray-100 text-gray-700"
}`}
>
{isOnboarded ? "Onboarded" : "Ausstehend"}
</span>
</div>
</div>
<div className="h-px bg-white/50" />
{/* Actions */}
<div className="flex items-center justify-between">
<Tooltip>
<TooltipTrigger asChild>
<a
href={`mailto:${email}`}
aria-label={`E-Mail an ${email} senden`}
className="p-2 rounded-md hover:bg-white/50 transition flex items-center justify-center"
>
<Mail size={20} className="text-gray-700" />
</a>
</TooltipTrigger>
<TooltipContent side="top">{email}</TooltipContent>
</Tooltip>
<div className="flex items-center gap-1">
{canResend && (
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
onClick={onResendEmail}
disabled={isResendingEmail}
aria-label={
isResendingEmail
? "Onboarding-Mail wird erneut gesendet"
: "Onboarding-Mail erneut senden"
}
aria-busy={isResendingEmail || undefined}
className="p-2 rounded-md hover:bg-blue-500/10 transition disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center"
>
{isResendingEmail ? (
<Loader2 size={20} className="text-blue-600 animate-spin" />
) : (
<Send size={20} className="text-blue-600" />
)}
</button>
</TooltipTrigger>
<TooltipContent side="top">Onboarding-Mail erneut senden</TooltipContent>
</Tooltip>
)}
{showDeleteIcon && onDelete && (
<button
onClick={onDelete}
aria-label="Mitarbeiter löschen"
className="p-2 rounded-md hover:bg-red-500/10 transition flex items-center justify-center"
>
<Trash2 size={20} className="text-red-600" />
</button>
)}
{onEdit && (
<button
onClick={onEdit}
aria-label="Mitarbeiter bearbeiten"
className="p-2 rounded-md hover:bg-white/50 transition flex items-center justify-center"
>
<Pen size={20} className="text-gray-700" />
</button>
)}
</div>
</div>
</div>
);
};
export default EmployeeListItem;