diff --git a/src/components/layout/TabbedLayout.tsx b/src/components/layout/TabbedLayout.tsx index 3870ed3..48fd89d 100644 --- a/src/components/layout/TabbedLayout.tsx +++ b/src/components/layout/TabbedLayout.tsx @@ -39,6 +39,7 @@ export type TabProps = { label: string; to: string; params: Record; + search: Record; active?: boolean; }; @@ -46,12 +47,13 @@ const activeTabClass = "text-slate-100 font-bold bg-slate-800 border-t border-b border-l"; const inactiveTabClass = "text-slate-300 bg-slate-900 border"; -export function Tab({ label, to, params, active }: TabProps) { +export function Tab({ label, to, params, active, search }: TabProps) { return ( {label} diff --git a/src/routes/_app/_authenticated/campaigns.$campaignId.tsx b/src/routes/_app/_authenticated/campaigns.$campaignId.tsx index 21f9312..21e9f61 100644 --- a/src/routes/_app/_authenticated/campaigns.$campaignId.tsx +++ b/src/routes/_app/_authenticated/campaigns.$campaignId.tsx @@ -1,21 +1,41 @@ -import { useCallback, useEffect, useState } from "react"; -import { createFileRoute, Link } from "@tanstack/react-router"; -import { pb } from "@/lib/pocketbase"; +import { DocumentRow } from "@/components/documents/DocumentRow"; import { SessionRow } from "@/components/documents/session/SessionRow"; -import { Button } from "@headlessui/react"; -import { Loader } from "@/components/Loader"; -import type { Campaign, Relationship, Session } from "@/lib/types"; import { Tab, TabbedLayout } from "@/components/layout/TabbedLayout"; +import { Loader } from "@/components/Loader"; +import { useDocument } from "@/context/document/hooks"; +import { pb } from "@/lib/pocketbase"; +import type { Campaign, DocumentId, Relationship, Session } from "@/lib/types"; +import { Button } from "@headlessui/react"; +import { createFileRoute, Link } from "@tanstack/react-router"; +import { useCallback, useEffect, useState } from "react"; +import { z } from "zod"; + +const CampaignTabs = { + sessions: "Sessions", + npcs: "NPCs", + locations: "Locations", + factions: "Factions", + threads: "Threads", +} as const; + +const campaignSearchSchema = z.object({ + tab: z + .enum(Object.keys(CampaignTabs) as (keyof typeof CampaignTabs)[]) + .default("sessions"), + docId: z.optional(z.string().transform((s) => s as DocumentId)), +}); export const Route = createFileRoute( "/_app/_authenticated/campaigns/$campaignId", )({ component: RouteComponent, pendingComponent: Loader, + validateSearch: (s) => campaignSearchSchema.parse(s), }); function RouteComponent() { const params = Route.useParams(); + const { tab, docId } = Route.useSearch(); const [loading, setLoading] = useState(true); const [campaign, setCampaign] = useState(null); @@ -93,16 +113,19 @@ function RouteComponent() { ← Back to campaigns } - tabs={[ + tabs={Object.entries(CampaignTabs).map(([key, label]) => ( , - ]} + search={{ + tab: key, + }} + /> + ))} content={
@@ -135,6 +158,19 @@ function RouteComponent() { )}
} + flyout={docId && } /> ); } +function Flyout({ docId }: { docId: DocumentId }) { + const { docResult } = useDocument(docId); + + if (docResult?.type !== "ready") { + return ; + } + + const doc = docResult.value.doc; + + // TODO: Document preview + return ; +}