wip fix filter

This commit is contained in:
rizrmd 2024-05-30 21:20:03 -07:00
parent 13bd7e3858
commit 4433f5fcd1
34 changed files with 1413 additions and 561 deletions

32
comps/filter/FieldModifier.tsx Executable file
View File

@ -0,0 +1,32 @@
import capitalize from "lodash.capitalize";
import { FC } from "react";
import { FilterFieldType, modifiers } from "./utils/types";
export const FieldModifier: FC<{
type: FilterFieldType;
modifier: string;
onChange: (modifier: string) => void;
}> = ({ type, modifier, onChange }) => {
if (!modifier && modifiers[type]) {
onChange(Object.keys(modifiers[type])[0]);
}
return (
<select
onChange={(e) => {
const val = e.currentTarget.value;
onChange(val);
}}
>
{modifiers[type]
? Object.entries(modifiers[type]).map(([value, label]) => {
return (
<option selected={modifier === value} value={value}>
{label}
</option>
);
})
: null}
</select>
);
};

View File

@ -1,159 +0,0 @@
import { useLocal } from "@/utils/use-local";
import get from "lodash.get";
import { FC, ReactNode, useEffect, useRef } from "react";
import { FMLocal } from "../form/Form";
import { createPortal, render } from "react-dom";
import { Label } from "@/comps/ui/label";
import { Input } from "@/comps/ui/input";
type FilterPosition = 'regular' | 'inline' | 'popup';
type FilterForm = {
gen_fields: GenField[];
gen_table: string;
name: string;
value: any;
position: FilterPosition;
children?: ReactNode;
onClose?: () => void;
};
type GenField = {
name: string,
is_pk: boolean,
type: string,
optional: boolean
};
export const MasterFilter: FC<FilterForm> = ({ gen_fields, gen_table, name, value, position, children, onClose }): ReactNode => {
const local = useLocal({
data: [] as any[],
columns: [] as string[],
fields: [] as GenField[],
tableName: "",
isGenerated: false,
// fm: {} as FMLocal,
argInit: {} as { fm: FMLocal; submit: any; reload: any },
argOnLoad: {} as { arg: { fm: FMLocal } },
argOnSubmit: {} as {
arg: {
fm: FMLocal;
form: any;
error: any;
}
}
});
useEffect(() => {
if (!isEditor) {
const w = window as any;
if (typeof w["prasi_filter"] !== "object") w["prasi_filter"] = {};
if (typeof w["prasi_filter"][name] !== "object")
w["prasi_filter"][name] = {};
const val = value();
w["prasi_filter"][name] = {
...w["prasi_filter"][name],
...val
};
w.prasiContext.render();
}
}, [])
const renderContent = (): ReactNode => (
<div className={`filter-content filter-${position}`}>
<h3>Filter</h3>
<form>
{local.fields.map((field) => (
<div>
{field.type === 'varchar' && (
<div className="grid w-full max-w-sm items-center gap-1.5" >
<Label htmlFor={field.name}>{field.name}</Label>
<Input id={field.name} type="text" />
</div>
)}
{field.type === "number" && (
<div className="grid w-full max-w-sm items-center gap-1.5" >
<Label htmlFor={field.name}>{field.name}</Label>
<Input id={field.name} type="number" />
</div>
)}
</div>
))}
</form >
</div >
);
const generateFilter = () => {
local.isGenerated = true;
local.tableName = gen_table;
gen_fields.forEach((data: any) => {
local.fields.push(JSON.parse(data));
});
local.render();
console.log('tableName', local.tableName);
console.log('fields', local.fields);
};
if (position === 'popup') {
let popup = document.querySelector(".main-content-preview > .portal");
if (!popup) {
popup = document.createElement("div");
popup.classList.add("portal");
const main = document.querySelector(".main-content-preview");
if (main) {
main.appendChild(popup);
}
}
return (
<>
{createPortal(
<div
onClick={onClose}
className={cx(
css`
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
background: white;
z-index: 100;
`,
"c-flex c-flex-col"
)}
>
{renderContent()}
</div>,
popup
)}
</>
);
}
if (local.isGenerated) {
return renderContent();
} else {
return (
<div>
<button
onClick={generateFilter}
style={{
backgroundColor: "#4CAF50",
border: "none",
color: "white",
padding: "15px 32px",
textAlign: "center",
textDecoration: "none",
display: "inline-block",
fontSize: "16px",
margin: "4px 2px",
cursor: "pointer",
borderRadius: "10px",
}}
>
Generate
</button>
</div >
)
}
};

67
comps/filter/FilterContent.tsx Executable file
View File

@ -0,0 +1,67 @@
import { FC } from "react";
import { BaseForm } from "../form/base/BaseForm";
import { FilterLocal } from "./utils/types";
import { useLocal } from "lib/utils/use-local";
export const FilterContent: FC<{
mode: string;
filter: FilterLocal;
PassProp: any;
child: any;
_item: PrasiItem;
}> = ({ mode, filter, PassProp, child, _item }) => {
const internal = useLocal({});
return (
<div
className={cx(
`c-flex c-flex-1 filter-content filter-${mode}`,
css`
&.filter-content {
border-radius: 4px;
background: #fff;
}
&.filter-regular {
width: 100%;
/* Styles specific to sidebar */
}
&.filter-inline {
display: flex;
align-items: center;
/* Styles specific to topbar */
}
&.filter-popup {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 1000;
/* Styles specific to popup */
}
`
)}
>
<BaseForm
data={filter.data}
on_submit={(form) => {
console.log("skrg nyubmit");
}}
render={internal.render}
>
{(form) => {
filter.form = form;
return (
<>
{!!(PassProp && child) && (
<PassProp filter={filter}>{child}</PassProp>
)}
</>
);
}}
</BaseForm>
</div>
);
};

74
comps/filter/FilterField.tsx Executable file
View File

