fix
This commit is contained in:
parent
ec73db3925
commit
59495ee186
|
|
@ -43,6 +43,7 @@ export const BaseForm = <T extends Record<string, any>>({
|
|||
},
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (local.fm && local.fm.data !== data) {
|
||||
for (const k of Object.keys(local.fm.data)) {
|
||||
|
|
@ -53,6 +54,9 @@ export const BaseForm = <T extends Record<string, any>>({
|
|||
}
|
||||
local.fm.render();
|
||||
}
|
||||
return () => {
|
||||
delete (local as any).fm;
|
||||
};
|
||||
}, [data]);
|
||||
|
||||
const fm = local.fm;
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
import { useLocal } from "lib/utils/use-local";
|
||||
import ExcelJS from "exceljs";
|
||||
import { FC, MouseEvent } from "react";
|
||||
// import ExcelJS from "exceljs";
|
||||
|
||||
export const ExportExcel: FC<{
|
||||
data: any[];
|
||||
fileName?: string;
|
||||
}> = ({ data, fileName = "exported_data.xlsx" }): JSX.Element => {
|
||||
const local = useLocal({
|
||||
data: [] as any[],
|
||||
});
|
||||
local.data = data;
|
||||
local.render();
|
||||
children?: any;
|
||||
className?: string;
|
||||
}> = ({
|
||||
data,
|
||||
fileName = "exported_data.xlsx",
|
||||
children,
|
||||
className,
|
||||
}): JSX.Element => {
|
||||
const getAllKeys = (arr: Array<Record<string, any>>): string[] => {
|
||||
const keysSet = new Set<string>();
|
||||
|
||||
|
|
@ -21,56 +22,30 @@ export const ExportExcel: FC<{
|
|||
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>) => {
|
||||
try {
|
||||
// const workbook = new ExcelJS.Workbook();
|
||||
// const worksheet = workbook.addWorksheet("Sheet 1");
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
const worksheet = workbook.addWorksheet("Sheet 1");
|
||||
|
||||
// const columns = getAllKeys(local.data);
|
||||
// worksheet.addRow(columns);
|
||||
console.log(data);
|
||||
const columns = getAllKeys(data);
|
||||
worksheet.addRow(columns);
|
||||
|
||||
// local.data.forEach((row) => {
|
||||
// const values = columns.map((col) => row[col]);
|
||||
// worksheet.addRow(values);
|
||||
// });
|
||||
data.forEach((row) => {
|
||||
const values = columns.map((col) => row[col]);
|
||||
worksheet.addRow(values);
|
||||
});
|
||||
|
||||
// const buffer = await workbook.xlsx.writeBuffer();
|
||||
const buffer = await workbook.xlsx.writeBuffer();
|
||||
|
||||
// const blob = new Blob([buffer], {
|
||||
// type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
// });
|
||||
const blob = new Blob([buffer], {
|
||||
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
});
|
||||
|
||||
// const a = document.createElement("a");
|
||||
// a.href = window.URL.createObjectURL(blob);
|
||||
// a.download = "my-exported-data.xlsx";
|
||||
// a.click();
|
||||
const a = document.createElement("a");
|
||||
a.href = window.URL.createObjectURL(blob);
|
||||
a.download = fileName;
|
||||
a.click();
|
||||
|
||||
console.log("Data exported");
|
||||
} catch (error) {
|
||||
|
|
@ -79,10 +54,8 @@ export const ExportExcel: FC<{
|
|||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button onClick={handleExport} style={{ background: "#00ffff" }}>
|
||||
Export
|
||||
<button onClick={handleExport} className={className}>
|
||||
{children || "Export"}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
import { GFCol, parseGenField } 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 { parseGenField } from "lib/gen/utils";
|
||||
import { useLocal } from "lib/utils/use-local";
|
||||
import get from "lodash.get";
|
||||
import {
|
||||
|
|
@ -11,77 +8,20 @@ import {
|
|||
Loader2,
|
||||
Sticker,
|
||||
} from "lucide-react";
|
||||
import {
|
||||
ChangeEvent,
|
||||
FC,
|
||||
MouseEvent,
|
||||
ReactElement,
|
||||
ReactNode,
|
||||
useEffect,
|
||||
} from "react";
|
||||
import DataGrid, {
|
||||
ColumnOrColumnGroup,
|
||||
RenderCellProps,
|
||||
Row,
|
||||
SortColumn,
|
||||
} from "react-data-grid";
|
||||
import { ChangeEvent, FC, MouseEvent, useEffect } from "react";
|
||||
import DataGrid, { ColumnOrColumnGroup, Row } from "react-data-grid";
|
||||
import "react-data-grid/lib/styles.css";
|
||||
import { createPortal } from "react-dom";
|
||||
import { filterWhere } from "../filter/parser/filter-where";
|
||||
import { getFilter } from "../filter/utils/get-filter";
|
||||
import { MDLocal } from "../md/utils/typings";
|
||||
import { Skeleton } from "../ui/skeleton";
|
||||
import { toast, Toaster } from "../ui/toast";
|
||||
import { TableListProp, useTableListLocal } from "./TableListLocal";
|
||||
import { TLList } from "./TLList";
|
||||
import { TLSlider } from "./TLSlider";
|
||||
import { sortTree } from "./utils/sort-tree";
|
||||
import { OnRowClick } from "./utils/type";
|
||||
|
||||
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 selectCellClassname = css`
|
||||
display: flex;
|
||||
|
|
@ -93,7 +33,8 @@ const selectCellClassname = css`
|
|||
}
|
||||
`;
|
||||
|
||||
export const TableList: FC<TableListProp> = ({
|
||||
export const TableList: FC<TableListProp> = (props) => {
|
||||
const {
|
||||
name,
|
||||
on_load,
|
||||
child,
|
||||
|
|
@ -116,7 +57,7 @@ export const TableList: FC<TableListProp> = ({
|
|||
cache_row,
|
||||
__props,
|
||||
md,
|
||||
}) => {
|
||||
} = props;
|
||||
let mode = _mode;
|
||||
if (mode === "auto") {
|
||||
if (w.isMobile) {
|
||||
|
|
@ -126,286 +67,7 @@ export const TableList: FC<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);
|
||||
}
|
||||
);
|
||||
const local = useTableListLocal(props);
|
||||
|
||||
const reload = local.reload;
|
||||
if (md) {
|
||||
|
|
@ -787,11 +449,9 @@ export const TableList: FC<TableListProp> = ({
|
|||
}}
|
||||
>
|
||||
<div
|
||||
className={cx(
|
||||
css`
|
||||
className={cx(css`
|
||||
width: 16px;
|
||||
`
|
||||
)}
|
||||
`)}
|
||||
>
|
||||
{props.row?.__children?.length > 0 && (
|
||||
<>
|
||||
|
|
@ -1172,13 +832,6 @@ 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) {
|
||||
const fn = new Function(
|
||||
`return ${get(child, `component.props.${name}.valueBuilt`) || `null`}`
|
||||
|
|
|
|||
|
|
@ -0,0 +1,374 @@
|
|||
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,10 +33,6 @@ export const MDRenderMaster: FC<{
|
|||
size: width,
|
||||
min_size: min_width,
|
||||
});
|
||||
// if (md.panel) {
|
||||
// md.panel.min_size = min_width;
|
||||
// md.panel.size = width;
|
||||
// }
|
||||
}
|
||||
}, Object.values(md.deps || {}) || []);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { BreadItem } from "lib/comps/custom/Breadcrumb";
|
||||
import { FMLocal } from "lib/comps/form/typings";
|
||||
import { GFCol } from "lib/gen/utils";
|
||||
import { LinkParam } from "lib/comps/form/field/type/TypeLink";
|
||||
import { FMLocal } from "lib/comps/form/typings";
|
||||
import { TableListLocal } from "lib/comps/list/TableListLocal";
|
||||
import { GFCol } from "lib/gen/utils";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
type ID_MASTER_DETAIL = string;
|
||||
|
|
@ -58,7 +59,7 @@ export type MDLocalInternal = {
|
|||
master: {
|
||||
reload: (arg?: { toast: boolean }) => void;
|
||||
render: () => void;
|
||||
list?: any;
|
||||
list?: TableListLocal;
|
||||
pk?: string;
|
||||
};
|
||||
params: {
|
||||
|
|
@ -68,6 +69,7 @@ export type MDLocalInternal = {
|
|||
parse: () => void;
|
||||
apply: () => void;
|
||||
};
|
||||
|
||||
pk?: GFCol;
|
||||
props: {
|
||||
mode: "full" | "h-split" | "v-split";
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
||||
"input-otp": "^1.4.1",
|
||||
"@radix-ui/react-label": "^2.0.2",
|
||||
"input-otp": "^1.4.1",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.0",
|
||||
"@radix-ui/react-popover": "^1.0.7",
|
||||
"@radix-ui/react-progress": "^1.1.0",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ export const baseurl = (url: string) => {
|
|||
location.host === "localhost:4550"
|
||||
) {
|
||||
const id_site = location.pathname.split("/")[2];
|
||||
if (url.startsWith(`/prod/${id_site}`)) return url;
|
||||
|
||||
if (url.startsWith("/")) return `/prod/${id_site}${url}`;
|
||||
else return `/prod/${id_site}/${url}`;
|
||||
|
|
|
|||
Loading…
Reference in New Issue