import { Type } from "core/utils"; import { type Entity, querySchema } from "data"; import { Fragment } from "react"; import { TbDots } from "react-icons/tb"; import { useApi, useApiQuery } from "ui/client"; import { useBknd } from "ui/client/bknd"; 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 { useSearch } from "ui/hooks/use-search"; import * as AppShell from "ui/layouts/AppShell/AppShell"; import { routes, useNavigate } from "ui/lib/routes"; import { useCreateUserModal } from "ui/modules/auth/hooks/use-create-user-modal"; import { EntityTable2 } from "ui/modules/data/components/EntityTable2"; // @todo: migrate to Typebox const searchSchema = Type.Composite( [ Type.Pick(querySchema, ["select", "where", "sort"]), Type.Object({ page: Type.Optional(Type.Number({ default: 1 })), perPage: Type.Optional(Type.Number({ default: 10 })) }) ], { additionalProperties: false } ); const PER_PAGE_OPTIONS = [5, 10, 25]; export function DataEntityList({ params }) { const { $data } = useBkndData(); const entity = $data.entity(params.entity as string); if (!entity) { return ; } useBrowserTitle(["Data", entity?.label ?? params.entity]); const [navigate] = useNavigate(); const search = useSearch(searchSchema, { select: entity.getSelect(undefined, "form"), sort: entity.getDefaultSort() }); const $q = useApiQuery( (api) => api.data.readMany(entity?.name as any, { select: search.value.select, limit: search.value.perPage, offset: (search.value.page - 1) * search.value.perPage, sort: `${search.value.sort.dir === "asc" ? "" : "-"}${search.value.sort.by}` }), { enabled: !!entity, revalidateOnFocus: true, keepPreviousData: true } ); const data = $q.data?.data; const meta = $q.data?.body.meta; function handleClickRow(row: Record) { if (entity) navigate(routes.data.entity.edit(entity.name, row.id)); } function handleClickPage(page: number) { search.set("page", page); } function handleSortClick(name: string) { const sort = search.value.sort!; const newSort = { by: name, dir: sort.by === name && sort.dir === "asc" ? "desc" : "asc" }; // // @ts-expect-error - somehow all search keys are optional console.log("new sort", newSort); search.set("sort", newSort as any); } function handleClickPerPage(perPage: number) { // @todo: also reset page to 1 search.set("perPage", perPage); } const isUpdating = $q.isLoading && $q.isValidating; return ( navigate(routes.data.schema.entity(entity.name)) }, { label: "Data Schema", onClick: () => navigate(routes.data.schema.root()) }, { label: "Advanced Settings", onClick: () => navigate(routes.settings.path(["data", "entities", entity.name]), { absolute: true }) } ]} position="bottom-end" > } > {entity.label}
{/*
*/}
); } function EntityCreateButton({ entity }: { entity: Entity }) { const b = useBknd(); const createUserModal = useCreateUserModal(); const [navigate] = useNavigate(); if (!entity) return null; if (entity.type !== "regular") { const system = { users: b.app.config.auth.entity_name, media: b.app.config.media.entity_name }; if (system.users === entity.name) { return ( ); } return null; } return ( ); }