import "katex/dist/katex.min.css";
import { DomUtils, parseDocument } from "htmlparser2";
// TODO: API - remove trailing lines from HTML comment/content
// TODO: Server side image extraction and latex rendering
// TEMP SOLUTION
import { renderLatex } from "~/lib/latex"; // Custom LaTeX rendering function
// function removeTrailingLines(htmlString: string) {
// return htmlString.replace(/( \s*)+$/gi, "").trim();
// }
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 { 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);
// TODO: Handle user loading state
if (userError) {
console.error(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 (
{/*
*/}
goBack(navigate)}>
Retour
Recharger
Résumé de Colle
{colle.grade && (
{formatGrade(colle.grade)}/20
)}
{colle.subject.name} {subjectEmoji}
{/* Desktop Action Buttons */}
Partager
Recharger
Ajouter aux favoris
{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 && (
)}
{colle.content && (
<>
>
)}
{colle.comment && (
<>
>
)}
{/* 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: */}
{false ? "Favori" : "Ajouter"}
Partager
);
}
// 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 (
{renderLatex(displayedComment)}
{isLongComment && (
{isExpanded ? (
Afficher moins
) : (
Afficher plus
)}
)}
);
}
// 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();
}