Compare commits
No commits in common. "f56494bca5da507b747faeef54aa061bad838458" and "874c9f8fa1bc259d6dc27b884df8035ee1335bd7" have entirely different histories.
f56494bca5
...
874c9f8fa1
|
|
@ -13,7 +13,7 @@ export const FilterContent: FC<{
|
||||||
child: any;
|
child: any;
|
||||||
_item: PrasiItem;
|
_item: PrasiItem;
|
||||||
}> = ({ mode, filter, PassProp, child, _item, onSubmit }) => {
|
}> = ({ mode, filter, PassProp, child, _item, onSubmit }) => {
|
||||||
filter.PassProp = PassProp;
|
const internal = useLocal({});
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cx(
|
className={cx(
|
||||||
|
|
@ -94,6 +94,7 @@ export const FilterContent: FC<{
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
|
/* Styles specific to popup */
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-inner {
|
.form-inner {
|
||||||
|
|
@ -103,100 +104,61 @@ export const FilterContent: FC<{
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<BaseForm
|
<BaseForm
|
||||||
tag="blank"
|
|
||||||
data={filter.data}
|
data={filter.data}
|
||||||
name={filter.name}
|
onSubmit={async (form) => {
|
||||||
onSubmit={async ({ fm }) => {
|
const fm = form.fm;
|
||||||
try {
|
try {
|
||||||
if (typeof fm.data === "object") {
|
if (typeof form.fm?.data === "object") {
|
||||||
fm.render();
|
form.render();
|
||||||
}
|
form.fm.render();
|
||||||
|
|
||||||
if (mode === "raw" && fm) {
|
|
||||||
const submit = async (fm: FMLocal) => {
|
|
||||||
fm.render();
|
|
||||||
if (typeof onSubmit === "function") {
|
|
||||||
const data = await onSubmit(fm);
|
|
||||||
if (typeof fm.data === "object") {
|
|
||||||
fm.data = {
|
|
||||||
__status: "submit",
|
|
||||||
...fm.data,
|
|
||||||
_where: data,
|
|
||||||
};
|
|
||||||
fm.render();
|
|
||||||
filter.data = {
|
|
||||||
__status: "submit",
|
|
||||||
...fm.data,
|
|
||||||
_where: data,
|
|
||||||
};
|
|
||||||
filter.render();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
await submit(fm);
|
|
||||||
}
|
|
||||||
const f = getFilter(filter.name);
|
|
||||||
if (f) {
|
|
||||||
for (const list of Object.values(f.list.ref)) {
|
|
||||||
list.reload();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (ex) {}
|
} catch (ex) {}
|
||||||
return true;
|
|
||||||
}}
|
|
||||||
// onSubmit={async (form) => {
|
|
||||||
// const fm = form.fm;
|
|
||||||
// try {
|
|
||||||
// if (typeof form.fm?.data === "object") {
|
|
||||||
// form.render();
|
|
||||||
// form.fm.render();
|
|
||||||
// }
|
|
||||||
// } catch (ex) {}
|
|
||||||
|
|
||||||
// if (mode === "raw" && fm) {
|
if (mode === "raw" && fm) {
|
||||||
// if (form && form.fm) {
|
if (form && form.fm) {
|
||||||
// Object.keys(form.fm.data).map((e) => {
|
Object.keys(form.fm.data).map((e) => {
|
||||||
// if (!form?.fm.data?.[e]) {
|
if (!form?.fm.data?.[e]) {
|
||||||
// delete form.fm?.data[e];
|
delete form.fm?.data[e];
|
||||||
// }
|
}
|
||||||
// });
|
});
|
||||||
// }
|
}
|
||||||
// const submit = async (fm: FMLocal) => {
|
const submit = async (fm: FMLocal) => {
|
||||||
// fm.render();
|
fm.render();
|
||||||
// if (typeof onSubmit === "function") {
|
if (typeof onSubmit === "function") {
|
||||||
// const data = await onSubmit(fm);
|
const data = await onSubmit(fm);
|
||||||
// if (typeof form.fm?.data === "object") {
|
if (typeof form.fm?.data === "object") {
|
||||||
// form.fm.data = {
|
form.fm.data = {
|
||||||
// __status: "submit",
|
__status: "submit",
|
||||||
// ...form.fm.data,
|
...form.fm.data,
|
||||||
// _where: data,
|
_where: data,
|
||||||
// };
|
};
|
||||||
// form.fm.render();
|
form.fm.render();
|
||||||
// filter.data = {
|
filter.data = {
|
||||||
// __status: "submit",
|
__status: "submit",
|
||||||
// ...form.fm.data,
|
...form.fm.data,
|
||||||
// _where: data,
|
_where: data,
|
||||||
// };
|
};
|
||||||
// filter.render();
|
filter.render();
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// };
|
};
|
||||||
// await submit(fm);
|
await submit(fm);
|
||||||
// }
|
}
|
||||||
// const f = getFilter(filter.name);
|
const f = getFilter(filter.name);
|
||||||
// if (f) {
|
if (f) {
|
||||||
// for (const list of Object.values(f.list.ref)) {
|
for (const list of Object.values(f.list.ref)) {
|
||||||
// list.reload();
|
list.reload();
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }}
|
}}
|
||||||
|
render={internal.render}
|
||||||
>
|
>
|
||||||
{({ fm }) => {
|
{(form) => {
|
||||||
filter.fm = fm;
|
filter.form = form;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!!(PassProp && child) && (
|
{!!(PassProp && child) && (
|
||||||
<PassProp filter={filter} fm={filter.fm}>
|
<PassProp filter={filter} fm={form.fm}>
|
||||||
{child}
|
{child}
|
||||||
</PassProp>
|
</PassProp>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,8 @@ export const FilterField: FC<{
|
||||||
search_timeout: null as any,
|
search_timeout: null as any,
|
||||||
});
|
});
|
||||||
if (!name) return <>No Name</>;
|
if (!name) return <>No Name</>;
|
||||||
if (!filter.fm) return <div>Loading...</div>;
|
if (!filter.form) return <div>Loading...</div>;
|
||||||
|
|
||||||
const fm = filter.fm;
|
|
||||||
filter.types[name] = type;
|
filter.types[name] = type;
|
||||||
|
|
||||||
const singleOptions = ["equal", "not_equal"];
|
const singleOptions = ["equal", "not_equal"];
|
||||||
|
|
@ -33,183 +32,167 @@ export const FilterField: FC<{
|
||||||
internal.render_timeout = setTimeout(() => {
|
internal.render_timeout = setTimeout(() => {
|
||||||
filter_window.prasiContext.render();
|
filter_window.prasiContext.render();
|
||||||
}, 500);
|
}, 500);
|
||||||
}, [fm]);
|
}, [filter.form]);
|
||||||
|
|
||||||
let show_modifier = filter.mode !== "inline";
|
let show_modifier = filter.mode !== "inline";
|
||||||
const arg = {
|
return null;
|
||||||
name: name || "",
|
// return (
|
||||||
fm,
|
// <BaseField
|
||||||
label: label || name || "",
|
// {...filter.form.fieldProps({
|
||||||
render: internal.render,
|
// name: name || "",
|
||||||
prefix: show_modifier
|
// label: label || name || "",
|
||||||
? () => (
|
// render: internal.render,
|
||||||
<FieldModifier
|
// prefix: show_modifier
|
||||||
onChange={(modifier) => {
|
// ? () => (
|
||||||
filter.modifiers[name] = modifier;
|
// <FieldModifier
|
||||||
filter.render();
|
// onChange={(modifier) => {
|
||||||
filter_window.prasiContext.render();
|
// filter.modifiers[name] = modifier;
|
||||||
}}
|
// filter.render();
|
||||||
modifier={filter.modifiers[name]}
|
// filter_window.prasiContext.render();
|
||||||
type={type}
|
// }}
|
||||||
/>
|
// modifier={filter.modifiers[name]}
|
||||||
)
|
// type={type}
|
||||||
: undefined,
|
// />
|
||||||
onLoad() {
|
// )
|
||||||
return [{ label: "halo", value: "asda" }];
|
// : undefined,
|
||||||
},
|
// onLoad() {
|
||||||
subType: singleOptions.includes(filter.modifiers[name])
|
// return [{ label: "halo", value: "asda" }];
|
||||||
? "dropdown"
|
// },
|
||||||
: "typeahead",
|
// subType: singleOptions.includes(filter.modifiers[name])
|
||||||
};
|
// ? "dropdown"
|
||||||
|
// : "typeahead",
|
||||||
|
// })}
|
||||||
|
// >
|
||||||
|
// {(field) => {
|
||||||
|
// if (type === "search-all") {
|
||||||
|
// return (
|
||||||
|
// <div className={cx("search-all c-flex items-center")}>
|
||||||
|
// <div className="c-pl-2">
|
||||||
|
// <svg
|
||||||
|
// xmlns="http://www.w3.org/2000/svg"
|
||||||
|
// width="14"
|
||||||
|
// height="14"
|
||||||
|
// viewBox="0 0 24 24"
|
||||||
|
// fill="none"
|
||||||
|
// stroke="currentColor"
|
||||||
|
// strokeWidth="2"
|
||||||
|
// strokeLinecap="round"
|
||||||
|
// strokeLinejoin="round"
|
||||||
|
// >
|
||||||
|
// <circle cx="11" cy="11" r="8" />
|
||||||
|
// <path d="m21 21-4.3-4.3" />
|
||||||
|
// </svg>
|
||||||
|
// </div>
|
||||||
|
// <input
|
||||||
|
// type="search"
|
||||||
|
// value={field.fm?.data?.[name]}
|
||||||
|
// placeholder={field.field.label}
|
||||||
|
// onBlur={() => {
|
||||||
|
// // clearTimeout(internal.search_timeout);
|
||||||
|
// // filter.form?.submit();
|
||||||
|
// }}
|
||||||
|
// spellCheck={false}
|
||||||
|
// className="c-flex-1 c-transition-all c-bg-transparent c-outline-none c-px-2 c-text-sm c-w-full"
|
||||||
|
// onChange={(e) => {
|
||||||
|
// field.fm.data[name] = e.currentTarget.value;
|
||||||
|
// field.fm.render();
|
||||||
|
// clearTimeout(internal.search_timeout);
|
||||||
|
// internal.search_timeout = setTimeout(() => {
|
||||||
|
// filter.form?.submit();
|
||||||
|
// }, 1500);
|
||||||
|
// }}
|
||||||
|
// />
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
return (
|
// return (
|
||||||
<BaseField
|
// <>
|
||||||
fm={fm}
|
// {type === "text" && (
|
||||||
label={label || name}
|
// <FieldTypeInput
|
||||||
name={name || ""}
|
// {...field}
|
||||||
PassProp={filter.PassProp}
|
// prop={{
|
||||||
>
|
// type: "input",
|
||||||
{(field) => {
|
// sub_type: "text",
|
||||||
if (type === "search-all") {
|
// }}
|
||||||
return (
|
// />
|
||||||
<div className={cx("search-all c-flex items-center")}>
|
// )}
|
||||||
<div className="c-pl-2">
|
// {type === "number" && (
|
||||||
<svg
|
// <>
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
// <FieldTypeInput
|
||||||
width="14"
|
// {...field}
|
||||||
height="14"
|
// field={{
|
||||||
viewBox="0 0 24 24"
|
// ...field.field,
|
||||||
fill="none"
|
// name:
|
||||||
stroke="currentColor"
|
// filter.modifiers[name] === "between"
|
||||||
strokeWidth="2"
|
// ? name
|
||||||
strokeLinecap="round"
|
// : `${name}_from`,
|
||||||
strokeLinejoin="round"
|
// }}
|
||||||
>
|
// prop={{
|
||||||
<circle cx="11" cy="11" r="8" />
|
// type: "input",
|
||||||
<path d="m21 21-4.3-4.3" />
|
// sub_type: "number",
|
||||||
</svg>
|
// }}
|
||||||
</div>
|
// />
|
||||||
<input
|
// {filter.modifiers[name] === "between" && (
|
||||||
type="search"
|
// <FieldTypeInput
|
||||||
value={field.fm?.data?.[name]}
|
// {...field}
|
||||||
placeholder={field.field.label}
|
// field={{ ...field.field, name: `${name}_to` }}
|
||||||
onBlur={() => {
|
// prop={{
|
||||||
// clearTimeout(internal.search_timeout);
|
// type: "input",
|
||||||
// filter.form?.submit();
|
// sub_type: "number",
|
||||||
}}
|
// }}
|
||||||
spellCheck={false}
|
// />
|
||||||
className="c-flex-1 c-transition-all c-bg-transparent c-outline-none c-px-2 c-text-sm c-w-full"
|
// )}
|
||||||
onChange={(e) => {
|
// </>
|
||||||
field.fm.data[name] = e.currentTarget.value;
|
// )}
|
||||||
field.fm.render();
|
// {type === "date" && (
|
||||||
clearTimeout(internal.search_timeout);
|
// <>
|
||||||
if (!field.fm.data[name]) {
|
// <FieldTypeInput
|
||||||
fm.submit();
|
// {...field}
|
||||||
} else {
|
// field={{
|
||||||
internal.search_timeout = setTimeout(() => {
|
// ...field.field,
|
||||||
fm?.submit();
|
// name:
|
||||||
}, 1500);
|
// filter.modifiers[name] === "between"
|
||||||
}
|
// ? name
|
||||||
}}
|
// : `${name}_from`,
|
||||||
onKeyDown={(e) => {
|
// }}
|
||||||
if (e.key === "Enter") {
|
// prop={{
|
||||||
clearTimeout(internal.search_timeout);
|
// type: "input",
|
||||||
fm?.submit();
|
// sub_type: "date",
|
||||||
}
|
// }}
|
||||||
}}
|
// />
|
||||||
/>
|
// {filter.modifiers[name] === "between" && (
|
||||||
</div>
|
// <FieldTypeInput
|
||||||
);
|
// {...field}
|
||||||
}
|
// field={{ ...field.field, name: `${name}_to` }}
|
||||||
|
// prop={{
|
||||||
return (
|
// type: "input",
|
||||||
<>
|
// sub_type: "date",
|
||||||
{type === "text" && (
|
// }}
|
||||||
<FieldTypeInput
|
// />
|
||||||
{...field}
|
// )}
|
||||||
arg={arg}
|
// </>
|
||||||
prop={{
|
// )}
|
||||||
type: "input",
|
// {type === "boolean" && (
|
||||||
sub_type: "text",
|
// <FieldCheckbox
|
||||||
}}
|
// arg={field.arg}
|
||||||
/>
|
// field={field.field}
|
||||||
)}
|
// fm={field.fm}
|
||||||
{type === "number" && (
|
// />
|
||||||
<>
|
// )}
|
||||||
<FieldTypeInput
|
// {type === "options" && (
|
||||||
arg={arg}
|
// <>
|
||||||
{...field}
|
// {singleOptions.includes(filter.modifiers[name]) && (
|
||||||
field={{
|
// <SingleOption {...field} />
|
||||||
...field.field,
|
// )}
|
||||||
name:
|
// {multiOptions.includes(filter.modifiers[name]) && (
|
||||||
filter.modifiers[name] === "between"
|
// <MultiOption {...field} />
|
||||||
? name
|
// )}
|
||||||
: `${name}_from`,
|
// </>
|
||||||
}}
|
// )}
|
||||||
prop={{
|
// </>
|
||||||
type: "input",
|
// );
|
||||||
sub_type: "number",
|
// }}
|
||||||
}}
|
// </BaseField>
|
||||||
/>
|
// );
|
||||||
{filter.modifiers[name] === "between" && (
|
|
||||||
<FieldTypeInput
|
|
||||||
arg={arg}
|
|
||||||
{...field}
|
|
||||||
field={{ ...field.field, name: `${name}_to` }}
|
|
||||||
prop={{
|
|
||||||
type: "input",
|
|
||||||
sub_type: "number",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{type === "date" && (
|
|
||||||
<>
|
|
||||||
<FieldTypeInput
|
|
||||||
arg={arg}
|
|
||||||
{...field}
|
|
||||||
field={{
|
|
||||||
...field.field,
|
|
||||||
name:
|
|
||||||
filter.modifiers[name] === "between"
|
|
||||||
? name
|
|
||||||
: `${name}_from`,
|
|
||||||
}}
|
|
||||||
prop={{
|
|
||||||
type: "input",
|
|
||||||
sub_type: "date",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{filter.modifiers[name] === "between" && (
|
|
||||||
<FieldTypeInput
|
|
||||||
arg={arg}
|
|
||||||
{...field}
|
|
||||||
field={{ ...field.field, name: `${name}_to` }}
|
|
||||||
prop={{
|
|
||||||
type: "input",
|
|
||||||
sub_type: "date",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{type === "boolean" && (
|
|
||||||
<FieldCheckbox arg={arg} field={field.field} fm={field.fm} />
|
|
||||||
)}
|
|
||||||
{type === "options" && (
|
|
||||||
<>
|
|
||||||
{singleOptions.includes(filter.modifiers[name]) && (
|
|
||||||
<SingleOption arg={arg} {...field} />
|
|
||||||
)}
|
|
||||||
{multiOptions.includes(filter.modifiers[name]) && (
|
|
||||||
<MultiOption arg={arg} {...field} />
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</BaseField>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@ export const MasterFilter: FC<FilterProps> = ({
|
||||||
}
|
}
|
||||||
filter.raw_status = "ready";
|
filter.raw_status = "ready";
|
||||||
filter.render();
|
filter.render();
|
||||||
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
filter.raw_status = "ready";
|
filter.raw_status = "ready";
|
||||||
|
|
@ -94,7 +95,6 @@ export const MasterFilter: FC<FilterProps> = ({
|
||||||
if (mode === "raw" && filter.raw_status !== "ready") {
|
if (mode === "raw" && filter.raw_status !== "ready") {
|
||||||
return <FieldLoading />;
|
return <FieldLoading />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FilterContent
|
<FilterContent
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { FMLocal, GenField } from "../../form/typings";
|
import { BaseFormLocal } from "../../form/base/types";
|
||||||
|
import { GenField } from "../../form/typings";
|
||||||
|
|
||||||
export type FilterFieldType =
|
export type FilterFieldType =
|
||||||
| "search-all"
|
| "search-all"
|
||||||
|
|
@ -13,8 +14,7 @@ export const default_filter_local = {
|
||||||
columns: [] as string[],
|
columns: [] as string[],
|
||||||
fields: [] as GenField[],
|
fields: [] as GenField[],
|
||||||
tableName: "",
|
tableName: "",
|
||||||
fm: null as null | FMLocal,
|
form: null as null | BaseFormLocal<any>,
|
||||||
PassProp: null as null | any,
|
|
||||||
modifiers: {} as Record<string, string>,
|
modifiers: {} as Record<string, string>,
|
||||||
types: {} as Record<string, FilterFieldType>,
|
types: {} as Record<string, FilterFieldType>,
|
||||||
name: "",
|
name: "",
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,6 @@ export const BaseForm = <T extends Record<string, any>>({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (local.fm && local.fm.data !== data) {
|
if (local.fm && local.fm.data !== data) {
|
||||||
for (const k of Object.keys(local.fm.data)) {
|
for (const k of Object.keys(local.fm.data)) {
|
||||||
|
|
@ -54,9 +53,6 @@ export const BaseForm = <T extends Record<string, any>>({
|
||||||
}
|
}
|
||||||
local.fm.render();
|
local.fm.render();
|
||||||
}
|
}
|
||||||
return () => {
|
|
||||||
delete (local as any).fm;
|
|
||||||
};
|
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
const fm = local.fm;
|
const fm = local.fm;
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,11 @@ export const TypeDropdown: FC<{
|
||||||
if (Array.isArray(res)) {
|
if (Array.isArray(res)) {
|
||||||
const list: any = res.map((e: any, idx: number) => {
|
const list: any = res.map((e: any, idx: number) => {
|
||||||
return {
|
return {
|
||||||
label: arg.opt_get_label?.(e, "list", {
|
label: arg.opt_get_label(e, "list", {
|
||||||
prev: res[idx - 1],
|
prev: res[idx - 1],
|
||||||
next: res[idx + 1],
|
next: res[idx + 1],
|
||||||
}),
|
}),
|
||||||
tag: arg.opt_get_label?.(e, "label"),
|
tag: arg.opt_get_label(e, "label"),
|
||||||
value: e.value,
|
value: e.value,
|
||||||
data: e.data,
|
data: e.data,
|
||||||
};
|
};
|
||||||
|
|
@ -48,7 +48,7 @@ export const TypeDropdown: FC<{
|
||||||
let f = list.find((ex: any) => ex.value === v);
|
let f = list.find((ex: any) => ex.value === v);
|
||||||
|
|
||||||
if (!f) {
|
if (!f) {
|
||||||
arg.opt_set_value?.({
|
arg.opt_set_value({
|
||||||
fm,
|
fm,
|
||||||
name: field.name,
|
name: field.name,
|
||||||
type: field.type,
|
type: field.type,
|
||||||
|
|
@ -75,7 +75,7 @@ export const TypeDropdown: FC<{
|
||||||
|
|
||||||
if (field.type === "single-option") {
|
if (field.type === "single-option") {
|
||||||
if (!value && local.options.length > 0) {
|
if (!value && local.options.length > 0) {
|
||||||
arg.opt_set_value?.({
|
arg.opt_set_value({
|
||||||
fm,
|
fm,
|
||||||
name: field.name,
|
name: field.name,
|
||||||
type: field.type,
|
type: field.type,
|
||||||
|
|
@ -83,7 +83,7 @@ export const TypeDropdown: FC<{
|
||||||
selected: [local.options[0]?.value],
|
selected: [local.options[0]?.value],
|
||||||
});
|
});
|
||||||
} else if (value) {
|
} else if (value) {
|
||||||
arg.opt_set_value?.({
|
arg.opt_set_value({
|
||||||
fm,
|
fm,
|
||||||
name: field.name,
|
name: field.name,
|
||||||
type: field.type,
|
type: field.type,
|
||||||
|
|
@ -191,7 +191,7 @@ export const TypeDropdown: FC<{
|
||||||
popupClassName={popupClassName}
|
popupClassName={popupClassName}
|
||||||
onSelect={({ search, item }) => {
|
onSelect={({ search, item }) => {
|
||||||
if (item) {
|
if (item) {
|
||||||
arg.opt_set_value?.({
|
arg.opt_set_value({
|
||||||
fm,
|
fm,
|
||||||
name: field.name,
|
name: field.name,
|
||||||
type: field.type,
|
type: field.type,
|
||||||
|
|
@ -226,7 +226,7 @@ export const TypeDropdown: FC<{
|
||||||
note="dropdown"
|
note="dropdown"
|
||||||
popupClassName={popupClassName}
|
popupClassName={popupClassName}
|
||||||
onChange={(values) => {
|
onChange={(values) => {
|
||||||
arg.opt_set_value?.({
|
arg.opt_set_value({
|
||||||
fm,
|
fm,
|
||||||
name: field.name,
|
name: field.name,
|
||||||
type: field.type,
|
type: field.type,
|
||||||
|
|
|
||||||
|
|
@ -104,8 +104,6 @@ export const FieldTypeInput: FC<{
|
||||||
input.render();
|
input.render();
|
||||||
if (prop.onChange) {
|
if (prop.onChange) {
|
||||||
prop.onChange(fm.data[field.name]);
|
prop.onChange(fm.data[field.name]);
|
||||||
} else {
|
|
||||||
arg.on_change?.({ value: fm.data[field.name], name: field.name, fm });
|
|
||||||
}
|
}
|
||||||
clearTimeout(input.change_timeout);
|
clearTimeout(input.change_timeout);
|
||||||
input.change_timeout = setTimeout(fm.render, 300);
|
input.change_timeout = setTimeout(fm.render, 300);
|
||||||
|
|
@ -234,8 +232,6 @@ export const FieldTypeInput: FC<{
|
||||||
mask="____-____-_______"
|
mask="____-____-_______"
|
||||||
replacement={{ _: /\d/ }}
|
replacement={{ _: /\d/ }}
|
||||||
onChange={(ev) => {
|
onChange={(ev) => {
|
||||||
console.log("onchange");
|
|
||||||
|
|
||||||
fm.data[field.name] = ev.currentTarget.value.replace(/\D/g, "");
|
fm.data[field.name] = ev.currentTarget.value.replace(/\D/g, "");
|
||||||
renderOnChange();
|
renderOnChange();
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,16 @@
|
||||||
import ExcelJS from "exceljs";
|
import { useLocal } from "lib/utils/use-local";
|
||||||
import { FC, MouseEvent } from "react";
|
import { FC, MouseEvent } from "react";
|
||||||
|
// import ExcelJS from "exceljs";
|
||||||
|
|
||||||
export const ExportExcel: FC<{
|
export const ExportExcel: FC<{
|
||||||
data: any[];
|
data: any[];
|
||||||
fileName?: string;
|
fileName?: string;
|
||||||
children?: any;
|
}> = ({ data, fileName = "exported_data.xlsx" }): JSX.Element => {
|
||||||
className?: string;
|
const local = useLocal({
|
||||||
}> = ({
|
data: [] as any[],
|
||||||
data,
|
});
|
||||||
fileName = "exported_data.xlsx",
|
local.data = data;
|
||||||
children,
|
local.render();
|
||||||
className,
|
|
||||||
}): JSX.Element => {
|
|
||||||
const getAllKeys = (arr: Array<Record<string, any>>): string[] => {
|
const getAllKeys = (arr: Array<Record<string, any>>): string[] => {
|
||||||
const keysSet = new Set<string>();
|
const keysSet = new Set<string>();
|
||||||
|
|
||||||
|
|
@ -21,30 +20,57 @@ export const ExportExcel: FC<{
|
||||||
|
|
||||||
return Array.from(keysSet);
|
return Array.from(keysSet);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// const handleExport = async (e: MouseEvent<HTMLButtonElement>) => {
|
||||||
|
// try {
|
||||||
|
// const workbook = new ExcelJS.Workbook();
|
||||||
|
// const worksheet = workbook.addWorksheet("Sheet 1");
|
||||||
|
|
||||||
|
// const columns = getAllKeys(local.data);
|
||||||
|
// worksheet.addRow(columns);
|
||||||
|
|
||||||
|
// local.data.forEach((row) => {
|
||||||
|
// const values = columns.map((col) => row[col]);
|
||||||
|
// worksheet.addRow(values);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const buffer = await workbook.xlsx.writeBuffer();
|
||||||
|
|
||||||
|
// const blob = new Blob([buffer], {
|
||||||
|
// type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||||
|
// });
|
||||||
|
|
||||||
|
// // FileSaver.saveAs(blob, fileName);
|
||||||
|
|
||||||
|
// console.log("Data exported");
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error("Error exporting data:", error);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
const handleExport = async (e: MouseEvent<HTMLButtonElement>) => {
|
const handleExport = async (e: MouseEvent<HTMLButtonElement>) => {
|
||||||
try {
|
try {
|
||||||
const workbook = new ExcelJS.Workbook();
|
// const workbook = new ExcelJS.Workbook();
|
||||||
const worksheet = workbook.addWorksheet("Sheet 1");
|
// const worksheet = workbook.addWorksheet("Sheet 1");
|
||||||
|
|
||||||
const columns = getAllKeys(data);
|
// const columns = getAllKeys(local.data);
|
||||||
worksheet.addRow(columns);
|
// worksheet.addRow(columns);
|
||||||
|
|
||||||
data.forEach((row) => {
|
// local.data.forEach((row) => {
|
||||||
const values = columns.map((col) => row[col]);
|
// const values = columns.map((col) => row[col]);
|
||||||
worksheet.addRow(values);
|
// worksheet.addRow(values);
|
||||||
});
|
// });
|
||||||
|
|
||||||
const buffer = await workbook.xlsx.writeBuffer();
|
// const buffer = await workbook.xlsx.writeBuffer();
|
||||||
|
|
||||||
const blob = new Blob([buffer], {
|
// const blob = new Blob([buffer], {
|
||||||
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
// type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||||
});
|
// });
|
||||||
|
|
||||||
const a = document.createElement("a");
|
// const a = document.createElement("a");
|
||||||
a.href = window.URL.createObjectURL(blob);
|
// a.href = window.URL.createObjectURL(blob);
|
||||||
a.download = fileName;
|
// a.download = "my-exported-data.xlsx";
|
||||||
a.click();
|
// a.click();
|
||||||
|
|
||||||
console.log("Data exported");
|
console.log("Data exported");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -53,8 +79,10 @@ export const ExportExcel: FC<{
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button onClick={handleExport} className={className}>
|
<div>
|
||||||
{children || "Export"}
|
<button onClick={handleExport} style={{ background: "#00ffff" }}>
|
||||||
</button>
|
Export
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,8 @@
|
||||||
import { parseGenField } from "lib/gen/utils";
|
import { GFCol, parseGenField } from "lib/gen/utils";
|
||||||
|
import { cn } from "lib/utils";
|
||||||
|
import { fields_map } from "lib/utils/format-value";
|
||||||
|
import { call_prasi_events } from "lib/utils/prasi-events";
|
||||||
|
import { set } from "lib/utils/set";
|
||||||
import { useLocal } from "lib/utils/use-local";
|
import { useLocal } from "lib/utils/use-local";
|
||||||
import get from "lodash.get";
|
import get from "lodash.get";
|
||||||
import {
|
import {
|
||||||
|
|
@ -8,20 +12,77 @@ import {
|
||||||
Loader2,
|
Loader2,
|
||||||
Sticker,
|
Sticker,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { ChangeEvent, FC, MouseEvent, useEffect } from "react";
|
import {
|
||||||
import DataGrid, { ColumnOrColumnGroup, Row } from "react-data-grid";
|
ChangeEvent,
|
||||||
|
FC,
|
||||||
|
MouseEvent,
|
||||||
|
ReactElement,
|
||||||
|
ReactNode,
|
||||||
|
useEffect,
|
||||||
|
} from "react";
|
||||||
|
import DataGrid, {
|
||||||
|
ColumnOrColumnGroup,
|
||||||
|
RenderCellProps,
|
||||||
|
Row,
|
||||||
|
SortColumn,
|
||||||
|
} from "react-data-grid";
|
||||||
import "react-data-grid/lib/styles.css";
|
import "react-data-grid/lib/styles.css";
|
||||||
import { createPortal } from "react-dom";
|
import { createPortal } from "react-dom";
|
||||||
|
import { filterWhere } from "../filter/parser/filter-where";
|
||||||
import { getFilter } from "../filter/utils/get-filter";
|
import { getFilter } from "../filter/utils/get-filter";
|
||||||
|
import { MDLocal } from "../md/utils/typings";
|
||||||
import { Skeleton } from "../ui/skeleton";
|
import { Skeleton } from "../ui/skeleton";
|
||||||
import { toast, Toaster } from "../ui/toast";
|
import { toast, Toaster } from "../ui/toast";
|
||||||
import { TableListProp, useTableListLocal } from "./TableListLocal";
|
|
||||||
import { TLList } from "./TLList";
|
import { TLList } from "./TLList";
|
||||||
import { TLSlider } from "./TLSlider";
|
import { TLSlider } from "./TLSlider";
|
||||||
import { sortTree } from "./utils/sort-tree";
|
import { sortTree } from "./utils/sort-tree";
|
||||||
|
import { OnRowClick } from "./utils/type";
|
||||||
|
|
||||||
let EMPTY_SET = new Set() as ReadonlySet<any>;
|
let EMPTY_SET = new Set() as ReadonlySet<any>;
|
||||||
|
|
||||||
|
type SelectedRow = (arg: {
|
||||||
|
row: any;
|
||||||
|
rows: any[];
|
||||||
|
idx: any;
|
||||||
|
select?: boolean;
|
||||||
|
data?: any[];
|
||||||
|
}) => boolean;
|
||||||
|
type TableListProp = {
|
||||||
|
child: any;
|
||||||
|
PassProp: any;
|
||||||
|
list: { type: string; item_w: string };
|
||||||
|
name: string;
|
||||||
|
value?: any[];
|
||||||
|
on_load?: (arg: {
|
||||||
|
reload: () => Promise<void>;
|
||||||
|
orderBy?: Record<string, "asc" | "desc" | Record<string, "asc" | "desc">>;
|
||||||
|
paging: { take: number; skip: number };
|
||||||
|
mode: "count" | "query";
|
||||||
|
}) => Promise<any[]>;
|
||||||
|
on_init: (arg?: any) => any;
|
||||||
|
mode: "table" | "list" | "grid" | "auto";
|
||||||
|
_item: PrasiItem;
|
||||||
|
__props?: any;
|
||||||
|
gen_fields: string[];
|
||||||
|
row_click: OnRowClick;
|
||||||
|
selected: SelectedRow;
|
||||||
|
show_header?: boolean;
|
||||||
|
id_parent?: string;
|
||||||
|
feature?: Array<any>;
|
||||||
|
filter_name: string;
|
||||||
|
render_row?: (child: any, data: any) => ReactNode;
|
||||||
|
row_height?: number | ((row: any) => number);
|
||||||
|
render_col?: (arg: {
|
||||||
|
props: RenderCellProps<any, unknown>;
|
||||||
|
tbl: any;
|
||||||
|
child: any;
|
||||||
|
}) => ReactNode;
|
||||||
|
gen_table?: string;
|
||||||
|
softdel_type?: string;
|
||||||
|
paging?: boolean;
|
||||||
|
cache_row?: boolean;
|
||||||
|
md?: MDLocal;
|
||||||
|
};
|
||||||
const w = window as any;
|
const w = window as any;
|
||||||
const selectCellClassname = css`
|
const selectCellClassname = css`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -33,31 +94,30 @@ const selectCellClassname = css`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const TableList: FC<TableListProp> = (props) => {
|
export const TableList: FC<TableListProp> = ({
|
||||||
const {
|
name,
|
||||||
name,
|
on_load,
|
||||||
on_load,
|
child,
|
||||||
child,
|
PassProp,
|
||||||
PassProp,
|
mode: _mode,
|
||||||
mode: _mode,
|
on_init,
|
||||||
on_init,
|
_item,
|
||||||
_item,
|
gen_fields,
|
||||||
gen_fields,
|
row_click,
|
||||||
row_click,
|
selected,
|
||||||
selected,
|
id_parent,
|
||||||
id_parent,
|
feature,
|
||||||
feature,
|
filter_name,
|
||||||
filter_name,
|
row_height: rowHeight,
|
||||||
row_height: rowHeight,
|
render_col,
|
||||||
render_col,
|
show_header,
|
||||||
show_header,
|
list,
|
||||||
list,
|
value,
|
||||||
value,
|
paging,
|
||||||
paging,
|
cache_row,
|
||||||
cache_row,
|
__props,
|
||||||
__props,
|
md,
|
||||||
md,
|
}) => {
|
||||||
} = props;
|
|
||||||
let mode = _mode;
|
let mode = _mode;
|
||||||
if (mode === "auto") {
|
if (mode === "auto") {
|
||||||
if (w.isMobile) {
|
if (w.isMobile) {
|
||||||
|
|
@ -67,7 +127,286 @@ export const TableList: FC<TableListProp> = (props) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const local = useTableListLocal(props);
|
let ls_sort = localStorage.getItem(
|
||||||
|
`sort-${location.pathname}-${location.hash}-${name}`
|
||||||
|
) as unknown as { columns: any; orderBy: any };
|
||||||
|
if (ls_sort) {
|
||||||
|
ls_sort = JSON.parse(ls_sort as any);
|
||||||
|
}
|
||||||
|
const local = useLocal(
|
||||||
|
{
|
||||||
|
times: 0,
|
||||||
|
selectedRows: [] as {
|
||||||
|
pk: string | number;
|
||||||
|
rows: any;
|
||||||
|
}[],
|
||||||
|
el: null as null | HTMLDivElement,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
selectedAllRows: false as boolean,
|
||||||
|
selectedRowIds: [] as (string | number)[],
|
||||||
|
pk: null as null | GFCol,
|
||||||
|
scrolled: false,
|
||||||
|
data: [] as any[],
|
||||||
|
status: "init" as
|
||||||
|
| "loading"
|
||||||
|
| "ready"
|
||||||
|
| "resizing"
|
||||||
|
| "reload"
|
||||||
|
| "init"
|
||||||
|
| "error",
|
||||||
|
where: null as any,
|
||||||
|
firstKey: "",
|
||||||
|
should_toast: true,
|
||||||
|
paging: {
|
||||||
|
take: 100,
|
||||||
|
skip: 0,
|
||||||
|
timeout: null as any,
|
||||||
|
total: 0,
|
||||||
|
last_length: 0,
|
||||||
|
scroll: (currentTarget: HTMLDivElement) => {
|
||||||
|
if (
|
||||||
|
isEditor ||
|
||||||
|
local.data.length < local.paging.take ||
|
||||||
|
local.data.length === 0 ||
|
||||||
|
local.status !== "ready" ||
|
||||||
|
!isAtBottom(currentTarget) ||
|
||||||
|
local.reloading
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (local.paging.last_length <= local.data.length) {
|
||||||
|
local.paging.skip = local.data.length;
|
||||||
|
local.reload();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
grid_ref: null as null | HTMLDivElement,
|
||||||
|
collapsed: new Set<number>(),
|
||||||
|
cached_row: new WeakMap<any, ReactElement>(),
|
||||||
|
filtering: "" as ReactNode | string | true,
|
||||||
|
reloading: null as any,
|
||||||
|
reload: (arg?: { toast: boolean }) => {
|
||||||
|
if (local.reloading) return local.reloading;
|
||||||
|
|
||||||
|
local.reloading = new Promise<void>(async (done) => {
|
||||||
|
let should_toast = true;
|
||||||
|
if (arg?.toast === false) should_toast = false;
|
||||||
|
local.should_toast = should_toast;
|
||||||
|
|
||||||
|
local.filtering = "";
|
||||||
|
if (typeof on_load === "function") {
|
||||||
|
local.status = "loading";
|
||||||
|
local.render();
|
||||||
|
|
||||||
|
const orderBy = local.sort.orderBy || undefined;
|
||||||
|
const where = filterWhere(filter_name, __props);
|
||||||
|
|
||||||
|
if (where?.OR?.length > 0) {
|
||||||
|
const key = Object.keys(where.OR[0])[0];
|
||||||
|
if (key && where.OR[0][key]) {
|
||||||
|
let filtering = where.OR[0][key].contains;
|
||||||
|
if (typeof local.filtering === "string" && filtering) {
|
||||||
|
filtering = filtering.slice(1, -1);
|
||||||
|
} else {
|
||||||
|
filtering = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filtering) {
|
||||||
|
local.filtering = (
|
||||||
|
<div className="c-pt-2">
|
||||||
|
Searching for: <pre>"{filtering.trim()}"</pre>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (md) {
|
||||||
|
await new Promise<void>((resolve) => {
|
||||||
|
const ival = setInterval(() => {
|
||||||
|
if (!md.header.loading) {
|
||||||
|
clearInterval(ival);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
}, 10);
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
Array.isArray(md?.params?.links) &&
|
||||||
|
md?.params?.links?.length
|
||||||
|
) {
|
||||||
|
const last = md.params.links[md.params.links.length - 1];
|
||||||
|
|
||||||
|
if (last && last.where) {
|
||||||
|
if ((last.name && last.name === md.name) || !last.name) {
|
||||||
|
for (const [k, v] of Object.entries(last.where)) {
|
||||||
|
where[k] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
call_prasi_events("tablelist", "where", [
|
||||||
|
__props?.gen__table,
|
||||||
|
where,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const load_args: any = {
|
||||||
|
async reload() {},
|
||||||
|
orderBy,
|
||||||
|
where,
|
||||||
|
paging: {
|
||||||
|
take: local.paging.take > 0 ? local.paging.take : undefined,
|
||||||
|
skip: local.paging.skip,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (id_parent) {
|
||||||
|
load_args.paging = {};
|
||||||
|
}
|
||||||
|
const result = on_load({ ...load_args, mode: "query" });
|
||||||
|
const callback = (data: any[]) => {
|
||||||
|
if (
|
||||||
|
id_parent ||
|
||||||
|
!local.paging ||
|
||||||
|
(local.paging && !local.paging.take) ||
|
||||||
|
local.paging.skip === 0
|
||||||
|
) {
|
||||||
|
local.data = data;
|
||||||
|
} else {
|
||||||
|
local.data = [...local.data, ...data];
|
||||||
|
}
|
||||||
|
|
||||||
|
local.paging.last_length = local.data.length;
|
||||||
|
|
||||||
|
local.status = "ready";
|
||||||
|
local.reloading = null;
|
||||||
|
local.render();
|
||||||
|
|
||||||
|
done();
|
||||||
|
setTimeout(() => {
|
||||||
|
if (
|
||||||
|
local.grid_ref &&
|
||||||
|
!id_parent &&
|
||||||
|
(paging !== undefined || paging)
|
||||||
|
) {
|
||||||
|
local.paging.scroll(local.grid_ref);
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (result instanceof Promise) {
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
callback(await result);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
local.status = "error";
|
||||||
|
toast.dismiss();
|
||||||
|
toast.error(
|
||||||
|
<div className="c-flex c-text-red-600 c-items-center">
|
||||||
|
<AlertTriangle className="c-h-4 c-w-4 c-mr-1" />
|
||||||
|
Failed to load data
|
||||||
|
</div>,
|
||||||
|
{
|
||||||
|
dismissible: true,
|
||||||
|
className: css`
|
||||||
|
background: #ffecec;
|
||||||
|
border: 2px solid red;
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
} else callback(result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return local.reloading;
|
||||||
|
},
|
||||||
|
sort: {
|
||||||
|
columns: (ls_sort?.columns || []) as SortColumn[],
|
||||||
|
on_change: (cols: SortColumn[]) => {
|
||||||
|
if (feature?.find((e) => e === "sorting")) {
|
||||||
|
local.sort.columns = cols;
|
||||||
|
local.paging.skip = 0;
|
||||||
|
if (cols.length > 0) {
|
||||||
|
let { columnKey, direction } = cols[0];
|
||||||
|
|
||||||
|
if (columnKey.includes(".")) {
|
||||||
|
let root: any = {};
|
||||||
|
set(root, columnKey, direction === "ASC" ? "asc" : "desc");
|
||||||
|
local.sort.orderBy = root;
|
||||||
|
} else {
|
||||||
|
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 (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) {
|
||||||
|
local.sort.orderBy = {
|
||||||
|
[columnKey]: {
|
||||||
|
[field.name]: direction === "ASC" ? "asc" : "desc",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else if (rel.relation) {
|
||||||
|
local.sort.orderBy = {
|
||||||
|
[columnKey]: {
|
||||||
|
[rel.relation.to.fields[0]]:
|
||||||
|
direction === "ASC" ? "asc" : "desc",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (should_set) {
|
||||||
|
local.sort.orderBy = {
|
||||||
|
[columnKey]: direction === "ASC" ? "asc" : "desc",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
local.sort.orderBy = null;
|
||||||
|
}
|
||||||
|
localStorage.setItem(
|
||||||
|
`sort-${location.pathname}-${location.hash}-${name}`,
|
||||||
|
JSON.stringify({
|
||||||
|
columns: local.sort.columns,
|
||||||
|
orderBy: local.sort.orderBy,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
local.status = "reload";
|
||||||
|
local.render();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
orderBy: (ls_sort?.orderBy || null) as null | Record<
|
||||||
|
string,
|
||||||
|
"asc" | "desc" | Record<string, "asc" | "desc">
|
||||||
|
>,
|
||||||
|
},
|
||||||
|
soft_delete: {
|
||||||
|
field: null as any,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
({ setDelayedRender }) => {
|
||||||
|
setDelayedRender(true);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const reload = local.reload;
|
const reload = local.reload;
|
||||||
if (md) {
|
if (md) {
|
||||||
|
|
@ -449,9 +788,11 @@ export const TableList: FC<TableListProp> = (props) => {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={cx(css`
|
className={cx(
|
||||||
width: 16px;
|
css`
|
||||||
`)}
|
width: 16px;
|
||||||
|
`
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{props.row?.__children?.length > 0 && (
|
{props.row?.__children?.length > 0 && (
|
||||||
<>
|
<>
|
||||||
|
|
@ -832,6 +1173,13 @@ const dataGridStyle = (local: { el: null | HTMLDivElement }) => {
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function isAtBottom(currentTarget: HTMLDivElement): boolean {
|
||||||
|
return (
|
||||||
|
currentTarget.scrollTop + 10 >=
|
||||||
|
currentTarget.scrollHeight - currentTarget.clientHeight
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function getProp(child: any, name: string, defaultValue?: any) {
|
function getProp(child: any, name: string, defaultValue?: any) {
|
||||||
const fn = new Function(
|
const fn = new Function(
|
||||||
`return ${get(child, `component.props.${name}.valueBuilt`) || `null`}`
|
`return ${get(child, `component.props.${name}.valueBuilt`) || `null`}`
|
||||||
|
|
|
||||||
|
|
@ -1,374 +0,0 @@
|
||||||
import { GFCol } from "lib/gen/utils";
|
|
||||||
import { fields_map } from "lib/utils/format-value";
|
|
||||||
import { call_prasi_events } from "lib/utils/prasi-events";
|
|
||||||
import { set } from "lib/utils/set";
|
|
||||||
import { useLocal } from "lib/utils/use-local";
|
|
||||||
import { AlertTriangle } from "lucide-react";
|
|
||||||
import { ReactElement, ReactNode } from "react";
|
|
||||||
import { RenderCellProps, SortColumn } from "react-data-grid";
|
|
||||||
import "react-data-grid/lib/styles.css";
|
|
||||||
import { filterWhere } from "../filter/parser/filter-where";
|
|
||||||
import { MDLocal } from "../md/utils/typings";
|
|
||||||
import { toast } from "../ui/toast";
|
|
||||||
import { OnRowClick } from "./utils/type";
|
|
||||||
|
|
||||||
type SelectedRow = (arg: {
|
|
||||||
row: any;
|
|
||||||
rows: any[];
|
|
||||||
idx: any;
|
|
||||||
select?: boolean;
|
|
||||||
data?: any[];
|
|
||||||
}) => boolean;
|
|
||||||
|
|
||||||
export type TableListProp = {
|
|
||||||
child: any;
|
|
||||||
PassProp: any;
|
|
||||||
list: { type: string; item_w: string };
|
|
||||||
name: string;
|
|
||||||
value?: any[];
|
|
||||||
on_load?: (arg: {
|
|
||||||
reload: () => Promise<void>;
|
|
||||||
orderBy?: Record<string, "asc" | "desc" | Record<string, "asc" | "desc">>;
|
|
||||||
paging: { take: number; skip: number };
|
|
||||||
mode: "count" | "query";
|
|
||||||
}) => Promise<any[]>;
|
|
||||||
on_init: (arg?: any) => any;
|
|
||||||
mode: "table" | "list" | "grid" | "auto";
|
|
||||||
_item: PrasiItem;
|
|
||||||
__props?: any;
|
|
||||||
gen_fields: string[];
|
|
||||||
row_click: OnRowClick;
|
|
||||||
selected: SelectedRow;
|
|
||||||
show_header?: boolean;
|
|
||||||
id_parent?: string;
|
|
||||||
feature?: Array<any>;
|
|
||||||
filter_name: string;
|
|
||||||
render_row?: (child: any, data: any) => ReactNode;
|
|
||||||
row_height?: number | ((row: any) => number);
|
|
||||||
render_col?: (arg: {
|
|
||||||
props: RenderCellProps<any, unknown>;
|
|
||||||
tbl: any;
|
|
||||||
child: any;
|
|
||||||
}) => ReactNode;
|
|
||||||
gen_table?: string;
|
|
||||||
softdel_type?: string;
|
|
||||||
paging?: boolean;
|
|
||||||
cache_row?: boolean;
|
|
||||||
md?: MDLocal;
|
|
||||||
};
|
|
||||||
export type TableListLocal = ReturnType<typeof useTableListLocal>;
|
|
||||||
export const useTableListLocal = ({
|
|
||||||
name,
|
|
||||||
on_load,
|
|
||||||
child,
|
|
||||||
PassProp,
|
|
||||||
mode: _mode,
|
|
||||||
on_init,
|
|
||||||
_item,
|
|
||||||
gen_fields,
|
|
||||||
row_click,
|
|
||||||
selected,
|
|
||||||
id_parent,
|
|
||||||
feature,
|
|
||||||
filter_name,
|
|
||||||
row_height: rowHeight,
|
|
||||||
render_col,
|
|
||||||
show_header,
|
|
||||||
list,
|
|
||||||
value,
|
|
||||||
paging,
|
|
||||||
cache_row,
|
|
||||||
__props,
|
|
||||||
md,
|
|
||||||
}: TableListProp) => {
|
|
||||||
let ls_sort = localStorage.getItem(
|
|
||||||
`sort-${location.pathname}-${location.hash}-${name}`
|
|
||||||
) as unknown as { columns: any; orderBy: any };
|
|
||||||
if (ls_sort) {
|
|
||||||
ls_sort = JSON.parse(ls_sort as any);
|
|
||||||
}
|
|
||||||
|
|
||||||
const local = useLocal(
|
|
||||||
{
|
|
||||||
times: 0,
|
|
||||||
selectedRows: [] as {
|
|
||||||
pk: string | number;
|
|
||||||
rows: any;
|
|
||||||
}[],
|
|
||||||
el: null as null | HTMLDivElement,
|
|
||||||
width: 0,
|
|
||||||
height: 0,
|
|
||||||
selectedAllRows: false as boolean,
|
|
||||||
selectedRowIds: [] as (string | number)[],
|
|
||||||
pk: null as null | GFCol,
|
|
||||||
scrolled: false,
|
|
||||||
data: [] as any[],
|
|
||||||
status: "init" as
|
|
||||||
| "loading"
|
|
||||||
| "ready"
|
|
||||||
| "resizing"
|
|
||||||
| "reload"
|
|
||||||
| "init"
|
|
||||||
| "error",
|
|
||||||
where: null as any,
|
|
||||||
firstKey: "",
|
|
||||||
should_toast: true,
|
|
||||||
paging: {
|
|
||||||
take: 100,
|
|
||||||
skip: 0,
|
|
||||||
timeout: null as any,
|
|
||||||
total: 0,
|
|
||||||
last_length: 0,
|
|
||||||
scroll: (currentTarget: HTMLDivElement) => {
|
|
||||||
if (
|
|
||||||
isEditor ||
|
|
||||||
local.data.length < local.paging.take ||
|
|
||||||
local.data.length === 0 ||
|
|
||||||
local.status !== "ready" ||
|
|
||||||
!isAtBottom(currentTarget) ||
|
|
||||||
local.reloading
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (local.paging.last_length <= local.data.length) {
|
|
||||||
local.paging.skip = local.data.length;
|
|
||||||
local.reload();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
grid_ref: null as null | HTMLDivElement,
|
|
||||||
collapsed: new Set<number>(),
|
|
||||||
cached_row: new WeakMap<any, ReactElement>(),
|
|
||||||
filtering: "" as ReactNode | string | true,
|
|
||||||
reloading: null as any,
|
|
||||||
reload: (arg?: { toast: boolean }) => {
|
|
||||||
if (local.reloading) return local.reloading;
|
|
||||||
|
|
||||||
local.reloading = new Promise<void>(async (done) => {
|
|
||||||
let should_toast = true;
|
|
||||||
if (arg?.toast === false) should_toast = false;
|
|
||||||
local.should_toast = should_toast;
|
|
||||||
|
|
||||||
local.filtering = "";
|
|
||||||
if (typeof on_load === "function") {
|
|
||||||
local.status = "loading";
|
|
||||||
local.render();
|
|
||||||
|
|
||||||
const orderBy = local.sort.orderBy || undefined;
|
|
||||||
const where = filterWhere(filter_name, __props);
|
|
||||||
|
|
||||||
if (where?.OR?.length > 0) {
|
|
||||||
const key = Object.keys(where.OR[0])[0];
|
|
||||||
if (key && where.OR[0][key]) {
|
|
||||||
let filtering = where.OR[0][key].contains;
|
|
||||||
if (typeof local.filtering === "string" && filtering) {
|
|
||||||
filtering = filtering.slice(1, -1);
|
|
||||||
} else {
|
|
||||||
filtering = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filtering) {
|
|
||||||
local.filtering = (
|
|
||||||
<div className="c-pt-2">
|
|
||||||
Searching for: <pre>"{filtering.trim()}"</pre>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (md) {
|
|
||||||
await new Promise<void>((resolve) => {
|
|
||||||
const ival = setInterval(() => {
|
|
||||||
if (!md.header.loading) {
|
|
||||||
clearInterval(ival);
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
}, 10);
|
|
||||||
});
|
|
||||||
if (
|
|
||||||
Array.isArray(md?.params?.links) &&
|
|
||||||
md?.params?.links?.length
|
|
||||||
) {
|
|
||||||
const last = md.params.links[md.params.links.length - 1];
|
|
||||||
|
|
||||||
if (last && last.where) {
|
|
||||||
if ((last.name && last.name === md.name) || !last.name) {
|
|
||||||
for (const [k, v] of Object.entries(last.where)) {
|
|
||||||
where[k] = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
call_prasi_events("tablelist", "where", [
|
|
||||||
__props?.gen__table,
|
|
||||||
where,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const load_args: any = {
|
|
||||||
async reload() {},
|
|
||||||
orderBy,
|
|
||||||
where,
|
|
||||||
paging: {
|
|
||||||
take: local.paging.take > 0 ? local.paging.take : undefined,
|
|
||||||
skip: local.paging.skip,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (id_parent) {
|
|
||||||
load_args.paging = {};
|
|
||||||
}
|
|
||||||
const result = on_load({ ...load_args, mode: "query" });
|
|
||||||
const callback = (data: any[]) => {
|
|
||||||
if (
|
|
||||||
id_parent ||
|
|
||||||
!local.paging ||
|
|
||||||
(local.paging && !local.paging.take) ||
|
|
||||||
local.paging.skip === 0
|
|
||||||
) {
|
|
||||||
local.data = data;
|
|
||||||
} else {
|
|
||||||
local.data = [...local.data, ...data];
|
|
||||||
}
|
|
||||||
|
|
||||||
local.paging.last_length = local.data.length;
|
|
||||||
|
|
||||||
local.status = "ready";
|
|
||||||
local.reloading = null;
|
|
||||||
local.render();
|
|
||||||
|
|
||||||
done();
|
|
||||||
setTimeout(() => {
|
|
||||||
if (
|
|
||||||
local.grid_ref &&
|
|
||||||
!id_parent &&
|
|
||||||
(paging !== undefined || paging)
|
|
||||||
) {
|
|
||||||
local.paging.scroll(local.grid_ref);
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (result instanceof Promise) {
|
|
||||||
(async () => {
|
|
||||||
try {
|
|
||||||
callback(await result);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
local.status = "error";
|
|
||||||
toast.dismiss();
|
|
||||||
toast.error(
|
|
||||||
<div className="c-flex c-text-red-600 c-items-center">
|
|
||||||
<AlertTriangle className="c-h-4 c-w-4 c-mr-1" />
|
|
||||||
Failed to load data
|
|
||||||
</div>,
|
|
||||||
{
|
|
||||||
dismissible: true,
|
|
||||||
className: css`
|
|
||||||
background: #ffecec;
|
|
||||||
border: 2px solid red;
|
|
||||||
`,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
} else callback(result);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return local.reloading;
|
|
||||||
},
|
|
||||||
sort: {
|
|
||||||
columns: (ls_sort?.columns || []) as SortColumn[],
|
|
||||||
on_change: (cols: SortColumn[]) => {
|
|
||||||
if (feature?.find((e) => e === "sorting")) {
|
|
||||||
local.sort.columns = cols;
|
|
||||||
local.paging.skip = 0;
|
|
||||||
if (cols.length > 0) {
|
|
||||||
let { columnKey, direction } = cols[0];
|
|
||||||
|
|
||||||
if (columnKey.includes(".")) {
|
|
||||||
let root: any = {};
|
|
||||||
set(root, columnKey, direction === "ASC" ? "asc" : "desc");
|
|
||||||
local.sort.orderBy = root;
|
|
||||||
} else {
|
|
||||||
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 (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) {
|
|
||||||
local.sort.orderBy = {
|
|
||||||
[columnKey]: {
|
|
||||||
[field.name]: direction === "ASC" ? "asc" : "desc",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
} else if (rel.relation) {
|
|
||||||
local.sort.orderBy = {
|
|
||||||
[columnKey]: {
|
|
||||||
[rel.relation.to.fields[0]]:
|
|
||||||
direction === "ASC" ? "asc" : "desc",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (should_set) {
|
|
||||||
local.sort.orderBy = {
|
|
||||||
[columnKey]: direction === "ASC" ? "asc" : "desc",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
local.sort.orderBy = null;
|
|
||||||
}
|
|
||||||
localStorage.setItem(
|
|
||||||
`sort-${location.pathname}-${location.hash}-${name}`,
|
|
||||||
JSON.stringify({
|
|
||||||
columns: local.sort.columns,
|
|
||||||
orderBy: local.sort.orderBy,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
local.status = "reload";
|
|
||||||
local.render();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
orderBy: (ls_sort?.orderBy || null) as null | Record<
|
|
||||||
string,
|
|
||||||
"asc" | "desc" | Record<string, "asc" | "desc">
|
|
||||||
>,
|
|
||||||
},
|
|
||||||
soft_delete: {
|
|
||||||
field: null as any,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
({ setDelayedRender }) => {
|
|
||||||
setDelayedRender(true);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return local;
|
|
||||||
};
|
|
||||||
|
|
||||||
function isAtBottom(currentTarget: HTMLDivElement): boolean {
|
|
||||||
return (
|
|
||||||
currentTarget.scrollTop + 10 >=
|
|
||||||
currentTarget.scrollHeight - currentTarget.clientHeight
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -33,6 +33,10 @@ export const MDRenderMaster: FC<{
|
||||||
size: width,
|
size: width,
|
||||||
min_size: min_width,
|
min_size: min_width,
|
||||||
});
|
});
|
||||||
|
// if (md.panel) {
|
||||||
|
// md.panel.min_size = min_width;
|
||||||
|
// md.panel.size = width;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}, Object.values(md.deps || {}) || []);
|
}, Object.values(md.deps || {}) || []);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
import { BreadItem } from "lib/comps/custom/Breadcrumb";
|
import { BreadItem } from "lib/comps/custom/Breadcrumb";
|
||||||
import { LinkParam } from "lib/comps/form/field/type/TypeLink";
|
|
||||||
import { FMLocal } from "lib/comps/form/typings";
|
import { FMLocal } from "lib/comps/form/typings";
|
||||||
import { TableListLocal } from "lib/comps/list/TableListLocal";
|
|
||||||
import { GFCol } from "lib/gen/utils";
|
import { GFCol } from "lib/gen/utils";
|
||||||
|
import { LinkParam } from "lib/comps/form/field/type/TypeLink";
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
type ID_MASTER_DETAIL = string;
|
type ID_MASTER_DETAIL = string;
|
||||||
|
|
@ -59,7 +58,7 @@ export type MDLocalInternal = {
|
||||||
master: {
|
master: {
|
||||||
reload: (arg?: { toast: boolean }) => void;
|
reload: (arg?: { toast: boolean }) => void;
|
||||||
render: () => void;
|
render: () => void;
|
||||||
list?: TableListLocal;
|
list?: any;
|
||||||
pk?: string;
|
pk?: string;
|
||||||
};
|
};
|
||||||
params: {
|
params: {
|
||||||
|
|
@ -69,7 +68,6 @@ export type MDLocalInternal = {
|
||||||
parse: () => void;
|
parse: () => void;
|
||||||
apply: () => void;
|
apply: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
pk?: GFCol;
|
pk?: GFCol;
|
||||||
props: {
|
props: {
|
||||||
mode: "full" | "h-split" | "v-split";
|
mode: "full" | "h-split" | "v-split";
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ export const Typeahead: FC<{
|
||||||
onChange,
|
onChange,
|
||||||
className,
|
className,
|
||||||
popupClassName,
|
popupClassName,
|
||||||
disabledSearch,
|
disabledSearch
|
||||||
}) => {
|
}) => {
|
||||||
const local = useLocal({
|
const local = useLocal({
|
||||||
value: [] as string[],
|
value: [] as string[],
|
||||||
|
|
@ -309,23 +309,21 @@ export const Typeahead: FC<{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const valueLabel = local.value
|
const valueLabel = local.value?.map((value) => {
|
||||||
?.map((value) => {
|
if (local.mode === "single") {
|
||||||
if (local.mode === "single") {
|
const item = options.find((item) => item.value === value);
|
||||||
const item = options.find((item) => item.value === value);
|
|
||||||
|
|
||||||
if (!local.open && !allow_new) {
|
if (!local.open && !allow_new) {
|
||||||
local.select = item || null;
|
local.select = item || null;
|
||||||
|
|
||||||
local.search.input = item?.tag || item?.label || "";
|
local.search.input = item?.tag || item?.label || "";
|
||||||
}
|
|
||||||
return item;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const item = local.options.find((e) => e.value === value);
|
|
||||||
return item;
|
return item;
|
||||||
})
|
}
|
||||||
.filter((e) => e);
|
|
||||||
|
const item = local.options.find((e) => e.value === value);
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
|
||||||
let inputval = local.search.input;
|
let inputval = local.search.input;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,7 @@
|
||||||
"@radix-ui/react-alert-dialog": "^1.1.1",
|
"@radix-ui/react-alert-dialog": "^1.1.1",
|
||||||
"@radix-ui/react-dialog": "^1.1.1",
|
"@radix-ui/react-dialog": "^1.1.1",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
||||||
"input-otp": "^1.4.1",
|
|
||||||
"@radix-ui/react-label": "^2.0.2",
|
"@radix-ui/react-label": "^2.0.2",
|
||||||
"input-otp": "^1.4.1",
|
|
||||||
"@radix-ui/react-navigation-menu": "^1.2.0",
|
"@radix-ui/react-navigation-menu": "^1.2.0",
|
||||||
"@radix-ui/react-popover": "^1.0.7",
|
"@radix-ui/react-popover": "^1.0.7",
|
||||||
"@radix-ui/react-progress": "^1.1.0",
|
"@radix-ui/react-progress": "^1.1.0",
|
||||||
|
|
@ -25,8 +23,10 @@
|
||||||
"@types/autosize": "^4.0.3",
|
"@types/autosize": "^4.0.3",
|
||||||
"@types/lodash.capitalize": "^4.2.9",
|
"@types/lodash.capitalize": "^4.2.9",
|
||||||
"@types/lodash.get": "^4.4.9",
|
"@types/lodash.get": "^4.4.9",
|
||||||
|
"@types/react": "^18.2.65",
|
||||||
"drizzle-orm": "^0.33.0",
|
"drizzle-orm": "^0.33.0",
|
||||||
"bun-types": "^1.1.24",
|
"bun-types": "^1.1.24",
|
||||||
|
"@types/react-dom": "^18.3.0",
|
||||||
"rou3": "^0.5.1",
|
"rou3": "^0.5.1",
|
||||||
"@types/uuid": "^9.0.8",
|
"@types/uuid": "^9.0.8",
|
||||||
"@wojtekmaj/react-qr-svg": "^1.0.0",
|
"@wojtekmaj/react-qr-svg": "^1.0.0",
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { FieldLoading } from "lib/comps/ui/field-loading";
|
|
||||||
import { getBasename } from "lib/utils/pathname";
|
|
||||||
import { useLocal } from "lib/utils/use-local";
|
import { useLocal } from "lib/utils/use-local";
|
||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
|
import { FieldLoading } from "../../..";
|
||||||
import { loadSession } from "./utils/load";
|
import { loadSession } from "./utils/load";
|
||||||
|
import { getBasename } from "lib/utils/pathname";
|
||||||
|
|
||||||
const w = window as unknown as {
|
const w = window as unknown as {
|
||||||
user: any;
|
user: any;
|
||||||
|
|
|
||||||
|
|
@ -70,15 +70,7 @@ export const useServerRouter = <T extends ReturnType<typeof newServerRouter>>(
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
async handle(
|
async handle(arg: ServerContext | SessionContext<any>) {
|
||||||
arg: ServerContext | SessionContext<any>,
|
|
||||||
opt?: {
|
|
||||||
rewrite?: (arg: {
|
|
||||||
body: Bun.BodyInit;
|
|
||||||
headers?: Record<string, string>;
|
|
||||||
}) => Bun.BodyInit;
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
const { url, req, handle } = arg;
|
const { url, req, handle } = arg;
|
||||||
const found = findRoute(rou, undefined, url.pathname);
|
const found = findRoute(rou, undefined, url.pathname);
|
||||||
if (found) {
|
if (found) {
|
||||||
|
|
@ -109,7 +101,7 @@ export const useServerRouter = <T extends ReturnType<typeof newServerRouter>>(
|
||||||
|
|
||||||
return new Response(JSON.stringify(result));
|
return new Response(JSON.stringify(result));
|
||||||
}
|
}
|
||||||
return handle(req, opt);
|
return handle(req);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,14 @@
|
||||||
|
/// <reference types="bun-types" />
|
||||||
|
|
||||||
import { ServerWebSocket } from "bun";
|
import { ServerWebSocket } from "bun";
|
||||||
import { useServerRouter } from "../server/server-route";
|
import { useServerRouter } from "../server/server-route";
|
||||||
import { newSessionStore } from "./store/session-store";
|
import { newSessionStore } from "./store/session-store";
|
||||||
import { ServerContext } from "./type";
|
import { ServerContext } from "./type";
|
||||||
|
|
||||||
type WS = ServerWebSocket<{ url: string }>;
|
type WS = ServerWebSocket<{ url: string }>;
|
||||||
export type SessionServerHandler = {
|
type SessionServerHandler = {
|
||||||
cleanup: () => Promise<void>;
|
cleanup: () => Promise<void>;
|
||||||
handle: (
|
handle: (arg: ServerContext) => Promise<Response>;
|
||||||
arg: ServerContext,
|
|
||||||
opt?: {
|
|
||||||
rewrite?: (arg: {
|
|
||||||
body: Bun.BodyInit;
|
|
||||||
headers?: Record<string, string>;
|
|
||||||
}) => Bun.BodyInit;
|
|
||||||
}
|
|
||||||
) => Promise<Response>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const initSessionServer = <T>(
|
export const initSessionServer = <T>(
|
||||||
|
|
@ -30,7 +24,7 @@ export const initSessionServer = <T>(
|
||||||
const server_handler: SessionServerHandler = {
|
const server_handler: SessionServerHandler = {
|
||||||
async cleanup() {},
|
async cleanup() {},
|
||||||
|
|
||||||
async handle(server_arg, opt) {
|
async handle(server_arg) {
|
||||||
const { req, handle, url } = server_arg;
|
const { req, handle, url } = server_arg;
|
||||||
|
|
||||||
const route_arg = {
|
const route_arg = {
|
||||||
|
|
@ -42,16 +36,16 @@ export const initSessionServer = <T>(
|
||||||
};
|
};
|
||||||
|
|
||||||
if (url.pathname.startsWith("/_session/")) {
|
if (url.pathname.startsWith("/_session/")) {
|
||||||
const res = await session_router.handle(route_arg, opt);
|
const res = await session_router.handle(route_arg);
|
||||||
if (res) return res;
|
if (res) return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arg.router) {
|
if (arg.router) {
|
||||||
const res = await arg.router.handle(route_arg, opt);
|
const res = await arg.router.handle(route_arg);
|
||||||
if (res) return res;
|
if (res) return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
return handle(req, opt);
|
return handle(req);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,15 +71,7 @@ export interface SessionContext<T> extends ServerContext {
|
||||||
export type ServerContext = {
|
export type ServerContext = {
|
||||||
req: Request;
|
req: Request;
|
||||||
server: Server;
|
server: Server;
|
||||||
handle: (
|
handle: (req: Request) => Promise<Response>;
|
||||||
req: Request,
|
|
||||||
opt?: {
|
|
||||||
rewrite?: (arg: {
|
|
||||||
body: Bun.BodyInit;
|
|
||||||
headers?: Record<string, string>;
|
|
||||||
}) => Bun.BodyInit;
|
|
||||||
}
|
|
||||||
) => Promise<Response>;
|
|
||||||
mode: "dev" | "prod";
|
mode: "dev" | "prod";
|
||||||
url: {
|
url: {
|
||||||
raw: URL;
|
raw: URL;
|
||||||
|
|
@ -87,16 +79,8 @@ export type ServerContext = {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SessionAuth = (
|
export type SessionAuth = {
|
||||||
| {
|
method: "user-pass";
|
||||||
method: "user-pass";
|
username: string;
|
||||||
username: string;
|
password: string;
|
||||||
password: string;
|
} & Record<string, any>;
|
||||||
}
|
|
||||||
| {
|
|
||||||
method: "user-otp";
|
|
||||||
uid: string;
|
|
||||||
otp: string;
|
|
||||||
}
|
|
||||||
) &
|
|
||||||
Record<string, any>;
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { type ClassValue, clsx } from "clsx"
|
||||||
|
import { twMerge } from "tailwind-merge"
|
||||||
|
|
||||||
|
export function cn(...inputs: ClassValue[]) {
|
||||||
|
return twMerge(clsx(inputs))
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,6 @@ export const baseurl = (url: string) => {
|
||||||
location.host === "localhost:4550"
|
location.host === "localhost:4550"
|
||||||
) {
|
) {
|
||||||
const id_site = location.pathname.split("/")[2];
|
const id_site = location.pathname.split("/")[2];
|
||||||
if (url.startsWith(`/prod/${id_site}`)) return url;
|
|
||||||
|
|
||||||
if (url.startsWith("/")) return `/prod/${id_site}${url}`;
|
if (url.startsWith("/")) return `/prod/${id_site}${url}`;
|
||||||
else return `/prod/${id_site}/${url}`;
|
else return `/prod/${id_site}/${url}`;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { FC, lazy } from "react";
|
import { FC, ReactNode, Suspense, lazy } from "react";
|
||||||
|
|
||||||
export const lazify = <T extends FC<any>>(fn: () => Promise<T>): T => {
|
export const lazify = <T extends FC<any>>(fn: () => Promise<T>): T => {
|
||||||
return lazy(async () => {
|
return lazy(async () => {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { FieldLocal, FMLocal } from "lib/comps/form/typings";
|
import { FieldLocal } from "lib/comps/form/typings";
|
||||||
import { MDLocal } from "lib/comps/md/utils/typings";
|
import { MDLocal } from "lib/comps/md/utils/typings";
|
||||||
|
import { FMLocal } from "../..";
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
import { Prisma } from "../../typings/prisma";
|
import { Prisma } from "../../typings/prisma";
|
||||||
import { set } from "./set";
|
import { set } from "./set";
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue