diff --git a/backend/app/core/config.py b/backend/app/core/config.py index 0272bfa..274368b 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -6,7 +6,7 @@ from pydantic_settings import BaseSettings, SettingsConfigDict # Single source of truth for the running app's version — surfaced in the API # title and embedded in exported backups so an old file can be told apart # from a current one. -APP_VERSION = "0.19.2" +APP_VERSION = "0.20.0" class Settings(BaseSettings): diff --git a/e2e/run.sh b/e2e/run.sh index 37d7c0d..00f6696 100755 --- a/e2e/run.sh +++ b/e2e/run.sh @@ -23,9 +23,13 @@ trap cleanup EXIT $COMPOSE up -d --build -echo "waiting for http://localhost:${AURUM_WEB_PORT} ..." +echo "waiting for http://localhost:${AURUM_WEB_PORT}/api/health ..." +# /api/health, not just / — nginx (the `web` container) answers the bare root +# well before the backend has finished running migrations + seeding, and a +# request racing ahead of that gets nginx's own HTML error page back instead +# of JSON, which the first test to touch the API then fails to parse. for _ in $(seq 1 60); do - if curl -sf "http://localhost:${AURUM_WEB_PORT}" >/dev/null; then + if curl -sf "http://localhost:${AURUM_WEB_PORT}/api/health" >/dev/null; then echo "stack is up" break fi diff --git a/frontend/package.json b/frontend/package.json index c880cef..03be0e6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "aurum-frontend", "private": true, - "version": "0.19.2", + "version": "0.20.0", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/components/transactions/TransactionFormModal.tsx b/frontend/src/components/transactions/TransactionFormModal.tsx index 0462c31..04ebade 100644 --- a/frontend/src/components/transactions/TransactionFormModal.tsx +++ b/frontend/src/components/transactions/TransactionFormModal.tsx @@ -233,6 +233,15 @@ export function TransactionFormModal({ open, onClose, transaction }: Transaction /> +
+ + setForm((prev) => ({ ...prev, notes: event.target.value }))} + /> +
+ {error &&

{error}

}
diff --git a/frontend/src/components/transactions/TransactionsTable.tsx b/frontend/src/components/transactions/TransactionsTable.tsx index 8a58c8c..94b27b9 100644 --- a/frontend/src/components/transactions/TransactionsTable.tsx +++ b/frontend/src/components/transactions/TransactionsTable.tsx @@ -1,4 +1,6 @@ -import { ArrowLeftRight, Pencil, Trash2 } from "lucide-react"; +import { useState } from "react"; +import { ArrowLeftRight, Pencil, StickyNote, Trash2 } from "lucide-react"; +import { Dialog } from "@/components/ui/Dialog"; import { getCategoryIcon } from "@/lib/icons"; import { formatCurrency, formatTransactionDate } from "@/lib/format"; import { useTranslation } from "@/lib/i18n"; @@ -13,67 +15,88 @@ interface TransactionsTableProps { export function TransactionsTable({ items, onEdit, onDelete }: TransactionsTableProps) { const { t } = useTranslation(); + const [noteTransaction, setNoteTransaction] = useState(null); if (items.length === 0) { return

{t("transactions.noneFound")}

; } return ( - + + setNoteTransaction(null)} + title={t("transactions.noteDialogTitle")} + > +

{noteTransaction?.notes}

+
+ ); } diff --git a/frontend/src/lib/i18n.ts b/frontend/src/lib/i18n.ts index b3c576d..88ed006 100644 --- a/frontend/src/lib/i18n.ts +++ b/frontend/src/lib/i18n.ts @@ -160,6 +160,9 @@ const ru = { "transactions.form.categoryLabel": "Категория", "transactions.form.noCategory": "Без категории", "transactions.form.merchantLabel": "Продавец (необязательно)", + "transactions.form.notesLabel": "Заметка (необязательно)", + "transactions.viewNote": "Посмотреть заметку", + "transactions.noteDialogTitle": "Заметка", "transactions.form.errorSelectAccount": "Выберите счёт", "transactions.form.errorSelectDestination": "Выберите счёт назначения для перевода", "transactions.form.errorSameAccount": "Счёт назначения должен отличаться от исходного счёта", @@ -486,6 +489,9 @@ const en: Record = { "transactions.form.categoryLabel": "Category", "transactions.form.noCategory": "No category", "transactions.form.merchantLabel": "Merchant (optional)", + "transactions.form.notesLabel": "Note (optional)", + "transactions.viewNote": "View note", + "transactions.noteDialogTitle": "Note", "transactions.form.errorSelectAccount": "Select an account", "transactions.form.errorSelectDestination": "Select a destination account for the transfer", "transactions.form.errorSameAccount": "The destination account must be different from the source account",