122
apps/web/src/app/page.tsx
Executable file
122
apps/web/src/app/page.tsx
Executable file
@@ -0,0 +1,122 @@
|
||||
"use client";
|
||||
|
||||
import { 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 { 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),
|
||||
});
|
||||
|
||||
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} />
|
||||
<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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user