Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
34fabb9
Token file now fitting earlier theme and has dark mode lght mode (com…
TimPolk Mar 26, 2026
3208653
Updated css in styles components
TimPolk Mar 26, 2026
c7bdc5b
Components css updated to match color themes, added landing page once…
TimPolk Mar 26, 2026
567ec20
Adjusted charts for css and styling with dark mode
TimPolk Mar 26, 2026
2750d85
Added settings to the dashboard
TimPolk Mar 26, 2026
fb0f2cf
Created settings page and utilities
TimPolk Mar 26, 2026
2b09695
Fixed barchart to fill in card better
TimPolk Mar 27, 2026
5d5d3e2
Reverted back to a centered hero card instead of split design
TimPolk Mar 27, 2026
efdea16
Created devMock for testing, changed dashboard to have better design …
TimPolk Mar 27, 2026
27a1dd0
Removed dev testing set up
TimPolk Mar 30, 2026
02651f7
Added hosting once again for mobile test
TimPolk Mar 30, 2026
de8b1e0
Added more authentication routes for passwords, and delete account
TimPolk Mar 30, 2026
0e4f4c3
Fix lambda memory and frontend crash
YounBrand Mar 30, 2026
8926610
Tf fix
YounBrand Mar 30, 2026
b556c15
fix frontend crash
YounBrand Mar 30, 2026
5b0cce7
Fix tests when docker is active
YounBrand Mar 30, 2026
9edc3a0
create investments/debts pages
CjKhaled Apr 1, 2026
8071be3
fix onboarding issues with email/profile
CjKhaled Apr 1, 2026
e85802b
ofinalize changes
CjKhaled Apr 1, 2026
3886236
finalize changes
CjKhaled Apr 1, 2026
7554ac6
fix lint issues
CjKhaled Apr 1, 2026
45118d2
Components css updated to match color themes, added landing page once…
TimPolk Mar 26, 2026
589cc82
Created settings page and utilities
TimPolk Mar 26, 2026
7327a93
Reverted back to a centered hero card instead of split design
TimPolk Mar 27, 2026
5ba2fa4
Created devMock for testing, changed dashboard to have better design …
TimPolk Mar 27, 2026
1ed2207
Added the rest of my changes
TimPolk Apr 6, 2026
95ace30
Tested on lint and test suite
TimPolk Apr 7, 2026
549ad26
Merge branch 'main' into UI
TimPolk Apr 7, 2026
e02b5eb
Fixed incompatible types in BarChart
TimPolk Apr 7, 2026
d4ec5f4
LineChart same issue fixed
TimPolk Apr 7, 2026
a5ef9cb
Vite fix
TimPolk Apr 7, 2026
6fa8995
Vite fix part 2
TimPolk Apr 7, 2026
29585a5
Undo breaking change
TimPolk Apr 7, 2026
a73994b
Possible fix
TimPolk Apr 7, 2026
088f0ef
Added anthropic key to tf
YounBrand Apr 7, 2026
8958466
Added confirmation button to verifyEmailPage to fix email verificatio…
YounBrand Apr 7, 2026
fb07c21
Error msg fix; jwt token fix; proposal card additional conf; rm bell
TimPolk Apr 7, 2026
47c04f4
Proposal Card fix v2
TimPolk Apr 7, 2026
6831848
Fixed type issues/warnings
TimPolk Apr 7, 2026
0064bb1
fix: persist login across sessions and improve auth UX. Removed propo…
YounBrand Apr 9, 2026
6c67f56
Merge branch 'UI' into httponly-cookies
YounBrand Apr 9, 2026
98d758d
Migrate auth token storage from localStorage to httpOnly cookies
YounBrand Apr 9, 2026
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
85 changes: 48 additions & 37 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Routes, Route, Navigate } from 'react-router-dom'
import { AuthProvider } from './context/AuthContext'
import { SettingsProvider } from './context/SettingsContext'
import ProtectedRoute from './pages/ProtectedRoute'
import AppShell from './components/layout/AppShell'
import Landing from './pages/Landing'
import LoginPage from './pages/LoginPage'
import SignUpPage from './pages/SignUpPage'
import CheckEmailPage from './pages/CheckEmailPage'
Expand All @@ -14,49 +16,58 @@ import InvestmentsPage from './pages/InvestmentsPage'
import DebtsPage from './pages/DebtsPage'
import ProposalsPage from './pages/ProposalsPage'
import ProfilePage from './pages/ProfilePage'
import SettingsPage from './pages/SettingsPage'
import ForgotPasswordPage from './pages/ForgotPasswordPage'
import ResetPasswordPage from './pages/ResetPasswordPage'

