mirror of
https://github.com/Since-3/Kuma.git
synced 2026-10-12 21:41:17 +00:00
171 lines
6.3 KiB
TypeScript
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;
|