From 6972a0309073e907932587065b61d395dcf9c26b Mon Sep 17 00:00:00 2001 From: ribardej Date: Thu, 23 Oct 2025 12:08:28 +0200 Subject: [PATCH 1/3] feat(frontend): Added Mock Bank connection --- 7project/frontend/src/pages/Dashboard.tsx | 71 ++++++++++++++-- 7project/frontend/src/pages/MockBankModal.tsx | 80 +++++++++++++++++++ 7project/frontend/src/ui.css | 29 +++++++ 3 files changed, 174 insertions(+), 6 deletions(-) create mode 100644 7project/frontend/src/pages/MockBankModal.tsx diff --git a/7project/frontend/src/pages/Dashboard.tsx b/7project/frontend/src/pages/Dashboard.tsx index ea8bfa9..e653c4b 100644 --- a/7project/frontend/src/pages/Dashboard.tsx +++ b/7project/frontend/src/pages/Dashboard.tsx @@ -4,6 +4,7 @@ import AccountPage from './AccountPage'; import AppearancePage from './AppearancePage'; import BalanceChart from './BalanceChart'; import CategoryPieChart from './CategoryPieChart'; +import MockBankModal, { type MockGenerationOptions } from './MockBankModal'; import { BACKEND_URL } from '../config'; function formatAmount(n: number) { @@ -16,6 +17,8 @@ export default function Dashboard({ onLogout }: { onLogout: () => void }) { const [categories, setCategories] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + const [isMockModalOpen, setMockModalOpen] = useState(false); + const [isGenerating, setIsGenerating] = useState(false); // Start CSAS (George) OAuth after login async function startOauthCsas() { @@ -92,6 +95,51 @@ export default function Dashboard({ onLogout }: { onLogout: () => void }) { } } + async function handleGenerateMockTransactions(options: MockGenerationOptions) { + setIsGenerating(true); + setMockModalOpen(false); + + const { count, minAmount, maxAmount, startDate, endDate, categoryIds } = options; + const newTransactions: Transaction[] = []; + + const startDateTime = new Date(startDate).getTime(); + const endDateTime = new Date(endDate).getTime(); + + for (let i = 0; i < count; i++) { + // Generate random data based on user input + const amount = parseFloat((Math.random() * (maxAmount - minAmount) + minAmount).toFixed(2)); + + const randomTime = Math.random() * (endDateTime - startDateTime) + startDateTime; + const date = new Date(randomTime); + const dateString = date.toISOString().split('T')[0]; + + const randomCategory = categoryIds.length > 0 + ? [categoryIds[Math.floor(Math.random() * categoryIds.length)]] + : []; + + const payload = { + amount, + date: dateString, + category_ids: randomCategory, + }; + + try { + const created = await createTransaction(payload); + newTransactions.push(created); + } catch (err) { + console.error("Failed to create mock transaction:", err); + alert('An error occurred while generating transactions. Check the console.'); + break; + } + } + + setTransactions(prev => [...newTransactions, ...prev].sort((a, b) => new Date(b.date || 0).getTime() - new Date(a.date || 0).getTime())); + setIsGenerating(false); + alert(`${newTransactions.length} mock transactions were successfully generated!`); + + await loadAll(); + } + useEffect(() => { loadAll(); }, [startDate, endDate]); const filtered = useMemo(() => { @@ -178,10 +226,16 @@ export default function Dashboard({ onLogout }: { onLogout: () => void }) {
{current === 'home' && ( <> -
-

Bank connections

-

Connect your CSAS (George) account.

- +
+

Bank connections

+
+

Connect your CSAS (George) account.

+ +
+
+

Generate data from a mock bank.

+ +
@@ -267,7 +321,6 @@ export default function Dashboard({ onLogout }: { onLogout: () => void }) { - @@ -277,7 +330,6 @@ export default function Dashboard({ onLogout }: { onLogout: () => void }) { {visible.map(t => ( - @@ -322,6 +374,13 @@ export default function Dashboard({ onLogout }: { onLogout: () => void }) { )} + setMockModalOpen(false)} + onGenerate={handleGenerateMockTransactions} + /> ); } diff --git a/7project/frontend/src/pages/MockBankModal.tsx b/7project/frontend/src/pages/MockBankModal.tsx new file mode 100644 index 0000000..5a15569 --- /dev/null +++ b/7project/frontend/src/pages/MockBankModal.tsx @@ -0,0 +1,80 @@ +// src/MockBankModal.tsx +import { useState } from 'react'; +import { type Category } from '../api'; + +// Define the shape of the generation options +export interface MockGenerationOptions { + count: number; + minAmount: number; + maxAmount: number; + startDate: string; + endDate: string; + categoryIds: number[]; +} + +interface MockBankModalProps { + isOpen: boolean; + isGenerating: boolean; + categories: Category[]; // Pass in available categories + onClose: () => void; + onGenerate: (options: MockGenerationOptions) => void; +} + +export default function MockBankModal({ isOpen, isGenerating, categories, onClose, onGenerate }: MockBankModalProps) { + // State for all the new form fields + const [count, setCount] = useState('10'); + const [minAmount, setMinAmount] = useState('-200'); + const [maxAmount, setMaxAmount] = useState('200'); + const [startDate, setStartDate] = useState(() => new Date(Date.now() - 365 * 24 * 60 * 60 * 1000).toISOString().split('T')[0]); // Default to one year ago + const [endDate, setEndDate] = useState(() => new Date().toISOString().split('T')[0]); // Default to today + const [selectedCategoryIds, setSelectedCategoryIds] = useState([]); + + if (!isOpen) return null; + + function handleGenerateClick() { + const options: MockGenerationOptions = { + count: parseInt(count, 10), + minAmount: parseFloat(minAmount), + maxAmount: parseFloat(maxAmount), + startDate, + endDate, + categoryIds: selectedCategoryIds.map(Number), + }; + + // Basic validation + if (!isNaN(options.count) && options.count > 0) { + onGenerate(options); + } + } + + return ( +
+
e.stopPropagation()}> +

Generate Mock Transactions

+

+ Customize the random transactions you'd like to import. +

+
+ setCount(e.target.value)} placeholder="Number of transactions" /> +
+ setMinAmount(e.target.value)} placeholder="Min amount" /> + setMaxAmount(e.target.value)} placeholder="Max amount" /> +
+
+ setStartDate(e.target.value)} placeholder="Earliest date" /> + setEndDate(e.target.value)} placeholder="Latest date" /> +
+ +
+
+ + +
+
+
+ ); +} \ No newline at end of file diff --git a/7project/frontend/src/ui.css b/7project/frontend/src/ui.css index d456317..744f50f 100644 --- a/7project/frontend/src/ui.css +++ b/7project/frontend/src/ui.css @@ -122,3 +122,32 @@ body.auth-page #root { /* Utility */ .muted { color: var(--muted); } .space-y > * + * { margin-top: 12px; } + +/* Modal mock bank */ +.modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +} + +.modal-content { + background: var(--panel); + padding: 24px; + border-radius: var(--radius); + box-shadow: var(--shadow); + width: 100%; + max-width: 400px; +} + +.connection-row { + display: flex; + justify-content: space-between; + align-items: center; +} From e488771cc76c1416c42edbfdb05ccd174706e7e9 Mon Sep 17 00:00:00 2001 From: Dejan Ribarovski Date: Thu, 23 Oct 2025 12:53:04 +0200 Subject: [PATCH 2/3] Update 7project/frontend/src/pages/MockBankModal.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- 7project/frontend/src/pages/MockBankModal.tsx | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/7project/frontend/src/pages/MockBankModal.tsx b/7project/frontend/src/pages/MockBankModal.tsx index 5a15569..63d32e2 100644 --- a/7project/frontend/src/pages/MockBankModal.tsx +++ b/7project/frontend/src/pages/MockBankModal.tsx @@ -32,19 +32,39 @@ export default function MockBankModal({ isOpen, isGenerating, categories, onClos if (!isOpen) return null; function handleGenerateClick() { + const parsedCount = parseInt(count, 10); + const parsedMinAmount = parseFloat(minAmount); + const parsedMaxAmount = parseFloat(maxAmount); + const parsedStartDate = new Date(startDate); + const parsedEndDate = new Date(endDate); + + // Validation + if ( + isNaN(parsedCount) || parsedCount <= 0 || + isNaN(parsedMinAmount) || isNaN(parsedMaxAmount) || + parsedMaxAmount < parsedMinAmount || + isNaN(parsedStartDate.getTime()) || isNaN(parsedEndDate.getTime()) || + parsedEndDate < parsedStartDate + ) { + alert( + "Please ensure:\n" + + "- Count is a positive number\n" + + "- Min and Max Amount are valid numbers, and Max >= Min\n" + + "- Start and End Date are valid, and End Date >= Start Date" + ); + return; + } + const options: MockGenerationOptions = { - count: parseInt(count, 10), - minAmount: parseFloat(minAmount), - maxAmount: parseFloat(maxAmount), + count: parsedCount, + minAmount: parsedMinAmount, + maxAmount: parsedMaxAmount, startDate, endDate, categoryIds: selectedCategoryIds.map(Number), }; - // Basic validation - if (!isNaN(options.count) && options.count > 0) { - onGenerate(options); - } + onGenerate(options); } return ( From 9fc8601e4da854c047a672397418e444e0950877 Mon Sep 17 00:00:00 2001 From: Dejan Ribarovski Date: Thu, 23 Oct 2025 12:53:39 +0200 Subject: [PATCH 3/3] Update 7project/frontend/src/pages/Dashboard.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- 7project/frontend/src/pages/Dashboard.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/7project/frontend/src/pages/Dashboard.tsx b/7project/frontend/src/pages/Dashboard.tsx index e653c4b..c4cf021 100644 --- a/7project/frontend/src/pages/Dashboard.tsx +++ b/7project/frontend/src/pages/Dashboard.tsx @@ -133,7 +133,6 @@ export default function Dashboard({ onLogout }: { onLogout: () => void }) { } } - setTransactions(prev => [...newTransactions, ...prev].sort((a, b) => new Date(b.date || 0).getTime() - new Date(a.date || 0).getTime())); setIsGenerating(false); alert(`${newTransactions.length} mock transactions were successfully generated!`);
ID Date Amount Description
{t.id} {t.date || ''} {formatAmount(t.amount)} {t.description || ''}