diff --git a/src/components/documents/GenericEditForm.tsx b/src/components/documents/GenericEditForm.tsx index 637e1cd..a466d22 100644 --- a/src/components/documents/GenericEditForm.tsx +++ b/src/components/documents/GenericEditForm.tsx @@ -1,33 +1,30 @@ import { AutoSaveTextarea } from "@/components/AutoSaveTextarea"; import { pb } from "@/lib/pocketbase"; -import type { AnyDocument, Location } from "@/lib/types"; +import { getDocumentType, type AnyDocument } from "@/lib/types"; import { useDocumentCache } from "@/context/document/hooks"; +import { + getFieldsForType, + type DocumentField, + type FieldType, +} from "@/lib/fields"; export type GenericFieldType = "multiline" | "singleline" | "checkbox"; export type Props = { doc: T; - fields: { [K in keyof T["data"]]: GenericFieldType }; }; -export const GenericEditForm = ({ - doc, - fields, -}: Props) => { +export const GenericEditForm = ({ doc }: Props) => { + const docType = getDocumentType(doc) as T["type"]; + const fields = getFieldsForType(docType); + return (
{ // The type checker seems to lose the types when using Object.entries here. - Object.entries(fields).map( - ([fieldName, fieldType]: [string, unknown]) => ( - - ), - ) + fields.map((documentField) => ( + + )) }
); @@ -35,51 +32,46 @@ export const GenericEditForm = ({ const GenericEditFormField = ({ doc, - fieldName, - fieldType, + field, }: { doc: T; - fieldName: keyof T["data"]; - fieldType: GenericFieldType; + field: DocumentField; }) => { const { dispatch } = useDocumentCache(); // The type checker really doesn't like indexing into this type implicitly, so we'll store it in a temporary to give it the right hints. const data = doc.data as T["data"]; - async function saveField(value: string) { + async function saveField(value: string | boolean) { const updated: T = await pb.collection("documents").update(doc.id, { - data: { - ...doc.data, - [fieldName]: value, - }, + data: field.setter(value, doc.data), }); dispatch({ type: "setDocument", doc: updated }); } - switch (fieldType) { - case "multiline": + switch (field.fieldType) { + case "longText": return ( ); - case "singleline": + case "shortText": return ( ); - case "checkbox": + case "toggle": return ( saveField(e.target.value)} + checked={!!field.getter(data)} + onChange={(e) => saveField(!!e.target.value)} className="accent-emerald-500 w-5 h-5" /> ); diff --git a/src/lib/fields.ts b/src/lib/fields.ts new file mode 100644 index 0000000..87d5578 --- /dev/null +++ b/src/lib/fields.ts @@ -0,0 +1,88 @@ +import { + type DocumentData, + type DocumentsByType, + type DocumentType, +} from "./types"; + +export type FieldType = "shortText" | "longText" | "toggle"; + +export type ValueForFieldType = { + shortText: string; + longText: string; + toggle: boolean; +}[T]; + +export type DocumentField = { + name: string; + fieldType: F; + getter: (doc: DocumentData) => ValueForFieldType; + setter: ( + value: ValueForFieldType, + doc: DocumentData, + ) => DocumentData; +}; + +const simpleField = ( + name: string, + key: keyof DocumentData, + fieldType: F, +): DocumentField => ({ + name, + fieldType, + getter: (doc) => doc[key] as unknown as ValueForFieldType, + setter: (value, doc) => ({ ...doc, [key]: value }), +}); + +const simpleFields = ( + fields: Record, FieldType]>, +): DocumentField[] => + Object.entries(fields).map(([name, [key, fieldType]]) => + simpleField(name, key, fieldType), + ); + +export function getFieldsForType( + docType: D, +): DocumentField[] { + switch (docType) { + case "front": + return simpleFields<"front">({ + Name: ["name", "shortText"], + Description: ["description", "longText"], + Resolved: ["resolved", "toggle"], + }); + case "location": + return [ + simpleField("Name", "name", "shortText"), + simpleField("Description", "description", "longText"), + ]; + case "monster": + return [simpleField("Name", "name", "shortText")]; + case "npc": + return [ + simpleField("Name", "name", "shortText"), + simpleField("Description", "description", "longText"), + ]; + case "scene": + return [simpleField("Text", "text", "longText")]; + case "secret": + return [ + simpleField("Discovered", "discovered", "toggle"), + simpleField("Text", "text", "shortText"), + ]; + case "session": + return [ + simpleField("Name", "name", "shortText"), + simpleField("Strong Start", "strongStart", "longText"), + ]; + case "thread": + return [ + simpleField("Resolved", "resolved", "toggle"), + simpleField("Text", "text", "shortText"), + ]; + case "treasure": + return [ + simpleField("Discovered", "discovered", "toggle"), + simpleField("Text", "text", "shortText"), + ]; + } +} diff --git a/src/lib/types.ts b/src/lib/types.ts index 3c2296d..0d460d7 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -75,11 +75,6 @@ export type DocumentType = | "thread" | "treasure"; -export type DocumentData = { - type: Type; - data: Data; -}; - export type Document = RecordModel & { id: DocumentId; collectionName: typeof CollectionIds.Documents; @@ -102,6 +97,23 @@ export type AnyDocument = | Thread | Treasure; +export type DocumentsByType = { + front: Front; + location: Location; + monster: Monster; + npc: Npc; + scene: Scene; + secret: Secret; + session: Session; + thread: Thread; + treasure: Treasure; +}; + +export type DocumentData = + DocumentsByType[Type]["data"]; + +export type GetDocumentType = D["type"]; + export function getDocumentType(doc: AnyDocument): DocumentType { return doc.type; }