generated from nhcarrigan/template
24beaf3131
Shows a relative time label (e.g. "☁️ 2m ago") in the resource bar seeded from the server on load and updated after every auto-save or manual save. A 💾 button lets players trigger an immediate cloud save outside the 30-second auto-save cycle, with a ⏳ spinner and disabled state while the request is in-flight.
107 lines
3.5 KiB
TypeScript
107 lines
3.5 KiB
TypeScript
import { useState } from "react";
|
|
import { useGame } from "../../context/GameContext.js";
|
|
import { ResourceBar } from "../ui/ResourceBar.js";
|
|
import { AchievementPanel } from "./AchievementPanel.js";
|
|
import { AchievementToast } from "./AchievementToast.js";
|
|
import { AdventurerPanel } from "./AdventurerPanel.js";
|
|
import { BattleModal } from "./BattleModal.js";
|
|
import { BossPanel } from "./BossPanel.js";
|
|
import { ClickArea } from "./ClickArea.js";
|
|
import { EditProfileModal } from "./EditProfileModal.js";
|
|
import { EquipmentPanel } from "./EquipmentPanel.js";
|
|
import { OfflineModal } from "./OfflineModal.js";
|
|
import { PrestigePanel } from "./PrestigePanel.js";
|
|
import { QuestPanel } from "./QuestPanel.js";
|
|
import { UpgradePanel } from "./UpgradePanel.js";
|
|
|
|
type Tab = "adventurers" | "upgrades" | "quests" | "bosses" | "equipment" | "achievements" | "prestige";
|
|
|
|
const TABS: { id: Tab; label: string }[] = [
|
|
{ id: "adventurers", label: "⚔️ Adventurers" },
|
|
{ id: "upgrades", label: "🔧 Upgrades" },
|
|
{ id: "quests", label: "📜 Quests" },
|
|
{ id: "bosses", label: "👹 Bosses" },
|
|
{ id: "equipment", label: "🗡️ Equipment" },
|
|
{ id: "achievements", label: "🏆 Achievements" },
|
|
{ id: "prestige", label: "⭐ Prestige" },
|
|
];
|
|
|
|
export const GameLayout = (): React.JSX.Element => {
|
|
const { state, isLoading, error, battleResult, dismissBattle, lastSavedAt, isSyncing, forceSync } = useGame();
|
|
const [activeTab, setActiveTab] = useState<Tab>("adventurers");
|
|
const [editingProfile, setEditingProfile] = useState(false);
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<div className="loading-screen">
|
|
<p>Loading your adventure...</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<div className="error-screen">
|
|
<p>Error: {error}</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!state) return <div className="loading-screen"><p>Loading...</p></div>;
|
|
|
|
const profileUrl = `/profile/${state.player.discordId}`;
|
|
|
|
return (
|
|
<div className="game-layout">
|
|
<ResourceBar
|
|
resources={state.resources}
|
|
prestigeCount={state.prestige.count}
|
|
profileUrl={profileUrl}
|
|
onEditProfile={() => { setEditingProfile(true); }}
|
|
lastSavedAt={lastSavedAt}
|
|
isSyncing={isSyncing}
|
|
onForceSync={forceSync}
|
|
/>
|
|
<OfflineModal />
|
|
<AchievementToast />
|
|
{battleResult && (
|
|
<BattleModal battle={battleResult} onDismiss={dismissBattle} />
|
|
)}
|
|
{editingProfile && (
|
|
<EditProfileModal onClose={() => { setEditingProfile(false); }} />
|
|
)}
|
|
|
|
<div className="game-main">
|
|
<aside className="game-sidebar">
|
|
<ClickArea />
|
|
</aside>
|
|
|
|
<main className="game-content">
|
|
<nav className="tab-bar">
|
|
{TABS.map((tab) => (
|
|
<button
|
|
key={tab.id}
|
|
className={`tab-button ${activeTab === tab.id ? "active" : ""}`}
|
|
onClick={() => { setActiveTab(tab.id); }}
|
|
type="button"
|
|
>
|
|
{tab.label}
|
|
</button>
|
|
))}
|
|
</nav>
|
|
|
|
<div className="tab-content">
|
|
{activeTab === "adventurers" && <AdventurerPanel />}
|
|
{activeTab === "upgrades" && <UpgradePanel />}
|
|
{activeTab === "quests" && <QuestPanel />}
|
|
{activeTab === "bosses" && <BossPanel />}
|
|
{activeTab === "equipment" && <EquipmentPanel />}
|
|
{activeTab === "achievements" && <AchievementPanel />}
|
|
{activeTab === "prestige" && <PrestigePanel />}
|
|
</div>
|
|
</main>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|