fix category

This commit is contained in:
rizky 2024-08-12 04:34:44 -07:00
parent 362cfdff27
commit ad86c97c9a
9 changed files with 227 additions and 164 deletions

View File

@ -13,8 +13,8 @@ export const FilterField: FC<{
name?: string;
label?: string;
type: FilterFieldType;
modifiers?: any[]
}> = ({ filter, name, label, type,modifiers }) => {
modifiers?: any[];
}> = ({ filter, name, label, type, modifiers }) => {
const internal = useLocal({ render_timeout: null as any });
if (!name) return <>No Name</>;
if (!filter.form) return <div>Loading...</div>;
@ -85,7 +85,7 @@ export const FilterField: FC<{
prop={{
type: "input",
sub_type: "search",
placeholder: "Search...",
placeholder: field.field.label,
onBlur(e) {
filter.form?.submit();
},

View File

@ -71,6 +71,8 @@ export const Field: FC<FieldProp> = (arg) => {
{...props}
className={cx(
"field",
field.type,
sub_type,
"c-flex c-relative",
editorClassName,
field.type === "single-option" && sub_type === "checkbox"

View File

@ -290,3 +290,47 @@ const getFileName = (url: string) => {
const extension = fileName.substring(dotIndex + 1);
return { name, extension, fullname };
};
export const ImgThumb = ({
className,
url,
w,
h,
}: {
className?: string;
url: string;
w: number;
h: number;
}) => {
const local = useLocal({ error: false });
return (
<div
className={cx(
"img-thumb",
className,
css`
width: ${w}px;
height: ${h}px;
background-image: linear-gradient(45deg, #ccc 25%, transparent 25%),
linear-gradient(135deg, #ccc 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #ccc 75%),
linear-gradient(135deg, transparent 75%, #ccc 75%);
background-size: 25px 25px; /* Must be a square */
background-position: 0 0, 12.5px 0, 12.5px -12.5px, 0px 12.5px; /* Must be half of one side of the square */
`
)}
>
{!local.error && (
<img
onError={() => {
local.error = true;
local.render();
}}
src={siteurl(
`/_img/${url.substring("_file/".length)}?w=${w}&h=${h}&fit=cover`
)}
/>
)}
</div>
);
};

View File

@ -262,7 +262,7 @@ export const FieldUploadMulti: FC<{
<div className="c-flex c-pt-1">
<div
className={cx(
"c-flex c-border c-rounded c-cursor-pointer hover:c-bg-blue-50",
"button c-flex c-border c-rounded c-cursor-pointer hover:c-bg-blue-50",
css`
&:hover {
border: 1px solid #1c4ed8;

View File

@ -35,9 +35,9 @@ 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 { sortTree } from "./utils/sort-tree";
import { toast } from "../ui/toast";
import { Arrow } from "../custom/Datepicker/components/utils";
import { sortTree } from "./utils/sort-tree";
import { getPathname } from "lib/exports";
type OnRowClick = (arg: {
row: any;
@ -176,6 +176,104 @@ export const TableList: FC<TableListProp> = ({
collapsed: new Set<number>(),
cached_row: new WeakMap<any, ReactElement>(),
filtering: "" as ReactNode | string | true,
reload: (arg?: { toast: boolean }) => {
return new Promise<void>((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.slice(1, -1);
} else {
filtering = "";
}
if (filtering) {
local.filtering = (
<div className="c-pt-2">
Searching for: <pre>"{filtering.trim()}"</pre>
</div>
);
}
}
}
if (md) {
const last = md.params.links[md.params.links.length - 1];
if (last && last.where) {
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 (local.paging.skip === 0) {
local.data = data;
} else {
local.data = [...local.data, ...data];
}
local.status = "ready";
local.render();
done();
};
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);
}
});
},
sort: {
columns: (ls_sort?.columns || []) as SortColumn[],
on_change: (cols: SortColumn[]) => {
@ -255,104 +353,7 @@ export const TableList: FC<TableListProp> = ({
},
});
const reload = useCallback(
(arg?: { toast: boolean }) => {
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.slice(1, -1);
} else {
filtering = "";
}
if (filtering) {
local.filtering = (
<div className="c-pt-2">
Searching for: <pre>"{filtering.trim()}"</pre>
</div>
);
}
}
}
if (md) {
const last = md.params.links[md.params.links.length - 1];
if (last && last.where) {
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 (local.paging.skip === 0) {
local.data = data;
} else {
local.data = [...local.data, ...data];
}
local.status = "ready";
local.render();
};
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);
}
},
[on_load, local.sort.orderBy, local.paging.take, local.paging.skip]
);
const reload = local.reload;
if (md) {
md.master.reload = reload;
}
@ -366,6 +367,7 @@ export const TableList: FC<TableListProp> = ({
// code ini digunakan untuk mengambil nama dari pk yang akan digunakan sebagai key untuk id
const pk = local.pk?.name || "id";
useEffect(() => {
if (isEditor || value) {
on_init(local);
@ -373,7 +375,10 @@ export const TableList: FC<TableListProp> = ({
}
(async () => {
on_init(local);
if (local.status === "reload" && typeof on_load === "function") {
if (
(local.status === "init" || local.status === "reload") &&
typeof on_load === "function"
) {
reload();
}
})();
@ -703,35 +708,24 @@ export const TableList: FC<TableListProp> = ({
if (columns.length > 1) columns = columns.slice(0, 0 + 1);
}
if (local.status === "loading") {
toast.dismiss();
toast.loading(
<>
<Loader2 className="c-h-4 c-w-4 c-animate-spin" />
Loading Data ...
</>
);
} else {
toast.dismiss();
}
if (local.status === "resizing" && !isEditor) {
local.status = "ready";
local.render();
return null;
}
if (!isEditor) {
if (local.status === "loading") {
if (local.should_toast) {
toast.dismiss();
toast.loading(
<>
<Loader2 className="c-h-4 c-w-4 c-animate-spin" />
Loading {local.paging.skip === 0 ? "Data" : "more rows"} ...
</>,
{
dismissible: true,
}
);
} else {
local.should_toast = true;
}
} else {
if (local.status !== "error") {
toast.dismiss();
}
}
}
if (document.getElementsByClassName("prasi-toaster").length === 0) {
const elemDiv = document.createElement("div");
elemDiv.className = "prasi-toaster";
@ -908,32 +902,16 @@ export const TableList: FC<TableListProp> = ({
}
}}
>
<div className="c-absolute c-inset-0">
{toaster_el && createPortal(<Toaster cn={cn} />, toaster_el)}
{local.status === "init" ? (
<DataGrid
style={{ opacity: 0 }}
columns={[
{
key: "_",
name: "",
renderCell({ rowIdx }) {
if (local.paging.take < rowIdx) {
local.paging.take = rowIdx;
}
clearTimeout(local.paging.timeout);
local.paging.timeout = setTimeout(() => {
local.status = "reload";
local.paging.take = local.paging.take * 5;
local.render();
}, 100);
return <></>;
},
},
]}
rows={genRows(200)}
/>
) : (
{toaster_el && createPortal(<Toaster cn={cn} />, toaster_el)}
{local.status !== "ready" ? (
<div className="c-flex c-flex-col c-space-y-2 c-m-4 c-absolute c-left-0 c-top-0">
<Skeleton className={cx("c-w-[200px] c-h-[11px]")} />
<Skeleton className={cx("c-w-[170px] c-h-[11px]")} />
<Skeleton className={cx("c-w-[180px] c-h-[11px]")} />
</div>
) : (
<div className="c-absolute c-inset-0">
<>
{Array.isArray(data) && data.length > 0 ? (
<div
@ -965,7 +943,7 @@ export const TableList: FC<TableListProp> = ({
) : (
<div className="c-flex c-items-center c-justify-center c-flex-1 w-full h-full c-flex-col ">
<Sticker size={35} strokeWidth={1} />
<div className="c-pt-1">
<div className="c-pt-1 c-text-center">
No&nbsp;Data
<br />
{local.filtering && (
@ -983,8 +961,8 @@ export const TableList: FC<TableListProp> = ({
</div>
)}
</>
)}
</div>
</div>
)}
</div>
);
} else {

View File

@ -1,20 +1,51 @@
import { FC, useState } from "react";
import { FC, ReactNode, useEffect, useState } from "react";
import { breadcrumbPrefix } from "../utils/md-hash";
import { MDLocal, MDRef } from "../utils/typings";
import { BreadItem } from "lib/comps/custom/Breadcrumb";
import { useLocal } from "lib/utils/use-local";
export const MDHeader: FC<{ md: MDLocal; mdr: MDRef }> = ({ md, mdr }) => {
const [_, set] = useState({});
const local = useLocal({ breads_length: 0 });
const head = mdr.item.edit.props?.header.value;
const PassProp = mdr.PassProp;
md.header.render = () => set({});
md.header.render = local.render;
const prefix = breadcrumbPrefix(md);
let breads: (BreadItem & { url: string })[] = [];
if (md.selected && md.header.child.breadcrumb) {
md.header.breadcrumb = [...prefix, ...md.header.child.breadcrumb()];
breads = [...prefix, ...md.header.child.breadcrumb()] as any;
} else if (!md.selected && md.header.master.breadcrumb) {
md.header.breadcrumb = [...prefix, ...md.header.master.breadcrumb()];
breads = [...prefix, ...md.header.master.breadcrumb()] as any;
}
md.header.breadcrumb = [];
let overrideLabel = "" as ReactNode;
for (const v of breads) {
if (v.label === "--reset--") {
md.header.breadcrumb = [];
overrideLabel = "";
continue;
}
if (v.url === "--override--") {
overrideLabel = v.label;
continue;
}
if (overrideLabel) {
v.label = overrideLabel;
overrideLabel = "";
}
md.header.breadcrumb.push(v);
}
useEffect(() => {
if (local.breads_length !== md.header.breadcrumb.length) {
local.breads_length = md.header.breadcrumb.length;
if (!md.selected && md.master.reload) md.master.reload();
}
}, [md.header.breadcrumb.length]);
if (md.internal.reset_detail) return null;
return <PassProp md={md}>{head}</PassProp>;
};

View File

@ -81,7 +81,7 @@ export const masterDetailApplyParams = (md: MDLocal) => {
};
export const breadcrumbPrefix = (md: MDLocal) => {
const prefix: BreadItem[] = [];
let prefix: (BreadItem & { url: string })[] = [];
if (md.params.links && md.params.links.length > 0) {
const hashes: string[] = [];
for (const link of md.params.links) {
@ -93,6 +93,7 @@ export const breadcrumbPrefix = (md: MDLocal) => {
for (const p of link.prefix) {
prefix.push({
label: p.label,
url: p.url || link.url,
onClick(ev) {
let url = "";
@ -103,7 +104,7 @@ export const breadcrumbPrefix = (md: MDLocal) => {
if (p.md) {
url = `${p.url || link.url}#${p.md.name}=${p.md.value}${lnk}`;
} else {
url = `${p.url || link.url}${lnk}`;
url = `${p.url || link.url}${lnk}`;
}
if (url) {

View File

@ -12,6 +12,7 @@ export const toast = {
sonner.dismiss();
} else {
clearTimeout(timer.timeout);
timer.timeout = null;
}
},
loading: (
@ -41,6 +42,8 @@ export const toast = {
clearTimeout(timer.timeout);
timer.timeout = setTimeout(() => {
sonner.error(el, props);
clearTimeout(timer.timeout);
timer.timeout = null;
}, timer.limit);
},

View File

@ -26,6 +26,10 @@ export const Typeahead = lazify(
async () => (await import("@/comps/ui/typeahead")).Typeahead
);
export const ImgThumb = lazify(
async () => (await import("@/comps/form/field/type/FilePreview")).ImgThumb
);
/** Master - Detail - List - Form */
export const MasterDetail = lazify(
async () => (await import("@/comps/md/MasterDetail")).MasterDetail