@ -0,0 +1,74 @@
import { FC, useEffect } from "react";
import { BaseField } from "../form/base/BaseField";
import { FilterLocal, filter_window } from "./utils/types";
import { FieldTypeText } from "../form/field/type/TypeText";
import { FieldModifier } from "./FieldModifier";
import { useLocal } from "lib/utils/use-local";
export const FilterField: FC<{
filter: FilterLocal;
name?: string;
label?: string;
type: "text" | "number" | "boolean";
}> = ({ filter, name, label, type }) => {
const internal = useLocal({ render_timeout: null as any });
if (!name) return <>No Name</>;
if (!filter.form) return <div>Loading...</div>;
filter.types[name] = type;
useEffect(() => {
clearTimeout(internal.render_timeout);
internal.render_timeout = setTimeout(() => {
filter_window.prasiContext.render();
}, 500);
}, [filter.form?.data[name]]);
return (
<BaseField
{...filter.form.fieldProps({
name: name || "",
label: label || name || "",
render: internal.render,
prefix: () => (
<FieldModifier
onChange={(modifier) => {
filter.modifiers[name] = modifier;
filter.render();
filter_window.prasiContext.render();
}}
modifier={filter.modifiers[name]}
type={type}
/>
),
})}
>
{(field) => (
<>
{type === "text" && (
<FieldTypeText
{...field}
prop={{
type: "text",
sub_type: "text",
prefix: "",
suffix: "",
}}
/>
)}
{type === "number" && (
<FieldTypeText
{...field}
prop={{
type: "text",
sub_type: "number",
prefix: "",
suffix: "",
}}
/>
)}
</>
)}
</BaseField>
);
};

View File

@ -1,29 +0,0 @@
import { FC, ReactNode } from "react";
export const Label: FC<{ children: ReactNode; text: string }> = ({
children,
text,
}) => {
return (
<label className="field c-flex c-w-full c-flex-col c-space-y-1 c-px-2">
<div className="label c-text-sm c-flex c-items-center c-mt-3 c-capitalize">
{text}
</div>
<div className="field-inner c-flex c-flex-1 c-flex-col">
<div
className={cx(
"field-outer c-flex c-flex-1 c-flex-row c-rounded c-border c-text-sm c-flex-wrap",
css`
> * {
width: 100%;
padding: 5px;
}
`
)}
>
{children}
</div>
</div>
</label>
);
};

103
comps/filter/MasterFilter.tsx Executable file
View File

@ -0,0 +1,103 @@
import { useLocal } from "@/utils/use-local";
import { FC, ReactNode, useEffect } from "react";
import { createPortal } from "react-dom";
import { GenField } from "../form/typings";
import { default_filter_local, filter_window } from "./utils/types";
import { FilterContent } from "./FilterContent";
import { getPathname } from "lib/utils/pathname";
type FilterMode = "regular" | "inline" | "popup";
type FilterProps = {
gen_fields: GenField[];
gen_table: string;
name: string;
value: any;
mode: FilterMode;
children?: ReactNode;
onClose?: () => void;
PassProp: any;
child: any;
_item: PrasiItem;
};
export const MasterFilter: FC<FilterProps> = ({
gen_fields,
gen_table,
name,
value,
mode,
PassProp,
child,
onClose,
_item,
}): ReactNode => {
const filter = useLocal({ ...default_filter_local });
filter.name = name;
if (!isEditor) {
if (!filter_window.prasi_filter) {
filter_window.prasi_filter = {};
}
const pf = filter_window.prasi_filter;
if (pf) {
const pathname = getPathname();
if (!pf[pathname]) pf[pathname] = {};
if (!pf[pathname][name]) pf[pathname][name] = {};
pf[pathname][name][_item.id] = filter;
}
}
if (mode === "popup") {
let popup = document.querySelector(".main-content-preview > .portal");
if (!popup) {
popup = document.createElement("div");
popup.classList.add("portal");
const main = document.querySelector(".main-content-preview");
if (main) {
main.appendChild(popup);
}
}
return (
<>
{createPortal(
<div
onClick={onClose}
className={cx(
css`
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
background: white;
z-index: 100;
`,
"c-flex c-flex-col"
)}
>
<FilterContent
PassProp={PassProp}
child={child}
mode={mode}
_item={_item}
filter={filter}
/>
</div>,
popup
)}
</>
);
}
return (
<FilterContent
PassProp={PassProp}
_item={_item}
child={child}
mode={mode}
filter={filter}
/>
);
};

View File

@ -1,26 +0,0 @@
.filter-content {
padding: 1rem;
border: 1px solid #ccc;
border-radius: 4px;
background: #fff;
}
.filter-regular {
width: 250px;
/* Styles specific to sidebar */
}
.filter-inline {
display: flex;
align-items: center;
/* Styles specific to topbar */
}
.filter-popup {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 1000;
/* Styles specific to popup */
}

View File

@ -0,0 +1,41 @@
import { getPathname } from "lib/utils/pathname";
import { filter_window } from "./types";
export const filterWhere = (filter_name: string) => {
const pf = filter_window.prasi_filter?.[getPathname()]?.[filter_name];
const where: any = {};
const AND: any[] = [];
if (pf) {
for (const [k, filter] of Object.entries(pf)) {
for (const [name, value] of Object.entries(filter.data)) {
const type = filter.types[name];
const modifier = filter.modifiers[name];
switch (type) {
case "text":
{
if (modifier === "contains")
where[name] = {
contains: value,
mode: "insensitive",
};
}
break;
case "date": {
let is_value_valid = false;
// TODO: pastikan value bisa diparse pakai any-date-parser
if (is_value_valid) {
if (modifier === "between") {
AND.push({ [name]: { gt: value } });
AND.push({ [name]: { lt: value } });
}
}
}
}
}
}
if (AND.length > 0) where.AND = AND;
}
return where;
};

33
comps/filter/utils/types.tsx Executable file
View File

@ -0,0 +1,33 @@
import { BaseFormLocal } from "../../form/base/types";
import { GenField } from "../../form/typings";
export type FilterFieldType = "text" | "number" | "boolean" | "date";
export const default_filter_local = {
data: {} as any,
columns: [] as string[],
fields: [] as GenField[],
tableName: "",
form: null as null | BaseFormLocal<any>,
modifiers: {} as Record<string, string>,
types: {} as Record<string, FilterFieldType>,
name: "",
};
export const modifiers = {
text: { contains: "Contains", ends_with: "Ends With" },
boolean: {},
number: {},
date: {
between: "Between",
},
};
export type FilterModifier = typeof modifiers;
export type FilterLocal = typeof default_filter_local & { render: () => void };
export const filter_window = window as unknown as {
prasi_filter: Record<string, Record<string, Record<string, FilterLocal>>>;
prasiContext: {
render: () => void;
};
};

116
comps/form/base/BaseField.tsx Executable file
View File

