import { useEffect, useRef, useState } from 'react'; import { Images, Loader2 } from 'lucide-react'; import { useClient } from 'hooks/useClient'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { readableError } from './errors'; /** * Choose WHICH carousel a block shows. * * ── A reference, not a copy ── * * `carousels` is a named, ordered set built to be shown in more than one place — the old home carousel * already read one by id. So a carousel block stores that id and owns nothing else: reordering the set * reorders every block pointing at it, and a photograph swapped there is swapped everywhere. That is * the whole reason the set has a name instead of being a list of files on a page. * * It follows that this picker chooses a SET, and editing what is IN the set happens where the set * lives — under Carousels. A block that could also rearrange its own pictures would be a second place * to do the same job, and the two would disagree. * * ── No thumbnails, deliberately ── * * The plugin has no way to address a rendition: `MediaPicker` hands that job to the platform's file * browser, and every path stored here names an ORIGINAL that a browser cannot display. Rather than * build a second, worse image pipeline for a dialog, this shows the name and the count — which is what * tells two carousels apart, since the name is the thing somebody chose. */ export type CarouselSummary = { id: number; name: string; slug: string; items: unknown[]; }; export const CarouselPicker = ({ open, onClose, onPick, selected, }: { open: boolean; onClose: () => void; onPick: (carouselId: number) => void; /** The one this block already shows, so reopening the picker says where it stands. */ selected?: number | null; }) => { const client = useClient(); const clientRef = useRef(client); clientRef.current = client; const [carousels, setCarousels] = useState(null); const [error, setError] = useState(null); // Re-read on every open rather than once. A carousel created in the other panel while this session // has been running would otherwise be missing from a list that looks complete. useEffect(() => { if (!open) return; setCarousels(null); setError(null); void (async () => { try { const response = await clientRef.current.get<{ data: CarouselSummary[] }>('/fofos-admin/carousels'); setCarousels(response.data); } catch (failure) { setError(readableError(failure)); } })(); }, [open]); return ( !next && onClose()}> Choose a carousel {error &&

{error}

} {!carousels && !error && (

Loading…

)} {/* Nothing to choose is a real state and says what to do about it, rather than an empty box. Creating one belongs under Carousels — see the note above about one place per job. */} {carousels?.length === 0 && (

No carousels yet. Make one under Carousels, then come back.

)}
{carousels?.map((carousel) => { const count = Array.isArray(carousel.items) ? carousel.items.length : 0; const current = carousel.id === selected; return ( ); })}
); };