wip fix
This commit is contained in:
parent
99e6c1c530
commit
0a02fc3c0e
|
|
@ -40,6 +40,11 @@ export const Relation: FC<RelationProps> = ({
|
|||
if (form) {
|
||||
local.status = "loading";
|
||||
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 select = {} as any;
|
||||
local.pk_field = "";
|
||||
|
|
@ -67,6 +72,8 @@ export const Relation: FC<RelationProps> = ({
|
|||
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) => {
|
||||
if (typeof value === "object") {
|
||||
|
|
@ -109,9 +116,10 @@ export const Relation: FC<RelationProps> = ({
|
|||
content={
|
||||
<div
|
||||
className={cx(
|
||||
"c-text-sm",
|
||||
"c-text-sm c-relative c-overflow-auto",
|
||||
css`
|
||||
width: ${local.ref.input?.clientWidth || 100}px;
|
||||
max-height: 300px;
|
||||
`
|
||||
)}
|
||||
>
|
||||
|
|
@ -124,15 +132,25 @@ export const Relation: FC<RelationProps> = ({
|
|||
</>
|
||||
)}
|
||||
{local.status === "ready" && (
|
||||
<>
|
||||
<div className="c-flex c-flex-1 c-flex-col">
|
||||
{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 (
|
||||
<div
|
||||
tabIndex={0}
|
||||
key={item.value + "_" + idx}
|
||||
className={cx(
|
||||
"c-px-3 c-py-1 cursor-pointer option-item",
|
||||
item.value === value
|
||||
is_active
|
||||
? "c-bg-blue-600 c-text-white"
|
||||
: "hover:c-bg-blue-50",
|
||||
idx > 0 && "c-border-t",
|
||||
|
|
@ -149,11 +167,11 @@ export const Relation: FC<RelationProps> = ({
|
|||
}
|
||||
}}
|
||||
>
|
||||
{item.label}
|
||||
{item.label || "-"}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
|
|
@ -197,7 +215,7 @@ export const Relation: FC<RelationProps> = ({
|
|||
/>
|
||||
{!local.open && (
|
||||
<div className="c-absolute c-text-sm c-inset-0 c-px-3 c-flex c-items-center">
|
||||
{local.label}
|
||||
{local.label || "-"}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,16 +1,22 @@
|
|||
import { Form as FForm } from "@/comps/ui/form";
|
||||
import { Toaster } from "@/comps/ui/sonner";
|
||||
import { useLocal } from "@/utils/use-local";
|
||||
import { FC } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { toast } from "sonner";
|
||||
import { FormHook } from "./utils/utils";
|
||||
import { AlertTriangle, Check, Loader2 } from "lucide-react";
|
||||
import { cn } from "@/utils";
|
||||
|
||||
export const Form: FC<{
|
||||
on_init: (arg: { submit: any }) => any;
|
||||
on_load: () => any;
|
||||
on_submit: (arg: { form: any; error: any }) => any;
|
||||
on_submit: (arg: { form: any; error: any }) => Promise<any>;
|
||||
body: any;
|
||||
form: FormHook;
|
||||
PassProp: any;
|
||||
cache: () => any;
|
||||
layout: "auto" | "1-col" | "2-col";
|
||||
}> = ({
|
||||
on_init,
|
||||
|
|
@ -19,10 +25,11 @@ export const Form: FC<{
|
|||
form,
|
||||
PassProp,
|
||||
on_submit,
|
||||
cache,
|
||||
layout: _layout,
|
||||
}) => {
|
||||
const form_hook = useForm<any>({
|
||||
defaultValues: on_load,
|
||||
defaultValues: {},
|
||||
});
|
||||
|
||||
const local = useLocal({
|
||||
|
|
@ -33,6 +40,13 @@ export const Form: FC<{
|
|||
});
|
||||
|
||||
form.hook = form_hook;
|
||||
if (!form.cache && typeof cache === "function") {
|
||||
try {
|
||||
form.cache = cache() || {};
|
||||
} catch (e) {}
|
||||
}
|
||||
if (!form.cache) form.cache = {};
|
||||
|
||||
if (!form.validation) {
|
||||
form.validation = {};
|
||||
}
|
||||
|
|
@ -45,7 +59,19 @@ export const Form: FC<{
|
|||
|
||||
const submit = () => {
|
||||
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();
|
||||
form.hook.clearErrors();
|
||||
for (const [k, v] of Object.entries(form.validation)) {
|
||||
|
|
@ -61,22 +87,88 @@ export const Form: FC<{
|
|||
}
|
||||
}
|
||||
|
||||
on_submit({
|
||||
await on_submit({
|
||||
form: data,
|
||||
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);
|
||||
};
|
||||
|
||||
if (!local.init) {
|
||||
local.init = true;
|
||||
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;
|
||||
|
||||
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 (
|
||||
<FForm {...form_hook}>
|
||||
{toaster_el && createPortal(<Toaster cn={cn} />, toaster_el)}
|
||||
<form
|
||||
className={
|
||||
"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;
|
||||
}}
|
||||
onSubmit={(e) => {
|
||||
console.log("on submit");
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
submit();
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ export type FormHook = {
|
|||
ref: HTMLFormElement;
|
||||
submit: any;
|
||||
label: Record<string, string>;
|
||||
cache: any;
|
||||
validation: Record<string, "required">;
|
||||
render: () => void;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,15 +1,26 @@
|
|||
import { useLocal } from "@/utils/use-local";
|
||||
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 { Skeleton } from "../ui/skeleton";
|
||||
|
||||
type OnRowClick = {
|
||||
row: any;
|
||||
rows: any[];
|
||||
idx: any;
|
||||
event: React.MouseEvent<HTMLDivElement, MouseEvent>;
|
||||
};
|
||||
export const Table: FC<{
|
||||
columns: () => Promise<ColumnOrColumnGroup<any>[]>;
|
||||
on_load: () => Promise<any[]>;
|
||||
child: any;
|
||||
PassProp: any;
|
||||
}> = ({ columns, on_load, child, PassProp }) => {
|
||||
row_click: (arg: OnRowClick) => void;
|
||||
}> = ({ columns, on_load, child, PassProp, row_click }) => {
|
||||
const local = useLocal({
|
||||
loading: false,
|
||||
data: undefined as unknown as any[],
|
||||
|
|
@ -56,6 +67,7 @@ export const Table: FC<{
|
|||
|
||||
return (
|
||||
<TableInternal
|
||||
row_click={row_click}
|
||||
columns={local.columns}
|
||||
data={local.loading ? undefined : local.data}
|
||||
render={local.render}
|
||||
|
|
@ -67,7 +79,8 @@ const TableInternal: FC<{
|
|||
columns: ColumnOrColumnGroup<any>[];
|
||||
data?: any[];
|
||||
render: () => void;
|
||||
}> = ({ columns, data, render }) => {
|
||||
row_click: (arg: OnRowClick) => void;
|
||||
}> = ({ columns, data, render, row_click }) => {
|
||||
const local = useLocal({
|
||||
width: 0,
|
||||
height: 0,
|
||||
|
|
@ -134,7 +147,6 @@ const TableInternal: FC<{
|
|||
>
|
||||
<DataGrid
|
||||
columns={columns}
|
||||
selectedRows={null}
|
||||
sortColumns={sort}
|
||||
onSortColumnsChange={([col]) => {
|
||||
local.sort = [];
|
||||
|
|
@ -160,6 +172,24 @@ const TableInternal: FC<{
|
|||
typeof data === "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: (
|
||||
<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">
|
||||
|
|
|
|||
|
|
@ -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 TabsPrimitive from "@radix-ui/react-tabs"
|
||||
import * as React from "react";
|
||||
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<
|
||||
React.ElementRef<typeof TabsPrimitive.List>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> & {
|
||||
overrideClassName?: boolean;
|
||||
}
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.List
|
||||
ref={ref}
|
||||
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
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TabsList.displayName = TabsPrimitive.List.displayName
|
||||
));
|
||||
TabsList.displayName = TabsPrimitive.List.displayName;
|
||||
|
||||
const TabsTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger> & {
|
||||
overrideClassName?: boolean;
|
||||
}
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.Trigger
|
||||
ref={ref}
|
||||
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
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
|
||||
));
|
||||
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
||||
|
||||
const TabsContent = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.Content>,
|
||||
|
|
@ -47,7 +55,7 @@ const TabsContent = React.forwardRef<
|
|||
)}
|
||||
{...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 { on_load } from "./gen_form/on_load";
|
||||
import { on_submit } from "./gen_form/on_submit";
|
||||
import { GFCol as Col } from "./gen_form/type";
|
||||
import { NewFieldArg, newField } from "./gen_form/new_field";
|
||||
import capitalize from "lodash.capitalize";
|
||||
import { GFCol as Col, formatName } from "../utils";
|
||||
import { NewFieldArg, newField } from "./new_field";
|
||||
import { on_load } from "./on_load";
|
||||
import { on_submit } from "./on_submit";
|
||||
|
||||
export const gen_form = (modify: (data: any) => void, data: any) => {
|
||||
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;
|
||||
}
|
||||
}
|
||||
console.log(new_fields);
|
||||
|
||||
const result: any = {};
|
||||
if (pk) {
|
||||
|
|
@ -76,7 +74,6 @@ export const gen_form = (modify: (data: any) => void, data: any) => {
|
|||
result["body"] = data["body"];
|
||||
const childs = get(result, "body.content.childs");
|
||||
if (Array.isArray(childs)) {
|
||||
|
||||
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!");
|
||||
};
|
||||
|
||||
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,
|
||||
name: "prop_11",
|
||||
type: "string",
|
||||
value:
|
||||
'async () => {\n return {\n orderBy: {\n regional: "asc",\n },\n };\n}',
|
||||
valueBuilt:
|
||||
' async () => {\n return {\n orderBy: {\n regional: "asc"\n }\n };\n};\n',
|
||||
value: "async () => {\n return {\n };\n}",
|
||||
valueBuilt: " async () => {\n return {\n };\n};\n",
|
||||
meta: {
|
||||
type: "text",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,21 +1,26 @@
|
|||
import { GFCol } from "./type";
|
||||
import { GFCol } from "../utils";
|
||||
|
||||
export const on_load = ({
|
||||
pk,
|
||||
table,
|
||||
select,
|
||||
pks,
|
||||
opt,
|
||||
}: {
|
||||
pk: GFCol;
|
||||
table: string;
|
||||
select: any;
|
||||
pks: Record<string, string>;
|
||||
opt?: {
|
||||
before_load: string;
|
||||
};
|
||||
}) => {
|
||||
return `\
|
||||
async (opt) => {
|
||||
if (isEditor) return {};
|
||||
|
||||
let id = ${pk.type === "int" ? "parseInt(params.id)" : "params.id"};
|
||||
${opt?.before_load}
|
||||
|
||||
if (id){
|
||||
const item = await db.${table}.findFirst({
|
||||
|
|
@ -30,7 +35,8 @@ async (opt) => {
|
|||
.map(([k, v]) => {
|
||||
return `\
|
||||
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")}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { GFCol } from "./type";
|
||||
import { GFCol } from "../utils";
|
||||
|
||||
export const on_submit = ({
|
||||
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