import { useEffect, useState } from 'react'; import { useQuery } from '@tanstack/react-query'; import { api } from '@/api/client'; import type { GridRow } from '@/api/types'; import { SourceText } from '@/components/SourceText'; import { TranslationInput } from '@/editor/TranslationInput'; interface Props { projectUuid: string; locale: string; direction: 'ltr' | 'rtl'; pluralCategories: string[]; platform?: string; onClose: () => void; } /** * Mode Focus — la fonctionnalité qui différencie l'outil. * * Une traductrice n'explore pas un catalogue : elle **vide une file**. Lui * proposer une grille et la laisser choisir par où commencer, c'est lui * transférer une décision qu'elle n'a pas envie de prendre quarante-sept fois. * * Ici : une clé, tout son contexte, et deux raccourcis. Pas de menu, pas de * barre latérale, pas de souris. C'est ce qui fait passer « traduire 47 clés » * de quarante-cinq minutes à douze. */ export function FocusMode({ projectUuid, locale, direction, pluralCategories, platform, onClose, }: Props) { const [index, setIndex] = useState(0); const [done, setDone] = useState>(new Set()); // La file est chargée UNE fois à l'ouverture. La rafraîchir à chaque // enregistrement ferait disparaître la ligne courante sous les doigts — // le pire défaut possible pour un mode de saisie en rafale. const queue = useQuery({ queryKey: ['focus-queue', projectUuid, locale, platform], queryFn: () => api.grid({ projectUuid, locale, platform, status: 'untranslated', itemsPerPage: 200, }), staleTime: Infinity, refetchOnWindowFocus: false, }); const rows = queue.data?.items ?? []; const current: GridRow | undefined = rows[index]; useEffect(() => { function onKey(event: KeyboardEvent) { if (event.key === 'Escape') { event.preventDefault(); onClose(); } } window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }, [onClose]); function next() { setIndex((value) => Math.min(value + 1, rows.length)); } function previous() { setIndex((value) => Math.max(value - 1, 0)); } return (
{Math.min(index + 1, rows.length)} / {rows.length}
{done.size} traitée{done.size > 1 ? 's' : ''}
{queue.isLoading &&

Constitution de la file…

} {!queue.isLoading && current === undefined && (

File terminée.

{done.size > 0 ? `${done.size} traduction${done.size > 1 ? 's' : ''} enregistrée${done.size > 1 ? 's' : ''}.` : 'Aucune clé ne restait à traiter.'}

)} {current !== undefined && (

{current.keyPath}

{current.platforms.map((slug) => ( {slug} ))}
{current.description !== null && current.description !== '' && (

{current.description}

)}

{current.sourceValue !== null && }

setDone((prev) => new Set(prev).add(current.id))} onRequestNext={next} />

⌘↵{' '} enregistrer et suivante Échap{' '} quitter

)}
); }