frontend/app/components/home/colle-card.tsx
2025-07-29 17:55:32 +02:00

178 lines
5.6 KiB
TypeScript

import type React from "react";
import type { Colle } from "~/lib/api";
import { DateTime } from "luxon";
import { Link, useNavigate } from "react-router";
import {
Card,
CardContent,
CardHeader,
CardFooter,
} from "~/components/ui/card";
import { User, UserCheck, Paperclip, Star, MapPinHouse } from "lucide-react";
import { Badge } from "~/components/ui/badge";
import { Button } from "~/components/ui/button";
import { titleCase } from "~/lib/utils";
// TODO: Preferences for subject colors
const getSubjectColor = (_: string) => {
// Mock placeholder function
return "bg-blue-100 text-blue-800"; // Default color
};
const getSubjectEmoji = (_: string) => {
// Mock placeholder function
return "📚"; // Default emoji
};
type ColleCardProps = {
colle: Colle;
onToggleFavorite: (id: number, favorite: boolean) => void;
beforeClick: () => void;
isFavorite: boolean;
};
export default function ColleCard({
colle,
onToggleFavorite,
beforeClick,
isFavorite,
}: ColleCardProps) {
const navigate = useNavigate();
// TODO: Remove this if scroll restoration is not needed (test first)
const handleCardClick = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
beforeClick();
setTimeout(() => navigate(`/colles/${colle.id}`), 100);
};
// TODO: Favorites
const handleToggleFavorite = (e: React.MouseEvent) => {
e.stopPropagation(); // Prevent card click
e.preventDefault();
const newValue = !isFavorite;
onToggleFavorite(colle.id, newValue);
};
const subjectColor = getSubjectColor(colle.subject.name);
const subjectEmoji = getSubjectEmoji(colle.subject.name);
return (
<Link to={`/colles/${colle.id}`} onClick={handleCardClick}>
<Card
id={`colle-${colle.id}`}
className="h-full cursor-pointer hover:shadow-md transition-shadow border-primary"
>
<CardHeader className="pb-0 pt-3 px-4 flex flex-row items-center justify-between">
<div>
<div className="font-medium">{formatDate(colle.date)}</div>
<div className="font-normal text-sm text-muted-foreground">
{formatTime(colle.date)}
</div>
</div>
{colle.grade && (
<div className="flex items-center gap-2 h-full mb-3">
<Button
variant="ghost"
size="icon"
className={`h-8 w-8 ${
isFavorite ? "text-yellow-500" : "text-muted-foreground"
}`}
onClick={handleToggleFavorite}
>
<Star
className={`h-5 w-5 ${isFavorite ? "fill-yellow-500" : ""}`}
/>
<span className="sr-only">Ajouter aux favoris</span>
</Button>
<div
className={`px-2 py-1 rounded-md text-sm font-medium ${subjectColor}`}
>
{formatGrade(colle.grade)}/20
</div>
</div>
)}
</CardHeader>
<CardContent className="pb-0 pt-0 px-4" onClick={handleCardClick}>
<div className="flex items-center justify-between">
<div className="space-y-1">
<div className="flex items-center gap-2">
<User className="h-4 w-4 text-muted-foreground" />
<span className="font-medium">{colle.student.fullName}</span>
</div>
<div className="flex items-center gap-2">
<UserCheck className="h-4 w-4 text-muted-foreground" />
<span>{colle.examiner.name}</span>
</div>
{colle.room && (
<div className="flex items-center gap-2">
<MapPinHouse className="h-4 w-4 text-muted-foreground" />
<span>{colle.room.name}</span>
</div>
)}
</div>
</div>
</CardContent>
<CardFooter className="pt-1 pb-3 px-4 flex flex-col items-start">
<div className="w-full flex justify-between items-center">
<div className="flex items-center gap-1">
<Badge className={subjectColor}>
{colle.subject.name + " " + subjectEmoji}
</Badge>
{isFavorite && (
<Badge variant="secondary">
<Star className="h-3 w-3 fill-yellow-500 text-yellow-500 mr-1" />
Favori
</Badge>
)}
</div>
{/* TODO: Attachments */}
{colle.attachmentsCount > 0 && (
<div className="flex items-center gap-1 text-muted-foreground">
<Paperclip className="h-3.5 w-3.5" />
<span className="text-xs">{colle.attachmentsCount}</span>
</div>
)}
</div>
</CardFooter>
</Card>
</Link>
);
}
const formatDate = (date: string) => {
const dt = DateTime.fromISO(date).setLocale("fr");
const str = dt.toLocaleString({
weekday: "long",
day: "numeric",
month: "long",
year: "numeric",
});
return titleCase(str);
};
const formatTime = (date: string) => {
const dt = DateTime.fromISO(date).setLocale("fr");
return dt.toLocaleString({
hour: "2-digit",
minute: "2-digit",
});
};
const formatGrade = (grade?: number) => {
if (grade === undefined || grade === null || grade < 0 || grade > 20)
return "N/A";
const rounded = Math.round(grade * 10) / 10;
const str =
rounded % 1 === 0
? rounded.toFixed(0) // no decimals if .0
: rounded.toFixed(1); // one decimal otherwise
return str.replace(".", ",").padStart(2, "0"); // pad with zero if needed
};