feat(transactions): işlem formu, bölme, liste, filtreler, ayrıntı ve CSV

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-05 03:23:30 +03:00
parent 1208e2d1eb
commit dcfa2f8c8d
8 changed files with 1761 additions and 75 deletions
+1 -1
View File
@@ -56,7 +56,7 @@ export function BottomNav({ canWrite }: { canWrite: boolean }) {
<div className="flex flex-1 items-center justify-center">
<button
type="button"
onClick={openQuickAdd}
onClick={() => openQuickAdd()}
disabled={!canWrite}
title={canWrite ? undefined : tr.households.viewerReadOnly}
aria-label={tr.quickAdd.button}
+1 -1
View File
@@ -27,7 +27,7 @@ export type NavItem = {
/** Şu an tamamlanmış en yüksek faz — sayfalar eklendikçe artar. */
/** Alt fazlar ondalıkla: 6.1 = Faz 6a. */
export const CURRENT_PHASE = 6.1;
export const CURRENT_PHASE = 6.2;
export const isAvailable = (item: NavItem) => item.phase <= CURRENT_PHASE;
+21 -72
View File
@@ -1,16 +1,22 @@
"use client";
import { ArrowDownLeft, ArrowLeftRight, ArrowUpRight, Plus } from "lucide-react";
import { Plus } from "lucide-react";
import { createContext, use, useState } from "react";
import { toast } from "sonner";
import { ResponsiveDialog } from "@/components/responsive-dialog";
import { Button } from "@/components/ui/button";
import {
TransactionFormDialog,
type TransactionFormMode,
} from "@/features/transactions/components/transaction-form";
import { tr } from "@/i18n/tr";
import { cn } from "@/lib/utils";
type QuickAddKind = "expense" | "income" | "transfer";
type QuickAddApi = {
/** Hızlı ekle: yeni işlem formu (varsayılan gider; sekmelerle gelir/transfer). */
openQuickAdd: (type?: "expense" | "income" | "transfer") => void;
/** Düzenle / kopyala (işlem listesi ve ayrıntısı). */
openTransactionForm: (mode: TransactionFormMode) => void;
};
const QuickAddContext = createContext<{ openQuickAdd: () => void } | null>(null);
const QuickAddContext = createContext<QuickAddApi | null>(null);
export function useQuickAdd() {
const ctx = use(QuickAddContext);
@@ -18,74 +24,17 @@ export function useQuickAdd() {
return ctx;
}
const CHOICES: {
kind: QuickAddKind;
label: string;
hint: string;
icon: typeof Plus;
tone: string;
}[] = [
{
kind: "expense",
label: tr.quickAdd.expense,
hint: tr.quickAdd.expenseHint,
icon: ArrowUpRight,
tone: "text-expense bg-expense/10",
},
{
kind: "income",
label: tr.quickAdd.income,
hint: tr.quickAdd.incomeHint,
icon: ArrowDownLeft,
tone: "text-income bg-income/10",
},
{
kind: "transfer",
label: tr.quickAdd.transfer,
hint: tr.quickAdd.transferHint,
icon: ArrowLeftRight,
tone: "text-budget bg-budget/10",
},
];
/** Hızlı ekle (gider / gelir / transfer) — her ekrandan açılabilir. */
/** Uygulama genelinde tek işlem formu; her ekrandan açılabilir (Bölüm 4). */
export function QuickAddProvider({ children }: { children: React.ReactNode }) {
const [open, setOpen] = useState(false);
function choose(kind: QuickAddKind) {
setOpen(false);
// FAZ 6: ilgili işlem formu açılacak.
toast.info(`${CHOICES.find((c) => c.kind === kind)?.label}: ${tr.quickAdd.notYet}`);
}
const [mode, setMode] = useState<TransactionFormMode | null>(null);
const api: QuickAddApi = {
openQuickAdd: (type = "expense") => setMode({ kind: "create", type }),
openTransactionForm: setMode,
};
return (
<QuickAddContext value={{ openQuickAdd: () => setOpen(true) }}>
<QuickAddContext value={api}>
{children}
<ResponsiveDialog
open={open}
onOpenChange={setOpen}
title={tr.quickAdd.title}
description={tr.quickAdd.description}
>
<div className="grid gap-2 pb-2 sm:grid-cols-3">
{CHOICES.map(({ kind, label, hint, icon: Icon, tone }) => (
<button
key={kind}
type="button"
onClick={() => choose(kind)}
className="flex items-center gap-3 rounded-xl border p-4 text-left transition-colors outline-none hover:bg-muted focus-visible:ring-3 focus-visible:ring-ring/50 sm:flex-col sm:text-center"
>
<span className={cn("flex size-10 items-center justify-center rounded-full", tone)}>
<Icon className="size-5" />
</span>
<span className="flex flex-col">
<span className="font-medium">{label}</span>
<span className="text-xs text-muted-foreground">{hint}</span>
</span>
</button>
))}
</div>
</ResponsiveDialog>
<TransactionFormDialog mode={mode} onOpenChange={(open) => !open && setMode(null)} />
</QuickAddContext>
);
}
@@ -94,7 +43,7 @@ export function QuickAddProvider({ children }: { children: React.ReactNode }) {
export function QuickAddButton({ className }: { className?: string }) {
const { openQuickAdd } = useQuickAdd();
return (
<Button onClick={openQuickAdd} className={className}>
<Button onClick={() => openQuickAdd()} className={className}>
<Plus />
{tr.quickAdd.button}
</Button>