This commit is contained in:
rizky 2024-04-15 12:25:34 -07:00
parent 3bc6330379
commit c98e77afa6
24 changed files with 372 additions and 105 deletions

View File

@ -3,7 +3,7 @@ import { useLocal } from "@/utils/use-local";
import { ChevronDown } from "lucide-react"; import { ChevronDown } from "lucide-react";
import { FC, ReactNode } from "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<{ export const RawDropdown: FC<{
options: OptionItem[]; options: OptionItem[];
@ -28,8 +28,18 @@ export const RawDropdown: FC<{
let filtered = options; let filtered = options;
if (local.filter) { if (local.filter) {
filtered = options.filter((e) => { filtered = options.filter((e: any) => {
if (e.label.toLowerCase().includes(local.filter)) return true; 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; return false;
}); });
} }
@ -118,7 +128,7 @@ export const RawDropdown: FC<{
{!isEditor && ( {!isEditor && (
<input <input
spellCheck={false} spellCheck={false}
value={local.open ? local.input.value : "Halo"} value={local.open ? local.input.value : ""}
className={cx( className={cx(
"c-absolute c-inset-0 c-w-full c-h-full c-outline-none c-p-0", "c-absolute c-inset-0 c-w-full c-h-full c-outline-none c-p-0",
disabled disabled

View File

@ -17,13 +17,26 @@ export const TypeCustom: FC<{ field: FieldLocal; fm: FMLocal }> = ({
local.custom = field.custom; local.custom = field.custom;
} }
if (field.custom) { if (!local.exec) {
const res = local.custom(); local.exec = true;
if (res instanceof Promise) { const callback = (value: any, should_render: boolean) => {
console.error("Custom Function cannot be async"); local.result = value;
return null; if (should_render) {
} else { local.render();
local.result = res; 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);
}
} }
} }

View File

@ -3,11 +3,13 @@ import { FC, useEffect } from "react";
import { FMLocal, FieldLocal } from "../../typings"; import { FMLocal, FieldLocal } from "../../typings";
import { OptionItem, RawDropdown } from "../raw/Dropdown"; import { OptionItem, RawDropdown } from "../raw/Dropdown";
import { FieldLoading } from "../raw/FieldLoading"; import { FieldLoading } from "../raw/FieldLoading";
import { sortTree } from "@/comps/list/sort-tree";
export type PropTypeRelation = { export type PropTypeRelation = {
type: "has-one" | "has-many"; type: "has-one" | "has-many";
on_load: (opt: { value?: any }) => Promise<{ items: any[]; pk: string }>; on_load: (opt: { value?: any }) => Promise<{ items: any[]; pk: string }>;
label: (item: any, pk: string) => string; label: (item: any, pk: string) => string;
id_parent: string;
}; };
export const FieldTypeRelation: FC<{ export const FieldTypeRelation: FC<{
field: FieldLocal; field: FieldLocal;
@ -35,7 +37,7 @@ export const FieldTypeRelation: FC<{
field.status = "ready"; field.status = "ready";
input.render(); input.render();
}; };
const res = prop.on_load({}); const res = prop.on_load({ value });
if (res instanceof Promise) res.then(callback); if (res instanceof Promise) res.then(callback);
else callback(res); else callback(res);
} }
@ -43,12 +45,33 @@ export const FieldTypeRelation: FC<{
let list: OptionItem[] = []; let list: OptionItem[] = [];
if (input.list && input.pk && input.list.length) { 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; if (typeof item !== "object") continue;
let label = ""; let label = "";
if (typeof prop.label === "function") { if (typeof prop.label === "function") {
label = prop.label(item, input.pk); 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 { } else {
const label_arr: string[] = []; const label_arr: string[] = [];
@ -67,7 +90,7 @@ export const FieldTypeRelation: FC<{
} }
let selected = null; let selected = null;
if (typeof value === "object") { if (value && typeof value === "object") {
if (input.pk) selected = value[input.pk]; if (input.pk) selected = value[input.pk];
} else { } else {
selected = value; selected = value;
@ -82,6 +105,11 @@ export const FieldTypeRelation: FC<{
options={list} options={list}
value={selected} value={selected}
onChange={(val) => { onChange={(val) => {
if (val === null) {
fm.data[field.name] = null;
fm.render();
return;
}
if (input.list && input.pk) { if (input.list && input.pk) {
for (const item of input.list) { for (const item of input.list) {
if (item[input.pk] === val) { if (item[input.pk] === val) {

View File

@ -151,6 +151,7 @@ export const formType = (active: { item_id: string }, meta: any) => {
data: typeof ___data; data: typeof ___data;
reload: () => Promise<void>; reload: () => Promise<void>;
submit: () => Promise<void>; submit: () => Promise<void>;
render: () => void;
error: { error: {
list: { name: string; error: string }[]; list: { name: string; error: string }[];
set: (name: string, error: string) => void; set: (name: string, error: string) => void;
@ -165,7 +166,7 @@ export const formType = (active: { item_id: string }, meta: any) => {
}; };
}; };
props: any; props: any;
size: { size: {
width: number; width: number;
height: number; height: number;
field: "full" | "half"; field: "full" | "half";

View File

@ -14,12 +14,10 @@ export const formInit = (fm: FMLocal, props: FMProps) => {
const { on_load, sonar } = fm.props; const { on_load, sonar } = fm.props;
fm.error = formError(fm); fm.error = formError(fm);
if (isEditor) { fm.field_def = {};
fm.field_def = {}; const defs = parseGenField(fm.props.gen_fields);
const defs = parseGenField(fm.props.gen_fields); for (const d of defs) {
for (const d of defs) { fm.field_def[d.name] = d;
fm.field_def[d.name] = d;
}
} }
fm.reload = () => { fm.reload = () => {

View File

@ -14,6 +14,8 @@ import { createPortal } from "react-dom";
import { Toaster, toast } from "sonner"; import { Toaster, toast } from "sonner";
import { getProp } from "../md/utils/get-prop"; import { getProp } from "../md/utils/get-prop";
import { Skeleton } from "../ui/skeleton"; import { Skeleton } from "../ui/skeleton";
import { sortTree } from "./sort-tree";
import { GFCol, parseGenField } from "@/gen/utils";
type OnRowClick = (arg: { type OnRowClick = (arg: {
row: any; row: any;
@ -36,6 +38,7 @@ type TableListProp = {
_meta: Record<string, any>; _meta: Record<string, any>;
gen_fields: string[]; gen_fields: string[];
row_click: OnRowClick; row_click: OnRowClick;
id_parent?: string;
}; };
export const TableList: FC<TableListProp> = ({ export const TableList: FC<TableListProp> = ({
@ -47,6 +50,7 @@ export const TableList: FC<TableListProp> = ({
_meta, _meta,
gen_fields, gen_fields,
row_click, row_click,
id_parent,
}) => { }) => {
const local = useLocal({ const local = useLocal({
el: null as null | HTMLDivElement, el: null as null | HTMLDivElement,
@ -58,6 +62,7 @@ export const TableList: FC<TableListProp> = ({
if (local.status === "ready") local.status = "resizing"; if (local.status === "ready") local.status = "resizing";
local.render(); local.render();
}), }),
pk: null as null | GFCol,
scrolled: false, scrolled: false,
data: [] as any[], data: [] as any[],
status: "init" as "loading" | "ready" | "resizing" | "reload" | "init", status: "init" as "loading" | "ready" | "resizing" | "reload" | "init",
@ -91,14 +96,23 @@ export const TableList: FC<TableListProp> = ({
if (fields) { if (fields) {
const rel = fields?.find((e) => e.name === columnKey); const rel = fields?.find((e) => e.name === columnKey);
if (rel && rel.checked) { if (rel && rel.checked) {
const field = rel.checked.find((e) => !e.is_pk); should_set = false;
if (field) {
should_set = false; if (rel.type === "has-many") {
local.sort.orderBy = { local.sort.orderBy = {
[columnKey]: { [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<TableListProp> = ({
local.render(); local.render();
const orderBy = local.sort.orderBy || undefined; const orderBy = local.sort.orderBy || undefined;
const load_args = { const load_args: any = {
async reload() {}, async reload() {},
orderBy, orderBy,
paging: { take: local.paging.take, skip: local.paging.skip }, paging: { take: local.paging.take, skip: local.paging.skip },
}; };
if (id_parent) {
load_args.paging = {};
}
const result = on_load({ ...load_args, mode: "query" }); const result = on_load({ ...load_args, mode: "query" });
const callback = (data: any[]) => { const callback = (data: any[]) => {
@ -190,6 +207,7 @@ export const TableList: FC<TableListProp> = ({
col={{ col={{
name: props.column.key, name: props.column.key,
value: props.row[props.column.key], value: props.row[props.column.key],
depth: props.row.__depth || 0,
}} }}
rows={local.data} rows={local.data}
> >
@ -233,12 +251,23 @@ export const TableList: FC<TableListProp> = ({
} }
const toaster_el = document.getElementsByClassName("prasi-toaster")[0]; 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 (isEditor && local.status !== "ready") {
if (local.data.length === 0) { if (local.data.length === 0) {
const load_args = { const load_args: any = {
async reload() {}, async reload() {},
paging: { take: local.paging.take, skip: local.paging.skip }, paging: { take: local.paging.take, skip: local.paging.skip },
}; };
if (id_parent) load_args.paging = {};
if (typeof on_load === "function") { if (typeof on_load === "function") {
local.data = on_load({ ...load_args, mode: "query" }) as any; local.data = on_load({ ...load_args, mode: "query" }) as any;
} }
@ -248,6 +277,11 @@ export const TableList: FC<TableListProp> = ({
let selected_idx = -1; 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") { if (mode === "table") {
return ( return (
<div <div
@ -300,7 +334,7 @@ export const TableList: FC<TableListProp> = ({
sortColumns={local.sort.columns} sortColumns={local.sort.columns}
onSortColumnsChange={local.sort.on_change} onSortColumnsChange={local.sort.on_change}
columns={columns} columns={columns}
rows={local.data || []} rows={data}
onScroll={local.paging.scroll} onScroll={local.paging.scroll}
renderers={ renderers={
local.status !== "ready" local.status !== "ready"

83
comps/list/sort-tree.ts Executable file
View File

@ -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<string, any>;
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<string, number>;
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;
};

View File

@ -28,12 +28,16 @@ export const MDAction: FC<{ md: MDLocal; PassProp: any; child: any }> = ({
} }
return ( return (
<div className={cx("c-flex c-flex-row c-space-x-1")}> <div
className={cx(
"c-flex c-flex-row c-space-x-1 c-items-stretch c-self-stretch c-h-full"
)}
>
{md.actions.map((e, idx) => { {md.actions.map((e, idx) => {
if (isValidElement(e)) { if (isValidElement(e)) {
return <Fragment key={idx}>{e}</Fragment>; return <Fragment key={idx}>{e}</Fragment>;
} }
if (typeof e === "object" && e.label) { if (typeof e === "object" && (e.action || e.label)) {
return <PassProp item={e}>{child}</PassProp>; return <PassProp item={e}>{child}</PassProp>;
} }
})} })}

View File

@ -16,7 +16,6 @@ export const MDTab: FC<{ md: MDLocal; mdr: MDRef }> = ({ md, mdr }) => {
return null; return null;
} }
return ( return (
<> <>
{md.props.show_head === "only-child" && <MDHeader md={md} mdr={mdr} />} {md.props.show_head === "only-child" && <MDHeader md={md} mdr={mdr} />}
@ -41,7 +40,7 @@ export const MDNavTab: FC<{ md: MDLocal; mdr: MDRef }> = ({ md, mdr }) => {
<div <div
className={cx( className={cx(
"tab-list c-flex c-text-sm", "tab-list c-flex c-text-sm",
mode === "v-tab" && "c-flex-row c-border-b c-pl-2", mode === "v-tab" && "c-flex-row c-border-b c-pl-2 c-pt-2",
mode === "h-tab" && "c-flex-col c-border-r c-pl-2 c-min-w-[100px]" mode === "h-tab" && "c-flex-col c-border-r c-pl-2 c-min-w-[100px]"
)} )}
> >

View File

@ -44,7 +44,7 @@ export const MasterDetail: FC<{
active: "", active: "",
list: [], list: [],
}, },
internal: { action_should_refresh: false }, internal: { action_should_refresh: true },
childs: {}, childs: {},
props: { props: {
mode, mode,
@ -65,7 +65,7 @@ export const MasterDetail: FC<{
masterDetailApplyParams(md); masterDetailApplyParams(md);
}, },
}, },
master: { internal: null, render() {}, pk: null }, master: { internal: null, render() {} },
}); });
const local = useLocal({ init: false }); const local = useLocal({ init: false });
if (isEditor) { if (isEditor) {

View File

@ -36,7 +36,7 @@ export const masterDetailApplyParams = (md: MDLocal) => {
} }
} }
const pk = md.master.pk; const pk = md.pk;
if (pk && row[pk.name]) { if (pk && row[pk.name]) {
md.params.hash[md.name] = row[pk.name]; md.params.hash[md.name] = row[pk.name];
} }

View File

@ -30,7 +30,7 @@ export const masterDetailInit = (
md.master.internal = child; md.master.internal = child;
const pk = parseGenField(md.props.gen_fields).find((e) => e.is_pk); const pk = parseGenField(md.props.gen_fields).find((e) => e.is_pk);
if (pk) { if (pk) {
md.master.pk = pk; md.pk = pk;
} }
} }
if (cid === "cb52075a-14ab-455a-9847-6f1d929a2a73") { if (cid === "cb52075a-14ab-455a-9847-6f1d929a2a73") {
@ -62,7 +62,7 @@ export const masterDetailInit = (
export const masterDetailSelected = (md: MDLocal) => { export const masterDetailSelected = (md: MDLocal) => {
md.params.parse(); md.params.parse();
const pk = md.master.pk; const pk = md.pk;
if (pk) { if (pk) {
const value = md.params.hash[md.name]; const value = md.params.hash[md.name];
if (value) { if (value) {

View File

@ -18,13 +18,14 @@ export type MDLocalInternal = {
list: string[]; list: string[];
}; };
internal: { action_should_refresh: boolean }; internal: { action_should_refresh: boolean };
master: { internal: any; render: () => void; pk: null | GFCol }; master: { internal: any; render: () => void };
params: { params: {
hash: any; hash: any;
tabs: any; tabs: any;
parse: () => void; parse: () => void;
apply: () => void; apply: () => void;
}; };
pk?: GFCol;
props: { props: {
mode: "full" | "h-split" | "v-split"; mode: "full" | "h-split" | "v-split";
show_head: "always" | "only-master" | "only-child" | "hidden"; show_head: "always" | "only-master" | "only-child" | "hidden";
@ -75,19 +76,28 @@ export const MasterDetailType = `const md = {
parse: () => void; parse: () => void;
apply: () => 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 }; internal: { action_should_refresh: boolean };
render: () => void; render: () => void;
master: { internal: any; render: () => void; pk: null | { master: { internal: any; render: () => void; };
name: string; pk?: {
type: string; name: string;
is_pk: boolean; type: string;
optional: boolean; is_pk: boolean;
relation?: { optional: boolean;
from: { table: string; fields: string[] }; relation?: {
to: { table: string; fields: string[] }; from: { table: string; fields: string[] };
fields: GFCol[]; to: { table: string; fields: string[] };
}; fields: GFCol[];
} };
}; };
childs: Record< childs: Record<
string, string,

View File

@ -60,7 +60,6 @@ export const gen_form = async (modify: (data: any) => void, data: any) => {
result["body"] = data["body"]; result["body"] = data["body"];
result.body.content.childs = []; result.body.content.childs = [];
console.log(fields);
for (const item of fields.filter((e) => !e.is_pk)) { for (const item of fields.filter((e) => !e.is_pk)) {
result.body.content.childs.push(await newField(item)); result.body.content.childs.push(await newField(item));
} }

View File

@ -34,8 +34,8 @@ async (opt) => {
if (isEditor) return ${JSON.stringify(sample)}; if (isEditor) return ${JSON.stringify(sample)};
let raw_id = params.id; let raw_id = params.id;
if (typeof md === 'object' && md.selected && md.master?.pk) { if (typeof md === 'object' && md.selected && md.pk) {
const pk = md.master?.pk?.name; const pk = md.pk?.name;
if (md.selected[pk]) { if (md.selected[pk]) {
raw_id = md.selected[pk]; raw_id = md.selected[pk];
} }

View File

@ -34,6 +34,11 @@ async ({ form, error }: IForm) => {
const pks = ${JSON.stringify(pks)}; const pks = ${JSON.stringify(pks)};
for (const [k, v] of Object.entries(pks)) { for (const [k, v] of Object.entries(pks)) {
if (typeof data[k] === 'object') { if (typeof data[k] === 'object') {
if (data[k] === null) {
data[k] = {
disconnect: true
}
}
if (data[k][v]) { if (data[k][v]) {
data[k] = { data[k] = {
connect: { connect: {

View File

@ -3,7 +3,11 @@ import { GFCol, parseGenField } from "../utils";
import { newField } from "./new_field"; import { newField } from "./new_field";
import { on_load } from "./on_load"; 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 table = JSON.parse(data.gen_table.value);
const raw_fields = JSON.parse(data.gen_fields.value) as ( const raw_fields = JSON.parse(data.gen_fields.value) as (
| string | 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) { if (!pk) {
alert("Failed to generate! Primary Key not found. "); alert("Failed to generate! Primary Key not found. ");
return; return;
@ -40,7 +48,13 @@ export const gen_relation = async (modify: (data: any) => void, data: any) => {
const code = {} as any; const code = {} as any;
if (data["on_load"]) { if (data["on_load"]) {
result["on_load"] = 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; 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) => { (item:any, pk:string) => {
return \`${Object.entries(select) return \`${Object.entries(select)
.filter(([k, v]) => { .filter(([k, v]) => {
if (typeof v !== "object" && k !== pk?.name) { if (typeof v !== "object" && k !== pk?.name && k !== arg.id_parent) {
return true; return true;
} }
}) })

View File

@ -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 = `<FormatValue value={item["${arg.name}"]} tree_depth={item.__depth} />`;
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({ return createItem({
name: arg.name, name: arg.name,
adv: { adv: {
js: `\ js: `\
<div {...props} className={cx(props.className, "")}> <div {...props} className={cx(props.className, "")}>
{item["${arg.name}"]} ${result}
</div>`, </div>`,
jsBuilt: `render(React.createElement("div", Object.assign({}, props, { className: cx(props.className, "") }), item["${arg.name}"]));`, jsBuilt: result_built,
}, },
dim: { dim: {
h: "full", h: "full",

View File

@ -6,6 +6,7 @@ export const on_load = ({
select, select,
pks, pks,
opt, opt,
id_parent,
}: { }: {
pk: GFCol; pk: GFCol;
table: string; table: string;
@ -15,6 +16,7 @@ export const on_load = ({
before_load: string; before_load: string;
after_load: string; after_load: string;
}; };
id_parent: string;
}) => { }) => {
const sample: any = {}; const sample: any = {};
for (const [k, v] of Object.entries(select) as 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({ let items = await db.${table}.findMany({
where: !!id
? {
${pk.name}: id,
}
: undefined,
select: ${JSON.stringify(select, null, 2).split("\n").join("\n ")}, select: ${JSON.stringify(select, null, 2).split("\n").join("\n ")},
}); });

View File

@ -6,7 +6,7 @@ import { codeBuild } from "../master_detail/utils";
export const gen_table_list = async ( export const gen_table_list = async (
modify: (data: any) => void, modify: (data: any) => void,
data: any, 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 table = JSON.parse(data.gen_table.value) as string;
const raw_fields = JSON.parse(data.gen_fields.value) as ( 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) { if (!pk) {
alert("Failed to generate! Primary Key not found. "); alert("Failed to generate! Primary Key not found. ");
return; return;
@ -60,11 +64,19 @@ export const gen_table_list = async (
} }
); );
let first = true;
const child = createItem({ const child = createItem({
name: sub_name, name: sub_name,
childs: fields childs: fields
.map((e) => { .map((e) => {
if (e.is_pk) return; 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 { return {
component: { component: {
id: "297023a4-d552-464a-971d-f40dcd940b77", id: "297023a4-d552-464a-971d-f40dcd940b77",
@ -84,10 +96,10 @@ export const gen_table_list = async (
adv: { adv: {
js: `\ js: `\
<div {...props} className={cx(props.className, "")}> <div {...props} className={cx(props.className, "")}>
<FormatValue value={col.value} name={col.name} gen_fields={gen_fields} /> <FormatValue value={col.value} name={col.name} gen_fields={gen_fields} ${tree_depth} />
</div>`, </div>`,
jsBuilt: `\ 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} })));
`, `,
}, },
}), }),

View File

@ -48,6 +48,9 @@ type BreadItem = {
actions: `\ actions: `\
async () => { async () => {
return [ return [
{
action: "delete",
},
{ {
label: "Save", label: "Save",
action: "save", action: "save",
@ -68,7 +71,9 @@ async ({ submit, reload }: Init) => {
if (tab) { if (tab) {
const actions = await getProp(tab.internal, "actions", { md }); const actions = await getProp(tab.internal, "actions", { md });
if (Array.isArray(actions)) { 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) { if (save_btn) {
save_btn.onClick = async () => { save_btn.onClick = async () => {
await submit(); await submit();

View File

@ -1,4 +1,8 @@
const cache = {} as Record<string, any>;
export const gen_prop_fields = async (gen_table: string) => { export const gen_prop_fields = async (gen_table: string) => {
if (cache[gen_table]) return cache[gen_table];
const result: { const result: {
label: string; label: string;
value: string; value: string;
@ -48,5 +52,10 @@ export const gen_prop_fields = async (gen_table: string) => {
options, options,
}); });
} }
if (!cache[gen_table]) {
cache[gen_table] = result;
}
return result; return result;
}; };

View File

@ -1,6 +1,10 @@
const cache: any = [];
export const gen_props_table = async () => { export const gen_props_table = async () => {
if (cache.length > 0) return cache;
const result = [{ value: "", label: "" }]; const result = [{ value: "", label: "" }];
return [ const final = [
...result, ...result,
...(await db._schema.tables()).map((e) => ({ ...(await db._schema.tables()).map((e) => ({
value: e, value: e,
@ -8,4 +12,10 @@ export const gen_props_table = async () => {
reload: ["gen_fields", "gen_label"], reload: ["gen_fields", "gen_label"],
})), })),
]; ];
for (const f of final) {
cache.push(f);
}
return cache;
}; };

View File

@ -7,47 +7,76 @@ export const FormatValue: FC<{
value: any; value: any;
name: string; name: string;
gen_fields: string[]; gen_fields: string[];
tree_depth?: number;
}> = (prop) => { }> = (prop) => {
const { value, gen_fields, name } = prop; const { value, gen_fields, name, tree_depth } = prop;
const gf = JSON.stringify(gen_fields); if (gen_fields) {
if (!fields_map.has(gf)) { const gf = JSON.stringify(gen_fields);
fields_map.set( if (!fields_map.has(gf)) {
gf, fields_map.set(
gen_fields.map((e: any) => { gf,
if (typeof e === "string") { gen_fields.map((e: any) => {
return JSON.parse(e); if (typeof e === "string") {
} else { return JSON.parse(e);
return { } else {
...JSON.parse(e.value), return {
checked: e.checked.map(JSON.parse), ...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 = (
<div
className={css`
padding-left: ${tree_depth * 5}px;
`}
>
<div
className={cx(
" c-border-l c-border-b c-border-black c-w-[10px] c-h-[15px]",
css`
margin-top: -10px;
`
)}
></div>
</div>
); );
} }
const fields = fields_map.get(gf); return (
<div className="c-flex c-space-x-2 c-items-center">
if (typeof value === "object" && value) { {prefix}
const rel = fields?.find((e) => e.name === name); <div>{value}</div>
if (rel && rel.checked) { </div>
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}</>;
}; };