/** * INBOX-1: Anfragen-Posteingang — Liste mit Status-Filter (Gridify) und * manuellem Gmail-Abruf („Anfragen abrufen“, POST /api/requests/sync). * Solange Gmail unkonfiguriert ist, antwortet der Sync mit MailNotConfigured * → freundlicher deutscher Hinweis statt Fehler. */ import { useState } from 'react' import { Link } from 'react-router-dom' import { de } from '../strings/de' import { REQUEST_STATUSES, listRequests, syncRequests, type RequestStatus, } from '../api/requests' import { useApi, useMutation } from '../hooks/useApi' import { formatDateTime } from '../format/labels' import { FilterPanel } from '../components/FilterPanel' import './anfragen.css' const PAGE_SIZE = 20 export function StatusBadge({ status }: { status: RequestStatus }) { return ( {de.pages.anfragen.statusLabels[status]} ) } export default function AnfragenPage() { const t = de.pages.anfragen const [status, setStatus] = useState('') const [page, setPage] = useState(1) const [notice, setNotice] = useState(null) const requests = useApi( () => listRequests({ page, pageSize: PAGE_SIZE, orderBy: 'receivedAt desc', filter: status === '' ? undefined : `status==${status}`, }), [page, status], ) const sync = useMutation(() => syncRequests()) async function onSync() { setNotice(null) const result = await sync.run() if (!result.ok) return // sync.error treibt den Alert if (result.value.error === 'MailNotConfigured') setNotice(t.mailNotConfigured) else if (result.value.error === 'MailAuthFailed') setNotice(t.mailAuthFailed) else { setNotice(t.syncImported(result.value.imported)) if (result.value.imported > 0) requests.reload() } } return ( {t.title} {requests.data && {t.countText(requests.data.totalCount)}} {sync.pending ? t.syncing : t.sync} {notice && {notice}} {sync.error && {sync.error}} {/* Status-Filter */} { setStatus(''); setPage(1) }} > {t.detail.statusLabel} { setStatus(e.target.value as RequestStatus | '') setPage(1) }} > {t.filterAll} {REQUEST_STATUSES.map((s) => ( {t.statusLabels[s]} ))} {requests.loading && {de.common.loading}} {requests.error && ( {requests.error} {de.common.retry} )} {requests.data && requests.data.items.length === 0 && ( {status === '' ? t.empty : t.emptyFiltered} )} {requests.data && requests.data.items.length > 0 && ( {requests.data.items.map((r) => ( {r.fromName ?? r.fromAddress} {r.subject ?? de.pages.anfragen.detail.noSubject} {formatDateTime(r.receivedAt)} ))} )} {requests.data && requests.data.totalCount > PAGE_SIZE && ( setPage(page - 1)}> {de.common.previous} {de.common.page} {page} {de.common.of}{' '} {Math.max(1, Math.ceil(requests.data.totalCount / PAGE_SIZE))} = Math.ceil(requests.data.totalCount / PAGE_SIZE)} onClick={() => setPage(page + 1)} > {de.common.next} )} ) }
{t.countText(requests.data.totalCount)}
{de.common.loading}
{status === '' ? t.empty : t.emptyFiltered}