diff --git a/app/(dashboard)/invoices/page.tsx b/app/(dashboard)/invoices/page.tsx index 0de10d9a..7012ca49 100644 --- a/app/(dashboard)/invoices/page.tsx +++ b/app/(dashboard)/invoices/page.tsx @@ -1,13 +1,15 @@ 'use client' -import { useState, useEffect } from 'react' +import { useState, useEffect, useMemo } from 'react' import dynamic from 'next/dynamic' import Link from 'next/link' import { useRouter, useSearchParams } from 'next/navigation' import { useTranslations } from 'next-intl' import { createClient } from '@/lib/supabase/client' +import { fetchAllRows } from '@/lib/supabase/fetch-all' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' +import { RowStatus, type RowStatusDescriptor } from '@/components/ui/row-status' import { Input } from '@/components/ui/input' import { Skeleton } from '@/components/ui/skeleton' import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog' @@ -23,11 +25,26 @@ import { formatCurrency, formatDate } from '@/lib/utils' import { cn } from '@/lib/utils' import { invoiceDisplayNumber } from '@/lib/invoices/display' import { getDisplayTotal } from '@/lib/invoices/rounding' -import { Plus, Search, ReceiptText, Repeat, FileInput, FileDown } from 'lucide-react' +import { + sortInvoiceList, + type InvoiceListSort, + type InvoiceListSortColumn, +} from '@/lib/invoices/invoice-list-sort' +import { + ArrowDown, + ArrowUp, + ArrowUpDown, + Plus, + Search, + ReceiptText, + Repeat, + FileInput, + FileDown, +} from 'lucide-react' import { EmptyInvoices } from '@/components/ui/empty-state' import { useCompany } from '@/contexts/CompanyContext' import { useCanWrite } from '@/lib/hooks/use-can-write' -import type { FiscalPeriod, Invoice, InvoiceStatus } from '@/types' +import type { FiscalPeriod, Invoice } from '@/types' function NewInvoiceDialogLoading() { const t = useTranslations('invoices') @@ -83,6 +100,53 @@ function daysOverdue(dueDateStr: string): number { return Math.round((today.getTime() - dueDate.getTime()) / (1000 * 60 * 60 * 24)) } +interface SortableHeaderProps { + label: string + sortLabel: string + column: InvoiceListSortColumn + sort: InvoiceListSort | null + onSort: (column: InvoiceListSortColumn) => void + className?: string + align?: 'left' | 'right' +} + +function SortableHeader({ + label, + sortLabel, + column, + sort, + onSort, + className, + align = 'left', +}: SortableHeaderProps) { + const active = sort?.column === column + const direction = active ? sort.direction : null + const SortIcon = direction === 'asc' ? ArrowUp : direction === 'desc' ? ArrowDown : ArrowUpDown + + return ( +
| {t('th_nr')} | -{t('th_customer')} | -{t('th_due')} | -{t('th_amount')} | -{t('th_status')} | +
|---|