diff --git a/src/components/documents/DocumentEditForm.tsx b/src/components/documents/DocumentEditForm.tsx index b5eba63..06f11c8 100644 --- a/src/components/documents/DocumentEditForm.tsx +++ b/src/components/documents/DocumentEditForm.tsx @@ -28,14 +28,6 @@ export const DocumentEditForm = ({ document }: { document: AnyDocument }) => { case "treasure": return ; case "thread": - return ( - - ); + return ; } }; diff --git a/src/components/documents/DocumentLink.tsx b/src/components/documents/DocumentLink.tsx index bdce0f0..5cfe017 100644 --- a/src/components/documents/DocumentLink.tsx +++ b/src/components/documents/DocumentLink.tsx @@ -8,37 +8,45 @@ export type Props = React.PropsWithChildren<{ }>; export function DocumentLink({ childDocId, className, children }: Props) { - const docPath = useDocumentPath(); + // const docPath = useDocumentPath(); + // + // const params = useParams({ + // strict: false, + // }); + // + // const campaignSearch = useSearch({ + // from: "/_app/_authenticated/campaigns/$campaignId", + // shouldThrow: false, + // }); + // + // const to = params.campaignId + // ? `/campaigns/${params.campaignId}` + // : docPath + // ? makeDocumentPath( + // docPath.documentId, + // docPath?.relationshipType, + // childDocId, + // ) + // : undefined; + // + // const search = campaignSearch + // ? { tab: campaignSearch.tab, docId: childDocId } + // : undefined; + // + // if (to === undefined) { + // throw new Error("Not in a document or campaign context"); + // } + // + // return ( + // + // {children} + // + // ); - const params = useParams({ - strict: false, - }); - - const campaignSearch = useSearch({ - from: "/_app/_authenticated/campaigns/$campaignId", - shouldThrow: false, - }); - - const to = params.campaignId - ? `/campaigns/${params.campaignId}` - : docPath - ? makeDocumentPath( - docPath.documentId, - docPath?.relationshipType, - childDocId, - ) - : undefined; - - const search = campaignSearch - ? { tab: campaignSearch.tab, docId: childDocId } - : undefined; - - if (to === undefined) { - throw new Error("Not in a document or campaign context"); - } + const to = makeDocumentPath(childDocId); return ( - + {children} ); diff --git a/src/components/documents/GenericEditForm.tsx b/src/components/documents/GenericEditForm.tsx index a466d22..922415c 100644 --- a/src/components/documents/GenericEditForm.tsx +++ b/src/components/documents/GenericEditForm.tsx @@ -56,6 +56,7 @@ const GenericEditFormField = ({ multiline={true} value={field.getter(data) as string} onSave={saveField} + id={field.name} /> ); case "shortText": @@ -64,6 +65,7 @@ const GenericEditFormField = ({ multiline={false} value={field.getter(data) as string} onSave={saveField} + id={field.name} /> ); case "toggle": @@ -73,6 +75,7 @@ const GenericEditFormField = ({ checked={!!field.getter(data)} onChange={(e) => saveField(!!e.target.value)} className="accent-emerald-500 w-5 h-5" + id={field.name} /> ); } diff --git a/src/components/documents/GenericNewDocumentForm.tsx b/src/components/documents/GenericNewDocumentForm.tsx new file mode 100644 index 0000000..5b51c69 --- /dev/null +++ b/src/components/documents/GenericNewDocumentForm.tsx @@ -0,0 +1,152 @@ +import { AutoSaveTextarea } from "@/components/AutoSaveTextarea"; +import { useDocumentCache } from "@/context/document/hooks"; +import { + getFieldsForType, + type DocumentField, + type FieldType, + type ValueForFieldType, +} from "@/lib/fields"; +import { pb } from "@/lib/pocketbase"; +import { + type CampaignId, + type DocumentData, + type DocumentsByType, + type DocumentType, +} from "@/lib/types"; +import { useCallback, useState } from "react"; + +export type GenericFieldType = "multiline" | "singleline" | "checkbox"; + +export type Props = { + docType: T; + campaignId: CampaignId; + onCreate: (doc: DocumentsByType[T]) => Promise; +}; + +export const GenericNewDocumentForm = ({ + docType, + campaignId, + onCreate, +}: Props) => { + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + const { dispatch } = useDocumentCache(); + + const fields = getFieldsForType(docType); + + const [docData, setDocData] = useState>( + fields.reduce((d, f) => f.setDefault(d), {} as DocumentData), + ); + + const updateData = + (field: DocumentField) => + (value: ValueForFieldType) => + setDocData(field.setter(value, docData)); + + const saveData = useCallback(async () => { + setIsLoading(true); + console.log(`Creating ${docType}: `, docData); + try { + const newDocument: DocumentsByType[T] = await pb + .collection("documents") + .create({ + campaign: campaignId, + type: docType, + data: docData, + }); + await onCreate(newDocument); + dispatch({ + type: "setDocument", + doc: newDocument, + }); + } catch (e: unknown) { + if (e instanceof Error) { + setError(e.message); + } else { + setError("An unknown error occurred while creating the session."); + } + } + setIsLoading(false); + }, [campaignId, setIsLoading, setError, docData]); + + return ( +
+ {error && ( + // TODO: class and style for errors +
{error}
+ )} + { + // The type checker seems to lose the types when using Object.entries here. + fields.map((field) => ( + + )) + } + +
+ ); +}; + +const GenericNewFormField = ({ + field, + value, + onUpdate, +}: { + field: DocumentField; + value: ValueForFieldType; + onUpdate: (value: ValueForFieldType) => void; +}) => { + return ( +
+ + +
+ ); +}; + +const GenericNewFormInput = ({ + field, + value, + onUpdate, +}: { + field: DocumentField; + value: ValueForFieldType; + onUpdate: (value: ValueForFieldType) => void; +}) => { + switch (field.fieldType) { + case "longText": + return ( +