Uses generic forms everywhere, gets rid of most doc-specific stuff
This commit is contained in:
@@ -1,33 +0,0 @@
|
|||||||
import { type AnyDocument } from "@/lib/types";
|
|
||||||
import { LocationEditForm } from "./location/LocationEditForm";
|
|
||||||
import { MonsterEditForm } from "./monsters/MonsterEditForm";
|
|
||||||
import { NpcEditForm } from "./npc/NpcEditForm";
|
|
||||||
import { SceneEditForm } from "./scene/SceneEditForm";
|
|
||||||
import { SecretEditForm } from "./secret/SecretEditForm";
|
|
||||||
import { SessionEditForm } from "./session/SessionEditForm";
|
|
||||||
import { TreasureEditForm } from "./treasure/TreasureEditForm";
|
|
||||||
import { GenericEditForm } from "./GenericEditForm";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a form for any document type depending on the relationship.
|
|
||||||
*/
|
|
||||||
export const DocumentEditForm = ({ document }: { document: AnyDocument }) => {
|
|
||||||
switch (document.type) {
|
|
||||||
case "location":
|
|
||||||
return <LocationEditForm location={document} />;
|
|
||||||
case "monster":
|
|
||||||
return <MonsterEditForm monster={document} />;
|
|
||||||
case "npc":
|
|
||||||
return <NpcEditForm npc={document} />;
|
|
||||||
case "scene":
|
|
||||||
return <SceneEditForm scene={document} />;
|
|
||||||
case "secret":
|
|
||||||
return <SecretEditForm secret={document} />;
|
|
||||||
case "session":
|
|
||||||
return <SessionEditForm session={document} />;
|
|
||||||
case "treasure":
|
|
||||||
return <TreasureEditForm treasure={document} />;
|
|
||||||
case "thread":
|
|
||||||
return <GenericEditForm doc={document} />;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -5,7 +5,7 @@ import { type AnyDocument } from "@/lib/types";
|
|||||||
import { Link } from "@tanstack/react-router";
|
import { Link } from "@tanstack/react-router";
|
||||||
import { Editing, EditToggle, NotEditing } from "../EditToggle";
|
import { Editing, EditToggle, NotEditing } from "../EditToggle";
|
||||||
import { BasicPreview } from "./BasicPreview";
|
import { BasicPreview } from "./BasicPreview";
|
||||||
import { DocumentEditForm } from "./DocumentEditForm";
|
import { GenericEditForm } from "./GenericEditForm";
|
||||||
|
|
||||||
export const DocumentPreview = ({ doc }: { doc: AnyDocument }) => {
|
export const DocumentPreview = ({ doc }: { doc: AnyDocument }) => {
|
||||||
const relationships = relationshipsForDocument(doc);
|
const relationships = relationshipsForDocument(doc);
|
||||||
@@ -13,7 +13,7 @@ export const DocumentPreview = ({ doc }: { doc: AnyDocument }) => {
|
|||||||
<div>
|
<div>
|
||||||
<EditToggle>
|
<EditToggle>
|
||||||
<Editing>
|
<Editing>
|
||||||
<DocumentEditForm document={doc} />
|
<GenericEditForm doc={doc} />
|
||||||
</Editing>
|
</Editing>
|
||||||
<NotEditing>
|
<NotEditing>
|
||||||
<ShowDocument doc={doc} />
|
<ShowDocument doc={doc} />
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
// DocumentRow.tsx
|
|
||||||
// Generic row component for displaying any document type.
|
|
||||||
import { type AnyDocument } from "@/lib/types";
|
|
||||||
import { LocationPrintRow } from "./location/LocationPrintRow";
|
|
||||||
import { MonsterPrintRow } from "./monsters/MonsterPrintRow";
|
|
||||||
import { NpcPrintRow } from "./npc/NpcPrintRow";
|
|
||||||
import { ScenePrintRow } from "./scene/ScenePrintRow";
|
|
||||||
import { SecretPrintRow } from "./secret/SecretPrintRow";
|
|
||||||
import { SessionPrintRow } from "./session/SessionPrintRow";
|
|
||||||
import { TreasurePrintRow } from "./treasure/TreasurePrintRow";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a row for any document type. Prioritizes Session, then Secret, then falls back to ID and creation time.
|
|
||||||
* If rendering a SecretRow, uses the provided session prop if available.
|
|
||||||
*/
|
|
||||||
export const DocumentPrintRow = ({ document }: { document: AnyDocument }) => {
|
|
||||||
switch (document.type) {
|
|
||||||
case "location":
|
|
||||||
return <LocationPrintRow location={document} />;
|
|
||||||
case "monster":
|
|
||||||
return <MonsterPrintRow monster={document} />;
|
|
||||||
case "npc":
|
|
||||||
return <NpcPrintRow npc={document} />;
|
|
||||||
case "scene":
|
|
||||||
return <ScenePrintRow scene={document} />;
|
|
||||||
case "secret":
|
|
||||||
return <SecretPrintRow secret={document} />;
|
|
||||||
case "session":
|
|
||||||
return <SessionPrintRow session={document} />;
|
|
||||||
case "treasure":
|
|
||||||
return <TreasurePrintRow treasure={document} />;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -3,12 +3,12 @@ import { displayName, relationshipsForDocument } from "@/lib/relationships";
|
|||||||
import { RelationshipType, type DocumentId } from "@/lib/types";
|
import { RelationshipType, type DocumentId } from "@/lib/types";
|
||||||
import { Link } from "@tanstack/react-router";
|
import { Link } from "@tanstack/react-router";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { Loader } from "../Loader";
|
|
||||||
import { DocumentTitle } from "./DocumentTitle";
|
|
||||||
import { Tab, TabbedLayout } from "../layout/TabbedLayout";
|
import { Tab, TabbedLayout } from "../layout/TabbedLayout";
|
||||||
import { DocumentEditForm } from "./DocumentEditForm";
|
import { Loader } from "../Loader";
|
||||||
import { RelatedDocumentList } from "./RelatedDocumentList";
|
|
||||||
import { DocumentPreview } from "./DocumentPreview";
|
import { DocumentPreview } from "./DocumentPreview";
|
||||||
|
import { DocumentTitle } from "./DocumentTitle";
|
||||||
|
import { GenericEditForm } from "./GenericEditForm";
|
||||||
|
import { RelatedDocumentList } from "./RelatedDocumentList";
|
||||||
|
|
||||||
export function DocumentView({
|
export function DocumentView({
|
||||||
documentId,
|
documentId,
|
||||||
@@ -83,7 +83,7 @@ export function DocumentView({
|
|||||||
]}
|
]}
|
||||||
content={
|
content={
|
||||||
relationshipType === null ? (
|
relationshipType === null ? (
|
||||||
<DocumentEditForm document={doc} />
|
<GenericEditForm doc={doc} />
|
||||||
) : (
|
) : (
|
||||||
<RelatedDocumentList
|
<RelatedDocumentList
|
||||||
documentId={doc.id}
|
documentId={doc.id}
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ export const NewCampaignDocumentForm = ({
|
|||||||
switch (docType) {
|
switch (docType) {
|
||||||
case "session":
|
case "session":
|
||||||
return <NewSessionForm campaignId={campaignId} onCreate={onCreate} />;
|
return <NewSessionForm campaignId={campaignId} onCreate={onCreate} />;
|
||||||
case "thread":
|
default:
|
||||||
case "location":
|
|
||||||
return (
|
return (
|
||||||
<GenericNewDocumentForm
|
<GenericNewDocumentForm
|
||||||
docType={docType}
|
docType={docType}
|
||||||
@@ -30,9 +29,5 @@ export const NewCampaignDocumentForm = ({
|
|||||||
onCreate={onCreate}
|
onCreate={onCreate}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
default:
|
|
||||||
throw new Error(
|
|
||||||
`Rendered NewCampaignDocumentForm with unsupported docType: ${docType}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,12 +3,8 @@ import {
|
|||||||
type CampaignId,
|
type CampaignId,
|
||||||
type AnyDocument,
|
type AnyDocument,
|
||||||
} from "@/lib/types";
|
} from "@/lib/types";
|
||||||
import { NewLocationForm } from "./location/NewLocationForm";
|
import { GenericNewDocumentForm } from "./GenericNewDocumentForm";
|
||||||
import { NewMonsterForm } from "./monsters/NewMonsterForm";
|
import { docTypeForRelationshipType } from "@/lib/relationships";
|
||||||
import { NewNpcForm } from "./npc/NewNpcForm";
|
|
||||||
import { NewSceneForm } from "./scene/NewSceneForm";
|
|
||||||
import { NewSecretForm } from "./secret/NewSecretForm";
|
|
||||||
import { NewTreasureForm } from "./treasure/NewTreasureForm";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders a form for any document type depending on the relationship.
|
* Renders a form for any document type depending on the relationship.
|
||||||
@@ -22,20 +18,11 @@ export const NewRelatedDocumentForm = ({
|
|||||||
relationshipType: RelationshipType;
|
relationshipType: RelationshipType;
|
||||||
onCreate: (doc: AnyDocument) => Promise<void>;
|
onCreate: (doc: AnyDocument) => Promise<void>;
|
||||||
}) => {
|
}) => {
|
||||||
switch (relationshipType) {
|
return (
|
||||||
case RelationshipType.Locations:
|
<GenericNewDocumentForm
|
||||||
return <NewLocationForm campaign={campaignId} onCreate={onCreate} />;
|
docType={docTypeForRelationshipType(relationshipType)}
|
||||||
case RelationshipType.Monsters:
|
campaignId={campaignId}
|
||||||
return <NewMonsterForm campaign={campaignId} onCreate={onCreate} />;
|
onCreate={onCreate}
|
||||||
case RelationshipType.Npcs:
|
/>
|
||||||
return <NewNpcForm campaign={campaignId} onCreate={onCreate} />;
|
);
|
||||||
case RelationshipType.Secrets:
|
|
||||||
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";
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
|
|
||||||
import { pb } from "@/lib/pocketbase";
|
|
||||||
import type { Location } from "@/lib/types";
|
|
||||||
import { useDocumentCache } from "@/context/document/hooks";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders an editable location form
|
|
||||||
*/
|
|
||||||
export const LocationEditForm = ({ location }: { location: Location }) => {
|
|
||||||
const { dispatch } = useDocumentCache();
|
|
||||||
async function saveLocationName(name: string) {
|
|
||||||
const updated: Location = await pb
|
|
||||||
.collection("documents")
|
|
||||||
.update(location.id, {
|
|
||||||
data: {
|
|
||||||
...location.data,
|
|
||||||
name,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
dispatch({ type: "setDocument", doc: updated });
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveLocationDescription(description: string) {
|
|
||||||
const updated: Location = await pb
|
|
||||||
.collection("documents")
|
|
||||||
.update(location.id, {
|
|
||||||
data: {
|
|
||||||
...location.data,
|
|
||||||
description,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
dispatch({ type: "setDocument", doc: updated });
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="">
|
|
||||||
<AutoSaveTextarea
|
|
||||||
multiline={false}
|
|
||||||
value={location.data.name}
|
|
||||||
onSave={saveLocationName}
|
|
||||||
/>
|
|
||||||
<AutoSaveTextarea
|
|
||||||
value={location.data.description}
|
|
||||||
onSave={saveLocationDescription}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import type { Location } from "@/lib/types";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders an print-friendly location row
|
|
||||||
*/
|
|
||||||
export const LocationPrintRow = ({ location }: { location: Location }) => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h4>{location.data.name}</h4>
|
|
||||||
<p>{location.data.description}</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
import { useState } from "react";
|
|
||||||
import type { CampaignId, Location } from "@/lib/types";
|
|
||||||
import { pb } from "@/lib/pocketbase";
|
|
||||||
import { BaseForm } from "@/components/form/BaseForm";
|
|
||||||
import { MultiLineInput } from "@/components/form/MultiLineInput";
|
|
||||||
import { SingleLineInput } from "@/components/form/SingleLineInput";
|
|
||||||
import { useDocumentCache } from "@/context/document/hooks";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a form to add a new location. Calls onCreate with the new location document.
|
|
||||||
*/
|
|
||||||
export const NewLocationForm = ({
|
|
||||||
campaign,
|
|
||||||
onCreate,
|
|
||||||
}: {
|
|
||||||
campaign: CampaignId;
|
|
||||||
onCreate: (location: Location) => Promise<void>;
|
|
||||||
}) => {
|
|
||||||
const { dispatch } = useDocumentCache();
|
|
||||||
const [name, setName] = useState("");
|
|
||||||
const [description, setDescription] = useState("");
|
|
||||||
const [adding, setAdding] = useState(false);
|
|
||||||
const [error, setError] = useState<string | null>(null);
|
|
||||||
|
|
||||||
async function handleSubmit(e: React.FormEvent) {
|
|
||||||
e.preventDefault();
|
|
||||||
if (!name.trim()) return;
|
|
||||||
setAdding(true);
|
|
||||||
setError(null);
|
|
||||||
try {
|
|
||||||
const locationDoc: Location = await pb.collection("documents").create({
|
|
||||||
campaign,
|
|
||||||
type: "location",
|
|
||||||
data: {
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
setName("");
|
|
||||||
setDescription("");
|
|
||||||
dispatch({ type: "setDocument", doc: locationDoc });
|
|
||||||
await onCreate(locationDoc);
|
|
||||||
} catch (e: any) {
|
|
||||||
setError(e?.message || "Failed to add location.");
|
|
||||||
} finally {
|
|
||||||
setAdding(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseForm
|
|
||||||
title="Create new Location"
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
isLoading={adding || !name.trim()}
|
|
||||||
error={error}
|
|
||||||
content={
|
|
||||||
<>
|
|
||||||
<SingleLineInput
|
|
||||||
label="Name"
|
|
||||||
value={name}
|
|
||||||
onChange={setName}
|
|
||||||
disabled={adding}
|
|
||||||
placeholder="Enter location name"
|
|
||||||
/>
|
|
||||||
<MultiLineInput
|
|
||||||
label="Description"
|
|
||||||
value={description}
|
|
||||||
placeholder="Enter location description"
|
|
||||||
onChange={setDescription}
|
|
||||||
disabled={adding}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
|
|
||||||
import { pb } from "@/lib/pocketbase";
|
|
||||||
import type { Monster } from "@/lib/types";
|
|
||||||
import { useDocumentCache } from "@/context/document/hooks";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders an editable monster row
|
|
||||||
*/
|
|
||||||
export const MonsterEditForm = ({ monster }: { monster: Monster }) => {
|
|
||||||
const { dispatch } = useDocumentCache();
|
|
||||||
async function saveMonsterName(name: string) {
|
|
||||||
const updated: Monster = await pb
|
|
||||||
.collection("documents")
|
|
||||||
.update(monster.id, {
|
|
||||||
data: {
|
|
||||||
...monster.data,
|
|
||||||
name,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
dispatch({ type: "setDocument", doc: updated });
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="">
|
|
||||||
<AutoSaveTextarea
|
|
||||||
multiline={false}
|
|
||||||
value={monster.data.name}
|
|
||||||
onSave={saveMonsterName}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import type { Monster } from "@/lib/types";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders an editable monster row
|
|
||||||
*/
|
|
||||||
export const MonsterPrintRow = ({ monster }: { monster: Monster }) => {
|
|
||||||
return <div>{monster.data.name}</div>;
|
|
||||||
};
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
import { useState } from "react";
|
|
||||||
import type { CampaignId, Monster } from "@/lib/types";
|
|
||||||
import { pb } from "@/lib/pocketbase";
|
|
||||||
import { BaseForm } from "@/components/form/BaseForm";
|
|
||||||
import { SingleLineInput } from "@/components/form/SingleLineInput";
|
|
||||||
import { useDocumentCache } from "@/context/document/hooks";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a form to add a new monster. Calls onCreate with the new monster document.
|
|
||||||
*/
|
|
||||||
export const NewMonsterForm = ({
|
|
||||||
campaign,
|
|
||||||
onCreate,
|
|
||||||
}: {
|
|
||||||
campaign: CampaignId;
|
|
||||||
onCreate: (monster: Monster) => Promise<void>;
|
|
||||||
}) => {
|
|
||||||
const { dispatch } = useDocumentCache();
|
|
||||||
const [name, setName] = useState("");
|
|
||||||
const [adding, setAdding] = useState(false);
|
|
||||||
const [error, setError] = useState<string | null>(null);
|
|
||||||
|
|
||||||
async function handleSubmit(e: React.FormEvent) {
|
|
||||||
e.preventDefault();
|
|
||||||
if (!name.trim()) return;
|
|
||||||
setAdding(true);
|
|
||||||
setError(null);
|
|
||||||
try {
|
|
||||||
const monsterDoc: Monster = await pb.collection("documents").create({
|
|
||||||
campaign,
|
|
||||||
type: "monster",
|
|
||||||
data: {
|
|
||||||
name,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
setName("");
|
|
||||||
dispatch({ type: "setDocument", doc: monsterDoc });
|
|
||||||
await onCreate(monsterDoc);
|
|
||||||
} catch (e: any) {
|
|
||||||
setError(e?.message || "Failed to add monster.");
|
|
||||||
} finally {
|
|
||||||
setAdding(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseForm
|
|
||||||
title="Create new monster"
|
|
||||||
isLoading={adding || !name.trim()}
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
error={error}
|
|
||||||
content={
|
|
||||||
<SingleLineInput
|
|
||||||
value={name}
|
|
||||||
onChange={setName}
|
|
||||||
placeholder="Monster description"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
import { useState } from "react";
|
|
||||||
import type { CampaignId, Npc } from "@/lib/types";
|
|
||||||
import { pb } from "@/lib/pocketbase";
|
|
||||||
import { BaseForm } from "@/components/form/BaseForm";
|
|
||||||
import { SingleLineInput } from "@/components/form/SingleLineInput";
|
|
||||||
import { MultiLineInput } from "@/components/form/MultiLineInput";
|
|
||||||
import { useDocumentCache } from "@/context/document/hooks";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a form to add a new npc. Calls onCreate with the new npc document.
|
|
||||||
*/
|
|
||||||
export const NewNpcForm = ({
|
|
||||||
campaign,
|
|
||||||
onCreate,
|
|
||||||
}: {
|
|
||||||
campaign: CampaignId;
|
|
||||||
onCreate: (npc: Npc) => Promise<void>;
|
|
||||||
}) => {
|
|
||||||
const { dispatch } = useDocumentCache();
|
|
||||||
const [name, setName] = useState("");
|
|
||||||
const [description, setDescription] = useState("");
|
|
||||||
const [adding, setAdding] = useState(false);
|
|
||||||
const [error, setError] = useState<string | null>(null);
|
|
||||||
|
|
||||||
async function handleSubmit(e: React.FormEvent) {
|
|
||||||
e.preventDefault();
|
|
||||||
if (!name.trim()) return;
|
|
||||||
setAdding(true);
|
|
||||||
setError(null);
|
|
||||||
try {
|
|
||||||
const npcDoc: Npc = await pb.collection("documents").create({
|
|
||||||
campaign,
|
|
||||||
type: "npc",
|
|
||||||
data: {
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
setName("");
|
|
||||||
setDescription("");
|
|
||||||
dispatch({ type: "setDocument", doc: npcDoc });
|
|
||||||
await onCreate(npcDoc);
|
|
||||||
} catch (e: any) {
|
|
||||||
setError(e?.message || "Failed to add npc.");
|
|
||||||
} finally {
|
|
||||||
setAdding(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseForm
|
|
||||||
title="Create new NPC"
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
isLoading={adding}
|
|
||||||
error={error}
|
|
||||||
content={
|
|
||||||
<>
|
|
||||||
<SingleLineInput
|
|
||||||
label="Name"
|
|
||||||
value={name}
|
|
||||||
onChange={setName}
|
|
||||||
disabled={adding}
|
|
||||||
placeholder="Enter NPC name"
|
|
||||||
/>
|
|
||||||
<MultiLineInput
|
|
||||||
label="Description"
|
|
||||||
value={description}
|
|
||||||
placeholder="Enter NPC description"
|
|
||||||
onChange={setDescription}
|
|
||||||
disabled={adding}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
|
|
||||||
import { useDocumentCache } from "@/context/document/hooks";
|
|
||||||
import { pb } from "@/lib/pocketbase";
|
|
||||||
import type { Npc } from "@/lib/types";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders an editable npc form
|
|
||||||
*/
|
|
||||||
export const NpcEditForm = ({ npc }: { npc: Npc }) => {
|
|
||||||
const { dispatch } = useDocumentCache();
|
|
||||||
async function saveNpcName(name: string) {
|
|
||||||
const updated: Npc = await pb.collection("documents").update(npc.id, {
|
|
||||||
data: {
|
|
||||||
...npc.data,
|
|
||||||
name,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
dispatch({ type: "setDocument", doc: updated });
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveNpcDescription(description: string) {
|
|
||||||
const updated: Npc = await pb.collection("documents").update(npc.id, {
|
|
||||||
data: {
|
|
||||||
...npc.data,
|
|
||||||
description,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
dispatch({ type: "setDocument", doc: updated });
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="">
|
|
||||||
<AutoSaveTextarea
|
|
||||||
multiline={false}
|
|
||||||
value={npc.data.name}
|
|
||||||
onSave={saveNpcName}
|
|
||||||
/>
|
|
||||||
<AutoSaveTextarea
|
|
||||||
value={npc.data.description}
|
|
||||||
onSave={saveNpcDescription}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import type { Npc } from "@/lib/types";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders an editable npc row
|
|
||||||
*/
|
|
||||||
export const NpcPrintRow = ({ npc }: { npc: Npc }) => {
|
|
||||||
return (
|
|
||||||
<div className="">
|
|
||||||
<h4>{npc.data.name}</h4>
|
|
||||||
<p>{npc.data.description}</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
// SceneForm.tsx
|
|
||||||
// Form for adding a new scene to a session.
|
|
||||||
import { useState } from "react";
|
|
||||||
import type { CampaignId, Scene } from "@/lib/types";
|
|
||||||
import { pb } from "@/lib/pocketbase";
|
|
||||||
import { BaseForm } from "@/components/form/BaseForm";
|
|
||||||
import { MultiLineInput } from "@/components/form/MultiLineInput";
|
|
||||||
import { useDocumentCache } from "@/context/document/hooks";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a form to add a new scene. Calls onCreate with the new scene document.
|
|
||||||
*/
|
|
||||||
export const NewSceneForm = ({
|
|
||||||
campaign,
|
|
||||||
onCreate,
|
|
||||||
}: {
|
|
||||||
campaign: CampaignId;
|
|
||||||
onCreate: (scene: Scene) => Promise<void>;
|
|
||||||
}) => {
|
|
||||||
const { dispatch } = useDocumentCache();
|
|
||||||
const [text, setText] = useState("");
|
|
||||||
const [adding, setAdding] = useState(false);
|
|
||||||
const [error, setError] = useState<string | null>(null);
|
|
||||||
|
|
||||||
async function handleSubmit(e: React.FormEvent) {
|
|
||||||
e.preventDefault();
|
|
||||||
if (!text.trim()) return;
|
|
||||||
setAdding(true);
|
|
||||||
setError(null);
|
|
||||||
try {
|
|
||||||
const sceneDoc: Scene = await pb.collection("documents").create({
|
|
||||||
campaign,
|
|
||||||
type: "scene",
|
|
||||||
data: {
|
|
||||||
text,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
setText("");
|
|
||||||
dispatch({ type: "setDocument", doc: sceneDoc });
|
|
||||||
await onCreate(sceneDoc);
|
|
||||||
} catch (e: any) {
|
|
||||||
setError(e?.message || "Failed to add scene.");
|
|
||||||
} finally {
|
|
||||||
setAdding(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseForm
|
|
||||||
title="Create new scene"
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
error={error}
|
|
||||||
buttonText={adding ? "Adding..." : "Create"}
|
|
||||||
content={
|
|
||||||
<>
|
|
||||||
<MultiLineInput
|
|
||||||
value={text}
|
|
||||||
onChange={(v) => setText(v)}
|
|
||||||
disabled={adding}
|
|
||||||
placeholder="Scene description..."
|
|
||||||
aria-label="Add new scene"
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
|
|
||||||
import { pb } from "@/lib/pocketbase";
|
|
||||||
import type { Scene } from "@/lib/types";
|
|
||||||
import { useDocumentCache } from "@/context/document/hooks";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders an editable scene form
|
|
||||||
*/
|
|
||||||
export const SceneEditForm = ({ scene }: { scene: Scene }) => {
|
|
||||||
const { dispatch } = useDocumentCache();
|
|
||||||
|
|
||||||
async function saveScene(text: string) {
|
|
||||||
const updated: Scene = await pb.collection("documents").update(scene.id, {
|
|
||||||
data: {
|
|
||||||
...scene.data,
|
|
||||||
text,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
dispatch({ type: "setDocument", doc: updated });
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="">
|
|
||||||
<AutoSaveTextarea value={scene.data.text} onSave={saveScene} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import type { Scene } from "@/lib/types";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders an editable scene row
|
|
||||||
*/
|
|
||||||
export const ScenePrintRow = ({ scene }: { scene: Scene }) => {
|
|
||||||
return <div className="">{scene.data.text}</div>;
|
|
||||||
};
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
// SecretForm.tsx
|
|
||||||
// Form for adding a new secret to a session.
|
|
||||||
import { useState } from "react";
|
|
||||||
import type { CampaignId, Secret } from "@/lib/types";
|
|
||||||
import { pb } from "@/lib/pocketbase";
|
|
||||||
import { BaseForm } from "@/components/form/BaseForm";
|
|
||||||
import { SingleLineInput } from "@/components/form/SingleLineInput";
|
|
||||||
import { useDocumentCache } from "@/context/document/hooks";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a form to add a new secret. Calls onCreate with the new secret document.
|
|
||||||
*/
|
|
||||||
export const NewSecretForm = ({
|
|
||||||
campaign,
|
|
||||||
onCreate,
|
|
||||||
}: {
|
|
||||||
campaign: CampaignId;
|
|
||||||
onCreate: (secret: Secret) => Promise<void>;
|
|
||||||
}) => {
|
|
||||||
const { dispatch } = useDocumentCache();
|
|
||||||
const [newSecret, setNewSecret] = useState("");
|
|
||||||
const [adding, setAdding] = useState(false);
|
|
||||||
const [error, setError] = useState<string | null>(null);
|
|
||||||
|
|
||||||
async function handleSubmit(e: React.FormEvent) {
|
|
||||||
e.preventDefault();
|
|
||||||
if (!newSecret.trim()) return;
|
|
||||||
setAdding(true);
|
|
||||||
setError(null);
|
|
||||||
try {
|
|
||||||
const secretDoc: Secret = await pb.collection("documents").create({
|
|
||||||
campaign,
|
|
||||||
type: "secret",
|
|
||||||
data: {
|
|
||||||
text: newSecret,
|
|
||||||
discovered: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
setNewSecret("");
|
|
||||||
dispatch({ type: "setDocument", doc: secretDoc as Secret });
|
|
||||||
await onCreate(secretDoc);
|
|
||||||
} catch (e: any) {
|
|
||||||
setError(e?.message || "Failed to add secret.");
|
|
||||||
} finally {
|
|
||||||
setAdding(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseForm
|
|
||||||
title="Create new secret"
|
|
||||||
isLoading={adding || !newSecret.trim()}
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
error={error}
|
|
||||||
content={
|
|
||||||
<SingleLineInput
|
|
||||||
value={newSecret}
|
|
||||||
onChange={setNewSecret}
|
|
||||||
placeholder="Secret description"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
// Displays a single secret with discovered checkbox and text.
|
|
||||||
import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
|
|
||||||
import { useDocumentCache } from "@/context/document/hooks";
|
|
||||||
import { pb } from "@/lib/pocketbase";
|
|
||||||
import type { Secret } from "@/lib/types";
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders an editable secret form.
|
|
||||||
* Handles updating the discovered state and discoveredIn relationship.
|
|
||||||
*/
|
|
||||||
export const SecretEditForm = ({ secret }: { secret: Secret }) => {
|
|
||||||
const { dispatch } = useDocumentCache();
|
|
||||||
const [checked, setChecked] = useState(
|
|
||||||
!!(secret.data as any)?.secret?.discovered,
|
|
||||||
);
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
|
|
||||||
async function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
|
|
||||||
const newChecked = e.target.checked;
|
|
||||||
setLoading(true);
|
|
||||||
setChecked(newChecked);
|
|
||||||
try {
|
|
||||||
const updated: Secret = await pb
|
|
||||||
.collection("documents")
|
|
||||||
.update(secret.id, {
|
|
||||||
data: {
|
|
||||||
...secret.data,
|
|
||||||
discovered: newChecked,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
dispatch({ type: "setDocument", doc: updated });
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveText(text: string) {
|
|
||||||
const updated: Secret = await pb.collection("documents").update(secret.id, {
|
|
||||||
data: {
|
|
||||||
...secret.data,
|
|
||||||
text,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
dispatch({ type: "setDocument", doc: updated });
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={checked}
|
|
||||||
onChange={handleChange}
|
|
||||||
className="accent-emerald-500 w-5 h-5"
|
|
||||||
aria-label="Discovered"
|
|
||||||
disabled={loading}
|
|
||||||
/>
|
|
||||||
<AutoSaveTextarea
|
|
||||||
multiline={false}
|
|
||||||
value={secret.data.text}
|
|
||||||
onSave={saveText}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
// SecretRow.tsx
|
|
||||||
// Displays a single secret with discovered checkbox and text.
|
|
||||||
import type { Secret } from "@/lib/types";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a secret row with a discovered checkbox and secret text.
|
|
||||||
* Handles updating the discovered state and discoveredIn relationship.
|
|
||||||
*/
|
|
||||||
export const SecretPrintRow = ({ secret }: { secret: Secret }) => {
|
|
||||||
return (
|
|
||||||
<li className="flex items-center gap-3">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
className="flex-none accent-emerald-500 w-5 h-5"
|
|
||||||
aria-label="Discovered"
|
|
||||||
/>
|
|
||||||
<span>
|
|
||||||
{(secret.data as any)?.secret?.text || (
|
|
||||||
<span className="italic text-slate-400">(No secret text)</span>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
|
|
||||||
import { useDocumentCache } from "@/context/document/hooks";
|
|
||||||
import { pb } from "@/lib/pocketbase";
|
|
||||||
import type { Session } from "@/lib/types";
|
|
||||||
|
|
||||||
export const SessionEditForm = ({ session }: { session: Session }) => {
|
|
||||||
const { dispatch } = useDocumentCache();
|
|
||||||
|
|
||||||
async function saveStrongStart(strongStart: string) {
|
|
||||||
const doc: Session = await pb.collection("documents").update(session.id, {
|
|
||||||
data: {
|
|
||||||
...session.data,
|
|
||||||
strongStart,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
dispatch({
|
|
||||||
type: "setDocument",
|
|
||||||
doc,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<form>
|
|
||||||
<h3 className="text-lg font-bold mb-4 text-slate-100">Strong Start</h3>
|
|
||||||
<AutoSaveTextarea
|
|
||||||
value={session.data.strongStart}
|
|
||||||
onSave={saveStrongStart}
|
|
||||||
placeholder="Enter a strong start for this session..."
|
|
||||||
aria-label="Strong Start"
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import type { Session } from "@/lib/types";
|
|
||||||
|
|
||||||
export const SessionPrintRow = ({ session }: { session: Session }) => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h3 className="text-lg font-bold text-slate-600">StrongStart</h3>
|
|
||||||
<div className="">{session.data.strongStart}</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { FormattedDate } from "@/components/FormattedDate";
|
|
||||||
import type { Session } from "@/lib/types";
|
|
||||||
import { Link } from "@tanstack/react-router";
|
|
||||||
|
|
||||||
export const SessionRow = ({ session }: { session: Session }) => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Link
|
|
||||||
to="/campaigns/$campaignId"
|
|
||||||
params={{ campaignId: session.campaign }}
|
|
||||||
search={{ tab: "sessions", docId: session.id }}
|
|
||||||
className="block font-semibold text-lg text-slate-300"
|
|
||||||
>
|
|
||||||
{session.name ? session.name : <FormattedDate date={session.created} />}
|
|
||||||
</Link>
|
|
||||||
<div className="">{session.data.strongStart}</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
// TreasureForm.tsx
|
|
||||||
// Form for adding a new treasure to a session.
|
|
||||||
import { useState } from "react";
|
|
||||||
import type { CampaignId, Treasure } from "@/lib/types";
|
|
||||||
import { pb } from "@/lib/pocketbase";
|
|
||||||
import { BaseForm } from "@/components/form/BaseForm";
|
|
||||||
import { SingleLineInput } from "@/components/form/SingleLineInput";
|
|
||||||
import { useDocumentCache } from "@/context/document/hooks";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a form to add a new treasure. Calls onCreate with the new treasure document.
|
|
||||||
*/
|
|
||||||
export const NewTreasureForm = ({
|
|
||||||
campaign,
|
|
||||||
onCreate,
|
|
||||||
}: {
|
|
||||||
campaign: CampaignId;
|
|
||||||
onCreate: (treasure: Treasure) => Promise<void>;
|
|
||||||
}) => {
|
|
||||||
const { dispatch } = useDocumentCache();
|
|
||||||
const [newTreasure, setNewTreasure] = useState("");
|
|
||||||
const [adding, setAdding] = useState(false);
|
|
||||||
const [error, setError] = useState<string | null>(null);
|
|
||||||
|
|
||||||
async function handleSubmit(e: React.FormEvent) {
|
|
||||||
e.preventDefault();
|
|
||||||
if (!newTreasure.trim()) return;
|
|
||||||
setAdding(true);
|
|
||||||
setError(null);
|
|
||||||
try {
|
|
||||||
const treasureDoc: Treasure = await pb.collection("documents").create({
|
|
||||||
campaign,
|
|
||||||
type: "treasure",
|
|
||||||
data: {
|
|
||||||
text: newTreasure,
|
|
||||||
discovered: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
setNewTreasure("");
|
|
||||||
dispatch({
|
|
||||||
type: "setDocument",
|
|
||||||
doc: treasureDoc,
|
|
||||||
});
|
|
||||||
await onCreate(treasureDoc);
|
|
||||||
} catch (e: any) {
|
|
||||||
setError(e?.message || "Failed to add treasure.");
|
|
||||||
} finally {
|
|
||||||
setAdding(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseForm
|
|
||||||
title="Create new treasure"
|
|
||||||
isLoading={adding || !newTreasure.trim()}
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
error={error}
|
|
||||||
content={
|
|
||||||
<SingleLineInput
|
|
||||||
value={newTreasure}
|
|
||||||
onChange={setNewTreasure}
|
|
||||||
placeholder="Treasure description"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
// Displays a single treasure with discovered checkbox and text.
|
|
||||||
import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
|
|
||||||
import { useDocumentCache } from "@/context/document/hooks";
|
|
||||||
import { pb } from "@/lib/pocketbase";
|
|
||||||
import type { Treasure } from "@/lib/types";
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders an editable treasure form.
|
|
||||||
* Handles updating the discovered state and discoveredIn relationship.
|
|
||||||
*/
|
|
||||||
export const TreasureEditForm = ({ treasure }: { treasure: Treasure }) => {
|
|
||||||
const { dispatch } = useDocumentCache();
|
|
||||||
const [checked, setChecked] = useState(
|
|
||||||
!!(treasure.data as any)?.treasure?.discovered,
|
|
||||||
);
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
|
|
||||||
async function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
|
|
||||||
const newChecked = e.target.checked;
|
|
||||||
setLoading(true);
|
|
||||||
setChecked(newChecked);
|
|
||||||
try {
|
|
||||||
const updated: Treasure = await pb
|
|
||||||
.collection("documents")
|
|
||||||
.update(treasure.id, {
|
|
||||||
data: {
|
|
||||||
...treasure.data,
|
|
||||||
treasure: {
|
|
||||||
...(treasure.data as any).treasure,
|
|
||||||
discovered: newChecked,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
dispatch({ type: "setDocument", doc: updated });
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveText(text: string) {
|
|
||||||
const updated: Treasure = await pb
|
|
||||||
.collection("documents")
|
|
||||||
.update(treasure.id, {
|
|
||||||
data: {
|
|
||||||
...treasure.data,
|
|
||||||
text,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
dispatch({ type: "setDocument", doc: updated });
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={checked}
|
|
||||||
onChange={handleChange}
|
|
||||||
className="accent-emerald-500 w-5 h-5"
|
|
||||||
aria-label="Discovered"
|
|
||||||
disabled={loading}
|
|
||||||
/>
|
|
||||||
<AutoSaveTextarea
|
|
||||||
multiline={false}
|
|
||||||
value={treasure.data.text}
|
|
||||||
onSave={saveText}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
// TreasureRow.tsx
|
|
||||||
// Displays a single treasure with discovered checkbox and text.
|
|
||||||
import type { Treasure } from "@/lib/types";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a treasure row with a discovered checkbox and treasure text.
|
|
||||||
* Handles updating the discovered state and discoveredIn relationship.
|
|
||||||
*/
|
|
||||||
export const TreasurePrintRow = ({ treasure }: { treasure: Treasure }) => {
|
|
||||||
return (
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
className="flex-none accent-emerald-500 w-5 h-5"
|
|
||||||
aria-label="Discovered"
|
|
||||||
/>
|
|
||||||
<span>
|
|
||||||
{(treasure.data as any)?.treasure?.text || (
|
|
||||||
<span className="italic text-slate-400">(No treasure text)</span>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,4 +1,9 @@
|
|||||||
import { getDocumentType, RelationshipType, type AnyDocument } from "./types";
|
import {
|
||||||
|
getDocumentType,
|
||||||
|
RelationshipType,
|
||||||
|
type AnyDocument,
|
||||||
|
type DocumentType,
|
||||||
|
} from "./types";
|
||||||
|
|
||||||
export function displayName(relationshipType: RelationshipType) {
|
export function displayName(relationshipType: RelationshipType) {
|
||||||
return relationshipType.charAt(0).toUpperCase() + relationshipType.slice(1);
|
return relationshipType.charAt(0).toUpperCase() + relationshipType.slice(1);
|
||||||
@@ -19,3 +24,17 @@ export function relationshipsForDocument(doc: AnyDocument): RelationshipType[] {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DocTypeForRelationshipType: { [k in RelationshipType]: DocumentType } = {
|
||||||
|
[RelationshipType.DiscoveredIn]: "session",
|
||||||
|
[RelationshipType.Locations]: "location",
|
||||||
|
[RelationshipType.Monsters]: "monster",
|
||||||
|
[RelationshipType.Npcs]: "npc",
|
||||||
|
[RelationshipType.Scenes]: "scene",
|
||||||
|
[RelationshipType.Secrets]: "secret",
|
||||||
|
[RelationshipType.Treasures]: "treasure",
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export function docTypeForRelationshipType(rt: RelationshipType): DocumentType {
|
||||||
|
return DocTypeForRelationshipType[rt];
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user