@ -0,0 +1,116 @@
import { ReactNode } from "react";
import { FMLocal, FieldLocal, FieldProp } from "../typings";
import { Label } from "../field/Label";
import { FieldLoading } from "../field/raw/FieldLoading";
export const BaseField = (prop: {
field: FieldLocal;
fm: FMLocal;
arg: FieldProp;
children: (arg: {
field: FieldLocal;
fm: FMLocal;
arg: FieldProp;
}) => ReactNode;
}) => {
const { field, fm, arg } = prop;
const w = field.width;
const mode = fm.props.label_mode;
const prefix =
typeof field.prefix === "function"
? field.prefix()
: typeof field.prefix === "string"
? field.prefix
: null;
const suffix =
typeof field.suffix === "function"
? field.suffix()
: typeof field.suffix === "string"
? field.prefix
: null;
const name = field.name;
const errors = fm.error.get(name);
let type_field = typeof arg.type === "function" ? arg.type() : arg.type; // tipe field
return (
<label
className={cx(
"field",
"c-flex",
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",
w === "⅓" && "c-w-1/3",
w === "¼" && "c-w-1/4",
mode === "horizontal" && "c-flex-row c-items-center",
mode === "vertical" && "c-flex-col c-space-y-1"
)}
>
{mode !== "hidden" && <Label field={field} fm={fm} />}
<div className="field-inner c-flex c-flex-1 c-flex-col">
<div
className={cx(
!["toogle", "button", "radio", "checkbox"].includes(arg.sub_type)
? "field-outer c-flex c-flex-1 c-flex-row c-rounded c-border c-text-sm c-flex-wrap"
: "",
fm.status === "loading"
? css`
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",
css`
& > .field-inner {
min-height: 35px;
}
`
)}
>
{prefix && prefix !== "" ? (
<div
className="
c-px-2 c-bg-gray-200 c-flex c-flex-row c-items-center"
>
{prefix}
</div>
) : (
<></>
)}
{fm.status === "loading" ? (
<FieldLoading />
) : (
<div
className={cx(
"field-inner c-flex-1 c-flex c-items-center",
field.disabled && "c-pointer-events-none"
)}
>
{prop.children(prop)}
</div>
)}
{suffix && suffix !== "" ? (
<div
className="
c-px-2 c-bg-gray-200 c-flex c-flex-row c-items-center"
>
{suffix}
</div>
) : (
<></>
)}
</div>
</div>
</label>
);
};

111
comps/form/base/BaseForm.tsx Executable file
View File

@ -0,0 +1,111 @@
import { ReactNode, useCallback, useEffect, useState } from "react";
import { BaseFormLocal, default_base_form_local } from "./types";
import { useLocal } from "lib/utils/use-local";
import { FieldLocal, FieldProp } from "../typings";
export type BaseFormProps<T> = {
data: T;
className?: string;
on_submit?: (form: BaseFormLocal<T>) => Promise<any> | any;
children: ReactNode | ((form: BaseFormLocal<T>) => ReactNode);
render?: () => void;
};
export const BaseForm = <T extends Record<string, any>>(
props: BaseFormProps<T>
) => {
const { data, children, className, on_submit, render } = props;
const form = useLocal({ ...default_base_form_local }) as BaseFormLocal<T>;
if (render) {
form.render = render;
}
form.submit = useCallback(async () => {
if (form.status === "ready") {
form.status = "submitting";
form.render();
const result = await on_submit?.(form);
setTimeout(() => {
form.status = "ready";
form.render();
}, 1000);
return result;
}
}, [on_submit]);
form.createArg = (arg) => {
const prop: FieldProp = { name: arg.name } as any;
if (arg.onChange) prop.on_change = arg.onChange;
return prop;
};
form.createField = (arg) => {
const prop: FieldLocal = {
name: arg.name,
label: typeof arg.label !== "undefined" ? arg.label : arg.name,
render: arg.render || form.render,
width: "auto",
prefix: arg.prefix,
} as any;
return prop;
};
form.createFm = () => {
return {
data: form.data,
props: { label_mode: "vertical" },
error: {
get: () => {
return [];
},
},
size: { field: "full" },
render: form.render,
} as any;
};
form.fieldProps = (arg) => {
return {
fm: form.createFm(),
arg: form.createArg(arg),
field: form.createField(arg),
};
};
useEffect(() => {
form.data = data;
form.render();
}, [data]);
if (form.status === "init") {
form.status = "ready";
}
return (
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
form.submit();
}}
className={cx(
"form c-flex-1 c-flex-col c-w-full c-h-full c-relative c-overflow-auto",
className
)}
>
<div
className={cx(
"form-inner c-flex c-flex-1 c-flex-wrap c-items-start c-content-start c-absolute c-inset-0",
css`
padding-right: 10px;
`
)}
>
{typeof children === "function" ? children(form) : children}
</div>
</form>
);
};

29
comps/form/base/types.ts Executable file
View File

@ -0,0 +1,29 @@
import { ReactNode } from "react";
import { FMLocal, FieldLocal, FieldProp } from "../typings";
export const default_base_form_local = {
status: "init" as "init" | "submitting" | "ready",
data: {} as any,
};
type CreateFieldArg = {
name: string;
label?: string;
prefix?: ReactNode | (() => ReactNode);
onChange?: (value: any) => void;
render?: () => void;
};
export type BaseFormLocal<T> = Omit<typeof default_base_form_local, "data"> & {
data: T;
createArg: (arg: CreateFieldArg) => FieldProp;
createField: (arg: CreateFieldArg) => FieldLocal;
createFm: () => FMLocal;
fieldProps: (arg: CreateFieldArg) => {
fm: FMLocal;
field: FieldLocal;
arg: FieldProp;
};
submit: () => Promise<any> | any;
render: () => void;
};

View File

@ -55,7 +55,6 @@ export const TypeDropdown: FC<{
} else {
selected = value;
}
console.log({selected})
return (
<>
{field.status === "loading" ? (
@ -75,7 +74,6 @@ export const TypeDropdown: FC<{
if (item[input.pk] === val) {
fm.data[field.name] = val;
fm.render();
arg.on_change({value: item})
break;
}
}

View File

@ -1,14 +1,12 @@
import { FC } from "react";
import { FMLocal, FieldLocal, FieldProp } from "../../typings";
import { AutoHeightTextarea } from "@/comps/custom/AutoHeightTextarea";
import { useLocal } from "@/utils/use-local";
import parser from "any-date-parser";
import { AutoHeightTextarea } from "@/comps/custom/AutoHeightTextarea";
import { M } from "src/data/unitShortcuts";
import { format } from "date-fns";
import get from "lodash.get";
import { FC } from "react";
import { FMLocal, FieldLocal, FieldProp } from "../../typings";
import { FieldMoney } from "./TypeMoney";
import { FieldUpload } from "./TypeUpload";
import { FieldRichText } from "./TypeRichText";
import { FieldUpload } from "./TypeUpload";
export type PropTypeText = {
type: "text" | "date";
@ -45,9 +43,11 @@ export const FieldTypeText: FC<{
break;
default:
}
const input = useLocal({});
let display: any = null;
let value: any = fm.data[field.name];
// let value: any = "2024-05-14T05:58:01.376Z" // case untuk date time
field.input = input;
field.prop = prop;
@ -75,6 +75,7 @@ export const FieldTypeText: FC<{
} else if (["number"].includes(type_field)) {
value = Number(value) || null;
}
return (
<>
{type_field === "textarea" ? (
@ -153,7 +154,6 @@ export const FieldTypeText: FC<{
<input
type={type_field}
onChange={(ev) => {
console.log("onchange");
if (["date", "datetime", "datetime-local"].includes(type_field)) {
let result = null;
try {

View File

@ -20,6 +20,16 @@ export type FMProps = {
on_load_deps?: any[];
};
export type GenField = {
name: string,
is_pk: boolean,
type: string,
optional: boolean,
} | {
checked: GenField[],
value: GFCol
};
type FieldType =
| "-"
| "relation"

View File

@ -36,7 +36,8 @@ type TableListProp = {
}) => Promise<any[]>;
on_init: (arg?: any) => any;
mode: "table" | "list" | "grid" | "auto";
_meta: Record<string, any>;
// _meta: Record<string, any>;
_item: PrasiItem;
gen_fields: string[];
row_click: OnRowClick;
selected: SelectedRow;
@ -61,7 +62,7 @@ export const TableList: FC<TableListProp> = ({
PassProp,
mode,
on_init,
_meta,
_item,
gen_fields,
row_click,
selected,
@ -247,7 +248,6 @@ export const TableList: FC<TableListProp> = ({
e.preventDefault();
e.stopPropagation();
const checked = !!local.selectedRows.find((data) => data.pk === rowId);
console.log(checked);
if (!checked) {
// jika checkbox tercheck, maka rowData akan diambil jika memiliki id yang sama dengan rowId yang dikirim
const checkedRowData = rowData.filter((row) => row[pk] === rowId);
@ -256,14 +256,12 @@ export const TableList: FC<TableListProp> = ({
rows: checkedRowData,
});
local.render();
console.log("selected", local.selectedRows);
} else {
// jika tidak, maka akan dihapus
local.selectedRows = local.selectedRows.filter(
(data) => data.pk !== rowId
);
local.render();
console.log("deselected", local.selectedRows);
}
};
@ -271,9 +269,12 @@ export const TableList: FC<TableListProp> = ({
(e: any) => e.name === sub_name || e.name === mode
);
if (mode_child) {
const meta = _meta[mode_child.id];
if (meta && meta.item.childs) {
childs = meta.item.childs;
const tbl = _item.edit.childs[0].edit.childs.find(
(e) => get(e, "id") === mode_child.id
);
const meta = tbl;
if (meta && meta.childs) {
childs = meta.childs;
}
}
let columns: ColumnOrColumnGroup<any>[] = [];
@ -322,37 +323,36 @@ export const TableList: FC<TableListProp> = ({
cellClass: selectCellClassname,
});
}
// for (const child of childs) {
// const key = getProp(child, "name", {});
// const name = getProp(child, "title", {});
// const width = parseInt(getProp(child, "width", {}));
for (const child of childs) {
const key = getProp(child, "name", {});
const name = getProp(child, "title", {});
const width = parseInt(getProp(child, "width", {}));
// columns.push({
// key,
// name,
// width: width > 0 ? width : undefined,
// resizable: true,
// sortable: true,
// renderCell(props) {
// return (
// <PassProp
// idx={props.rowIdx}
// row={props.row}
// col={{
// name: props.column.key,
// value: props.row[props.column.key],
// depth: props.row.__depth || 0,
// }}
// rows={local.data}
// >
// {child}
// </PassProp>
// );
// },
// });
// }
columns.push({
key,
name,
width: width > 0 ? width : undefined,
resizable: true,
sortable: true,
renderCell(props) {
return (
<PassProp
idx={props.rowIdx}
row={props.row}
col={{
name: props.column.key,
value: props.row[props.column.key],
depth: props.row.__depth || 0,
}}
rows={local.data}
>
{child}
</PassProp>
);
},
});
}
if (mode === "list") {
// ambil satu index saja;
if (columns.length > 1) columns = columns.slice(0, 0 + 1);
}
@ -420,12 +420,19 @@ export const TableList: FC<TableListProp> = ({
if (id_parent && local.pk && local.sort.columns.length === 0) {
data = sortTree(local.data, id_parent, local.pk.name);
}
console.log("render?")
// return "123"
if (mode === "table") {
return (
<div
className={cx(
"c-w-full c-h-full c-flex-1 c-relative c-overflow-hidden",
dataGridStyle(local)
dataGridStyle(local),
css`
.rdg {
display: grid !important;
}
`
)}
ref={(el) => {
if (!local.el && el) {
@ -485,7 +492,7 @@ export const TableList: FC<TableListProp> = ({
row: props.row,
rows: local.data,
});
// return "halo"
return (
<Row
key={key}
@ -651,3 +658,11 @@ function isAtBottom({ currentTarget }: React.UIEvent<HTMLDivElement>): boolean {
currentTarget.scrollHeight - currentTarget.clientHeight
);
}
function getProp(child: IItem, name: string, defaultValue?: any) {
const fn = new Function(
`return ${get(child, `component.props.${name}.valueBuilt`) || `null`}`
);
return fn() || defaultValue;
}

View File

@ -73,7 +73,6 @@ export const MasterDetail: FC<MDProps> = (arg) => {
if (isEditor) {
editorMDInit(md, mdr, arg);
}
return (
<div
className={cx(

157
comps/md/gen/form/fields.ts Executable file
View File

@ -0,0 +1,157 @@
import { createItem } from "lib/gen/utils";
import capitalize from "lodash.capitalize";
import { ArrowBigDown } from "lucide-react";
export type GFCol = {
name: string;
type: string;
is_pk: boolean;
optional: boolean;
relation?: {
from: { table: string; fields: string[] };
to: { table: string; fields: string[] };
fields: GFCol[];
};
};
export const newField = (arg: GFCol, opt: { parent_table: string, value: Array<string> }) => {
console.log({ arg, opt });
let type = "input";
if (["int", "string", "text"].includes(arg.type)) {
if (["int"].includes(arg.type)) {
return createItem({
component: {
id: "32550d01-42a3-4b15-a04a-2c2d5c3c8e67",
props: {
name: arg.name,
label: formatName(arg.name),
type,
sub_type: "number",
child: {
childs: [],
},
},
},
});
} else {
return createItem({
component: {
id: "32550d01-42a3-4b15-a04a-2c2d5c3c8e67",
props: {
name: arg.name,
label: formatName(arg.name),
type,
sub_type: "text",
child: {
childs: [],
},
},
},
});
}
} else if (["timestamptz", "date"].includes(arg.type) && arg.relation) {
return createItem({
component: {
id: "32550d01-42a3-4b15-a04a-2c2d5c3c8e67",
props: {
name: arg.name,
label: formatName(arg.name),
type: "date",
sub_type: "datetime",
child: {
childs: [],
},
},
},
});
} else if (["has-many", "has-one"].includes(arg.type) && arg.relation) {
if(["has-one"].includes(arg.type)){
return createItem({
component: {
id: "32550d01-42a3-4b15-a04a-2c2d5c3c8e67",
props: {
name: arg.name,
label: formatName(arg.name),
type: "single-option",
sub_type: "dropdown",
rel__gen_table: arg.name,
rel__gen_fields: [`[${opt.value.join(",")}]`],
child: {
childs: [],
},
},
},
});
// return {
// name: "item",
// type: "item",
// component: {
// id: "32550d01-42a3-4b15-a04a-2c2d5c3c8e67",
// props: {
// name: {
// mode: "string",
// value: arg.name
// },
// label: {
// mode: "string",
// value: formatName(arg.name)
// },
// type: {
// mode: "string",
// value: "single-option"
// },
// sub_type: {
// mode: "string",
// value: "dropdown"
// },
// rel__gen_table: {
// mode: "string",
// value: arg.name
// },
// rel__gen_fields: {
// mode: "raw",
// value: `${JSON.stringify(opt.val)}`
// }
// },
// },
// };
}else{
return createItem({
component: {
id: "32550d01-42a3-4b15-a04a-2c2d5c3c8e67",
props: {
name: arg.name,
label: formatName(arg.name),
type: "date",
sub_type: "datetime",
child: {
childs: [],
},
},
},
});
}
} else {
// type not found,
return createItem({
component: {
id: "32550d01-42a3-4b15-a04a-2c2d5c3c8e67",
props: {
name: arg.name,
label: formatName(arg.name),
type,
sub_type: "text",
child: {
childs: [],
},
},
},
});
}
};
export const formatName = (name: string) => {
return (name || "")
.split("_")
.filter((e) => e.length > 1)
.map((e) => capitalize(e))
.join(" ");
};

66
comps/md/gen/form/on_load.ts Executable file
View File

@ -0,0 +1,66 @@
import { GFCol } from "./fields";
export const on_load = ({
pk,
table,
select,
pks,
opt,
}: {
pk: GFCol;
table: string;
select: any;
pks: Record<string, string>;
opt?: {
before_load: string;
after_load: string;
};
}) => {
const sample: any = {};
for (const [k, v] of Object.entries(select) as any) {
if (typeof v === "object") {
sample[k] = {};
Object.keys(v.select).map((e) => {
sample[k][e] = "sample";
});
} else {
sample[k] = "sample";
}
}
return `\
async (opt) => {
if (isEditor) return ${JSON.stringify(sample)};
let raw_id = params.id;
if (typeof md === 'object' && md.selected && md.pk) {
const pk = md.pk?.name;
if (md.selected[pk]) {
raw_id = md.selected[pk];
}
}
${
opt?.before_load
? opt.before_load
: `let id = ${pk.type === "int" ? "parseInt(raw_id)" : "raw_id"};`
}
let item = {};
if (id){
item = await db.${table}.findFirst({
where: {
${pk.name}: id,
},
select: ${JSON.stringify(select, null, 2).split("\n").join("\n ")},
});
${opt?.after_load ? opt?.after_load : ""}
return item;
} else {
${opt?.after_load ? opt?.after_load : ""}
}
}`;
};

75
comps/md/gen/gen-form.ts Executable file
View File

@ -0,0 +1,75 @@
import { createItem, parseGenField } from "lib/gen/utils";
import get from "lodash.get";
import { generateTableList } from "./gen-table-list";
import { generateSelect } from "./md-select";
import { on_load } from "./tbl-list/on_load";
import { on_submit } from "./tbl-list/on_submit";
import { newField } from "./form/fields";
import { createId } from "@paralleldrive/cuid2";
export const generateForm = async (
modify: (data: any) => void,
data: any,
item: PrasiItem,
commit: boolean
) => {
const table = JSON.parse(data.gen_table.value);
console.log("halo");
console.log(table);
const raw_fields = JSON.parse(data.gen_fields.value) as (
| string
| { value: string; checked: string[] }
)[];
let pk = "";
console.log({ raw_fields });
let pks: Record<string, string> = {};
const fields = parseGenField(raw_fields);
// convert ke bahasa prisma untuk select
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;
}
if (pk) {
console.log("masuk");
if (data["on_load"]) {
result.on_load = {
mode: "raw",
value: on_load({ pk, table, select, pks }),
};
}
if (data["on_submit"]) {
result.on_submit = {
mode: "raw",
value: on_submit({ pk, table, select, pks }),
};
}
result.body = data["body"];
console.log({ fields, result });
const childs = [];
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 });
childs.push(field);
}
if (commit) {
const body = item.edit.childs[0] as PrasiItem;
item.edit.setProp("body", {
mode: "jsx",
value: createItem({
childs: childs,
}),
});
// await item.edit.commit();
} else {
}
}
};

129
comps/md/gen/gen-table-list.ts Executable file
View File

@ -0,0 +1,129 @@
import capitalize from "lodash.capitalize";
import { GFCol, createItem, parseGenField } from "../../../gen/utils";
import { generateSelect } from "./md-select";
import { on_load } from "./tbl-list/on_load";
import { modeTableList } from "./mode-table-list";
import get from "lodash.get";
import set from "lodash.set";
export const generateTableList = async (
modify: (data: any) => void,
data: any,
item: PrasiItem,
arg: { mode: "table" | "list" | "grid" | "auto"; id_parent?: string },
commit: boolean
) => {
const table = JSON.parse(data.gen_table.value) as string;
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);
// convert ke bahasa prisma untuk select
const res = generateSelect(fields);
pk = res.pk;
const select = res.select as any;
const result: Record<string, PropVal> = {};
if (arg.id_parent) {
select[arg.id_parent] = true;
}
if (!pk) {
alert("Failed to generate! Primary Key not found. ");
return;
}
let childs = [] as any;
if (pk) {
let sub_name = modeTableList(arg.mode);
let rows = Array.isArray(get(data, "child.content.childs"))
? get(data, "child.content.childs")
: Array.isArray(get(data, "child.childs"))
? get(data, "child.childs")
: [];
rows = rows.filter((e: PrasiItem) => e.name !== sub_name);
childs = childs.concat(rows);
if (data["on_load"]) {
result.on_load = {
mode: "raw",
value: on_load({ pk, table, select, pks }),
};
}
let first = true;
const child_sub_name = createItem({
name: sub_name,
childs: fields
.map((e, idx) => {
if (idx >= 1 && arg.mode === "list") {
return;
}
if (e.is_pk && arg.mode === "table") return;
let tree_depth = "";
let tree_depth_built = "";
if (first) {
tree_depth = `tree_depth={col.depth}`;
tree_depth_built = `tree_depth:col.depth`;
first = false;
}
return {
component: {
id: "297023a4-d552-464a-971d-f40dcd940b77",
props: {
name: e.name,
title: formatName(e.name),
child: createItem({
childs: [
createItem({
name: "cell",
padding: {
l: 8,
b: 0,
t: 0,
r: 8,
},
adv: {
js: `\
<div {...props} className={cx(props.className, "")}>
<FormatValue value={col.value} name={col.name} gen_fields={gen_fields} ${tree_depth} />
</div>`,
jsBuilt: `\
render(React.createElement("div", Object.assign({}, props, { className: cx(props.className, "") }),React.createElement(FormatValue, { value: col.value, name: col.name, gen_fields: gen_fields, ${tree_depth_built} })));
`,
},
}),
],
}),
},
},
};
})
.filter((e) => e) as any,
});
childs.push(child_sub_name);
if (commit) {
Object.keys(result).map((e) => {
item.edit.setProp(e, result[e]);
});
console.log({ childs });
item.edit.setChilds(childs);
await item.edit.commit();
} else {
set(data, "child.value.childs", childs);
Object.keys(result).map((e) => {
set(data, e, result[e]);
});
}
}
};
const formatName = (name: string) => {
return name
.split("_")
.filter((e) => e.length > 1)
.map((e) => capitalize(e))
.join(" ");
};

122
comps/md/gen/md-form.ts Executable file
View File

@ -0,0 +1,122 @@
import { createItem } from "lib/gen/utils";
import get from "lodash.get";
import { generateTableList } from "./gen-table-list";
export const generateMDForm = async (
arg: { item: PrasiItem; table: string; fields: any },
data: any,
commit: boolean
) => {
const item = arg.item;
const tab_master = item.edit.childs[0].edit.childs.find(
(e) => get(e, "component.id") === "c68415ca-dac5-44fe-aeb6-936caf8cc491"
);
const props: Record<string, PropVal> = {
gen_table: {
mode: "string",
value: arg.table,
},
name: {
mode: "string",
value: arg.table,
},
generate: {
mode: "string",
value: "y",
},
on_load: {
mode: "string",
value: "",
},
row_click: {
mode: "raw",
value: `\
({ row, rows, idx, event }: OnRowClick) => {
md.selected = row;
md.internal.action_should_refresh = true;
md.params.apply();
md.render();
};
type OnRowClick = {
row: any;
rows: any[];
idx: any;
event: React.MouseEvent<HTMLDivElement, MouseEvent>;
}
`,
},
selected: {
mode: "raw",
value: `\
({ row, rows, idx }: SelectedRow) => {
try {
if (typeof md === "object") {
if (Array.isArray(md.selected)) {
if (md.selected.length) {
let select = md.selected.find((e) => e === row)
if(select) return true
}
} else {
if (md.selected === row) {
return true;
}
}
}
} catch (e) {
}
return false;
};
type SelectedRow = {
row: any;
rows: any[];
idx: any;
}`,
},
gen_fields: {
mode: "raw",
value: `${JSON.stringify(arg.fields)}`,
},
child: {
mode: "jsx",
value: createItem({
name: "halo",
childs: [],
}),
},
};
const tablelist: any = {
type: "item",
name: "item",
component: {
id: "567d5362-2cc8-4ca5-a531-f771a5c866c2",
props,
},
};
generateTableList(
async (props: any) => {},
props,
tablelist,
{ mode: "table" },
false
);
tab_master?.edit.setChilds([ {
type: "item",
name: "item",
component: {
id: "567d5362-2cc8-4ca5-a531-f771a5c866c2",
props,
},
}]);
console.log({
type: "item",
name: "item",
component: {
id: "567d5362-2cc8-4ca5-a531-f771a5c866c2",
props,
},
})
// console.log(tablelist);
};

View File

@ -1,6 +1,6 @@
import { createItem } from "lib/gen/utils";
import get from "lodash.get";
import { generateTableList } from "./md-table-list";
import { generateTableList } from "./gen-table-list";
export const generateList = async (
arg: { item: PrasiItem; table: string; fields: any },
@ -82,47 +82,32 @@ idx: any;
child: {
mode: "jsx",
value: createItem({
name: "halo"
})
}
name: "halo",
childs: [],
}),
},
};
// console.log(true);
console.log({ tab_master });
const tablelist = tab_master?.childs[0];
// generateTableList(
// async (props: any) => {},
// props,
// tablelist,
// { mode: "table" },
// false
// );
// // console.log({modify: async (props: any) => {}, data, item, arg: { mode: "table" }});
// const tablelist: PrasiItem= get(item,"edit.childs[0].edit.childs[0].edit.childs[0]");
// const data_tbl = get(tablelist, "component.props")
// console.log({tablelist, data_tbl})
// if(typeof tab_master === "object"){
// // const master_child: PrasiItem= tab_master;
// // const prop_tablelist = get(tablelist, "component.props")
// // console.log({tablelist})
// //
// }
// tab_master?.edit.setChilds([
// {
// type: "item",
// name: "item",
// component: {
// id: "567d5362-2cc8-4ca5-a531-f771a5c866c2",
// props,
// },
// },
// ]);
console.log({
const tablelist: any = {
type: "item",
name: "item",
component: {
id: "567d5362-2cc8-4ca5-a531-f771a5c866c2",
props,
},
})
};
generateTableList(
async (props: any) => {},
props,
tablelist,
{ mode: "table" },
false
);
tab_master?.edit.setChilds([ {
type: "item",
name: "item",
component: {
id: "567d5362-2cc8-4ca5-a531-f771a5c866c2",
props,
},
}]);
};

View File

@ -1,241 +0,0 @@
import capitalize from "lodash.capitalize";
import { GFCol, createItem, parseGenField } from "../../../gen/utils";
import { generateSelect } from "./md-select";
import { on_load } from "./tbl-list/on_load";
import { modeTableList } from "./mode-table-list";
import get from "lodash.get";
import set from "lodash.set";
export const generateTableList = async (
modify: (data: any) => void,
data: any,
item: PrasiItem,
arg: { mode: "table" | "list" | "grid" | "auto"; id_parent?: string },
commit: boolean
) => {
item.edit.setChilds([
{
type: "item",
name: "1",
},
{
type: "item",
name: "2",
},
]);
await item.edit.commit();
// console.log({ data, item, arg });
// const table = data.gen_table.value as string;
// 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 result = {} as any;
// // generate value dari raw_field array string convert ke value selected prisma
// const res = generateSelect(fields);
// pk = res.pk;
// const select = res.select as any;
// const result: Record<string, PropVal> = {};
// if (arg.id_parent) {
// select[arg.id_parent] = true;
// }
// if (!pk) {
// alert("Failed to generate! Primary Key not found. ");
// return;
// }
// let childs = [] as any;
// if (pk) {
// let sub_name = modeTableList(arg.mode);
// let rows = Array.isArray(get(data, "child.content.childs"))
// ? get(data, "child.content.childs")
// : Array.isArray(get(data, "child.childs"))
// ? get(data, "child.childs")
// : [];
// rows = rows.filter((e: PrasiItem) => e.name !== sub_name);
// childs = childs.concat(rows);
// if (data["on_load"]) {
// result.on_load = {
// mode: "raw",
// value: on_load({ pk, table, select, pks }),
// };
// }
// let first = true;
// const child_sub_name = createItem({
// name: sub_name,
// childs: fields
// .map((e, idx) => {
// if (idx >= 1 && arg.mode === "list") {
// return;
// }
// if (e.is_pk && arg.mode === "table") return;
// let tree_depth = "";
// let tree_depth_built = "";
// if (first) {
// tree_depth = `tree_depth={col.depth}`;
// tree_depth_built = `tree_depth:col.depth`;
// first = false;
// }
// return {
// component: {
// id: "297023a4-d552-464a-971d-f40dcd940b77",
// props: {
// name: e.name,
// title: formatName(e.name),
// child: createItem({
// childs: [
// createItem({
// name: "cell",
// padding: {
// l: 8,
// b: 0,
// t: 0,
// r: 8,
// },
// adv: {
// js: `\
// <div {...props} className={cx(props.className, "")}>
// <FormatValue value={col.value} name={col.name} gen_fields={gen_fields} ${tree_depth} />
// </div>`,
// jsBuilt: `\
// render(React.createElement("div", Object.assign({}, props, { className: cx(props.className, "") }),React.createElement(FormatValue, { value: col.value, name: col.name, gen_fields: gen_fields, ${tree_depth_built} })));
// `,
// },
// }),
// ],
// }),
// },
// },
// };
// })
// .filter((e) => e) as any,
// });
// childs.push(child_sub_name);
// // result.child = {
// // mode: "jsx",
// // value: createItem({ name: "child", childs: [child] }),
// // };
// // item.edit.setChilds([child]);
// if (commit) {
// Object.keys(result).map((e) => {
// item.edit.setProp(e, result[e]);
// });
// console.log({ childs });
// item.edit.setChilds([
// {
// name: sub_name,
// },
// {
// name: "123",
// },
// ]);
// await item.edit.commit();
// } else {
// set(item, "childs", childs);
// Object.keys(result).map((e) => {
// set(data, e, result[e]);
// });
// }
// console.log({ res, item });
// console.log({ data });
// console.log({ result });
// }
// return;
// if (pk) {
// console.log("pk");
// const code = {} as any;
// if (data["on_load"]) {
// result["on_load"] = data["on_load"];
// result["on_load"].value = on_load({ pk, table, select, pks });
// delete result["on_load"].valueBuilt;
// code.on_load = result["on_load"].value;
// }
// let sub_name = "fields";
// switch (arg.mode) {
// case "table":
// sub_name = "tbl-col";
// break;
// case "list":
// sub_name = "md-list";
// break;
// }
// let first = true;
// console.log(sub_name);
// const child = {
// name: sub_name,
// childs: fields
// .map((e, idx) => {
// if (idx >= 1 && arg.mode === "list") {
// return;
// }
// if (e.is_pk && arg.mode === "table") return;
// let tree_depth = "";
// let tree_depth_built = "";
// if (first) {
// tree_depth = `tree_depth={col.depth}`;
// tree_depth_built = `tree_depth:col.depth`;
// first = false;
// }
// return {
// component: {
// id: "297023a4-d552-464a-971d-f40dcd940b77",
// props: {
// name: {
// mode: "string",
// value: e.name
// },
// title: {
// mode: "string",
// value: formatName(e.name)
// },
// child: createItem({
// childs: [
// createItem({
// name: "cell",
// padding: {
// l: 8,
// b: 0,
// t: 0,
// r: 8,
// },
// adv: {
// js: `\
// <div {...props} className={cx(props.className, "")}>
// <FormatValue value={col.value} name={col.name} gen_fields={gen_fields} ${tree_depth} />
// </div>`,
// jsBuilt: `\
// render(React.createElement("div", Object.assign({}, props, { className: cx(props.className, "") }),React.createElement(FormatValue, { value: col.value, name: col.name, gen_fields: gen_fields, ${tree_depth_built} })));
// `,
// },
// }),
// ],
// }),
// },
// },
// };
// })
// .filter((e) => e) as any,
// };
// data.child.value = child;
// result["child"] = data.child
// }
// modify(result);
// console.log({ child: data["child"] });
};
const formatName = (name: string) => {
return name
.split("_")
.filter((e) => e.length > 1)
.map((e) => capitalize(e))
.join(" ");
};

View File

@ -0,0 +1,85 @@
import { GFCol } from "lib/gen/utils";
export const on_submit = ({
pk,
table,
select,
pks,
}: {
pk: string;
table: string;
select: any;
pks: Record<string, string>;
}) => {
const id = pk;
return `\
async ({ form, error }: IForm) => {
if (typeof form !== "object") return false;
if (typeof error === "object" && Object.keys(error).length > 0) return false;
let original_actions = [];
if (typeof md === "object") {
original_actions = md.actions;
md.actions = [{ label: <>...</> }];
md.render();
}
let result = false;
try {
const data = { ...form };
delete data.${id};
if (data) {
const pks = ${JSON.stringify(pks)};
for (const [k, v] of Object.entries(pks)) {
if (typeof data[k] === 'object') {
if (data[k] === null) {
data[k] = {
disconnect: true
}
}
if (data[k][v]) {
data[k] = {
connect: {
[v]: data[k][v]
}
}
}
}
}
}
if (form.${id}) {
await db.${table}.update({
where: {
${id}: form.${id},
},
data,
});
} else {
const res = await db.${table}.create({
data,
select: { ${id}: true },
});
if (res) form.${id} = res.${id};
}
result = true;
} catch (e) {
console.error(e);
result = false;
}
if (typeof md === "object") {
md.actions = original_actions;
md.selected = null;
md.render();
}
return result;
};
type IForm = { form: any; error: Record<string, string> }`;
};

View File

@ -38,7 +38,18 @@ export const MDMaster: FC<{ md: MDLocal; mdr: MDRef }> = ({ md, mdr }) => {
return (
<>
{md.props.show_head === "only-master" && <MDHeader md={md} mdr={mdr} />}
<div
className={cx(
css`
> div {
flex: 1;
}
`,
"c-flex c-flex-1"
)}
>
<PassProp md={md}>{mdr.master}</PassProp>
</div>
</>
);
};

View File

@ -19,7 +19,6 @@ export const editorMDInit = (md: MDLocal, mdr: MDRef, arg: MDProps) => {
md.props.gen_table = gen_table;
md.props.on_init = on_init;
console.log(get(mdr, "master.edit.childs.0"));
if (!mdr.master || (mdr.master && !get(mdr, "master.edit.childs.0.childs.length"))) {
md.breadcrumb = [
{

View File

@ -34,13 +34,18 @@ export const ExportExcel = lazify(
/** Filter */
export const MasterFilter = lazify(
async () => (await import("@/comps/filter/Filter")).MasterFilter
async () => (await import("@/comps/filter/MasterFilter")).MasterFilter
);
export const FilterField = lazify(
async () => (await import("@/comps/filter/FilterField")).FilterField
);
/** Generator */
export { generateMasterDetail } from "lib/comps/md/gen/md-gen";
/** ETC */
export { filterWhere } from "@/comps/filter/utils/filter-where";
export {
FMLocal,
FieldTypeCustom,
@ -54,7 +59,8 @@ export { TableListType } from "lib/comps/list/utils/typings";
export { Button, FloatButton } from "@/comps/ui/button";
export { prasi_gen } from "@/gen/prasi_gen";
export { password } from "@/utils/password";
export {generateTableList} from "@/comps/md/gen/md-table-list"
export { generateTableList } from "@/comps/md/gen/gen-table-list";
export { generateForm } from "@/comps/md/gen/gen-form";
/** Session */
export {

View File

@ -10,11 +10,13 @@ export const parseGenField = (fields: PropOptRaw) => {
if (typeof f === "string") {
try {
const field = JSON.parse(f);
field["value"] = f
result.push(field);
} catch (e) {}
} else {
const field = JSON.parse(f.value);
field.relation.fields = parseGenField(f.checked);
field["value"] = f
result.push(field);
}
}
@ -46,6 +48,7 @@ export type GFCol = {
to: { table: string; fields: string[] };
fields: GFCol[];
};
value: Array<string> | string;
};
export const formatName = (name: string) => {

View File

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

View File

@ -2,6 +2,8 @@ export const prasi_user = window as unknown as {
user: {
id: string;
username: string;
name: string;
fullname: string;
m_role: {
id: string;
name: string;

View File

@ -62,12 +62,12 @@ export const Layout: FC<LYTChild> = (props) => {
fn();
const path = getPathname();
const no_layout = props.exception;
loadSession("/auth/login");
if (Array.isArray(no_layout))
if (no_layout.length) {
if (no_layout.includes(path)) {
return <>{props.defaultLayout}</>;
}
}
loadSession("/dev/auth");
return <>{props.children}</>;
};

View File

@ -22,13 +22,13 @@ export const FormatValue: FC<{
return {
...JSON.parse(e.value),
checked: e.checked.map((ex: any) => {
if(typeof ex === "string"){
return JSON.parse(e.value)
if (typeof ex === "string") {
return JSON.parse(e.value);
}
try{
try {
return JSON.parse(ex["value"]);
}catch(em){
return null
} catch (em) {
return null;
}
}),
};
@ -42,19 +42,20 @@ export const FormatValue: FC<{
if (typeof value === "object" && value) {
const rel = fields?.find((e) => e.name === name);
if (rel && rel.checked) {
const result = rel.checked
.filter((e) => !e.is_pk)
.map((e) => {
return value[e.name];
})
.join(" - ");
if (rel.type === "has-one") {
const result = [];
for (const [k,v] of Object.entries(value) as any ) {
if (!k.toLowerCase().includes('id')) result.push(v);
}
return result.join(' - ');
}
if (Array.isArray(value)) {
const len = value.length;
if (len === 0) return ` - `;
return `${len} item${len > 1 ? "s" : ""}`;
}
return result;
}
return JSON.stringify(value);

33
utils/parse-gen-fields.ts Executable file
View File

@ -0,0 +1,33 @@
import { GFCol } from "lib/gen/utils";
export const parseGenFields = (gen_fields: any) => {
const fields_map = new Map<string, (GFCol & { checked?: GFCol[] })[]>();
if (gen_fields) {
const gf = JSON.stringify(gen_fields);
if (!fields_map.has(gf)) {
fields_map.set(
gf,
gen_fields.map((e: any) => {
if (typeof e === "string") {
return JSON.parse(e);
} else {
return {
...JSON.parse(e.value),
checked: e.checked.map((ex: any) => {
if (typeof ex === "string") {
return JSON.parse(e.value);
}
try {
return JSON.parse(ex["value"]);
} catch (em) {
return null;
}
}),
};
}
})
);
}
}
return fields_map;
};