209 lines
5.5 KiB
TypeScript
209 lines
5.5 KiB
TypeScript
import { format, isPast } from "date-fns";
|
|
import { de } from "date-fns/locale";
|
|
import { Calendar, MapPin, User } from "lucide-react";
|
|
|
|
import {
|
|
Card,
|
|
CardContent,
|
|
CardHeader,
|
|
CardTitle,
|
|
} from "@/components/ui/card";
|
|
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
|
import { Separator } from "@/components/ui/separator";
|
|
import { Label } from "@/components/ui/label";
|
|
import { TaskStatus, TaskWithRelations } from "@/types";
|
|
import { Badge } from "../ui/badge";
|
|
|
|
/**
|
|
* Props für TaskInfoCard Component
|
|
*/
|
|
interface TaskInfoCardProps {
|
|
task: TaskWithRelations;
|
|
}
|
|
|
|
/**
|
|
* Task Info Card Component
|
|
* Zeigt alle Details einer Task an
|
|
* - Nur shadcn/ui Komponenten (Card, Avatar, Separator, Label)
|
|
* - Formatierung mit date-fns
|
|
*/
|
|
export function TaskInfoCard({ task }: TaskInfoCardProps) {
|
|
|
|
/**
|
|
* Badge Name basierend auf Status
|
|
*/
|
|
const getStatusName = (status: TaskStatus) => {
|
|
switch (status) {
|
|
case "UNERLEDIGT":
|
|
return "Unerledigt" as const;
|
|
case "IN_BEARBEITUNG":
|
|
return "In Bearbeitung" as const;
|
|
case "ERLEDIGT":
|
|
return "Erledigt" as const;
|
|
default:
|
|
return "Unerledigt" as const;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Badge Variant basierend auf Status
|
|
*/
|
|
const getStatusVariant = (status: TaskStatus) => {
|
|
switch (status) {
|
|
case "UNERLEDIGT":
|
|
return "destructive" as const;
|
|
case "IN_BEARBEITUNG":
|
|
return "secondary" as const;
|
|
case "ERLEDIGT":
|
|
return "default" as const;
|
|
default:
|
|
return "outline" as const;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Initialen für Avatar generieren
|
|
*/
|
|
const getInitials = (name: string) => {
|
|
return name
|
|
.split(" ")
|
|
.map((n) => n[0])
|
|
.join("")
|
|
.toUpperCase()
|
|
.slice(0, 2);
|
|
};
|
|
|
|
/**
|
|
* Prüfe ob Frist überschritten ist
|
|
*/
|
|
const isOverdue = isPast(new Date(task.bisWann)) && task.status !== "ERLEDIGT";
|
|
|
|
return (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Details</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
{/* Status */}
|
|
<div className="space-y-2">
|
|
<Label className="flex items-center gap-2">
|
|
<Calendar className="h-4 w-4" />
|
|
Status
|
|
</Label>
|
|
<div>
|
|
<Badge variant={getStatusVariant(task.status)}>
|
|
{getStatusName(task.status)}
|
|
</Badge>
|
|
</div>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
{/* Ort */}
|
|
<div className="space-y-2">
|
|
<Label className="flex items-center gap-2">
|
|
<MapPin className="h-4 w-4" />
|
|
Ort
|
|
</Label>
|
|
<p className="text-sm">{task.wo}</p>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
{/* Frist */}
|
|
<div className="space-y-2">
|
|
<Label className="flex items-center gap-2">
|
|
<Calendar className="h-4 w-4" />
|
|
Frist
|
|
</Label>
|
|
<p className={`text-sm ${isOverdue ? "text-destructive font-semibold" : ""}`}>
|
|
{format(new Date(task.bisWann), "dd.MM.yyyy", { locale: de })}
|
|
{isOverdue && " (überfällig)"}
|
|
</p>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
{/* Zuständig */}
|
|
{task.assignee && (
|
|
<>
|
|
<div className="space-y-2">
|
|
<Label className="flex items-center gap-2">
|
|
<User className="h-4 w-4" />
|
|
Zuständig
|
|
</Label>
|
|
<div className="flex items-center gap-2">
|
|
<Avatar>
|
|
<AvatarFallback>
|
|
{getInitials(task.assignee.name)}
|
|
</AvatarFallback>
|
|
</Avatar>
|
|
<div>
|
|
<p className="text-sm font-medium">{task.assignee.name}</p>
|
|
<p className="text-xs text-muted-foreground">
|
|
{task.assignee.email}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<Separator />
|
|
</>
|
|
)}
|
|
|
|
{/* Erstellt von */}
|
|
<div className="space-y-2">
|
|
<Label>Erstellt von</Label>
|
|
<div className="flex items-center gap-2">
|
|
<Avatar>
|
|
<AvatarFallback>
|
|
{getInitials(task.creator.name)}
|
|
</AvatarFallback>
|
|
</Avatar>
|
|
<div>
|
|
<p className="text-sm font-medium">{task.creator.name}</p>
|
|
<p className="text-xs text-muted-foreground">
|
|
{task.creator.email}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
{/* Erstellt am */}
|
|
<div className="space-y-2">
|
|
<Label>Erstellt am</Label>
|
|
<p className="text-sm">
|
|
{format(new Date(task.createdAt), "dd.MM.yyyy HH:mm", { locale: de })}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Optional: Beauftragt am */}
|
|
{task.beauftragtAm && (
|
|
<>
|
|
<Separator />
|
|
<div className="space-y-2">
|
|
<Label>Beauftragt am</Label>
|
|
<p className="text-sm">
|
|
{format(new Date(task.beauftragtAm), "dd.MM.yyyy", { locale: de })}
|
|
</p>
|
|
</div>
|
|
</>
|
|
)}
|
|
|
|
{/* Optional: Firma beauftragt */}
|
|
{task.firmaBeauftragt && (
|
|
<>
|
|
<Separator />
|
|
<div className="space-y-2">
|
|
<Label>Firma beauftragt</Label>
|
|
<p className="text-sm">{task.firmaBeauftragt}</p>
|
|
</div>
|
|
</>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|