fix
This commit is contained in:
parent
1a65145ab7
commit
5e8b25d174
|
|
@ -1,261 +0,0 @@
|
|||
import {
|
||||
FloatingFocusManager,
|
||||
FloatingOverlay,
|
||||
FloatingPortal,
|
||||
useClick,
|
||||
useDismiss,
|
||||
useFloating,
|
||||
useId,
|
||||
useInteractions,
|
||||
useMergeRefs,
|
||||
useRole,
|
||||
} from "@floating-ui/react";
|
||||
import { useLocal } from "lib/utils/use-local";
|
||||
import * as React from "react";
|
||||
|
||||
interface ModalOptions {
|
||||
initialOpen?: boolean;
|
||||
open?: boolean;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
fade?: boolean;
|
||||
}
|
||||
|
||||
export function useModal({
|
||||
initialOpen = true,
|
||||
open: controlledOpen,
|
||||
onOpenChange: setControlledOpen,
|
||||
}: ModalOptions) {
|
||||
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(initialOpen);
|
||||
const [labelId, setLabelId] = React.useState<string | undefined>();
|
||||
const [descriptionId, setDescriptionId] = React.useState<
|
||||
string | undefined
|
||||
>();
|
||||
|
||||
const open = controlledOpen ?? uncontrolledOpen;
|
||||
const setOpen = setControlledOpen ?? setUncontrolledOpen;
|
||||
|
||||
const data = useFloating({
|
||||
open,
|
||||
onOpenChange: setOpen,
|
||||
});
|
||||
|
||||
const context = data.context;
|
||||
|
||||
const click = useClick(context, {
|
||||
enabled: controlledOpen == null,
|
||||
});
|
||||
const dismiss = useDismiss(context, {
|
||||
outsidePressEvent: "mousedown",
|
||||
escapeKey: false,
|
||||
});
|
||||
const role = useRole(context);
|
||||
|
||||
const interactions = useInteractions([click, dismiss, role]);
|
||||
|
||||
return React.useMemo(
|
||||
() => ({
|
||||
open,
|
||||
setOpen,
|
||||
...interactions,
|
||||
...data,
|
||||
labelId,
|
||||
descriptionId,
|
||||
setLabelId,
|
||||
setDescriptionId,
|
||||
}),
|
||||
[open, setOpen, interactions, data, labelId, descriptionId]
|
||||
);
|
||||
}
|
||||
|
||||
type ContextType =
|
||||
| (ReturnType<typeof useModal> & {
|
||||
setLabelId: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
setDescriptionId: React.Dispatch<
|
||||
React.SetStateAction<string | undefined>
|
||||
>;
|
||||
})
|
||||
| null;
|
||||
|
||||
const ModalContext = React.createContext<ContextType>(null);
|
||||
|
||||
export const useModalContext = () => {
|
||||
const context = React.useContext(ModalContext);
|
||||
|
||||
if (context == null) {
|
||||
throw new Error("Modal components must be wrapped in <Modal />");
|
||||
}
|
||||
|
||||
return context;
|
||||
};
|
||||
|
||||
export function Modal({
|
||||
children,
|
||||
...options
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
} & ModalOptions) {
|
||||
const dialog = useModal(options);
|
||||
return (
|
||||
<ModalContext.Provider value={dialog}>
|
||||
<ModalContent fade={options.fade} className={cx("modal", "outline-none")}>
|
||||
{children}
|
||||
</ModalContent>
|
||||
</ModalContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
interface ModalTriggerProps {
|
||||
children: React.ReactNode;
|
||||
asChild?: boolean;
|
||||
}
|
||||
|
||||
export const ModalTrigger = React.forwardRef<
|
||||
HTMLElement,
|
||||
React.HTMLProps<HTMLElement> & ModalTriggerProps
|
||||
>(function ModalTrigger({ children, asChild = false, ...props }, propRef) {
|
||||
const context = useModalContext();
|
||||
const childrenRef = (children as any).ref;
|
||||
const ref = useMergeRefs([context.refs.setReference, propRef, childrenRef]);
|
||||
|
||||
// `asChild` allows the user to pass any element as the anchor
|
||||
if (asChild && React.isValidElement(children)) {
|
||||
return React.cloneElement(
|
||||
children,
|
||||
context.getReferenceProps({
|
||||
ref,
|
||||
...props,
|
||||
...children.props,
|
||||
"data-state": context.open ? "open" : "closed",
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
data-state={context.open ? "open" : "closed"}
|
||||
{...context.getReferenceProps(props as any)}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
});
|
||||
|
||||
export const ModalContent = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLProps<HTMLDivElement> & { fade?: boolean }
|
||||
>(function ModalContent(props, propRef) {
|
||||
const local = useLocal({ preview: false, timeout: null as any });
|
||||
const { context: floatingContext, ...context } = useModalContext();
|
||||
const ref = useMergeRefs([context.refs.setFloating, propRef]);
|
||||
|
||||
if (!floatingContext.open) return null;
|
||||
const _props = { ...props };
|
||||
if (typeof _props.fade !== "undefined") {
|
||||
delete _props.fade;
|
||||
}
|
||||
|
||||
const floatingDivProps = context.getFloatingProps(_props);
|
||||
return (
|
||||
<FloatingPortal>
|
||||
<FloatingOverlay
|
||||
className={cx(
|
||||
"modal-overlay",
|
||||
"flex items-center justify-center transition-all ",
|
||||
css`
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
`,
|
||||
local.preview ? "opacity-20 duration-1000" : "duration-300"
|
||||
)}
|
||||
lockScroll
|
||||
>
|
||||
<FloatingFocusManager context={floatingContext}>
|
||||
<div
|
||||
ref={ref}
|
||||
onPointerMove={() => {
|
||||
if (props.fade !== false) {
|
||||
clearTimeout(local.timeout);
|
||||
if (local.preview) {
|
||||
local.preview = false;
|
||||
local.render();
|
||||
}
|
||||
}
|
||||
}}
|
||||
onPointerLeave={(e) => {
|
||||
// if (Object.keys(w.openedPopupID || {}).length > 0) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (props.fade !== false) {
|
||||
clearTimeout(local.timeout);
|
||||
local.timeout = setTimeout(() => {
|
||||
local.preview = true;
|
||||
local.render();
|
||||
}, 1000);
|
||||
}
|
||||
}}
|
||||
aria-labelledby={context.labelId}
|
||||
aria-describedby={context.descriptionId}
|
||||
{...floatingDivProps}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
</FloatingFocusManager>
|
||||
</FloatingOverlay>
|
||||
</FloatingPortal>
|
||||
);
|
||||
});
|
||||
|
||||
export const ModalHeading = React.forwardRef<
|
||||
HTMLHeadingElement,
|
||||
React.HTMLProps<HTMLHeadingElement>
|
||||
>(function ModalHeading({ children, ...props }, ref) {
|
||||
const { setLabelId } = useModalContext();
|
||||
const id = useId();
|
||||
|
||||
// Only sets `aria-labelledby` on the Modal root element
|
||||
// if this component is mounted inside it.
|
||||
React.useLayoutEffect(() => {
|
||||
setLabelId(id);
|
||||
return () => setLabelId(undefined);
|
||||
}, [id, setLabelId]);
|
||||
|
||||
return (
|
||||
<h2 {...props} ref={ref} id={id}>
|
||||
{children}
|
||||
</h2>
|
||||
);
|
||||
});
|
||||
|
||||
export const ModalDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLProps<HTMLParagraphElement>
|
||||
>(function ModalDescription({ children, ...props }, ref) {
|
||||
const { setDescriptionId } = useModalContext();
|
||||
const id = useId();
|
||||
|
||||
// Only sets `aria-describedby` on the Modal root element
|
||||
// if this component is mounted inside it.
|
||||
React.useLayoutEffect(() => {
|
||||
setDescriptionId(id);
|
||||
return () => setDescriptionId(undefined);
|
||||
}, [id, setDescriptionId]);
|
||||
|
||||
return (
|
||||
<p {...props} ref={ref} id={id}>
|
||||
{children}
|
||||
</p>
|
||||
);
|
||||
});
|
||||
|
||||
export const ModalClose = React.forwardRef<
|
||||
HTMLButtonElement,
|
||||
React.ButtonHTMLAttributes<HTMLButtonElement>
|
||||
>(function ModalClose(props, ref) {
|
||||
const { setOpen } = useModalContext();
|
||||
return (
|
||||
<button type="button" {...props} ref={ref} onClick={() => setOpen(false)} />
|
||||
);
|
||||
});
|
||||
|
|
@ -49,6 +49,13 @@ export const FilterContent: FC<{
|
|||
}
|
||||
.field-outer {
|
||||
margin: 3px;
|
||||
|
||||
border: 1px solid #cecece;
|
||||
|
||||
&.focused {
|
||||
border: 1px solid #1c4ed8;
|
||||
outline: 1px solid #1c4ed8;
|
||||
}
|
||||
}
|
||||
|
||||
.search-all {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,11 @@ import { generateSelect } from "../../md/gen/md-select";
|
|||
import { newField } from "./fields";
|
||||
import { get_rel_many } from "./get_rel_many";
|
||||
import { on_load } from "./on_load";
|
||||
import { genFormOnLoad } from "./gen-form/on-load";
|
||||
import { genFormOnSubmit } from "./gen-form/on-submit";
|
||||
import { genFormOnInit } from "./gen-form/on-init";
|
||||
import { genFormSubmit } from "./gen-form/submit";
|
||||
import { walkGenForm } from "./walker";
|
||||
|
||||
export const generateForm = async (
|
||||
_: any,
|
||||
|
|
@ -47,209 +52,15 @@ export const generateForm = async (
|
|||
}
|
||||
|
||||
if (pk) {
|
||||
const gen_form_args = { result, pk, pks, table, select, is_md, rel_many };
|
||||
if (data["on_load"]) {
|
||||
result.on_load = {
|
||||
mode: "raw",
|
||||
value: on_load({
|
||||
pk,
|
||||
table,
|
||||
select,
|
||||
pks,
|
||||
opt: is_md
|
||||
? {
|
||||
after_load: `\
|
||||
if (typeof md === "object") {
|
||||
opt.fm.status = "ready";
|
||||
md.selected = opt.fm.data;
|
||||
if (!md.selected) {
|
||||
md.tab.active = "master";
|
||||
alert("Data Not Found");
|
||||
md.params.apply();
|
||||
}
|
||||
md.header.render();
|
||||
md.render();
|
||||
}`,
|
||||
is_md: true,
|
||||
}
|
||||
: { is_md },
|
||||
}),
|
||||
};
|
||||
genFormOnLoad(gen_form_args);
|
||||
}
|
||||
if (data["on_submit"]) {
|
||||
result.on_submit = {
|
||||
mode: "raw",
|
||||
value: `\
|
||||
async ({ form, error, fm }: IForm) => {
|
||||
let result = false;
|
||||
try {${
|
||||
is_md &&
|
||||
`\
|
||||
if (typeof md !== "undefined") {
|
||||
fm.status = "saving";
|
||||
md.render();
|
||||
}`
|
||||
}
|
||||
const data = { ...form };
|
||||
const record = {} as Record<string, any>;
|
||||
|
||||
const relation_ref = ${JSON.stringify(rel_many)};
|
||||
const has_many = [] as Array<{
|
||||
table: string;
|
||||
data: Array<any>;
|
||||
fk: string;
|
||||
}>;
|
||||
|
||||
|
||||
// validasi
|
||||
fm.error.clear();
|
||||
for (const [k, field] of Object.entries(fm.fields)) {
|
||||
validateField(field, fm);
|
||||
}
|
||||
${
|
||||
is_md &&
|
||||
`\
|
||||
if (fm.error.list.length > 0) {
|
||||
if (typeof md !== "undefined") {
|
||||
fm.status = "ready";
|
||||
md.render();
|
||||
}
|
||||
return false;
|
||||
}`
|
||||
}
|
||||
|
||||
call_prasi_events("form", "before_save", [fm, data]);
|
||||
|
||||
// pisahkan antara has_many dengan field biasa
|
||||
for (const [k, v] of Object.entries(data) as any) {
|
||||
if (Array.isArray(v)) {
|
||||
const rel =
|
||||
Array.isArray(relation_ref) && relation_ref.length
|
||||
? relation_ref.find((e) => e.table === k)
|
||||
: null;
|
||||
if (rel) {
|
||||
has_many.push({
|
||||
table: k,
|
||||
data: v,
|
||||
fk: rel.fk,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
record[k] = v;
|
||||
}
|
||||
}
|
||||
|
||||
// prisma create / update ga boleh ada record.${pk}
|
||||
if (record) delete record.${pk};
|
||||
|
||||
if (form.${pk}) {
|
||||
await db.${table}.update({
|
||||
where: {
|
||||
${pk}: form.${pk},
|
||||
},
|
||||
data: {
|
||||
...record,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
const res = await db.${table}.create({
|
||||
//@ts-ignore
|
||||
data: {
|
||||
...record,
|
||||
},
|
||||
});
|
||||
|
||||
if (res) {
|
||||
form.id = res.id;
|
||||
fm.is_newly_created = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_many.length) {
|
||||
const exec_query_bulk = async (
|
||||
current: { table: string; data: Array<any>; fk: string },
|
||||
list: Array<{ table: string; data: Array<any>; fk: string }>,
|
||||
index: number,
|
||||
) => {
|
||||
if (list.length) {
|
||||
const data = current.data.map((e) => {
|
||||
const record = {
|
||||
...e,
|
||||
${table}: {
|
||||
connect: {
|
||||
${pk}: form.${pk},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
call_prasi_events("form", "before_save", [fm, record]);
|
||||
|
||||
return record;
|
||||
});
|
||||
await db._batch.upsert({
|
||||
table: current.table,
|
||||
where: {
|
||||
[current.fk]: form.${pk},
|
||||
},
|
||||
data: data,
|
||||
mode: "relation",
|
||||
} as any);
|
||||
|
||||
if (list.length > 1) {
|
||||
try {
|
||||
index++;
|
||||
if (index <= list.length - 1) {
|
||||
await exec_query_bulk(list[index], list, index);
|
||||
}
|
||||
} catch (ex) {}
|
||||
}
|
||||
}
|
||||
};
|
||||
await exec_query_bulk(has_many[0], has_many, 0);
|
||||
}
|
||||
result = true;
|
||||
|
||||
call_prasi_events("form", "after_save", [fm, data]);
|
||||
|
||||
${
|
||||
is_md &&
|
||||
`if (typeof md !== "undefined") {
|
||||
fm.status = "ready";
|
||||
fm.data = form;
|
||||
md.selected = form;
|
||||
md.render();
|
||||
fm.render();
|
||||
}`
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
type IForm = { form: any; error: Record<string, string>; fm: FMLocal }
|
||||
`,
|
||||
};
|
||||
genFormOnSubmit(gen_form_args);
|
||||
}
|
||||
if (typeof is_md === "boolean" && is_md) {
|
||||
result.on_init = {
|
||||
mode: "raw",
|
||||
value: `\
|
||||
({ submit, reload, fm }: Init) => {
|
||||
// on init
|
||||
if (!isEditor) {
|
||||
if (typeof md === "object") {
|
||||
md.childs["form"] = {
|
||||
fm: fm
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
type Init = { submit: () => Promise<boolean>; reload: () => void; fm: FMLocal }
|
||||
`,
|
||||
};
|
||||
genFormOnInit(gen_form_args);
|
||||
}
|
||||
const child_fields = [];
|
||||
for (const item of fields.filter((e) => !e.is_pk)) {
|
||||
|
|
@ -262,175 +73,8 @@ type IForm = { form: any; error: Record<string, string>; fm: FMLocal }
|
|||
}
|
||||
let submit = null;
|
||||
if (typeof is_md === "boolean" && !is_md)
|
||||
submit = {
|
||||
id: createId(),
|
||||
dim: { h: "fit", w: "full", hUnit: "px", wUnit: "px" },
|
||||
name: "submit",
|
||||
type: "item",
|
||||
adv: {
|
||||
css: "",
|
||||
js: '<>\n {\n /** if */\n fm.status === "ready" ? (\n /** then */\n <div {...props} className={cx(props.className, "")}>\n {children}\n </div>\n ) : (\n /** else */\n <div {...props} className={cx(props.className, "")}>\n <FieldLoading />\n </div>\n )\n }\n</>',
|
||||
jsBuilt:
|
||||
'render(/* @__PURE__ */ React.createElement(\n React.Fragment,\n null,\n /** if */\n fm.status === "ready" ? (\n /** then */\n /* @__PURE__ */ React.createElement("div", { ...props, className: cx(props.className, "") }, children)\n ) : (\n /** else */\n /* @__PURE__ */ React.createElement("div", { ...props, className: cx(props.className, "") }, /* @__PURE__ */ React.createElement(FieldLoading, null))\n )\n));\n',
|
||||
},
|
||||
childs: [
|
||||
{
|
||||
id: createId(),
|
||||
dim: { h: "fit", w: "fit", hUnit: "px", wUnit: "px" },
|
||||
name: "bottom",
|
||||
type: "item",
|
||||
childs: [
|
||||
{
|
||||
id: createId(),
|
||||
adv: {
|
||||
js: '<Button\n {...props}\n onClick={(e) => {\n console.log(isEditor);\n if (!isEditor) on_click(e);\n }}\n variant={variant !== "primary" ? variant : undefined}\n size={size !== "default" ? size : undefined}\n>\n {label}\n</Button>',
|
||||
css: "& {\n display: flex;\n box-shadow: rgba(50, 50, 93, 0.25) 0px 2px 5px -1px, rgba(0, 0, 0, 0.3) 0px 1px 3px -1px;\n\n &:hover {\n cursor: pointer;\n\n\n\n\n\n // &.mobile {}\n // &.desktop {}\n // &:hover {}\n }\n}",
|
||||
jsBuilt:
|
||||
'render(/* @__PURE__ */ React.createElement(\n Button,\n {\n ...props,\n onClick: (e) => {\n console.log(isEditor);\n if (!isEditor)\n on_click(e);\n },\n variant: variant !== "primary" ? variant : void 0,\n size: size !== "default" ? size : void 0\n },\n label\n));\n',
|
||||
},
|
||||
dim: { h: "full", w: "full" },
|
||||
name: "button",
|
||||
type: "item",
|
||||
childs: [],
|
||||
mobile: { linktag: {} },
|
||||
script: {
|
||||
props: {
|
||||
size: { value: ' "default";\n' },
|
||||
variant: { value: ' "primary";\n' },
|
||||
on_click: {
|
||||
value:
|
||||
" (e) => {\n e.preventDefault();\n e.stopPropagation();\n fm.submit();\n};\n",
|
||||
},
|
||||
},
|
||||
},
|
||||
component: {
|
||||
id: "a15d152d-0118-408f-89f1-f6b2dfbd2e05",
|
||||
props: {
|
||||
size: {
|
||||
idx: 5,
|
||||
meta: {
|
||||
type: "option",
|
||||
options: '["default", "sm", "lg", "icon","nosize"]',
|
||||
optionsBuilt:
|
||||
' ["default", "sm", "lg", "icon", "nosize"];\n',
|
||||
},
|
||||
name: "prop_5",
|
||||
type: "string",
|
||||
value: '"default"',
|
||||
valueBuilt: ' "default";\n',
|
||||
},
|
||||
label: {
|
||||
idx: 1,
|
||||
meta: { type: "content-element" },
|
||||
name: "prop_1",
|
||||
type: "string",
|
||||
value: '"hello"',
|
||||
content: {
|
||||
id: createId(),
|
||||
adv: {
|
||||
js: '<div {...props} className={cx(props.className, "")}>\n {children}\n</div>',
|
||||
css: "",
|
||||
jsBuilt:
|
||||
'render(/* @__PURE__ */ React.createElement("div", { ...props, className: cx(props.className, "") }, children));\n',
|
||||
},
|
||||
dim: { h: "full", w: "full" },
|
||||
name: "label",
|
||||
type: "item",
|
||||
childs: [
|
||||
{
|
||||
id: createId(),
|
||||
name: "Wrapped",
|
||||
type: "item",
|
||||
childs: [
|
||||
{
|
||||
id: createId(),
|
||||
adv: {
|
||||
js: '<div {...props} className={cx(props.className, "")}>\n <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" strokeWidth="2"><path d="M16 21v-2c0-1.886 0-2.828-.586-3.414C14.828 15 13.886 15 12 15h-1c-1.886 0-2.828 0-3.414.586C7 16.172 7 17.114 7 19v2" /><path strokeLinecap="round" d="M7 8h5" /><path d="M3 9c0-2.828 0-4.243.879-5.121C4.757 3 6.172 3 9 3h7.172c.408 0 .613 0 .796.076c.184.076.329.22.618.51l2.828 2.828c.29.29.434.434.51.618c.076.183.076.388.076.796V15c0 2.828 0 4.243-.879 5.121C19.243 21 17.828 21 15 21H9c-2.828 0-4.243 0-5.121-.879C3 19.243 3 17.828 3 15z" /></g></svg>\n</div>',
|
||||
css: "",
|
||||
jsBuilt:
|
||||
'render(/* @__PURE__ */ React.createElement("div", { ...props, className: cx(props.className, "") }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24" }, /* @__PURE__ */ React.createElement("g", { fill: "none", stroke: "currentColor", "strokeWidth": "2" }, /* @__PURE__ */ React.createElement("path", { d: "M16 21v-2c0-1.886 0-2.828-.586-3.414C14.828 15 13.886 15 12 15h-1c-1.886 0-2.828 0-3.414.586C7 16.172 7 17.114 7 19v2" }), /* @__PURE__ */ React.createElement("path", { "strokeLinecap": "round", d: "M7 8h5" }), /* @__PURE__ */ React.createElement("path", { d: "M3 9c0-2.828 0-4.243.879-5.121C4.757 3 6.172 3 9 3h7.172c.408 0 .613 0 .796.076c.184.076.329.22.618.51l2.828 2.828c.29.29.434.434.51.618c.076.183.076.388.076.796V15c0 2.828 0 4.243-.879 5.121C19.243 21 17.828 21 15 21H9c-2.828 0-4.243 0-5.121-.879C3 19.243 3 17.828 3 15z" })))));\n',
|
||||
},
|
||||
dim: { h: "full", w: "full" },
|
||||
html: "aadd",
|
||||
name: "new_text",
|
||||
text: "",
|
||||
type: "text",
|
||||
layout: { dir: "col", gap: 0, align: "center" },
|
||||
script: {},
|
||||
},
|
||||
{
|
||||
id: createId(),
|
||||
adv: {
|
||||
js: '<div {...props} className={cx(props.className, "")}>\n Save\n</div>',
|
||||
css: "",
|
||||
jsBuilt:
|
||||
'render(/* @__PURE__ */ React.createElement("div", { ...props, className: cx(props.className, "") }, "Save"));\n',
|
||||
},
|
||||
dim: { h: "full", w: "full" },
|
||||
name: "new_item",
|
||||
type: "item",
|
||||
childs: [],
|
||||
script: {},
|
||||
},
|
||||
],
|
||||
layout: {
|
||||
dir: "row",
|
||||
gap: 10,
|
||||
wrap: "flex-nowrap",
|
||||
align: "top-left",
|
||||
},
|
||||
},
|
||||
],
|
||||
hidden: false,
|
||||
layout: {
|
||||
dir: "col",
|
||||
gap: 0,
|
||||
wrap: "flex-nowrap",
|
||||
align: "center",
|
||||
},
|
||||
script: {},
|
||||
},
|
||||
valueBuilt: '"hello"',
|
||||
},
|
||||
variant: {
|
||||
idx: 3,
|
||||
meta: {
|
||||
type: "option",
|
||||
options:
|
||||
'["primary", "secondary", "outline", "ghost", "link", "destructive"]',
|
||||
option_mode: "button",
|
||||
optionsBuilt:
|
||||
' ["primary", "secondary", "outline", "ghost", "link", "destructive"];\n',
|
||||
},
|
||||
name: "prop_3",
|
||||
type: "string",
|
||||
value: '"primary"',
|
||||
valueBuilt: ' "primary";\n',
|
||||
},
|
||||
on_click: {
|
||||
idx: 1,
|
||||
meta: { type: "text" },
|
||||
name: "prop_1",
|
||||
type: "string",
|
||||
value:
|
||||
"(e) => {\n e.preventDefault();\n e.stopPropagation();\n fm.submit();\n}",
|
||||
valueBuilt:
|
||||
" (e) => {\n e.preventDefault();\n e.stopPropagation();\n fm.submit();\n};\n",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
layout: {
|
||||
dir: "col",
|
||||
gap: 0,
|
||||
wrap: "flex-nowrap",
|
||||
align: "center",
|
||||
},
|
||||
},
|
||||
],
|
||||
padding: { b: 10, l: 10, r: 10, t: 10 },
|
||||
};
|
||||
submit = genFormSubmit(gen_form_args);
|
||||
|
||||
const body_prop = {
|
||||
adv: {
|
||||
js: "<div\n {...props}\n className={cx(\n props.className,\n css`\n align-content: start;`,\n )}\n>\n {children}\n</div>",
|
||||
|
|
@ -448,7 +92,11 @@ type IForm = { form: any; error: Record<string, string>; fm: FMLocal }
|
|||
align: "top-left",
|
||||
},
|
||||
};
|
||||
const child_body = createItem({
|
||||
const existing_childs = (
|
||||
(item.component?.props.body as any)?.content as IItem
|
||||
).childs;
|
||||
|
||||
let child_body = createItem({
|
||||
name: "item",
|
||||
...body_prop,
|
||||
childs: [
|
||||
|
|
@ -476,6 +124,11 @@ type IForm = { form: any; error: Record<string, string>; fm: FMLocal }
|
|||
submit,
|
||||
].filter((e) => e),
|
||||
});
|
||||
|
||||
if (Array.isArray(existing_childs) && existing_childs.length > 0) {
|
||||
walkGenForm(child_body, existing_childs as any);
|
||||
}
|
||||
|
||||
if (commit) {
|
||||
Object.keys(result).map((e) => {
|
||||
item.edit.setProp(e, result[e]);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
import { GenFormArgs } from "./types";
|
||||
|
||||
export const genFormOnInit = ({
|
||||
result,
|
||||
pk,
|
||||
pks,
|
||||
table,
|
||||
select,
|
||||
is_md,
|
||||
}: GenFormArgs) => {
|
||||
result.on_init = {
|
||||
mode: "raw",
|
||||
value: `\
|
||||
({ submit, reload, fm }: Init) => {
|
||||
// on init
|
||||
if (!isEditor) {
|
||||
if (typeof md === "object") {
|
||||
md.childs["form"] = {
|
||||
fm: fm
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
type Init = { submit: () => Promise<boolean>; reload: () => void; fm: FMLocal }
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
import { on_load } from "../on_load";
|
||||
import { GenFormArgs } from "./types";
|
||||
|
||||
export const genFormOnLoad = ({
|
||||
result,
|
||||
pk,
|
||||
pks,
|
||||
table,
|
||||
select,
|
||||
is_md,
|
||||
}: GenFormArgs) => {
|
||||
result.on_load = {
|
||||
mode: "raw",
|
||||
value: on_load({
|
||||
pk,
|
||||
table,
|
||||
select,
|
||||
pks,
|
||||
opt: is_md
|
||||
? {
|
||||
after_load: `\
|
||||
if (typeof md === "object") {
|
||||
opt.fm.status = "ready";
|
||||
md.selected = opt.fm.data;
|
||||
if (!md.selected) {
|
||||
md.tab.active = "master";
|
||||
alert("Data Not Found");
|
||||
md.params.apply();
|
||||
}
|
||||
md.header.render();
|
||||
md.render();
|
||||
}`,
|
||||
is_md: true,
|
||||
}
|
||||
: { is_md },
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
import { GenFormArgs } from "./types";
|
||||
|
||||
export const genFormOnSubmit = ({
|
||||
result,
|
||||
pk,
|
||||
pks,
|
||||
table,
|
||||
select,
|
||||
is_md,
|
||||
rel_many
|
||||
}: GenFormArgs) => {
|
||||
result.on_submit = {
|
||||
mode: "raw",
|
||||
value: `\
|
||||
async ({ form, error, fm }: IForm) => {
|
||||
let result = false;
|
||||
try {${
|
||||
is_md &&
|
||||
`\
|
||||
if (typeof md !== "undefined") {
|
||||
fm.status = "saving";
|
||||
md.render();
|
||||
}`
|
||||
}
|
||||
const data = { ...form };
|
||||
const record = {} as Record<string, any>;
|
||||
|
||||
const relation_ref = ${JSON.stringify(rel_many)};
|
||||
const has_many = [] as Array<{
|
||||
table: string;
|
||||
data: Array<any>;
|
||||
fk: string;
|
||||
}>;
|
||||
|
||||
|
||||
// validasi
|
||||
fm.error.clear();
|
||||
for (const [k, field] of Object.entries(fm.fields)) {
|
||||
validateField(field, fm);
|
||||
}
|
||||
${
|
||||
is_md &&
|
||||
`\
|
||||
if (fm.error.list.length > 0) {
|
||||
if (typeof md !== "undefined") {
|
||||
fm.status = "ready";
|
||||
md.render();
|
||||
}
|
||||
return false;
|
||||
}`
|
||||
}
|
||||
|
||||
call_prasi_events("form", "before_save", [fm, data]);
|
||||
|
||||
// pisahkan antara has_many dengan field biasa
|
||||
for (const [k, v] of Object.entries(data) as any) {
|
||||
if (Array.isArray(v)) {
|
||||
const rel =
|
||||
Array.isArray(relation_ref) && relation_ref.length
|
||||
? relation_ref.find((e) => e.table === k)
|
||||
: null;
|
||||
if (rel) {
|
||||
has_many.push({
|
||||
table: k,
|
||||
data: v,
|
||||
fk: rel.fk,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
record[k] = v;
|
||||
}
|
||||
}
|
||||
|
||||
// prisma create / update ga boleh ada record.${pk}
|
||||
if (record) delete record.${pk};
|
||||
|
||||
if (form.${pk}) {
|
||||
await db.${table}.update({
|
||||
where: {
|
||||
${pk}: form.${pk},
|
||||
},
|
||||
data: {
|
||||
...record,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
const res = await db.${table}.create({
|
||||
//@ts-ignore
|
||||
data: {
|
||||
...record,
|
||||
},
|
||||
});
|
||||
|
||||
if (res) {
|
||||
form.id = res.id;
|
||||
fm.is_newly_created = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_many.length) {
|
||||
const exec_query_bulk = async (
|
||||
current: { table: string; data: Array<any>; fk: string },
|
||||
list: Array<{ table: string; data: Array<any>; fk: string }>,
|
||||
index: number,
|
||||
) => {
|
||||
if (list.length) {
|
||||
const data = current.data.map((e) => {
|
||||
const record = {
|
||||
...e,
|
||||
${table}: {
|
||||
connect: {
|
||||
${pk}: form.${pk},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
call_prasi_events("form", "before_save", [fm, record]);
|
||||
|
||||
return record;
|
||||
});
|
||||
await db._batch.upsert({
|
||||
table: current.table,
|
||||
where: {
|
||||
[current.fk]: form.${pk},
|
||||
},
|
||||
data: data,
|
||||
mode: "relation",
|
||||
} as any);
|
||||
|
||||
if (list.length > 1) {
|
||||
try {
|
||||
index++;
|
||||
if (index <= list.length - 1) {
|
||||
await exec_query_bulk(list[index], list, index);
|
||||
}
|
||||
} catch (ex) {}
|
||||
}
|
||||
}
|
||||
};
|
||||
await exec_query_bulk(has_many[0], has_many, 0);
|
||||
}
|
||||
result = true;
|
||||
|
||||
call_prasi_events("form", "after_save", [fm, data]);
|
||||
|
||||
${
|
||||
is_md &&
|
||||
`if (typeof md !== "undefined") {
|
||||
fm.status = "ready";
|
||||
fm.data = form;
|
||||
md.selected = form;
|
||||
md.render();
|
||||
fm.render();
|
||||
}`
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
type IForm = { form: any; error: Record<string, string>; fm: FMLocal }
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
import { createId } from "@paralleldrive/cuid2";
|
||||
import { GenFormArgs } from "./types";
|
||||
|
||||
export const genFormSubmit = ({}: GenFormArgs) => {
|
||||
return {
|
||||
id: createId(),
|
||||
dim: { h: "fit", w: "full", hUnit: "px", wUnit: "px" },
|
||||
name: "submit",
|
||||
type: "item",
|
||||
adv: {
|
||||
css: "",
|
||||
js: '<>\n {\n /** if */\n fm.status === "ready" ? (\n /** then */\n <div {...props} className={cx(props.className, "")}>\n {children}\n </div>\n ) : (\n /** else */\n <div {...props} className={cx(props.className, "")}>\n <FieldLoading />\n </div>\n )\n }\n</>',
|
||||
jsBuilt:
|
||||
'render(/* @__PURE__ */ React.createElement(\n React.Fragment,\n null,\n /** if */\n fm.status === "ready" ? (\n /** then */\n /* @__PURE__ */ React.createElement("div", { ...props, className: cx(props.className, "") }, children)\n ) : (\n /** else */\n /* @__PURE__ */ React.createElement("div", { ...props, className: cx(props.className, "") }, /* @__PURE__ */ React.createElement(FieldLoading, null))\n )\n));\n',
|
||||
},
|
||||
childs: [
|
||||
{
|
||||
id: createId(),
|
||||
dim: { h: "fit", w: "fit", hUnit: "px", wUnit: "px" },
|
||||
name: "bottom",
|
||||
type: "item",
|
||||
childs: [
|
||||
{
|
||||
id: createId(),
|
||||
adv: {
|
||||
js: '<Button\n {...props}\n onClick={(e) => {\n console.log(isEditor);\n if (!isEditor) on_click(e);\n }}\n variant={variant !== "primary" ? variant : undefined}\n size={size !== "default" ? size : undefined}\n>\n {label}\n</Button>',
|
||||
css: "& {\n display: flex;\n box-shadow: rgba(50, 50, 93, 0.25) 0px 2px 5px -1px, rgba(0, 0, 0, 0.3) 0px 1px 3px -1px;\n\n &:hover {\n cursor: pointer;\n\n\n\n\n\n // &.mobile {}\n // &.desktop {}\n // &:hover {}\n }\n}",
|
||||
jsBuilt:
|
||||
'render(/* @__PURE__ */ React.createElement(\n Button,\n {\n ...props,\n onClick: (e) => {\n console.log(isEditor);\n if (!isEditor)\n on_click(e);\n },\n variant: variant !== "primary" ? variant : void 0,\n size: size !== "default" ? size : void 0\n },\n label\n));\n',
|
||||
},
|
||||
dim: { h: "full", w: "full" },
|
||||
name: "button",
|
||||
type: "item",
|
||||
childs: [],
|
||||
mobile: { linktag: {} },
|
||||
script: {
|
||||
props: {
|
||||
size: { value: ' "default";\n' },
|
||||
variant: { value: ' "primary";\n' },
|
||||
on_click: {
|
||||
value:
|
||||
" (e) => {\n e.preventDefault();\n e.stopPropagation();\n fm.submit();\n};\n",
|
||||
},
|
||||
},
|
||||
},
|
||||
component: {
|
||||
id: "a15d152d-0118-408f-89f1-f6b2dfbd2e05",
|
||||
props: {
|
||||
size: {
|
||||
idx: 5,
|
||||
meta: {
|
||||
type: "option",
|
||||
options: '["default", "sm", "lg", "icon","nosize"]',
|
||||
optionsBuilt:
|
||||
' ["default", "sm", "lg", "icon", "nosize"];\n',
|
||||
},
|
||||
name: "prop_5",
|
||||
type: "string",
|
||||
value: '"default"',
|
||||
valueBuilt: ' "default";\n',
|
||||
},
|
||||
label: {
|
||||
idx: 1,
|
||||
meta: { type: "content-element" },
|
||||
name: "prop_1",
|
||||
type: "string",
|
||||
value: '"hello"',
|
||||
content: {
|
||||
id: createId(),
|
||||
adv: {
|
||||
js: '<div {...props} className={cx(props.className, "")}>\n {children}\n</div>',
|
||||
css: "",
|
||||
jsBuilt:
|
||||
'render(/* @__PURE__ */ React.createElement("div", { ...props, className: cx(props.className, "") }, children));\n',
|
||||
},
|
||||
dim: { h: "full", w: "full" },
|
||||
name: "label",
|
||||
type: "item",
|
||||
childs: [
|
||||
{
|
||||
id: createId(),
|
||||
name: "Wrapped",
|
||||
type: "item",
|
||||
childs: [
|
||||
{
|
||||
id: createId(),
|
||||
adv: {
|
||||
js: '<div {...props} className={cx(props.className, "")}>\n <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" strokeWidth="2"><path d="M16 21v-2c0-1.886 0-2.828-.586-3.414C14.828 15 13.886 15 12 15h-1c-1.886 0-2.828 0-3.414.586C7 16.172 7 17.114 7 19v2" /><path strokeLinecap="round" d="M7 8h5" /><path d="M3 9c0-2.828 0-4.243.879-5.121C4.757 3 6.172 3 9 3h7.172c.408 0 .613 0 .796.076c.184.076.329.22.618.51l2.828 2.828c.29.29.434.434.51.618c.076.183.076.388.076.796V15c0 2.828 0 4.243-.879 5.121C19.243 21 17.828 21 15 21H9c-2.828 0-4.243 0-5.121-.879C3 19.243 3 17.828 3 15z" /></g></svg>\n</div>',
|
||||
css: "",
|
||||
jsBuilt:
|
||||
'render(/* @__PURE__ */ React.createElement("div", { ...props, className: cx(props.className, "") }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24" }, /* @__PURE__ */ React.createElement("g", { fill: "none", stroke: "currentColor", "strokeWidth": "2" }, /* @__PURE__ */ React.createElement("path", { d: "M16 21v-2c0-1.886 0-2.828-.586-3.414C14.828 15 13.886 15 12 15h-1c-1.886 0-2.828 0-3.414.586C7 16.172 7 17.114 7 19v2" }), /* @__PURE__ */ React.createElement("path", { "strokeLinecap": "round", d: "M7 8h5" }), /* @__PURE__ */ React.createElement("path", { d: "M3 9c0-2.828 0-4.243.879-5.121C4.757 3 6.172 3 9 3h7.172c.408 0 .613 0 .796.076c.184.076.329.22.618.51l2.828 2.828c.29.29.434.434.51.618c.076.183.076.388.076.796V15c0 2.828 0 4.243-.879 5.121C19.243 21 17.828 21 15 21H9c-2.828 0-4.243 0-5.121-.879C3 19.243 3 17.828 3 15z" })))));\n',
|
||||
},
|
||||
dim: { h: "full", w: "full" },
|
||||
html: "aadd",
|
||||
name: "new_text",
|
||||
text: "",
|
||||
type: "text",
|
||||
layout: { dir: "col", gap: 0, align: "center" },
|
||||
script: {},
|
||||
},
|
||||
{
|
||||
id: createId(),
|
||||
adv: {
|
||||
js: '<div {...props} className={cx(props.className, "")}>\n Save\n</div>',
|
||||
css: "",
|
||||
jsBuilt:
|
||||
'render(/* @__PURE__ */ React.createElement("div", { ...props, className: cx(props.className, "") }, "Save"));\n',
|
||||
},
|
||||
dim: { h: "full", w: "full" },
|
||||
name: "new_item",
|
||||
type: "item",
|
||||
childs: [],
|
||||
script: {},
|
||||
},
|
||||
],
|
||||
layout: {
|
||||
dir: "row",
|
||||
gap: 10,
|
||||
wrap: "flex-nowrap",
|
||||
align: "top-left",
|
||||
},
|
||||
},
|
||||
],
|
||||
hidden: false,
|
||||
layout: {
|
||||
dir: "col",
|
||||
gap: 0,
|
||||
wrap: "flex-nowrap",
|
||||
align: "center",
|
||||
},
|
||||
script: {},
|
||||
},
|
||||
valueBuilt: '"hello"',
|
||||
},
|
||||
variant: {
|
||||
idx: 3,
|
||||
meta: {
|
||||
type: "option",
|
||||
options:
|
||||
'["primary", "secondary", "outline", "ghost", "link", "destructive"]',
|
||||
option_mode: "button",
|
||||
optionsBuilt:
|
||||
' ["primary", "secondary", "outline", "ghost", "link", "destructive"];\n',
|
||||
},
|
||||
name: "prop_3",
|
||||
type: "string",
|
||||
value: '"primary"',
|
||||
valueBuilt: ' "primary";\n',
|
||||
},
|
||||
on_click: {
|
||||
idx: 1,
|
||||
meta: { type: "text" },
|
||||
name: "prop_1",
|
||||
type: "string",
|
||||
value:
|
||||
"(e) => {\n e.preventDefault();\n e.stopPropagation();\n fm.submit();\n}",
|
||||
valueBuilt:
|
||||
" (e) => {\n e.preventDefault();\n e.stopPropagation();\n fm.submit();\n};\n",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
layout: {
|
||||
dir: "col",
|
||||
gap: 0,
|
||||
wrap: "flex-nowrap",
|
||||
align: "center",
|
||||
},
|
||||
},
|
||||
],
|
||||
padding: { b: 10, l: 10, r: 10, t: 10 },
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
export type GenFormArgs = {
|
||||
result: any;
|
||||
pk: string;
|
||||
pks: Record<string, string>;
|
||||
table: string;
|
||||
is_md: boolean;
|
||||
select: any;
|
||||
rel_many: {
|
||||
table: string;
|
||||
fk: string;
|
||||
}[];
|
||||
};
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
export const walkGenForm = (
|
||||
new_childs: IItem[],
|
||||
existing_childs: PrasiItem[]
|
||||
) => {
|
||||
const fields = {} as Record<string, PrasiItem>;
|
||||
|
||||
for (const item of new_childs[0].childs) {
|
||||
const name = item.component?.props?.name;
|
||||
console.log(name);
|
||||
}
|
||||
// for (const item of existing_childs) {
|
||||
// walk(item);
|
||||
// }
|
||||
};
|
||||
|
||||
const walk = (item: IItem) => {
|
||||
if (item.component?.id) {
|
||||
console.log(item);
|
||||
}
|
||||
|
||||
for (const child of item.childs) {
|
||||
walk(child);
|
||||
}
|
||||
};
|
||||
|
|
@ -12,10 +12,6 @@ export const Popover = lazify(
|
|||
async () => (await import("@/comps/custom/Popover")).Popover
|
||||
);
|
||||
|
||||
export const Modal = lazify(
|
||||
async () => (await import("lib/comps/custom/Modal")).Modal
|
||||
);
|
||||
|
||||
export const Typeahead = lazify(
|
||||
async () => (await import("@/comps/ui/typeahead")).Typeahead
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in New Issue