Dialong Pop, Import, sheet Component
This commit is contained in:
parent
cec4fefeaf
commit
7c3d24255b
|
|
@ -0,0 +1,93 @@
|
|||
import { useLocal } from "@/utils/use-local";
|
||||
import { glb } from "app/lib/goal";
|
||||
import { Button } from "lib/comps/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogOverlay,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "lib/comps/ui/dialog";
|
||||
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
||||
import { X } from "lucide-react";
|
||||
import { FC, useEffect } from "react";
|
||||
|
||||
export const Pop: FC<{
|
||||
child: any;
|
||||
PassProp: any;
|
||||
props: any;
|
||||
content: any;
|
||||
}> = ({ child, PassProp, props, content }) => {
|
||||
const local = useLocal({
|
||||
open: false,
|
||||
});
|
||||
return (
|
||||
<Dialog open={local.open}>
|
||||
<DialogTrigger asChild>
|
||||
<div {...props} className={cx(props.className, "")}>
|
||||
<PassProp
|
||||
pop={{
|
||||
open: () => {
|
||||
local.open = true;
|
||||
local.render();
|
||||
},
|
||||
close: () => {
|
||||
local.open = false;
|
||||
local.render();
|
||||
},
|
||||
}}
|
||||
>
|
||||
{child}
|
||||
</PassProp>
|
||||
</div>
|
||||
</DialogTrigger>
|
||||
<DialogOverlay
|
||||
onClick={() => {
|
||||
local.open = false;
|
||||
local.render();
|
||||
}}
|
||||
className={cx(
|
||||
"",
|
||||
css`
|
||||
background-color: #00000038 !important;
|
||||
`
|
||||
)}
|
||||
/>
|
||||
<DialogContent className="sm:s-max-w-[425px]">
|
||||
<DialogPrimitive.Close
|
||||
onClick={() => {
|
||||
local.open = false;
|
||||
local.render();
|
||||
}}
|
||||
className={cx(
|
||||
"c-absolute c-right-4 c-top-4 c-rounded-sm c-opacity-70 c-ring-offset-background c-transition-opacity hover:c-opacity-100 focus:c-outline-none focus:c-ring-2 focus:c-ring-ring focus:c-ring-offset-2 disabled:c-pointer-events-none data-[state=open]:c-bg-accent data-[state=open]:c-text-muted-foreground",
|
||||
css`
|
||||
right: 1rem;
|
||||
`
|
||||
)}
|
||||
>
|
||||
<X className="c-h-4 c-w-4" />
|
||||
<span className="c-sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
<DialogHeader className="c-hidden">
|
||||
<DialogTitle></DialogTitle>
|
||||
</DialogHeader>
|
||||
<PassProp
|
||||
pop={{
|
||||
open: () => {
|
||||
local.open = true;
|
||||
local.render();
|
||||
},
|
||||
close: () => {
|
||||
local.open = false;
|
||||
local.render();
|
||||
},
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</PassProp>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
import { useLocal } from "@/utils/use-local";
|
||||
import { FC, useEffect } from "react";
|
||||
import * as XLSX from "xlsx";
|
||||
|
||||
export const Import: FC<{
|
||||
child: any;
|
||||
PassProp: any;
|
||||
props: any;
|
||||
sample: string;
|
||||
desc: string;
|
||||
title: string;
|
||||
task: (e: any) => Promise<any>;
|
||||
excel: any;
|
||||
done: (e: any) => void;
|
||||
}> = ({ child, PassProp, props, title, desc, sample, task, excel, done }) => {
|
||||
const local = useLocal({
|
||||
ready: false,
|
||||
fase: "start" as "start" | "running" | "end",
|
||||
data: [] as any[],
|
||||
progress: 0 as number,
|
||||
props: {
|
||||
title,
|
||||
desc,
|
||||
sample,
|
||||
},
|
||||
file: {
|
||||
name: isEditor ? "sample" : (null as any),
|
||||
size: isEditor ? "sample" : (null as any),
|
||||
},
|
||||
excel: {
|
||||
failed: {
|
||||
data: [] as any[],
|
||||
download: () => {
|
||||
const ws = XLSX.utils.json_to_sheet(local.excel.failed.data);
|
||||
// Buat workbook dan tambahkan worksheet
|
||||
const wb = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(wb, ws, "report");
|
||||
// Ekspor file
|
||||
XLSX.writeFile(wb, "failed_import.xlsx");
|
||||
}
|
||||
}
|
||||
},
|
||||
recap: {
|
||||
success: [] as any[],
|
||||
failed: [] as any[],
|
||||
},
|
||||
action: {
|
||||
run: () => {
|
||||
local.fase = "running";
|
||||
local.progress = 0;
|
||||
local.ready = false;
|
||||
local.render();
|
||||
},
|
||||
done: () => {
|
||||
local.fase = "end";
|
||||
local.render();
|
||||
}
|
||||
}
|
||||
});
|
||||
useEffect(() => {
|
||||
if (local.fase === "start") {
|
||||
local.ready = false;
|
||||
local.render();
|
||||
}
|
||||
setTimeout(() => {
|
||||
local.render();
|
||||
}, 1000);
|
||||
}, [local.fase]);
|
||||
useEffect(() => {
|
||||
if (local.ready) {
|
||||
const callback = () => {
|
||||
local.fase = "end";
|
||||
local.ready =false;
|
||||
local.render();
|
||||
if (typeof done === "function") {
|
||||
done(local);
|
||||
}
|
||||
};
|
||||
const sleep = (ms: number) => {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
};
|
||||
const startImport = async ({
|
||||
list,
|
||||
task,
|
||||
}: {
|
||||
list: any[];
|
||||
task: (e: any) => Promise<void>;
|
||||
}) => {
|
||||
let n = 0;
|
||||
while (n < list.length) {
|
||||
if (!local.ready) {
|
||||
await new Promise((resolve) => {
|
||||
const interval = setInterval(() => {
|
||||
if (local.ready) {
|
||||
clearInterval(interval);
|
||||
resolve([]);
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
|
||||
await task(list[n]);
|
||||
await sleep(100);
|
||||
n++;
|
||||
}
|
||||
};
|
||||
let idx = 0;
|
||||
if (local.progress <= 0) {
|
||||
const res = startImport({
|
||||
list: local.data,
|
||||
task: async (e) => {
|
||||
if (typeof task === "function") {
|
||||
const result = await task({data: e, excel});
|
||||
if (typeof result === "boolean") {
|
||||
if (!result) {
|
||||
local.recap.failed.push(e);
|
||||
local.excel.failed.data.push(e);
|
||||
} else {
|
||||
local.recap.success.push(e);
|
||||
}
|
||||
} else {
|
||||
local.recap.success.push(e);
|
||||
}
|
||||
}
|
||||
idx++;
|
||||
local.progress = (idx / local.data.length) * 100;
|
||||
local.render();
|
||||
},
|
||||
});
|
||||
|
||||
if (res instanceof Promise) res.then(callback);
|
||||
else callback();
|
||||
}
|
||||
}
|
||||
}, [local.ready]);
|
||||
return (
|
||||
<div {...props} className={cx(props.className, "")}>
|
||||
<PassProp
|
||||
li={local}
|
||||
>
|
||||
{child}
|
||||
</PassProp>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
export const formatBytes = (bytes: number, decimals = 2) => {
|
||||
if (bytes === 0) return "0 Bytes";
|
||||
const k = 1024;
|
||||
const dm = decimals < 0 ? 0 : decimals;
|
||||
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
|
||||
};
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
import { useLocal } from "@/utils/use-local";
|
||||
import * as SheetPrimitive from "@radix-ui/react-dialog";
|
||||
import { X } from "lucide-react";
|
||||
import { FC, useEffect } from "react";
|
||||
import {
|
||||
Sheet,
|
||||
SheetClose,
|
||||
SheetContent,
|
||||
SheetDescription,
|
||||
SheetFooter,
|
||||
SheetHeader,
|
||||
SheetOverlay,
|
||||
SheetTitle,
|
||||
SheetTrigger,
|
||||
} from "lib/comps/ui/sheet";
|
||||
|
||||
export const SheetCn: FC<{
|
||||
child: any;
|
||||
PassProp: any;
|
||||
props: any;
|
||||
content: any;
|
||||
header: string;
|
||||
open: string;
|
||||
}> = ({ child, PassProp, props, content, header, open }) => {
|
||||
const local = useLocal({
|
||||
open: false,
|
||||
});
|
||||
useEffect(() => {
|
||||
if (isEditor) {
|
||||
let op = open === "y" ? true : false;
|
||||
if (local.open !== op) {
|
||||
local.open = op;
|
||||
local.render();
|
||||
}
|
||||
}
|
||||
}, [open]);
|
||||
return (
|
||||
<Sheet open={local.open}>
|
||||
<SheetTrigger asChild>
|
||||
<div {...props} className={cx(props.className, "")}>
|
||||
<PassProp
|
||||
sheet={{
|
||||
open: () => {
|
||||
local.open = true;
|
||||
local.render();
|
||||
setTimeout(() => {
|
||||
document.body.style.pointerEvents = "auto";
|
||||
}, 1000);
|
||||
},
|
||||
close: () => {
|
||||
local.open = false;
|
||||
local.render();
|
||||
},
|
||||
}}
|
||||
>
|
||||
{child}
|
||||
</PassProp>
|
||||
</div>
|
||||
</SheetTrigger>
|
||||
<SheetOverlay
|
||||
onClick={() => {
|
||||
local.open = false;
|
||||
local.render();
|
||||
}}
|
||||
className={cx(
|
||||
"",
|
||||
css`
|
||||
background-color: #00000038 !important;
|
||||
z-index: 1;
|
||||
`
|
||||
)}
|
||||
/>
|
||||
<SheetContent
|
||||
className={cx(
|
||||
"sm:s-max-w-[425px]",
|
||||
css`
|
||||
z-index: 2;
|
||||
`
|
||||
)}
|
||||
>
|
||||
<SheetPrimitive.Close
|
||||
onClick={() => {
|
||||
local.open = false;
|
||||
local.render();
|
||||
}}
|
||||
className={cx(
|
||||
"c-absolute c-right-4 c-top-4 c-rounded-sm c-opacity-70 c-ring-offset-background c-transition-opacity hover:c-opacity-100 focus:c-outline-none focus:c-ring-2 focus:c-ring-ring focus:c-ring-offset-2 disabled:c-pointer-events-none data-[state=open]:c-bg-accent data-[state=open]:c-text-muted-foreground",
|
||||
css`
|
||||
right: 1rem;
|
||||
`
|
||||
)}
|
||||
>
|
||||
<X className="c-h-4 c-w-4" />
|
||||
<span className="c-sr-only">Close</span>
|
||||
</SheetPrimitive.Close>
|
||||
<SheetHeader
|
||||
className={cx(
|
||||
css`
|
||||
padding: 0px 0px 0px 10px;
|
||||
`
|
||||
)}
|
||||
>
|
||||
<SheetTitle>{header}</SheetTitle>
|
||||
</SheetHeader>
|
||||
<PassProp
|
||||
sheet={{
|
||||
open: () => {
|
||||
local.open = true;
|
||||
local.render();
|
||||
setTimeout(() => {
|
||||
document.body.style.pointerEvents = "auto";
|
||||
}, 1000);
|
||||
},
|
||||
close: () => {
|
||||
local.open = false;
|
||||
local.render();
|
||||
},
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</PassProp>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
);
|
||||
};
|
||||
12
exports.tsx
12
exports.tsx
|
|
@ -15,6 +15,7 @@ export const Dialog = lazify(
|
|||
async () => (await import("@/comps/ui/dialog")).Dialog
|
||||
);
|
||||
|
||||
|
||||
export const Typeahead = lazify(
|
||||
async () => (await import("@/comps/ui/typeahead")).Typeahead
|
||||
);
|
||||
|
|
@ -94,7 +95,18 @@ export const ScrollArea = lazify(
|
|||
export const KeyValue = lazify(
|
||||
async () => (await import("@/comps/form/field/type/KeyValue")).KeyValue
|
||||
);
|
||||
export const Pop = lazify(
|
||||
async () => (await import("@/comps/dialog/Dialog")).Pop
|
||||
);
|
||||
|
||||
export const Import = lazify(
|
||||
async () => (await import("@/comps/import/Import")).Import
|
||||
);
|
||||
|
||||
export const Sheet = lazify(
|
||||
async () => (await import("@/comps/sheet/sheet")).SheetCn
|
||||
);
|
||||
export {formatBytes} from "@/comps/import/lib/formatBytes"
|
||||
export { fetchLinkParams } from "@/comps/form/field/type/TypeLink";
|
||||
export { FieldLoading, Spinner } from "@/comps/ui/field-loading";
|
||||
export { lang } from "lib/lang";
|
||||
|
|
|
|||
Loading…
Reference in New Issue