From 1d5cb533605f01c1583d8e11ccff669198f4dec5 Mon Sep 17 00:00:00 2001 From: rizky Date: Thu, 8 Aug 2024 23:32:30 -0700 Subject: [PATCH] fix --- comps/filter/parser/filter-where.ts | 3 +- comps/form/field/table-edit/TableEdit.tsx | 165 ---------------- comps/form/gen/gen-table-edit.ts | 7 - comps/list/TableList.tsx | 227 +++++++++++++++------- comps/list/utils/sort-tree.ts | 167 +++------------- comps/md/gen/gen-table-list.ts | 11 +- comps/md/gen/tbl-list/on_load.ts | 4 + utils/format-value.tsx | 42 ++-- 8 files changed, 214 insertions(+), 412 deletions(-) diff --git a/comps/filter/parser/filter-where.ts b/comps/filter/parser/filter-where.ts index 19bfe0d..f68a10f 100755 --- a/comps/filter/parser/filter-where.ts +++ b/comps/filter/parser/filter-where.ts @@ -14,7 +14,7 @@ export const filterWhere = (filter_name: string, p: any) => { } for (const pf of Object.values(f.filter.ref)) { if (pf.mode === "raw") { - const data = pf.data?._where ? pf.data?._where : pf.data + const data = pf.data?._where ? pf.data?._where : pf.data; for (const [k, v] of Object.entries(data)) { where[k] = v; } @@ -34,6 +34,5 @@ export const filterWhere = (filter_name: string, p: any) => { type: p.sft__type, }); } -console.log({where}) return where; }; diff --git a/comps/form/field/table-edit/TableEdit.tsx b/comps/form/field/table-edit/TableEdit.tsx index b7e4391..673f2a4 100755 --- a/comps/form/field/table-edit/TableEdit.tsx +++ b/comps/form/field/table-edit/TableEdit.tsx @@ -286,171 +286,6 @@ export const TableEdit: FC<{ ); - return ( - <> -
- - { - local.tbl.data.push(e ? e : {}); - fm.render(); - - setTimeout(() => { - const last = Array.from( - ref.current?.querySelectorAll(".rdg-row") || [] - ).pop(); - const input = last?.querySelector("input"); - if (input) { - input.focus(); - } - }, 100); - }, - }} - fm_parent={parent} - > - {bottom} - -
- - ); }; function getProp(child: any, name: string, defaultValue?: any) { diff --git a/comps/form/gen/gen-table-edit.ts b/comps/form/gen/gen-table-edit.ts index 6e0073c..2a4b7e0 100755 --- a/comps/form/gen/gen-table-edit.ts +++ b/comps/form/gen/gen-table-edit.ts @@ -50,13 +50,6 @@ export const genTableEdit = async ( }, false ); - let tree_depth = ""; - let tree_depth_built = ""; - if (first) { - tree_depth = `tree_depth={col.depth}`; - tree_depth_built = `tree_depth:col.depth`; - first = false; - } childs.push({ component: { id: "297023a4-d552-464a-971d-f40dcd940b77", diff --git a/comps/list/TableList.tsx b/comps/list/TableList.tsx index 5ae2c7e..32961ee 100755 --- a/comps/list/TableList.tsx +++ b/comps/list/TableList.tsx @@ -4,7 +4,13 @@ import { fields_map } from "@/utils/format-value"; import { useLocal } from "@/utils/use-local"; import { set } from "lib/utils/set"; import get from "lodash.get"; -import { AlertTriangle, Loader2, Sticker } from "lucide-react"; +import { + AlertTriangle, + ChevronDown, + ChevronRight, + Loader2, + Sticker, +} from "lucide-react"; import { ChangeEvent, FC, @@ -31,6 +37,7 @@ import { MDLocal } from "../md/utils/typings"; import { Skeleton } from "../ui/skeleton"; import { sortTree } from "./utils/sort-tree"; import { toast } from "../ui/toast"; +import { Arrow } from "../custom/Datepicker/components/utils"; type OnRowClick = (arg: { row: any; @@ -150,6 +157,7 @@ export const TableList: FC = ({ | "init" | "error", where: null as any, + firstKey: "", should_toast: true, paging: { take: 0, @@ -165,6 +173,7 @@ export const TableList: FC = ({ } }, }, + collapsed: new Set(), cached_row: new WeakMap(), sort: { columns: (ls_sort?.columns || []) as SortColumn[], @@ -421,10 +430,73 @@ export const TableList: FC = ({ } let columns: ColumnOrColumnGroup[] = []; let isCheckbox = false; + let isTree = false; try { if (feature?.find((e) => e === "checkbox")) isCheckbox = true; + if (feature?.find((e) => e === "tree")) isTree = true; } catch (e) {} + if (local.status === "init") { + const fields = parseGenField(gen_fields); + for (const field of fields) { + if (field.is_pk) { + local.pk = field; + } + } + } + + if (typeof value !== "undefined") { + local.data = value; + local.status = "ready" as any; + } else { + if (isEditor && local.status !== "ready") { + if (local.data.length === 0) { + const load_args: any = { + async reload() {}, + where: {}, + paging: { + take: local.paging.take > 0 ? local.paging.take : undefined, + skip: local.paging.skip, + }, + }; + if (id_parent) load_args.paging = {}; + if (typeof on_load === "function") { + let res = on_load({ ...load_args, mode: "query" }) as any; + if (typeof res === "object" && res instanceof Promise) { + res.then((e) => { + local.data = e; + }); + } else { + local.data = res; + } + } + } + local.status = "ready"; + } + } + let data = Array.isArray(local.data) ? local.data : []; + if (typeof local.data === "string") console.error(local.data); + + if (isEditor) { + if (data.length > 0) { + w.prasi_table_list_temp_data = data; + } else if ( + w.prasi_table_list_temp_data && + w.prasi_table_list_temp_data.length > 0 + ) { + data = w.prasi_table_list_temp_data; + } + } + + if (isTree && id_parent && local.pk && local.sort.columns.length === 0) { + data = sortTree(local.data, id_parent, local.pk.name).filter((e) => { + if (local.pk && local.collapsed.has(e?.__parent?.[local.pk.name])) { + return false; + } + return true; + }); + } + if (childs.length && isCheckbox) { columns.push({ key: SELECT_COLUMN_KEY, @@ -466,13 +538,14 @@ export const TableList: FC = ({ cellClass: selectCellClassname, }); } + + let first = true; for (const child of childs) { let key = getProp(child, "name", {}); const name = getProp(child, "title", ""); const type = getProp(child, "type", ""); const width = parseInt(getProp(child, "width", {})); if (type === "checkbox") { - const on_click = getProp(child, "opt__on_click", ""); columns.push({ key, name, @@ -528,21 +601,79 @@ export const TableList: FC = ({ }); return ( - - {child} - + <> + {isTree && local.firstKey === key && local.pk && ( +
{ + if (!local.pk) return; + if (props?.row?.__children?.length > 0) { + e.stopPropagation(); + if (!local.collapsed.has(props.row?.[local.pk.name])) { + local.collapsed.add(props.row?.[local.pk.name]); + } else { + local.collapsed.delete(props.row?.[local.pk.name]); + } + local.render(); + } + }} + > +
+ {props.row?.__children?.length > 0 && ( + <> + {local.collapsed.has(props.row?.[local.pk.name]) ? ( + + ) : ( + + )} + + )} +
+ {props.row?.__parent && + props.row?.__children?.length === 0 && ( +
+ )} +
+ )} + + {child} + + ); }, }); + + if (first) { + first = false; + local.firstKey = key; + } } } if (mode === "list") { @@ -585,63 +716,6 @@ 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 (typeof value !== "undefined") { - local.data = value; - local.status = "ready" as any; - } else { - if (isEditor && local.status !== "ready") { - if (local.data.length === 0) { - const load_args: any = { - async reload() {}, - where: {}, - paging: { - take: local.paging.take > 0 ? local.paging.take : undefined, - skip: local.paging.skip, - }, - }; - if (id_parent) load_args.paging = {}; - if (typeof on_load === "function") { - let res = on_load({ ...load_args, mode: "query" }) as any; - if (typeof res === "object" && res instanceof Promise) { - res.then((e) => { - local.data = e; - }); - } else { - local.data = res; - } - } - } - local.status = "ready"; - } - } - - let data = Array.isArray(local.data) ? local.data : []; - if (typeof local.data === "string") console.error(local.data); - - if (isEditor) { - if (data.length > 0) { - w.prasi_table_list_temp_data = data; - } else if ( - w.prasi_table_list_temp_data && - w.prasi_table_list_temp_data.length > 0 - ) { - data = w.prasi_table_list_temp_data; - } - } - - if (id_parent && local.pk && local.sort.columns.length === 0) { - data = sortTree(local.data, id_parent, local.pk.name); - } - if (mode === "table") { return (
= ({ css` .rdg { display: grid !important; + + .rdg-cell, + .rdg-header-sort-name { + display: flex; + flex-direction: row; + align-items: stretch; + + &.rdg-header-sort-name { + align-items: center; + } + } } ` )} diff --git a/comps/list/utils/sort-tree.ts b/comps/list/utils/sort-tree.ts index 27ab46c..5badca8 100755 --- a/comps/list/utils/sort-tree.ts +++ b/comps/list/utils/sort-tree.ts @@ -42,158 +42,53 @@ export const treePrefix = (props: any) => { } return prefix; }; - export const sortTree = (list: any[], parent_key: string, pk: string) => { const nodes: { [id: string]: any } = {}; - const result: any[] = []; // First pass: Create nodes list.forEach((node) => { const id = node[pk]; - nodes[id] = { ...node, __depth: 0, __children: [] }; + nodes[id] = { ...node, __depth: 0, __children: [], __parent: null }; }); - // Second pass: Build the tree structure + // Second pass: Build relationships list.forEach((node) => { const id = node[pk]; const parentId = node[parent_key]; - if (parentId === null || parentId === undefined) { - result.push(nodes[id]); - } else { - if (nodes[parentId]) { - nodes[parentId].__children.push(nodes[id]); - } else { - // Handle the case where a parent is missing - result.push(nodes[id]); - } + + if (parentId && parentId !== id && nodes[parentId]) { + nodes[id].__parent = nodes[parentId]; + nodes[parentId].__children.push(nodes[id]); } }); - // Function to flatten the tree - function flattenTree(node: any, depth: number = 0): any[] { - node.__depth = depth; - const children = node.__children || []; - delete node.__children; - return [ - node, - ...children - .sort((a: any, b: any) => { - if ( - a.__children.length === 0 && - b.__children.length === 0 && - a.name && - b.name - ) { - return a.name.localeCompare(b.name); - } + // Function to calculate depth + const calculateDepth = (node: any, visited: Set = new Set()): number => { + if (visited.has(node.id)) return 0; // Prevent cycles + visited.add(node.id); + + if (!node.__parent) return 0; + return 1 + calculateDepth(node.__parent, visited); + }; - return (b.__children?.length || 0) - (a.__children?.length || 0); - }) - .flatMap((child: any) => flattenTree(child, depth + 1)), - ]; - } + // Calculate depths + Object.values(nodes).forEach((node: any) => { + node.__depth = calculateDepth(node); + }); - // Flatten and assign indices - const flatResult = result.flatMap((node) => flattenTree(node)); - flatResult.forEach((node, index) => { + // Sort nodes + const sortedNodes = Object.values(nodes).sort((a: any, b: any) => { + if (a.__depth !== b.__depth) return a.__depth - b.__depth; + if (a.__children.length !== b.__children.length) { + return b.__children.length - a.__children.length; + } + return a.name.localeCompare(b.name); + }); + + // Assign indices + sortedNodes.forEach((node: any, index: number) => { node.idx = index; }); - return flatResult; -}; - -// export const sortTree = (list: any[], parent_key: string, pk: string) => { -// let meta = {} as Record< -// string, -// { item: any; idx: string; depth: number; id_parent: any } -// >; - -// let mode = "" as "" | "str" | "num"; -// let _list = list.sort((a, b) => { -// if (!mode) { -// mode = typeof a[pk] === "string" ? "str" : "num"; -// } - -// if (mode === "str") return b[pk].toLocaleString(a[pk]); - -// return a[pk] - b[pk]; -// }); - -// 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; -// }; + return sortedNodes; +}; \ No newline at end of file diff --git a/comps/md/gen/gen-table-list.ts b/comps/md/gen/gen-table-list.ts index 16a653f..788ac12 100755 --- a/comps/md/gen/gen-table-list.ts +++ b/comps/md/gen/gen-table-list.ts @@ -181,13 +181,6 @@ const genTable = async (opt: GenOpt) => { return; } if (e.is_pk && (arg.mode === "table" || arg.mode === "auto")) 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", @@ -211,10 +204,10 @@ const genTable = async (opt: GenOpt) => { adv: { js: `\
- +
`, jsBuilt: `\ -render(React.createElement("div", Object.assign({}, props, { className: cx(props.className, \`s-\${_item?.edit?.parent?.item?.id}\` , "") }),React.createElement(FormatValue, { value: col.value, name: col.name, gen_fields: gen__fields, ${tree_depth_built} }))); +render(React.createElement("div", Object.assign({}, props, { className: cx(props.className, \`s-\${_item?.edit?.parent?.item?.id}\` , "") }),React.createElement(FormatValue, { value: col.value, name: col.name, gen_fields: gen__fields }))); `, }, }), diff --git a/comps/md/gen/tbl-list/on_load.ts b/comps/md/gen/tbl-list/on_load.ts index f47e814..cf3f780 100755 --- a/comps/md/gen/tbl-list/on_load.ts +++ b/comps/md/gen/tbl-list/on_load.ts @@ -55,6 +55,10 @@ async (arg: TableOnLoad) => { const fields = parseGenField(gen__fields); const gen = generateSelect(fields); + if (opt__feature.includes("tree") && opt__id_parent) { + gen.select[opt__id_parent] = true + } + const result = {items: []} result.items = await db.${table}.findMany({ select: gen.select, diff --git a/utils/format-value.tsx b/utils/format-value.tsx index d5551c4..cda2a67 100755 --- a/utils/format-value.tsx +++ b/utils/format-value.tsx @@ -11,10 +11,9 @@ export const FormatValue: FC<{ value: any; name: string; gen_fields: string[]; - tree_depth?: number; mode?: "money" | "datetime" | "timeago" | "date"; }> = (prop) => { - const { value, gen_fields, name, tree_depth, mode } = prop; + const { value, gen_fields, name, mode } = prop; if (gen_fields) { const gf = JSON.stringify(gen_fields); if (!fields_map.has(gf)) { @@ -122,29 +121,28 @@ export const FormatValue: FC<{ } } - let prefix = <>; - if (typeof tree_depth === "number" && tree_depth > 0) { - prefix = ( -
-
-
- ); - } + // let prefix = <>; + // if (typeof tree_depth === "number" && tree_depth > 0) { + // prefix = ( + //
+ //
+ //
+ // ); + // } return (
- {prefix}
{value}
);