Compare commits

...

3 Commits

14 changed files with 73 additions and 49 deletions

View File

@@ -63,7 +63,7 @@ export function AutoSaveTextarea({
<textarea
value={value}
onChange={handleChange}
className={`w-full min-h-[4rem] p-2 rounded border bg-slate-800 text-slate-100 border-slate-700 focus:outline-none focus:ring-2 focus:ring-violet-500 transition-colors ${flash ? "ring-2 ring-emerald-400 border-emerald-400 bg-emerald-950" : ""} ${className}`}
className={`w-full min-h-[6em] field-sizing-content p-2 rounded border bg-slate-800 text-slate-100 border-slate-700 focus:outline-none focus:ring-2 focus:ring-violet-500 transition-colors ${flash ? "ring-2 ring-emerald-400 border-emerald-400 bg-emerald-950" : ""} ${className}`}
{...props}
/>
) : (

View File

@@ -40,11 +40,11 @@ export function DocumentList<T extends Document>({
return (
<section className="w-full max-w-2xl mx-auto">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center justify-between my-4">
<h2 className="text-xl font-bold text-slate-100">{title}</h2>
<button
type="button"
className="inline-flex items-center justify-center rounded-full bg-violet-600 hover:bg-violet-700 text-white w-9 h-9 focus:outline-none focus:ring-2 focus:ring-violet-400"
className="inline-flex items-center justify-center rounded-full bg-violet-600 hover:bg-violet-700 text-white w-8 h-8 focus:outline-none focus:ring-2 focus:ring-violet-400"
aria-label="Add new item"
onClick={() => setOpen(true)}
>

View File

@@ -3,8 +3,9 @@ import { pb } from "@/lib/pocketbase";
import type { Document, RelationshipType } from "@/lib/types";
import { useState } from "react";
import { Loader } from "./Loader";
import { DocumentForm } from "./documents/DocumentForm";
import { NewRelatedDocumentForm } from "./documents/NewRelatedDocumentForm";
import { DocumentRow } from "./documents/DocumentRow";
import { displayName } from "@/lib/relationships";
interface RelationshipListProps {
root: Document;
@@ -61,14 +62,12 @@ export function RelationshipList({
return (
<DocumentList
title={
relationshipType.charAt(0).toUpperCase() + relationshipType.slice(1)
}
title={displayName(relationshipType)}
items={items}
error={error}
renderRow={(document) => <DocumentRow document={document} />}
newItemForm={(onSubmit) => (
<DocumentForm
<NewRelatedDocumentForm
campaignId={root.campaign}
relationshipType={relationshipType}
onCreate={async (doc: Document) => {

View File

@@ -1,10 +1,10 @@
import { RelationshipType, type CampaignId, type Document } from "@/lib/types";
import { LocationForm } from "./location/LocationForm";
import { MonsterForm } from "./monsters/MonsterForm";
import { NpcForm } from "./npc/NpcForm";
import { SceneForm } from "./scene/SceneForm";
import { SecretForm } from "./secret/SecretForm";
import { TreasureForm } from "./treasure/TreasureForm";
import { NewLocationForm } from "./location/NewLocationForm";
import { NewMonsterForm } from "./monsters/NewMonsterForm";
import { NewNpcForm } from "./npc/NewNpcForm";
import { NewSceneForm } from "./scene/NewSceneForm";
import { NewSecretForm } from "./secret/NewSecretForm";
import { NewTreasureForm } from "./treasure/NewTreasureForm";
function assertUnreachable(_x: never): never {
throw new Error("DocumentForm switch is not exhaustive");
@@ -13,7 +13,7 @@ function assertUnreachable(_x: never): never {
/**
* Renders a form for any document type depending on the relationship.
*/
export const DocumentForm = ({
export const NewRelatedDocumentForm = ({
campaignId,
relationshipType,
onCreate,
@@ -24,19 +24,19 @@ export const DocumentForm = ({
}) => {
switch (relationshipType) {
case RelationshipType.Locations:
return <LocationForm campaign={campaignId} onCreate={onCreate} />;
return <NewLocationForm campaign={campaignId} onCreate={onCreate} />;
case RelationshipType.Monsters:
return <MonsterForm campaign={campaignId} onCreate={onCreate} />;
return <NewMonsterForm campaign={campaignId} onCreate={onCreate} />;
case RelationshipType.Npcs:
return <NpcForm campaign={campaignId} onCreate={onCreate} />;
return <NewNpcForm campaign={campaignId} onCreate={onCreate} />;
case RelationshipType.Secrets:
return <SecretForm campaign={campaignId} onCreate={onCreate} />;
return <NewSecretForm campaign={campaignId} onCreate={onCreate} />;
case RelationshipType.Treasures:
return <NewTreasureForm campaign={campaignId} onCreate={onCreate} />;
case RelationshipType.Scenes:
return <NewSceneForm campaign={campaignId} onCreate={onCreate} />;
case RelationshipType.DiscoveredIn:
return "Form not supported here";
case RelationshipType.Treasures:
return <TreasureForm campaign={campaignId} onCreate={onCreate} />;
case RelationshipType.Scenes:
return <SceneForm campaign={campaignId} onCreate={onCreate} />;
}
return assertUnreachable(relationshipType);

View File

@@ -5,7 +5,7 @@ import { pb } from "@/lib/pocketbase";
/**
* Renders a form to add a new location. Calls onCreate with the new location document.
*/
export const LocationForm = ({
export const NewLocationForm = ({
campaign,
onCreate,
}: {

View File

@@ -5,7 +5,7 @@ import { pb } from "@/lib/pocketbase";
/**
* Renders a form to add a new monster. Calls onCreate with the new monster document.
*/
export const MonsterForm = ({
export const NewMonsterForm = ({
campaign,
onCreate,
}: {

View File

@@ -5,7 +5,7 @@ import { pb } from "@/lib/pocketbase";
/**
* Renders a form to add a new npc. Calls onCreate with the new npc document.
*/
export const NpcForm = ({
export const NewNpcForm = ({
campaign,
onCreate,
}: {

View File

@@ -7,7 +7,7 @@ import { pb } from "@/lib/pocketbase";
/**
* Renders a form to add a new scene. Calls onCreate with the new scene document.
*/
export const SceneForm = ({
export const NewSceneForm = ({
campaign,
onCreate,
}: {

View File

@@ -7,7 +7,7 @@ import { pb } from "@/lib/pocketbase";
/**
* Renders a form to add a new secret. Calls onCreate with the new secret document.
*/
export const SecretForm = ({
export const NewSecretForm = ({
campaign,
onCreate,
}: {

View File

@@ -1,7 +1,7 @@
import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
import type { Session } from "@/lib/types";
export const SessionForm = ({
export const EditSessionForm = ({
session,
onSubmit,
}: {

View File

@@ -7,7 +7,7 @@ import { pb } from "@/lib/pocketbase";
/**
* Renders a form to add a new treasure. Calls onCreate with the new treasure document.
*/
export const TreasureForm = ({
export const NewTreasureForm = ({
campaign,
onCreate,
}: {

5
src/lib/relationships.ts Normal file
View File

@@ -0,0 +1,5 @@
import type { RelationshipType } from "./types";
export function displayName(relationshipType: RelationshipType) {
return relationshipType.charAt(0).toUpperCase() + relationshipType.slice(1);
}

View File

@@ -11,7 +11,7 @@ export const Route = createFileRoute("/_app")({
function AppHeader() {
const { user, logout, isLoading } = useAuth();
return (
<header className="flex items-center justify-between px-8 py-4 border-b border-slate-700 bg-slate-900">
<header className="flex flex-wrap items-center justify-between px-8 py-4 border-b border-slate-700 bg-slate-900">
<h1 className="text-2xl font-bold text-slate-100 m-0">
DM's Table Companion
</h1>

View File

@@ -8,7 +8,9 @@ import {
type Document,
} from "@/lib/types";
import { RelationshipList } from "@/components/RelationshipList";
import { SessionForm } from "@/components/documents/session/SessionForm";
import { EditSessionForm } from "@/components/documents/session/EditSessionForm";
import { displayName } from "@/lib/relationships";
import { TabGroup, TabList, Tab, TabPanels, TabPanel } from "@headlessui/react";
export const Route = createFileRoute(
"/_app/_authenticated/document/$documentId",
@@ -47,8 +49,17 @@ function RouteComponent() {
console.log("Parsed data: ", relationships);
const relationshipList = [
RelationshipType.Scenes,
RelationshipType.Secrets,
RelationshipType.Locations,
RelationshipType.Npcs,
RelationshipType.Monsters,
RelationshipType.Treasures,
];
return (
<div className="max-w-xl mx-auto py-8">
<div className="max-w-xl mx-auto py-2 px-4">
<Link
to="/document/$documentId/print"
params={{ documentId: session.id }}
@@ -56,22 +67,31 @@ function RouteComponent() {
>
Print
</Link>
<SessionForm session={session as Session} onSubmit={handleSaveSession} />
{[
RelationshipType.Scenes,
RelationshipType.Secrets,
RelationshipType.Locations,
RelationshipType.Npcs,
RelationshipType.Monsters,
RelationshipType.Treasures,
].map((relationshipType) => (
<RelationshipList
key={relationshipType}
root={session}
relationshipType={relationshipType}
items={relationships[relationshipType] ?? []}
/>
))}
<EditSessionForm
session={session as Session}
onSubmit={handleSaveSession}
/>
<TabGroup>
<TabList className="flex flex-row flex-wrap gap-1 mt-2">
{relationshipList.map((relationshipType) => (
<Tab className="px-3 py-2 rounded bg-slate-800 text-slate-100 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-violet-500 data-selected:bg-violet-900 data-selected:border-violet-700">
{displayName(relationshipType)}
</Tab>
))}
</TabList>
<TabPanels>
{relationshipList.map((relationshipType) => (
<TabPanel>
<RelationshipList
key={relationshipType}
root={session}
relationshipType={relationshipType}
items={relationships[relationshipType] ?? []}
/>
</TabPanel>
))}
</TabPanels>
</TabGroup>
</div>
);
}