This commit is contained in:
rizrmd 2024-06-11 01:23:29 -07:00
parent 32ead5308f
commit 3f56904fd7
32 changed files with 1920 additions and 384 deletions

View File

@ -18,6 +18,7 @@ import useOnClickOutside from "../hooks";
import { Period, DatepickerType, ColorKeys } from "../types"; import { Period, DatepickerType, ColorKeys } from "../types";
import { Arrow, VerticalDash } from "./utils"; import { Arrow, VerticalDash } from "./utils";
import { createPortal } from "react-dom";
const Datepicker: React.FC<DatepickerType> = ({ const Datepicker: React.FC<DatepickerType> = ({
primaryColor = "blue", primaryColor = "blue",
@ -70,8 +71,8 @@ const Datepicker: React.FC<DatepickerType> = ({
const [inputRef, setInputRef] = useState(React.createRef<HTMLInputElement>()); const [inputRef, setInputRef] = useState(React.createRef<HTMLInputElement>());
// Custom Hooks use // Custom Hooks use
useOnClickOutside(containerRef, () => { useOnClickOutside(calendarContainerRef, () => {
const container = containerRef.current; const container = calendarContainerRef.current;
if (container) { if (container) {
hideDatepicker(); hideDatepicker();
} }
@ -341,13 +342,28 @@ const Datepicker: React.FC<DatepickerType> = ({
: defaultContainerClassName; : defaultContainerClassName;
}, [containerClassName]); }, [containerClassName]);
const toaster = document.querySelector(".prasi-toaster");
const rect = inputRef.current?.getBoundingClientRect();
return ( return (
<DatepickerContext.Provider value={contextValues}> <DatepickerContext.Provider value={contextValues}>
<div className={containerClassNameOverload} ref={containerRef}> <div className={containerClassNameOverload} ref={containerRef}>
<Input setContextRef={setInputRef} /> <Input setContextRef={setInputRef} />
{toaster &&
createPortal(
<div <div
className="c-transition-all c-ease-out c-duration-300 c-absolute c-z-10 c-mt-[1px] c-text-sm 2xl:c-text-sm c-translate-y-4 c-opacity-0 c-hidden" className={cx(
"c-transition-all c-ease-out c-absolute c-z-50 c-duration-300 c-mt-[1px] c-text-sm 2xl:c-text-sm c-translate-y-4 c-hidden c-opacity-0",
rect &&
css`
top: ${rect.top +
rect.height +
rect.height / 2}px !important;
left: ${rect.left -
rect.width +
rect.width / 2}px !important;
`
)}
ref={calendarContainerRef} ref={calendarContainerRef}
> >
<Arrow ref={arrowRef} /> <Arrow ref={arrowRef} />
@ -393,7 +409,9 @@ const Datepicker: React.FC<DatepickerType> = ({
{showFooter && <Footer />} {showFooter && <Footer />}
</div> </div>
</div> </div>,
toaster
)}
</div> </div>
</DatepickerContext.Provider> </DatepickerContext.Provider>
); );

View File

@ -62,7 +62,7 @@ const Input: React.FC<Props> = (e: Props) => {
primaryColor as keyof (typeof RING_COLOR)["second-focus"] primaryColor as keyof (typeof RING_COLOR)["second-focus"]
]; ];
const defaultInputClassName = `c-relative c-transition-all c-duration-300 c-pl-2 c-pr-14 c-w-full c-border-gray-300 dark:c-bg-slate-800 dark:c-text-white/80 dark:c-border-slate-600 c-rounded-lg c-tracking-wide c-font-light c-text-sm c-placeholder-gray-400 c-bg-white c-outline-none focus:c-ring-0 disabled:c-opacity-40 disabled:c-cursor-not-allowed ${border} ${ring}`; const defaultInputClassName = `c-relative c-transition-all c-duration-300 c-pl-2 c-pr-14 c-w-full c-border-gray-300 dark:c-bg-slate-800 dark:c-text-white/80 dark:c-border-slate-600 c-rounded-lg c-tracking-wide c-font-light c-text-sm c-placeholder-gray-400 c-bg-transparent c-outline-none focus:c-ring-0 disabled:c-opacity-40 disabled:c-cursor-not-allowed ${border} ${ring}`;
return typeof inputClassName === "function" return typeof inputClassName === "function"
? inputClassName(defaultInputClassName) ? inputClassName(defaultInputClassName)

View File

@ -14,39 +14,39 @@ export function classNames(...classes: (false | null | undefined | string)[]) {
export function getTextColorByPrimaryColor(color: string) { export function getTextColorByPrimaryColor(color: string) {
switch (color) { switch (color) {
case "blue": case "blue":
return "text-blue-500"; return "c-text-blue-500";
case "orange": case "orange":
return "text-orange-500"; return "c-text-orange-500";
case "yellow": case "yellow":
return "text-yellow-500"; return "c-text-yellow-500";
case "red": case "red":
return "text-red-500"; return "c-text-red-500";
case "purple": case "purple":
return "text-purple-500"; return "c-text-purple-500";
case "amber": case "amber":
return "text-amber-500"; return "c-text-amber-500";
case "lime": case "lime":
return "text-lime-500"; return "c-text-lime-500";
case "green": case "green":
return "text-green-500"; return "c-text-green-500";
case "emerald": case "emerald":
return "text-emerald-500"; return "c-text-emerald-500";
case "teal": case "teal":
return "text-teal-500"; return "c-text-teal-500";
case "cyan": case "cyan":
return "text-cyan-500"; return "c-text-cyan-500";
case "sky": case "sky":
return "text-sky-500"; return "c-text-sky-500";
case "indigo": case "indigo":
return "text-indigo-500"; return "c-text-indigo-500";
case "violet": case "violet":
return "text-violet-500"; return "c-text-violet-500";
case "fuchsia": case "fuchsia":
return "text-fuchsia-500"; return "c-text-fuchsia-500";
case "pink": case "pink":
return "text-pink-500"; return "c-text-pink-500";
case "rose": case "rose":
return "text-rose-500"; return "c-text-rose-500";
default: default:
return ""; return "";
} }

5
comps/filter/gen/gen-filter.ts Executable file
View File

@ -0,0 +1,5 @@
export const generateFilter = (
data: any,
item: PrasiItem,
commit: boolean
) => {};

View File

@ -9,6 +9,7 @@ export type BaseFormProps<T> = {
on_submit?: (form: BaseFormLocal<T>) => Promise<any> | any; on_submit?: (form: BaseFormLocal<T>) => Promise<any> | any;
children: ReactNode | ((form: BaseFormLocal<T>) => ReactNode); children: ReactNode | ((form: BaseFormLocal<T>) => ReactNode);
render?: () => void; render?: () => void;
is_form?: boolean;
}; };
export const BaseForm = <T extends Record<string, any>>( export const BaseForm = <T extends Record<string, any>>(
props: BaseFormProps<T> props: BaseFormProps<T>
@ -101,7 +102,29 @@ export const BaseForm = <T extends Record<string, any>>(
if (form.status === "init") { if (form.status === "init") {
form.status = "ready"; form.status = "ready";
} }
if (typeof props.is_form === "boolean") {
if (!props.is_form) {
return (
<div
className={cx(
"form c-flex-1 c-flex-col c-w-full c-h-full c-relative c-overflow-auto c-contents",
className
)}
>
<div
className={cx(
"form-inner c-flex-1 c-flex-wrap c-items-start c-content-start c-absolute c-inset-0 c-contents",
css`
padding-right: 10px;
`
)}
>
{typeof children === "function" ? children(form) : children}
</div>
</div>
);
}
}
return ( return (
<form <form
onSubmit={(e) => { onSubmit={(e) => {

View File

@ -1,13 +1,17 @@
import { useLocal } from "@/utils/use-local";
import { FC, useEffect } from "react"; import { FC, useEffect } from "react";
import { FieldProp } from "../typings"; import { FieldProp } from "../typings";
import { useField } from "../utils/use-field"; import { useField } from "../utils/use-field";
import { validate } from "../utils/validate"; import { validate } from "../utils/validate";
import { FieldInput } from "./FieldInput"; import { FieldInput } from "./FieldInput";
import { Label } from "./Label"; import { Label } from "./Label";
import { useLocal } from "@/utils/use-local";
export const Field: FC<FieldProp> = (arg) => { export const Field: FC<FieldProp> = (arg) => {
const showlabel = arg.show_label || "y"; const showlabel = arg.show_label || "y";
let type: any = typeof arg.type === "function" ? arg.type() : arg.type; // tipe field
let sub_type: any = arg.sub_type; // tipe field
const { fm } = arg; const { fm } = arg;
const field = useField(arg); const field = useField(arg);
const name = field.name; const name = field.name;
@ -27,6 +31,46 @@ export const Field: FC<FieldProp> = (arg) => {
const errors = fm.error.get(name); const errors = fm.error.get(name);
const props = { ...arg.props }; const props = { ...arg.props };
delete props.className; delete props.className;
if (type === "-" || !type || sub_type === "-" || !sub_type) {
return (
<>
<div className="c-p-4">
Field {arg.label} is not ready
<br />
<div
className={css`
font-size: 12px;
font-weight: normal;
`}
>
{arg.msg_error}
</div>
</div>
</>
);
}
if (
(type === "multi-option" && sub_type === "-") ||
(type === "multi-option" && sub_type === "table-edit" && (!arg.gen_table || arg.gen_table === ""))
) {
return (
<>
<div className="c-p-4">
Table Edit {arg.label} is not ready
<br />
<div
className={css`
font-size: 12px;
font-weight: normal;
`}
>
{arg.msg_error}
</div>
</div>
</>
);
}
return ( return (
<label <label
className={cx( className={cx(
@ -35,8 +79,8 @@ export const Field: FC<FieldProp> = (arg) => {
css` css`
padding: 5px 0px 0px 10px; padding: 5px 0px 0px 10px;
`, `,
w === "auto" && fm.size.field === "full" && "c-w-full", w === "auto" && fm.size.field === "full" && "c-w-full",
w === "auto" && fm.size.field === "half" && "c-w-1/2",
w === "full" && "c-w-full", w === "full" && "c-w-full",
w === "¾" && "c-w-3/4", w === "¾" && "c-w-3/4",
w === "½" && "c-w-1/2", w === "½" && "c-w-1/2",
@ -47,8 +91,9 @@ export const Field: FC<FieldProp> = (arg) => {
)} )}
{...props} {...props}
> >
{mode !== "hidden" && showlabel === "y" && (
{mode !== "hidden" && showlabel === "y" && <Label field={field} fm={fm} />} <Label field={field} fm={fm} />
)}
<div className="field-inner c-flex c-flex-1 c-flex-col"> <div className="field-inner c-flex c-flex-1 c-flex-col">
<FieldInput <FieldInput
field={field} field={field}

View File

@ -1,6 +1,8 @@
import get from "lodash.get";
import { FC, isValidElement } from "react"; import { FC, isValidElement } from "react";
import { FieldLoading } from "../../ui/field-loading"; import { FieldLoading } from "../../ui/field-loading";
import { FMLocal, FieldLocal, FieldProp } from "../typings"; import { FMLocal, FieldLocal, FieldProp } from "../typings";
import { TableEdit } from "./table-edit/TableEdit";
import { FieldTypeInput, PropTypeInput } from "./type/TypeInput"; import { FieldTypeInput, PropTypeInput } from "./type/TypeInput";
import { MultiOption } from "./type/TypeMultiOption"; import { MultiOption } from "./type/TypeMultiOption";
import { SingleOption } from "./type/TypeSingleOption"; import { SingleOption } from "./type/TypeSingleOption";
@ -16,7 +18,7 @@ export const FieldInput: FC<{
child: any; child: any;
_item: PrasiItem; _item: PrasiItem;
arg: FieldProp; arg: FieldProp;
}> = ({ field, fm, arg }) => { }> = ({ field, fm, arg, _item, PassProp, child}) => {
// return <></> // return <></>
const prefix = const prefix =
typeof field.prefix === "function" typeof field.prefix === "function"
@ -46,8 +48,29 @@ export const FieldInput: FC<{
} }
} }
} }
if(type_field === "multi-option" && arg.sub_type === "table-edit"){ if (type_field === "multi-option" && arg.sub_type === "table-edit") {
return <>{arg.child}</> const childsTableEdit = get(
_item,
"edit.props.child.value.childs"
) as unknown as Array<PrasiItem>;
const tableEdit = {
child: get(_item, "edit.props.child.value") as PrasiItem,
bottom: childsTableEdit.find((e) => e.name === "bottom") as PrasiItem,
};
return (
<TableEdit
on_init={() => {
return fm;
}}
name={arg.name}
child={child}
PassProp={PassProp}
item={_item}
bottom={tableEdit.bottom}
body={tableEdit.child}
/>
);
return <>{arg.child}</>;
} }
return ( return (
<div <div
@ -65,8 +88,7 @@ export const FieldInput: FC<{
? field.focused ? field.focused
? "c-border-red-600 c-bg-red-50 c-outline c-outline-red-700" ? "c-border-red-600 c-bg-red-50 c-outline c-outline-red-700"
: "c-border-red-600 c-bg-red-50" : "c-border-red-600 c-bg-red-50"
: field.focused && : field.focused && "c-border-blue-700 c-outline c-outline-blue-700",
"c-border-blue-700 c-outline c-outline-blue-700",
css` css`
& > .field-inner { & > .field-inner {
min-height: 35px; min-height: 35px;

View File

@ -1,8 +1,8 @@
import { FC, MouseEvent } from "react";
import { FMLocal } from "../../typings";
import { useLocal } from "lib/utils/use-local";
import { BaseForm } from "../../base/BaseForm";
import { TableList } from "lib/comps/list/TableList"; import { TableList } from "lib/comps/list/TableList";
import { useLocal } from "lib/utils/use-local";
import { FC } from "react";
import { BaseForm } from "../../base/BaseForm";
import { FMLocal } from "../../typings";
import get from "lodash.get"; import get from "lodash.get";
export const TableEdit: FC<{ export const TableEdit: FC<{
@ -15,20 +15,40 @@ export const TableEdit: FC<{
body: any; body: any;
}> = ({ on_init, name, child, PassProp, item, bottom, body }) => { }> = ({ on_init, name, child, PassProp, item, bottom, body }) => {
const fm = on_init(); const fm = on_init();
const local = useLocal({}, () => { const local = useLocal(
// {
}); tbl: null as any,
const value = fm.data[name] || []; },
() => {}
);
const value = isEditor ? [{}] :Array.isArray(fm.data[name]) ? fm.data[name] : [];
return ( return (
<> <>
<div className="c-w-full c-h-full c-flex c-flex-col"> <div className="c-w-full c-h-full c-flex c-flex-col">
<div className="c-h-[250px] c-w-full"> <div
className={cx(
"c-h-[50px] c-w-full",
css`
> .rdg {
overflow-y: hidden !important;
}
`
)}
style={{
height: isEditor
? 100
: value.length === 0
? 50
: value.length * 50 + 50,
}}
>
<TableList <TableList
rowHeight={50} rowHeight={50}
feature={["checkbox"]} feature={[]}
child={child} child={child}
PassProp={PassProp} PassProp={PassProp}
name={""} name={""}
value={value}
on_init={() => {}} on_init={() => {}}
mode={"table"} mode={"table"}
_item={item} _item={item}
@ -38,24 +58,12 @@ export const TableEdit: FC<{
return false; return false;
}} }}
filter_name={""} filter_name={""}
on_load={async (arg: {
reload: () => Promise<void>;
orderBy?:
| Record<
string,
"desc" | "asc" | Record<string, "desc" | "asc">
>
| undefined;
paging: { take: number; skip: number };
mode: "count" | "query";
}) => {
return value;
}}
render_col={(arg: any) => { render_col={(arg: any) => {
const { props, tbl, child } = arg; const { props, tbl, child } = arg;
const fm_row = { ...fm }; const fm_row = { ...fm };
fm_row.data = props.row; fm_row.data = props.row;
fm_row.render = fm.render; fm_row.render = fm.render;
local.tbl = tbl;
return ( return (
<PassProp <PassProp
idx={props.rowIdx} idx={props.rowIdx}
@ -69,8 +77,6 @@ export const TableEdit: FC<{
fm={fm_row} fm={fm_row}
ext_fm={{ ext_fm={{
remove: () => { remove: () => {
console.log(fm.data)
console.log(props.row)
if (Array.isArray(fm.data[name])) { if (Array.isArray(fm.data[name])) {
fm.data[name] = value.filter( fm.data[name] = value.filter(
(e: any) => e !== props.row (e: any) => e !== props.row
@ -78,7 +84,6 @@ export const TableEdit: FC<{
fm.render(); fm.render();
tbl.data = fm.data[name]; tbl.data = fm.data[name];
tbl.render(); tbl.render();
console.log(fm.data)
} }
}, },
add: () => { add: () => {
@ -98,6 +103,7 @@ export const TableEdit: FC<{
return ( return (
<div className="c-contents"> <div className="c-contents">
<BaseForm <BaseForm
is_form={false}
data={data} data={data}
on_submit={(form) => {}} on_submit={(form) => {}}
render={fm.render} render={fm.render}
@ -111,8 +117,59 @@ export const TableEdit: FC<{
}} }}
/> />
</div> </div>
<PassProp
ext_fm={{
add: () => {
if (Array.isArray(value)) {
value.push({});
fm.data[name] = value;
fm.render();
} else {
fm.data[name] = [{}];
fm.render();
}
},
}}
>
{bottom} {bottom}
</PassProp>
</div> </div>
</> </>
); );
}; };
export const Footer = (
fm: FMLocal,
name: string,
PassProp: any,
child: any,
value: Array<any>
) => {
return (
<>
{/* <div>
<PassProp
ext_fm={{
remove: () => {
if (Array.isArray(fm.data[name])) {
fm.data[name] = value.filter(
(e: any) => e !== props.row
);
fm.render();
tbl.data = fm.data[name];
tbl.render();
console.log(fm.data)
}
},
add: () => {
if (Array.isArray(value)) {
value.push({});
} else {
alert("value bukan array");
}
},
}}></PassProp>
</div> */}
</>
);
};

View File

@ -13,12 +13,15 @@ export const TypeDropdown: FC<{
loaded: false, loaded: false,
options: [], options: [],
}); });
let value = typeof arg.opt_get_value === "function" ? arg.opt_get_value({ let value =
typeof arg.opt_get_value === "function"
? arg.opt_get_value({
fm, fm,
name: field.name, name: field.name,
options: local.options, options: local.options,
type: field.type, type: field.type,
}) : fm.data[field.name];; })
: fm.data[field.name];
useEffect(() => { useEffect(() => {
if (typeof arg.on_load === "function") { if (typeof arg.on_load === "function") {
const options = arg.on_load({}); const options = arg.on_load({});
@ -48,6 +51,7 @@ export const TypeDropdown: FC<{
if (!local.loaded) return <FieldLoading />; if (!local.loaded) return <FieldLoading />;
if (field.type === "single-option") if (field.type === "single-option")
return ( return (
<>
<Typeahead <Typeahead
value={value} value={value}
onSelect={({ search, item }) => { onSelect={({ search, item }) => {
@ -71,6 +75,7 @@ export const TypeDropdown: FC<{
return local.options; return local.options;
}} }}
/> />
</>
); );
return ( return (
@ -80,6 +85,7 @@ export const TypeDropdown: FC<{
onSelect={({ search, item }) => { onSelect={({ search, item }) => {
return item?.value || search; return item?.value || search;
}} }}
note="dropdown"
onChange={(values) => { onChange={(values) => {
arg.opt_set_value({ arg.opt_set_value({
fm, fm,

View File

@ -148,7 +148,7 @@ export const FieldTypeInput: FC<{
</> </>
) : type_field === "money" ? ( ) : type_field === "money" ? (
<> <>
<FieldMoney field={field} fm={fm} prop={prop} /> <FieldMoney field={field} fm={fm} prop={prop} arg={arg} />
</> </>
) : type_field === "rich-text" ? ( ) : type_field === "rich-text" ? (
<> <>

View File

@ -1,12 +1,13 @@
import { useLocal } from "@/utils/use-local"; import { useLocal } from "@/utils/use-local";
import { FC } from "react"; import { FC } from "react";
import { FMLocal, FieldLocal } from "../../typings"; import { FMLocal, FieldLocal, FieldProp } from "../../typings";
import { PropTypeInput } from "./TypeInput"; import { PropTypeInput } from "./TypeInput";
export const FieldMoney: FC<{ export const FieldMoney: FC<{
field: FieldLocal; field: FieldLocal;
fm: FMLocal; fm: FMLocal;
prop: PropTypeInput; prop: PropTypeInput;
}> = ({ field, fm, prop }) => { arg: FieldProp;
}> = ({ field, fm, prop, arg }) => {
let type_field = prop.sub_type; let type_field = prop.sub_type;
let value: any = fm.data[field.name]; let value: any = fm.data[field.name];
const input = useLocal({ const input = useLocal({
@ -15,13 +16,14 @@ export const FieldMoney: FC<{
ref: null as any, ref: null as any,
}); });
let display: any = null; let display: any = null;
// console.log({ prop }); const money = formatMoney(Number(value) || 0)
return ( return (
<div className="c-flex-grow c-flex-row c-flex c-w-full c-h-full"> <div className="c-flex-grow c-flex-row c-flex c-w-full c-h-full">
<div <div
className={cx( className={cx(
input.display ? "c-hidden" : "", input.display ? "c-hidden" : "",
"c-flex-grow c-px-2 c-flex c-flex-row c-items-center" "c-flex-grow c-px-2 c-flex c-flex-row c-items-center",
money === "0" ? "c-text-gray-400" : ""
)} )}
onClick={() => { onClick={() => {
if (input.ref) { if (input.ref) {
@ -31,7 +33,7 @@ export const FieldMoney: FC<{
} }
}} }}
> >
{formatMoney(Number(value) || 0)} {money === "0" ? arg.placeholder : money}
</div> </div>
<input <input
ref={(el) => (input.ref = el)} ref={(el) => (input.ref = el)}

View File

@ -10,6 +10,7 @@ import { get_value } from "./get-value";
import { set_value } from "./set-value"; import { set_value } from "./set-value";
import get from "lodash.get"; import get from "lodash.get";
import { gen_rel_many } from "./gen-rel-many"; import { gen_rel_many } from "./gen-rel-many";
import { genTableEdit } from "./gen-table-edit";
export type GFCol = { export type GFCol = {
name: string; name: string;
type: string; type: string;
@ -23,8 +24,10 @@ export type GFCol = {
}; };
export const newField = ( export const newField = (
arg: GFCol, arg: GFCol,
opt: { parent_table: string; value: Array<string> } opt: { parent_table: string; value: Array<string> },
show_label: boolean
) => { ) => {
let show = typeof show_label === "boolean" ? show_label : true;
let type = "input"; let type = "input";
if (["int", "string", "text"].includes(arg.type)) { if (["int", "string", "text"].includes(arg.type)) {
if (["int"].includes(arg.type)) { if (["int"].includes(arg.type)) {
@ -32,6 +35,7 @@ export const newField = (
component: { component: {
id: "32550d01-42a3-4b15-a04a-2c2d5c3c8e67", id: "32550d01-42a3-4b15-a04a-2c2d5c3c8e67",
props: { props: {
ext__show_label: show ? "y" : "n",
name: arg.name, name: arg.name,
label: formatName(arg.name), label: formatName(arg.name),
type, type,
@ -51,6 +55,7 @@ export const newField = (
label: formatName(arg.name), label: formatName(arg.name),
type, type,
sub_type: "text", sub_type: "text",
ext__show_label: show ? "y" : "n",
child: { child: {
childs: [], childs: [],
}, },
@ -58,7 +63,20 @@ export const newField = (
}, },
}); });
} }
} else if (["timestamptz", "date"].includes(arg.type) && arg.relation) { } else if (["boolean"].includes(arg.type)) {
return createItem({
component: {
id: "32550d01-42a3-4b15-a04a-2c2d5c3c8e67",
props: {
name: arg.name,
label: formatName(arg.name),
ext__show_label: show ? "y" : "n",
type: "single-option",
sub_type: "toogle",
},
},
});
} else if (["timestamptz", "date"].includes(arg.type)) {
return createItem({ return createItem({
component: { component: {
id: "32550d01-42a3-4b15-a04a-2c2d5c3c8e67", id: "32550d01-42a3-4b15-a04a-2c2d5c3c8e67",
@ -66,6 +84,7 @@ export const newField = (
name: arg.name, name: arg.name,
label: formatName(arg.name), label: formatName(arg.name),
type: "date", type: "date",
ext__show_label: show ? "y" : "n",
sub_type: "datetime", sub_type: "datetime",
child: { child: {
childs: [], childs: [],
@ -90,6 +109,7 @@ export const newField = (
name: arg.name, name: arg.name,
label: formatName(arg.name), label: formatName(arg.name),
type: "single-option", type: "single-option",
ext__show_label: show ? "y" : "n",
sub_type: "dropdown", sub_type: "dropdown",
rel__gen_table: arg.name, rel__gen_table: arg.name,
opt__on_load: [load], opt__on_load: [load],
@ -121,8 +141,12 @@ export const newField = (
}, },
}); });
} else { } else {
const result = gen_rel_many({table_parent: opt.parent_table, arg, rel: fields }) const result = gen_rel_many({
console.log({result}) table_parent: opt.parent_table,
arg,
rel: fields,
});
if (result.on_load) {
return createItem({ return createItem({
component: { component: {
id: "32550d01-42a3-4b15-a04a-2c2d5c3c8e67", id: "32550d01-42a3-4b15-a04a-2c2d5c3c8e67",
@ -133,6 +157,7 @@ export const newField = (
sub_type: "checkbox", sub_type: "checkbox",
rel__gen_table: arg.name, rel__gen_table: arg.name,
opt__on_load: [result.on_load], opt__on_load: [result.on_load],
ext__show_label: show ? "y" : "n",
opt__label: [result.get_label], opt__label: [result.get_label],
opt__get_value: [result.get_value], opt__get_value: [result.get_value],
opt__set_value: [result.set_value], opt__set_value: [result.set_value],
@ -142,6 +167,27 @@ export const newField = (
}, },
}, },
}); });
} else {
return createItem({
component: {
id: "32550d01-42a3-4b15-a04a-2c2d5c3c8e67",
props: {
name: arg.name,
ext__show_label: show ? "y" : "n",
label: formatName(arg.name),
type: "-",
ext__width: "full",
sub_type: "-",
msg_error: `\
Select type (multi-option) and sub type (table-edit) select table(${arg.name}) and fields Click generate
`,
child: {
childs: [],
},
},
},
});
}
} }
} else { } else {
// type not found, // type not found,
@ -150,6 +196,7 @@ export const newField = (
id: "32550d01-42a3-4b15-a04a-2c2d5c3c8e67", id: "32550d01-42a3-4b15-a04a-2c2d5c3c8e67",
props: { props: {
name: arg.name, name: arg.name,
ext__show_label: show ? "y" : "n",
label: formatName(arg.name), label: formatName(arg.name),
type, type,
sub_type: "text", sub_type: "text",

View File

@ -46,7 +46,9 @@ export const generateForm = async (
try { try {
const data = { ...form }; // data form const data = { ...form }; // data form
const data_rel = ${JSON.stringify(rel_many)} // list relasi has many const data_rel = ${JSON.stringify(
rel_many
)} // list relasi has many
const data_master = {} as Record<string, any> | any; // variabel untuk data master const data_master = {} as Record<string, any> | any; // variabel untuk data master
const data_array = [] as Array<{ const data_array = [] as Array<{
table: string; table: string;
@ -135,42 +137,177 @@ export const generateForm = async (
}; };
type IForm = { form: any; error: Record<string, string> } type IForm = { form: any; error: Record<string, string> }
` `,
}; };
} }
const childs = []; const childs = [];
console.log({fields})
for (const item of fields.filter((e) => !e.is_pk)) { for (const item of fields.filter((e) => !e.is_pk)) {
let value = [] as Array<string>; let value = [] as Array<string>;
if (["has-one", "has-many"].includes(item.type)) { if (["has-one", "has-many"].includes(item.type)) {
value = get(item, "value.checked") as any; value = get(item, "value.checked") as any;
} }
const field = newField(item, { parent_table: table, value }); const field = newField(item, { parent_table: table, value }, true);
childs.push(field); childs.push(field);
} }
childs.push( childs.push({
createItem({ id: createId(),
name: "btn-wrapper", dim: { h: "fit", w: "full", hUnit: "px", wUnit: "px" },
layout: { dir: "row", align: "top-left", gap: 0, wrap: "flex-nowrap" }, name: "submit",
padding: { l: 10, b: 10, t: 10, r: 10 }, type: "item",
childs: [ childs: [
createItem({ {
id: createId(),
dim: { h: "fit", w: "full", 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: { component: {
id: "a15d152d-0118-408f-89f1-f6b2dfbd2e05", id: "a15d152d-0118-408f-89f1-f6b2dfbd2e05",
props: { props: {
on_click: [ size: {
`\ idx: 5,
() => { meta: {
fm.submit(); 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" stroke-width="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 stroke-linecap="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", "stroke-width": "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", { "stroke-linecap": "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: {
// console.log({ childs }); 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 },
});
if (commit) { if (commit) {
Object.keys(result).map((e) => { Object.keys(result).map((e) => {
item.edit.setProp(e, result[e]); item.edit.setProp(e, result[e]);
@ -184,12 +321,12 @@ export const generateForm = async (
}); });
await item.edit.commit(); await item.edit.commit();
} else { } else {
console.log({data}) console.log({ data });
set(data, "body.value.childs", childs); set(data, "body.value.childs", childs);
Object.keys(result).map((e) => { Object.keys(result).map((e) => {
set(data, e, result[e]); set(data, e, result[e]);
}); });
return return;
} }
} }
}; };

13
comps/form/gen/gen-rel-form.ts Executable file
View File

@ -0,0 +1,13 @@
import { generateSelect } from "lib/comps/md/gen/md-select";
import { get_rel_many } from "./get_rel_many";
import { parseGenField } from "lib/gen/utils";
export const genRelForm = (rel: any) => {
const raw_fields = JSON.parse(rel) as (
| string
| { value: string; checked: string[] }
)[];
const fields = parseGenField(raw_fields);
const res = generateSelect(fields);
const rel_many = get_rel_many(fields);
}

View File

@ -8,12 +8,14 @@ export const gen_rel_many = (prop: {
rel: any; rel: any;
}) => { }) => {
const { table_parent, arg, rel } = prop; const { table_parent, arg, rel } = prop;
console.log({ rel });
const parent = rel.find((e: any) => e.name === table_parent); const parent = rel.find((e: any) => e.name === table_parent);
const master = rel.find( const master = rel.find(
(e: any) => e.name !== table_parent && e.type === "has-one" (e: any) => e.name !== table_parent && e.type === "has-one"
); );
const result = {} as Record<string, any>; const result = {} as Record<string, any>;
// select master // select master
if (master) {
const select = generateSelect(master.relation.fields); const select = generateSelect(master.relation.fields);
result.on_load = on_load_rel({ result.on_load = on_load_rel({
pk: select.pk, pk: select.pk,
@ -21,7 +23,7 @@ export const gen_rel_many = (prop: {
select: select.select, select: select.select,
pks: {}, pks: {},
}); });
const pk_master = master.relation.fields.find((e: any) => get(e, "is_pk")) const pk_master = master.relation.fields.find((e: any) => get(e, "is_pk"));
const get_value = `\ const get_value = `\
(arg: { (arg: {
options: { label: string; value: string; item?: string }[]; options: { label: string; value: string; item?: string }[];
@ -69,7 +71,7 @@ export const gen_rel_many = (prop: {
} }
return result; return result;
} }
` `;
const set_value = `\ const set_value = `\
(arg: { (arg: {
selected: any[]; selected: any[];
@ -114,7 +116,7 @@ export const gen_rel_many = (prop: {
} }
fm.render(); fm.render();
} }
` `;
const cols = []; const cols = [];
for (const [k, v] of Object.entries(select.select) as any) { for (const [k, v] of Object.entries(select.select) as any) {
if (k !== select.pk && typeof v !== "object") { if (k !== select.pk && typeof v !== "object") {
@ -139,9 +141,61 @@ export const gen_rel_many = (prop: {
} }
return row.label; return row.label;
} }
` `;
result.get_label = get_label; result.get_label = get_label;
result.get_value = get_value; result.get_value = get_value;
result.set_value = set_value; result.set_value = set_value;
} else {
console.log("tidak punya master");
result.get_label = `\
(row: { value: string; label: string; item?: any }) => {
return row.label;
}
`;
result.get_value = `\
(arg: {
options: { label: string; value: string; item?: string }[];
fm: FMLocal;
name: string;
type: string;
}) => {
const { options, fm, name, type } = arg;
if (isEditor) {
return fm.data[name];
}
let result = null;
result = fm.data[name];
switch (type) {
case "single-option":
try {
const data = fm.data[name];
if (typeof data === "object") {
if (typeof data?.connect?.id === "string") {
result = data.connect.id;
}else if (typeof data?.id === "string") {
result = data.id;
}
}
} catch (ex) { }
break;
}
return result;
}
`;
result.set_value = `\
(arg: {
selected: any[];
options: { label: string; value: string; item?: string }[];
fm: FMLocal;
name: string;
type: string;
}) => {
fm.render();
}
`;
}
return result; return result;
}; };

View File

@ -1,10 +1,46 @@
export const generateRelation = ( import { createItem } from "lib/gen/utils";
import { genTableEdit } from "./gen-table-edit";
import { createId } from "@paralleldrive/cuid2";
export const generateRelation = async (
data: { data: {
rel__gen_table: any; rel__gen_table: any;
rel__gen_field: any; rel__gen_fields: any;
sub_type: any;
}, },
item: PrasiItem, item: PrasiItem,
commit: boolean commit: boolean
) => { ) => {
console.log(data, item, commit); let sub_type = getValueProp(data.sub_type.value);
if (sub_type === "table-edit") {
const result = (await genTableEdit(
item,
{
gen__table: data.rel__gen_table,
gen__fields: data.rel__gen_fields,
},
false
)) as any;
item.edit.setProp("child", {
mode: "jsx",
value: {
id: createId(),
name: "item",
type: "item",
edit: null as any,
childs: result,
},
});
await item.edit.commit();
}
};
const getValueProp = (data: any) => {
let table = "" as string;
try {
table = eval(data);
} catch (e) {
table = data;
}
return table;
}; };

506
comps/form/gen/gen-table-edit.ts Executable file
View File

@ -0,0 +1,506 @@
import { generateSelect } from "lib/comps/md/gen/md-select";
import { createItem, parseGenField } from "lib/gen/utils";
import get from "lodash.get";
import { formatName, newField } from "./fields";
import { set } from "lib/utils/set";
import { createId } from "@paralleldrive/cuid2";
export const genTableEdit = async (
item: PrasiItem,
data: any,
commit: boolean
) => {
let table = "" as string;
try {
table = eval(data.gen__table.value);
} catch (e) {
table = data.gen__table.value;
}
const raw_fields = JSON.parse(data.gen__fields.value) as (
| string
| { value: string; checked: string[] }
)[];
let pk = "";
let pks: Record<string, string> = {};
const fields = parseGenField(raw_fields);
const res = generateSelect(fields);
pk = res.pk;
const select = res.select as any;
const result: Record<string, PropVal> = {};
if (!pk) {
alert("Failed to generate! Primary Key not found. ");
return;
}
const childs = [] as Array<any>;
let first = true;
fields
.map((e, idx) => {
if (e.is_pk) return;
let value = [] as Array<string>;
if (["has-one", "has-many"].includes(e.type)) {
value = get(e, "value.checked") as any;
}
const field = newField(e, { parent_table: table, value }, false);
let tree_depth = "";
let tree_depth_built = "";
if (first) {
tree_depth = `tree_depth={col.depth}`;
tree_depth_built = `tree_depth:col.depth`;
first = false;
}
childs.push({
component: {
id: "297023a4-d552-464a-971d-f40dcd940b77",
props: {
name: e.name,
title: formatName(e.name),
child: createItem({
childs: [field],
}),
},
},
});
})
.filter((e) => e) as any;
childs.push({
component: {
id: "297023a4-d552-464a-971d-f40dcd940b77",
props: {
name: "option",
title: "option",
child: {
id: createId(),
name: "option",
type: "item",
childs: [
{
id: createId(),
adv: { css: "" },
dim: { h: "fit", w: "fit" },
name: "info",
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: {},
component: {
id: "a15d152d-0118-408f-89f1-f6b2dfbd2e05",
props: {
size: {
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(),
adv: {
js: '<div {...props} className={cx(props.className, "")}>\n <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 16 16"><path fill="currentColor" d="m8.93 6.588l-2.29.287l-.082.38l.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.808 1.319c.545 0 1.178-.252 1.465-.598l.088-.416c-.2.176-.492.246-.686.246c-.275 0-.375-.193-.304-.533zM9 4.5a1 1 0 1 1-2 0a1 1 0 0 1 2 0"/></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 16 16" }, /* @__PURE__ */ React.createElement("path", { fill: "currentColor", d: "m8.93 6.588l-2.29.287l-.082.38l.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.808 1.319c.545 0 1.178-.252 1.465-.598l.088-.416c-.2.176-.492.246-.686.246c-.275 0-.375-.193-.304-.533zM9 4.5a1 1 0 1 1-2 0a1 1 0 0 1 2 0" }))));\n',
},
dim: { h: "full", w: "full" },
html: "submit",
name: "new_text",
text: "",
type: "text",
layout: { dir: "col", gap: 0, align: "center" },
script: {},
},
],
hidden: false,
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: '"outline"',
valueBuilt: '"outline"',
},
on_click: {
idx: 1,
meta: { type: "text" },
name: "prop_1",
type: "string",
value:
"(e) => {\n e.preventDefault();\n e.stopPropagation();\n}",
valueBuilt:
" (e) => {\n e.preventDefault();\n e.stopPropagation();\n};\n",
},
},
ref_ids: {},
},
},
],
},
{
id: createId(),
adv: { css: "" },
dim: { h: "fit", w: "fit" },
name: "remove",
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",
mobile: { linktag: {} },
script: {},
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(),
adv: {
js: '<div {...props} className={cx(props.className, "")}>\n <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 512 512"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32" d="m112 112l20 320c.95 18.49 14.4 32 32 32h184c17.67 0 30.87-13.51 32-32l20-320" /><path fill="currentColor" stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="32" d="M80 112h352" /><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32" d="M192 112V72h0a23.93 23.93 0 0 1 24-24h80a23.93 23.93 0 0 1 24 24h0v40m-64 64v224m-72-224l8 224m136-224l-8 224" /></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 512 512" }, /* @__PURE__ */ React.createElement("path", { fill: "none", stroke: "currentColor", "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "32", d: "m112 112l20 320c.95 18.49 14.4 32 32 32h184c17.67 0 30.87-13.51 32-32l20-320" }), /* @__PURE__ */ React.createElement("path", { fill: "currentColor", stroke: "currentColor", "stroke-linecap": "round", "stroke-miterlimit": "10", "stroke-width": "32", d: "M80 112h352" }), /* @__PURE__ */ React.createElement("path", { fill: "none", stroke: "currentColor", "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "32", d: "M192 112V72h0a23.93 23.93 0 0 1 24-24h80a23.93 23.93 0 0 1 24 24h0v40m-64 64v224m-72-224l8 224m136-224l-8 224" }))));\n',
},
dim: { h: "full", w: "full" },
html: "submit",
name: "new_text",
text: "",
type: "text",
layout: { dir: "col", gap: 0, align: "center" },
script: {},
},
],
hidden: false,
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: '"destructive"',
valueBuilt: '"destructive"',
},
on_click: {
idx: 1,
meta: { type: "text" },
name: "prop_1",
type: "string",
value:
'(e) => {\n e.preventDefault();\n e.stopPropagation();\n if (typeof ext_fm === "object") {\n ext_fm.remove();\n }\n}',
valueBuilt:
' (e) => {\n e.preventDefault();\n e.stopPropagation();\n if (typeof ext_fm === "object") {\n ext_fm.remove();\n }\n};\n',
},
},
ref_ids: {},
},
},
],
},
],
layout: {
dir: "row",
gap: 10,
wrap: "flex-nowrap",
align: "top-left",
},
padding: { b: 0, l: 10, r: 10, t: 0 },
},
},
},
});
const child_sub_name = createItem({
name: "tbl-col",
childs: childs,
});
if (commit) {
item.edit.setProp("bottom", {
mode: "jsx",
value: {
id: createId(),
name: "btn-submit",
type: "item",
edit: null as any,
childs: [
{
id: createId(),
name: "button",
type: "item",
component: {
id: "a15d152d-0118-408f-89f1-f6b2dfbd2e05",
props: {
on_click: {
type: "raw",
value: `\
(e) => {
e.preventDefault();
e.stopPropagation();
if (typeof fm === "object") {
const value = fm.data[name] || [];
if (Array.isArray(value)) {
value.push({});
fm.render();
}else{
value = [];
fm.render();
}
}
}
`,
},
},
},
childs: [],
},
],
},
});
item.edit.setProp("child", {
mode: "jsx",
value: {
id: createId(),
name: "item",
type: "item",
edit: null as any,
childs: [child_sub_name],
},
});
await item.edit.commit();
} else {
const result = [child_sub_name] || [];
const option = {
id: createId(),
name: "bottom",
type: "item",
childs: [
{
id: createId(),
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 if (typeof ext_fm === "object") {\n console.log("masuk");\n ext_fm.add();\n }\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: "vxmz8pt05cm5biygbunoxk4b",
adv: {
js: '<div {...props} className={cx(props.className, "")}>\n <svg\n xmlns="http://www.w3.org/2000/svg"\n width="20"\n height="20"\n viewBox="0 0 24 24"\n >\n <path\n fill="currentColor"\n d="M11.883 3.007L12 3a1 1 0 0 1 .993.883L13 4v7h7a1 1 0 0 1 .993.883L21 12a1 1 0 0 1-.883.993L20 13h-7v7a1 1 0 0 1-.883.993L12 21a1 1 0 0 1-.993-.883L11 20v-7H4a1 1 0 0 1-.993-.883L3 12a1 1 0 0 1 .883-.993L4 11h7V4a1 1 0 0 1 .883-.993L12 3z"\n />\n </svg>\n</div>',
css: "",
jsBuilt:
'render(/* @__PURE__ */ React.createElement("div", { ...props, className: cx(props.className, "") }, /* @__PURE__ */ React.createElement(\n "svg",\n {\n xmlns: "http://www.w3.org/2000/svg",\n width: "20",\n height: "20",\n viewBox: "0 0 24 24"\n },\n /* @__PURE__ */ React.createElement(\n "path",\n {\n fill: "currentColor",\n d: "M11.883 3.007L12 3a1 1 0 0 1 .993.883L13 4v7h7a1 1 0 0 1 .993.883L21 12a1 1 0 0 1-.883.993L20 13h-7v7a1 1 0 0 1-.883.993L12 21a1 1 0 0 1-.993-.883L11 20v-7H4a1 1 0 0 1-.993-.883L3 12a1 1 0 0 1 .883-.993L4 11h7V4a1 1 0 0 1 .883-.993L12 3z"\n }\n )\n)));\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 Add\n</div>',
css: "",
jsBuilt:
'render(/* @__PURE__ */ React.createElement("div", { ...props, className: cx(props.className, "") }, "Add"));\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,
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 if (typeof ext_fm === "object") {\n console.log("masuk")\n ext_fm.add();\n // const value = fm.data[name] || [];\n // if (Array.isArray(value)) {\n // value.push({});\n // fm.render();\n // } else {\n // value = [];\n // fm.render();\n // }\n }\n}',
valueBuilt:
' (e) => {\n e.preventDefault();\n e.stopPropagation();\n if (typeof ext_fm === "object") {\n console.log("masuk");\n ext_fm.add();\n }\n};\n',
},
},
ref_ids: {},
},
},
],
},
],
};
result.push(option);
return result;
}
};

View File

@ -86,6 +86,8 @@ export type FieldProp = {
sub_type: string; sub_type: string;
placeholder: string; placeholder: string;
show_label: boolean; show_label: boolean;
msg_error: string;
gen_table: string;
}; };
export type FMInternal = { export type FMInternal = {

494
comps/list/TableList.css Executable file
View File

@ -0,0 +1,494 @@
@layer rdg {
@layer Defaults,
FocusSink,
CheckboxInput,
CheckboxIcon,
CheckboxLabel,
Cell,
HeaderCell,
SummaryCell,
EditCell,
Row,
HeaderRow,
SummaryRow,
GroupedRow,
Root;
}
.mlln6zg7-0-0-beta-44 {
@layer rdg.MeasuringCell {
contain: strict;
grid-row: 1;
visibility: hidden;
}
}
.cj343x07-0-0-beta-44 {
@layer rdg.Cell {
/* max-content does not work with size containment
* dynamically switching between different containment styles incurs a heavy relayout penalty
* Chromium bug: at odd zoom levels or subpixel positioning,
* layout/paint/style containment can make cell borders disappear
* https://bugs.chromium.org/p/chromium/issues/detail?id=1326946
*/
position: relative; /* needed for absolute positioning to work */
padding-block: 0;
padding-inline: 8px;
border-inline-end: 1px solid var(--rdg-border-color);
border-block-end: 1px solid var(--rdg-border-color);
grid-row-start: var(--rdg-grid-row-start);
background-color: inherit;
white-space: nowrap;
overflow: clip;
text-overflow: ellipsis;
outline: none;
&[aria-selected="true"] {
outline: 2px solid var(--rdg-selection-color);
outline-offset: -2px;
}
}
}
.csofj7r7-0-0-beta-44 {
@layer rdg.Cell {
position: sticky;
/* Should have a higher value than 0 to show up above unfrozen cells */
z-index: 1;
/* Add box-shadow on the last frozen cell */
&:nth-last-child(1 of &) {
box-shadow: var(--rdg-cell-frozen-box-shadow);
}
}
}
.c1bn88vv7-0-0-beta-44 {
@layer rdg.CheckboxLabel {
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
inset: 0;
margin-inline-end: 1px; /* align checkbox in row group cell */
}
}
.c1qt073l7-0-0-beta-44 {
@layer rdg.CheckboxInput {
all: unset;
}
}
.cf71kmq7-0-0-beta-44 {
@layer rdg.CheckboxIcon {
content: "";
inline-size: 20px;
block-size: 20px;
border: 2px solid var(--rdg-border-color);
background-color: var(--rdg-background-color);
.c1qt073l7-0-0-beta-44:checked + & {
background-color: var(--rdg-checkbox-color);
outline: 4px solid var(--rdg-background-color);
outline-offset: -6px;
}
.c1qt073l7-0-0-beta-44:focus + & {
border-color: var(--rdg-checkbox-focus-color);
}
}
}
.c1lwve4p7-0-0-beta-44 {
@layer rdg.CheckboxLabel {
cursor: default;
.cf71kmq7-0-0-beta-44 {
border-color: var(--rdg-checkbox-disabled-border-color);
background-color: var(--rdg-checkbox-disabled-background-color);
}
}
}
.g1s9ylgp7-0-0-beta-44 {
@layer rdg.GroupCellContent {
outline: none;
}
}
.cz54e4y7-0-0-beta-44 {
@layer rdg.GroupCellCaret {
margin-inline-start: 4px;
stroke: currentColor;
stroke-width: 1.5px;
fill: transparent;
vertical-align: middle;
> path {
transition: d 0.1s;
}
}
}
.c1w9bbhr7-0-0-beta-44 {
@layer rdg.DragHandle {
--rdg-drag-handle-size: 8px;
z-index: 0;
cursor: move;
inline-size: var(--rdg-drag-handle-size);
block-size: var(--rdg-drag-handle-size);
background-color: var(--rdg-selection-color);
place-self: end;
&:hover {
--rdg-drag-handle-size: 16px;
border: 2px solid var(--rdg-selection-color);
background-color: var(--rdg-background-color);
}
}
}
.c1creorc7-0-0-beta-44 {
@layer rdg.DragHandle {
z-index: 1;
position: sticky;
}
}
.cis5rrm7-0-0-beta-44 {
@layer rdg.EditCell {
padding: 0;
}
}
.h44jtk67-0-0-beta-44 {
@layer rdg.SortableHeaderCell {
display: flex;
}
}
.hcgkhxz7-0-0-beta-44 {
@layer rdg.SortableHeaderCellName {
flex-grow: 1;
overflow: clip;
text-overflow: ellipsis;
}
}
.c6l2wv17-0-0-beta-44 {
@layer rdg.HeaderCell {
cursor: pointer;
}
}
.c1kqdw7y7-0-0-beta-44 {
@layer rdg.HeaderCell {
touch-action: none;
}
}
.r1y6ywlx7-0-0-beta-44 {
@layer rdg.HeaderCell {
cursor: col-resize;
position: absolute;
inset-block-start: 0;
inset-inline-end: 0;
inset-block-end: 0;
inline-size: 10px;
}
}
.c1bezg5o7-0-0-beta-44 {
opacity: 0.5;
}
.c1vc96037-0-0-beta-44 {
background-color: var(--rdg-header-draggable-background-color);
}
.r1upfr807-0-0-beta-44 {
@layer rdg.Row {
display: contents;
line-height: var(--rdg-row-height);
background-color: var(--rdg-background-color);
&:hover {
background-color: var(--rdg-row-hover-background-color);
}
&[aria-selected="true"] {
background-color: var(--rdg-row-selected-background-color);
&:hover {
background-color: var(--rdg-row-selected-hover-background-color);
}
}
}
}
.r190mhd37-0-0-beta-44 {
@layer rdg.FocusSink {
outline: 2px solid var(--rdg-selection-color);
outline-offset: -2px;
}
}
.r139qu9m7-0-0-beta-44 {
@layer rdg.FocusSink {
&::before {
content: "";
display: inline-block;
height: 100%;
position: sticky;
inset-inline-start: 0;
border-inline-start: 2px solid var(--rdg-selection-color);
}
}
}
.h10tskcx7-0-0-beta-44 {
@layer rdg.HeaderRow {
display: contents;
line-height: var(--rdg-header-row-height);
background-color: var(--rdg-header-background-color);
font-weight: bold;
& > .cj343x07-0-0-beta-44 {
/* Should have a higher value than 1 to show up above regular cells and the focus sink */
z-index: 2;
position: sticky;
}
& > .csofj7r7-0-0-beta-44 {
z-index: 3;
}
}
}
.c6ra8a37-0-0-beta-44 {
@layer rdg.Cell {
background-color: #ccccff;
}
}
.cq910m07-0-0-beta-44 {
@layer rdg.Cell {
background-color: #ccccff;
&.c6ra8a37-0-0-beta-44 {
background-color: #9999ff;
}
}
}
.a3ejtar7-0-0-beta-44 {
@layer rdg.SortIcon {
fill: currentColor;
> path {
transition: d 0.1s;
}
}
}
.rnvodz57-0-0-beta-44 {
@layer rdg.Defaults {
*,
*::before,
*::after {
box-sizing: inherit;
}
}
@layer rdg.Root {
--rdg-color: #000;
--rdg-border-color: #ddd;
--rdg-summary-border-color: #aaa;
--rdg-background-color: hsl(0deg 0% 100%);
--rdg-header-background-color: hsl(0deg 0% 97.5%);
--rdg-header-draggable-background-color: hsl(0deg 0% 90.5%);
--rdg-row-hover-background-color: hsl(0deg 0% 96%);
--rdg-row-selected-background-color: hsl(207deg 76% 92%);
--rdg-row-selected-hover-background-color: hsl(207deg 76% 88%);
--rdg-checkbox-color: hsl(207deg 100% 29%);
--rdg-checkbox-focus-color: hsl(207deg 100% 69%);
--rdg-checkbox-disabled-border-color: #ccc;
--rdg-checkbox-disabled-background-color: #ddd;
--rdg-selection-color: #66afe9;
--rdg-font-size: 14px;
--rdg-cell-frozen-box-shadow: calc(2px * var(--rdg-sign)) 0 5px -2px rgba(136, 136, 136, 0.3);
display: grid;
/* https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context */
/* We set a stacking context so internal elements don't render on top of external elements. */
/* size containment is not used as it could break "width: min-content" for example, and the grid would infinitely resize on Chromium browsers */
contain: content;
content-visibility: auto;
block-size: 350px;
border: 1px solid var(--rdg-border-color);
box-sizing: border-box;
overflow: auto;
background-color: var(--rdg-background-color);
color: var(--rdg-color);
font-size: var(--rdg-font-size);
/* needed on Firefox to fix scrollbars */
&::before {
content: "";
grid-column: 1/-1;
grid-row: 1/-1;
}
/* &.rdg-dark {
--rdg-color-scheme: dark;
--rdg-color: #ddd;
--rdg-border-color: #444;
--rdg-summary-border-color: #555;
--rdg-background-color: hsl(0deg 0% 13%);
--rdg-header-background-color: hsl(0deg 0% 10.5%);
--rdg-header-draggable-background-color: hsl(0deg 0% 17.5%);
--rdg-row-hover-background-color: hsl(0deg 0% 9%);
--rdg-row-selected-background-color: hsl(207deg 76% 42%);
--rdg-row-selected-hover-background-color: hsl(207deg 76% 38%);
--rdg-checkbox-color: hsl(207deg 100% 79%);
--rdg-checkbox-focus-color: hsl(207deg 100% 89%);
--rdg-checkbox-disabled-border-color: #000;
--rdg-checkbox-disabled-background-color: #333;
} */
&.rdg-light {
--rdg-color-scheme: light;
}
/*
@media (prefers-color-scheme: dark) {
&:not(.rdg-light) {
--rdg-color: #ddd;
--rdg-border-color: #444;
--rdg-summary-border-color: #555;
--rdg-background-color: hsl(0deg 0% 13%);
--rdg-header-background-color: hsl(0deg 0% 10.5%);
--rdg-header-draggable-background-color: hsl(0deg 0% 17.5%);
--rdg-row-hover-background-color: hsl(0deg 0% 9%);
--rdg-row-selected-background-color: hsl(207deg 76% 42%);
--rdg-row-selected-hover-background-color: hsl(207deg 76% 38%);
--rdg-checkbox-color: hsl(207deg 100% 79%);
--rdg-checkbox-focus-color: hsl(207deg 100% 89%);
--rdg-checkbox-disabled-border-color: #000;
--rdg-checkbox-disabled-background-color: #333;
}
} */
> :nth-last-child(1 of .rdg-top-summary-row) {
> .cj343x07-0-0-beta-44 {
border-block-end: 2px solid var(--rdg-summary-border-color);
}
}
> :nth-child(1 of .rdg-bottom-summary-row) {
> .cj343x07-0-0-beta-44 {
border-block-start: 2px solid var(--rdg-summary-border-color);
}
}
}
}
.vlqv91k7-0-0-beta-44 {
@layer rdg.Root {
user-select: none;
& .r1upfr807-0-0-beta-44 {
cursor: move;
}
}
}
.f1lsfrzw7-0-0-beta-44 {
@layer rdg.FocusSink {
grid-column: 1/-1;
pointer-events: none;
/* Should have a higher value than 1 to show up above regular frozen cells */
z-index: 1;
}
}
.f1cte0lg7-0-0-beta-44 {
@layer rdg.FocusSink {
/* Should have a higher value than 3 to show up above header and summary rows */
z-index: 3;
}
}
.s8wc6fl7-0-0-beta-44 {
@layer rdg.SummaryCell {
inset-block-start: var(--rdg-summary-row-top);
inset-block-end: var(--rdg-summary-row-bottom);
}
}
.skuhp557-0-0-beta-44 {
@layer rdg.SummaryRow {
line-height: var(--rdg-summary-row-height);
> .cj343x07-0-0-beta-44 {
position: sticky;
}
}
}
.tf8l5ub7-0-0-beta-44 {
@layer rdg.SummaryRow {
> .cj343x07-0-0-beta-44 {
z-index: 2;
}
> .csofj7r7-0-0-beta-44 {
z-index: 3;
}
}
}
.g1yxluv37-0-0-beta-44 {
@layer rdg.GroupedRow {
&:not([aria-selected="true"]) {
background-color: var(--rdg-header-background-color);
}
> .cj343x07-0-0-beta-44:not(:last-child, .csofj7r7-0-0-beta-44),
> :nth-last-child(n + 2 of .csofj7r7-0-0-beta-44) {
border-inline-end: none;
}
}
}
.t7vyx3i7-0-0-beta-44 {
@layer rdg.TextEditor {
appearance: none;
box-sizing: border-box;
inline-size: 100%;
block-size: 100%;
padding-block: 0;
padding-inline: 6px;
border: 2px solid #ccc;
vertical-align: top;
color: var(--rdg-color);
background-color: var(--rdg-background-color);
font-family: inherit;
font-size: var(--rdg-font-size);
&:focus {
border-color: var(--rdg-selection-color);
outline: none;
}
&::placeholder {
color: #999;
opacity: 1;
}
}
}

View File

@ -11,7 +11,7 @@ import DataGrid, {
SELECT_COLUMN_KEY, SELECT_COLUMN_KEY,
SortColumn, SortColumn,
} from "react-data-grid"; } from "react-data-grid";
import "react-data-grid/lib/styles.css"; import "./TableList.css";
import { createPortal } from "react-dom"; import { createPortal } from "react-dom";
import { Toaster, toast } from "sonner"; import { Toaster, toast } from "sonner";
import { Skeleton } from "../ui/skeleton"; import { Skeleton } from "../ui/skeleton";
@ -29,7 +29,8 @@ type TableListProp = {
child: any; child: any;
PassProp: any; PassProp: any;
name: string; name: string;
on_load: (arg: { value?: any[];
on_load?: (arg: {
reload: () => Promise<void>; reload: () => Promise<void>;
orderBy?: Record<string, "asc" | "desc" | Record<string, "asc" | "desc">>; orderBy?: Record<string, "asc" | "desc" | Record<string, "asc" | "desc">>;
paging: { take: number; skip: number }; paging: { take: number; skip: number };
@ -47,7 +48,7 @@ type TableListProp = {
filter_name: string; filter_name: string;
render_row?: (child: any, data: any) => ReactNode; render_row?: (child: any, data: any) => ReactNode;
rowHeight?: number; rowHeight?: number;
render_col?: (props: any) => ReactNode render_col?: (props: any) => ReactNode;
}; };
const w = window as any; const w = window as any;
const selectCellClassname = css` const selectCellClassname = css`
@ -73,7 +74,10 @@ export const TableList: FC<TableListProp> = ({
id_parent, id_parent,
feature, feature,
filter_name, filter_name,
render_row,rowHeight,render_col render_row,
rowHeight,
render_col,
value
}) => { }) => {
const where = get(w, `prasi_filter.${filter_name}`) ?? "hello"; const where = get(w, `prasi_filter.${filter_name}`) ?? "hello";
const whereQuery = filterWhere("hello"); const whereQuery = filterWhere("hello");
@ -121,6 +125,7 @@ export const TableList: FC<TableListProp> = ({
sort: { sort: {
columns: [] as SortColumn[], columns: [] as SortColumn[],
on_change: (cols: SortColumn[]) => { on_change: (cols: SortColumn[]) => {
if (feature?.find((e) => e === "sorting")) {
local.sort.columns = cols; local.sort.columns = cols;
local.paging.skip = 0; local.paging.skip = 0;
@ -165,6 +170,7 @@ export const TableList: FC<TableListProp> = ({
} }
local.status = "reload"; local.status = "reload";
local.render(); local.render();
}
}, },
orderBy: null as null | Record< orderBy: null as null | Record<
string, string,
@ -172,6 +178,9 @@ export const TableList: FC<TableListProp> = ({
>, >,
}, },
}); });
if(typeof value !== "undefined"){
local.data = value;
}
// code ini digunakan untuk mengambil nama dari pk yang akan digunakan sebagai key untuk id // code ini digunakan untuk mengambil nama dari pk yang akan digunakan sebagai key untuk id
const pk = local.pk?.name || "id"; const pk = local.pk?.name || "id";
useEffect(() => { useEffect(() => {
@ -207,7 +216,6 @@ export const TableList: FC<TableListProp> = ({
} }
})(); })();
}, [local.status, on_load, local.sort.orderBy]); }, [local.status, on_load, local.sort.orderBy]);
const raw_childs = get( const raw_childs = get(
child, child,
"props.meta.item.component.props.child.content.childs" "props.meta.item.component.props.child.content.childs"
@ -233,7 +241,7 @@ export const TableList: FC<TableListProp> = ({
// function untuk menghandle checkbox di header (digunakan untuk check all) // function untuk menghandle checkbox di header (digunakan untuk check all)
const headerCheckboxClick = (e: ChangeEvent<HTMLInputElement>) => { const headerCheckboxClick = (e: ChangeEvent<HTMLInputElement>) => {
if (e.target.checked) { if (e.target.checked && Array.isArray(rowData)) {
// jika checbox checked, maka semua rowData akan dimasukkan ke dalam local selected rows // jika checbox checked, maka semua rowData akan dimasukkan ke dalam local selected rows
rowData.forEach((data) => { rowData.forEach((data) => {
local.selectedRows.push({ local.selectedRows.push({
@ -270,7 +278,6 @@ export const TableList: FC<TableListProp> = ({
local.render(); local.render();
} }
}; };
const mode_child = raw_childs.find( const mode_child = raw_childs.find(
(e: any) => e.name === sub_name || e.name === mode (e: any) => e.name === sub_name || e.name === mode
); );
@ -288,6 +295,7 @@ export const TableList: FC<TableListProp> = ({
try { try {
if (feature?.find((e) => e === "checkbox")) isCheckbox = true; if (feature?.find((e) => e === "checkbox")) isCheckbox = true;
} catch (e) {} } catch (e) {}
if (childs.length && isCheckbox) { if (childs.length && isCheckbox) {
columns.push({ columns.push({
key: SELECT_COLUMN_KEY, key: SELECT_COLUMN_KEY,
@ -341,7 +349,8 @@ export const TableList: FC<TableListProp> = ({
resizable: true, resizable: true,
sortable: true, sortable: true,
renderCell(props) { renderCell(props) {
if(typeof render_col === "function") return render_col({props,tbl: local, child}) if (typeof render_col === "function")
return render_col({ props, tbl: local, child });
return ( return (
<PassProp <PassProp
idx={props.rowIdx} idx={props.rowIdx}
@ -403,7 +412,7 @@ export const TableList: FC<TableListProp> = ({
} }
} }
} }
if(typeof value === "undefined")
if (isEditor && local.status !== "ready") { if (isEditor && local.status !== "ready") {
if (local.data.length === 0) { if (local.data.length === 0) {
const load_args: any = { const load_args: any = {
@ -420,13 +429,13 @@ export const TableList: FC<TableListProp> = ({
local.status = "ready"; local.status = "ready";
} }
let selected_idx = -1; let selected_idx = -1;
let data = local.data || []; let data = local.data || [];
if (id_parent && local.pk && local.sort.columns.length === 0) { if (id_parent && local.pk && local.sort.columns.length === 0) {
data = sortTree(local.data, id_parent, local.pk.name); data = sortTree(local.data, id_parent, local.pk.name);
} }
// return "halo dek"
if (mode === "table") { if (mode === "table") {
return ( return (
<div <div
@ -446,7 +455,6 @@ export const TableList: FC<TableListProp> = ({
} }
}} }}
> >
123
{local.status !== "ready" && ( {local.status !== "ready" && (
<div className="c-flex c-flex-col c-space-y-2 c-m-4 c-absolute c-left-0 c-top-0"> <div className="c-flex c-flex-col c-space-y-2 c-m-4 c-absolute c-left-0 c-top-0">
<Skeleton className={cx("c-w-[200px] c-h-[11px]")} /> <Skeleton className={cx("c-w-[200px] c-h-[11px]")} />
@ -530,9 +538,7 @@ export const TableList: FC<TableListProp> = ({
return ( return (
<> <>
<div className="c-contents"> <div className="c-contents">{child_row}</div>
{child_row}
</div>
</> </>
); );
}, },

View File

@ -13,10 +13,12 @@ export const generateTableList = async (
arg: { mode: "table" | "list" | "grid" | "auto"; id_parent?: string }, arg: { mode: "table" | "list" | "grid" | "auto"; id_parent?: string },
commit: boolean commit: boolean
) => { ) => {
console.log({data}) let table = "" as string;
console.log(typeof data.gen_table.value) try {
const table = eval(data.gen_table.value) as string; table = eval(data.gen_table.value);
console.log({table}) } catch (e) {
table = data.gen_table.value;
}
const raw_fields = JSON.parse(data.gen_fields.value) as ( const raw_fields = JSON.parse(data.gen_fields.value) as (
| string | string
| { value: string; checked: string[] } | { value: string; checked: string[] }
@ -24,7 +26,6 @@ export const generateTableList = async (
let pk = ""; let pk = "";
let pks: Record<string, string> = {}; let pks: Record<string, string> = {};
const fields = parseGenField(raw_fields); const fields = parseGenField(raw_fields);
console.log({fields})
// convert ke bahasa prisma untuk select // convert ke bahasa prisma untuk select
const res = generateSelect(fields); const res = generateSelect(fields);
pk = res.pk; pk = res.pk;
@ -55,7 +56,6 @@ export const generateTableList = async (
value: on_load({ pk, table, select, pks }), value: on_load({ pk, table, select, pks }),
}; };
} }
console.log(result.opt__on_load?.value)
let first = true; let first = true;
const child_sub_name = createItem({ const child_sub_name = createItem({
name: sub_name, name: sub_name,
@ -106,16 +106,14 @@ render(React.createElement("div", Object.assign({}, props, { className: cx(props
}) })
.filter((e) => e) as any, .filter((e) => e) as any,
}); });
childs.push(child_sub_name)
// console.log({childs})
if (commit) { if (commit) {
Object.keys(result).map((e) => { Object.keys(result).map((e) => {
item.edit.setProp(e, result[e]); item.edit.setProp(e, result[e]);
}); });
item.edit.setChilds(childs); item.edit.setChilds([child_sub_name]);
await item.edit.commit(); await item.edit.commit();
} else { } else {
set(data, "child.value.childs", childs); set(data, "child.value.childs", [child_sub_name]);
Object.keys(result).map((e) => { Object.keys(result).map((e) => {
set(data, e, result[e]); set(data, e, result[e]);
}); });

View File

@ -60,6 +60,7 @@ export const generateMDForm = async (
label: "List ${formatName(arg.table)}", label: "List ${formatName(arg.table)}",
onClick: () => { onClick: () => {
md.selected = null; md.selected = null;
md.tab.active = "master";
md.internal.action_should_refresh = true; md.internal.action_should_refresh = true;
md.params.apply(); md.params.apply();
md.render(); md.render();
@ -92,6 +93,14 @@ export const generateMDForm = async (
} }
` `
}) })
console.log({form: {
type: "item",
name: "item",
component: {
id: "c4e65c26-4f36-48aa-a5b3-0771feac082e",
props,
},
}})
tab_detail?.edit.setChilds([ tab_detail?.edit.setChilds([
{ {
type: "item", type: "item",

View File

@ -119,6 +119,14 @@ idx: any;
} }
` `
}) })
console.log({
type: "item",
name: "item",
component: {
id: "567d5362-2cc8-4ca5-a531-f771a5c866c2",
props,
},
})
tab_master?.edit.setChilds([ { tab_master?.edit.setChilds([ {
type: "item", type: "item",
name: "item", name: "item",

View File

@ -15,6 +15,7 @@ export const MDDetail: FC<{ md: MDLocal; mdr: MDRef }> = ({ md, mdr }) => {
if (!detail) { if (!detail) {
return null; return null;
} }
console.log(md.tab.list);
return ( return (
<> <>
{md.props.show_head === "only-child" && <MDHeader md={md} mdr={mdr} />} {md.props.show_head === "only-child" && <MDHeader md={md} mdr={mdr} />}
@ -114,10 +115,11 @@ export const MDRenderTab: FC<{
breadcrumb: () => Array<any>; breadcrumb: () => Array<any>;
}> = ({ child, on_init, breadcrumb }) => { }> = ({ child, on_init, breadcrumb }) => {
useEffect(() => { useEffect(() => {
let md = on_init(); let md = on_init();
md.breadcrumb.list = breadcrumb(); md.breadcrumb.list = breadcrumb();
md.breadcrumb.render(); if (!isEditor) {
}, []) md.breadcrumb.reload();
}
}, []);
return <>{child}</>; return <>{child}</>;
}; };

View File

@ -15,7 +15,9 @@ export const MDRenderMaster: FC<{
useEffect(() => { useEffect(() => {
let md = on_init(); let md = on_init();
md.breadcrumb.list = breadcrumb(); md.breadcrumb.list = breadcrumb();
md.breadcrumb.render(); if(!isEditor){
md.breadcrumb.reload();
}
if (md) { if (md) {
let width = 0; let width = 0;
let min_width = 0; let min_width = 0;

View File

@ -25,8 +25,10 @@ export const Typeahead: FC<{
focusOpen?: boolean; focusOpen?: boolean;
disabled?: boolean; disabled?: boolean;
mode?: "multi" | "single"; mode?: "multi" | "single";
note?: string;
}> = ({ }> = ({
value, value,
note,
options: options_fn, options: options_fn,
onSelect, onSelect,
unique, unique,
@ -94,6 +96,9 @@ export const Typeahead: FC<{
if (typeof value === "object" && value) { if (typeof value === "object" && value) {
local.value = value; local.value = value;
local.render(); local.render();
} else if (typeof value === "string") {
local.value = [value];
local.render();
} }
}); });
} else { } else {
@ -257,6 +262,7 @@ export const Typeahead: FC<{
search: local.search.input, search: local.search.input,
existing: local.options, existing: local.options,
}); });
if (res) { if (res) {
const applyOptions = ( const applyOptions = (
result: (string | { value: string; label: string })[] result: (string | { value: string; label: string })[]
@ -267,8 +273,10 @@ export const Typeahead: FC<{
}); });
local.render(); local.render();
}; };
if (res instanceof Promise) { if (res instanceof Promise) {
applyOptions(await res); const result = await res;
applyOptions(result);
} else { } else {
applyOptions(res); applyOptions(res);
} }
@ -385,7 +393,9 @@ export const Typeahead: FC<{
}} }}
> >
<input <input
placeholder={local.mode === "multi" ? placeholder : ""} placeholder={
local.mode === "multi" ? placeholder : valueLabel[0]?.label
}
type="text" type="text"
ref={input} ref={input}
value={local.search.input} value={local.search.input}
@ -482,7 +492,7 @@ export const Typeahead: FC<{
}} }}
spellCheck={false} spellCheck={false}
className={cx( className={cx(
"c-flex-1 c-mb-2 c-text-sm c-outline-none", "c-flex-1 c-mb-2 c-text-sm c-outline-none c-bg-transparent",
local.mode === "single" ? "c-cursor-pointer" : "" local.mode === "single" ? "c-cursor-pointer" : ""
)} )}
onKeyDown={keydown} onKeyDown={keydown}
@ -493,7 +503,7 @@ export const Typeahead: FC<{
<div <div
className={cx( className={cx(
"c-absolute c-pointer-events-none c-z-10 c-inset-0 c-left-auto c-flex c-items-center ", "c-absolute c-pointer-events-none c-z-10 c-inset-0 c-left-auto c-flex c-items-center ",
"c-bg-white c-justify-center c-w-6 c-mr-1 c-my-2", " c-justify-center c-w-6 c-mr-1 c-my-2 c-bg-transparent",
disabled && "c-hidden" disabled && "c-hidden"
)} )}
> >

View File

@ -1,4 +1,5 @@
import { lazify } from "./utils/lazify"; export { FieldLoading } from "./comps/ui/field-loading";
import { lazify, lazifyMany } from "./utils/lazify";
/** Master - Detail - List - Form */ /** Master - Detail - List - Form */
export const MasterDetail = lazify( export const MasterDetail = lazify(
@ -19,16 +20,18 @@ export const Breadcrumb = lazify(
export const TableList = lazify( export const TableList = lazify(
async () => (await import("@/comps/list/TableList")).TableList async () => (await import("@/comps/list/TableList")).TableList
); );
export const Form = lazify(
async () => (await import("@/comps/form/Form")).Form
);
export const TableEdit = lazify( export const TableEdit = lazify(
async () => (await import("@/comps/form/field/table-edit/TableEdit")).TableEdit async () =>
); (await import("@/comps/form/field/table-edit/TableEdit")).TableEdit
export const Field = lazify(
async () => (await import("@/comps/form/field/Field")).Field
); );
const form = lazifyMany({
Form: async () => (await import("@/comps/form/Form")).Form,
Field: async () => (await import("@/comps/form/field/Field")).Field,
});
export const Form = form.Form;
export const Field = form.Field;
/** Export - Import */ /** Export - Import */
export const ImportExcel = lazify( export const ImportExcel = lazify(
async () => (await import("@/comps/list/ImportExcel")).ImportExcel async () => (await import("@/comps/list/ImportExcel")).ImportExcel
@ -49,7 +52,10 @@ export const FilterField = lazify(
/** Generator */ /** Generator */
export { generateMasterDetail } from "@/comps/md/gen/md-gen"; export { generateMasterDetail } from "@/comps/md/gen/md-gen";
export { genTableEdit } from "@/comps/form/gen/gen-table-edit";
export { generateFilter as genereteFilter } from "@/comps/filter/gen/gen-filter";
export { generateRelation } from "@/comps/form/gen/gen-rel";
export { parseGenField } from "@/gen/utils";
/** ETC */ /** ETC */
export { filterModifier } from "@/comps/filter/utils/filter-modifier"; export { filterModifier } from "@/comps/filter/utils/filter-modifier";
export { filterWhere } from "@/comps/filter/utils/filter-where"; export { filterWhere } from "@/comps/filter/utils/filter-where";

View File

@ -1,6 +1,7 @@
import { createId } from "@paralleldrive/cuid2"; import { createId } from "@paralleldrive/cuid2";
import { gen_relation } from "../gen_relation/gen_relation"; import { gen_relation } from "../gen_relation/gen_relation";
import { GFCol, createItem, formatName } from "../utils"; import { GFCol, createItem, formatName } from "../utils";
export const newItem = (component: { export const newItem = (component: {
id: string; id: string;
props: Record<string, string>; props: Record<string, string>;

View File

@ -142,6 +142,6 @@ export const createItem = (arg: SimplifiedItem): any => {
type: "item", type: "item",
component, component,
adv: arg.adv, adv: arg.adv,
childs: arg.childs?.map(createItem), childs: arg.childs?.map(createItem) || [],
}; };
}; };

View File

@ -1,4 +1,4 @@
import { FC } from "react"; import { FC, useEffect } from "react";
import { prasi_user } from "./utils/user"; import { prasi_user } from "./utils/user";
const w = window as unknown as { const w = window as unknown as {
@ -13,11 +13,11 @@ export type LGProps = {
export const Login: FC<LGProps> = (props) => { export const Login: FC<LGProps> = (props) => {
w.prasi_home = props.url_home[0]; w.prasi_home = props.url_home[0];
console.log("render?"); useEffect(() => {
try { try {
const home = prasi_user.prasi_home[prasi_user.user.m_role.name]; const home = prasi_user.prasi_home[prasi_user.user.m_role.name];
navigate(home); navigate(home);
} catch (e: any) { } catch (e: any) {}
} }, []);
return <>{props.body}</>; return <>{props.body}</>;
}; };

View File

@ -1,5 +1,5 @@
import { getPathname } from "lib/utils/pathname"; import { getPathname } from "lib/utils/pathname";
import { FC, ReactNode, useLayoutEffect, useState } from "react"; import { FC, ReactNode, useEffect, useLayoutEffect, useState } from "react";
import { loadSession } from "../login/utils/load"; import { loadSession } from "../login/utils/load";
const w = window as any; const w = window as any;
@ -62,7 +62,9 @@ export const Layout: FC<LYTChild> = (props) => {
fn(); fn();
const path = getPathname(); const path = getPathname();
const no_layout = props.exception; const no_layout = props.exception;
useEffect(() => {
loadSession("/auth/login"); loadSession("/auth/login");
}, []);
if (Array.isArray(no_layout)) if (Array.isArray(no_layout))
if (no_layout.length) { if (no_layout.length) {
if (no_layout.includes(path)) { if (no_layout.includes(path)) {

View File

@ -1,9 +1,6 @@
import { FC, lazy } from "react"; import { FC, ReactNode, Suspense, lazy } from "react";
export const lazify = <T extends FC<any>>( export const lazify = <T extends FC<any>>(fn: () => Promise<T>): T => {
fn: () => Promise<T>,
note?: string
): T => {
return lazy(async () => { return lazy(async () => {
const result = await fn(); const result = await fn();
return { return {
@ -11,3 +8,31 @@ export const lazify = <T extends FC<any>>(
}; };
}) as any; }) as any;
}; };
export const lazifyMany = <T extends Record<string, FC<any>>>(fns: {
[K in keyof T]: () => Promise<T[K]>;
}) => {
const cached = {} as any;
const result = {} as any;
for (const [k, v] of Object.entries(fns)) {
const fn = v as any;
result[k] = lazy(async () => {
if (cached[k]) return { default: cached[k] };
const result = await fn();
cached[k] = result;
for (const [i, j] of Object.entries(fns)) {
if (!cached[i]) {
cached[i] = await (j as any)();
}
}
return {
default: result,
};
}) as any;
}
return result as T;
};