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
|
async () => (await import("@/comps/ui/dialog")).Dialog
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
export const Typeahead = lazify(
|
export const Typeahead = lazify(
|
||||||
async () => (await import("@/comps/ui/typeahead")).Typeahead
|
async () => (await import("@/comps/ui/typeahead")).Typeahead
|
||||||
);
|
);
|
||||||
|
|
@ -94,7 +95,18 @@ export const ScrollArea = lazify(
|
||||||
export const KeyValue = lazify(
|
export const KeyValue = lazify(
|
||||||
async () => (await import("@/comps/form/field/type/KeyValue")).KeyValue
|
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 { fetchLinkParams } from "@/comps/form/field/type/TypeLink";
|
||||||
export { FieldLoading, Spinner } from "@/comps/ui/field-loading";
|
export { FieldLoading, Spinner } from "@/comps/ui/field-loading";
|
||||||
export { lang } from "lib/lang";
|
export { lang } from "lib/lang";
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue