149 lines
4.6 KiB
TypeScript
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>
|
|
);
|
|
}
|