import type { PrimaryFieldType } from "core"; import { ucFirst } from "core/utils"; import type { Entity, EntityData, EntityRelation } from "data"; import { Fragment, useState } from "react"; import { TbDots } from "react-icons/tb"; import { useApiQuery, useEntityQuery } from "ui/client"; import { useBkndData } from "ui/client/schema/data/use-bknd-data"; import { Button } from "ui/components/buttons/Button"; import { IconButton } from "ui/components/buttons/IconButton"; import { Message } from "ui/components/display/Message"; import { Dropdown } from "ui/components/overlay/Dropdown"; import { useBrowserTitle } from "ui/hooks/use-browser-title"; import * as AppShell from "ui/layouts/AppShell/AppShell"; import { Breadcrumbs2 } from "ui/layouts/AppShell/Breadcrumbs2"; import { routes, useNavigate } from "ui/lib/routes"; import { bkndModals } from "ui/modals"; import { EntityForm } from "ui/modules/data/components/EntityForm"; import { EntityTable2 } from "ui/modules/data/components/EntityTable2"; import { useEntityForm } from "ui/modules/data/hooks/useEntityForm"; export function DataEntityUpdate({ params }) { const { $data, relations } = useBkndData(); const entity = $data.entity(params.entity as string); if (!entity) { return ; } const entityId = params.id as PrimaryFieldType; const [error, setError] = useState(null); const [navigate] = useNavigate(); useBrowserTitle(["Data", entity.label, `#${entityId}`]); const targetRelations = relations.listableRelationsOf(entity); const local_relation_refs = relations .sourceRelationsOf(entity) ?.map((r) => r.other(entity).reference); const $q = useEntityQuery( entity.name, entityId, { with: local_relation_refs, }, { keepPreviousData: false, revalidateOnFocus: false, shouldRetryOnError: false, }, ); function goBack() { window.history.go(-1); } async function onSubmitted(changeSet?: EntityData) { //return; if (!changeSet) { goBack(); return; } try { await $q.update(changeSet); if (error) setError(null); goBack(); } catch (e) { setError(e instanceof Error ? e.message : "Failed to update"); } } async function handleDelete() { if (confirm("Are you sure to delete?")) { try { await $q._delete(); if (error) setError(null); goBack(); } catch (e) { setError(e instanceof Error ? e.message : "Failed to delete"); } } } const data = $q.data; const { Form, handleSubmit } = useEntityForm({ action: "update", entity, initialData: $q.data?.toJSON(), onSubmitted, }); if (!data && !$q.isLoading) { return ( ); } const makeKey = (key: string | number = "") => `${entity.name}_${entityId}_${String(key)}`; const fieldsDisabled = $q.isLoading || $q.isValidating || Form.state.isSubmitting; return ( { bkndModals.open("debug", { data: { data: data as any, entity: entity.toJSON(), schema: entity.toSchema({ clean: true }), form: Form.state.values, state: Form.state, }, }); }, }, { label: "Settings", onClick: () => navigate(routes.settings.path(["data", "entities", entity.name]), { absolute: true, }), }, { label: "Delete", onClick: handleDelete, destructive: true, disabled: fieldsDisabled, }, ]} > [state.canSubmit, state.isSubmitting]} children={([canSubmit, isSubmitting]) => ( )} /> } className="pl-3" > {$q.isLoading ? (
Loading...
) : ( {error && (
Update failed: {error}
)} {targetRelations.length > 0 ? ( ) : null}
)}
); } function EntityDetailRelations({ id, entity, relations, }: { id: PrimaryFieldType; entity: Entity; relations: EntityRelation[]; }) { const [selected, setSelected] = useState( // @ts-ignore relations.length > 0 ? relations[0] : undefined, ); function handleClick(relation: EntityRelation) { setSelected(relation); } if (relations.length === 0) { return null; } //console.log("selected", selected, relations[0].helper(entity.name).other.reference); return (
{ const other = relation.other(entity); return { as: "button", type: "button", label: ucFirst(other.reference), onClick: () => handleClick(relation), active: selected?.other(entity).reference === other.reference, badge: relation.type(), }; })} />
); } function EntityDetailInner({ id, entity, relation, }: { id: PrimaryFieldType; entity: Entity; relation: EntityRelation; }) { const other = relation.other(entity); const [navigate] = useNavigate(); const [search, setSearch] = useState({ select: other.entity.getSelect(undefined, "table"), sort: other.entity.getDefaultSort(), limit: 10, offset: 0, }); // @todo: add custom key for invalidation const $q = useApiQuery( (api) => api.data.readManyByReference(entity.name, id, other.reference, search), { keepPreviousData: true, revalidateOnFocus: true, }, ); function handleClickRow(row: Record) { navigate(routes.data.entity.edit(other.entity.name, row.id)); } let handleClickNew: any; try { if (other.entity.type !== "system") { const ref = relation.getReferenceQuery(other.entity, id, other.reference); handleClickNew = () => { navigate(routes.data.entity.create(other.entity.name), { query: ref.where, }); //navigate(routes.data.entity.create(other.entity.name) + `?${query}`); }; } } catch (e) {} if (!$q.data) { return null; } const isUpdating = $q.isValidating || $q.isLoading; return (
{ setSearch((s) => ({ ...s, offset: (page - 1) * s.limit, })); }} />
); }