Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ See `backend/app/db/schema.sql`. Key tables:
- `user:{id}:categories` — 24h TTL
- `user:{id}:upcoming_bills` — 15 min TTL
- `insights:{id}` — 24h TTL (invalidate on new expense/bill)
- `user:{id}:weekly_digest:{yyyy-mm-dd}` — 10 min TTL
- Invalidation
- On expense/bill create/update/delete -> delete affected monthly_summary, upcoming_bills, insights
- Rate limiting (optional): `rl:{userId}:{endpoint}:{minute}` with short TTL
Expand All @@ -66,13 +67,20 @@ OpenAPI: `backend/app/openapi.yaml`
- Bills: CRUD `/bills`, pay/mark `/bills/{id}/pay`
- Reminders: CRUD `/reminders`, trigger `/reminders/run`
- Insights: `/insights/monthly`, `/insights/budget-suggestion`
- Digest: `/digest/weekly`, `/digest/weekly/<date>`, `/digest/history`, `POST /digest/generate`

## MVP UI/UX Plan
- Auth screens: register/login.
- Dashboard:
- Monthly spend chart, category breakdown donut.
- Upcoming bills list with due dates and pay status.
- AI budget suggestion card.
- Weekly Digest:
- Smart weekly financial summary with income, expenses, and net flow.
- Top spending categories breakdown with percentage bars.
- Week-over-week trend comparison (income/expense change %).
- AI-powered insights generated via Gemini (heuristic fallback).
- Historical digest browser for past weeks.
- Expenses page: add expense (amount, category, notes, date), list & filter.
- Bills page: create bill (name, amount, cadence, due date, channel), toggle WhatsApp/email.
- Settings: profile, categories, reminders default channel, export (premium).
Expand Down Expand Up @@ -108,6 +116,7 @@ finmind/
__init__.py
ai.py
cache.py
digest.py
reminders.py
db/
schema.sql
Expand All @@ -125,6 +134,7 @@ finmind/
Charts.tsx
pages/
Dashboard.tsx
Digest.tsx
Expenses.tsx
Bills.tsx
Settings.tsx
Expand Down
9 changes: 9 additions & 0 deletions app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import NotFound from "./pages/NotFound";
import { Landing } from "./pages/Landing";
import ProtectedRoute from "./components/auth/ProtectedRoute";
import Account from "./pages/Account";
import { Digest } from "./pages/Digest";

const queryClient = new QueryClient({
defaultOptions: {
Expand Down Expand Up @@ -83,6 +84,14 @@ const App = () => (
</ProtectedRoute>
}
/>
<Route
path="digest"
element={
<ProtectedRoute>
<Digest />
</ProtectedRoute>
}
/>
<Route
path="account"
element={
Expand Down
48 changes: 48 additions & 0 deletions app/src/api/digest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { api } from './client';

export type CategoryBreakdown = {
category_id: number | null;
category_name: string;
amount: number;
share_pct: number;
};

export type DigestTrends = {
expense_change_pct: number;
income_change_pct: number;
previous_week_expenses: number;
previous_week_income: number;
expense_trend: 'up' | 'down' | 'flat';
income_trend: 'up' | 'down' | 'flat';
};

export type WeeklyDigest = {
id: number;
user_id: number;
week_start: string;
week_end: string;
total_income: number;
total_expenses: number;
net_flow: number;
top_categories: CategoryBreakdown[];
trends: DigestTrends;
ai_insights: string;
created_at: string | null;
};

export async function getWeeklyDigest(): Promise<WeeklyDigest> {
return api<WeeklyDigest>('/digest/weekly');
}

export async function getWeeklyDigestByDate(dateStr: string): Promise<WeeklyDigest> {
return api<WeeklyDigest>(`/digest/weekly/${encodeURIComponent(dateStr)}`);
}

export async function getDigestHistory(limit?: number): Promise<WeeklyDigest[]> {
const query = limit ? `?limit=${limit}` : '';
return api<WeeklyDigest[]>(`/digest/history${query}`);
}

export async function forceGenerateDigest(): Promise<WeeklyDigest> {
return api<WeeklyDigest>('/digest/generate', { method: 'POST' });
}
1 change: 1 addition & 0 deletions app/src/components/layout/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { logout as logoutApi } from '@/api/auth';

const navigation = [
{ name: 'Dashboard', href: '/dashboard' },
{ name: 'Digest', href: '/digest' },
{ name: 'Budgets', href: '/budgets' },
{ name: 'Bills', href: '/bills' },
{ name: 'Reminders', href: '/reminders' },
Expand Down
Loading