feat: import transactions from a CSV bank statement

Fourth of the four ideas picked from the Maybe/Sure feature audit, and the
last one queued up. A 3-step wizard (account -> column mapping -> preview)
at /transactions/import, parsed entirely client-side (new lib/csv.ts —
delimiter auto-detect, 5 date formats, thousands/decimal-separator-aware
amounts) and submitted through a new all-or-nothing POST /transactions/bulk
endpoint, mirroring backup restore's validate-everything-before-touching-
anything approach.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
ZProger
2026-08-25 02:44:30 +03:00
parent 94782ee073
commit 19f5cfa429
11 changed files with 706 additions and 8 deletions

View File

@@ -1,6 +1,6 @@
import { useEffect, useState } from "react";
import { useSearchParams } from "react-router-dom";
import { Plus, Search, X } from "lucide-react";
import { Link, useSearchParams } from "react-router-dom";
import { FileUp, Plus, Search, X } from "lucide-react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/Card";
import { Button } from "@/components/ui/Button";
import { Input, Select } from "@/components/ui/Input";
@@ -137,10 +137,18 @@ export function TransactionsPage() {
/>
</div>
</div>
<Button onClick={openCreateModal} className="w-full sm:w-auto">
<Plus size={16} />
{t("transactions.addButton")}
</Button>
<div className="flex gap-2">
<Link to="/transactions/import" className="flex-1 sm:flex-none">
<Button variant="secondary" className="w-full sm:w-auto">
<FileUp size={16} />
{t("transactions.importButton")}
</Button>
</Link>
<Button onClick={openCreateModal} className="flex-1 sm:w-auto">
<Plus size={16} />
{t("transactions.addButton")}
</Button>
</div>
</div>
<div className="relative">