Start supporting edit forms for any document type.

This commit is contained in:
2025-06-13 11:14:58 -07:00
parent 38eee14253
commit 6845bd06bf
4 changed files with 63 additions and 26 deletions

View File

@@ -3,7 +3,7 @@ import { pb } from "@/lib/pocketbase";
import type { Document, RelationshipType } from "@/lib/types"; import type { Document, RelationshipType } from "@/lib/types";
import { useState } from "react"; import { useState } from "react";
import { Loader } from "./Loader"; import { Loader } from "./Loader";
import { DocumentForm } from "./documents/DocumentForm"; import { NewRelatedDocumentForm } from "./documents/NewRelatedDocumentForm";
import { DocumentRow } from "./documents/DocumentRow"; import { DocumentRow } from "./documents/DocumentRow";
interface RelationshipListProps { interface RelationshipListProps {
@@ -68,7 +68,7 @@ export function RelationshipList({
error={error} error={error}
renderRow={(document) => <DocumentRow document={document} />} renderRow={(document) => <DocumentRow document={document} />}
newItemForm={(onSubmit) => ( newItemForm={(onSubmit) => (
<DocumentForm <NewRelatedDocumentForm
campaignId={root.campaign} campaignId={root.campaign}
relationshipType={relationshipType} relationshipType={relationshipType}
onCreate={async (doc: Document) => { onCreate={async (doc: Document) => {

View File

@@ -0,0 +1,12 @@
import type { CampaignId, Document } from "@/lib/types";
export type FormTarget<T extends Document> =
| {
type: "new";
campaignId: CampaignId;
document?: undefined;
}
| {
type: "existing";
document: T;
};

View File

@@ -7,13 +7,13 @@ import { SecretForm } from "./secret/SecretForm";
import { TreasureForm } from "./treasure/TreasureForm"; import { TreasureForm } from "./treasure/TreasureForm";
function assertUnreachable(_x: never): never { function assertUnreachable(_x: never): never {
throw new Error("DocumentForm switch is not exhaustive"); throw new Error("NewRelatedDocumentForm switch is not exhaustive");
} }
/** /**
* Renders a form for any document type depending on the relationship. * Renders a form for any document type depending on the relationship.
*/ */
export const DocumentForm = ({ export const NewRelatedDocumentForm = ({
campaignId, campaignId,
relationshipType, relationshipType,
onCreate, onCreate,
@@ -24,19 +24,27 @@ export const DocumentForm = ({
}) => { }) => {
switch (relationshipType) { switch (relationshipType) {
case RelationshipType.Locations: case RelationshipType.Locations:
return <LocationForm campaign={campaignId} onCreate={onCreate} />; return (
<LocationForm
target={{
type: "new",
campaignId,
}}
onSubmit={onCreate}
/>
);
case RelationshipType.Monsters: case RelationshipType.Monsters:
return <MonsterForm campaign={campaignId} onCreate={onCreate} />; return <MonsterForm campaign={campaignId} onCreate={onCreate} />;
case RelationshipType.Npcs: case RelationshipType.Npcs:
return <NpcForm campaign={campaignId} onCreate={onCreate} />; return <NpcForm campaign={campaignId} onCreate={onCreate} />;
case RelationshipType.Secrets: case RelationshipType.Secrets:
return <SecretForm campaign={campaignId} onCreate={onCreate} />; return <SecretForm campaign={campaignId} onCreate={onCreate} />;
case RelationshipType.DiscoveredIn:
return "Form not supported here";
case RelationshipType.Treasures: case RelationshipType.Treasures:
return <TreasureForm campaign={campaignId} onCreate={onCreate} />; return <TreasureForm campaign={campaignId} onCreate={onCreate} />;
case RelationshipType.Scenes: case RelationshipType.Scenes:
return <SceneForm campaign={campaignId} onCreate={onCreate} />; return <SceneForm campaign={campaignId} onCreate={onCreate} />;
case RelationshipType.DiscoveredIn:
return "Form not supported here";
} }
return assertUnreachable(relationshipType); return assertUnreachable(relationshipType);

View File

@@ -1,19 +1,22 @@
import { useState } from "react"; import { useState } from "react";
import type { CampaignId, Location } from "@/lib/types"; import type { Location } from "@/lib/types";
import { pb } from "@/lib/pocketbase"; import { pb } from "@/lib/pocketbase";
import type { FormTarget } from "../Forms";
/** /**
* Renders a form to add a new location. Calls onCreate with the new location document. * Renders a form to add a new location. Calls onCreate with the new location document.
*/ */
export const LocationForm = ({ export const LocationForm = ({
campaign, target,
onCreate, onSubmit,
}: { }: {
campaign: CampaignId; target: FormTarget<Location>;
onCreate: (location: Location) => Promise<void>; onSubmit: (location: Location) => Promise<void>;
}) => { }) => {
const [name, setName] = useState(""); const [name, setName] = useState(target.document?.data.location.name ?? "");
const [description, setDescription] = useState(""); const [description, setDescription] = useState(
target.document?.data.location.description ?? "",
);
const [adding, setAdding] = useState(false); const [adding, setAdding] = useState(false);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
@@ -23,8 +26,9 @@ export const LocationForm = ({
setAdding(true); setAdding(true);
setError(null); setError(null);
try { try {
if (target.type === "new") {
const locationDoc: Location = await pb.collection("documents").create({ const locationDoc: Location = await pb.collection("documents").create({
campaign, campaign: target.campaignId,
data: { data: {
location: { location: {
name, name,
@@ -34,7 +38,20 @@ export const LocationForm = ({
}); });
setName(""); setName("");
setDescription(""); setDescription("");
await onCreate(locationDoc); await onSubmit(locationDoc);
} else {
const locationDoc: Location = await pb
.collection("documents")
.update(target.document.id, {
data: {
location: {
name,
description,
},
},
});
await onSubmit(locationDoc);
}
} catch (e: any) { } catch (e: any) {
setError(e?.message || "Failed to add location."); setError(e?.message || "Failed to add location.");
} finally { } finally {