Adds tabs to the campaign page, but they don't do much yet
This commit is contained in:
@@ -39,6 +39,7 @@ export type TabProps = {
|
|||||||
label: string;
|
label: string;
|
||||||
to: string;
|
to: string;
|
||||||
params: Record<string, any>;
|
params: Record<string, any>;
|
||||||
|
search: Record<string, any>;
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -46,12 +47,13 @@ const activeTabClass =
|
|||||||
"text-slate-100 font-bold bg-slate-800 border-t border-b border-l";
|
"text-slate-100 font-bold bg-slate-800 border-t border-b border-l";
|
||||||
const inactiveTabClass = "text-slate-300 bg-slate-900 border";
|
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 (
|
return (
|
||||||
<Link
|
<Link
|
||||||
key={label}
|
key={label}
|
||||||
to={to}
|
to={to}
|
||||||
params={params}
|
params={params}
|
||||||
|
search={search}
|
||||||
className={`block p-2 border-slate-700 whitespace-nowrap ${active ? activeTabClass : inactiveTabClass}`}
|
className={`block p-2 border-slate-700 whitespace-nowrap ${active ? activeTabClass : inactiveTabClass}`}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
|
|||||||
@@ -1,21 +1,41 @@
|
|||||||
import { useCallback, useEffect, useState } from "react";
|
import { DocumentRow } from "@/components/documents/DocumentRow";
|
||||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
|
||||||
import { pb } from "@/lib/pocketbase";
|
|
||||||
import { SessionRow } from "@/components/documents/session/SessionRow";
|
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 { 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(
|
export const Route = createFileRoute(
|
||||||
"/_app/_authenticated/campaigns/$campaignId",
|
"/_app/_authenticated/campaigns/$campaignId",
|
||||||
)({
|
)({
|
||||||
component: RouteComponent,
|
component: RouteComponent,
|
||||||
pendingComponent: Loader,
|
pendingComponent: Loader,
|
||||||
|
validateSearch: (s) => campaignSearchSchema.parse(s),
|
||||||
});
|
});
|
||||||
|
|
||||||
function RouteComponent() {
|
function RouteComponent() {
|
||||||
const params = Route.useParams();
|
const params = Route.useParams();
|
||||||
|
const { tab, docId } = Route.useSearch();
|
||||||
|
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [campaign, setCampaign] = useState<Campaign | null>(null);
|
const [campaign, setCampaign] = useState<Campaign | null>(null);
|
||||||
@@ -93,16 +113,19 @@ function RouteComponent() {
|
|||||||
← Back to campaigns
|
← Back to campaigns
|
||||||
</Link>
|
</Link>
|
||||||
}
|
}
|
||||||
tabs={[
|
tabs={Object.entries(CampaignTabs).map(([key, label]) => (
|
||||||
<Tab
|
<Tab
|
||||||
label="sessions"
|
label={label}
|
||||||
active
|
active={tab === key}
|
||||||
to="/campaigns/$campaignId"
|
to={Route.to}
|
||||||
params={{
|
params={{
|
||||||
campaignId: campaign.id,
|
campaignId: campaign.id,
|
||||||
}}
|
}}
|
||||||
/>,
|
search={{
|
||||||
]}
|
tab: key,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
content={
|
content={
|
||||||
<div>
|
<div>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
@@ -135,6 +158,19 @@ function RouteComponent() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
flyout={docId && <Flyout key={docId} docId={docId} />}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
function Flyout({ docId }: { docId: DocumentId }) {
|
||||||
|
const { docResult } = useDocument(docId);
|
||||||
|
|
||||||
|
if (docResult?.type !== "ready") {
|
||||||
|
return <Loader />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const doc = docResult.value.doc;
|
||||||
|
|
||||||
|
// TODO: Document preview
|
||||||
|
return <DocumentRow document={doc} root={doc} />;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user