Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | 15x 15x 15x 15x 15x 10x 2x 2x 15x 2x 1x 13x 1x 1x | import { useState, useEffect } from 'react' import Box from '@mui/material/Box' import Typography from '@mui/material/Typography' import Pagination from '@mui/material/Pagination' import Seo from '../components/Seo' import { PageWrapper } from '../components/PageWrapper' import { ErrorDisplay } from '../components/ErrorDisplay' import { InvoicesFilters } from '../components/InvoicesFilters' import { InvoiceGrid } from '../components/InvoiceGrid' import { useInvoices } from '../hooks/useInvoices' import { useLanguageStore } from '../stores/useLanguageStore' import type { InvoiceFilters } from '../types/invoices' import OlympicLoader from '../components/OlympicLoader' import { useTranslation } from 'react-i18next' export default function InvoicesPage() { const lang = useLanguageStore(s => s.lang) const { t } = useTranslation('invoices') const [filters, setFilters] = useState<InvoiceFilters>({ status: '', date_from: '', date_to: '', sort_by: 'created_at', sort_order: 'desc', per_page: 15, page: 1, }) const { invoices, total, loading, error, validationErrors } = useInvoices(filters, lang) // reset invalid filters useEffect(() => { if (!validationErrors) return // Pour chaque champ, si validationErrors[field] est truthy, // on ajoute { field: valeurParDéfaut } dans newFilters. const newFilters: Partial<InvoiceFilters> = { ...(validationErrors.status && { status: '' }), ...(validationErrors.date_from && { date_from: '' }), ...(validationErrors.date_to && { date_to: '' }), ...(validationErrors.sort_by && { sort_by: 'created_at' }), ...(validationErrors.sort_order && { sort_order: 'desc' }), ...(validationErrors.per_page && { per_page: 15 }), ...(validationErrors.page && { page: 1 }), } // On merge ensuite setFilters(f => ({ ...f, ...newFilters })) }, [validationErrors]) if (error) { return ( <PageWrapper> <ErrorDisplay title={t('errors.title')} message={t('errors.message')} showRetry retryButtonText={t('invoices.retry')} onRetry={() => setFilters(f => ({ ...f }))} showHome homeButtonText={t('invoices.go_home')} /> </PageWrapper> ) } return ( <> <Seo title={t('seo.title')} description={t('seo.description')} /> <PageWrapper disableCard> <Typography variant="h4" sx={{ px: 2 }}> {t('invoices.title')} </Typography> <Box sx={{ display: 'flex', flexDirection: { xs: 'column', md: 'row' }, gap: 2, p: 2 }}> <InvoicesFilters filters={filters} onChange={upd => setFilters(f => ({ ...f, ...upd }))} /> <Box component="main" flex={1}> {loading ? <Box textAlign="center" py={8}><OlympicLoader/></Box> : <InvoiceGrid invoices={invoices} /> } {!loading && invoices.length > 0 && ( <Box textAlign="center" mt={4}> <Pagination count={Math.ceil(total / filters.per_page) || 1} page={filters.page} onChange={(_, p) => setFilters(f => ({ ...f, page: p }))} /> </Box> )} </Box> </Box> </PageWrapper> </> ) } |