'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 } 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 }[]; 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(); // 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 userRating = ratings.find((r) => r.userId === user.id)?.rating ?? null; return (
{/* Section header */}
{/* Colored accent bar */}
{user.name || user.email} {isCurrentUser && ( vous )}

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

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

Aucun GIF pour le moment

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

Aucun GIF pour le moment

)}
)}
); })}
); }