xtablo-source/apps/main/src/components/ProjectCard.tsx
2026-03-08 21:28:44 +01:00

149 lines
4.6 KiB
TypeScript

import { cn } from "@xtablo/shared";
import type { UserTablo } from "@xtablo/shared/types/tablos.types";
import { Calendar, Trash2 } from "lucide-react";
import { useTranslation } from "react-i18next";
type StatusConfig = {
label: string;
badgeClass: string;
progressColor: string;
};
function useStatusConfig(status: string): StatusConfig {
const { t } = useTranslation("pages");
switch (status) {
case "todo":
return {
label: t("tablo.status.todo"),
badgeClass:
"bg-blue-50 text-blue-600 border-blue-200 dark:bg-blue-950/30 dark:text-blue-400 dark:border-blue-800",
progressColor: "bg-blue-500",
};
case "in_progress":
return {
label: t("tablo.status.inProgress"),
badgeClass:
"bg-yellow-50 text-yellow-600 border-yellow-200 dark:bg-yellow-950/30 dark:text-yellow-400 dark:border-yellow-800",
progressColor: "bg-purple-500",
};
case "done":
return {
label: t("tablo.status.done"),
badgeClass:
"bg-green-50 text-green-600 border-green-200 dark:bg-green-950/30 dark:text-green-400 dark:border-green-800",
progressColor: "bg-green-500",
};
default:
return {
label: t("tablo.status.todo"),
badgeClass:
"bg-blue-50 text-blue-600 border-blue-200 dark:bg-blue-950/30 dark:text-blue-400 dark:border-blue-800",
progressColor: "bg-blue-500",
};
}
}
function getProgressFromStatus(status: string): number {
switch (status) {
case "todo":
return 0;
case "in_progress":
return 50;
case "done":
return 100;
default:
return 0;
}
}
export interface ProjectCardProps {
tablo: UserTablo;
onClick?: (tabloId: string) => void;
onMenuClick?: (tabloId: string) => void;
className?: string;
}
export function ProjectCard({ tablo, onClick, onMenuClick, className }: ProjectCardProps) {
const { t } = useTranslation("pages");
const statusConfig = useStatusConfig(tablo.status);
const progress = getProgressFromStatus(tablo.status);
const formattedDate = new Intl.DateTimeFormat(undefined, {
month: "short",
day: "2-digit",
year: "numeric",
}).format(new Date(tablo.created_at));
return (
<div
className={cn(
"bg-white dark:bg-gray-800 rounded-2xl p-4 border border-[#EAECF0] dark:border-gray-700 hover:shadow-md transition-shadow cursor-pointer",
className
)}
onClick={() => onClick?.(tablo.id)}
>
{/* Status + Menu */}
<div className="flex items-start justify-between mb-4">
<span
className={cn(
"px-3 py-1 rounded-full text-xs font-medium border",
statusConfig.badgeClass
)}
>
{statusConfig.label}
</span>
<button
className="text-gray-400 hover:text-red-500 dark:text-gray-500 dark:hover:text-red-400"
onClick={(e) => {
e.stopPropagation();
onMenuClick?.(tablo.id);
}}
>
<Trash2 className="w-4 h-4" />
</button>
</div>
{/* Thumbnail + Name */}
<div className="flex items-center gap-3 mb-4">
<div
className={cn(
"w-12 h-12 rounded-xl flex items-center justify-center shrink-0 overflow-hidden",
!tablo.image && (tablo.color || "bg-gray-400")
)}
>
{tablo.image ? (
<img src={tablo.image} alt={tablo.name} className="w-full h-full object-cover" />
) : (
<span className="text-white font-bold text-lg">
{tablo.name.charAt(0).toUpperCase()}
</span>
)}
</div>
<h3 className="font-semibold text-gray-900 dark:text-gray-100 flex-1 truncate">
{tablo.name}
</h3>
</div>
{/* Date */}
<div className="flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400 mb-4">
<Calendar className="w-4 h-4" />
<span>{formattedDate}</span>
</div>
{/* Progress */}
<div className="mb-3">
<div className="flex items-center justify-between text-sm mb-2">
<span className="text-gray-600 dark:text-gray-400">{t("tablo.card.progress")}:</span>
<span className="font-semibold text-gray-900 dark:text-gray-100">{progress}%</span>
</div>
<div className="w-full bg-gray-100 dark:bg-gray-700 rounded-full h-2">
<div
className={cn("h-2 rounded-full transition-all", statusConfig.progressColor)}
style={{ width: `${progress}%` }}
/>
</div>
</div>
</div>
);
}