import {useCallback, useEffect, useState} from 'react'; import {Link} from 'react-router-dom'; import {useTranslation} from 'react-i18next'; import {ChevronLeft, ChevronRight, Link2, Loader2, X} from 'lucide-react'; import {toast} from 'sonner'; import {Badge} from '@/components/ui/badge'; import {Button} from '@/components/ui/button'; import {Card, CardContent, CardHeader, CardTitle} from '@/components/ui/card'; import GameCover from '../components/GameCover.jsx'; import FitText from '../components/FitText.jsx'; import PageContainer from '../components/PageContainer.jsx'; import {dismissPair, fetchLinkQueue, linkGames} from '../utils/links.js'; const PAGE_SIZE = 20; function PairGame({game}) { return ( ); } /** * AUDITOR or higher: the pairs IGDB suggests and users asked for. Core checks the role. */ export default function LinkQueuePage() { const {t} = useTranslation(); const [page, setPage] = useState(0); const [result, setResult] = useState({content: [], totalPages: 0}); const [status, setStatus] = useState('loading'); const [busy, setBusy] = useState(false); const load = useCallback(async () => { try { const response = await fetchLinkQueue({page, size: PAGE_SIZE}); if (response.status === 401) return; if (response.status === 403) { setStatus('forbidden'); return; } if (!response.ok) { setStatus('error'); return; } const data = await response.json(); // The last pair of the last page was decided: show the page before. if (data.content.length === 0 && page > 0) { setPage(page - 1); return; } setResult(data); setStatus('ready'); } catch (err) { console.error('Link queue error:', err); setStatus('error'); } }, [page]); useEffect(() => { load(); }, [load]); const run = async (request, doneKey) => { setBusy(true); try { const response = await request(); if (response.status === 401) return; if (response.ok) { toast.success(t(doneKey)); } else { const data = await response.json().catch(() => ({})); toast.error(t(data.code === 'ALREADY_LINKED' ? 'LINKS.ERROR_CODES.ALREADY_LINKED' : 'LINKS.ERROR')); } await load(); } catch (err) { console.error('Link queue action error:', err); toast.error(t('INFO.ERROR.NETWORK_FAILURE')); } finally { setBusy(false); } }; if (status === 'forbidden') { return ( {t('LINKS.NO_ACCESS')} ); } return ( {t('LINKS.QUEUE_TITLE')} {status === 'loading' && } {status === 'error' && {t('LINKS.ERROR')}} {status === 'ready' && result.content.length === 0 && ( {t('LINKS.QUEUE_EMPTY')} )} {status === 'ready' && result.content.length > 0 && ( {result.content.map(({a, b, igdb, requests}) => ( {igdb && {t('LINKS.FROM_IGDB')}} {requests > 0 && {t('LINKS.REQUESTED_BY', {count: requests})}} run(() => linkGames(a.appid, b.appid), 'LINKS.LINKED')}> {t('LINKS.LINK')} run(() => dismissPair(a.appid, b.appid), 'LINKS.DISMISSED')}> {t('LINKS.DISMISS')} ))} )} {status === 'ready' && result.totalPages > 1 && ( setPage(page - 1)}> {t('ADMIN.PREVIOUS')} {t('LINKS.PAGE_OF', {current: page + 1, total: result.totalPages})} = result.totalPages} onClick={() => setPage(page + 1)}> {t('ADMIN.NEXT')} )} ); }
{t('LINKS.NO_ACCESS')}
{t('LINKS.ERROR')}
{t('LINKS.QUEUE_EMPTY')}