ship it
This commit is contained in:
parent
3bc6330379
commit
c98e77afa6
|
|
@ -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 && (
|
||||
<input
|
||||
spellCheck={false}
|
||||
value={local.open ? local.input.value : "Halo"}
|
||||
value={local.open ? local.input.value : ""}
|
||||
className={cx(
|
||||
"c-absolute c-inset-0 c-w-full c-h-full c-outline-none c-p-0",
|
||||
disabled
|
||||
|
|
|
|||
|
|
@ -17,13 +17,26 @@ export const TypeCustom: FC<{ field: FieldLocal; fm: FMLocal }> = ({
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -151,6 +151,7 @@ export const formType = (active: { item_id: string }, meta: any) => {
|
|||
data: typeof ___data;
|
||||
reload: () => Promise<void>;
|
||||
submit: () => Promise<void>;
|
||||
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";
|
||||
|
|
|
|||
|
|
@ -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 = () => {
|
||||
|
|
|
|||
|
|
@ -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<string, any>;
|
||||
gen_fields: string[];
|
||||
row_click: OnRowClick;
|
||||
id_parent?: string;
|
||||
};
|
||||
|
||||
export const TableList: FC<TableListProp> = ({
|
||||
|
|
@ -47,6 +50,7 @@ export const TableList: FC<TableListProp> = ({
|
|||
_meta,
|
||||
gen_fields,
|
||||
row_click,
|
||||
id_parent,
|
||||
}) => {
|
||||
const local = useLocal({
|
||||
el: null as null | HTMLDivElement,
|
||||
|
|
@ -58,6 +62,7 @@ export const TableList: FC<TableListProp> = ({
|
|||
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<TableListProp> = ({
|
|||
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<TableListProp> = ({
|
|||
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<TableListProp> = ({
|
|||
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<TableListProp> = ({
|
|||
}
|
||||
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<TableListProp> = ({
|
|||
|
||||
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 (
|
||||
<div
|
||||
|
|
@ -300,7 +334,7 @@ export const TableList: FC<TableListProp> = ({
|
|||
sortColumns={local.sort.columns}
|
||||
onSortColumnsChange={local.sort.on_change}
|
||||
columns={columns}
|
||||
rows={local.data || []}
|
||||
rows={data}
|
||||
onScroll={local.paging.scroll}
|
||||
renderers={
|
||||
local.status !== "ready"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -28,12 +28,16 @@ export const MDAction: FC<{ md: MDLocal; PassProp: any; child: any }> = ({
|
|||
}
|
||||
|
||||
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) => {
|
||||
if (isValidElement(e)) {
|
||||
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>;
|
||||
}
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ export const MDTab: FC<{ md: MDLocal; mdr: MDRef }> = ({ md, mdr }) => {
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
{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
|
||||
className={cx(
|
||||
"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]"
|
||||
)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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({
|
||||
name: arg.name,
|
||||
adv: {
|
||||
js: `\
|
||||
<div {...props} className={cx(props.className, "")}>
|
||||
{item["${arg.name}"]}
|
||||
${result}
|
||||
</div>`,
|
||||
jsBuilt: `render(React.createElement("div", Object.assign({}, props, { className: cx(props.className, "") }), item["${arg.name}"]));`,
|
||||
jsBuilt: result_built,
|
||||
},
|
||||
dim: {
|
||||
h: "full",
|
||||
|
|
|
|||
|
|
@ -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 ")},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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: `\
|
||||
<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>`,
|
||||
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} })));
|
||||
`,
|
||||
},
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
const cache = {} as Record<string, any>;
|
||||
|
||||
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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 = (
|
||||
<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);
|
||||
|
||||
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 (
|
||||
<div className="c-flex c-space-x-2 c-items-center">
|
||||
{prefix}
|
||||
<div>{value}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue