'use client'; import { useMemo, useState, useTransition } from 'react'; import { DndContext, closestCenter, PointerSensor, useSensor, useSensors, type DragEndEvent, } from '@dnd-kit/core'; import { SortableContext, rectSortingStrategy, useSortable, arrayMove, } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; import { setGifMoodUserRating, reorderGifMoodItems, setGifMoodUserHidden } from '@/actions/gif-mood'; import { Avatar } from '@/components/ui/Avatar'; import { GifMoodCard } from './GifMoodCard'; import { GifMoodAddForm } from './GifMoodAddForm'; import { GIF_MOOD_MAX_ITEMS } from '@/lib/types'; interface GifMoodItem { id: string; gifUrl: string; note: string | null; order: number; userId: string; user: { id: string; name: string | null; email: string; }; } interface Share { id: string; userId: string; user: { id: string; name: string | null; email: string; }; } interface GifMoodBoardProps { sessionId: string; currentUserId: string; items: GifMoodItem[]; shares: Share[]; owner: { id: string; name: string | null; email: string; }; ratings: { userId: string; rating: number | null; hidden: boolean }[]; canEdit: boolean; } function WeekRating({ sessionId, isCurrentUser, canEdit, initialRating, }: { sessionId: string; isCurrentUser: boolean; canEdit: boolean; initialRating: number | null; }) { const [prevInitialRating, setPrevInitialRating] = useState(initialRating); const [rating, setRating] = useState(initialRating); const [hovered, setHovered] = useState(null); const [isPending, startTransition] = useTransition(); if (prevInitialRating !== initialRating) { setPrevInitialRating(initialRating); setRating(initialRating); } const interactive = isCurrentUser && canEdit; const display = hovered ?? rating; function handleClick(n: number) { if (!interactive) return; setRating(n); startTransition(async () => { await setGifMoodUserRating(sessionId, n); }); } return (
setHovered(null)} > {[1, 2, 3, 4, 5].map((n) => { const filled = display !== null && n <= display; return ( ); })}
); } function DragHandle(props: React.HTMLAttributes) { return (
); } function SortableGifMoodCard({ sessionId, item, currentUserId, canEdit, }: { sessionId: string; item: GifMoodItem; currentUserId: string; canEdit: boolean; }) { const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: item.id, }); return (
); } // Subtle accent colors for each user section const SECTION_COLORS = ['#ec4899', '#8b5cf6', '#3b82f6', '#10b981', '#f59e0b', '#ef4444']; const GRID_COLS: Record = { 4: 'grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4', 5: 'grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5', 6: 'grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6', }; function GridIcon({ cols }: { cols: number }) { return ( {Array.from({ length: cols }).map((_, i) => { const w = (20 - (cols - 1) * 2) / cols; const x = i * (w + 2); return ( ); })} ); } export function GifMoodBoard({ sessionId, currentUserId, items, shares, owner, ratings, canEdit, }: GifMoodBoardProps) { const [cols, setCols] = useState(4); const [, startReorderTransition] = useTransition(); const [, startHiddenTransition] = useTransition(); // Optimistic reorder state for the current user's items const [optimisticItems, setOptimisticItems] = useState([]); const [prevPropsItems, setPrevPropsItems] = useState(items); if (prevPropsItems !== items) { setPrevPropsItems(items); setOptimisticItems([]); } const sensors = useSensors( useSensor(PointerSensor, { activationConstraint: { distance: 5 } }) ); const allUsers = useMemo(() => { const map = new Map(); map.set(owner.id, owner); shares.forEach((s) => map.set(s.userId, s.user)); return Array.from(map.values()); }, [owner, shares]); const itemsByUser = useMemo(() => { const map = new Map(); items.forEach((item) => { const existing = map.get(item.userId) ?? []; existing.push(item); map.set(item.userId, existing); }); return map; }, [items]); const sortedUsers = useMemo(() => { return [...allUsers].sort((a, b) => { if (a.id === currentUserId) return -1; if (b.id === currentUserId) return 1; if (a.id === owner.id) return -1; if (b.id === owner.id) return 1; return (a.name || a.email).localeCompare(b.name || b.email, 'fr'); }); }, [allUsers, currentUserId, owner.id]); function handleDragEnd(event: DragEndEvent) { const { active, over } = event; if (!over || active.id === over.id) return; const currentItems = optimisticItems.length > 0 ? optimisticItems : (itemsByUser.get(currentUserId) ?? []); const oldIndex = currentItems.findIndex((i) => i.id === active.id); const newIndex = currentItems.findIndex((i) => i.id === over.id); if (oldIndex === -1 || newIndex === -1) return; const reordered = arrayMove(currentItems, oldIndex, newIndex); setOptimisticItems(reordered); startReorderTransition(async () => { await reorderGifMoodItems(sessionId, reordered.map((i) => i.id)); }); } return (
{/* Column size control */}
{[4, 5, 6].map((n) => ( ))}
{sortedUsers.map((user, index) => { const isCurrentUser = user.id === currentUserId; const serverItems = itemsByUser.get(user.id) ?? []; const userItems = isCurrentUser && optimisticItems.length > 0 ? optimisticItems : serverItems; const canAdd = canEdit && isCurrentUser && userItems.length < GIF_MOOD_MAX_ITEMS; const accentColor = SECTION_COLORS[index % SECTION_COLORS.length]; const userRatingEntry = ratings.find((r) => r.userId === user.id); const userRating = userRatingEntry?.rating ?? null; const isHidden = userRatingEntry?.hidden ?? false; const showHidden = !isCurrentUser && isHidden; // Skip participants who haven't added any GIFs yet (except current user) if (!isCurrentUser && userItems.length === 0) return null; return (
{/* Section header */}
{/* Colored accent bar */}
{user.name || user.email} {isCurrentUser && ( vous )}

{userItems.length} / {GIF_MOOD_MAX_ITEMS} GIF{userItems.length !== 1 ? 's' : ''}

{/* Hide/reveal toggle — current user only */} {isCurrentUser && canEdit && userItems.length > 0 && ( )}
{/* Hidden placeholder for other users */} {showHidden ? (

{userItems.length > 0 ? `${userItems.length} GIF${userItems.length !== 1 ? 's' : ''} caché${userItems.length !== 1 ? 's' : ''}` : 'GIFs cachés'}

) : null} {/* Grid */} {!showHidden && isCurrentUser && canEdit ? ( i.id)} strategy={rectSortingStrategy} >
{userItems.map((item) => ( ))} {canAdd && ( )} {!canAdd && userItems.length === 0 && (

Aucun GIF pour le moment

)}
) : !showHidden ? (
{userItems.map((item) => ( ))} {!canAdd && userItems.length === 0 && (

Aucun GIF pour le moment

)}
) : null}
); })}
); }