fix
This commit is contained in:
parent
c98e77afa6
commit
fa44a5b8d8
|
|
@ -46,6 +46,7 @@ export const FieldInput: FC<{
|
|||
|
||||
const props = found.component.props;
|
||||
let should_update = false;
|
||||
|
||||
for (const [k, v] of Object.entries(item.component.props) as any) {
|
||||
if (props[k] && props[k].valueBuilt === v.valueBuilt) {
|
||||
continue;
|
||||
|
|
@ -88,9 +89,8 @@ export const FieldInput: FC<{
|
|||
: field.focused &&
|
||||
"c-border-blue-700 c-outline c-outline-blue-700",
|
||||
css`
|
||||
> div {
|
||||
& > .field-inner {
|
||||
min-height: 35px;
|
||||
line-height: 35px;
|
||||
}
|
||||
`
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { Skeleton } from "@/comps/ui/skeleton";
|
|||
|
||||
export const FieldLoading = () => {
|
||||
return (
|
||||
<div className="c-flex c-flex-col c-space-y-1 c-p-1 c-justify-center">
|
||||
<div className="field-inner c-flex c-flex-col c-space-y-1 c-p-1 c-justify-center">
|
||||
<div className="c-flex c-space-x-1">
|
||||
<Skeleton
|
||||
className={css`
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@ export type PropTypeRelation = {
|
|||
on_load: (opt: { value?: any }) => Promise<{ items: any[]; pk: string }>;
|
||||
label: (item: any, pk: string) => string;
|
||||
id_parent: string;
|
||||
has_many: "checkbox" | "typeahead";
|
||||
has_many_list: (opt: {
|
||||
value?: any;
|
||||
}) => Promise<{ value: string; label: string }[]>;
|
||||
};
|
||||
export const FieldTypeRelation: FC<{
|
||||
field: FieldLocal;
|
||||
|
|
@ -17,6 +21,79 @@ export const FieldTypeRelation: FC<{
|
|||
prop: PropTypeRelation;
|
||||
PassProp: any;
|
||||
child: any;
|
||||
}> = (props) => {
|
||||
if (props.prop.type === "has-one") return <HasOne {...props} />;
|
||||
return <HasMany {...props} />;
|
||||
};
|
||||
|
||||
const HasMany: FC<{
|
||||
field: FieldLocal;
|
||||
fm: FMLocal;
|
||||
prop: PropTypeRelation;
|
||||
PassProp: any;
|
||||
child: any;
|
||||
}> = ({ field, fm, prop, PassProp, child }) => {
|
||||
const input = useLocal({
|
||||
list: null as null | any[],
|
||||
many: [] as { value: string; label: string }[],
|
||||
pk: "",
|
||||
});
|
||||
const value = fm.data[field.name];
|
||||
field.input = input;
|
||||
field.prop = prop;
|
||||
|
||||
useEffect(() => {
|
||||
if (!isEditor && input.list === null) {
|
||||
field.status = "loading";
|
||||
field.render();
|
||||
|
||||
const callback = (arg: { items: any[]; pk: string }) => {
|
||||
input.list = arg.items;
|
||||
input.pk = arg.pk;
|
||||
field.status = "ready";
|
||||
input.render();
|
||||
};
|
||||
const res = prop.on_load({ value });
|
||||
if (res instanceof Promise) res.then(callback);
|
||||
else callback(res);
|
||||
|
||||
const many_list_loaded = (arg: { value: string; label: string }[]) => {
|
||||
input.many = arg;
|
||||
input.render();
|
||||
};
|
||||
const many_res = prop.has_many_list({ value });
|
||||
if (res instanceof Promise) many_res.then(many_list_loaded);
|
||||
else many_list_loaded(res);
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (isEditor) {
|
||||
input.many = [
|
||||
{ value: "sample 1", label: "sample 1" },
|
||||
{ value: "sample 2", label: "sample 2" },
|
||||
];
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="c-flex c-flex-col c-p-2 c-items-stretch">
|
||||
{input.many.map((e, idx) => {
|
||||
return (
|
||||
<label key={idx} className="c-flex c-items-center c-space-x-1">
|
||||
<input type="checkbox" value={e.value} />
|
||||
<div className="c-flex-1">{e.label}</div>
|
||||
</label>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const HasOne: FC<{
|
||||
field: FieldLocal;
|
||||
fm: FMLocal;
|
||||
prop: PropTypeRelation;
|
||||
PassProp: any;
|
||||
child: any;
|
||||
}> = ({ field, fm, prop, PassProp, child }) => {
|
||||
const input = useLocal({
|
||||
list: null as null | any[],
|
||||
|
|
@ -27,7 +104,7 @@ export const FieldTypeRelation: FC<{
|
|||
field.prop = prop;
|
||||
|
||||
useEffect(() => {
|
||||
if (input.list === null) {
|
||||
if (!isEditor && input.list === null) {
|
||||
field.status = "loading";
|
||||
field.render();
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ export type FMProps = {
|
|||
label_mode: "vertical" | "horizontal" | "hidden";
|
||||
label_width: number;
|
||||
gen_fields: any;
|
||||
gen_table: string;
|
||||
};
|
||||
|
||||
export type FieldProp = {
|
||||
|
|
|
|||
|
|
@ -61,7 +61,9 @@ export const gen_form = async (modify: (data: any) => void, data: any) => {
|
|||
result["body"] = data["body"];
|
||||
result.body.content.childs = [];
|
||||
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, { parent_table: table })
|
||||
);
|
||||
}
|
||||
}
|
||||
modify(result);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { createId } from "@paralleldrive/cuid2";
|
||||
import { GFCol, createItem, formatName } from "../utils";
|
||||
import { gen_relation } from "../gen_relation/gen_relation";
|
||||
import { FMLocal } from "@/comps/form/typings";
|
||||
export const newItem = (component: {
|
||||
id: string;
|
||||
props: Record<string, string>;
|
||||
|
|
@ -29,7 +30,7 @@ export type NewFieldArg = {
|
|||
};
|
||||
};
|
||||
|
||||
export const newField = async (arg: GFCol) => {
|
||||
export const newField = async (arg: GFCol, opt: { parent_table: string }) => {
|
||||
const childs = [];
|
||||
|
||||
let type = "text";
|
||||
|
|
@ -62,11 +63,18 @@ export const newField = async (arg: GFCol) => {
|
|||
label: [`() => {}`],
|
||||
gen_table: arg.relation.to.table,
|
||||
gen_fields: [value, value],
|
||||
has_many_from:
|
||||
arg.type === "has-many" ? arg.relation.from.table : undefined,
|
||||
has_many_list: arg.type === "has-many" ? [`null`] : undefined,
|
||||
child: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
await gen_relation(() => {}, item.component.props);
|
||||
await gen_relation(() => {}, item.component.props, {
|
||||
id_parent: "",
|
||||
type: arg.type as any,
|
||||
parent_table: opt.parent_table,
|
||||
});
|
||||
childs.push(item);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { codeBuild } from "../master_detail/utils";
|
||||
import { gen_prop_fields } from "../prop/gen_prop_fields";
|
||||
import { GFCol, parseGenField } from "../utils";
|
||||
import { newField } from "./new_field";
|
||||
import { on_load } from "./on_load";
|
||||
|
|
@ -6,19 +7,42 @@ import { on_load } from "./on_load";
|
|||
export const gen_relation = async (
|
||||
modify: (data: any) => void,
|
||||
data: any,
|
||||
arg: { id_parent: string }
|
||||
arg: { id_parent: string; type: "has-many" | "has-one"; parent_table: string }
|
||||
) => {
|
||||
const table = JSON.parse(data.gen_table.value);
|
||||
const raw_fields = JSON.parse(data.gen_fields.value) as (
|
||||
| string
|
||||
| { value: string; checked: string[] }
|
||||
)[];
|
||||
const select = {} as any;
|
||||
let pk: null | GFCol = null;
|
||||
let pks: Record<string, string> = {};
|
||||
|
||||
const fields = parseGenField(raw_fields);
|
||||
if (arg.type === "has-one") {
|
||||
await genHasOne(modify, data, arg, { table, fields });
|
||||
} else {
|
||||
await genHasMany(modify, data, arg, { table, fields });
|
||||
}
|
||||
};
|
||||
|
||||
const genHasMany = async (
|
||||
modify: (data: any) => void,
|
||||
data: any,
|
||||
arg: {
|
||||
id_parent: string;
|
||||
type: "has-many" | "has-one";
|
||||
parent_table: string;
|
||||
},
|
||||
pass: {
|
||||
table: string;
|
||||
fields: GFCol[];
|
||||
}
|
||||
) => {
|
||||
const { table, fields } = pass;
|
||||
|
||||
let pk: null | GFCol = null;
|
||||
let pks: Record<string, string> = {};
|
||||
const select = {} as any;
|
||||
const result = {} as any;
|
||||
|
||||
for (const f of fields) {
|
||||
select[f.name] = true;
|
||||
if (f.relation) {
|
||||
|
|
@ -35,7 +59,119 @@ export const gen_relation = async (
|
|||
}
|
||||
}
|
||||
|
||||
if (arg.id_parent) {
|
||||
if (arg && arg.id_parent) {
|
||||
select[arg.id_parent] = true;
|
||||
}
|
||||
|
||||
if (!pk) {
|
||||
alert("Failed to generate! Primary Key not found. ");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pk) {
|
||||
const code = {} as any;
|
||||
if (data["on_load"]) {
|
||||
result["on_load"] = data["on_load"];
|
||||
result["on_load"].value = on_load({
|
||||
pk,
|
||||
pks,
|
||||
select,
|
||||
table,
|
||||
id_parent: arg.id_parent,
|
||||
});
|
||||
code.on_load = result["on_load"].value;
|
||||
}
|
||||
|
||||
if (data["has_many_from"] && arg.parent_table) {
|
||||
result["has_many_from"] = data["has_many_from"];
|
||||
result["has_many_from"].value = `"${arg.parent_table}"`;
|
||||
result["has_many_from"].valueBuilt = `"${arg.parent_table}"`;
|
||||
}
|
||||
|
||||
if (data["has_many_list"] && arg.parent_table) {
|
||||
const defs = parseGenField(
|
||||
(await gen_prop_fields(arg.parent_table)).map((e: any) => e.value)
|
||||
);
|
||||
const pk = defs.find((e) => e.is_pk);
|
||||
|
||||
result["has_many_list"] = data["has_many_list"];
|
||||
result["has_many_list"].value = `\
|
||||
async () => {
|
||||
const result: { value: string; label: string }[] = [];
|
||||
const list = await db.${arg.parent_table}.findMany({
|
||||
select: {
|
||||
${pk}: true,
|
||||
},
|
||||
where: { },
|
||||
});
|
||||
return result;
|
||||
}`;
|
||||
code.has_many_list = result["has_many_list"].value;
|
||||
}
|
||||
|
||||
if (data["label"]) {
|
||||
result["label"] = data["label"];
|
||||
result["label"].value = `\
|
||||
(item:any, pk:string) => {
|
||||
return \`${Object.entries(select)
|
||||
.filter(([k, v]) => {
|
||||
if (typeof v !== "object" && k !== pk?.name && k !== arg.id_parent) {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
.map(([name]) => {
|
||||
return `\${item.${name}}`;
|
||||
})
|
||||
.join(" ")}\`
|
||||
}`;
|
||||
code.on_load = result["on_load"].value;
|
||||
}
|
||||
|
||||
const res = await codeBuild(code);
|
||||
for (const [k, v] of Object.entries(res)) {
|
||||
result[k].valueBuilt = v[1];
|
||||
}
|
||||
|
||||
result["child"] = data["child"];
|
||||
result.child.content.childs = fields.filter((e) => !e.is_pk).map(newField);
|
||||
}
|
||||
|
||||
modify(result);
|
||||
};
|
||||
|
||||
const genHasOne = async (
|
||||
modify: (data: any) => void,
|
||||
data: any,
|
||||
arg: { id_parent: string; type: "has-many" | "has-one" },
|
||||
pass: {
|
||||
table: string;
|
||||
fields: GFCol[];
|
||||
}
|
||||
) => {
|
||||
const { table, fields } = pass;
|
||||
|
||||
let pk: null | GFCol = null;
|
||||
let pks: Record<string, string> = {};
|
||||
const select = {} as any;
|
||||
const result = {} as any;
|
||||
|
||||
for (const f of fields) {
|
||||
select[f.name] = true;
|
||||
if (f.relation) {
|
||||
select[f.name] = {
|
||||
select: {},
|
||||
};
|
||||
for (const r of f.relation.fields) {
|
||||
select[f.name].select[r.name] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (f.is_pk) {
|
||||
pk = f;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg && arg.id_parent) {
|
||||
select[arg.id_parent] = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,43 @@
|
|||
const cache = {} as Record<string, any>;
|
||||
const single = {} as Record<
|
||||
string,
|
||||
{
|
||||
cols: Record<
|
||||
string,
|
||||
{
|
||||
is_pk: boolean;
|
||||
type: string;
|
||||
optional: boolean;
|
||||
db_type: string;
|
||||
default?: any;
|
||||
}
|
||||
>;
|
||||
rels: Record<
|
||||
string,
|
||||
{
|
||||
type: "has-many" | "has-one";
|
||||
to: {
|
||||
table: string;
|
||||
fields: string[];
|
||||
};
|
||||
from: {
|
||||
table: string;
|
||||
fields: string[];
|
||||
};
|
||||
}
|
||||
>;
|
||||
}
|
||||
>;
|
||||
|
||||
const load_single = async (table: string) => {
|
||||
if (!single[table]) {
|
||||
single[table] = {
|
||||
cols: await db._schema.columns(table),
|
||||
rels: await db._schema.rels(table),
|
||||
};
|
||||
}
|
||||
return single[table];
|
||||
};
|
||||
|
||||
export const gen_prop_fields = async (gen_table: string) => {
|
||||
if (cache[gen_table]) return cache[gen_table];
|
||||
|
|
@ -9,27 +48,10 @@ export const gen_prop_fields = async (gen_table: string) => {
|
|||
options?: any[];
|
||||
checked?: boolean;
|
||||
}[] = [];
|
||||
const fields = await db._schema.columns(gen_table);
|
||||
for (const [k, v] of Object.entries(fields)) {
|
||||
result.push({
|
||||
value: JSON.stringify({
|
||||
name: k,
|
||||
is_pk: v.is_pk,
|
||||
type: v.db_type || v.type,
|
||||
optional: v.optional,
|
||||
}),
|
||||
label: k,
|
||||
checked: v.is_pk,
|
||||
});
|
||||
}
|
||||
const rels = await db._schema.rels(gen_table);
|
||||
for (const [k, v] of Object.entries(rels)) {
|
||||
let options = [];
|
||||
const to = v.to;
|
||||
const from = v.from;
|
||||
const fields = await db._schema.columns(v.to.table);
|
||||
for (const [k, v] of Object.entries(fields)) {
|
||||
options.push({
|
||||
const { cols, rels } = await load_single(gen_table);
|
||||
if (cols) {
|
||||
for (const [k, v] of Object.entries(cols) as any) {
|
||||
result.push({
|
||||
value: JSON.stringify({
|
||||
name: k,
|
||||
is_pk: v.is_pk,
|
||||
|
|
@ -40,22 +62,125 @@ export const gen_prop_fields = async (gen_table: string) => {
|
|||
checked: v.is_pk,
|
||||
});
|
||||
}
|
||||
result.push({
|
||||
value: JSON.stringify({
|
||||
name: k,
|
||||
is_pk: false,
|
||||
type: v.type,
|
||||
optional: true,
|
||||
relation: { from, to },
|
||||
}),
|
||||
label: k,
|
||||
options,
|
||||
});
|
||||
}
|
||||
|
||||
if (!cache[gen_table]) {
|
||||
cache[gen_table] = result;
|
||||
if (rels) {
|
||||
for (const [k, v] of Object.entries(rels)) {
|
||||
let options = [];
|
||||
const to = v.to;
|
||||
const from = v.from;
|
||||
const { cols, rels } = await load_single(v.to.table);
|
||||
if (cols) {
|
||||
for (const [k, v] of Object.entries(cols)) {
|
||||
options.push({
|
||||
value: JSON.stringify({
|
||||
name: k,
|
||||
is_pk: v.is_pk,
|
||||
type: v.db_type || v.type,
|
||||
optional: v.optional,
|
||||
}),
|
||||
label: k,
|
||||
checked: v.is_pk,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (rels) {
|
||||
for (const [k, v] of Object.entries(rels)) {
|
||||
let sub_opt = [];
|
||||
const to = v.to;
|
||||
const from = v.from;
|
||||
const { cols } = await load_single(v.to.table);
|
||||
for (const [k, v] of Object.entries(cols)) {
|
||||
sub_opt.push({
|
||||
value: JSON.stringify({
|
||||
name: k,
|
||||
is_pk: v.is_pk,
|
||||
type: v.db_type || v.type,
|
||||
optional: v.optional,
|
||||
}),
|
||||
label: k,
|
||||
checked: v.is_pk,
|
||||
});
|
||||
}
|
||||
options.push({
|
||||
value: JSON.stringify({
|
||||
name: k,
|
||||
is_pk: false,
|
||||
type: v.type,
|
||||
optional: true,
|
||||
relation: { from, to },
|
||||
}),
|
||||
label: k,
|
||||
options: sub_opt,
|
||||
});
|
||||
}
|
||||
}
|
||||
result.push({
|
||||
value: JSON.stringify({
|
||||
name: k,
|
||||
is_pk: false,
|
||||
type: v.type,
|
||||
optional: true,
|
||||
relation: { from, to },
|
||||
}),
|
||||
label: k,
|
||||
options,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// const result: {
|
||||
// label: string;
|
||||
// value: string;
|
||||
// options?: any[];
|
||||
// checked?: boolean;
|
||||
// }[] = [];
|
||||
// const fields = await db._schema.columns(gen_table);
|
||||
// for (const [k, v] of Object.entries(fields)) {
|
||||
// result.push({
|
||||
// value: JSON.stringify({
|
||||
// name: k,
|
||||
// is_pk: v.is_pk,
|
||||
// type: v.db_type || v.type,
|
||||
// optional: v.optional,
|
||||
// }),
|
||||
// label: k,
|
||||
// checked: v.is_pk,
|
||||
// });
|
||||
// }
|
||||
// const rels = await db._schema.rels(gen_table);
|
||||
// for (const [k, v] of Object.entries(rels)) {
|
||||
// let options = [];
|
||||
// const to = v.to;
|
||||
// const from = v.from;
|
||||
// const fields = await db._schema.columns(v.to.table);
|
||||
// for (const [k, v] of Object.entries(fields)) {
|
||||
// options.push({
|
||||
// value: JSON.stringify({
|
||||
// name: k,
|
||||
// is_pk: v.is_pk,
|
||||
// type: v.db_type || v.type,
|
||||
// optional: v.optional,
|
||||
// }),
|
||||
// label: k,
|
||||
// checked: v.is_pk,
|
||||
// });
|
||||
// }
|
||||
// result.push({
|
||||
// value: JSON.stringify({
|
||||
// name: k,
|
||||
// is_pk: false,
|
||||
// type: v.type,
|
||||
// optional: true,
|
||||
// relation: { from, to },
|
||||
// }),
|
||||
// label: k,
|
||||
// options,
|
||||
// });
|
||||
// }
|
||||
|
||||
// if (!cache[gen_table]) {
|
||||
// cache[gen_table] = result;
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -60,7 +60,10 @@ type SimplifiedItem = {
|
|||
name?: string;
|
||||
component?: {
|
||||
id: string;
|
||||
props: Record<string, string | SimplifiedItem | [any] | [any, any]>;
|
||||
props: Record<
|
||||
string,
|
||||
string | SimplifiedItem | [any] | [any, any] | undefined
|
||||
>;
|
||||
};
|
||||
childs?: SimplifiedItem[];
|
||||
adv?: {
|
||||
|
|
@ -80,6 +83,7 @@ export const createItem = (arg: SimplifiedItem): any => {
|
|||
|
||||
if (arg.component.props) {
|
||||
for (const [k, v] of Object.entries(arg.component.props)) {
|
||||
if (v === undefined) continue;
|
||||
if (typeof v === "object") {
|
||||
if (Array.isArray(v) && v.length === 1) {
|
||||
component.props[k] = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue