wip fix
This commit is contained in:
parent
99e6c1c530
commit
0a02fc3c0e
|
|
@ -40,6 +40,11 @@ export const Relation: FC<RelationProps> = ({
|
||||||
if (form) {
|
if (form) {
|
||||||
local.status = "loading";
|
local.status = "loading";
|
||||||
local.render();
|
local.render();
|
||||||
|
|
||||||
|
if (form.cache[name]) {
|
||||||
|
local.pk_field = form.cache[name].pk_field;
|
||||||
|
local.list = form.cache[name].list;
|
||||||
|
} else {
|
||||||
const table_fn = (db as any)[relation.table];
|
const table_fn = (db as any)[relation.table];
|
||||||
const select = {} as any;
|
const select = {} as any;
|
||||||
local.pk_field = "";
|
local.pk_field = "";
|
||||||
|
|
@ -67,6 +72,8 @@ export const Relation: FC<RelationProps> = ({
|
||||||
return { value: item[local.pk_field], label: label.join(" - ") };
|
return { value: item[local.pk_field], label: label.join(" - ") };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
form.cache[name] = { list: local.list, pk_field: local.pk_field };
|
||||||
|
}
|
||||||
|
|
||||||
const found = local.list.find((e) => {
|
const found = local.list.find((e) => {
|
||||||
if (typeof value === "object") {
|
if (typeof value === "object") {
|
||||||
|
|
@ -109,9 +116,10 @@ export const Relation: FC<RelationProps> = ({
|
||||||
content={
|
content={
|
||||||
<div
|
<div
|
||||||
className={cx(
|
className={cx(
|
||||||
"c-text-sm",
|
"c-text-sm c-relative c-overflow-auto",
|
||||||
css`
|
css`
|
||||||
width: ${local.ref.input?.clientWidth || 100}px;
|
width: ${local.ref.input?.clientWidth || 100}px;
|
||||||
|
max-height: 300px;
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
@ -124,15 +132,25 @@ export const Relation: FC<RelationProps> = ({
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{local.status === "ready" && (
|
{local.status === "ready" && (
|
||||||
<>
|
<div className="c-flex c-flex-1 c-flex-col">
|
||||||
{filtered.map((item, idx) => {
|
{filtered.map((item, idx) => {
|
||||||
|
let is_active = false;
|
||||||
|
if (typeof value === "object") {
|
||||||
|
const c = (value as any).connect;
|
||||||
|
if (c) {
|
||||||
|
is_active = item.value === c[local.pk_field];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
is_active = item.value === value;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
key={item.value + "_" + idx}
|
key={item.value + "_" + idx}
|
||||||
className={cx(
|
className={cx(
|
||||||
"c-px-3 c-py-1 cursor-pointer option-item",
|
"c-px-3 c-py-1 cursor-pointer option-item",
|
||||||
item.value === value
|
is_active
|
||||||
? "c-bg-blue-600 c-text-white"
|
? "c-bg-blue-600 c-text-white"
|
||||||
: "hover:c-bg-blue-50",
|
: "hover:c-bg-blue-50",
|
||||||
idx > 0 && "c-border-t",
|
idx > 0 && "c-border-t",
|
||||||
|
|
@ -149,11 +167,11 @@ export const Relation: FC<RelationProps> = ({
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{item.label}
|
{item.label || "-"}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
@ -197,7 +215,7 @@ export const Relation: FC<RelationProps> = ({
|
||||||
/>
|
/>
|
||||||
{!local.open && (
|
{!local.open && (
|
||||||
<div className="c-absolute c-text-sm c-inset-0 c-px-3 c-flex c-items-center">
|
<div className="c-absolute c-text-sm c-inset-0 c-px-3 c-flex c-items-center">
|
||||||
{local.label}
|
{local.label || "-"}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,22 @@
|
||||||
import { Form as FForm } from "@/comps/ui/form";
|
import { Form as FForm } from "@/comps/ui/form";
|
||||||
|
import { Toaster } from "@/comps/ui/sonner";
|
||||||
import { useLocal } from "@/utils/use-local";
|
import { useLocal } from "@/utils/use-local";
|
||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
|
import { createPortal } from "react-dom";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { FormHook } from "./utils/utils";
|
import { FormHook } from "./utils/utils";
|
||||||
|
import { AlertTriangle, Check, Loader2 } from "lucide-react";
|
||||||
|
import { cn } from "@/utils";
|
||||||
|
|
||||||
export const Form: FC<{
|
export const Form: FC<{
|
||||||
on_init: (arg: { submit: any }) => any;
|
on_init: (arg: { submit: any }) => any;
|
||||||
on_load: () => any;
|
on_load: () => any;
|
||||||
on_submit: (arg: { form: any; error: any }) => any;
|
on_submit: (arg: { form: any; error: any }) => Promise<any>;
|
||||||
body: any;
|
body: any;
|
||||||
form: FormHook;
|
form: FormHook;
|
||||||
PassProp: any;
|
PassProp: any;
|
||||||
|
cache: () => any;
|
||||||
layout: "auto" | "1-col" | "2-col";
|
layout: "auto" | "1-col" | "2-col";
|
||||||
}> = ({
|
}> = ({
|
||||||
on_init,
|
on_init,
|
||||||
|
|
@ -19,10 +25,11 @@ export const Form: FC<{
|
||||||
form,
|
form,
|
||||||
PassProp,
|
PassProp,
|
||||||
on_submit,
|
on_submit,
|
||||||
|
cache,
|
||||||
layout: _layout,
|
layout: _layout,
|
||||||
}) => {
|
}) => {
|
||||||
const form_hook = useForm<any>({
|
const form_hook = useForm<any>({
|
||||||
defaultValues: on_load,
|
defaultValues: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
const local = useLocal({
|
const local = useLocal({
|
||||||
|
|
@ -33,6 +40,13 @@ export const Form: FC<{
|
||||||
});
|
});
|
||||||
|
|
||||||
form.hook = form_hook;
|
form.hook = form_hook;
|
||||||
|
if (!form.cache && typeof cache === "function") {
|
||||||
|
try {
|
||||||
|
form.cache = cache() || {};
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
if (!form.cache) form.cache = {};
|
||||||
|
|
||||||
if (!form.validation) {
|
if (!form.validation) {
|
||||||
form.validation = {};
|
form.validation = {};
|
||||||
}
|
}
|
||||||
|
|
@ -45,7 +59,19 @@ export const Form: FC<{
|
||||||
|
|
||||||
const submit = () => {
|
const submit = () => {
|
||||||
clearTimeout(local.submit_timeout);
|
clearTimeout(local.submit_timeout);
|
||||||
local.submit_timeout = setTimeout(() => {
|
local.submit_timeout = setTimeout(async () => {
|
||||||
|
toast.loading(
|
||||||
|
<>
|
||||||
|
<Loader2 className="c-h-4 c-w-4 c-animate-spin" />
|
||||||
|
Saving ...
|
||||||
|
</>,
|
||||||
|
{
|
||||||
|
dismissible: true,
|
||||||
|
className: css`
|
||||||
|
background: #e4f7ff;
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
);
|
||||||
const data = form.hook.getValues();
|
const data = form.hook.getValues();
|
||||||
form.hook.clearErrors();
|
form.hook.clearErrors();
|
||||||
for (const [k, v] of Object.entries(form.validation)) {
|
for (const [k, v] of Object.entries(form.validation)) {
|
||||||
|
|
@ -61,22 +87,88 @@ export const Form: FC<{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
on_submit({
|
await on_submit({
|
||||||
form: data,
|
form: data,
|
||||||
error: form.hook.formState.errors,
|
error: form.hook.formState.errors,
|
||||||
});
|
});
|
||||||
|
toast.dismiss();
|
||||||
|
|
||||||
|
if (Object.keys(form.hook.formState.errors).length > 0) {
|
||||||
|
toast.error(
|
||||||
|
<div className="c-flex c-text-red-600 c-items-center">
|
||||||
|
<AlertTriangle className="c-h-4 c-w-4 c-mr-1" />
|
||||||
|
Save Failed, please correct{" "}
|
||||||
|
{Object.keys(form.hook.formState.errors).length} errors.
|
||||||
|
</div>,
|
||||||
|
{
|
||||||
|
dismissible: true,
|
||||||
|
className: css`
|
||||||
|
background: #ffecec;
|
||||||
|
border: 2px solid red;
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
toast.success(
|
||||||
|
<div className="c-flex c-text-blue-700 c-items-center">
|
||||||
|
<Check className="c-h-4 c-w-4 c-mr-1 " />
|
||||||
|
Data saved
|
||||||
|
</div>,
|
||||||
|
{
|
||||||
|
className: css`
|
||||||
|
background: #e4f5ff;
|
||||||
|
border: 2px solid blue;
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}, 300);
|
}, 300);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!local.init) {
|
if (!local.init) {
|
||||||
local.init = true;
|
local.init = true;
|
||||||
on_init({ submit });
|
on_init({ submit });
|
||||||
|
const res = on_load();
|
||||||
|
const loaded = (values: any) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
toast.dismiss();
|
||||||
|
});
|
||||||
|
if (!!values) {
|
||||||
|
for (const [k, v] of Object.entries(values)) {
|
||||||
|
form.hook.setValue(k, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
local.render();
|
||||||
|
};
|
||||||
|
if (res instanceof Promise) {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!isEditor) {
|
||||||
|
toast.loading(
|
||||||
|
<>
|
||||||
|
<Loader2 className="c-h-4 c-w-4 c-animate-spin" />
|
||||||
|
Loading data...
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
res.then(loaded);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
loaded(res);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
form.submit = submit;
|
form.submit = submit;
|
||||||
|
|
||||||
|
if (document.getElementsByClassName("prasi-toaster").length === 0) {
|
||||||
|
const elemDiv = document.createElement("div");
|
||||||
|
elemDiv.className = "prasi-toaster";
|
||||||
|
document.body.appendChild(elemDiv);
|
||||||
|
}
|
||||||
|
const toaster_el = document.getElementsByClassName("prasi-toaster")[0];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FForm {...form_hook}>
|
<FForm {...form_hook}>
|
||||||
|
{toaster_el && createPortal(<Toaster cn={cn} />, toaster_el)}
|
||||||
<form
|
<form
|
||||||
className={
|
className={
|
||||||
"flex-1 flex flex-col w-full items-stretch relative overflow-auto"
|
"flex-1 flex flex-col w-full items-stretch relative overflow-auto"
|
||||||
|
|
@ -85,7 +177,6 @@ export const Form: FC<{
|
||||||
if (el) form.ref = el;
|
if (el) form.ref = el;
|
||||||
}}
|
}}
|
||||||
onSubmit={(e) => {
|
onSubmit={(e) => {
|
||||||
console.log("on submit");
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
submit();
|
submit();
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ export type FormHook = {
|
||||||
ref: HTMLFormElement;
|
ref: HTMLFormElement;
|
||||||
submit: any;
|
submit: any;
|
||||||
label: Record<string, string>;
|
label: Record<string, string>;
|
||||||
|
cache: any;
|
||||||
validation: Record<string, "required">;
|
validation: Record<string, "required">;
|
||||||
render: () => void;
|
render: () => void;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,26 @@
|
||||||
import { useLocal } from "@/utils/use-local";
|
import { useLocal } from "@/utils/use-local";
|
||||||
import { FC, useEffect } from "react";
|
import { FC, useEffect } from "react";
|
||||||
import DataGrid, { ColumnOrColumnGroup, SortColumn } from "react-data-grid";
|
import DataGrid, {
|
||||||
|
ColumnOrColumnGroup,
|
||||||
|
Row,
|
||||||
|
SortColumn,
|
||||||
|
} from "react-data-grid";
|
||||||
import "react-data-grid/lib/styles.css";
|
import "react-data-grid/lib/styles.css";
|
||||||
import { Skeleton } from "../ui/skeleton";
|
import { Skeleton } from "../ui/skeleton";
|
||||||
|
|
||||||
|
type OnRowClick = {
|
||||||
|
row: any;
|
||||||
|
rows: any[];
|
||||||
|
idx: any;
|
||||||
|
event: React.MouseEvent<HTMLDivElement, MouseEvent>;
|
||||||
|
};
|
||||||
export const Table: FC<{
|
export const Table: FC<{
|
||||||
columns: () => Promise<ColumnOrColumnGroup<any>[]>;
|
columns: () => Promise<ColumnOrColumnGroup<any>[]>;
|
||||||
on_load: () => Promise<any[]>;
|
on_load: () => Promise<any[]>;
|
||||||
child: any;
|
child: any;
|
||||||
PassProp: any;
|
PassProp: any;
|
||||||
}> = ({ columns, on_load, child, PassProp }) => {
|
row_click: (arg: OnRowClick) => void;
|
||||||
|
}> = ({ columns, on_load, child, PassProp, row_click }) => {
|
||||||
const local = useLocal({
|
const local = useLocal({
|
||||||
loading: false,
|
loading: false,
|
||||||
data: undefined as unknown as any[],
|
data: undefined as unknown as any[],
|
||||||
|
|
@ -56,6 +67,7 @@ export const Table: FC<{
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableInternal
|
<TableInternal
|
||||||
|
row_click={row_click}
|
||||||
columns={local.columns}
|
columns={local.columns}
|
||||||
data={local.loading ? undefined : local.data}
|
data={local.loading ? undefined : local.data}
|
||||||
render={local.render}
|
render={local.render}
|
||||||
|
|
@ -67,7 +79,8 @@ const TableInternal: FC<{
|
||||||
columns: ColumnOrColumnGroup<any>[];
|
columns: ColumnOrColumnGroup<any>[];
|
||||||
data?: any[];
|
data?: any[];
|
||||||
render: () => void;
|
render: () => void;
|
||||||
}> = ({ columns, data, render }) => {
|
row_click: (arg: OnRowClick) => void;
|
||||||
|
}> = ({ columns, data, render, row_click }) => {
|
||||||
const local = useLocal({
|
const local = useLocal({
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
|
|
@ -134,7 +147,6 @@ const TableInternal: FC<{
|
||||||
>
|
>
|
||||||
<DataGrid
|
<DataGrid
|
||||||
columns={columns}
|
columns={columns}
|
||||||
selectedRows={null}
|
|
||||||
sortColumns={sort}
|
sortColumns={sort}
|
||||||
onSortColumnsChange={([col]) => {
|
onSortColumnsChange={([col]) => {
|
||||||
local.sort = [];
|
local.sort = [];
|
||||||
|
|
@ -160,6 +172,24 @@ const TableInternal: FC<{
|
||||||
typeof data === "undefined"
|
typeof data === "undefined"
|
||||||
? undefined
|
? undefined
|
||||||
: {
|
: {
|
||||||
|
renderRow(key, props) {
|
||||||
|
return (
|
||||||
|
<Row
|
||||||
|
key={key}
|
||||||
|
{...props}
|
||||||
|
onClick={(ev) => {
|
||||||
|
if (typeof row_click === "function") {
|
||||||
|
row_click({
|
||||||
|
event: ev,
|
||||||
|
idx: props.rowIdx,
|
||||||
|
row: props.row,
|
||||||
|
rows: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
noRowsFallback: (
|
noRowsFallback: (
|
||||||
<div className="c-flex-1 c-w-full absolute inset-0 c-flex c-flex-col c-items-center c-justify-center">
|
<div className="c-flex-1 c-w-full absolute inset-0 c-flex c-flex-col c-items-center c-justify-center">
|
||||||
<div className="c-max-w-[15%] c-flex c-flex-col c-items-center">
|
<div className="c-max-w-[15%] c-flex c-flex-col c-items-center">
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { FC } from "react";
|
||||||
|
import { MasterDetailConfig } from "./type";
|
||||||
|
import { Button } from "../ui/button";
|
||||||
|
|
||||||
|
export const MDAction: FC<{ md: MasterDetailConfig }> = ({ md }) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
"md-action c-flex c-space-x-2",
|
||||||
|
css`
|
||||||
|
button {
|
||||||
|
height: 25px;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{md.ui.actions.length > 0 &&
|
||||||
|
md.ui.actions.map((e, idx) => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant={e.type || "default"}
|
||||||
|
onClick={e.onClick}
|
||||||
|
className={cx(
|
||||||
|
"c-flex c-items-center",
|
||||||
|
css`
|
||||||
|
padding: 0px 8px !important;
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{e.icon && (
|
||||||
|
<span
|
||||||
|
className={cx(
|
||||||
|
"c-mr-[5px]",
|
||||||
|
css`
|
||||||
|
svg {
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
dangerouslySetInnerHTML={{ __html: e.icon }}
|
||||||
|
></span>
|
||||||
|
)}{" "}
|
||||||
|
{e.label}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { FC } from "react";
|
||||||
|
import { MasterDetailConfig } from "./type";
|
||||||
|
|
||||||
|
export const MDTitle: FC<{ md: MasterDetailConfig }> = ({ md }) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{md.ui.breadcrumb.length > 0
|
||||||
|
? md.ui.breadcrumb.map((e, idx) => {
|
||||||
|
let label = (typeof e === "object" ? e?.[0] : e) || "-";
|
||||||
|
let url = typeof e === "object" ? e?.[1] : null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{idx > 0 && (
|
||||||
|
<span className="c-pl-[6px] c-pr-[5px] c-text-gray-300">
|
||||||
|
/
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<span
|
||||||
|
onClick={() => {
|
||||||
|
if (url === "") {
|
||||||
|
md.ui.actions = [...md.ui.default_actions];
|
||||||
|
md.ui.breadcrumb = [];
|
||||||
|
md.ui.back = false;
|
||||||
|
md.selected = null;
|
||||||
|
md.render();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className={cx(
|
||||||
|
typeof url === "string"
|
||||||
|
? "c-cursor-pointer hover:c-underline"
|
||||||
|
: "c-text-gray-500"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: md.ui.title}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,163 @@
|
||||||
|
import { useLocal } from "@/utils/use-local";
|
||||||
|
import get from "lodash.get";
|
||||||
|
import { FC, useEffect } from "react";
|
||||||
|
import {
|
||||||
|
MasterDetailConfig,
|
||||||
|
MasterDetailLocal,
|
||||||
|
MasterDetailProp,
|
||||||
|
} from "./type";
|
||||||
|
import { Tab } from "../custom/Tab";
|
||||||
|
import { Tabs, TabsList, TabsTrigger } from "../ui/tabs";
|
||||||
|
|
||||||
|
export const MasterDetail: FC<MasterDetailProp> = (props) => {
|
||||||
|
const { header, PassProp, master, detail, mode, title, actions } = props;
|
||||||
|
const md = useLocal<MasterDetailLocal & { cache_internal: any }>({
|
||||||
|
mode,
|
||||||
|
selected: null,
|
||||||
|
active_tab: "",
|
||||||
|
ui: {
|
||||||
|
back: false,
|
||||||
|
title: title,
|
||||||
|
breadcrumb: [],
|
||||||
|
default_actions: null as any,
|
||||||
|
actions: null as any,
|
||||||
|
},
|
||||||
|
cache_internal: {},
|
||||||
|
cache: null as any,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!md.ui.actions) {
|
||||||
|
md.ui.actions = actions(md);
|
||||||
|
md.ui.default_actions = actions(md);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!md.cache) {
|
||||||
|
md.cache = (name: string, opt?: { reset: boolean }) => {
|
||||||
|
if (!md.cache_internal[name] || opt?.reset) md.cache_internal[name] = {};
|
||||||
|
return md.cache_internal[name];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEditor) {
|
||||||
|
useEffect(() => {
|
||||||
|
md.ui.title = title;
|
||||||
|
md.render();
|
||||||
|
}, [title]);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let back = false;
|
||||||
|
if (md.selected && md.mode === "breadcrumb") {
|
||||||
|
back = true;
|
||||||
|
}
|
||||||
|
if (back !== md.ui.back) {
|
||||||
|
md.ui.back = back;
|
||||||
|
md.render();
|
||||||
|
}
|
||||||
|
}, [md.selected]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
"c-flex-1 c-flex-col c-flex c-w-full c-h-full c-overflow-hidden"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<props.PassProp md={md}>{header}</props.PassProp>
|
||||||
|
<BreadcrumbMode props={props} md={md} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const BreadcrumbMode: FC<{
|
||||||
|
props: MasterDetailProp;
|
||||||
|
md: MasterDetailConfig;
|
||||||
|
}> = ({ props, md }) => {
|
||||||
|
return (
|
||||||
|
<div className={cx("c-flex-1 c-flex-col c-flex")}>
|
||||||
|
<div
|
||||||
|
className={cx(md.selected && "c-hidden", "c-flex c-flex-1 c-flex-col")}
|
||||||
|
>
|
||||||
|
<props.PassProp md={md}>{props.master}</props.PassProp>
|
||||||
|
</div>
|
||||||
|
{md.selected && <Detail props={props} md={md} />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Detail: FC<{
|
||||||
|
props: MasterDetailProp;
|
||||||
|
md: MasterDetailConfig;
|
||||||
|
}> = ({ props, md }) => {
|
||||||
|
const childs = get(
|
||||||
|
props.detail,
|
||||||
|
"props.meta.item.component.props.detail.content.childs"
|
||||||
|
);
|
||||||
|
|
||||||
|
let idx = childs.findIndex(
|
||||||
|
(e: any) => e.name.toLowerCase() === md.active_tab.toLowerCase()
|
||||||
|
);
|
||||||
|
if (idx < 0) {
|
||||||
|
idx = 0;
|
||||||
|
md.active_tab = childs[idx].name;
|
||||||
|
setTimeout(md.render);
|
||||||
|
}
|
||||||
|
const content = childs[idx];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{childs.length > 1 && (
|
||||||
|
<Tabs
|
||||||
|
value={content.name}
|
||||||
|
className={cx(
|
||||||
|
css`
|
||||||
|
padding: 0;
|
||||||
|
button {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<TabsList
|
||||||
|
overrideClassName
|
||||||
|
className={cx(
|
||||||
|
"c-flex c-w-full c-rounded-none c-border-b c-border-gray-300 c-justify-start",
|
||||||
|
css`
|
||||||
|
padding: 0 !important;
|
||||||
|
height: auto !important;
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{childs.map((e: { name: string }) => {
|
||||||
|
return (
|
||||||
|
<TabsTrigger
|
||||||
|
value={e.name}
|
||||||
|
onClick={() => {
|
||||||
|
md.active_tab = e.name;
|
||||||
|
md.render();
|
||||||
|
}}
|
||||||
|
overrideClassName
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
"c-p-1 c-h-10 c-flex c-items-center",
|
||||||
|
md.active_tab === e.name
|
||||||
|
? css`
|
||||||
|
border-bottom: 2px solid #3c82f6;
|
||||||
|
`
|
||||||
|
: "border-b-transparent"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className={cx("c-mr-1 c-flex-1 c-px-1 c-flex")}>
|
||||||
|
{e.name}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TabsTrigger>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TabsList>
|
||||||
|
</Tabs>
|
||||||
|
)}
|
||||||
|
<props.PassProp md={md}>{content}</props.PassProp>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { ReactElement, ReactNode } from "react";
|
||||||
|
|
||||||
|
export type MasterDetailProp = {
|
||||||
|
header: any;
|
||||||
|
PassProp: any;
|
||||||
|
master: any;
|
||||||
|
detail: any;
|
||||||
|
mode: "breadcrumb" | "vertical" | "horizontal";
|
||||||
|
title: string;
|
||||||
|
actions: (md: any) => MasterDetailAction[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type MasterDetailAction = {
|
||||||
|
label: string;
|
||||||
|
onClick?: () => Promise<void>;
|
||||||
|
type?: "default" | "ghost" | "secondary" | "destructive";
|
||||||
|
icon?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MasterDetailLocal = {
|
||||||
|
mode: MasterDetailProp["mode"];
|
||||||
|
selected: null | Record<string, any>;
|
||||||
|
active_tab: string;
|
||||||
|
ui: {
|
||||||
|
back: boolean;
|
||||||
|
title: string;
|
||||||
|
breadcrumb: ([string, string] | string)[];
|
||||||
|
default_actions: MasterDetailAction[];
|
||||||
|
actions: MasterDetailAction[];
|
||||||
|
};
|
||||||
|
cache: (name: string, opt?: { reset: boolean }) => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MasterDetailConfig = MasterDetailLocal & { render: () => void };
|
||||||
|
|
||||||
|
const action_type = `{
|
||||||
|
label: string;
|
||||||
|
onClick?: () => Promise<void>;
|
||||||
|
type?: "default" | "ghost" | "secondary" | "destructive";
|
||||||
|
icon?: string;
|
||||||
|
}`;
|
||||||
|
|
||||||
|
export const master_detail_typings = {
|
||||||
|
md: `{
|
||||||
|
mode: "breadcrumb" | "vertical" | "horizontal";
|
||||||
|
selected: null | Record<string, any>;
|
||||||
|
active_tab: string;
|
||||||
|
render: () => void;
|
||||||
|
ui: {
|
||||||
|
back: boolean;
|
||||||
|
title: string;
|
||||||
|
breadcrumb: ([string, string] | string)[];
|
||||||
|
default_actions: ${action_type}[];
|
||||||
|
actions: ${action_type}[];
|
||||||
|
};
|
||||||
|
cache: (name: string, opt?: { reset: boolean }) => any;
|
||||||
|
}`,
|
||||||
|
action_type,
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { useTheme } from "next-themes"
|
||||||
|
import { Toaster as Sonner } from "sonner"
|
||||||
|
|
||||||
|
type ToasterProps = React.ComponentProps<typeof Sonner>
|
||||||
|
|
||||||
|
const Toaster = ({ ...props }: ToasterProps) => {
|
||||||
|
const { theme = "system" } = useTheme()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Sonner
|
||||||
|
theme={theme as ToasterProps["theme"]}
|
||||||
|
className="c-toaster c-group"
|
||||||
|
toastOptions={{
|
||||||
|
classNames: {
|
||||||
|
toast:
|
||||||
|
"group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
|
||||||
|
description: "group-[.toast]:text-muted-foreground",
|
||||||
|
actionButton:
|
||||||
|
"group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
|
||||||
|
cancelButton:
|
||||||
|
"group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Toaster }
|
||||||
|
|
@ -1,39 +1,47 @@
|
||||||
import * as React from "react"
|
import * as React from "react";
|
||||||
import * as TabsPrimitive from "@radix-ui/react-tabs"
|
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
||||||
|
|
||||||
import { cn } from "@/utils"
|
import { cn } from "@/utils";
|
||||||
|
|
||||||
const Tabs = TabsPrimitive.Root
|
const Tabs = TabsPrimitive.Root;
|
||||||
|
|
||||||
const TabsList = React.forwardRef<
|
const TabsList = React.forwardRef<
|
||||||
React.ElementRef<typeof TabsPrimitive.List>,
|
React.ElementRef<typeof TabsPrimitive.List>,
|
||||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> & {
|
||||||
|
overrideClassName?: boolean;
|
||||||
|
}
|
||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<TabsPrimitive.List
|
<TabsPrimitive.List
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"c-inline-flex c-h-10 c-items-center c-justify-center c-rounded-md c-bg-muted c-p-1 c-text-muted-foreground",
|
props.overrideClassName === true
|
||||||
|
? ""
|
||||||
|
: "c-inline-flex c-h-10 c-items-center c-justify-center c-rounded-md c-bg-muted c-p-1 c-text-muted-foreground",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
TabsList.displayName = TabsPrimitive.List.displayName
|
TabsList.displayName = TabsPrimitive.List.displayName;
|
||||||
|
|
||||||
const TabsTrigger = React.forwardRef<
|
const TabsTrigger = React.forwardRef<
|
||||||
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
||||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger> & {
|
||||||
|
overrideClassName?: boolean;
|
||||||
|
}
|
||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<TabsPrimitive.Trigger
|
<TabsPrimitive.Trigger
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"c-inline-flex c-items-center c-justify-center c-whitespace-nowrap c-rounded-sm c-px-3 c-py-1.5 c-text-sm c-font-medium c-ring-offset-background c-transition-all focus-visible:c-outline-none focus-visible:c-ring-2 focus-visible:c-ring-ring focus-visible:c-ring-offset-2 disabled:c-pointer-events-none disabled:c-opacity-50 data-[state=active]:c-bg-background data-[state=active]:c-text-foreground data-[state=active]:c-shadow-sm",
|
props.overrideClassName === true
|
||||||
|
? ""
|
||||||
|
: "c-inline-flex c-items-center c-justify-center c-whitespace-nowrap c-rounded-sm c-px-3 c-py-1.5 c-text-sm c-font-medium c-ring-offset-background c-transition-all focus-visible:c-outline-none focus-visible:c-ring-2 focus-visible:c-ring-ring focus-visible:c-ring-offset-2 disabled:c-pointer-events-none disabled:c-opacity-50 data-[state=active]:c-bg-background data-[state=active]:c-text-foreground data-[state=active]:c-shadow-sm",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
|
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
||||||
|
|
||||||
const TabsContent = React.forwardRef<
|
const TabsContent = React.forwardRef<
|
||||||
React.ElementRef<typeof TabsPrimitive.Content>,
|
React.ElementRef<typeof TabsPrimitive.Content>,
|
||||||
|
|
@ -47,7 +55,7 @@ const TabsContent = React.forwardRef<
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
TabsContent.displayName = TabsPrimitive.Content.displayName
|
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
||||||
|
|
||||||
export { Tabs, TabsList, TabsTrigger, TabsContent }
|
export { Tabs, TabsList, TabsTrigger, TabsContent };
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
import get from "lodash.get";
|
import get from "lodash.get";
|
||||||
import { on_load } from "./gen_form/on_load";
|
import { GFCol as Col, formatName } from "../utils";
|
||||||
import { on_submit } from "./gen_form/on_submit";
|
import { NewFieldArg, newField } from "./new_field";
|
||||||
import { GFCol as Col } from "./gen_form/type";
|
import { on_load } from "./on_load";
|
||||||
import { NewFieldArg, newField } from "./gen_form/new_field";
|
import { on_submit } from "./on_submit";
|
||||||
import capitalize from "lodash.capitalize";
|
|
||||||
|
|
||||||
export const gen_form = (modify: (data: any) => void, data: any) => {
|
export const gen_form = (modify: (data: any) => void, data: any) => {
|
||||||
const mode = JSON.parse(data.gen_mode.value) as (
|
const mode = JSON.parse(data.gen_mode.value) as (
|
||||||
|
|
@ -59,7 +58,6 @@ export const gen_form = (modify: (data: any) => void, data: any) => {
|
||||||
select[col.name] = true;
|
select[col.name] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(new_fields);
|
|
||||||
|
|
||||||
const result: any = {};
|
const result: any = {};
|
||||||
if (pk) {
|
if (pk) {
|
||||||
|
|
@ -76,7 +74,6 @@ export const gen_form = (modify: (data: any) => void, data: any) => {
|
||||||
result["body"] = data["body"];
|
result["body"] = data["body"];
|
||||||
const childs = get(result, "body.content.childs");
|
const childs = get(result, "body.content.childs");
|
||||||
if (Array.isArray(childs)) {
|
if (Array.isArray(childs)) {
|
||||||
|
|
||||||
result.body.content.childs = new_fields.map(newField);
|
result.body.content.childs = new_fields.map(newField);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -84,11 +81,3 @@ export const gen_form = (modify: (data: any) => void, data: any) => {
|
||||||
|
|
||||||
alert("Prop Generated!");
|
alert("Prop Generated!");
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatName = (name: string) => {
|
|
||||||
return name
|
|
||||||
.split("_")
|
|
||||||
.filter((e) => e.length > 1)
|
|
||||||
.map((e) => capitalize(e))
|
|
||||||
.join(" ");
|
|
||||||
};
|
|
||||||
|
|
@ -265,10 +265,8 @@ export const newField = (arg: NewFieldArg) => {
|
||||||
idx: 11,
|
idx: 11,
|
||||||
name: "prop_11",
|
name: "prop_11",
|
||||||
type: "string",
|
type: "string",
|
||||||
value:
|
value: "async () => {\n return {\n };\n}",
|
||||||
'async () => {\n return {\n orderBy: {\n regional: "asc",\n },\n };\n}',
|
valueBuilt: " async () => {\n return {\n };\n};\n",
|
||||||
valueBuilt:
|
|
||||||
' async () => {\n return {\n orderBy: {\n regional: "asc"\n }\n };\n};\n',
|
|
||||||
meta: {
|
meta: {
|
||||||
type: "text",
|
type: "text",
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,26 @@
|
||||||
import { GFCol } from "./type";
|
import { GFCol } from "../utils";
|
||||||
|
|
||||||
export const on_load = ({
|
export const on_load = ({
|
||||||
pk,
|
pk,
|
||||||
table,
|
table,
|
||||||
select,
|
select,
|
||||||
pks,
|
pks,
|
||||||
|
opt,
|
||||||
}: {
|
}: {
|
||||||
pk: GFCol;
|
pk: GFCol;
|
||||||
table: string;
|
table: string;
|
||||||
select: any;
|
select: any;
|
||||||
pks: Record<string, string>;
|
pks: Record<string, string>;
|
||||||
|
opt?: {
|
||||||
|
before_load: string;
|
||||||
|
};
|
||||||
}) => {
|
}) => {
|
||||||
return `\
|
return `\
|
||||||
async (opt) => {
|
async (opt) => {
|
||||||
if (isEditor) return {};
|
if (isEditor) return {};
|
||||||
|
|
||||||
let id = ${pk.type === "int" ? "parseInt(params.id)" : "params.id"};
|
let id = ${pk.type === "int" ? "parseInt(params.id)" : "params.id"};
|
||||||
|
${opt?.before_load}
|
||||||
|
|
||||||
if (id){
|
if (id){
|
||||||
const item = await db.${table}.findFirst({
|
const item = await db.${table}.findFirst({
|
||||||
|
|
@ -30,7 +35,8 @@ async (opt) => {
|
||||||
.map(([k, v]) => {
|
.map(([k, v]) => {
|
||||||
return `\
|
return `\
|
||||||
if (k === "${k}") {
|
if (k === "${k}") {
|
||||||
item[k] = { connect: { ${v}: v["${v}"] } } as any;
|
if (v?.["${v}"]) item[k] = { connect: { ${v}: v?.["${v}"] } } as any;
|
||||||
|
else delete item[k];
|
||||||
}`;
|
}`;
|
||||||
})
|
})
|
||||||
.join("\n")}
|
.join("\n")}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { GFCol } from "./type";
|
import { GFCol } from "../utils";
|
||||||
|
|
||||||
export const on_submit = ({
|
export const on_submit = ({
|
||||||
pk,
|
pk,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
export type GFCol = {
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
is_pk: boolean;
|
|
||||||
optional: boolean;
|
|
||||||
};
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
export const form_before_load = (pk: string, title: string, name: string) => {
|
||||||
|
return `
|
||||||
|
const after_load = (item: any) => {
|
||||||
|
const set_actions = () =>
|
||||||
|
(md.ui.actions = [
|
||||||
|
{
|
||||||
|
label: "Delete",
|
||||||
|
type: "destructive",
|
||||||
|
onClick: async () => {
|
||||||
|
if (confirm("Are you sure ?")) {
|
||||||
|
md.ui.actions = [{ label: "Deleting...", type: "ghost" }];
|
||||||
|
md.render();
|
||||||
|
|
||||||
|
await db.m_aset.delete({ where: { ${pk}: item.${pk} } });
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
md.ui.actions = [...md.ui.default_actions];
|
||||||
|
md.ui.breadcrumb = [];
|
||||||
|
md.ui.back = false;
|
||||||
|
md.selected = null;
|
||||||
|
md.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon: \`<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></svg>\`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Save",
|
||||||
|
onClick: async () => {
|
||||||
|
md.ui.actions = [{ label: "Saving...", type: "ghost" }];
|
||||||
|
md.render();
|
||||||
|
await md.cache("form")._submit();
|
||||||
|
setTimeout(() => {
|
||||||
|
set_actions();
|
||||||
|
md.render();
|
||||||
|
}, 500);
|
||||||
|
},
|
||||||
|
icon: \`<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path><polyline points="17 21 17 13 7 13 7 21"></polyline><polyline points="7 3 7 8 15 8"></polyline></svg>\`,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
set_actions();
|
||||||
|
md.ui.breadcrumb = [[${title}, ""], item?.nama_aset_komersial];
|
||||||
|
md.render();
|
||||||
|
};
|
||||||
|
`;
|
||||||
|
};
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,223 @@
|
||||||
|
import { createId } from "@paralleldrive/cuid2";
|
||||||
|
|
||||||
|
export const gen_header = () => {
|
||||||
|
return {
|
||||||
|
id: createId(),
|
||||||
|
name: "prop_1",
|
||||||
|
type: "item",
|
||||||
|
dim: {
|
||||||
|
w: "full",
|
||||||
|
h: 40,
|
||||||
|
hUnit: "px",
|
||||||
|
},
|
||||||
|
childs: [
|
||||||
|
{
|
||||||
|
id: createId(),
|
||||||
|
name: "breadcrumb",
|
||||||
|
type: "item",
|
||||||
|
dim: {
|
||||||
|
w: "full",
|
||||||
|
h: 40,
|
||||||
|
hUnit: "px",
|
||||||
|
},
|
||||||
|
childs: [
|
||||||
|
{
|
||||||
|
id: createId(),
|
||||||
|
name: "back",
|
||||||
|
type: "item",
|
||||||
|
dim: {
|
||||||
|
w: "fit",
|
||||||
|
h: "full",
|
||||||
|
},
|
||||||
|
childs: [
|
||||||
|
{
|
||||||
|
id: createId(),
|
||||||
|
name: "icon",
|
||||||
|
type: "item",
|
||||||
|
dim: {
|
||||||
|
w: "full",
|
||||||
|
h: "full",
|
||||||
|
},
|
||||||
|
childs: [],
|
||||||
|
adv: {
|
||||||
|
html: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor"\n stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-left">\n <path d="m15 18-6-6 6-6" />\n</svg>',
|
||||||
|
css: "",
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
dir: "col",
|
||||||
|
align: "center",
|
||||||
|
gap: 0,
|
||||||
|
wrap: "flex-nowrap",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: createId(),
|
||||||
|
name: "back_text",
|
||||||
|
type: "text",
|
||||||
|
dim: {
|
||||||
|
w: "fit",
|
||||||
|
h: "full",
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
align: "center",
|
||||||
|
dir: "col",
|
||||||
|
gap: 0,
|
||||||
|
},
|
||||||
|
text: "",
|
||||||
|
html: "<div>Back</div>",
|
||||||
|
adv: {
|
||||||
|
css: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
adv: {
|
||||||
|
js: '<>\n {md.ui.back && (\n <div\n {...props}\n className={cx(props.className, "")}\n onClick={() => {\n md.ui.actions = [...md.ui.default_actions];\n md.ui.breadcrumb = [];\n md.ui.back = false;\n md.selected = null;\n md.render();\n }}\n >\n {children}\n </div>\n )}\n</>',
|
||||||
|
jsBuilt:
|
||||||
|
'render(/* @__PURE__ */ React.createElement(React.Fragment, null, md.ui.back && /* @__PURE__ */ React.createElement(\n "div",\n {\n ...props,\n className: cx(props.className, ""),\n onClick: () => {\n md.ui.actions = [...md.ui.default_actions];\n md.ui.breadcrumb = [];\n md.ui.back = false;\n md.selected = null;\n md.render();\n }\n },\n children\n)));\n',
|
||||||
|
css: "& {\n display: flex;\n cursor: pointer;\n\n // &.mobile {}\n // &.desktop {}\n &:hover {\n background: rgb(237, 246, 255);\n }\n}",
|
||||||
|
},
|
||||||
|
script: {},
|
||||||
|
layout: {
|
||||||
|
dir: "row",
|
||||||
|
align: "top-left",
|
||||||
|
gap: 0,
|
||||||
|
wrap: "flex-nowrap",
|
||||||
|
},
|
||||||
|
border: {
|
||||||
|
style: "solid",
|
||||||
|
stroke: {
|
||||||
|
r: 1,
|
||||||
|
},
|
||||||
|
color: "#ececeb",
|
||||||
|
},
|
||||||
|
padding: {
|
||||||
|
l: 10,
|
||||||
|
b: 0,
|
||||||
|
t: 0,
|
||||||
|
r: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: createId(),
|
||||||
|
name: "title",
|
||||||
|
type: "item",
|
||||||
|
childs: [],
|
||||||
|
adv: {
|
||||||
|
js: '<div {...props} className={cx(props.className, "")}>\n <MDTitle md={md} />\n</div>',
|
||||||
|
jsBuilt:
|
||||||
|
'render(/* @__PURE__ */ React.createElement("div", { ...props, className: cx(props.className, "") }, /* @__PURE__ */ React.createElement(MDTitle, { md })));\n',
|
||||||
|
},
|
||||||
|
script: {},
|
||||||
|
padding: {
|
||||||
|
l: 10,
|
||||||
|
b: 0,
|
||||||
|
t: 0,
|
||||||
|
r: 10,
|
||||||
|
},
|
||||||
|
dim: {
|
||||||
|
w: "full",
|
||||||
|
h: "full",
|
||||||
|
wUnit: "px",
|
||||||
|
hUnit: "px",
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
dir: "col",
|
||||||
|
align: "left",
|
||||||
|
gap: 0,
|
||||||
|
wrap: "flex-nowrap",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: createId(),
|
||||||
|
name: "right",
|
||||||
|
type: "item",
|
||||||
|
childs: [
|
||||||
|
{
|
||||||
|
id: createId(),
|
||||||
|
name: "mode",
|
||||||
|
type: "item",
|
||||||
|
dim: {
|
||||||
|
w: "fit",
|
||||||
|
h: "full",
|
||||||
|
},
|
||||||
|
childs: [],
|
||||||
|
adv: {
|
||||||
|
js: '<>\n {!md.ui.back && (\n <div {...props} className={cx(props.className, "")}>\n {children}\n </div>\n )}\n</>',
|
||||||
|
jsBuilt:
|
||||||
|
'render(/* @__PURE__ */ React.createElement(React.Fragment, null, !md.ui.back && /* @__PURE__ */ React.createElement("div", { ...props, className: cx(props.className, "") }, children)));\n',
|
||||||
|
css: "",
|
||||||
|
},
|
||||||
|
script: {},
|
||||||
|
padding: {
|
||||||
|
l: 0,
|
||||||
|
b: 0,
|
||||||
|
t: 0,
|
||||||
|
r: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: createId(),
|
||||||
|
name: "actions",
|
||||||
|
type: "item",
|
||||||
|
dim: {
|
||||||
|
w: "fit",
|
||||||
|
h: "full",
|
||||||
|
},
|
||||||
|
childs: [],
|
||||||
|
adv: {
|
||||||
|
js: '<div {...props} className={cx(props.className, "")}>\n <MDAction md={md} />\n</div>',
|
||||||
|
jsBuilt:
|
||||||
|
'render(/* @__PURE__ */ React.createElement("div", { ...props, className: cx(props.className, "") }, /* @__PURE__ */ React.createElement(MDAction, { md })));\n',
|
||||||
|
css: "",
|
||||||
|
},
|
||||||
|
script: {},
|
||||||
|
padding: {
|
||||||
|
l: 0,
|
||||||
|
b: 0,
|
||||||
|
t: 0,
|
||||||
|
r: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
layout: {
|
||||||
|
dir: "row",
|
||||||
|
align: "top-left",
|
||||||
|
gap: 0,
|
||||||
|
wrap: "flex-nowrap",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
adv: {
|
||||||
|
js: '<>\n {md.mode === "breadcrumb" && (\n <div {...props} className={cx(props.className, "")}>\n {children}\n </div>\n )}\n</>',
|
||||||
|
jsBuilt:
|
||||||
|
'render(/* @__PURE__ */ React.createElement(React.Fragment, null, md.mode === "breadcrumb" && /* @__PURE__ */ React.createElement("div", { ...props, className: cx(props.className, "") }, children)));\n',
|
||||||
|
css: "",
|
||||||
|
},
|
||||||
|
script: {},
|
||||||
|
padding: {
|
||||||
|
l: 0,
|
||||||
|
b: 0,
|
||||||
|
t: 0,
|
||||||
|
r: 0,
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
dir: "row",
|
||||||
|
align: "left",
|
||||||
|
gap: 0,
|
||||||
|
wrap: "flex-nowrap",
|
||||||
|
},
|
||||||
|
border: {
|
||||||
|
style: "solid",
|
||||||
|
stroke: {
|
||||||
|
b: 1,
|
||||||
|
},
|
||||||
|
color: "#ccc",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
adv: {
|
||||||
|
css: "",
|
||||||
|
},
|
||||||
|
hidden: false,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,302 @@
|
||||||
|
import { createId } from "@paralleldrive/cuid2";
|
||||||
|
|
||||||
|
export const gen_master = () => {
|
||||||
|
const res = {
|
||||||
|
id: createId(),
|
||||||
|
name: "prop_1",
|
||||||
|
type: "item",
|
||||||
|
dim: {
|
||||||
|
w: "full",
|
||||||
|
h: "full",
|
||||||
|
},
|
||||||
|
childs: [
|
||||||
|
{
|
||||||
|
id: createId(),
|
||||||
|
adv: {
|
||||||
|
js: "<Table on_load={on_load} columns={columns} child={child} PassProp={PassProp} />",
|
||||||
|
css: "",
|
||||||
|
jsBuilt:
|
||||||
|
"render(/* @__PURE__ */ React.createElement(Table, { on_load, columns, child, PassProp }));\n",
|
||||||
|
},
|
||||||
|
dim: {
|
||||||
|
h: "full",
|
||||||
|
w: "full",
|
||||||
|
},
|
||||||
|
name: "table",
|
||||||
|
type: "item",
|
||||||
|
childs: [
|
||||||
|
{
|
||||||
|
id: createId(),
|
||||||
|
adv: {
|
||||||
|
css: "",
|
||||||
|
},
|
||||||
|
dim: {
|
||||||
|
h: "full",
|
||||||
|
w: "full",
|
||||||
|
},
|
||||||
|
name: "jsx: child",
|
||||||
|
type: "item",
|
||||||
|
childs: [
|
||||||
|
{
|
||||||
|
id: createId(),
|
||||||
|
adv: {
|
||||||
|
js: '<>{cell.key !== "action" && cell.value}</>',
|
||||||
|
css: "",
|
||||||
|
jsBuilt:
|
||||||
|
'render(/* @__PURE__ */ React.createElement(React.Fragment, null, cell.key !== "action" && cell.value));\n',
|
||||||
|
},
|
||||||
|
dim: {
|
||||||
|
h: "full",
|
||||||
|
w: "full",
|
||||||
|
},
|
||||||
|
name: "text",
|
||||||
|
type: "item",
|
||||||
|
childs: [],
|
||||||
|
script: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: createId(),
|
||||||
|
adv: {
|
||||||
|
js: '<>\n {cell.key === "action" && (\n <div className="flex items-center h-full">\n <div\n className={cx(\n "bg-white border flex items-center px-3 cursor-pointer capitalize",\n css`\n height:25px; \n &:hover {\n background:blue;\n color:white;\n }\n `,\n )}\n >\n {cell.value}\n </div>\n </div>\n )}\n</>',
|
||||||
|
css: "",
|
||||||
|
jsBuilt:
|
||||||
|
'render(/* @__PURE__ */ React.createElement(React.Fragment, null, cell.key === "action" && /* @__PURE__ */ React.createElement("div", { className: "flex items-center h-full" }, /* @__PURE__ */ React.createElement(\n "div",\n {\n className: cx(\n "bg-white border flex items-center px-3 cursor-pointer capitalize",\n css`\n height:25px; \n &:hover {\n background:blue;\n color:white;\n }\n `\n )\n },\n cell.value\n))));\n',
|
||||||
|
},
|
||||||
|
dim: {
|
||||||
|
h: "full",
|
||||||
|
w: "full",
|
||||||
|
},
|
||||||
|
name: "action",
|
||||||
|
type: "item",
|
||||||
|
childs: [],
|
||||||
|
script: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
script: {},
|
||||||
|
component: {
|
||||||
|
id: "df4e3552-3221-496c-b07b-0c1295f811be",
|
||||||
|
props: {
|
||||||
|
child: {
|
||||||
|
idx: 3,
|
||||||
|
meta: {
|
||||||
|
type: "content-element",
|
||||||
|
},
|
||||||
|
name: "prop_3",
|
||||||
|
type: "string",
|
||||||
|
value: '"hello"',
|
||||||
|
content: {
|
||||||
|
id: createId(),
|
||||||
|
adv: {
|
||||||
|
css: "",
|
||||||
|
js: "<>{children}</>",
|
||||||
|
jsBuilt:
|
||||||
|
"render(/* @__PURE__ */ React.createElement(React.Fragment, null, children));\n",
|
||||||
|
},
|
||||||
|
dim: {
|
||||||
|
h: "full",
|
||||||
|
w: "full",
|
||||||
|
},
|
||||||
|
name: "prop_3",
|
||||||
|
type: "item",
|
||||||
|
childs: [
|
||||||
|
{
|
||||||
|
id: createId(),
|
||||||
|
adv: {
|
||||||
|
js: '<>\n {cell.key === "id" && (\n <div {...props} className={cx(props.className, "")}>\n {cell.value}\n </div>\n )}\n</>',
|
||||||
|
css: "",
|
||||||
|
jsBuilt:
|
||||||
|
'render(\n React.createElement(\n React.Fragment,\n null,\n cell.key === "id" &&\n React.createElement(\n "div",\n Object.assign({}, props, { className: cx(props.className, "") }),\n cell.value\n )\n )\n)',
|
||||||
|
},
|
||||||
|
dim: {
|
||||||
|
h: "full",
|
||||||
|
w: "full",
|
||||||
|
},
|
||||||
|
name: "id",
|
||||||
|
type: "item",
|
||||||
|
childs: [],
|
||||||
|
script: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: createId(),
|
||||||
|
adv: {
|
||||||
|
js: '<>\n {cell.key === "nama_aset_komersial" && (\n <div {...props} className={cx(props.className, "")}>\n {cell.value}\n </div>\n )}\n</>',
|
||||||
|
css: "",
|
||||||
|
jsBuilt:
|
||||||
|
'render(\n React.createElement(\n React.Fragment,\n null,\n cell.key === "nama_aset_komersial" &&\n React.createElement(\n "div",\n Object.assign({}, props, { className: cx(props.className, "") }),\n cell.value\n )\n )\n)',
|
||||||
|
},
|
||||||
|
dim: {
|
||||||
|
h: "full",
|
||||||
|
w: "full",
|
||||||
|
},
|
||||||
|
name: "nama_aset_komersial",
|
||||||
|
type: "item",
|
||||||
|
childs: [],
|
||||||
|
script: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: createId(),
|
||||||
|
adv: {
|
||||||
|
js: '<>\n {cell.key === "m_cabang" && (\n <div {...props} className={cx(props.className, "")}>\n {cell.value?.["nama_cabang"]}\n </div>\n )}\n</>',
|
||||||
|
css: "",
|
||||||
|
jsBuilt:
|
||||||
|
'render(\n React.createElement(\n React.Fragment,\n null,\n cell.key === "m_cabang" &&\n React.createElement(\n "div",\n Object.assign({}, props, { className: cx(props.className, "") }),\n cell.value?.["nama_cabang"]\n )\n )\n)',
|
||||||
|
},
|
||||||
|
dim: {
|
||||||
|
h: "full",
|
||||||
|
w: "full",
|
||||||
|
},
|
||||||
|
name: "m_cabang",
|
||||||
|
type: "item",
|
||||||
|
childs: [],
|
||||||
|
script: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: createId(),
|
||||||
|
adv: {
|
||||||
|
js: '<>\n {cell.key === "m_regional" && (\n <div {...props} className={cx(props.className, "")}>\n {cell.value?.["regional"]}\n </div>\n )}\n</>',
|
||||||
|
css: "",
|
||||||
|
jsBuilt:
|
||||||
|
'render(\n React.createElement(\n React.Fragment,\n null,\n cell.key === "m_regional" &&\n React.createElement(\n "div",\n Object.assign({}, props, { className: cx(props.className, "") }),\n cell.value?.["regional"]\n )\n )\n)',
|
||||||
|
},
|
||||||
|
dim: {
|
||||||
|
h: "full",
|
||||||
|
w: "full",
|
||||||
|
},
|
||||||
|
name: "m_regional",
|
||||||
|
type: "item",
|
||||||
|
childs: [],
|
||||||
|
script: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: createId(),
|
||||||
|
adv: {
|
||||||
|
js: '<>\n {cell.key === "luas_setifikat" && (\n <div {...props} className={cx(props.className, "")}>\n {cell.value}\n </div>\n )}\n</>',
|
||||||
|
css: "",
|
||||||
|
jsBuilt:
|
||||||
|
'render(\n React.createElement(\n React.Fragment,\n null,\n cell.key === "luas_setifikat" &&\n React.createElement(\n "div",\n Object.assign({}, props, { className: cx(props.className, "") }),\n cell.value\n )\n )\n)',
|
||||||
|
},
|
||||||
|
dim: {
|
||||||
|
h: "full",
|
||||||
|
w: "full",
|
||||||
|
},
|
||||||
|
name: "luas_setifikat",
|
||||||
|
type: "item",
|
||||||
|
childs: [],
|
||||||
|
script: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: createId(),
|
||||||
|
adv: {
|
||||||
|
js: '<>\n {cell.key === "tanggal_sertifikat" && (\n <div {...props} className={cx(props.className, "")}>\n {cell.value}\n </div>\n )}\n</>',
|
||||||
|
css: "",
|
||||||
|
jsBuilt:
|
||||||
|
'render(\n React.createElement(\n React.Fragment,\n null,\n cell.key === "tanggal_sertifikat" &&\n React.createElement(\n "div",\n Object.assign({}, props, { className: cx(props.className, "") }),\n cell.value\n )\n )\n)',
|
||||||
|
},
|
||||||
|
dim: {
|
||||||
|
h: "full",
|
||||||
|
w: "full",
|
||||||
|
},
|
||||||
|
name: "tanggal_sertifikat",
|
||||||
|
type: "item",
|
||||||
|
childs: [],
|
||||||
|
script: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
hidden: false,
|
||||||
|
},
|
||||||
|
typings:
|
||||||
|
"const typings = {\n cell: `{ key: string, value: any }`,\n row: `any`,\n idx: `number`,\n rows: `any[]`,\n}",
|
||||||
|
valueBuilt: ' "hello";\n',
|
||||||
|
},
|
||||||
|
columns: {
|
||||||
|
idx: 0,
|
||||||
|
meta: {
|
||||||
|
type: "text",
|
||||||
|
},
|
||||||
|
name: "prop_1",
|
||||||
|
type: "string",
|
||||||
|
value:
|
||||||
|
'async (): Promise<\n { key: string; name: string; width?: number; frozen?: boolean }[]\n > => {\n return [\n { key: "id", name: "#", width: 60, frozen: true },\n {"key":"nama_aset_komersial","name":"Nama Aset Komersial"},\n {"key":"m_cabang","name":"Cabang"},\n {"key":"m_regional","name":"Regional"},\n {"key":"luas_setifikat","name":"Luas Setifikat"},\n {"key":"tanggal_sertifikat","name":"Tanggal Sertifikat"}\n ];\n }',
|
||||||
|
valueBuilt:
|
||||||
|
' async () => {\n return [\n { key: "id", name: "#", width: 60, frozen: true },\n { "key": "nama_aset_komersial", "name": "Nama Aset Komersial" },\n { "key": "m_cabang", "name": "Cabang" },\n { "key": "m_regional", "name": "Regional" },\n { "key": "luas_setifikat", "name": "Luas Setifikat" },\n { "key": "tanggal_sertifikat", "name": "Tanggal Sertifikat" }\n ];\n};\n',
|
||||||
|
},
|
||||||
|
on_load: {
|
||||||
|
idx: 1,
|
||||||
|
meta: {
|
||||||
|
type: "text",
|
||||||
|
},
|
||||||
|
name: "prop_1",
|
||||||
|
type: "string",
|
||||||
|
value:
|
||||||
|
"async () => {\n if (isEditor)\n return [\n {\n id: createId(),\n },\n });\n\n return items;\n}",
|
||||||
|
valueBuilt:
|
||||||
|
' async () => {\n if (isEditor)\n return [\n {\n id: createId(),\n tanggal_sertifikat: "sample"\n }\n ];\n const items = await db.m_aset.findMany({\n select: {\n id: true,\n nama_aset_komersial: true,\n m_cabang: {\n select: {\n id: true,\n nama_cabang: true\n }\n },\n m_regional: {\n select: {\n regional: true,\n id: true\n }\n },\n luas_setifikat: true,\n tanggal_sertifikat: true\n },\n orderBy: {\n id: "desc"\n }\n });\n return items;\n};\n',
|
||||||
|
},
|
||||||
|
generate: {
|
||||||
|
idx: 5,
|
||||||
|
name: "prop_5",
|
||||||
|
type: "string",
|
||||||
|
value: '"n"',
|
||||||
|
valueBuilt: '"n"',
|
||||||
|
meta: {
|
||||||
|
type: "option",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
gen_table: {
|
||||||
|
idx: 7,
|
||||||
|
name: "prop_7",
|
||||||
|
type: "string",
|
||||||
|
value: '""',
|
||||||
|
valueBuilt: '""',
|
||||||
|
meta: {
|
||||||
|
type: "option",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
gen_fields: {
|
||||||
|
idx: 9,
|
||||||
|
name: "prop_9",
|
||||||
|
type: "string",
|
||||||
|
value: "[]",
|
||||||
|
valueBuilt: "[]",
|
||||||
|
meta: {
|
||||||
|
type: "option",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
row_click: {
|
||||||
|
idx: 11,
|
||||||
|
name: "prop_11",
|
||||||
|
type: "string",
|
||||||
|
value:
|
||||||
|
"({ row, rows, idx, event }: OnRowClick) => {\n md.selected = row;\n md.render();\n};\n\ntype OnRowClick = {\n row: any;\n rows: any[];\n idx: any;\n event: React.MouseEvent<HTMLDivElement, MouseEvent>;\n}",
|
||||||
|
valueBuilt:
|
||||||
|
" ({ row, rows, idx, event }) => {\n md.selected = row;\n md.render();\n};\n",
|
||||||
|
meta: {
|
||||||
|
type: "text",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
gen_button: {
|
||||||
|
idx: 11,
|
||||||
|
name: "prop_11",
|
||||||
|
type: "string",
|
||||||
|
value: '"hello"',
|
||||||
|
valueBuilt: '"hello"',
|
||||||
|
meta: {
|
||||||
|
type: "button",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ref_ids: {},
|
||||||
|
instances: {},
|
||||||
|
},
|
||||||
|
originalId: "gxwni8zmj8zhiogfa52eoq6a",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
adv: {
|
||||||
|
css: "& {\n display: flex;\n\n .rdg-row {\n cursor: pointer;\n }\n\n // &.mobile {}\n // &.desktop {}\n // &:hover {}\n}",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return { content: res, props: res.childs[0].component.props };
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
import get from "lodash.get";
|
||||||
|
import { GFCol as Col, GFCol, formatName } from "../utils";
|
||||||
|
import { NewFieldArg } from "../gen_form/new_field";
|
||||||
|
import { gen_header } from "./gen_header";
|
||||||
|
import { gen_master } from "./gen_master";
|
||||||
|
import { on_load as table_on_load } from "../gen_table/on_load";
|
||||||
|
import { on_load as form_on_load } from "../gen_form/on_load";
|
||||||
|
import { on_submit as form_on_submit } from "../gen_form/on_submit";
|
||||||
|
import { gen_columns } from "../gen_table/columns";
|
||||||
|
import { newField as table_new_field } from "../gen_table/new_field";
|
||||||
|
import { gen_detail } from "./gen_detail";
|
||||||
|
import { form_before_load } from "./form_before_load";
|
||||||
|
|
||||||
|
export const gen_md = (modify: (data: any) => void, data: any) => {
|
||||||
|
const table = JSON.parse(data.gen_table.value);
|
||||||
|
const fields = JSON.parse(data.gen_fields.value);
|
||||||
|
const select = {} as any;
|
||||||
|
const columns = [] as GFCol[];
|
||||||
|
const new_fields: NewFieldArg[] = [];
|
||||||
|
|
||||||
|
let pk: Col | null = null;
|
||||||
|
let pks: Record<string, string> = {};
|
||||||
|
|
||||||
|
for (let sel of fields) {
|
||||||
|
if (typeof sel === "object") {
|
||||||
|
const col = JSON.parse(sel.value) as Col;
|
||||||
|
select[col.name] = {};
|
||||||
|
const fields: string[] = [];
|
||||||
|
for (let s of sel.checked) {
|
||||||
|
const c = JSON.parse(s) as Col;
|
||||||
|
if (c.is_pk) {
|
||||||
|
pks[col.name] = c.name;
|
||||||
|
fields.push(`::${c.name}`);
|
||||||
|
select[col.name] = { select: { [c.name]: true } };
|
||||||
|
col.relation = { table: col.name, pk: sel.name };
|
||||||
|
} else {
|
||||||
|
fields.push(c.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
columns.push(col);
|
||||||
|
new_fields.push({
|
||||||
|
name: col.name,
|
||||||
|
label: formatName(col.name),
|
||||||
|
required: false,
|
||||||
|
type: "relation",
|
||||||
|
relation: {
|
||||||
|
table: col.name,
|
||||||
|
fields,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const col = JSON.parse(sel) as Col;
|
||||||
|
if (col.is_pk) {
|
||||||
|
pk = col;
|
||||||
|
} else {
|
||||||
|
new_fields.push({
|
||||||
|
name: col.name,
|
||||||
|
label: formatName(col.name),
|
||||||
|
required: !col.optional,
|
||||||
|
type: "text",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
select[col.name] = true;
|
||||||
|
columns.push(col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result: any = {};
|
||||||
|
if (pk) {
|
||||||
|
result["header"] = data["header"];
|
||||||
|
result["header"].content = gen_header();
|
||||||
|
|
||||||
|
result["master"] = data["master"];
|
||||||
|
const { content, props } = gen_master();
|
||||||
|
props["columns"].value = gen_columns(columns);
|
||||||
|
props["on_load"].value = table_on_load({ pk: pk.name, table, select, pks });
|
||||||
|
props["child"].content.childs = table_new_field(select, pks);
|
||||||
|
result["master"].content = content;
|
||||||
|
|
||||||
|
result["detail"] = data["detail"];
|
||||||
|
const detail = gen_detail();
|
||||||
|
const title = JSON.parse(get(data, "title.value"));
|
||||||
|
const before_load = form_before_load(pk.name, title, "");
|
||||||
|
|
||||||
|
detail.props["on_load"].value = form_on_load({ pk, pks, select, table });
|
||||||
|
detail.props["on_submit"].value = form_on_submit({
|
||||||
|
pk,
|
||||||
|
table,
|
||||||
|
select,
|
||||||
|
pks,
|
||||||
|
});
|
||||||
|
result["detail"].content = detail.content;
|
||||||
|
}
|
||||||
|
// modify(result);
|
||||||
|
|
||||||
|
alert("Prop Generated!");
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { GFCol, formatName } from "../utils";
|
||||||
|
|
||||||
|
export const gen_columns = (cols: GFCol[]) => {
|
||||||
|
return `async (): Promise<
|
||||||
|
{ key: string; name: string; width?: number; frozen?: boolean }[]
|
||||||
|
> => {
|
||||||
|
return [
|
||||||
|
{ key: "id", name: "#", width: 60, frozen: true },
|
||||||
|
${cols
|
||||||
|
.filter((e) => {
|
||||||
|
if (e.is_pk) return false;
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.map((e) => {
|
||||||
|
return (
|
||||||
|
" " +
|
||||||
|
JSON.stringify({
|
||||||
|
key: e.name,
|
||||||
|
name: formatName(e.name),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.join(",\n")}
|
||||||
|
];
|
||||||
|
}`;
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
import capitalize from "lodash.capitalize";
|
||||||
|
import { GFCol } from "../utils";
|
||||||
|
import { gen_columns } from "./columns";
|
||||||
|
import { newField } from "./new_field";
|
||||||
|
import { on_load } from "./on_load";
|
||||||
|
|
||||||
|
export const gen_table = (modify: (data: any) => void, data: any) => {
|
||||||
|
const table = JSON.parse(data.gen_table.value) as string;
|
||||||
|
const fields = JSON.parse(data.gen_fields.value) as (
|
||||||
|
| string
|
||||||
|
| { value: string; checked: string[] }
|
||||||
|
)[];
|
||||||
|
const select = {} as any;
|
||||||
|
const columns = [] as GFCol[];
|
||||||
|
let pk = "";
|
||||||
|
let pks: Record<string, string> = {};
|
||||||
|
|
||||||
|
for (const f of fields) {
|
||||||
|
if (typeof f === "string") {
|
||||||
|
const col = JSON.parse(f) as GFCol;
|
||||||
|
columns.push(col);
|
||||||
|
select[col.name] = true;
|
||||||
|
if (col.is_pk) pk = col.name;
|
||||||
|
} else {
|
||||||
|
const col = JSON.parse(f.value) as GFCol;
|
||||||
|
const subsel: any = {};
|
||||||
|
for (const s of f.checked) {
|
||||||
|
const sel = JSON.parse(s) as GFCol;
|
||||||
|
if (sel.is_pk) {
|
||||||
|
pks[col.name] = sel.name;
|
||||||
|
col.relation = { table: col.name, pk: sel.name };
|
||||||
|
}
|
||||||
|
subsel[sel.name] = true;
|
||||||
|
}
|
||||||
|
select[col.name] = { select: subsel };
|
||||||
|
columns.push(col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = {} as any;
|
||||||
|
if (data["columns"]) {
|
||||||
|
result["columns"] = data["columns"];
|
||||||
|
result["columns"].value = gen_columns(columns);
|
||||||
|
}
|
||||||
|
if (data["on_load"]) {
|
||||||
|
result["on_load"] = data["on_load"];
|
||||||
|
result["on_load"].value = on_load({ pk, table, select, pks });
|
||||||
|
}
|
||||||
|
if (data["child"]) {
|
||||||
|
result["child"] = data["child"];
|
||||||
|
result["child"].content.childs = newField(select, pks);
|
||||||
|
}
|
||||||
|
modify(result);
|
||||||
|
|
||||||
|
alert("Prop Generated!");
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatName = (name: string) => {
|
||||||
|
return name
|
||||||
|
.split("_")
|
||||||
|
.filter((e) => e.length > 1)
|
||||||
|
.map((e) => capitalize(e))
|
||||||
|
.join(" ");
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
import { createId } from "@paralleldrive/cuid2";
|
||||||
|
import { GFCol } from "../utils";
|
||||||
|
|
||||||
|
export const newField = (select: any, pks: Record<string, string>) => {
|
||||||
|
const result = [];
|
||||||
|
|
||||||
|
for (const [k, v] of Object.entries(select) as any) {
|
||||||
|
if (typeof v === "object") {
|
||||||
|
const res = Object.keys(v.select)
|
||||||
|
.filter((e) => e !== pks[k])
|
||||||
|
.map((e) => `cell.value?.["${e}"]`)
|
||||||
|
.join('+ " " +');
|
||||||
|
|
||||||
|
result.push({
|
||||||
|
id: createId(),
|
||||||
|
adv: {
|
||||||
|
js: `\
|
||||||
|
<>
|
||||||
|
{cell.key === "${k}" && (
|
||||||
|
<div {...props} className={cx(props.className, "")}>
|
||||||
|
{${res}}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>`,
|
||||||
|
css: "",
|
||||||
|
jsBuilt: `\
|
||||||
|
render(
|
||||||
|
React.createElement(
|
||||||
|
React.Fragment,
|
||||||
|
null,
|
||||||
|
cell.key === "${k}" &&
|
||||||
|
React.createElement(
|
||||||
|
"div",
|
||||||
|
Object.assign({}, props, { className: cx(props.className, "") }),
|
||||||
|
${res}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)`,
|
||||||
|
},
|
||||||
|
dim: {
|
||||||
|
h: "full",
|
||||||
|
w: "full",
|
||||||
|
},
|
||||||
|
name: k,
|
||||||
|
type: "item",
|
||||||
|
childs: [],
|
||||||
|
script: {},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
result.push({
|
||||||
|
id: createId(),
|
||||||
|
adv: {
|
||||||
|
js: `\
|
||||||
|
<>
|
||||||
|
{cell.key === "${k}" && (
|
||||||
|
<div {...props} className={cx(props.className, "")}>
|
||||||
|
{cell.value}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>`,
|
||||||
|
css: "",
|
||||||
|
jsBuilt: `\
|
||||||
|
render(
|
||||||
|
React.createElement(
|
||||||
|
React.Fragment,
|
||||||
|
null,
|
||||||
|
cell.key === "${k}" &&
|
||||||
|
React.createElement(
|
||||||
|
"div",
|
||||||
|
Object.assign({}, props, { className: cx(props.className, "") }),
|
||||||
|
cell.value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)`,
|
||||||
|
},
|
||||||
|
dim: {
|
||||||
|
h: "full",
|
||||||
|
w: "full",
|
||||||
|
},
|
||||||
|
name: k,
|
||||||
|
type: "item",
|
||||||
|
childs: [],
|
||||||
|
script: {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
import { GFCol } from "../utils";
|
||||||
|
|
||||||
|
export const on_load = ({
|
||||||
|
pk,
|
||||||
|
table,
|
||||||
|
select,
|
||||||
|
pks,
|
||||||
|
}: {
|
||||||
|
pk: string;
|
||||||
|
table: string;
|
||||||
|
select: any;
|
||||||
|
pks: Record<string, string>;
|
||||||
|
}) => {
|
||||||
|
const sample = {} as any;
|
||||||
|
|
||||||
|
for (const [k, v] of Object.entries(select) as any) {
|
||||||
|
if (typeof v === "object") {
|
||||||
|
sample[k] = {};
|
||||||
|
|
||||||
|
Object.keys(v.select)
|
||||||
|
.filter((e) => e !== pks[k])
|
||||||
|
.map((e) => {
|
||||||
|
sample[k][e] = "sample";
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
sample[k] = "sample";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return `\
|
||||||
|
async () => {
|
||||||
|
if (isEditor) return [${JSON.stringify(sample)}];
|
||||||
|
|
||||||
|
const items = await db.${table}.findMany({
|
||||||
|
select: ${JSON.stringify(select, null, 2).split("\n").join("\n ")},
|
||||||
|
orderBy: {
|
||||||
|
${pk}: "desc"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}`;
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
import capitalize from "lodash.capitalize";
|
||||||
|
|
||||||
|
export type GFCol = {
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
is_pk: boolean;
|
||||||
|
optional: boolean;
|
||||||
|
relation?: {
|
||||||
|
table: string;
|
||||||
|
pk: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const formatName = (name: string) => {
|
||||||
|
return (name || "")
|
||||||
|
.split("_")
|
||||||
|
.filter((e) => e.length > 1)
|
||||||
|
.map((e) => capitalize(e))
|
||||||
|
.join(" ");
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue