import "katex/dist/katex.min.css"; import { DomUtils, parseDocument } from "htmlparser2"; import Latex from "react-latex-next"; import { useState } from "react"; import { Navigate, useNavigate, useParams } from "react-router"; import { Button } from "~/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"; import { ArrowLeft, Clock, User, UserCheck, MessageSquare, Paperclip, Star, ChevronUp, ChevronDown, MapPinHouse, Users, RefreshCw, Share2, ExternalLink, } from "lucide-react"; import { Separator } from "~/components/ui/separator"; import UserDropdown from "~/components/user-dropdown"; import ColleDetailsSkeleton from "~/components/details/skeleton-details"; import AttachmentItem from "~/components/details/attachment"; // TODO: Scroll restoration // import { ScrollToTopOnMount } from "~/components/noscroll"; import Error from "~/components/error"; import { Badge } from "~/components/ui/badge"; import { AUTH_ERROR, useColle, useUser } from "~/lib/api"; import { toast } from "sonner"; import { formatDate, formatGrade, formatTime, goBack } 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 }; // TODO: Move all code to components export default function ColleDetailPage() { const { user, isLoading: isUserLoading, error: userError } = useUser(); const navigate = useNavigate(); const params = useParams<{ colleId: string }>(); const [isReloading, setIsReloading] = useState(false); if (isUserLoading) { return ; } if (userError?.message === AUTH_ERROR) { return ; } if (userError) { return ; } // TODO: Favorite toggle function const toggleStar = () => {}; const colleId = parseInt(params.colleId!); if (isNaN(colleId)) { return ; } const { colle, error, isLoading } = useColle(colleId); if (error) return ( ); if (isLoading || !colle) return ; const handleToggleFavorite = () => {}; const handleReload = () => { setIsReloading(true); // TODO: HARD RELOAD }; const handleShare = async () => { const shareUrl = window.location.href; const shareTitle = `Colle de ${colle.subject.name} - ${colle.student.firstName}`; const shareText = `Colle de ${colle.subject.name} du ${formatDate( colle.date )} à ${formatTime(colle.date)} - ${colle.student} avec ${ colle.examiner }.\n\nConsultez-le résumé ici :`; try { if (navigator.share) { await navigator.share({ title: shareTitle, text: shareText, url: shareUrl, }); } else { // Fallback to copying the URL await navigator.clipboard.writeText(shareUrl); toast("Lien copié dans le presse-papiers", { icon: "📋", description: "Vous pouvez le partager avec vos amis !", }); } } catch (error) { console.error("Error sharing:", error); } }; const handleOpenBJColle = () => { const url = `https://bjcolle.fr/students_oral_disp.php?colle=${colle.bjid}&hgfebrgl8ri3h=${colle.bjsecret}`; window.open(url, "_blank"); toast("Ouverture de BJColle", { icon: "🔗", description: "Redirection vers BJColle...", }); }; const subjectColor = getSubjectColor(colle.subject.name); const subjectEmoji = getSubjectEmoji(colle.subject.name); return (
{/* */}
Résumé de Colle {colle.grade && (
{formatGrade(colle.grade)}/20
)}
{colle.subject.name} {subjectEmoji}
{/* Desktop Action Buttons */}
{colle.grade && (
{formatGrade(colle.grade)}/20
)}

Date

{formatDate(colle.date)}, à {formatTime(colle.date)}

Colleur

{colle.examiner.name}

Étudiant

{colle.student.fullName}
{/* TODO: Colles groups - others students */} {/* {colle.group?.length > 0 && (

Autres élèves

{colle.group.map((linkedColle) => (
{linkedColle.student}
))}
)} */}
{colle.room && (

Salle

{colle.room.name}
)}
{colle.content && ( <>

Sujet

)} {colle.comment && ( <>

Commentaires

)} {/* TODO: Attachments */} {colle.attachments && colle.attachments?.length > 0 && (

Attachments

{colle.attachments.map((attachment, index) => ( ))}
)} {/* BJColle External Link */}
Accéder à la colle depuis BJColle
{/* Mobile Action Bar - Fixed at Bottom */}
); } // TODO: Custom render component for LaTeX rendering // Component for expandable comments function ExpandableComment({ comment }: { comment: string }) { // Crop comments longer than 750 characters const [isExpanded, setIsExpanded] = useState(false); const commentLimit = 750; // Character limit before truncating const isLongComment = removeHtmlElements(comment).length > commentLimit; const toggleExpand = () => { setIsExpanded(!isExpanded); }; const displayedComment = isExpanded || !isLongComment ? comment : `${comment.substring(0, commentLimit)}...`; return (
{displayedComment} {isLongComment && ( )}
); } // Remove HTML elements and return plain text (for text length calculation and cropping) function removeHtmlElements(htmlString: string) { const document = parseDocument(htmlString); return DomUtils.textContent(document).trim(); }