import { createFileRoute } from '@tanstack/react-router' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { useState } from 'react' import { PlusIcon, PencilIcon, Trash2Icon } from 'lucide-react' import { toast } from 'sonner' import { snapshotQueryOptions } from '@/queries/snapshot' import { api, ApiError } from '@/lib/api-client' import { PageShell } from '@/components/page-shell' import { PageHeader } from '@/components/page-header' import { Button } from '@cfdm/ui/components/button' import { DataTableCard, type DataTableColumn } from '@/components/data-table-card' import { QueryState } from '@/components/query-state' import { TableSkeleton } from '@/components/skeletons' import { ConfirmDialog } from '@/components/confirm-dialog' import { FormSheet } from '@/components/form-sheet' import { FormField } from '@/components/form-field' import { Input } from '@cfdm/ui/components/input' import { Textarea } from '@cfdm/ui/components/textarea' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@cfdm/ui/components/select' import type { Payment, PaymentType } from '@/types/entities' import { paymentTypeLabel, formatCurrency } from '@/lib/format' import { providerByIdMap, accountSelectLabel } from '@/lib/billmanager' export const Route = createFileRoute('/_auth/payments')({ loader: ({ context: { queryClient } }) => queryClient.ensureQueryData(snapshotQueryOptions()), component: PaymentsPage, }) interface FormState { id?: string type: PaymentType date: string amount: number currency: string providerAccountId: string note: string } const TODAY = new Date().toISOString().slice(0, 10) const EMPTY: FormState = { type: 'provider_balance_topup', date: TODAY, amount: 0, currency: 'RUB', providerAccountId: '', note: '' } function PaymentsPage() { const queryClient = useQueryClient() const { data: snapshot, isLoading, isError, error, refetch } = useQuery(snapshotQueryOptions()) const [open, setOpen] = useState(false) const [form, setForm] = useState(EMPTY) const saveMut = useMutation({ mutationFn: (r: FormState) => r.id ? api.update('payments', r.id, r as unknown as Partial) : api.create('payments', r as unknown as Payment), onSuccess: () => { void queryClient.invalidateQueries({ queryKey: ['snapshot'] }) toast.success('Платёж сохранён') setOpen(false) }, onError: (e: unknown) => toast.error(e instanceof ApiError ? e.message : 'Ошибка'), }) const delMut = useMutation({ mutationFn: (id: string) => api.remove('payments', id), onSuccess: () => { void queryClient.invalidateQueries({ queryKey: ['snapshot'] }) toast.success('Платёж удалён') }, onError: (e: unknown) => toast.error(e instanceof ApiError ? e.message : 'Ошибка'), }) const openCreate = () => { setForm({ ...EMPTY, providerAccountId: snapshot?.providerAccounts[0]?.id ?? '' }); setOpen(true) } const openEdit = (p: Payment) => { setForm({ id: p.id, type: p.type, date: p.date, amount: p.amount, currency: p.currency, providerAccountId: p.providerAccountId, note: p.note ?? '', }) setOpen(true) } const providerById = snapshot ? providerByIdMap(snapshot.providers) : new Map() const columns: DataTableColumn[] = [ { key: 'date', header: 'Дата', cell: (p) => {p.date} }, { key: 'account', header: 'Аккаунт', cell: (p) => { const acc = snapshot?.providerAccounts.find((a) => a.id === p.providerAccountId) return acc ? accountSelectLabel(acc, providerById) : '—' }, }, { key: 'type', header: 'Тип', cell: (p) => {paymentTypeLabel(p.type)}, }, { key: 'amount', header: 'Сумма', cell: (p) => {formatCurrency(p.amount, p.currency)}, }, { key: 'note', header: 'Заметка', cell: (p) => {p.note || '—'} }, { key: 'actions', header: '', className: 'w-24 text-right', cell: (p) => (
} title="Удалить платёж?" confirmLabel="Удалить" destructive onConfirm={() => delMut.mutate(p.id)} />
), }, ] const sorted = [...(snapshot?.payments ?? [])].sort((a, b) => b.date.localeCompare(a.date)) return ( Добавить} /> refetch()} skeleton={} empty={snapshot?.payments.length === 0} emptyTitle="Платежей нет" emptyAction={} > {() => p.id} />} saveMut.mutate(form)} submitting={saveMut.isPending} >
setForm({ ...form, date: e.target.value })} /> setForm({ ...form, amount: Number(e.target.value) })} /> setForm({ ...form, currency: e.target.value })} />