feat: add attachments

This commit is contained in:
Nathan Lamy 2025-08-19 20:07:32 +02:00
parent a89546c54e
commit 6428126514
4 changed files with 61 additions and 31 deletions

View file

@ -1,16 +1,16 @@
import { FileText, Image, File } from "lucide-react";
import type { Attachment } from "~/lib/api";
export default function AttachmentItem({ attachment }: { attachment: string }) {
export default function AttachmentItem({ attachment }: { attachment: Attachment }) {
return (
<a
// TODO: BAD: hardcoded URL, should be dynamic (environment variable or config)
href={"https://bjcolle.fr/" + attachment}
href={"https://bjcolle.fr/" + attachment.path}
target="_blank"
className="flex items-center gap-2 p-3 border rounded-md hover:bg-muted transition-colors cursor-pointer"
>
{getIcon(attachment)}
{getIcon(attachment.name)}
<span className="font-medium truncate">
{getName(attachment) || "Sans Nom"}
{attachment.name}
</span>
</a>
);
@ -44,9 +44,3 @@ const getIcon = (attachment: string) => {
return <File className="h-5 w-5 text-gray-500" />;
}
};
const getName = (attachment: string) => {
const parts = attachment.replace("pj_doc", "").split("_");
const nameParts = parts.slice(2); // remove the first two parts
return nameParts.join("_");
};

View file

@ -2,7 +2,7 @@ import type { Colle, UserPreferences } from "~/lib/api";
import { Link } from "react-router";
import { Card } from "~/components/ui/card";
import { User, Star, CalendarDays, MapPin } from "lucide-react";
import { User, Star, CalendarDays, MapPin, Paperclip } from "lucide-react";
import { Badge } from "~/components/ui/badge";
import {
cn,
@ -35,8 +35,11 @@ export default function ColleCard({
// onToggleFavorite(colle.id, newValue);
// };
const subjectColor = getColorClass(getSubjectColor(colle.subject.name, preferences));
const subjectColor = getColorClass(
getSubjectColor(colle.subject.name, preferences)
);
const subjectEmoji = getSubjectEmoji(colle.subject.name, preferences);
const attachmentsCount = colle.attachments?.length || 0;
return (
<Link to={`/colles/${colle.id}`}>
@ -114,13 +117,12 @@ export default function ColleCard({
</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>
)} */}
{attachmentsCount > 0 && (
<div className="flex items-center gap-1 text-muted-foreground">
<Paperclip className="h-3.5 w-3.5" />
<span className="text-xs">{attachmentsCount}</span>
</div>
)}
</div>
</div>
</div>

View file

@ -182,7 +182,7 @@ export interface Colle {
bjid?: string; // Optional field
content?: string; // Optional field
comment?: string; // Optional field
attachments?: string[]; // Optional field, array of attachment URLs
attachments?: Attachment[]; // Optional field, array of attachment URLs
}
interface CollePayload {
@ -278,6 +278,20 @@ export const useColle = (id: number) => {
};
};
export const refreshColle = async (id: number) => {
return makePostRequest(
`/colles/${id}/refresh`,
{},
"Échec de la demande de rafraîchissement de la colle",
"POST"
);
};
export interface Attachment {
path: string;
name: string;
}
/**
* === SUBJECTS API ===
*/

View file

@ -25,9 +25,10 @@ import ColleDetailsSkeleton from "~/components/details/skeleton-details";
import AttachmentItem from "~/components/details/attachment";
import Error from "~/components/error";
import { Badge } from "~/components/ui/badge";
import { AUTH_ERROR, useColle, useUser } from "~/lib/api";
import { AUTH_ERROR, refreshColle, useColle, useUser } from "~/lib/api";
import { toast } from "sonner";
import { formatDate, formatGrade, formatTime, goBack } from "~/lib/utils";
import { useQueryClient } from "@tanstack/react-query";
// TODO: Preferences for subject colors
const getSubjectColor = (_: string) => {
@ -42,10 +43,17 @@ const getSubjectEmoji = (_: string) => {
// TODO: Move all code to components
export default function ColleDetailPage() {
const { user, isLoading: isUserLoading, error: userError } = useUser();
const queryClient = useQueryClient();
const navigate = useNavigate();
const params = useParams<{ colleId: string }>();
const colleId = parseInt(params.colleId!);
if (isNaN(colleId)) {
return <Navigate to="/" />;
}
const { colle, error, isLoading } = useColle(colleId);
const [isReloading, setIsReloading] = useState(false);
if (isUserLoading) {
@ -61,12 +69,6 @@ export default function ColleDetailPage() {
// TODO: Favorite toggle function
const toggleStar = () => {};
const colleId = parseInt(params.colleId!);
if (isNaN(colleId)) {
return <Navigate to="/" />;
}
const { colle, error, isLoading } = useColle(colleId);
if (error)
return (
<Error
@ -82,7 +84,26 @@ export default function ColleDetailPage() {
const handleReload = () => {
setIsReloading(true);
// TODO: HARD RELOAD
refreshColle(colle.id)
.then(() => {
toast("Les données de cette colle vont être mises à jour", {
icon: "🔄",
description: "Rafraîchissement en cours...",
});
})
.catch((error) => {
console.error("Error refreshing colle:", error);
toast.error("Échec du rafraîchissement de la colle", {
icon: "❌",
description: "Veuillez réessayer plus tard.",
});
})
.finally(() => {
setIsReloading(false);
queryClient.invalidateQueries({
queryKey: ["colle", colle.id],
});
});
};
const handleShare = async () => {
@ -331,12 +352,11 @@ export default function ColleDetailPage() {
</>
)}
{/* TODO: Attachments */}
{colle.attachments && colle.attachments?.length > 0 && (
<div>
<h3 className="text-lg font-medium mb-3 flex items-center gap-2">
<Paperclip className="h-5 w-5" />
Attachments
Pièces jointes
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
{colle.attachments.map((attachment, index) => (