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 { Arrow, VerticalDash } from "./utils";
import { createPortal } from "react-dom";
const Datepicker: React.FC<DatepickerType> = ({
primaryColor = "blue",
@ -70,8 +71,8 @@ const Datepicker: React.FC<DatepickerType> = ({
const [inputRef, setInputRef] = useState(React.createRef<HTMLInputElement>());
// Custom Hooks use
useOnClickOutside(containerRef, () => {
const container = containerRef.current;
useOnClickOutside(calendarContainerRef, () => {
const container = calendarContainerRef.current;
if (container) {
hideDatepicker();
}
@ -341,59 +342,76 @@ const Datepicker: React.FC<DatepickerType> = ({
: defaultContainerClassName;
}, [containerClassName]);
const toaster = document.querySelector(".prasi-toaster");
const rect = inputRef.current?.getBoundingClientRect();
return (
<DatepickerContext.Provider value={contextValues}>
<div className={containerClassNameOverload} ref={containerRef}>
<Input setContextRef={setInputRef} />
<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"
ref={calendarContainerRef}
>
<Arrow ref={arrowRef} />
{toaster &&
createPortal(
<div
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}
>
<Arrow ref={arrowRef} />
<div className=" c-mt-2.5 c-shadow-sm c-border c-border-gray-300 c-px-1 c-py-0.5 c-bg-white dark:c-bg-slate-800 dark:c-text-white dark:c-border-slate-600 c-rounded-lg">
<div className="c-flex c-flex-col lg:c-flex-row c-py-2">
{showShortcuts && <Shortcuts />}
<div
className={`c-flex c-items-stretch c-flex-col md:c-flex-row c-space-y-4 md:c-space-y-0 md:c-space-x-1.5 ${
showShortcuts ? "md:pl-2" : "md:c-pl-1"
} c-pr-2 lg:c-pr-1`}
>
<Calendar
date={firstDate}
onClickPrevious={previousMonthFirst}
onClickNext={nextMonthFirst}
changeMonth={changeFirstMonth}
changeYear={changeFirstYear}
minDate={minDate}
maxDate={maxDate}
/>
{useRange && (
<>
<div className="c-flex c-items-center">
<VerticalDash />
</div>
<div className=" c-mt-2.5 c-shadow-sm c-border c-border-gray-300 c-px-1 c-py-0.5 c-bg-white dark:c-bg-slate-800 dark:c-text-white dark:c-border-slate-600 c-rounded-lg">
<div className="c-flex c-flex-col lg:c-flex-row c-py-2">
{showShortcuts && <Shortcuts />}
<div
className={`c-flex c-items-stretch c-flex-col md:c-flex-row c-space-y-4 md:c-space-y-0 md:c-space-x-1.5 ${
showShortcuts ? "md:pl-2" : "md:c-pl-1"
} c-pr-2 lg:c-pr-1`}
>
<Calendar
date={secondDate}
onClickPrevious={previousMonthSecond}
onClickNext={nextMonthSecond}
changeMonth={changeSecondMonth}
changeYear={changeSecondYear}
date={firstDate}
onClickPrevious={previousMonthFirst}
onClickNext={nextMonthFirst}
changeMonth={changeFirstMonth}
changeYear={changeFirstYear}
minDate={minDate}
maxDate={maxDate}
/>
</>
)}
</div>
</div>
{showFooter && <Footer />}
</div>
</div>
{useRange && (
<>
<div className="c-flex c-items-center">
<VerticalDash />
</div>
<Calendar
date={secondDate}
onClickPrevious={previousMonthSecond}
onClickNext={nextMonthSecond}
changeMonth={changeSecondMonth}
changeYear={changeSecondYear}
minDate={minDate}
maxDate={maxDate}
/>
</>
)}
</div>
</div>
{showFooter && <Footer />}
</div>
</div>,
toaster
)}
</div>
</DatepickerContext.Provider>
);

View File

@ -62,7 +62,7 @@ const Input: React.FC<Props> = (e: Props) => {
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"
? inputClassName(defaultInputClassName)

View File

@ -14,39 +14,39 @@ export function classNames(...classes: (false | null | undefined | string)[]) {
export function getTextColorByPrimaryColor(color: string) {
switch (color) {
case "blue":
return "text-blue-500";
return "c-text-blue-500";
case "orange":
return "text-orange-500";
return "c-text-orange-500";
case "yellow":
return "text-yellow-500";
return "c-text-yellow-500";
case "red":
return "text-red-500";
return "c-text-red-500";
case "purple":
return "text-purple-500";
return "c-text-purple-500";
case "amber":
return "text-amber-500";
return "c-text-amber-500";
case "lime":
return "text-lime-500";
return "c-text-lime-500";
case "green":
return "text-green-500";
return "c-text-green-500";
case "emerald":
return "text-emerald-500";
return "c-text-emerald-500";
case "teal":
return "text-teal-500";
return "c-text-teal-500";
case "cyan":
return "text-cyan-500";
return "c-text-cyan-500";
case "sky":
return "text-sky-500";
return "c-text-sky-500";
case "indigo":
return "text-indigo-500";
return "c-text-indigo-500";
case "violet":
return "text-violet-500";
return "c-text-violet-500";
case "fuchsia":
return "text-fuchsia-500";
return "c-text-fuchsia-500";
case "pink":
return "text-pink-500";
return "c-text-pink-500";
case "rose":
return "text-rose-500";
return "c-text-rose-500";
default:
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;
children: ReactNode | ((form: BaseFormLocal<T>) => ReactNode);
render?: () => void;
is_form?: boolean;
};
export const BaseForm = <T extends Record<string, any>>(
props: BaseFormProps<T>
@ -101,7 +102,29 @@ export const BaseForm = <T extends Record<string, any>>(
if (form.status === "init") {
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 (
<form
onSubmit={(e) => {

View File

@ -1,13 +1,17 @@
import { useLocal } from "@/utils/use-local";
import { FC, useEffect } from "react";
import { FieldProp } from "../typings";
import { useField } from "../utils/use-field";
import { validate } from "../utils/validate";
import { FieldInput } from "./FieldInput";
import { Label } from "./Label";
import { useLocal } from "@/utils/use-local";
export const Field: FC<FieldProp> = (arg) => {
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 field = useField(arg);
const name = field.name;
@ -27,6 +31,46 @@ export const Field: FC<FieldProp> = (arg) => {
const errors = fm.error.get(name);
const props = { ...arg.props };
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 (
<label
className={cx(
@ -35,8 +79,8 @@ export const Field: FC<FieldProp> = (arg) => {
css`
padding: 5px 0px 0px 10px;
`,
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 === "¾" && "c-w-3/4",
w === "½" && "c-w-1/2",
@ -47,8 +91,9 @@ export const Field: FC<FieldProp> = (arg) => {
)}
{...props}
>
{mode !== "hidden" && showlabel === "y" && <Label field={field} fm={fm} />}
{mode !== "hidden" && showlabel === "y" && (
<Label field={field} fm={fm} />
)}
<div className="field-inner c-flex c-flex-1 c-flex-col">
<FieldInput
field={field}

View File

@ -1,6 +1,8 @@
import get from "lodash.get";
import { FC, isValidElement } from "react";
import { FieldLoading } from "../../ui/field-loading";
import { FMLocal, FieldLocal, FieldProp } from "../typings";
import { TableEdit } from "./table-edit/TableEdit";
import { FieldTypeInput, PropTypeInput } from "./type/TypeInput";
import { MultiOption } from "./type/TypeMultiOption";
import { SingleOption } from "./type/TypeSingleOption";
@ -16,20 +18,20 @@ export const FieldInput: FC<{
child: any;
_item: PrasiItem;
arg: FieldProp;
}> = ({ field, fm, arg }) => {
}> = ({ field, fm, arg, _item, PassProp, child}) => {
// return <></>
const prefix =
typeof field.prefix === "function"
? field.prefix()
: typeof field.prefix === "string"
? field.prefix
: null;
? field.prefix
: null;
const suffix =
typeof field.suffix === "function"
? field.suffix()
: typeof field.suffix === "string"
? field.prefix
: null;
? field.prefix
: null;
const name = field.name;
const errors = fm.error.get(name);
let type_field: any = typeof arg.type === "function" ? arg.type() : arg.type; // tipe field
@ -46,8 +48,29 @@ export const FieldInput: FC<{
}
}
}
if(type_field === "multi-option" && arg.sub_type === "table-edit"){
return <>{arg.child}</>
if (type_field === "multi-option" && arg.sub_type === "table-edit") {
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 (
<div
@ -60,13 +83,12 @@ export const FieldInput: FC<{
border-color: transparent;
`
: field.disabled
? "c-border-gray-100"
: errors.length > 0
? field.focused
? "c-border-red-600 c-bg-red-50 c-outline c-outline-red-700"
: "c-border-red-600 c-bg-red-50"
: field.focused &&
"c-border-blue-700 c-outline c-outline-blue-700",
? "c-border-gray-100"
: errors.length > 0
? field.focused
? "c-border-red-600 c-bg-red-50 c-outline c-outline-red-700"
: "c-border-red-600 c-bg-red-50"
: field.focused && "c-border-blue-700 c-outline c-outline-blue-700",
css`
& > .field-inner {
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 { useLocal } from "lib/utils/use-local";
import { FC } from "react";
import { BaseForm } from "../../base/BaseForm";
import { FMLocal } from "../../typings";
import get from "lodash.get";
export const TableEdit: FC<{
@ -15,20 +15,40 @@ export const TableEdit: FC<{
body: any;
}> = ({ on_init, name, child, PassProp, item, bottom, body }) => {
const fm = on_init();
const local = useLocal({}, () => {
//
});
const value = fm.data[name] || [];
const local = useLocal(
{
tbl: null as any,
},
() => {}
);
const value = isEditor ? [{}] :Array.isArray(fm.data[name]) ? fm.data[name] : [];
return (
<>
<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
rowHeight={50}
feature={["checkbox"]}
feature={[]}
child={child}
PassProp={PassProp}
name={""}
value={value}
on_init={() => {}}
mode={"table"}
_item={item}
@ -38,24 +58,12 @@ export const TableEdit: FC<{
return false;
}}
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) => {
const { props, tbl, child } = arg;
const fm_row = { ...fm };
fm_row.data = props.row;
fm_row.render = fm.render;
local.tbl = tbl;
return (
<PassProp
idx={props.rowIdx}
@ -69,8 +77,6 @@ export const TableEdit: FC<{
fm={fm_row}
ext_fm={{
remove: () => {
console.log(fm.data)
console.log(props.row)
if (Array.isArray(fm.data[name])) {
fm.data[name] = value.filter(
(e: any) => e !== props.row
@ -78,7 +84,6 @@ export const TableEdit: FC<{
fm.render();
tbl.data = fm.data[name];
tbl.render();
console.log(fm.data)
}
},
add: () => {
@ -98,6 +103,7 @@ export const TableEdit: FC<{
return (
<div className="c-contents">
<BaseForm
is_form={false}
data={data}
on_submit={(form) => {}}
render={fm.render}
@ -111,8 +117,59 @@ export const TableEdit: FC<{
}}
/>
</div>
{bottom}
<PassProp
ext_fm={{
add: () => {
if (Array.isArray(value)) {
value.push({});
fm.data[name] = value;
fm.render();
} else {
fm.data[name] = [{}];
fm.render();
}
},
}}
>
{bottom}
</PassProp>
</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,
options: [],
});
let value = typeof arg.opt_get_value === "function" ? arg.opt_get_value({
fm,
name: field.name,
options: local.options,
type: field.type,
}) : fm.data[field.name];;
let value =
typeof arg.opt_get_value === "function"
? arg.opt_get_value({
fm,
name: field.name,
options: local.options,
type: field.type,
})
: fm.data[field.name];
useEffect(() => {
if (typeof arg.on_load === "function") {
const options = arg.on_load({});
@ -48,29 +51,31 @@ export const TypeDropdown: FC<{
if (!local.loaded) return <FieldLoading />;
if (field.type === "single-option")
return (
<Typeahead
value={value}
onSelect={({ search, item }) => {
if (item) {
arg.opt_set_value({
fm,
name: field.name,
type: field.type,
options: local.options,
selected: [item.value],
});
}
return item?.value || search;
}}
allowNew={false}
autoPopupWidth={true}
focusOpen={true}
mode={"single"}
placeholder={arg.placeholder}
options={() => {
return local.options;
}}
/>
<>
<Typeahead
value={value}
onSelect={({ search, item }) => {
if (item) {
arg.opt_set_value({
fm,
name: field.name,
type: field.type,
options: local.options,
selected: [item.value],
});
}
return item?.value || search;
}}
allowNew={false}
autoPopupWidth={true}
focusOpen={true}
mode={"single"}
placeholder={arg.placeholder}
options={() => {
return local.options;
}}
/>
</>
);
return (
@ -80,6 +85,7 @@ export const TypeDropdown: FC<{
onSelect={({ search, item }) => {
return item?.value || search;
}}
note="dropdown"
onChange={(values) => {
arg.opt_set_value({
fm,

View File

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

View File

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

View File

@ -10,6 +10,7 @@ import { get_value } from "./get-value";
import { set_value } from "./set-value";
import get from "lodash.get";
import { gen_rel_many } from "./gen-rel-many";
import { genTableEdit } from "./gen-table-edit";
export type GFCol = {
name: string;
type: string;
@ -23,8 +24,10 @@ export type GFCol = {
};
export const newField = (
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";
if (["int", "string", "text"].includes(arg.type)) {
if (["int"].includes(arg.type)) {
@ -32,6 +35,7 @@ export const newField = (
component: {
id: "32550d01-42a3-4b15-a04a-2c2d5c3c8e67",
props: {
ext__show_label: show ? "y" : "n",
name: arg.name,
label: formatName(arg.name),
type,
@ -51,6 +55,7 @@ export const newField = (
label: formatName(arg.name),
type,
sub_type: "text",
ext__show_label: show ? "y" : "n",
child: {
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({
component: {
id: "32550d01-42a3-4b15-a04a-2c2d5c3c8e67",
@ -66,6 +84,7 @@ export const newField = (
name: arg.name,
label: formatName(arg.name),
type: "date",
ext__show_label: show ? "y" : "n",
sub_type: "datetime",
child: {
childs: [],
@ -90,6 +109,7 @@ export const newField = (
name: arg.name,
label: formatName(arg.name),
type: "single-option",
ext__show_label: show ? "y" : "n",
sub_type: "dropdown",
rel__gen_table: arg.name,
opt__on_load: [load],
@ -121,27 +141,53 @@ export const newField = (
},
});
} else {
const result = gen_rel_many({table_parent: opt.parent_table, arg, rel: fields })
console.log({result})
return createItem({
component: {
id: "32550d01-42a3-4b15-a04a-2c2d5c3c8e67",
props: {
name: arg.name,
label: formatName(arg.name),
type: "multi-option",
sub_type: "checkbox",
rel__gen_table: arg.name,
opt__on_load: [result.on_load],
opt__label: [result.get_label],
opt__get_value: [result.get_value],
opt__set_value: [result.set_value],
child: {
childs: [],
const result = gen_rel_many({
table_parent: opt.parent_table,
arg,
rel: fields,
});
if (result.on_load) {
return createItem({
component: {
id: "32550d01-42a3-4b15-a04a-2c2d5c3c8e67",
props: {
name: arg.name,
label: formatName(arg.name),
type: "multi-option",
sub_type: "checkbox",
rel__gen_table: arg.name,
opt__on_load: [result.on_load],
ext__show_label: show ? "y" : "n",
opt__label: [result.get_label],
opt__get_value: [result.get_value],
opt__set_value: [result.set_value],
child: {
childs: [],
},
},
},
},
});
});
} 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 {
// type not found,
@ -150,6 +196,7 @@ export const newField = (
id: "32550d01-42a3-4b15-a04a-2c2d5c3c8e67",
props: {
name: arg.name,
ext__show_label: show ? "y" : "n",
label: formatName(arg.name),
type,
sub_type: "text",

View File

@ -46,7 +46,9 @@ export const generateForm = async (
try {
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_array = [] as Array<{
table: string;
@ -135,42 +137,177 @@ export const generateForm = async (
};
type IForm = { form: any; error: Record<string, string> }
`
`,
};
}
const childs = [];
console.log({fields})
for (const item of fields.filter((e) => !e.is_pk)) {
let value = [] as Array<string>;
if (["has-one", "has-many"].includes(item.type)) {
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(
createItem({
name: "btn-wrapper",
layout: { dir: "row", align: "top-left", gap: 0, wrap: "flex-nowrap" },
padding: { l: 10, b: 10, t: 10, r: 10 },
childs: [
createItem({
component: {
id: "a15d152d-0118-408f-89f1-f6b2dfbd2e05",
props: {
on_click: [
`\
() => {
fm.submit();
}
`,
],
childs.push({
id: createId(),
dim: { h: "fit", w: "full", hUnit: "px", wUnit: "px" },
name: "submit",
type: "item",
childs: [
{
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: {
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" 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: {
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",
},
},
},
},
}),
],
})
);
// console.log({ childs });
],
layout: { dir: "col", gap: 0, wrap: "flex-nowrap", align: "center" },
},
],
padding: { b: 10, l: 10, r: 10, t: 10 },
});
if (commit) {
Object.keys(result).map((e) => {
item.edit.setProp(e, result[e]);
@ -184,12 +321,12 @@ export const generateForm = async (
});
await item.edit.commit();
} else {
console.log({data})
console.log({ data });
set(data, "body.value.childs", childs);
Object.keys(result).map((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,21 +8,153 @@ export const gen_rel_many = (prop: {
rel: any;
}) => {
const { table_parent, arg, rel } = prop;
console.log({ rel });
const parent = rel.find((e: any) => e.name === table_parent);
const master = rel.find(
(e: any) => e.name !== table_parent && e.type === "has-one"
);
const result = {} as Record<string, any>;
// select master
const select = generateSelect(master.relation.fields);
result.on_load = on_load_rel({
pk: select.pk,
table: master.name,
select: select.select,
pks: {},
});
const pk_master = master.relation.fields.find((e: any) => get(e, "is_pk"))
const get_value = `\
if (master) {
const select = generateSelect(master.relation.fields);
result.on_load = on_load_rel({
pk: select.pk,
table: master.name,
select: select.select,
pks: {},
});
const pk_master = master.relation.fields.find((e: any) => get(e, "is_pk"));
const 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;
case "multi-option":
const selected = [];
const data = fm.data[name];
if (Array.isArray(data) && data.length) {
data.map((e) => {
try {
if (typeof e === "object") {
if (typeof e["${master.name}"].connect?.${pk_master.name} === "string") {
selected.push(e["${master.name}"].connect.${pk_master.name});
} else if (typeof e["${master.name}"]?.${pk_master.name} === "string") {
selected.push(e["${master.name}"].${pk_master.name});
}
}
} catch (ex) { }
})
}
return selected;
break;
}
return result;
}
`;
const set_value = `\
(arg: {
selected: any[];
options: { label: string; value: string; item?: string }[];
fm: FMLocal;
name: string;
type: string;
}) => {
const { selected, options, fm, name, type } = arg;
switch (type) {
case "single-option":
fm.data[name] = {
connect: {
id: selected[0],
},
};
break;
case "multi-option":
let parent = {} as any;
try {
parent = {
${arg.relation.from.table}: {
connect: {
${arg.relation.from.fields[0]}: fm.data.id || null,
},
},
};
} catch (e) {}
fm.data[name] = selected.map((e) => {
return {
${master.name}: {
connect: {
id: e,
},
},
...parent,
};
});
break;
default:
fm.data[name] = selected.map((e) => e);
}
fm.render();
}
`;
const cols = [];
for (const [k, v] of Object.entries(select.select) as any) {
if (k !== select.pk && typeof v !== "object") {
cols.push(k);
}
}
const get_label = `\
(row: { value: string; label: string; item?: any }) => {
const cols = ${JSON.stringify(cols)};
if (isEditor) {
return row.label;
}
const result = [];
if (!!row.item && !Array.isArray(row.item)) {
cols.map((e) => {
if (row.item[e]) {
result.push(row.item[e]);
}
});
return result.join(" - ");
}
return row.label;
}
`;
result.get_label = get_label;
result.get_value = get_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;
@ -48,29 +180,11 @@ export const gen_rel_many = (prop: {
}
} catch (ex) { }
break;
case "multi-option":
const selected = [];
const data = fm.data[name];
if (Array.isArray(data) && data.length) {
data.map((e) => {
try {
if (typeof e === "object") {
if (typeof e["${master.name}"].connect?.${pk_master.name} === "string") {
selected.push(e["${master.name}"].connect.${pk_master.name});
} else if (typeof e["${master.name}"]?.${pk_master.name} === "string") {
selected.push(e["${master.name}"].${pk_master.name});
}
}
} catch (ex) { }
})
}
return selected;
break;
}
return result;
}
`
const set_value = `\
`;
result.set_value = `\
(arg: {
selected: any[];
options: { label: string; value: string; item?: string }[];
@ -78,70 +192,10 @@ export const gen_rel_many = (prop: {
name: string;
type: string;
}) => {
const { selected, options, fm, name, type } = arg;
switch (type) {
case "single-option":
fm.data[name] = {
connect: {
id: selected[0],
},
};
break;
case "multi-option":
let parent = {} as any;
try {
parent = {
${arg.relation.from.table}: {
connect: {
${arg.relation.from.fields[0]}: fm.data.id || null,
},
},
};
} catch (e) {}
fm.data[name] = selected.map((e) => {
return {
${master.name}: {
connect: {
id: e,
},
},
...parent,
};
});
break;
default:
fm.data[name] = selected.map((e) => e);
}
fm.render();
}
`
const cols = [];
for (const [k, v] of Object.entries(select.select) as any) {
if (k !== select.pk && typeof v !== "object") {
cols.push(k);
}
`;
}
const get_label = `\
(row: { value: string; label: string; item?: any }) => {
const cols = ${JSON.stringify(cols)};
if (isEditor) {
return row.label;
}
const result = [];
if (!!row.item && !Array.isArray(row.item)) {
cols.map((e) => {
if (row.item[e]) {
result.push(row.item[e]);
}
});
return result.join(" - ");
}
return row.label;
}
`
result.get_label = get_label;
result.get_value = get_value;
result.set_value = set_value;
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: {
rel__gen_table: any;
rel__gen_field: any;
rel__gen_fields: any;
sub_type: any;
},
item: PrasiItem,
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;
placeholder: string;
show_label: boolean;
msg_error: string;
gen_table: string;
};
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,
SortColumn,
} from "react-data-grid";
import "react-data-grid/lib/styles.css";
import "./TableList.css";
import { createPortal } from "react-dom";
import { Toaster, toast } from "sonner";
import { Skeleton } from "../ui/skeleton";
@ -29,7 +29,8 @@ type TableListProp = {
child: any;
PassProp: any;
name: string;
on_load: (arg: {
value?: any[];
on_load?: (arg: {
reload: () => Promise<void>;
orderBy?: Record<string, "asc" | "desc" | Record<string, "asc" | "desc">>;
paging: { take: number; skip: number };
@ -47,7 +48,7 @@ type TableListProp = {
filter_name: string;
render_row?: (child: any, data: any) => ReactNode;
rowHeight?: number;
render_col?: (props: any) => ReactNode
render_col?: (props: any) => ReactNode;
};
const w = window as any;
const selectCellClassname = css`
@ -73,7 +74,10 @@ export const TableList: FC<TableListProp> = ({
id_parent,
feature,
filter_name,
render_row,rowHeight,render_col
render_row,
rowHeight,
render_col,
value
}) => {
const where = get(w, `prasi_filter.${filter_name}`) ?? "hello";
const whereQuery = filterWhere("hello");
@ -121,50 +125,52 @@ export const TableList: FC<TableListProp> = ({
sort: {
columns: [] as SortColumn[],
on_change: (cols: SortColumn[]) => {
local.sort.columns = cols;
local.paging.skip = 0;
if (feature?.find((e) => e === "sorting")) {
local.sort.columns = cols;
local.paging.skip = 0;
if (cols.length > 0) {
const { columnKey, direction } = cols[0];
if (cols.length > 0) {
const { columnKey, direction } = cols[0];
let should_set = true;
const gf = JSON.stringify(gen_fields);
const fields = fields_map.get(gf);
let should_set = true;
const gf = JSON.stringify(gen_fields);
const fields = fields_map.get(gf);
if (fields) {
const rel = fields?.find((e) => e.name === columnKey);
if (rel && rel.checked) {
should_set = false;
if (fields) {
const rel = fields?.find((e) => e.name === columnKey);
if (rel && rel.checked) {
should_set = false;
if (rel.type === "has-many") {
local.sort.orderBy = {
[columnKey]: {
_count: direction === "ASC" ? "asc" : "desc",
},
};
} else {
const field = rel.checked.find((e) => !e.is_pk);
if (field) {
if (rel.type === "has-many") {
local.sort.orderBy = {
[columnKey]: {
[field.name]: direction === "ASC" ? "asc" : "desc",
_count: direction === "ASC" ? "asc" : "desc",
},
};
} else {
const field = rel.checked.find((e) => !e.is_pk);
if (field) {
local.sort.orderBy = {
[columnKey]: {
[field.name]: direction === "ASC" ? "asc" : "desc",
},
};
}
}
}
}
}
if (should_set) {
local.sort.orderBy = {
[columnKey]: direction === "ASC" ? "asc" : "desc",
};
if (should_set) {
local.sort.orderBy = {
[columnKey]: direction === "ASC" ? "asc" : "desc",
};
}
} else {
local.sort.orderBy = null;
}
} else {
local.sort.orderBy = null;
local.status = "reload";
local.render();
}
local.status = "reload";
local.render();
},
orderBy: null as null | Record<
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
const pk = local.pk?.name || "id";
useEffect(() => {
@ -207,7 +216,6 @@ export const TableList: FC<TableListProp> = ({
}
})();
}, [local.status, on_load, local.sort.orderBy]);
const raw_childs = get(
child,
"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)
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
rowData.forEach((data) => {
local.selectedRows.push({
@ -270,7 +278,6 @@ export const TableList: FC<TableListProp> = ({
local.render();
}
};
const mode_child = raw_childs.find(
(e: any) => e.name === sub_name || e.name === mode
);
@ -288,6 +295,7 @@ export const TableList: FC<TableListProp> = ({
try {
if (feature?.find((e) => e === "checkbox")) isCheckbox = true;
} catch (e) {}
if (childs.length && isCheckbox) {
columns.push({
key: SELECT_COLUMN_KEY,
@ -341,7 +349,8 @@ export const TableList: FC<TableListProp> = ({
resizable: true,
sortable: true,
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 (
<PassProp
idx={props.rowIdx}
@ -403,22 +412,23 @@ export const TableList: FC<TableListProp> = ({
}
}
}
if (isEditor && local.status !== "ready") {
if (local.data.length === 0) {
const load_args: any = {
async reload() {},
where,
paging: { take: local.paging.take, skip: local.paging.skip },
};
if (id_parent) load_args.paging = {};
if (typeof on_load === "function") {
local.data = on_load({ ...load_args, mode: "query" }) as any;
if(typeof value === "undefined")
if (isEditor && local.status !== "ready") {
if (local.data.length === 0) {
const load_args: any = {
async reload() {},
where,
paging: { take: local.paging.take, skip: local.paging.skip },
};
if (id_parent) load_args.paging = {};
if (typeof on_load === "function") {
local.data = on_load({ ...load_args, mode: "query" }) as any;
}
}
local.status = "ready";
}
local.status = "ready";
}
let selected_idx = -1;
@ -426,7 +436,6 @@ export const TableList: FC<TableListProp> = ({
if (id_parent && local.pk && local.sort.columns.length === 0) {
data = sortTree(local.data, id_parent, local.pk.name);
}
// return "halo dek"
if (mode === "table") {
return (
<div
@ -446,7 +455,6 @@ export const TableList: FC<TableListProp> = ({
}
}}
>
123
{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">
<Skeleton className={cx("c-w-[200px] c-h-[11px]")} />
@ -482,7 +490,7 @@ export const TableList: FC<TableListProp> = ({
) : (
<>
<DataGrid
rowHeight={rowHeight || 35}
rowHeight={rowHeight || 35}
sortColumns={local.sort.columns}
onSortColumnsChange={local.sort.on_change}
columns={columns}
@ -530,9 +538,7 @@ export const TableList: FC<TableListProp> = ({
return (
<>
<div className="c-contents">
{child_row}
</div>
<div className="c-contents">{child_row}</div>
</>
);
},

View File

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

View File

@ -60,6 +60,7 @@ export const generateMDForm = async (
label: "List ${formatName(arg.table)}",
onClick: () => {
md.selected = null;
md.tab.active = "master";
md.internal.action_should_refresh = true;
md.params.apply();
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([
{
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([ {
type: "item",
name: "item",

View File

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

View File

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

View File

@ -25,8 +25,10 @@ export const Typeahead: FC<{
focusOpen?: boolean;
disabled?: boolean;
mode?: "multi" | "single";
note?: string;
}> = ({
value,
note,
options: options_fn,
onSelect,
unique,
@ -94,6 +96,9 @@ export const Typeahead: FC<{
if (typeof value === "object" && value) {
local.value = value;
local.render();
} else if (typeof value === "string") {
local.value = [value];
local.render();
}
});
} else {
@ -257,6 +262,7 @@ export const Typeahead: FC<{
search: local.search.input,
existing: local.options,
});
if (res) {
const applyOptions = (
result: (string | { value: string; label: string })[]
@ -267,8 +273,10 @@ export const Typeahead: FC<{
});
local.render();
};
if (res instanceof Promise) {
applyOptions(await res);
const result = await res;
applyOptions(result);
} else {
applyOptions(res);
}
@ -385,7 +393,9 @@ export const Typeahead: FC<{
}}
>
<input
placeholder={local.mode === "multi" ? placeholder : ""}
placeholder={
local.mode === "multi" ? placeholder : valueLabel[0]?.label
}
type="text"
ref={input}
value={local.search.input}
@ -482,7 +492,7 @@ export const Typeahead: FC<{
}}
spellCheck={false}
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" : ""
)}
onKeyDown={keydown}
@ -493,7 +503,7 @@ export const Typeahead: FC<{
<div
className={cx(
"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"
)}
>

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 */
export const MasterDetail = lazify(
@ -19,16 +20,18 @@ export const Breadcrumb = lazify(
export const TableList = lazify(
async () => (await import("@/comps/list/TableList")).TableList
);
export const Form = lazify(
async () => (await import("@/comps/form/Form")).Form
);
export const TableEdit = lazify(
async () => (await import("@/comps/form/field/table-edit/TableEdit")).TableEdit
);
export const Field = lazify(
async () => (await import("@/comps/form/field/Field")).Field
async () =>
(await import("@/comps/form/field/table-edit/TableEdit")).TableEdit
);
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 const ImportExcel = lazify(
async () => (await import("@/comps/list/ImportExcel")).ImportExcel
@ -49,7 +52,10 @@ export const FilterField = lazify(
/** Generator */
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 */
export { filterModifier } from "@/comps/filter/utils/filter-modifier";
export { filterWhere } from "@/comps/filter/utils/filter-where";

View File

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

View File

@ -142,6 +142,6 @@ export const createItem = (arg: SimplifiedItem): any => {
type: "item",
component,
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";
const w = window as unknown as {
@ -13,11 +13,11 @@ export type LGProps = {
export const Login: FC<LGProps> = (props) => {
w.prasi_home = props.url_home[0];
console.log("render?");
try {
const home = prasi_user.prasi_home[prasi_user.user.m_role.name];
navigate(home);
} catch (e: any) {
}
useEffect(() => {
try {
const home = prasi_user.prasi_home[prasi_user.user.m_role.name];
navigate(home);
} catch (e: any) {}
}, []);
return <>{props.body}</>;
};

View File

@ -1,5 +1,5 @@
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";
const w = window as any;
@ -62,7 +62,9 @@ export const Layout: FC<LYTChild> = (props) => {
fn();
const path = getPathname();
const no_layout = props.exception;
loadSession("/auth/login");
useEffect(() => {
loadSession("/auth/login");
}, []);
if (Array.isArray(no_layout))
if (no_layout.length) {
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>>(
fn: () => Promise<T>,
note?: string
): T => {
export const lazify = <T extends FC<any>>(fn: () => Promise<T>): T => {
return lazy(async () => {
const result = await fn();
return {
@ -11,3 +8,31 @@ export const lazify = <T extends FC<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;
};