72 lines
2.4 KiB
TypeScript
72 lines
2.4 KiB
TypeScript
import { useEffect, useState } from "react";
|
||
import { api, BASE_URL } from "./api/client";
|
||
import "./App.css";
|
||
import { Activities } from "./pages/Activities";
|
||
import { Analysis } from "./pages/Analysis";
|
||
import { Plan } from "./pages/Plan";
|
||
import { Profile } from "./pages/Profile";
|
||
import type { SessionInfo } from "./types/api";
|
||
|
||
const TABS = [
|
||
{ key: "activities", label: "Activities", icon: "☰", Component: Activities },
|
||
{ key: "analysis", label: "Analysis", icon: "🔍", Component: Analysis },
|
||
{ key: "plan", label: "Training plan", icon: "✎", Component: Plan },
|
||
] as const;
|
||
|
||
type TabKey = (typeof TABS)[number]["key"];
|
||
|
||
function App({ session }: { session: SessionInfo }) {
|
||
const [tab, setTab] = useState<TabKey>("activities");
|
||
// Profile isn't a tab: it's reached via the profile name in the top-right
|
||
// corner instead, since (for now, single-profile) it's account settings,
|
||
// not a content view alongside Activities/Analysis/Training plan.
|
||
const [showProfile, setShowProfile] = useState(false);
|
||
const [profileName, setProfileName] = useState<string | null>(null);
|
||
|
||
useEffect(() => {
|
||
api.getProfile().then((p) => setProfileName(p.Name)).catch(() => {});
|
||
}, []);
|
||
|
||
const Active = TABS.find((t) => t.key === tab)!.Component;
|
||
|
||
return (
|
||
<div className="app">
|
||
<header className="app-header">
|
||
<h1>🧞♀️ geniusrun</h1>
|
||
<nav className="tabs">
|
||
{TABS.map((t) => (
|
||
<button
|
||
key={t.key}
|
||
className={!showProfile && t.key === tab ? "tab active" : "tab"}
|
||
onClick={() => {
|
||
setShowProfile(false);
|
||
setTab(t.key);
|
||
}}
|
||
>
|
||
<span className="tab-icon">{t.icon}</span>
|
||
{t.label}
|
||
</button>
|
||
))}
|
||
</nav>
|
||
<div className="header-actions">
|
||
<button
|
||
type="button"
|
||
className={showProfile ? "profile-name active" : "profile-name"}
|
||
onClick={() => setShowProfile(true)}
|
||
>
|
||
{profileName ?? "Profile"}
|
||
</button>
|
||
<form method="post" action={`${BASE_URL}/api/session/logout`} title={session.email}>
|
||
<button type="submit" className="logout-link">
|
||
Log out
|
||
</button>
|
||
</form>
|
||
</div>
|
||
</header>
|
||
<main>{showProfile ? <Profile onSaved={(p) => setProfileName(p.Name)} /> : <Active />}</main>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default App;
|