Files
adventure/apps/web/src/app/page.tsx
Zaine 421c96c814
Some checks failed
CI / test (push) Has been cancelled
docs for memories
2026-06-29 15:00:34 +01:00

128 lines
4.6 KiB
TypeScript
Executable File

"use client";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import { useState } from "react";
import { AppShell } from "@/components/layout/app-shell";
import { CharacterCard } from "@/components/features/character-card";
import { TodaysAdventure } from "@/components/features/todays-adventure";
import { DailyReflection } from "@/components/features/daily-reflection";
import { QuestGiverSidebar, ReadingWidget } from "@/components/features/sidebar-widgets";
import { DaySwitcher } from "@/components/features/day-switcher";
import { CatchUpCard } from "@/components/features/catch-up-card";
import { QuickLogPanel } from "@/components/features/quick-log-panel";
import { ResourceLinkCapture } from "@/components/features/resource-link-capture";
import { formatDisplayDate, todayString } from "@/lib/dates-client";
async function fetchDashboard(date?: string) {
const url = date ? `/api/dashboard?date=${date}` : "/api/dashboard";
const res = await fetch(url);
if (!res.ok) throw new Error("Failed to load dashboard");
return res.json();
}
export default function HomePage() {
const [activeDate, setActiveDate] = useState<string | undefined>(undefined);
const { data, isLoading, error } = useQuery({
queryKey: ["dashboard", activeDate],
queryFn: () => fetchDashboard(activeDate),
placeholderData: keepPreviousData,
staleTime: 30_000,
refetchOnWindowFocus: false,
});
if (isLoading) {
return (
<AppShell>
<div className="p-8 text-center">Loading your adventure...</div>
</AppShell>
);
}
if (error || !data) {
return (
<AppShell>
<div className="p-8 text-center text-[var(--muted-rose)]">
Could not load dashboard. Is the database running?
</div>
</AppShell>
);
}
const date = data.today?.date ?? data.dateContext?.logicalToday ?? todayString();
const ctx = data.dateContext;
return (
<AppShell>
<div className="p-4 pb-20 md:pb-4">
{ctx && (
<DaySwitcher
activeDate={date}
logicalToday={ctx.logicalToday}
logicalYesterday={ctx.logicalYesterday}
isGraceWindow={ctx.isGraceWindow}
onSelectDate={setActiveDate}
/>
)}
<p className="text-sm text-[var(--warm-grey)] mb-4">
{formatDisplayDate(date)}
</p>
{data.catchUpGaps?.length > 0 && (
<CatchUpCard gaps={data.catchUpGaps} onSelectDate={setActiveDate} />
)}
<div className="grid grid-cols-1 lg:grid-cols-12 gap-4">
<div className="lg:col-span-4">
<CharacterCard
displayName={data.user.displayName}
currentTitle={data.user.currentTitle}
level={data.progress.level}
totalXp={data.progress.totalXp}
portraitConfig={data.user.portraitConfig}
scores={{
consistencyScore: data.progress.consistencyScore,
disciplineScore: data.progress.disciplineScore,
learningScore: data.progress.learningScore,
spiritualScore: data.progress.spiritualScore,
healthScore: data.progress.healthScore,
readingScore: data.progress.readingScore,
}}
/>
</div>
<div className="lg:col-span-5 space-y-4">
{data.today && (
<TodaysAdventure
date={date}
chapter={data.progress.currentChapter}
journeyDay={data.progress.journeyDay}
isRestDay={data.today.isRestDay}
isCustomized={data.today.isCustomized}
workHoursTarget={data.today.workHoursTarget}
dayMode={data.today.dayMode ?? "normal"}
isBackfilled={data.today.isBackfilled ?? false}
items={data.today.items}
todos={data.today.todos}
/>
)}
<QuickLogPanel date={date} />
<ResourceLinkCapture date={date} />
<DailyReflection
date={date}
initial={data.reflection}
dateLabel={formatDisplayDate(date)}
/>
</div>
<div className="lg:col-span-3 space-y-4">
<QuestGiverSidebar suggestions={data.suggestions} />
<ReadingWidget
activeBooks={data.reading.activeBooks}
streak={data.reading.streak}
weeklyPages={data.reading.weeklyPages}
weeklyGoal={data.reading.weeklyGoal}
/>
</div>
</div>
</div>
</AppShell>
);
}