feat: sidebar navigation to dedicated detail views
- Sidebar icons now navigate to full-screen views for each tool - Dashboard bento cards have 'Expand' buttons that open the detail view - Back arrow (←) in title bar returns to dashboard - Detail views wrapped in a glass-card with max-w-2xl layout - Settings page gets its own full layout with heading - World Map, Quest Designer, Soundboard show placeholder screens with icons - All nav items in sidebar: World, NPCs, Encounter, Dice, Session, Sound, Initiative, Random Tables, Calendar, Settings
This commit is contained in:
+162
-20
@@ -7,48 +7,129 @@ import {
|
|||||||
Volume2,
|
Volume2,
|
||||||
Settings,
|
Settings,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
|
ChevronLeft,
|
||||||
|
Sparkles,
|
||||||
|
Timer,
|
||||||
|
Calendar,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Dashboard } from "./components/Dashboard";
|
import { Dashboard } from "./components/Dashboard";
|
||||||
import { SettingsPanel } from "./components/SettingsPanel";
|
import { SettingsPanel } from "./components/SettingsPanel";
|
||||||
|
import { NpcGenerator } from "./components/NpcGenerator";
|
||||||
|
import { DiceRoller } from "./components/DiceRoller";
|
||||||
|
import { SessionLogger } from "./components/SessionLogger";
|
||||||
|
import { EncounterBuilder } from "./components/EncounterBuilder";
|
||||||
|
import { InitiativeTracker } from "./components/InitiativeTracker";
|
||||||
|
import { RandomTables } from "./components/RandomTables";
|
||||||
|
import { CalendarWidget } from "./components/CalendarWidget";
|
||||||
|
|
||||||
type View = "dashboard" | "settings";
|
export type View =
|
||||||
|
| "dashboard"
|
||||||
|
| "world"
|
||||||
|
| "npcs"
|
||||||
|
| "encounter"
|
||||||
|
| "dice"
|
||||||
|
| "session"
|
||||||
|
| "sound"
|
||||||
|
| "initiative"
|
||||||
|
| "tables"
|
||||||
|
| "calendar"
|
||||||
|
| "quest"
|
||||||
|
| "settings";
|
||||||
|
|
||||||
const navItems = [
|
const navItems: { icon: typeof Map; label: string; view: View }[] = [
|
||||||
{ icon: Map, label: "World" },
|
{ icon: Map, label: "World", view: "world" },
|
||||||
{ icon: User, label: "NPCs" },
|
{ icon: User, label: "NPCs", view: "npcs" },
|
||||||
{ icon: Swords, label: "Encounter" },
|
{ icon: Swords, label: "Encounter", view: "encounter" },
|
||||||
{ icon: Dice5, label: "Dice" },
|
{ icon: Dice5, label: "Dice", view: "dice" },
|
||||||
{ icon: ScrollText, label: "Session" },
|
{ icon: ScrollText, label: "Session", view: "session" },
|
||||||
{ icon: Volume2, label: "Sound" },
|
{ icon: Volume2, label: "Sound", view: "sound" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function renderView(view: View): React.ReactNode {
|
||||||
|
switch (view) {
|
||||||
|
case "npcs":
|
||||||
|
return <NpcGenerator />;
|
||||||
|
case "dice":
|
||||||
|
return <DiceRoller />;
|
||||||
|
case "session":
|
||||||
|
return <SessionLogger />;
|
||||||
|
case "encounter":
|
||||||
|
return <EncounterBuilder />;
|
||||||
|
case "initiative":
|
||||||
|
return <InitiativeTracker />;
|
||||||
|
case "tables":
|
||||||
|
return <RandomTables />;
|
||||||
|
case "calendar":
|
||||||
|
return <CalendarWidget />;
|
||||||
|
case "settings":
|
||||||
|
return <SettingsPanel />;
|
||||||
|
case "world":
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-center h-full text-[var(--color-text-dim)]">
|
||||||
|
<div className="text-center">
|
||||||
|
<Map size={64} className="mx-auto mb-4 opacity-30" />
|
||||||
|
<p className="text-lg font-heading">World Map</p>
|
||||||
|
<p className="text-sm mt-2">Interactive map canvas coming soon</p>
|
||||||
|
<p className="text-xs mt-1 opacity-60">react-konva integration pending</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case "quest":
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-center h-full text-[var(--color-text-dim)]">
|
||||||
|
<div className="text-center">
|
||||||
|
<Sparkles size={64} className="mx-auto mb-4 opacity-30" />
|
||||||
|
<p className="text-lg font-heading">Quest Designer</p>
|
||||||
|
<p className="text-sm mt-2">Branching flowchart coming soon</p>
|
||||||
|
<p className="text-xs mt-1 opacity-60">react-flow integration pending</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case "sound":
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-center h-full text-[var(--color-text-dim)]">
|
||||||
|
<div className="text-center">
|
||||||
|
<Volume2 size={64} className="mx-auto mb-4 opacity-30" />
|
||||||
|
<p className="text-lg font-heading">Soundboard</p>
|
||||||
|
<p className="text-sm mt-2">Local audio clips for ambience</p>
|
||||||
|
<p className="text-xs mt-1 opacity-60">Coming soon</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const [view, setView] = useState<View>("dashboard");
|
const [view, setView] = useState<View>("dashboard");
|
||||||
const [activeNav, setActiveNav] = useState(0);
|
|
||||||
|
const isDetailView = view !== "dashboard" && view !== "settings";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-screen w-screen overflow-hidden bg-[var(--color-bg-deep)]">
|
<div className="flex h-screen w-screen overflow-hidden bg-[var(--color-bg-deep)]">
|
||||||
{/* Left Rail */}
|
{/* Left Rail */}
|
||||||
<nav className="flex flex-col items-center w-14 bg-[var(--color-bg-surface)] border-r border-[var(--color-border-subtle)] py-3 gap-1 shrink-0">
|
<nav className="flex flex-col items-center w-14 bg-[var(--color-bg-surface)] border-r border-[var(--color-border-subtle)] py-3 gap-1 shrink-0">
|
||||||
{/* App Logo */}
|
{/* App Logo — goes to dashboard */}
|
||||||
<button
|
<button
|
||||||
onClick={() => setView("dashboard")}
|
onClick={() => setView("dashboard")}
|
||||||
className="w-8 h-8 rounded-lg bg-[var(--color-gold-bright)] flex items-center justify-center mb-4 text-[var(--color-bg-deep)] font-heading font-bold text-lg cursor-pointer"
|
className={`w-8 h-8 rounded-lg flex items-center justify-center mb-4 font-heading font-bold text-lg cursor-pointer transition-colors ${
|
||||||
title="DM-Pal"
|
view === "dashboard"
|
||||||
|
? "bg-[var(--color-gold-bright)] text-[var(--color-bg-deep)]"
|
||||||
|
: "bg-[var(--color-bg-card)] text-[var(--color-gold-bright)] hover:bg-[var(--color-gold-bright)] hover:text-[var(--color-bg-deep)]"
|
||||||
|
}`}
|
||||||
|
title="Dashboard"
|
||||||
>
|
>
|
||||||
⚔
|
⚔
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{navItems.map((item, i) => (
|
{navItems.map((item) => (
|
||||||
<button
|
<button
|
||||||
key={item.label}
|
key={item.label}
|
||||||
onClick={() => {
|
onClick={() => setView(item.view)}
|
||||||
setActiveNav(i);
|
|
||||||
setView("dashboard");
|
|
||||||
}}
|
|
||||||
className={`w-10 h-10 rounded-lg flex items-center justify-center transition-colors cursor-pointer ${
|
className={`w-10 h-10 rounded-lg flex items-center justify-center transition-colors cursor-pointer ${
|
||||||
i === activeNav && view === "dashboard"
|
view === item.view
|
||||||
? "bg-[var(--color-bg-card)] text-[var(--color-gold-bright)]"
|
? "bg-[var(--color-bg-card)] text-[var(--color-gold-bright)]"
|
||||||
: "text-[var(--color-text-dim)] hover:text-[var(--color-text-secondary)] hover:bg-[var(--color-bg-card)]"
|
: "text-[var(--color-text-dim)] hover:text-[var(--color-text-secondary)] hover:bg-[var(--color-bg-card)]"
|
||||||
}`}
|
}`}
|
||||||
@@ -61,7 +142,44 @@ export default function App() {
|
|||||||
{/* Spacer */}
|
{/* Spacer */}
|
||||||
<div className="flex-1" />
|
<div className="flex-1" />
|
||||||
|
|
||||||
{/* Settings at bottom */}
|
{/* Extra nav items */}
|
||||||
|
<button
|
||||||
|
onClick={() => setView("initiative")}
|
||||||
|
className={`w-10 h-10 rounded-lg flex items-center justify-center transition-colors cursor-pointer ${
|
||||||
|
view === "initiative"
|
||||||
|
? "bg-[var(--color-bg-card)] text-[var(--color-gold-bright)]"
|
||||||
|
: "text-[var(--color-text-dim)] hover:text-[var(--color-text-secondary)] hover:bg-[var(--color-bg-card)]"
|
||||||
|
}`}
|
||||||
|
title="Initiative"
|
||||||
|
>
|
||||||
|
<Timer size={18} />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setView("tables")}
|
||||||
|
className={`w-10 h-10 rounded-lg flex items-center justify-center transition-colors cursor-pointer ${
|
||||||
|
view === "tables"
|
||||||
|
? "bg-[var(--color-bg-card)] text-[var(--color-gold-bright)]"
|
||||||
|
: "text-[var(--color-text-dim)] hover:text-[var(--color-text-secondary)] hover:bg-[var(--color-bg-card)]"
|
||||||
|
}`}
|
||||||
|
title="Random Tables"
|
||||||
|
>
|
||||||
|
<Sparkles size={18} />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setView("calendar")}
|
||||||
|
className={`w-10 h-10 rounded-lg flex items-center justify-center transition-colors cursor-pointer ${
|
||||||
|
view === "calendar"
|
||||||
|
? "bg-[var(--color-bg-card)] text-[var(--color-gold-bright)]"
|
||||||
|
: "text-[var(--color-text-dim)] hover:text-[var(--color-text-secondary)] hover:bg-[var(--color-bg-card)]"
|
||||||
|
}`}
|
||||||
|
title="Calendar"
|
||||||
|
>
|
||||||
|
<Calendar size={18} />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div className="w-6 h-px bg-[var(--color-border-subtle)] my-1" />
|
||||||
|
|
||||||
|
{/* Settings */}
|
||||||
<button
|
<button
|
||||||
onClick={() => setView(view === "settings" ? "dashboard" : "settings")}
|
onClick={() => setView(view === "settings" ? "dashboard" : "settings")}
|
||||||
className={`w-10 h-10 rounded-lg flex items-center justify-center transition-colors cursor-pointer ${
|
className={`w-10 h-10 rounded-lg flex items-center justify-center transition-colors cursor-pointer ${
|
||||||
@@ -82,6 +200,15 @@ export default function App() {
|
|||||||
className="flex items-center h-10 px-4 bg-[var(--color-bg-surface)] border-b border-[var(--color-border-subtle)] shrink-0"
|
className="flex items-center h-10 px-4 bg-[var(--color-bg-surface)] border-b border-[var(--color-border-subtle)] shrink-0"
|
||||||
data-tauri-drag-region
|
data-tauri-drag-region
|
||||||
>
|
>
|
||||||
|
{isDetailView && (
|
||||||
|
<button
|
||||||
|
onClick={() => setView("dashboard")}
|
||||||
|
className="mr-3 text-[var(--color-text-secondary)] hover:text-[var(--color-gold-bright)] transition-colors cursor-pointer"
|
||||||
|
title="Back to dashboard"
|
||||||
|
>
|
||||||
|
<ChevronLeft size={18} />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
<span className="font-heading text-[var(--color-gold-bright)] text-base font-bold tracking-wider">
|
<span className="font-heading text-[var(--color-gold-bright)] text-base font-bold tracking-wider">
|
||||||
DM-Pal
|
DM-Pal
|
||||||
</span>
|
</span>
|
||||||
@@ -93,7 +220,22 @@ export default function App() {
|
|||||||
|
|
||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
<main className="flex-1 overflow-auto">
|
<main className="flex-1 overflow-auto">
|
||||||
{view === "dashboard" ? <Dashboard /> : <SettingsPanel />}
|
{view === "dashboard" ? (
|
||||||
|
<Dashboard onNavigate={setView} />
|
||||||
|
) : view === "settings" ? (
|
||||||
|
<div className="max-w-lg mx-auto p-6">
|
||||||
|
<h2 className="font-heading text-[var(--color-gold-bright)] text-lg font-semibold mb-4">
|
||||||
|
Settings
|
||||||
|
</h2>
|
||||||
|
<SettingsPanel />
|
||||||
|
</div>
|
||||||
|
) : isDetailView ? (
|
||||||
|
<div className="max-w-2xl mx-auto p-6">
|
||||||
|
<div className="glass-card p-6">
|
||||||
|
{renderView(view)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+156
-67
@@ -8,78 +8,167 @@ import {
|
|||||||
Timer,
|
Timer,
|
||||||
Volume2,
|
Volume2,
|
||||||
Calendar,
|
Calendar,
|
||||||
|
Maximize2,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { BentoCard } from "./BentoCard";
|
import { BentoCard } from "./BentoCard";
|
||||||
|
import type { View } from "../App";
|
||||||
|
|
||||||
|
interface DashboardProps {
|
||||||
|
onNavigate: (view: View) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Dashboard({ onNavigate }: DashboardProps) {
|
||||||
|
return (
|
||||||
|
<div className="grid grid-cols-4 grid-rows-4 gap-3 p-4 h-full bg-[var(--color-bg-deep)]">
|
||||||
|
{/* World Map — 2×2 */}
|
||||||
|
<BentoCard title="World Map" icon={<Map size={16} />} span="col-span-2 row-span-2">
|
||||||
|
<div className="flex flex-col items-center justify-center h-full text-[var(--color-text-dim)] text-sm gap-2">
|
||||||
|
<Map size={48} className="opacity-30" />
|
||||||
|
<p>Map canvas coming soon</p>
|
||||||
|
<button
|
||||||
|
onClick={() => onNavigate("world")}
|
||||||
|
className="text-[var(--color-gold-bright)] hover:text-[var(--color-gold-muted)] text-xs flex items-center gap-1 cursor-pointer transition-colors"
|
||||||
|
>
|
||||||
|
<Maximize2 size={12} /> Open full view
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</BentoCard>
|
||||||
|
|
||||||
|
{/* NPC Sheet — 1×1 */}
|
||||||
|
<BentoCard title="NPC Sheet" icon={<User size={16} />} span="col-span-1 row-span-1">
|
||||||
|
<div className="flex flex-col h-full">
|
||||||
|
<div className="flex-1 overflow-hidden">
|
||||||
|
<NpcGenerator />
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => onNavigate("npcs")}
|
||||||
|
className="text-[var(--color-gold-bright)] hover:text-[var(--color-gold-muted)] text-xs flex items-center justify-center gap-1 cursor-pointer transition-colors mt-1 pt-2 border-t border-[var(--color-border-subtle)]"
|
||||||
|
>
|
||||||
|
<Maximize2 size={12} /> Expand
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</BentoCard>
|
||||||
|
|
||||||
|
{/* Dice Roller — 1×1 */}
|
||||||
|
<BentoCard title="Dice Roller" icon={<Dice5 size={16} />} span="col-span-1 row-span-1">
|
||||||
|
<div className="flex flex-col h-full">
|
||||||
|
<div className="flex-1 overflow-hidden">
|
||||||
|
<DiceRoller />
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => onNavigate("dice")}
|
||||||
|
className="text-[var(--color-gold-bright)] hover:text-[var(--color-gold-muted)] text-xs flex items-center justify-center gap-1 cursor-pointer transition-colors mt-1 pt-2 border-t border-[var(--color-border-subtle)]"
|
||||||
|
>
|
||||||
|
<Maximize2 size={12} /> Expand
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</BentoCard>
|
||||||
|
|
||||||
|
{/* Encounter Builder — 1×1 */}
|
||||||
|
<BentoCard title="Encounter" icon={<Swords size={16} />} span="col-span-1 row-span-1">
|
||||||
|
<div className="flex flex-col h-full">
|
||||||
|
<div className="flex-1 overflow-hidden">
|
||||||
|
<EncounterBuilder />
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => onNavigate("encounter")}
|
||||||
|
className="text-[var(--color-gold-bright)] hover:text-[var(--color-gold-muted)] text-xs flex items-center justify-center gap-1 cursor-pointer transition-colors mt-1 pt-2 border-t border-[var(--color-border-subtle)]"
|
||||||
|
>
|
||||||
|
<Maximize2 size={12} /> Expand
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</BentoCard>
|
||||||
|
|
||||||
|
{/* Session Log — 2×1 */}
|
||||||
|
<BentoCard title="Session Log" icon={<ScrollText size={16} />} span="col-span-2 row-span-1">
|
||||||
|
<div className="flex flex-col h-full">
|
||||||
|
<div className="flex-1 overflow-hidden">
|
||||||
|
<SessionLogger />
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => onNavigate("session")}
|
||||||
|
className="text-[var(--color-gold-bright)] hover:text-[var(--color-gold-muted)] text-xs flex items-center justify-center gap-1 cursor-pointer transition-colors mt-1 pt-2 border-t border-[var(--color-border-subtle)]"
|
||||||
|
>
|
||||||
|
<Maximize2 size={12} /> Expand
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</BentoCard>
|
||||||
|
|
||||||
|
{/* Quest Designer — 2×1 */}
|
||||||
|
<BentoCard title="Quest Designer" icon={<Sparkles size={16} />} span="col-span-2 row-span-1">
|
||||||
|
<div className="flex flex-col items-center justify-center h-full text-[var(--color-text-dim)] text-sm gap-2">
|
||||||
|
<Sparkles size={32} className="opacity-30" />
|
||||||
|
<p>Quest flowchart coming soon</p>
|
||||||
|
<button
|
||||||
|
onClick={() => onNavigate("quest")}
|
||||||
|
className="text-[var(--color-gold-bright)] hover:text-[var(--color-gold-muted)] text-xs flex items-center gap-1 cursor-pointer transition-colors"
|
||||||
|
>
|
||||||
|
<Maximize2 size={12} /> Open
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</BentoCard>
|
||||||
|
|
||||||
|
{/* Initiative Tracker — 1×1 */}
|
||||||
|
<BentoCard title="Initiative" icon={<Timer size={16} />} span="col-span-1 row-span-1">
|
||||||
|
<div className="flex flex-col h-full">
|
||||||
|
<div className="flex-1 overflow-hidden">
|
||||||
|
<InitiativeTracker />
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => onNavigate("initiative")}
|
||||||
|
className="text-[var(--color-gold-bright)] hover:text-[var(--color-gold-muted)] text-xs flex items-center justify-center gap-1 cursor-pointer transition-colors mt-1 pt-2 border-t border-[var(--color-border-subtle)]"
|
||||||
|
>
|
||||||
|
<Maximize2 size={12} /> Expand
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</BentoCard>
|
||||||
|
|
||||||
|
{/* Random Tables — 1×1 */}
|
||||||
|
<BentoCard title="Random Tables" icon={<Sparkles size={16} />} span="col-span-1 row-span-1">
|
||||||
|
<div className="flex flex-col h-full">
|
||||||
|
<div className="flex-1 overflow-hidden">
|
||||||
|
<RandomTables />
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => onNavigate("tables")}
|
||||||
|
className="text-[var(--color-gold-bright)] hover:text-[var(--color-gold-muted)] text-xs flex items-center justify-center gap-1 cursor-pointer transition-colors mt-1 pt-2 border-t border-[var(--color-border-subtle)]"
|
||||||
|
>
|
||||||
|
<Maximize2 size={12} /> Expand
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</BentoCard>
|
||||||
|
|
||||||
|
{/* Calendar — 1×1 */}
|
||||||
|
<BentoCard title="Calendar" icon={<Calendar size={16} />} span="col-span-1 row-span-1">
|
||||||
|
<div className="flex flex-col h-full">
|
||||||
|
<div className="flex-1 overflow-hidden">
|
||||||
|
<CalendarWidget />
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => onNavigate("calendar")}
|
||||||
|
className="text-[var(--color-gold-bright)] hover:text-[var(--color-gold-muted)] text-xs flex items-center justify-center gap-1 cursor-pointer transition-colors mt-1 pt-2 border-t border-[var(--color-border-subtle)]"
|
||||||
|
>
|
||||||
|
<Maximize2 size={12} /> Expand
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</BentoCard>
|
||||||
|
|
||||||
|
{/* Soundboard — 1×1 */}
|
||||||
|
<BentoCard title="Soundboard" icon={<Volume2 size={16} />} span="col-span-1 row-span-1">
|
||||||
|
<div className="flex flex-col items-center justify-center h-full text-[var(--color-text-dim)] text-sm gap-2">
|
||||||
|
<Volume2 size={32} className="opacity-30" />
|
||||||
|
<p>Soundboard coming soon</p>
|
||||||
|
</div>
|
||||||
|
</BentoCard>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inline imports for dashboard cards (compact versions)
|
||||||
import { NpcGenerator } from "./NpcGenerator";
|
import { NpcGenerator } from "./NpcGenerator";
|
||||||
import { DiceRoller } from "./DiceRoller";
|
import { DiceRoller } from "./DiceRoller";
|
||||||
import { SessionLogger } from "./SessionLogger";
|
import { SessionLogger } from "./SessionLogger";
|
||||||
import { EncounterBuilder } from "./EncounterBuilder";
|
import { EncounterBuilder } from "./EncounterBuilder";
|
||||||
import { InitiativeTracker } from "./InitiativeTracker";
|
import { InitiativeTracker } from "./InitiativeTracker";
|
||||||
import { RandomTables } from "./RandomTables";
|
import { RandomTables } from "./RandomTables";
|
||||||
import { CalendarWidget } from "./CalendarWidget";
|
import { CalendarWidget } from "./CalendarWidget";
|
||||||
|
|
||||||
export function Dashboard() {
|
|
||||||
return (
|
|
||||||
<div className="grid grid-cols-4 grid-rows-4 gap-3 p-4 h-full bg-[var(--color-bg-deep)]">
|
|
||||||
{/* World Map — 2×2 */}
|
|
||||||
<BentoCard title="World Map" icon={<Map size={16} />} span="col-span-2 row-span-2">
|
|
||||||
<div className="flex items-center justify-center h-full text-[var(--color-text-dim)] text-sm">
|
|
||||||
<div className="text-center">
|
|
||||||
<Map size={48} className="mx-auto mb-2 opacity-30" />
|
|
||||||
<p>Map canvas coming soon</p>
|
|
||||||
<p className="text-xs mt-1">react-konva integration pending</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</BentoCard>
|
|
||||||
|
|
||||||
{/* NPC Sheet — 1×1 */}
|
|
||||||
<BentoCard title="NPC Sheet" icon={<User size={16} />} span="col-span-1 row-span-1">
|
|
||||||
<NpcGenerator />
|
|
||||||
</BentoCard>
|
|
||||||
|
|
||||||
{/* Dice Roller — 1×1 */}
|
|
||||||
<BentoCard title="Dice Roller" icon={<Dice5 size={16} />} span="col-span-1 row-span-1">
|
|
||||||
<DiceRoller />
|
|
||||||
</BentoCard>
|
|
||||||
|
|
||||||
{/* Encounter Builder — 1×1 */}
|
|
||||||
<BentoCard title="Encounter" icon={<Swords size={16} />} span="col-span-1 row-span-1">
|
|
||||||
<EncounterBuilder />
|
|
||||||
</BentoCard>
|
|
||||||
|
|
||||||
{/* Session Log — 2×1 */}
|
|
||||||
<BentoCard title="Session Log" icon={<ScrollText size={16} />} span="col-span-2 row-span-1">
|
|
||||||
<SessionLogger />
|
|
||||||
</BentoCard>
|
|
||||||
|
|
||||||
{/* Quest Designer — 2×1 */}
|
|
||||||
<BentoCard title="Quest Designer" icon={<Sparkles size={16} />} span="col-span-2 row-span-1">
|
|
||||||
<div className="flex items-center justify-center h-full text-[var(--color-text-dim)] text-sm">
|
|
||||||
Quest flowchart coming soon — react-flow integration pending
|
|
||||||
</div>
|
|
||||||
</BentoCard>
|
|
||||||
|
|
||||||
{/* Initiative Tracker — 1×1 */}
|
|
||||||
<BentoCard title="Initiative" icon={<Timer size={16} />} span="col-span-1 row-span-1">
|
|
||||||
<InitiativeTracker />
|
|
||||||
</BentoCard>
|
|
||||||
|
|
||||||
{/* Random Tables — 1×1 */}
|
|
||||||
<BentoCard title="Random Tables" icon={<Sparkles size={16} />} span="col-span-1 row-span-1">
|
|
||||||
<RandomTables />
|
|
||||||
</BentoCard>
|
|
||||||
|
|
||||||
{/* Calendar — 1×1 */}
|
|
||||||
<BentoCard title="Calendar" icon={<Calendar size={16} />} span="col-span-1 row-span-1">
|
|
||||||
<CalendarWidget />
|
|
||||||
</BentoCard>
|
|
||||||
|
|
||||||
{/* Soundboard — 1×1 */}
|
|
||||||
<BentoCard title="Soundboard" icon={<Volume2 size={16} />} span="col-span-1 row-span-1">
|
|
||||||
<div className="flex items-center justify-center h-full text-[var(--color-text-dim)] text-sm">
|
|
||||||
Soundboard coming soon
|
|
||||||
</div>
|
|
||||||
</BentoCard>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user