function App() {
return (
<AuthProvider>
<Routes>
{/* Public */}
<Route path="/login" element={<LoginPage />} />
<Route path="/signup" element={<SignUpPage />} />
<Route path="/check-email" element={<CheckEmailPage />} />
<Route path="/verify-email" element={<VerifyEmailPage />} />
<SettingsProvider>
<AuthProvider>
<Routes>
{/* Public */}
<Route path="/" element={<Landing />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/signup" element={<SignUpPage />} />
<Route path="/check-email" element={<CheckEmailPage />} />
<Route path="/verify-email" element={<VerifyEmailPage />} />
<Route path="/forgot-password" element={<ForgotPasswordPage />} />
<Route path="/reset-password" element={<ResetPasswordPage />} />

{/* Post-signup onboarding */}
<Route
path="/link-bank"
element={
<ProtectedRoute>
<LinkBankPage />
</ProtectedRoute>
}
/>
{/* Post-signup onboarding */}
<Route
path="/link-bank"
element={
<ProtectedRoute>
<LinkBankPage />
</ProtectedRoute>
}
/>

{/* Authenticated (inside AppShell) */}
<Route
element={
<ProtectedRoute>
<AppShell />
</ProtectedRoute>
}
>
<Route path="/dashboard" element={<DashboardPage />} />
<Route path="/budget" element={<BudgetPage />} />
<Route path="/savings" element={<SavingsPage />} />
<Route path="/investments" element={<InvestmentsPage />} />
<Route path="/debts" element={<DebtsPage />} />
<Route path="/proposals" element={<ProposalsPage />} />
<Route path="/profile" element={<ProfilePage />} />
</Route>
{/* Authenticated (inside AppShell) */}
<Route
element={
<ProtectedRoute>
<AppShell />
</ProtectedRoute>
}
>
<Route path="/dashboard" element={<DashboardPage />} />
<Route path="/budget" element={<BudgetPage />} />
<Route path="/savings" element={<SavingsPage />} />
<Route path="/investments" element={<InvestmentsPage />} />
<Route path="/debts" element={<DebtsPage />} />
<Route path="/proposals" element={<ProposalsPage />} />
<Route path="/profile" element={<ProfilePage />} />
<Route path="/settings" element={<SettingsPage />} />
</Route>

{/* Fallback */}
<Route path="*" element={<Navigate to="/dashboard" />} />
</Routes>
</AuthProvider>
{/* Fallback */}
<Route path="*" element={<Navigate to="/" />} />
</Routes>
</AuthProvider>
</SettingsProvider>
)
}

export default App
export default App
3 changes: 3 additions & 0 deletions client/src/components/charts/BarChart.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.bar-chart {
width: 100%;
}
121 changes: 86 additions & 35 deletions client/src/components/charts/BarChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import {
YAxis,
CartesianGrid,
Tooltip,
Cell,
ResponsiveContainer,
LabelList,
} from 'recharts'
import './BarChart.css'

export interface BarDataPoint {
label: string
Expand All @@ -17,42 +20,90 @@ export interface BarDataPoint {
interface BarChartProps {
data: BarDataPoint[]
color?: string
showValues?: boolean
}

export default function BarChart({ data, color = 'var(--color-chart-1)' }: BarChartProps) {
/* Hardcoded palette so bars are always visible in both themes */
const BAR_PALETTE = [
'#00D4AA', // teal — always visible on both light and dark
'#457B9D', // steel blue
'#14B8A6', // mint
'#F59E0B', // amber
'#3B82F6', // blue
'#8B5CF6', // purple
'#EF4444', // red
]

const fmt = (v: number) =>
new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
maximumFractionDigits: 0,
}).format(v)

export default function BarChart({ data, color, showValues = true }: BarChartProps) {
const maxValue = Math.max(...data.map((d) => d.value), 0)

return (
<ResponsiveContainer width="100%" height={200}>
<ReBarChart data={data} margin={{ top: 4, right: 4, bottom: 4, left: 4 }}>
<CartesianGrid stroke="var(--color-border)" strokeDasharray="3 3" vertical={false} />
<XAxis
dataKey="label"
tick={{ fill: 'var(--color-text-muted)', fontSize: 12 }}
axisLine={false}
tickLine={false}
/>
<YAxis
tick={{ fill: 'var(--color-text-muted)', fontSize: 12 }}
axisLine={false}
tickLine={false}
width={48}
tickFormatter={(v) => `$${v}`}
/>
<Tooltip
contentStyle={{
background: 'var(--color-bg-elevated)',
border: '1px solid var(--color-border)',
borderRadius: '10px',
color: 'var(--color-text-primary)',
fontSize: '0.875rem',
}}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
formatter={(value: any) => {
const n = typeof value === 'number' ? value : 0
return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(n)
}}
/>
<Bar dataKey="value" fill={color} radius={[4, 4, 0, 0]} />
</ReBarChart>
</ResponsiveContainer>
<div className="bar-chart">
<ResponsiveContainer width="100%" height={360}>
<ReBarChart data={data} margin={{ top: 24, right: 4, bottom: 4, left: 4 }}>
<CartesianGrid
stroke="var(--color-border)"
strokeDasharray="3 3"
vertical={false}
/>
<XAxis
dataKey="label"
tick={{ fill: 'var(--color-text-muted)', fontSize: 12 }}
axisLine={false}
tickLine={false}
/>
<YAxis
tick={{ fill: 'var(--color-text-muted)', fontSize: 12 }}
axisLine={false}
tickLine={false}
width={52}
domain={[0, Math.ceil(maxValue * 1.15 / 1000) * 1000]}
tickFormatter={(v) => `$${(v / 1000).toFixed(1)}k`}
/>
<Tooltip
cursor={{ fill: 'var(--color-bg-hover)', opacity: 0.5 }}
contentStyle={{
background: 'var(--color-bg-surface)',
border: '1px solid var(--color-border)',
borderRadius: '10px',
color: 'var(--color-text-primary)',
fontSize: '0.875rem',
boxShadow: 'var(--shadow-md)',
}}
formatter={(value: number | string | undefined) => {
const n = typeof value === 'number' ? value : 0
return fmt(n)
}}
/>
<Bar dataKey="value" radius={[6, 6, 0, 0]} maxBarSize={56}>
{data.map((entry, i) => (
<Cell
key={entry.label}
fill={entry.color || color || BAR_PALETTE[i % BAR_PALETTE.length]}
/>
))}
{showValues && (
<LabelList
dataKey="value"
position="top"
formatter={((v: unknown) => fmt(typeof v === 'number' ? v : 0)) as never}
style={{
fill: 'var(--color-text-dimmed)',
fontSize: '11px',
fontWeight: 600,
}}
/>
)}
</Bar>
</ReBarChart>
</ResponsiveContainer>
</div>
)
}
}
8 changes: 5 additions & 3 deletions client/src/components/charts/ChartCard.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
display: flex;
flex-direction: column;
gap: var(--space-4);
min-width: 0; /* prevent grid blowout from recharts */
overflow: hidden;
}

