From c98e77afa664b4b10f867f9a91287b24a1f64c67 Mon Sep 17 00:00:00 2001 From: rizky Date: Mon, 15 Apr 2024 12:25:34 -0700 Subject: [PATCH] ship it --- comps/form/field/raw/Dropdown.tsx | 18 ++++- comps/form/field/type/TypeCustom.tsx | 27 +++++-- comps/form/field/type/TypeRelation.tsx | 34 +++++++- comps/form/typings.ts | 3 +- comps/form/utils/init.tsx | 10 +-- comps/list/TableList.tsx | 48 ++++++++++-- comps/list/sort-tree.ts | 83 ++++++++++++++++++++ comps/md/MDAction.tsx | 8 +- comps/md/MDTab.tsx | 3 +- comps/md/MasterDetail.tsx | 4 +- comps/md/utils/md-hash.ts | 2 +- comps/md/utils/md-init.ts | 4 +- comps/md/utils/typings.ts | 34 +++++--- gen/gen_form/gen_form.ts | 1 - gen/gen_form/on_load.ts | 4 +- gen/gen_form/on_submit.ts | 5 ++ gen/gen_relation/gen_relation.ts | 20 ++++- gen/gen_relation/new_field.ts | 13 +++- gen/gen_relation/on_load.ts | 7 +- gen/gen_table_list/gen_table_list.tsx | 18 ++++- gen/master_detail/gen-form.ts | 7 +- gen/prop/gen_prop_fields.ts | 9 +++ gen/prop/gen_prop_table.ts | 12 ++- utils/format-value.tsx | 103 ++++++++++++++++--------- 24 files changed, 372 insertions(+), 105 deletions(-) create mode 100755 comps/list/sort-tree.ts diff --git a/comps/form/field/raw/Dropdown.tsx b/comps/form/field/raw/Dropdown.tsx index 0e0e607..50a6856 100755 --- a/comps/form/field/raw/Dropdown.tsx +++ b/comps/form/field/raw/Dropdown.tsx @@ -3,7 +3,7 @@ import { useLocal } from "@/utils/use-local"; import { ChevronDown } from "lucide-react"; import { FC, ReactNode } from "react"; -export type OptionItem = { value: string; label: string; el?: ReactNode }; +export type OptionItem = { value: any; label: string; el?: ReactNode }; export const RawDropdown: FC<{ options: OptionItem[]; @@ -28,8 +28,18 @@ export const RawDropdown: FC<{ let filtered = options; if (local.filter) { - filtered = options.filter((e) => { - if (e.label.toLowerCase().includes(local.filter)) return true; + filtered = options.filter((e: any) => { + if (typeof e === "string") { + if (e.toLowerCase().includes(local.filter)) { + return true; + } + return false; + } + if ( + typeof e.label === "string" && + e.label.toLowerCase().includes(local.filter) + ) + return true; return false; }); } @@ -118,7 +128,7 @@ export const RawDropdown: FC<{ {!isEditor && ( = ({ local.custom = field.custom; } - if (field.custom) { - const res = local.custom(); - if (res instanceof Promise) { - console.error("Custom Function cannot be async"); - return null; - } else { - local.result = res; + if (!local.exec) { + local.exec = true; + const callback = (value: any, should_render: boolean) => { + local.result = value; + if (should_render) { + local.render(); + setTimeout(() => { + local.exec = false; + }, 100); + } + }; + if (field.custom) { + const res = local.custom(); + if (res instanceof Promise) { + res.then((value) => { + callback(value, true); + }); + } else { + callback(res, false); + } } } diff --git a/comps/form/field/type/TypeRelation.tsx b/comps/form/field/type/TypeRelation.tsx index 7415cdd..11ac3b4 100755 --- a/comps/form/field/type/TypeRelation.tsx +++ b/comps/form/field/type/TypeRelation.tsx @@ -3,11 +3,13 @@ import { FC, useEffect } from "react"; import { FMLocal, FieldLocal } from "../../typings"; import { OptionItem, RawDropdown } from "../raw/Dropdown"; import { FieldLoading } from "../raw/FieldLoading"; +import { sortTree } from "@/comps/list/sort-tree"; export type PropTypeRelation = { type: "has-one" | "has-many"; on_load: (opt: { value?: any }) => Promise<{ items: any[]; pk: string }>; label: (item: any, pk: string) => string; + id_parent: string; }; export const FieldTypeRelation: FC<{ field: FieldLocal; @@ -35,7 +37,7 @@ export const FieldTypeRelation: FC<{ field.status = "ready"; input.render(); }; - const res = prop.on_load({}); + const res = prop.on_load({ value }); if (res instanceof Promise) res.then(callback); else callback(res); } @@ -43,12 +45,33 @@ export const FieldTypeRelation: FC<{ let list: OptionItem[] = []; if (input.list && input.pk && input.list.length) { - for (const item of input.list) { + if (fm.field_def[field.name]?.optional) { + list.push({ + value: null, + label: "-", + }); + } + + let sorted = input.list; + if (prop.id_parent && input.pk) { + sorted = sortTree(sorted, prop.id_parent, input.pk); + } + + for (const item of sorted) { if (typeof item !== "object") continue; let label = ""; if (typeof prop.label === "function") { label = prop.label(item, input.pk); + + if (!label) { + const label_arr: string[] = []; + + for (const [k, v] of Object.entries(item)) { + if (k !== input.pk) label_arr.push(v as any); + } + label = label_arr.join(" "); + } } else { const label_arr: string[] = []; @@ -67,7 +90,7 @@ export const FieldTypeRelation: FC<{ } let selected = null; - if (typeof value === "object") { + if (value && typeof value === "object") { if (input.pk) selected = value[input.pk]; } else { selected = value; @@ -82,6 +105,11 @@ export const FieldTypeRelation: FC<{ options={list} value={selected} onChange={(val) => { + if (val === null) { + fm.data[field.name] = null; + fm.render(); + return; + } if (input.list && input.pk) { for (const item of input.list) { if (item[input.pk] === val) { diff --git a/comps/form/typings.ts b/comps/form/typings.ts index 684914a..e9bd024 100755 --- a/comps/form/typings.ts +++ b/comps/form/typings.ts @@ -151,6 +151,7 @@ export const formType = (active: { item_id: string }, meta: any) => { data: typeof ___data; reload: () => Promise; submit: () => Promise; + render: () => void; error: { list: { name: string; error: string }[]; set: (name: string, error: string) => void; @@ -165,7 +166,7 @@ export const formType = (active: { item_id: string }, meta: any) => { }; }; props: any; - size: { + size: { width: number; height: number; field: "full" | "half"; diff --git a/comps/form/utils/init.tsx b/comps/form/utils/init.tsx index 85d2ff2..a7b5d00 100755 --- a/comps/form/utils/init.tsx +++ b/comps/form/utils/init.tsx @@ -14,12 +14,10 @@ export const formInit = (fm: FMLocal, props: FMProps) => { const { on_load, sonar } = fm.props; fm.error = formError(fm); - if (isEditor) { - fm.field_def = {}; - const defs = parseGenField(fm.props.gen_fields); - for (const d of defs) { - fm.field_def[d.name] = d; - } + fm.field_def = {}; + const defs = parseGenField(fm.props.gen_fields); + for (const d of defs) { + fm.field_def[d.name] = d; } fm.reload = () => { diff --git a/comps/list/TableList.tsx b/comps/list/TableList.tsx index a3fc44f..155282b 100755 --- a/comps/list/TableList.tsx +++ b/comps/list/TableList.tsx @@ -14,6 +14,8 @@ import { createPortal } from "react-dom"; import { Toaster, toast } from "sonner"; import { getProp } from "../md/utils/get-prop"; import { Skeleton } from "../ui/skeleton"; +import { sortTree } from "./sort-tree"; +import { GFCol, parseGenField } from "@/gen/utils"; type OnRowClick = (arg: { row: any; @@ -36,6 +38,7 @@ type TableListProp = { _meta: Record; gen_fields: string[]; row_click: OnRowClick; + id_parent?: string; }; export const TableList: FC = ({ @@ -47,6 +50,7 @@ export const TableList: FC = ({ _meta, gen_fields, row_click, + id_parent, }) => { const local = useLocal({ el: null as null | HTMLDivElement, @@ -58,6 +62,7 @@ export const TableList: FC = ({ if (local.status === "ready") local.status = "resizing"; local.render(); }), + pk: null as null | GFCol, scrolled: false, data: [] as any[], status: "init" as "loading" | "ready" | "resizing" | "reload" | "init", @@ -91,14 +96,23 @@ export const TableList: FC = ({ if (fields) { const rel = fields?.find((e) => e.name === columnKey); if (rel && rel.checked) { - const field = rel.checked.find((e) => !e.is_pk); - if (field) { - should_set = false; + should_set = false; + + if (rel.type === "has-many") { local.sort.orderBy = { [columnKey]: { - [field.name]: direction === "ASC" ? "asc" : "desc", + _count: direction === "ASC" ? "asc" : "desc", }, }; + } else { + const field = rel.checked.find((e) => !e.is_pk); + if (field) { + local.sort.orderBy = { + [columnKey]: { + [field.name]: direction === "ASC" ? "asc" : "desc", + }, + }; + } } } } @@ -129,11 +143,14 @@ export const TableList: FC = ({ local.render(); const orderBy = local.sort.orderBy || undefined; - const load_args = { + const load_args: any = { async reload() {}, orderBy, paging: { take: local.paging.take, skip: local.paging.skip }, }; + if (id_parent) { + load_args.paging = {}; + } const result = on_load({ ...load_args, mode: "query" }); const callback = (data: any[]) => { @@ -190,6 +207,7 @@ export const TableList: FC = ({ col={{ name: props.column.key, value: props.row[props.column.key], + depth: props.row.__depth || 0, }} rows={local.data} > @@ -233,12 +251,23 @@ export const TableList: FC = ({ } const toaster_el = document.getElementsByClassName("prasi-toaster")[0]; + if (local.status === "init") { + const fields = parseGenField(gen_fields); + for (const field of fields) { + if (field.is_pk) { + local.pk = field; + } + } + } + if (isEditor && local.status !== "ready") { if (local.data.length === 0) { - const load_args = { + const load_args: any = { async reload() {}, paging: { take: local.paging.take, skip: local.paging.skip }, }; + + if (id_parent) load_args.paging = {}; if (typeof on_load === "function") { local.data = on_load({ ...load_args, mode: "query" }) as any; } @@ -248,6 +277,11 @@ export const TableList: FC = ({ let selected_idx = -1; + let data = local.data || []; + if (id_parent && local.pk && local.sort.columns.length === 0) { + data = sortTree(local.data, id_parent, local.pk.name); + } + if (mode === "table") { return (
= ({ sortColumns={local.sort.columns} onSortColumnsChange={local.sort.on_change} columns={columns} - rows={local.data || []} + rows={data} onScroll={local.paging.scroll} renderers={ local.status !== "ready" diff --git a/comps/list/sort-tree.ts b/comps/list/sort-tree.ts new file mode 100755 index 0000000..0f2e3ea --- /dev/null +++ b/comps/list/sort-tree.ts @@ -0,0 +1,83 @@ +export const sortTree = (list: any, parent_key: string, pk: string) => { + let meta = {} as Record< + string, + { item: any; idx: string; depth: number; id_parent: any } + >; + + if (list.length > 0 && !isEditor) { + const new_list = []; + const unlisted = {} as Record; + for (const item of list) { + if (item[parent_key] === null) { + if (!meta[item[pk]]) { + meta[item[pk]] = { + item, + idx: new_list.length + "", + depth: 0, + id_parent: null, + }; + item.__depth = 0; + new_list.push(item); + } + } else { + unlisted[item[pk]] = item; + } + } + let cyclic = {} as Record; + while (Object.values(unlisted).length > 0) { + for (const item of Object.values(unlisted)) { + const parent = meta[item[parent_key]]; + if (!cyclic[item[pk]]) { + cyclic[item[pk]] = 1; + } else { + cyclic[item[pk]]++; + } + if (cyclic[item[pk]] > 5) { + item.__depth = 0; + meta[item[pk]] = { + item, + depth: 0, + idx: new_list.length + "", + id_parent: null, + }; + new_list.push(item); + delete unlisted[item[pk]]; + continue; + } + + if (item[parent_key] === item[pk]) { + item.__depth = 0; + + meta[item[pk]] = { + item, + depth: 0, + idx: new_list.length + "", + id_parent: null, + }; + new_list.push(item); + delete unlisted[item[pk]]; + continue; + } + + if (parent) { + item.__depth = parent.depth + 1; + + meta[item[pk]] = { + item, + depth: parent.depth + 1, + idx: parent.idx + ".", + id_parent: item[parent_key], + }; + delete unlisted[item[pk]]; + } + } + } + const sorted = Object.values(meta) + .sort((a, b) => a.idx.localeCompare(b.idx)) + .map((e) => e.item); + + return sorted; + } + + return list; +}; diff --git a/comps/md/MDAction.tsx b/comps/md/MDAction.tsx index a3a6801..2132ec5 100755 --- a/comps/md/MDAction.tsx +++ b/comps/md/MDAction.tsx @@ -28,12 +28,16 @@ export const MDAction: FC<{ md: MDLocal; PassProp: any; child: any }> = ({ } return ( -
+
{md.actions.map((e, idx) => { if (isValidElement(e)) { return {e}; } - if (typeof e === "object" && e.label) { + if (typeof e === "object" && (e.action || e.label)) { return {child}; } })} diff --git a/comps/md/MDTab.tsx b/comps/md/MDTab.tsx index 456d44d..12cc540 100755 --- a/comps/md/MDTab.tsx +++ b/comps/md/MDTab.tsx @@ -16,7 +16,6 @@ export const MDTab: FC<{ md: MDLocal; mdr: MDRef }> = ({ md, mdr }) => { return null; } - return ( <> {md.props.show_head === "only-child" && } @@ -41,7 +40,7 @@ export const MDNavTab: FC<{ md: MDLocal; mdr: MDRef }> = ({ md, mdr }) => {
diff --git a/comps/md/MasterDetail.tsx b/comps/md/MasterDetail.tsx index 0ee78b8..012b5c2 100755 --- a/comps/md/MasterDetail.tsx +++ b/comps/md/MasterDetail.tsx @@ -44,7 +44,7 @@ export const MasterDetail: FC<{ active: "", list: [], }, - internal: { action_should_refresh: false }, + internal: { action_should_refresh: true }, childs: {}, props: { mode, @@ -65,7 +65,7 @@ export const MasterDetail: FC<{ masterDetailApplyParams(md); }, }, - master: { internal: null, render() {}, pk: null }, + master: { internal: null, render() {} }, }); const local = useLocal({ init: false }); if (isEditor) { diff --git a/comps/md/utils/md-hash.ts b/comps/md/utils/md-hash.ts index 73e6a5a..345c86b 100755 --- a/comps/md/utils/md-hash.ts +++ b/comps/md/utils/md-hash.ts @@ -36,7 +36,7 @@ export const masterDetailApplyParams = (md: MDLocal) => { } } - const pk = md.master.pk; + const pk = md.pk; if (pk && row[pk.name]) { md.params.hash[md.name] = row[pk.name]; } diff --git a/comps/md/utils/md-init.ts b/comps/md/utils/md-init.ts index fff5467..736df07 100755 --- a/comps/md/utils/md-init.ts +++ b/comps/md/utils/md-init.ts @@ -30,7 +30,7 @@ export const masterDetailInit = ( md.master.internal = child; const pk = parseGenField(md.props.gen_fields).find((e) => e.is_pk); if (pk) { - md.master.pk = pk; + md.pk = pk; } } if (cid === "cb52075a-14ab-455a-9847-6f1d929a2a73") { @@ -62,7 +62,7 @@ export const masterDetailInit = ( export const masterDetailSelected = (md: MDLocal) => { md.params.parse(); - const pk = md.master.pk; + const pk = md.pk; if (pk) { const value = md.params.hash[md.name]; if (value) { diff --git a/comps/md/utils/typings.ts b/comps/md/utils/typings.ts index 9a99d61..ad3182a 100755 --- a/comps/md/utils/typings.ts +++ b/comps/md/utils/typings.ts @@ -18,13 +18,14 @@ export type MDLocalInternal = { list: string[]; }; internal: { action_should_refresh: boolean }; - master: { internal: any; render: () => void; pk: null | GFCol }; + master: { internal: any; render: () => void }; params: { hash: any; tabs: any; parse: () => void; apply: () => void; }; + pk?: GFCol; props: { mode: "full" | "h-split" | "v-split"; show_head: "always" | "only-master" | "only-child" | "hidden"; @@ -75,19 +76,28 @@ export const MasterDetailType = `const md = { parse: () => void; apply: () => void; }; + props: { + mode: "full" | "h-split" | "v-split"; + show_head: "always" | "only-master" | "only-child" | "hidden"; + tab_mode: "h-tab" | "v-tab" | "hidden"; + editor_tab: string; + gen_fields: any; + gen_table: string; + on_init: (md: any) => void; + }; internal: { action_should_refresh: boolean }; render: () => void; - master: { internal: any; render: () => void; pk: null | { - name: string; - type: string; - is_pk: boolean; - optional: boolean; - relation?: { - from: { table: string; fields: string[] }; - to: { table: string; fields: string[] }; - fields: GFCol[]; - }; - } + master: { internal: any; render: () => void; }; + pk?: { + name: string; + type: string; + is_pk: boolean; + optional: boolean; + relation?: { + from: { table: string; fields: string[] }; + to: { table: string; fields: string[] }; + fields: GFCol[]; + }; }; childs: Record< string, diff --git a/gen/gen_form/gen_form.ts b/gen/gen_form/gen_form.ts index bf12ba0..501e231 100755 --- a/gen/gen_form/gen_form.ts +++ b/gen/gen_form/gen_form.ts @@ -60,7 +60,6 @@ export const gen_form = async (modify: (data: any) => void, data: any) => { result["body"] = data["body"]; result.body.content.childs = []; - console.log(fields); for (const item of fields.filter((e) => !e.is_pk)) { result.body.content.childs.push(await newField(item)); } diff --git a/gen/gen_form/on_load.ts b/gen/gen_form/on_load.ts index 1738405..9e085c2 100755 --- a/gen/gen_form/on_load.ts +++ b/gen/gen_form/on_load.ts @@ -34,8 +34,8 @@ async (opt) => { if (isEditor) return ${JSON.stringify(sample)}; let raw_id = params.id; - if (typeof md === 'object' && md.selected && md.master?.pk) { - const pk = md.master?.pk?.name; + if (typeof md === 'object' && md.selected && md.pk) { + const pk = md.pk?.name; if (md.selected[pk]) { raw_id = md.selected[pk]; } diff --git a/gen/gen_form/on_submit.ts b/gen/gen_form/on_submit.ts index f8e9e58..7db5090 100755 --- a/gen/gen_form/on_submit.ts +++ b/gen/gen_form/on_submit.ts @@ -34,6 +34,11 @@ async ({ form, error }: IForm) => { const pks = ${JSON.stringify(pks)}; for (const [k, v] of Object.entries(pks)) { if (typeof data[k] === 'object') { + if (data[k] === null) { + data[k] = { + disconnect: true + } + } if (data[k][v]) { data[k] = { connect: { diff --git a/gen/gen_relation/gen_relation.ts b/gen/gen_relation/gen_relation.ts index 58768ce..55e73cb 100755 --- a/gen/gen_relation/gen_relation.ts +++ b/gen/gen_relation/gen_relation.ts @@ -3,7 +3,11 @@ import { GFCol, parseGenField } from "../utils"; import { newField } from "./new_field"; import { on_load } from "./on_load"; -export const gen_relation = async (modify: (data: any) => void, data: any) => { +export const gen_relation = async ( + modify: (data: any) => void, + data: any, + arg: { id_parent: string } +) => { const table = JSON.parse(data.gen_table.value); const raw_fields = JSON.parse(data.gen_fields.value) as ( | string @@ -31,6 +35,10 @@ export const gen_relation = async (modify: (data: any) => void, data: any) => { } } + if (arg.id_parent) { + select[arg.id_parent] = true; + } + if (!pk) { alert("Failed to generate! Primary Key not found. "); return; @@ -40,7 +48,13 @@ export const gen_relation = async (modify: (data: any) => void, data: any) => { const code = {} as any; if (data["on_load"]) { result["on_load"] = data["on_load"]; - result["on_load"].value = on_load({ pk, pks, select, table }); + result["on_load"].value = on_load({ + pk, + pks, + select, + table, + id_parent: arg.id_parent, + }); code.on_load = result["on_load"].value; } @@ -50,7 +64,7 @@ export const gen_relation = async (modify: (data: any) => void, data: any) => { (item:any, pk:string) => { return \`${Object.entries(select) .filter(([k, v]) => { - if (typeof v !== "object" && k !== pk?.name) { + if (typeof v !== "object" && k !== pk?.name && k !== arg.id_parent) { return true; } }) diff --git a/gen/gen_relation/new_field.ts b/gen/gen_relation/new_field.ts index 149f0bc..ebb3f9d 100755 --- a/gen/gen_relation/new_field.ts +++ b/gen/gen_relation/new_field.ts @@ -28,15 +28,22 @@ export type NewFieldArg = { }; }; -export const newField = (arg: GFCol) => { +export const newField = (arg: GFCol, idx: number) => { + let result = `{item["${arg.name}"]}`; + let result_built = `render(React.createElement("div", Object.assign({}, props, { className: cx(props.className, "") }), item["${arg.name}"]));`; + if (idx === 0) { + result = ``; + result_built = `render(React.createElement("div", Object.assign({}, props, { className: cx(props.className, "") }), + React.createElement(FormatValue, { value: item["${arg.name}"], tree_depth: item.__depth })));`; + } return createItem({ name: arg.name, adv: { js: `\
- {item["${arg.name}"]} + ${result}
`, - jsBuilt: `render(React.createElement("div", Object.assign({}, props, { className: cx(props.className, "") }), item["${arg.name}"]));`, + jsBuilt: result_built, }, dim: { h: "full", diff --git a/gen/gen_relation/on_load.ts b/gen/gen_relation/on_load.ts index cde8ca2..9c497ae 100755 --- a/gen/gen_relation/on_load.ts +++ b/gen/gen_relation/on_load.ts @@ -6,6 +6,7 @@ export const on_load = ({ select, pks, opt, + id_parent, }: { pk: GFCol; table: string; @@ -15,6 +16,7 @@ export const on_load = ({ before_load: string; after_load: string; }; + id_parent: string; }) => { const sample: any = {}; for (const [k, v] of Object.entries(select) as any) { @@ -41,11 +43,6 @@ async (opt: { value: any }) => { } let items = await db.${table}.findMany({ - where: !!id - ? { - ${pk.name}: id, - } - : undefined, select: ${JSON.stringify(select, null, 2).split("\n").join("\n ")}, }); diff --git a/gen/gen_table_list/gen_table_list.tsx b/gen/gen_table_list/gen_table_list.tsx index ae206d3..490efb2 100755 --- a/gen/gen_table_list/gen_table_list.tsx +++ b/gen/gen_table_list/gen_table_list.tsx @@ -6,7 +6,7 @@ import { codeBuild } from "../master_detail/utils"; export const gen_table_list = async ( modify: (data: any) => void, data: any, - arg: { mode: "table" | "list" | "grid" } + arg: { mode: "table" | "list" | "grid"; id_parent: string } ) => { const table = JSON.parse(data.gen_table.value) as string; const raw_fields = JSON.parse(data.gen_fields.value) as ( @@ -35,6 +35,10 @@ export const gen_table_list = async ( } } + if (arg.id_parent) { + select[arg.id_parent] = true; + } + if (!pk) { alert("Failed to generate! Primary Key not found. "); return; @@ -60,11 +64,19 @@ export const gen_table_list = async ( } ); + let first = true; const child = createItem({ name: sub_name, childs: fields .map((e) => { if (e.is_pk) return; + let tree_depth = ""; + let tree_depth_built = ""; + if (first) { + tree_depth = `tree_depth={col.depth}`; + tree_depth_built = `tree_depth:col.depth`; + first = false; + } return { component: { id: "297023a4-d552-464a-971d-f40dcd940b77", @@ -84,10 +96,10 @@ export const gen_table_list = async ( adv: { js: `\
- +
`, jsBuilt: `\ -render(React.createElement("div", Object.assign({}, props, { className: cx(props.className, "") }),React.createElement(FormatValue, { value: col.value, name: col.name, gen_fields: gen_fields }))); +render(React.createElement("div", Object.assign({}, props, { className: cx(props.className, "") }),React.createElement(FormatValue, { value: col.value, name: col.name, gen_fields: gen_fields, ${tree_depth_built} }))); `, }, }), diff --git a/gen/master_detail/gen-form.ts b/gen/master_detail/gen-form.ts index 0a4b37c..0d5413e 100755 --- a/gen/master_detail/gen-form.ts +++ b/gen/master_detail/gen-form.ts @@ -48,6 +48,9 @@ type BreadItem = { actions: `\ async () => { return [ + { + action: "delete", + }, { label: "Save", action: "save", @@ -68,7 +71,9 @@ async ({ submit, reload }: Init) => { if (tab) { const actions = await getProp(tab.internal, "actions", { md }); if (Array.isArray(actions)) { - const save_btn = actions.find((e) => e.action === "save"); + const save_btn = actions + .filter((e) => e) + .find((e) => e.action === "save"); if (save_btn) { save_btn.onClick = async () => { await submit(); diff --git a/gen/prop/gen_prop_fields.ts b/gen/prop/gen_prop_fields.ts index 7eafe4f..bf34a14 100755 --- a/gen/prop/gen_prop_fields.ts +++ b/gen/prop/gen_prop_fields.ts @@ -1,4 +1,8 @@ +const cache = {} as Record; + export const gen_prop_fields = async (gen_table: string) => { + if (cache[gen_table]) return cache[gen_table]; + const result: { label: string; value: string; @@ -48,5 +52,10 @@ export const gen_prop_fields = async (gen_table: string) => { options, }); } + + if (!cache[gen_table]) { + cache[gen_table] = result; + } + return result; }; diff --git a/gen/prop/gen_prop_table.ts b/gen/prop/gen_prop_table.ts index f72a54e..b803bc0 100755 --- a/gen/prop/gen_prop_table.ts +++ b/gen/prop/gen_prop_table.ts @@ -1,6 +1,10 @@ +const cache: any = []; + export const gen_props_table = async () => { + if (cache.length > 0) return cache; + const result = [{ value: "", label: "" }]; - return [ + const final = [ ...result, ...(await db._schema.tables()).map((e) => ({ value: e, @@ -8,4 +12,10 @@ export const gen_props_table = async () => { reload: ["gen_fields", "gen_label"], })), ]; + + for (const f of final) { + cache.push(f); + } + + return cache; }; diff --git a/utils/format-value.tsx b/utils/format-value.tsx index 237574f..44b54bf 100755 --- a/utils/format-value.tsx +++ b/utils/format-value.tsx @@ -7,47 +7,76 @@ export const FormatValue: FC<{ value: any; name: string; gen_fields: string[]; + tree_depth?: number; }> = (prop) => { - const { value, gen_fields, name } = prop; + const { value, gen_fields, name, tree_depth } = prop; - const gf = JSON.stringify(gen_fields); - if (!fields_map.has(gf)) { - fields_map.set( - gf, - gen_fields.map((e: any) => { - if (typeof e === "string") { - return JSON.parse(e); - } else { - return { - ...JSON.parse(e.value), - checked: e.checked.map(JSON.parse), - }; + if (gen_fields) { + const gf = JSON.stringify(gen_fields); + if (!fields_map.has(gf)) { + fields_map.set( + gf, + gen_fields.map((e: any) => { + if (typeof e === "string") { + return JSON.parse(e); + } else { + return { + ...JSON.parse(e.value), + checked: e.checked.map(JSON.parse), + }; + } + }) + ); + } + + const fields = fields_map.get(gf); + + if (typeof value === "object" && value) { + const rel = fields?.find((e) => e.name === name); + if (rel && rel.checked) { + const result = rel.checked + .filter((e) => !e.is_pk) + .map((e) => { + return value[e.name]; + }) + .join(" - "); + + if (Array.isArray(value)) { + const len = value.length; + if (len === 0) return ` - `; + return `${len} item${len > 1 ? "s" : ""}`; } - }) + return result; + } + + return JSON.stringify(value); + } + } + + let prefix = <>; + if (typeof tree_depth === "number" && tree_depth > 0) { + prefix = ( +
+
+
); } - const fields = fields_map.get(gf); - - if (typeof value === "object" && value) { - const rel = fields?.find((e) => e.name === name); - if (rel && rel.checked) { - const result = rel.checked - .filter((e) => !e.is_pk) - .map((e) => { - return value[e.name]; - }) - .join(" - "); - - if (Array.isArray(value)) { - const len = value.length; - if (len === 0) return ` - `; - return `${len} item${len > 1 ? "s" : ""}`; - } - return result; - } - - return JSON.stringify(value); - } - return <>{value}; + return ( +
+ {prefix} +
{value}
+
+ ); };