"use client"; import { ArrowLeft, CheckCheck, ChevronLeft, ChevronRight, List, Loader2, Scale, } from "lucide-react"; import Link from "next/link"; import { useState } from "react"; import { toast } from "sonner"; import { MoneyInput } from "@/components/money-input"; import { PageHeader } from "@/components/page-header"; import { ResponsiveDialog } from "@/components/responsive-dialog"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Checkbox } from "@/components/ui/checkbox"; import { Field, FieldDescription, FieldError, FieldGroup, FieldLabel } from "@/components/ui/field"; import { Input } from "@/components/ui/input"; import { tr } from "@/i18n/tr"; import { cn } from "@/lib/utils"; import { todayIstanbul } from "@/shared/dates"; import { formatDate, formatMoney } from "@/shared/format"; import { previewReconciliationAction, reconcileAccountAction } from "../actions"; import type { AccountStatement, ReconcilePreview, StatementRow } from "../statement"; import { RowIcon, rowSubtitle, rowTitle, useTransactionDetail } from "./transaction-parts"; const t = tr.statement; const r = t.reconcile; const monthFmt = new Intl.DateTimeFormat("tr-TR", { month: "long", year: "numeric", timeZone: "Europe/Istanbul", }); const monthLabel = (ym: string) => monthFmt.format(new Date(`${ym}-15T12:00:00+03:00`)); const dateLabel = (ymd: string) => formatDate(new Date(`${ymd}T12:00:00+03:00`)); function SignedMoney({ minor, className }: { minor: number; className?: string }) { return ( 0 && "text-income", minor < 0 && "text-expense", className, )} > {minor > 0 ? "+" : minor < 0 ? "−" : ""} {formatMoney(Math.abs(minor))} ); } export function StatementView({ statement: s, canWrite, }: { statement: AccountStatement; canWrite: boolean; }) { const { openDetail, element: detailElement } = useTransactionDetail(canWrite); const [reconcileOpen, setReconcileOpen] = useState(false); const base = `/hesaplar/${s.account.id}`; return (
{canWrite && s.canReconcile ? ( ) : null} } />

{monthLabel(s.month)}

{s.nextMonth ? ( ) : ( )}
{( [ [t.opening, s.openingMinor, false], [t.inflow, s.inflowMinor, true], [t.outflow, -s.outflowMinor, true], [t.closing, s.closingMinor, false], ] as const ).map(([label, value, colored]) => ( {label} {colored ? ( ) : ( {formatMoney(value)} )} ))}

{s.lastReconciliation ? t.lastReconciliation( dateLabel(s.lastReconciliation.date), formatMoney(s.lastReconciliation.statementBalanceMinor), ) : t.noReconciliation}

{s.rows.length === 0 ? ( {t.empty} ) : ( )} {s.truncated ?

{t.truncated}

: null} {detailElement}
); } function StatementRowView({ row, onOpen }: { row: StatementRow; onOpen: () => void }) { const content = ( <> {rowTitle(row.item)} {row.reconciled ? ( ) : null} {dateLabel(row.item.date)} · {rowSubtitle(row.item)} {formatMoney(row.balanceMinor)} ); const cls = "flex w-full items-center gap-3 p-3 text-left"; // Gizli karşı hesaba dokunan işlemin ayrıntısı açılmaz. return row.restricted ? (
{content}
) : ( ); } function ReconcileDialog({ open, onOpenChange, accountId, isLiability, }: { open: boolean; onOpenChange: (open: boolean) => void; accountId: string; isLiability: boolean; }) { const [date, setDate] = useState(todayIstanbul); const [balance, setBalance] = useState(""); const [preview, setPreview] = useState(null); const [accept, setAccept] = useState(false); const [errors, setErrors] = useState>({}); const [pending, setPending] = useState(false); function reset(next: boolean) { if (!next) { setPreview(null); setAccept(false); setErrors({}); setBalance(""); setDate(todayIstanbul()); } onOpenChange(next); } const input = () => ({ statementDate: date, statementBalance: balance, acceptAdjustment: accept, }); async function check() { setPending(true); const res = await previewReconciliationAction(accountId, input()); setPending(false); if (!res.ok) { setErrors({ ...res.fieldErrors, root: res.fieldErrors ? undefined : res.error }); return; } setErrors({}); setAccept(false); setPreview(res.data); } async function confirm() { setPending(true); const res = await reconcileAccountAction(accountId, input()); setPending(false); if (!res.ok) { setErrors({ root: res.error }); return; } toast.success( res.data.adjustmentId ? r.doneWithAdjustment(res.data.markedCount) : r.done(res.data.markedCount), ); reset(false); } const diff = preview?.differenceMinor ?? 0; const display = (minor: number) => formatMoney(isLiability ? -minor : minor); return ( {preview ? ( ) : ( )} } > {r.date} { setDate(e.target.value); setPreview(null); }} /> {errors.statementDate ? {errors.statementDate} : null} {isLiability ? r.debt : r.balance} { setBalance(v); setPreview(null); }} /> {isLiability ? r.debtHint : r.balanceHint} {errors.statementBalance ? {errors.statementBalance} : null} {preview ? (
{r.statementBalance}
{display(preview.statementBalanceMinor)}
{r.appBalance}
{display(preview.appBalanceMinor)}
{r.difference}
{diff === 0 ? (

{r.matches}

) : ( <>

{r.mismatch}

)} {r.unreconciled(preview.unreconciledCount)}
) : null} {errors.root ? {errors.root} : null}
); }