@@ -29,6 +29,12 @@ File này lưu trữ các quyết định thiết kế dài hạn và trạng th
-**Quản lý Saving Goal**: Trang `/saving-goals` và `/saving-goals/:id` dùng trực tiếp progress do Saving Goal API tính (`savedAmount`, `remainingAmount`, `progressPercentage`, `daysRemaining`, `isOverdue`) để giữ một nguồn sự thật chung cho UI, báo cáo và AI. `DELETE /saving-goals/:id` là archive/xóa mềm nhằm bảo toàn lịch sử; mục tiêu archive chỉ đọc đến khi restore. Contribution được quản lý riêng và mọi mutation contribution phải làm mới cả detail, list goal và lịch sử contribution.
-**Báo cáo tài chính**: Trang `/reports` dùng trực tiếp bốn Report API (`overview`, `cash-flow`, `spending-by-category`, `budget-performance`) qua TanStack Query và luôn giữ số liệu tách theo tiền tệ; không cộng gộp các loại tiền tệ khác nhau. Bộ lọc khoảng ngày tùy chọn gửi mốc kết thúc dạng exclusive sang Backend nhưng hiển thị ngày kết thúc dạng inclusive cho người dùng. Biểu đồ dùng SVG/CSS với semantic Clay tokens, không phụ thuộc thư viện chart. Thẻ Tổng quan ngân sách phải thể hiện số liệu tổng hợp (đã chi, còn lại, hạn mức và số lượng theo trạng thái), còn Hiệu suất ngân sách trình bày từng ngân sách; tổng quan chỉ ưu tiên summary `OVERALL` khi `budgetCount > 0`, nếu không fallback sang `CATEGORY`, và hiển thị `—` thay vì `0%` khi không có ngân sách hợp lệ. Khối AI tại báo cáo phải hiển thị trọn bản tóm tắt cùng tối đa ba điểm đáng chú ý, sau đó mới dẫn sang Trợ lý AI để phân tích sâu; khi điều hướng phải giữ `dateFrom`/`dateTo`/`currency` của kỳ báo cáo và nút quay lại phải trở về báo cáo.
-**Thông báo & nhắc nhở**: Trang `/notifications` là trung tâm chung cho inbox thông báo, lịch nhắc và tùy chọn cảnh báo. Unread count dùng query key riêng và được hiển thị bằng badge chuông toàn cục sau đăng nhập; danh sách/count/reminder polling mỗi 30 giây khi đang hoạt động, refetch khi focus và mọi mutation invalidate namespace liên quan. Reminder datetime luôn dùng Vietnamese wall time rồi chuyển sang UTC ISO bằng helper business-time. Với cảnh báo tự động do Backend hiện tạo bằng tiếng Anh, frontend nhận diện đúng mẫu theo `type`, dùng metadata để dựng title/message theo locale; nội dung người dùng nhập, legacy không khớp mẫu và AI tương lai được giữ nguyên. Notification `data` giữ kiểu mở `Record<string, unknown>` để tiếp nhận metadata cảnh báo/AI mà UI không phụ thuộc cấu trúc riêng. Điều hướng từ nút xem chi tiết gắn `fromNotifications` trong route state; các trang đích hệ thống dùng history back khi có marker này và giữ fallback back cố định cho luồng truy cập trực tiếp.
-**Giao dịch tự động định kỳ**: Trang `/recurring-transactions` quản lý lịch thu/chi tự động qua
TanStack Query, gồm tạo/sửa, pause/resume, xóa mềm, preview ngày chạy và lịch sử occurrence.
Trên trang chủ, đây là một mục thuộc **Danh mục Nghiệp vụ**, đặt cạnh giao dịch thường; không xếp trong
**Tính năng Nâng cao** dù được phát triển trong Upgrade 6.
Màn subscription yêu cầu người dùng chọn ví và xác nhận rõ trước khi chuyển một gợi ý thành lịch
tự động; UI luôn nêu rõ tính năng chỉ ghi sổ FinWise, không thực hiện thanh toán ngân hàng.
-**Tiêu đề trang**: Mỗi route cập nhật `document.title` theo mẫu `<Tên trang> | FinWise` thông qua component dùng chung trong router; route chưa nhận diện dùng tiêu đề mô tả sản phẩm mặc định.
-**Khoảng trống và nút điều khiển trên header hệ thống**: Zalo Mini App mặc định hiển thị `zaui-header` ở phía trên. Mọi page/layout phải chừa đủ khoảng cách phía trên (tính cả safe area khi cần) để nội dung và phần tử tương tác không bị header che khuất. Cụm điều khiển riêng của app (hiện gồm theme và ngôn ngữ) phải nằm trong `.finwise-header-controls`, đặt về bên trái vùng native `right-buttons` rộng 96px; đồng thời `zaui-header` phải dành đủ `padding-right` cho cả `right-buttons` và cụm này. Không đặt từng nút bằng offset `right` rời rạc vì có thể làm switch ngôn ngữ bị che khuất.
-**Cấu hình API**: Base URL mặc định là `http://localhost:7777/api/v1` (tương tác trực tiếp với port 7777 của Backend).
...
...
@@ -69,6 +75,9 @@ File này lưu trữ các quyết định thiết kế dài hạn và trạng th
- Module phát hiện chi tiêu bất thường (Upgrade 3) đã có frontend tại `/anomalies`, kết nối Anomaly API (`/anomalies/recent`, `/anomalies/evaluate`) qua TanStack Query; gồm công cụ kiểm tra nhanh giao dịch trước khi chi (Pre-Purchase Evaluator) dựa trên Modified Z-Score & MAD, danh sách lịch sử giao dịch bị gắn cờ với mức độ nghiêm trọng và mã lý do, skeleton loading và bản dịch Việt/Anh.
- Module tự động nhận diện dịch vụ định kỳ (Upgrade 4) đã có frontend tại `/subscriptions`, kết nối Subscription API (`/subscriptions/discover`, `/subscriptions/convert-to-reminder`) qua TanStack Query; gồm danh sách các gói cước định kỳ phát hiện được với chu kỳ và độ chắc chắn, cảnh báo tăng giá cước (price drift), nút 1-click chuyển đổi thành nhắc nhở tự động, skeleton loading và bản dịch Việt/Anh.
- Module truy vấn thông minh DSL (Upgrade 5) đã có frontend tại `/query`, kết nối Query API (`/query/parse`, `/query/execute`) qua TanStack Query; gồm thanh nhập liệu ngôn ngữ tự nhiên, chip câu hỏi gợi ý nhanh, bộ kiểm tra cây cú pháp trừu tượng AST, thẻ tổng hợp số liệu thống kê trực tiếp từ DB, phân tích theo nhóm (GroupBy) và danh sách giao dịch chi tiết, skeleton loading và bản dịch Việt/Anh.
- Module giao dịch tự động định kỳ (Upgrade 6) có frontend tại `/recurring-transactions`, kết nối
CRUD, pause/resume, preview và history API; hỗ trợ tạo lịch trực tiếp hoặc chuyển từ subscription,
có loading/error/empty state và bản dịch Việt/Anh.
"convertSuccess":"Successfully created reminder for {{name}}!",
"fetchError":"Could not discover recurring services. Please try again."
},
"recurringTransactions":{
"pageTitle":"Automated Recurring Transactions",
"bannerTitle":"Put repeat entries on autopilot",
"bannerDesc":"FinWise records income or expenses on your chosen schedule. This changes your in-app wallet balance but never initiates an external bank payment.",
"createButton":"Create recurring transaction",
"createTitle":"New recurring transaction",
"editTitle":"Edit future transactions",
"detailsTitle":"Schedule details",
"previewTitle":"Upcoming dates",
"historyTitle":"Execution history",
"noFutureDates":"No future dates remain in this schedule.",
"noHistory":"No occurrences have been processed yet.",
"detailsError":"Could not load schedule details.",
"fetchError":"Could not load recurring transactions.",
"emptyTitle":"No automated transactions yet",
"emptyDesc":"Create a schedule for rent, salary, subscriptions, or any repeat entry.",
"active":"Active",
"paused":"Paused",
"cycle":"Cycle",
"nextRun":"Next posting",
"pause":"Pause",
"resume":"Resume",
"actionError":"The schedule could not be updated. Check its wallet and category.",
"saveSuccess":"Recurring transaction saved.",
"deleteSuccess":"Recurring transaction removed. Posted transactions were preserved.",
"deleteTitle":"Remove recurring transaction?",
"deleteConfirm":"Future postings will stop. Transactions already posted to the ledger will remain unchanged.",
"attempts":"{{count}} attempt(s)",
"frequency":{
"DAILY":"Daily",
"WEEKLY":"Weekly",
"MONTHLY":"Monthly",
"YEARLY":"Yearly"
},
"policy":{
"SKIP":"Skip missed dates",
"CATCH_UP":"Post missed dates gradually"
},
"status":{
"POSTED":"Posted",
"FAILED":"Failed"
},
"form":{
"type":"Transaction type",
"amount":"Amount",
"wallet":"Wallet",
"category":"Category",
"selectWallet":"Choose a wallet",
"selectCategory":"Choose a category",
"description":"Description",
"location":"Location",
"frequency":"Frequency",
"repeatInterval":"Repeat every",
"anchorDate":"Start date",
"endDate":"End date (optional)",
"missedRunPolicy":"When dates were missed",
"ledgerNotice":"Saving activates automatic ledger posting. Each successful occurrence changes the selected wallet balance atomically."
},
"validation":{
"amount":"Enter a valid amount greater than zero.",
"wallet":"Choose a wallet.",
"category":"Choose a category.",
"anchorDate":"Choose a start date.",
"endDate":"End date cannot be before the start date."
},
"subscription":{
"button":"Automate transaction",
"title":"Automate {{name}}?",
"description":"Choose the wallet that will be adjusted when this recurring expense is recorded.",
"wallet":"Payment wallet",
"confirm":"Activate automation",
"success":"Automatic transaction created for {{name}}.",
"walletRequired":"Choose a wallet before activating."