Initial commit: Second Brain Platform
Complete platform with unified design system and real API integration. Apps: Dashboard, Fitness, Budget, Inventory, Trips, Reader, Media, Settings Infrastructure: SvelteKit + Python gateway + Docker Compose
This commit is contained in:
127
frontend-v2/src/routes/(app)/+page.svelte
Normal file
127
frontend-v2/src/routes/(app)/+page.svelte
Normal file
@@ -0,0 +1,127 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import DashboardActionCard from '$lib/components/dashboard/DashboardActionCard.svelte';
|
||||
import BudgetModule from '$lib/components/dashboard/BudgetModule.svelte';
|
||||
import FitnessModule from '$lib/components/dashboard/FitnessModule.svelte';
|
||||
import IssuesModule from '$lib/components/dashboard/IssuesModule.svelte';
|
||||
|
||||
let inventoryIssueCount = $state(0);
|
||||
let inventoryReviewCount = $state(0);
|
||||
let budgetUncatCount = $state(0);
|
||||
let budgetSpending = $state('');
|
||||
let budgetIncome = $state('');
|
||||
let fitnessCalRemaining = $state(0);
|
||||
let fitnessCalLogged = $state(0);
|
||||
let fitnessProtein = $state(0);
|
||||
let fitnessCarbs = $state(0);
|
||||
|
||||
onMount(async () => {
|
||||
const n = new Date();
|
||||
const today = `${n.getFullYear()}-${String(n.getMonth() + 1).padStart(2, '0')}-${String(n.getDate()).padStart(2, '0')}`;
|
||||
|
||||
try {
|
||||
const [invRes, budgetRes, uncatRes, fitTotalsRes, fitGoalsRes] = await Promise.all([
|
||||
fetch('/api/inventory/summary', { credentials: 'include' }),
|
||||
fetch('/api/budget/summary', { credentials: 'include' }),
|
||||
fetch('/api/budget/uncategorized-count', { credentials: 'include' }),
|
||||
fetch(`/api/fitness/entries/totals?date=${today}`, { credentials: 'include' }),
|
||||
fetch(`/api/fitness/goals/for-date?date=${today}`, { credentials: 'include' }),
|
||||
]);
|
||||
if (invRes.ok) {
|
||||
const data = await invRes.json();
|
||||
inventoryIssueCount = data.issueCount || 0;
|
||||
inventoryReviewCount = data.reviewCount || 0;
|
||||
}
|
||||
if (budgetRes.ok) {
|
||||
const data = await budgetRes.json();
|
||||
budgetSpending = '$' + Math.abs(data.spendingDollars || 0).toLocaleString('en-US');
|
||||
budgetIncome = '$' + Math.abs(data.incomeDollars || 0).toLocaleString('en-US');
|
||||
}
|
||||
if (uncatRes.ok) {
|
||||
const data = await uncatRes.json();
|
||||
budgetUncatCount = data.count || 0;
|
||||
}
|
||||
if (fitTotalsRes.ok) {
|
||||
const t = await fitTotalsRes.json();
|
||||
fitnessCalLogged = Math.round(t.total_calories || 0);
|
||||
fitnessProtein = Math.round(t.total_protein || 0);
|
||||
fitnessCarbs = Math.round(t.total_carbs || 0);
|
||||
}
|
||||
if (fitGoalsRes.ok) {
|
||||
const g = await fitGoalsRes.json();
|
||||
fitnessCalRemaining = Math.max(0, (g.calories || 2000) - fitnessCalLogged);
|
||||
}
|
||||
} catch { /* silent */ }
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="page">
|
||||
<div class="app-surface">
|
||||
<div class="page-header">
|
||||
<div class="page-title">Dashboard</div>
|
||||
<div class="page-greeting">Good to see you, <strong>Yusuf</strong></div>
|
||||
</div>
|
||||
|
||||
<div class="action-cards">
|
||||
<DashboardActionCard
|
||||
title="{budgetUncatCount} uncategorized transactions"
|
||||
description="{budgetSpending} spent · {budgetIncome} income"
|
||||
action="Review"
|
||||
variant="budget"
|
||||
size="primary"
|
||||
href="/budget"
|
||||
/>
|
||||
<DashboardActionCard
|
||||
title="{inventoryIssueCount} issue{inventoryIssueCount !== 1 ? 's' : ''} · {inventoryReviewCount} needs review"
|
||||
description="{inventoryIssueCount + inventoryReviewCount} items need attention"
|
||||
action="View"
|
||||
variant="inventory"
|
||||
href="/inventory"
|
||||
/>
|
||||
<DashboardActionCard
|
||||
title="{fitnessCalRemaining.toLocaleString()} calories remaining today"
|
||||
description="{fitnessCalLogged.toLocaleString()} cal logged · {fitnessProtein}g protein · {fitnessCarbs}g carbs"
|
||||
action="Log food"
|
||||
variant="fitness"
|
||||
href="/fitness"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="modules-grid">
|
||||
<BudgetModule />
|
||||
<div class="right-stack">
|
||||
<FitnessModule />
|
||||
<IssuesModule />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.action-cards {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--section-gap);
|
||||
margin-bottom: calc(var(--section-gap) + 8px);
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.modules-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1.3fr 0.7fr;
|
||||
gap: var(--module-gap);
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.right-stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--module-gap);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.modules-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user