Files
dm-companion/src/components/documents/GenericEditForm.tsx

83 lines
2.2 KiB
TypeScript

import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
import { pb } from "@/lib/pocketbase";
import { getDocumentType, type AnyDocument } from "@/lib/types";
import { useDocumentCache } from "@/context/document/hooks";
import {
getFieldsForType,
type DocumentField,
type FieldType,
} from "@/lib/fields";
import { ToggleInput } from "../form/ToggleInput";
export type GenericFieldType = "multiline" | "singleline" | "checkbox";
export type Props<T extends AnyDocument> = {
doc: T;
};
export const GenericEditForm = <T extends AnyDocument>({ doc }: Props<T>) => {
const docType = getDocumentType(doc) as T["type"];
const fields = getFieldsForType(docType);
return (
<div className="">
{
// The type checker seems to lose the types when using Object.entries here.
fields.map((documentField) => (
<GenericEditFormField doc={doc} field={documentField} />
))
}
</div>
);
};
const GenericEditFormField = <T extends AnyDocument>({
doc,
field,
}: {
doc: T;
field: DocumentField<T["type"], FieldType>;
}) => {
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 | boolean) {
const updated: T = await pb.collection("documents").update(doc.id, {
data: field.setter(value, doc.data),
});
dispatch({ type: "setDocument", doc: updated });
}
switch (field.fieldType) {
case "longText":
return (
<AutoSaveTextarea
multiline={true}
value={field.getter(data) as string}
onSave={saveField}
id={field.name}
/>
);
case "shortText":
return (
<AutoSaveTextarea
multiline={false}
value={field.getter(data) as string}
onSave={saveField}
id={field.name}
/>
);
case "toggle":
return (
<ToggleInput
label={field.name}
value={!!field.getter(data)}
onChange={saveField}
placeholder={field.name}
/>
);
}
};