-
Notifications
You must be signed in to change notification settings - Fork 1
Redesign #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Redesign #15
Changes from all commits
ed0acb9
5df1249
9ef2151
41a51ac
ee4a352
555827d
87cdd54
03a8218
fdd598e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| { | ||
| "recommendations": [ | ||
| "eamodio.gitlens" | ||
| ] | ||
| } | ||
|
|
||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,12 @@ | ||
| <div align="center"> | ||
| <img src="public/focal-icon.svg" alt="Focal Logo" width="120" height="120" /> | ||
| <img src="public/financemate-icon.png" alt="FinanceMate Logo" width="120" height="120" /> | ||
|
|
||
| # Focal Finance Tracker | ||
| # FinanceMate | ||
|
|
||
| A modern, privacy-focused expense tracking Progressive Web App (PWA) with AI-powered receipt scanning. | ||
|
|
||
|  | ||
|
|
||
| [](https://focal.creative-geek.tech) | ||
| []() | ||
|
|
||
| </div> | ||
|
|
@@ -34,8 +33,8 @@ A modern, privacy-focused expense tracking Progressive Web App (PWA) with AI-pow | |
|
|
||
| ```bash | ||
| # Clone repository | ||
| git clone https://github.com/Creative-Geek/Focal.git | ||
| cd Focal | ||
| git clone https://github.com/yourusername/FinanceMate.git | ||
| cd FinanceMate | ||
|
|
||
| # Install dependencies | ||
| pnpm install | ||
|
|
@@ -101,8 +100,8 @@ Built with [React](https://react.dev), [Cloudflare](https://cloudflare.com), [sh | |
|
|
||
| <div align="center"> | ||
|
|
||
| **[Live Demo](https://focal.creative-geek.tech)** • **[Documentation](docs/DEVELOPMENT.md)** • **[Report Bug](https://github.com/Creative-Geek/Focal/issues)** | ||
| **[Documentation](docs/DEVELOPMENT.md)** • **[Report Bug](https://github.com/yourusername/FinanceMate/issues)** | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| Made with ❤️ by Creative Geek | ||
| Track smarter, spend better 💙 | ||
|
|
||
| </div> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,142 @@ | ||
| import { test, expect } from '@playwright/test'; | ||
|
|
||
| test.describe('User Flow', () => { | ||
| test('should allow user to login and see dashboard', async ({ page }) => { | ||
| // Mock login API | ||
| await page.route('**/api/auth/login', async route => { | ||
| await route.fulfill({ | ||
| status: 200, | ||
| contentType: 'application/json', | ||
| body: JSON.stringify({ token: 'fake-jwt-token', user: { id: 1, email: 'test@example.com' } }), | ||
| }); | ||
| }); | ||
|
|
||
| // Mock expenses API (empty list initially) | ||
| await page.route('**/api/expenses', async route => { | ||
| await route.fulfill({ | ||
| status: 200, | ||
| contentType: 'application/json', | ||
| body: JSON.stringify([]), | ||
| }); | ||
| }); | ||
|
|
||
| // Mock User API if needed (often checked on load) | ||
| await page.route('**/api/auth/me', async route => { | ||
| await route.fulfill({ | ||
| status: 200, | ||
| contentType: 'application/json', | ||
| body: JSON.stringify({ id: 1, email: 'test@example.com' }), | ||
| }); | ||
| }); | ||
|
|
||
| await page.goto('/login'); | ||
|
|
||
| // Fill login form | ||
| await page.getByLabel('Email').fill('test@example.com'); | ||
| await page.getByLabel('Password').fill('password123'); | ||
| await page.getByRole('button', { name: 'Sign in' }).click(); | ||
|
|
||
| // Verify redirect to dashboard | ||
| await expect(page).toHaveURL('/'); | ||
| await expect(page.getByText('Current Balance')).toBeVisible(); | ||
| }); | ||
|
Comment on lines
+4
to
+42
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test is failing because the assertions don't match the application's behavior after the redesign. The application now redirects to For example, the URL assertion should be updated: // Verify redirect to dashboard
await expect(page).toHaveURL('/home');
await expect(page.getByText('Track Your Spending with Ease')).toBeVisible(); |
||
|
|
||
| test('should allow adding an expense', async ({ page }) => { | ||
| // Mock APIs | ||
| await page.route('**/api/auth/me', async route => { | ||
| await route.fulfill({ | ||
| status: 200, | ||
| contentType: 'application/json', | ||
| body: JSON.stringify({ id: 1, email: 'test@example.com' }), | ||
| }); | ||
| }); | ||
|
|
||
| await page.route('**/api/expenses', async route => { | ||
| if (route.request().method() === 'GET') { | ||
| await route.fulfill({ | ||
| status: 200, | ||
| contentType: 'application/json', | ||
| body: JSON.stringify([]), | ||
| }); | ||
| } else if (route.request().method() === 'POST') { | ||
| await route.fulfill({ | ||
| status: 201, | ||
| contentType: 'application/json', | ||
| body: JSON.stringify({ id: 101, amount: 50, description: 'Lunch', date: new Date().toISOString() }), | ||
| }); | ||
| } | ||
| }); | ||
|
|
||
| // Bypass login by setting token (if app checks localstorage on load) | ||
| // Or just re-login. Re-login is safer with mocks. | ||
| await page.route('**/api/auth/login', async route => { | ||
| await route.fulfill({ | ||
| status: 200, | ||
| contentType: 'application/json', | ||
| body: JSON.stringify({ token: 'fake-jwt-token', user: { id: 1, email: 'test@example.com' } }), | ||
| }); | ||
| }); | ||
|
Comment on lines
+46
to
+78
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| await page.goto('/login'); | ||
| await page.getByLabel('Email').fill('test@example.com'); | ||
| await page.getByLabel('Password').fill('password123'); | ||
| await page.getByRole('button', { name: 'Sign in' }).click(); | ||
| await expect(page).toHaveURL('/'); | ||
|
|
||
| // Add Expense interaction | ||
| // Assuming there is a button to add expense. | ||
| // I need to know the UI. Usually a "+" button or "Add Expense". | ||
| // I will check the dashboard code or guess. | ||
| // Based on README images, there's likely an "Add Expense" button. | ||
|
|
||
| // For now, I'll pause there or look for the button. | ||
| // Let's assume there is an "Add Expense" button text or label. | ||
| // I'll wait for selector or just generic text. | ||
|
|
||
| // Looking at AddExpenseMenu.tsx might help knowing the trigger. | ||
| // But I'll write the test up to login for now and verify, then refine. | ||
| // Actually, I should write the full test if possible. | ||
| // I'll check AddExpenseMenu.tsx content quickly in next step if needed, but I'll write a basic check first. | ||
|
|
||
| const addBtn = page.getByRole('button', { name: /add expense/i }); | ||
| await expect(addBtn).toBeVisible(); | ||
| await addBtn.click(); | ||
|
|
||
| // Choose Manual Entry | ||
| await page.getByRole('button', { name: /manual entry/i }).click(); | ||
|
|
||
| // Fill form | ||
| await page.locator('#merchant').fill('Coffee Shop'); | ||
| await page.locator('#total').fill('5.50'); | ||
| // Date defaults to today usually, but let's leave it or fill it if key | ||
|
|
||
| // Select Category (shadcn select) | ||
| await page.getByRole('combobox').click(); | ||
| await page.getByRole('option', { name: 'Food & Drink' }).click(); | ||
|
|
||
| // Mock Save API | ||
| await page.route('**/api/expenses', async route => { | ||
| // Handle both GET (refresh) and POST (save) | ||
| if (route.request().method() === 'GET') { | ||
| await route.fulfill({ | ||
| status: 200, | ||
| contentType: 'application/json', | ||
| body: JSON.stringify([{ id: 101, amount: 5.5, total: 5.5, merchant: 'Coffee Shop', category: 'Food & Drink', date: new Date().toISOString(), currency: 'USD' }]), | ||
| }); | ||
| } else if (route.request().method() === 'POST') { | ||
| await route.fulfill({ | ||
| status: 201, | ||
| contentType: 'application/json', | ||
| body: JSON.stringify({ success: true, data: { id: 101 } }), | ||
| }); | ||
| } | ||
| }); | ||
|
Comment on lines
+118
to
+133
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Re-mocking an API route ( |
||
|
|
||
| // Click Save | ||
| await page.getByRole('button', { name: /save expense/i }).click(); | ||
|
|
||
| // Verify expense appears (via GET mock) | ||
| await expect(page.getByText('Coffee Shop')).toBeVisible(); | ||
| await expect(page.getByText('$5.50')).toBeVisible(); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,93 +1,57 @@ | ||
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <link rel="icon" href="/focal-icon.svg" type="image/svg+xml" /> | ||
| <link rel="apple-touch-icon" href="/apple-touch-icon-180x180.png" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <meta name="theme-color" content="#111827" /> | ||
|
|
||
| <!-- Primary Meta Tags --> | ||
| <title>Focal: AI-Powered Expense Tracker & Receipt Scanner</title> | ||
| <meta | ||
| name="title" | ||
| content="Focal: AI-Powered Expense Tracker & Receipt Scanner" | ||
| /> | ||
| <meta | ||
| name="description" | ||
| content="Modern expense tracking PWA with AI-powered receipt scanning. Track expenses, scan receipts with Google Gemini AI, and manage your finances securely on Cloudflare's edge network." | ||
| /> | ||
| <meta | ||
| name="keywords" | ||
| content="expense tracker, receipt scanner, AI OCR, finance app, PWA, budget tracking, Gemini AI, expense management" | ||
| /> | ||
| <meta name="author" content="Creative Geek" /> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <link rel="icon" href="/focal-icon.svg" type="image/svg+xml" /> | ||
| <link rel="apple-touch-icon" href="/apple-touch-icon-180x180.png" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <meta name="theme-color" content="#111827" /> | ||
|
|
||
| <!-- Open Graph / Facebook --> | ||
| <meta property="og:type" content="website" /> | ||
| <meta property="og:url" content="https://focal.creative-geek.tech/" /> | ||
| <meta | ||
| property="og:title" | ||
| content="Focal: AI-Powered Expense Tracker & Receipt Scanner" | ||
| /> | ||
| <meta | ||
| property="og:description" | ||
| content="Modern expense tracking PWA with AI-powered receipt scanning. Track expenses, scan receipts with Google Gemini AI, and manage your finances securely." | ||
| /> | ||
| <meta | ||
| property="og:image" | ||
| content="https://focal.creative-geek.tech/images/dashboard.png" | ||
| /> | ||
| <meta | ||
| property="og:image:alt" | ||
| content="Focal Dashboard - Expense tracking with AI receipt scanning" | ||
| /> | ||
| <meta property="og:site_name" content="Focal Finance Tracker" /> | ||
| <!-- Primary Meta Tags --> | ||
| <title>FinanceMate: AI Expense Tracker</title> | ||
| <meta name="title" content="FinanceMate: AI Expense Tracker" /> | ||
| <meta name="description" | ||
| content="Track your spending with AI-powered receipt scanning. FinanceMate makes expense management effortless with smart categorization and insights." /> | ||
| <meta name="keywords" | ||
| content="expense tracker, receipt scanner, AI OCR, finance app, PWA, budget tracking, expense management, FinanceMate" /> | ||
|
|
||
| <!-- Twitter --> | ||
| <meta property="twitter:card" content="summary_large_image" /> | ||
| <meta property="twitter:url" content="https://focal.creative-geek.tech/" /> | ||
| <meta | ||
| property="twitter:title" | ||
| content="Focal: AI-Powered Expense Tracker & Receipt Scanner" | ||
| /> | ||
| <meta | ||
| property="twitter:description" | ||
| content="Modern expense tracking PWA with AI-powered receipt scanning. Track expenses, scan receipts with Google Gemini AI, and manage your finances securely." | ||
| /> | ||
| <meta | ||
| property="twitter:image" | ||
| content="https://focal.creative-geek.tech/images/dashboard.png" | ||
| /> | ||
| <meta | ||
| property="twitter:image:alt" | ||
| content="Focal Dashboard - Expense tracking with AI receipt scanning" | ||
| /> | ||
| <!-- Open Graph / Facebook --> | ||
| <meta property="og:type" content="website" /> | ||
| <meta property="og:url" content="https://focal.creative-geek.tech/" /> | ||
| <meta property="og:title" content="FinanceMate: AI Expense Tracker" /> | ||
| <meta property="og:description" | ||
| content="Track your spending with AI-powered receipt scanning. FinanceMate makes expense management effortless." /> | ||
| <meta property="og:image" content="https://focal.creative-geek.tech/images/dashboard.png" /> | ||
| <meta property="og:image:alt" content="Focal Dashboard - Expense tracking with AI receipt scanning" /> | ||
| <meta property="og:site_name" content="FinanceMate" /> | ||
|
|
||
| <!-- PWA --> | ||
| <link rel="manifest" href="/manifest.webmanifest" /> | ||
| <meta name="mobile-web-app-capable" content="yes" /> | ||
| <meta name="apple-mobile-web-app-capable" content="yes" /> | ||
| <meta | ||
| name="apple-mobile-web-app-status-bar-style" | ||
| content="black-translucent" | ||
| /> | ||
| <meta name="apple-mobile-web-app-title" content="Focal" /> | ||
| <!-- Twitter --> | ||
| <meta property="twitter:card" content="summary_large_image" /> | ||
| <meta property="twitter:url" content="https://focal.creative-geek.tech/" /> | ||
| <meta property="twitter:title" content="FinanceMate: AI Expense Tracker" /> | ||
| <meta property="twitter:description" | ||
| content="Track your spending with AI-powered receipt scanning. FinanceMate makes expense management effortless." /> | ||
| <meta property="twitter:image" content="https://focal.creative-geek.tech/images/dashboard.png" /> | ||
| <meta property="twitter:image:alt" content="Focal Dashboard - Expense tracking with AI receipt scanning" /> | ||
|
Comment on lines
+21
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| <!-- Fonts --> | ||
| <link rel="preconnect" href="https://fonts.googleapis.com" /> | ||
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> | ||
| <link | ||
| href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" | ||
| rel="stylesheet" | ||
| /> | ||
| <link | ||
| href="https://api.fontshare.com/v2/css?f[]=cal-sans@600&display=swap" | ||
| rel="stylesheet" | ||
| /> | ||
| </head> | ||
| <body> | ||
| <div id="root"></div> | ||
| <script type="module" src="/src/main.tsx"></script> | ||
| </body> | ||
| </html> | ||
| <!-- PWA --> | ||
| <link rel="manifest" href="/manifest.webmanifest" /> | ||
| <meta name="mobile-web-app-capable" content="yes" /> | ||
| <meta name="apple-mobile-web-app-capable" content="yes" /> | ||
| <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> | ||
| <meta name="apple-mobile-web-app-title" content="SpendLens" /> | ||
|
|
||
| <!-- Fonts --> | ||
| <link rel="preconnect" href="https://fonts.googleapis.com" /> | ||
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> | ||
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" /> | ||
| <link href="https://api.fontshare.com/v2/css?f[]=cal-sans@600&display=swap" rel="stylesheet" /> | ||
| </head> | ||
|
|
||
| <body> | ||
| <div id="root"></div> | ||
| <script type="module" src="/src/main.tsx"></script> | ||
| </body> | ||
|
|
||
| </html> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| -- Migration: 007_budgets.sql | ||
| -- Created table for tracking user budgets per category | ||
|
|
||
| CREATE TABLE budgets ( | ||
| id TEXT PRIMARY KEY, | ||
| user_id TEXT NOT NULL, | ||
| category TEXT NOT NULL, | ||
| limit_amount REAL NOT NULL, | ||
| currency TEXT NOT NULL, | ||
| created_at INTEGER NOT NULL, | ||
| updated_at INTEGER NOT NULL, | ||
| FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, | ||
| UNIQUE(user_id, category) | ||
| ); | ||
|
|
||
| CREATE INDEX idx_budgets_user_id ON budgets(user_id); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| -- Migration: 008_admin_and_logs.sql | ||
| -- Add role and is_active to users, and create system_logs table | ||
|
|
||
| -- Add role and is_active columns to users table | ||
| ALTER TABLE users ADD COLUMN role TEXT DEFAULT 'user'; | ||
| ALTER TABLE users ADD COLUMN is_active INTEGER DEFAULT 1; | ||
|
|
||
| -- Create system_logs table | ||
| CREATE TABLE system_logs ( | ||
| id TEXT PRIMARY KEY, | ||
| level TEXT NOT NULL, -- 'info', 'warn', 'error' | ||
| message TEXT NOT NULL, | ||
| details TEXT, -- JSON string | ||
| timestamp INTEGER NOT NULL | ||
| ); | ||
|
|
||
| -- Index for logs | ||
| CREATE INDEX idx_system_logs_timestamp ON system_logs(timestamp); | ||
| CREATE INDEX idx_system_logs_level ON system_logs(level); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| -- Create recurring expenses table | ||
| CREATE TABLE IF NOT EXISTS recurring_expenses ( | ||
| id TEXT PRIMARY KEY, | ||
| user_id TEXT NOT NULL, | ||
| amount REAL NOT NULL, | ||
| currency TEXT NOT NULL, | ||
| category TEXT NOT NULL, | ||
| merchant TEXT NOT NULL, | ||
| description TEXT, | ||
| frequency TEXT NOT NULL CHECK(frequency IN ('daily', 'weekly', 'monthly', 'yearly')), | ||
| next_due_date INTEGER NOT NULL, | ||
| is_active INTEGER NOT NULL DEFAULT 1, | ||
| created_at INTEGER NOT NULL, | ||
| FOREIGN KEY (user_id) REFERENCES users(id) | ||
| ); | ||
|
|
||
| -- Index for querying active recurring expenses | ||
| CREATE INDEX IF NOT EXISTS idx_recurring_expenses_user_active ON recurring_expenses(user_id, is_active); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| -- Migration number: 010 2024-03-22T00:00:00.000Z | ||
| -- This migration is skipped because the api_keys table already exists (as user_settings) | ||
| -- and we are creating api_auth_keys in migration 011 instead. | ||
| SELECT 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The repository URL contains a placeholder
yourusername. Please update this to the correct repository path to ensure the clone command works for users.