'use client'; import * as React from 'react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useLocale, useTranslations } from 'next-intl'; import { BookOpen, Check, Pencil, RefreshCw, X } from 'lucide-react'; import { getWikiReviewQueue, publishWikiContribution, rejectWikiContribution, type WikiContributionReview, type WikiContributionStatus, } from '@/lib/api/trade-assistant'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Callout } from '@/components/ui/callout'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { PageHeader } from '@/components/ui/PageHeader'; import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Textarea } from '@/components/ui/textarea'; import { EmptyState, ErrorState, PageSpinner } from '@/components/ui/states'; import { useToast } from '@/components/ui/toast'; /** * Relecture des pages que l'assistant propose au wiki. * * Le wiki est global : une page validee devient visible pour tous les clients * et citable par l'assistant dans toutes ses reponses suivantes. L'ecran est * donc construit pour qu'on lise avant de decider — le corps complet est * affiche, pas un extrait — et pour qu'on puisse corriger au passage plutot * que de renvoyer une page presque juste. */ export default function WikiReviewPage() { const t = useTranslations('admin.wikiReview'); const locale = useLocale(); const { toast } = useToast(); const queryClient = useQueryClient(); const [filter, setFilter] = React.useState('pending'); const queue = useQuery({ queryKey: ['wiki-review', filter], queryFn: () => getWikiReviewQueue(filter), }); const refresh = () => { queryClient.invalidateQueries({ queryKey: ['wiki-review'] }); // La page publique et la recherche lisent le meme jeu : une decision les // perime toutes les deux. queryClient.invalidateQueries({ queryKey: ['wiki-complements'] }); }; const publish = useMutation({ mutationFn: ({ id, body }: { id: string; body?: string }) => publishWikiContribution(id, body ? { body } : undefined), onSuccess: page => { toast.success(t('publishDone', { title: page.title })); refresh(); }, onError: () => toast.error(t('publishFailed')), }); const reject = useMutation({ mutationFn: ({ id, note }: { id: string; note?: string }) => rejectWikiContribution(id, note), onSuccess: page => { toast.success(t('rejectDone', { title: page.title })); refresh(); }, onError: () => toast.error(t('rejectFailed')), }); if (queue.isPending) return ; if (queue.isError) { return ( queue.refetch()} /> ); } const pages = queue.data.contributions; return ( <> queue.refetch()}> {t('refresh')} } />
{t('noticeBody')} setFilter(value as WikiContributionStatus)}> {t('tabs.pending')} {t('tabs.published')} {t('tabs.rejected')} {pages.length === 0 ? ( ) : ( pages.map(page => ( publish.mutate({ id, body })} onReject={(id, note) => reject.mutate({ id, note })} /> )) )}
); } /* -------------------------------------------------------------------------- */ interface ReviewCardProps { page: WikiContributionReview; locale: string; busy: boolean; onPublish: (id: string, body?: string) => void; onReject: (id: string, note?: string) => void; } /** * Une proposition, lisible en entier. * * Deux champs facultatifs, jamais ouverts par defaut : la correction du corps * et le motif du refus. Les afficher en permanence ferait de la relecture une * saisie ; les cacher derriere un bouton garde l'ecran sur la lecture. */ function ReviewCard({ page, locale, busy, onPublish, onReject }: ReviewCardProps) { const t = useTranslations('admin.wikiReview'); const [body, setBody] = React.useState(null); const [note, setNote] = React.useState(null); const editing = body !== null; const pending = page.status === 'pending'; const date = (value: string) => new Date(value).toLocaleString(locale === 'fr' ? 'fr-FR' : 'en-GB', { dateStyle: 'medium', timeStyle: 'short', }); return (
{t(`status.${page.status}`)} {page.topic} {page.locale.toUpperCase()}
{page.title}

{page.section}

{editing ? (