.chart-card__header {
Expand All @@ -11,8 +13,8 @@
}

.chart-card__title {
font-size: var(--text-base);
font-weight: var(--weight-semibold);
font-size: 0.90625rem;
font-weight: var(--weight-bold);
color: var(--color-text-primary);
font-family: var(--font-heading);
}
Expand All @@ -24,4 +26,4 @@

.chart-card__body {
flex: 1;
}
}
87 changes: 87 additions & 0 deletions client/src/components/charts/DonutChart.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
.donut {
display: flex;
flex-direction: column;
gap: var(--space-3);
}

/* ── Chart with center overlay ── */
.donut__chart-wrap {
position: relative;
width: 100%;
}

.donut__center {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
pointer-events: none;
display: flex;
flex-direction: column;
align-items: center;
gap: 0;
}

.donut__center-amount {
font-size: 1rem;
font-weight: var(--weight-extrabold);
color: var(--color-text-primary);
line-height: 1.1;
font-variant-numeric: tabular-nums;
}

.donut__center-label {
font-size: 0.5625rem;
font-weight: var(--weight-bold);
color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: 0.5px;
}

/* ── Legend — compact rows ── */
.donut__legend {
display: flex;
flex-direction: column;
gap: 0.25rem;
}

.donut__legend-row {
display: flex;
align-items: center;
gap: 0.375rem;
font-size: 0.6875rem;
line-height: 1.6;
}

.donut__legend-dot {
width: 8px;
height: 8px;
border-radius: 2px;
flex-shrink: 0;
}

.donut__legend-name {
font-weight: var(--weight-medium);
color: var(--color-text-secondary);
flex: 1;
min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

.donut__legend-val {
font-weight: var(--weight-semibold);
color: var(--color-text-primary);
font-variant-numeric: tabular-nums;
white-space: nowrap;
}

.donut__legend-pct {
font-weight: var(--weight-medium);
color: var(--color-text-muted);
min-width: 1.75rem;
text-align: right;
white-space: nowrap;
}
Loading
Loading