This commit is contained in:
rizky 2024-09-16 21:51:09 -07:00
parent 492ad60c48
commit 209e521adc
12 changed files with 250 additions and 169 deletions

View File

@ -1,4 +1,4 @@
import { useLocal } from "lib/utils/use-local"; import { useLocal } from "@/utils/use-local";
import { FC, useEffect } from "react"; import { FC, useEffect } from "react";
import { loadChart } from "./loader"; import { loadChart } from "./loader";
import type { Bar } from "react-chartjs-2"; import type { Bar } from "react-chartjs-2";
@ -14,8 +14,9 @@ const getRandomInt = (min: number, max: number) => {
export const BarChart: FC<{ export const BarChart: FC<{
data: () => ItemData; data: () => ItemData;
legend?: any legend?: any;
}> = ({ data, legend }) => { scale?: any;
}> = ({ data, legend, scale }) => {
const local = useLocal({ const local = useLocal({
data: null as unknown as ItemData, data: null as unknown as ItemData,
Bar: null as null | typeof Bar, Bar: null as null | typeof Bar,
@ -42,6 +43,7 @@ export const BarChart: FC<{
options={{ options={{
responsive: true, responsive: true,
maintainAspectRatio: false, maintainAspectRatio: false,
scales: scale,
plugins: plugins:
legend === "none" legend === "none"
? { ? {

View File

@ -415,10 +415,10 @@ const Days: React.FC<Props> = ({
hoverDay(item, "previous"); hoverDay(item, "previous");
}} }}
> >
<span className="c-relative"> <span className="c-relative">
{item} {item}
<span className="c-relative">{load_marker(item, "previous")}</span> {load_marker(item, "previous")}
</span> </span>
</button> </button>
))} ))}
@ -438,7 +438,7 @@ const Days: React.FC<Props> = ({
> >
<span className="c-relative"> <span className="c-relative">
{item} {item}
<span className="c-relative">{load_marker(item, "current")}</span> {load_marker(item, "current")}
</span> </span>
</button> </button>
))} ))}
@ -454,10 +454,10 @@ const Days: React.FC<Props> = ({
hoverDay(item, "next"); hoverDay(item, "next");
}} }}
> >
<span className="c-relative"> <span className="c-relative">
{item} {item}
<span className="c-relative">{load_marker(item, "next")}</span> {load_marker(item, "next")}
</span> </span>
</button> </button>
))} ))}
</div> </div>

View File

@ -1,76 +1,90 @@
import { useEffect, useRef, useState } from "react"; import { FC, useEffect, useRef, useState } from "react";
import QrScanner from "qr-scanner"; import QrScanner from "qr-scanner";
export const QrReader = () => { export const QrReader: FC<{ url: string }> = ({ url }) => {
const scanner = useRef<QrScanner>(); const scanner = useRef<QrScanner>();
const videoEl = useRef<HTMLVideoElement>(null); const videoEl = useRef<HTMLVideoElement>(null);
const qrBoxEl = useRef<HTMLDivElement>(null); const qrBoxEl = useRef<HTMLDivElement>(null);
const [qrOn, setQrOn] = useState<boolean>(true); const [qrOn, setQrOn] = useState<boolean>(true);
const [scannedResult, setScannedResult] = useState<string | undefined>(""); const [scannedResult, setScannedResult] = useState<string | undefined>("");
const [hasScanned, setHasScanned] = useState<boolean>(false);
// Success
const onScanSuccess = (result: QrScanner.ScanResult) => {
console.log(result);
setScannedResult(result?.data);
};
// Fail // Success
const onScanFail = (err: string | Error) => { const onScanSuccess = (result: QrScanner.ScanResult) => {
console.log(err); // console.log(result);
}; // navigate("/hasil/scan");
// setScannedResult(result?.data);
if (!hasScanned) {
console.log(result);
setScannedResult(result?.data);
setHasScanned(true);
scanner.current?.stop();
useEffect(() => { setTimeout(() => {
if (videoEl?.current && !scanner.current) { // console.log("Mencoba trigger hasil scan");
scanner.current = new QrScanner(videoEl?.current, onScanSuccess, { // navigate(`/hasil/scan?id_asset=${result?.data}`);
onDecodeError: onScanFail, navigate(url + result?.data);
preferredCamera: "environment", }, 1000);
highlightScanRegion: true, }
highlightCodeOutline: true,
overlay: qrBoxEl?.current || undefined,
});
scanner?.current
?.start()
.then(() => setQrOn(true))
.catch((err) => {
if (err) setQrOn(false);
});
}
return () => {
if (!videoEl?.current) {
scanner?.current?.stop();
}
}; };
}, []);
useEffect(() => { // Fail
if (!qrOn) const onScanFail = (err: string | Error) => {
alert( console.log(err);
"Camera is blocked or not accessible. Please allow camera in your browser permissions and Reload." };
);
}, [qrOn]);
return ( useEffect(() => {
<div className="qr-reader"> if (videoEl?.current && !scanner.current) {
{/* QR */} scanner.current = new QrScanner(videoEl?.current, onScanSuccess, {
<video ref={videoEl}></video> onDecodeError: onScanFail,
<div ref={qrBoxEl} className="qr-box"></div> preferredCamera: "environment",
highlightScanRegion: true,
highlightCodeOutline: true,
overlay: qrBoxEl?.current || undefined,
});
{/* Show Data Result if scan is success */} scanner?.current
{scannedResult && ( ?.start()
<p .then(() => setQrOn(true))
style={{ .catch((err) => {
position: "absolute", if (err) setQrOn(false);
top: 0, });
left: 0, }
zIndex: 99999,
color: "white", return () => {
}} if (!videoEl?.current) {
> scanner?.current?.stop();
Scanned Result: {scannedResult} }
</p> };
)} }, []);
</div>
); useEffect(() => {
if (!qrOn)
alert(
"Kamera tidak bisa diakses. Tolong beri izin untuk akses kamera dan silahkan reload."
);
}, [qrOn]);
return (
<div className="qr-reader">
<video ref={videoEl}></video>
<div ref={qrBoxEl} className="qr-box">
</div>
{scannedResult && (
<p
style={{
position: "absolute",
top: 0,
left: 0,
zIndex: 99999,
color: "white",
}}
>
Scanned Result: {scannedResult}
</p>
)}
</div>
);
}; };

View File

@ -1,4 +1,5 @@
import * as DialogPrimitive from "@radix-ui/react-dialog"; import { useLocal } from "@/utils/use-local";
import { Button } from "lib/comps/ui/button";
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
@ -7,9 +8,9 @@ import {
DialogTitle, DialogTitle,
DialogTrigger, DialogTrigger,
} from "lib/comps/ui/dialog"; } from "lib/comps/ui/dialog";
import { useLocal } from "lib/utils/use-local"; import * as DialogPrimitive from "@radix-ui/react-dialog";
import { X } from "lucide-react"; import { X } from "lucide-react";
import { FC } from "react"; import { FC, useEffect } from "react";
export const Pop: FC<{ export const Pop: FC<{
child: any; child: any;
@ -53,6 +54,7 @@ export const Pop: FC<{
)} )}
/> />
<DialogContent className="sm:s-max-w-[425px]"> <DialogContent className="sm:s-max-w-[425px]">
<div className="c-relative c-flex-grow">
<DialogPrimitive.Close <DialogPrimitive.Close
onClick={() => { onClick={() => {
local.open = false; local.open = false;
@ -62,6 +64,7 @@ export const Pop: FC<{
"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", "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` css`
right: 1rem; right: 1rem;
z-index: 1;
` `
)} )}
> >
@ -85,6 +88,7 @@ export const Pop: FC<{
> >
{content} {content}
</PassProp> </PassProp>
</div>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
); );

View File

@ -3,7 +3,7 @@ import { BaseForm } from "../form/base/BaseForm";
import { FilterLocal } from "./utils/types"; import { FilterLocal } from "./utils/types";
import { useLocal } from "lib/utils/use-local"; import { useLocal } from "lib/utils/use-local";
import { getFilter } from "./utils/get-filter"; import { getFilter } from "./utils/get-filter";
import { FMLocal } from "../form/typings"; import { FieldLoading, FMLocal } from "lib/exports";
export const FilterContent: FC<{ export const FilterContent: FC<{
mode: string; mode: string;
@ -106,32 +106,37 @@ export const FilterContent: FC<{
<BaseForm <BaseForm
data={filter.data} data={filter.data}
on_submit={async (form) => { on_submit={async (form) => {
const fm = form.fm;
try { try {
if (typeof form.fm?.data === "object") { if (typeof form.fm?.data === "object") {
const fm = form.fm?.data as any
form.render(); form.render();
form.fm.render(); form.fm.render();
} }
} catch (ex) {} } catch (ex) {}
if (typeof onSubmit === "function" && mode === "raw") { if (mode === "raw" && fm) {
const data = await onSubmit(form.fm); const submit = async (fm: FMLocal) => {
if (typeof form.fm?.data === "object") { fm.render();
form.fm.data = { if (typeof onSubmit === "function") {
__status: "submit", const data = await onSubmit(fm);
...form.fm.data, if (typeof form.fm?.data === "object") {
_where: data, form.fm.data = {
}; __status: "submit",
form.fm.render(); ...form.fm.data,
filter.data = { _where: data,
__status: "submit", };
...form.fm.data, form.fm.render();
_where: data, filter.data = {
}; __status: "submit",
filter.render(); ...form.fm.data,
} _where: data,
};
filter.render();
}
}
};
await submit(fm);
} }
// form.render();
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)) {

View File

@ -88,14 +88,14 @@ export const FilterField: FC<{
value={field.fm?.data?.[name]} value={field.fm?.data?.[name]}
placeholder={field.field.label} placeholder={field.field.label}
onBlur={() => { onBlur={() => {
clearTimeout(internal.search_timeout); // clearTimeout(internal.search_timeout);
filter.form?.submit(); // filter.form?.submit();
}} }}
spellCheck={false} spellCheck={false}
className="c-flex-1 c-transition-all c-bg-transparent c-outline-none c-px-2 c-text-sm c-w-full" className="c-flex-1 c-transition-all c-bg-transparent c-outline-none c-px-2 c-text-sm c-w-full"
onChange={(e) => { onChange={(e) => {
console.log("HALO")
field.fm.data[name] = e.currentTarget.value; field.fm.data[name] = e.currentTarget.value;
clearTimeout(internal.search_timeout); clearTimeout(internal.search_timeout);
internal.search_timeout = setTimeout(() => { internal.search_timeout = setTimeout(() => {
filter.form?.submit(); filter.form?.submit();

View File

@ -1,10 +1,10 @@
import { useLocal } from "lib/utils/use-local"; import { useLocal } from "@/utils/use-local";
import { FC, ReactNode } from "react"; import { FC, ReactNode, useEffect } from "react";
import { FMLocal, GenField } from "../form/typings"; import { FMLocal, GenField } from "../form/typings";
import { FilterContent } from "./FilterContent"; import { FilterContent } from "./FilterContent";
import { getFilter } from "./utils/get-filter"; import { getFilter } from "./utils/get-filter";
import { default_filter_local } from "./utils/types"; import { default_filter_local } from "./utils/types";
import { FieldLoading } from "../ui/field-loading"; import { FieldLoading } from "lib/exports";
type FilterMode = "raw" | "inline"; type FilterMode = "raw" | "inline";
@ -39,31 +39,42 @@ export const MasterFilter: FC<FilterProps> = ({
const filter = useLocal({ ...default_filter_local }); const filter = useLocal({ ...default_filter_local });
filter.name = name; filter.name = name;
filter.mode = mode; filter.mode = mode;
useEffect(() => {
if (!isEditor) { if (!isEditor) {
const wf = getFilter(name); const wf = getFilter(name);
if (wf) { if (wf) {
if (wf.filter.ref[_item.id]) { if (wf.filter.ref[_item.id]) {
filter.data = wf.filter.ref[_item.id].data; filter.data = wf.filter.ref[_item.id].data;
} else { filter.raw_status = "ready";
if (mode === "raw" && onLoad) { filter.render();
if (filter.raw_status === "init") { } else {
filter.raw_status = "loading"; if (mode === "raw" && onLoad) {
filter.data = onLoad(); if (filter.raw_status === "init") {
filter.raw_status = "ready"; filter.raw_status = "loading";
filter.render(); const data = onLoad();
} if (data instanceof Promise) {
data.then((e) => {
if (filter.raw_status !== "ready") { filter.data = e;
return <FieldLoading />; filter.raw_status = "ready";
filter.render();
});
} else {
filter.raw_status = "ready";
filter.data = data;
filter.render();
}
}
} }
} }
wf.filter.ref[_item.id] = filter;
wf.list.render();
} }
wf.filter.ref[_item.id] = filter;
wf.list.render();
} }
} }, []);
if (mode === "raw" && filter.raw_status !== "ready") {
return <FieldLoading />;
}
return ( return (
<> <>
<FilterContent <FilterContent

View File

@ -1,5 +1,5 @@
import { AutoHeightTextarea } from "lib/comps/custom/AutoHeightTextarea"; import { AutoHeightTextarea } from "@/comps/custom/AutoHeightTextarea";
import { useLocal } from "lib/utils/use-local"; import { useLocal } from "@/utils/use-local";
import parser from "any-date-parser"; import parser from "any-date-parser";
import Datepicker from "lib/comps/custom/Datepicker"; import Datepicker from "lib/comps/custom/Datepicker";
import { EyeIcon, EyeOff } from "lucide-react"; import { EyeIcon, EyeOff } from "lucide-react";
@ -217,7 +217,7 @@ export const FieldTypeInput: FC<{
asSingle={true} asSingle={true}
useRange={false} useRange={false}
onChange={(value) => { onChange={(value) => {
console.log({ value }); // console.log({ value });
fm.data[field.name] = value?.startDate fm.data[field.name] = value?.startDate
? new Date(value?.startDate) ? new Date(value?.startDate)
: null; : null;

View File

@ -1,5 +1,5 @@
import { GFCol } from "lib/gen/utils"; import { GFCol } from "@/gen/utils";
import { useLocal } from "lib/utils/use-local"; import { useLocal } from "@/utils/use-local";
import { ChangeEvent, FC, MouseEvent } from "react"; import { ChangeEvent, FC, MouseEvent } from "react";
// import { Workbook } from 'exceljs'; // import { Workbook } from 'exceljs';
// import * as XLSX from "xlsx"; // import * as XLSX from "xlsx";
@ -131,11 +131,7 @@ export const ImportExcel: FC<ImportExcelProps> = ({
}); });
rowIndex++; rowIndex++;
// let insertedData = await table.create({ data: insertRow });
// local.insertedData.push({
// pk: insertedData.id,
// rows: insertedData
// });
local.insertedData.push({ local.insertedData.push({
pk: insertRow[pk], pk: insertRow[pk],
rows: insertRow, rows: insertRow,

View File

@ -1,4 +1,4 @@
import { fetchLinkParams, parseLink } from "lib/utils/fetch-link-params"; import { fetchLinkParams, parseLink } from "lib/comps/form/field/type/TypeLink";
import { MDLocal } from "./typings"; import { MDLocal } from "./typings";
import { BreadItem } from "lib/comps/custom/Breadcrumb"; import { BreadItem } from "lib/comps/custom/Breadcrumb";
@ -37,7 +37,7 @@ export const masterDetailParseHash = (md: MDLocal) => {
} }
} }
if (changed) { if (changed) {
md.params.links = []; md.params.links = [];
md.header.loading = true; md.header.loading = true;
fetchLinkParams(parsed_link).then((links) => { fetchLinkParams(parsed_link).then((links) => {
@ -60,7 +60,7 @@ export const masterDetailApplyParams = (md: MDLocal) => {
delete md.params.tabs[md.name]; delete md.params.tabs[md.name];
} }
} }
console.log({select: JSON.parse(JSON.stringify(md.selected))})
const pk = md.pk; const pk = md.pk;
if (pk && row[pk.name]) { if (pk && row[pk.name]) {
md.params.hash[md.name] = row[pk.name]; md.params.hash[md.name] = row[pk.name];
@ -80,6 +80,18 @@ export const masterDetailApplyParams = (md: MDLocal) => {
if (!isEditor) { if (!isEditor) {
location.hash = hash; location.hash = hash;
} }
if(!isEditor){
if(md.props.tab_mode === "v-tab" || md.props.tab_mode === "h-tab"){
try{
if(row && md?.childs?.form?.fm && md?.childs?.form?.fm?.status === "ready" && md.selected?.id){
md.childs.form.fm.reload();
// console.log("MASUK???")
}
}catch(ex){
}
}
}
}; };
const cleanHash = (hash: string) => { const cleanHash = (hash: string) => {

View File

@ -1,4 +1,4 @@
import { useLocal } from "lib/utils/use-local"; import { useLocal } from "@/utils/use-local";
import * as SheetPrimitive from "@radix-ui/react-dialog"; import * as SheetPrimitive from "@radix-ui/react-dialog";
import { X } from "lucide-react"; import { X } from "lucide-react";
import { FC, useEffect } from "react"; import { FC, useEffect } from "react";
@ -21,7 +21,22 @@ export const SheetCn: FC<{
content: any; content: any;
header: string; header: string;
open: string; open: string;
}> = ({ child, PassProp, props, content, header, open }) => { close?: string;
mode?: "full" | "normal";
direction?: "left" | "right" | "bottom" | "top";
deps?: any;
}> = ({
child,
PassProp,
props,
content,
header,
open,
close,
mode,
direction,
deps
}) => {
const local = useLocal({ const local = useLocal({
open: false, open: false,
}); });
@ -40,6 +55,7 @@ export const SheetCn: FC<{
<div {...props} className={cx(props.className, "")}> <div {...props} className={cx(props.className, "")}>
<PassProp <PassProp
sheet={{ sheet={{
deps,
open: () => { open: () => {
local.open = true; local.open = true;
local.render(); local.render();
@ -71,39 +87,60 @@ export const SheetCn: FC<{
)} )}
/> />
<SheetContent <SheetContent
side={direction}
className={cx( className={cx(
"sm:s-max-w-[425px]", mode === "full" ? "c-w-screen" : "sm:s-max-w-[425px]",
css` css`
z-index: 2; z-index: 2;
` `
)} )}
> >
<SheetPrimitive.Close {close !== "n" ? (
onClick={() => { <SheetPrimitive.Close
local.open = false; onClick={() => {
local.render(); 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", className={cx(
css` "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",
right: 1rem; css`
` right: 1rem;
)} z-index: 99;
> `
<X className="c-h-4 c-w-4" /> )}
<span className="c-sr-only">Close</span> >
</SheetPrimitive.Close> <X className="c-h-4 c-w-4" />
<SheetHeader <span className="c-sr-only">Close</span>
className={cx( </SheetPrimitive.Close>
css` ) : (
padding: 0px 0px 0px 10px; <></>
` )}
)} {header !== "" && header ? (
> <SheetHeader
<SheetTitle>{header}</SheetTitle> className={cx(
</SheetHeader> css`
padding: 0px 0px 0px 10px;
`
)}
>
<SheetTitle>{header}</SheetTitle>
</SheetHeader>
) : (
<>
<SheetHeader
className={cx(
css`
padding: 0px;
`
)}
>
<SheetTitle></SheetTitle>
</SheetHeader></>
)}
<PassProp <PassProp
sheet={{ sheet={{
deps,
open: () => { open: () => {
local.open = true; local.open = true;
local.render(); local.render();

View File

@ -4,7 +4,7 @@ import * as React from "react";
import * as SheetPrimitive from "@radix-ui/react-dialog"; import * as SheetPrimitive from "@radix-ui/react-dialog";
import { cva, type VariantProps } from "class-variance-authority"; import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "lib/utils"; import { cn } from "@/utils";
const Sheet = SheetPrimitive.Root; const Sheet = SheetPrimitive.Root;
@ -30,7 +30,7 @@ const SheetOverlay = React.forwardRef<
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName; SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
const sheetVariants = cva( const sheetVariants = cva(
"c-fixed c-z-50 c-gap-4 c-bg-background c-p-6 c-shadow-lg c-transition c-ease-in-out data-[state=closed]:c-duration-300 data-[state=open]:c-duration-500 data-[state=open]:c-animate-in data-[state=closed]:c-animate-out", "c-fixed c-z-50 c-gap-4 c-bg-background c-shadow-lg c-transition c-ease-in-out data-[state=closed]:c-duration-300 data-[state=open]:c-duration-500 data-[state=open]:c-animate-in data-[state=closed]:c-animate-out",
{ {
variants: { variants: {
side: { side: {
@ -39,7 +39,7 @@ const sheetVariants = cva(
"c-inset-x-0 c-bottom-0 c-border-t data-[state=closed]:c-slide-out-to-bottom data-[state=open]:c-slide-in-from-bottom", "c-inset-x-0 c-bottom-0 c-border-t data-[state=closed]:c-slide-out-to-bottom data-[state=open]:c-slide-in-from-bottom",
left: "c-inset-y-0 c-left-0 c-h-full c-w-3/4 c-border-r data-[state=closed]:c-slide-out-to-left data-[state=open]:c-slide-in-from-left sm:c-max-w-sm", left: "c-inset-y-0 c-left-0 c-h-full c-w-3/4 c-border-r data-[state=closed]:c-slide-out-to-left data-[state=open]:c-slide-in-from-left sm:c-max-w-sm",
right: right:
"c-inset-y-0 c-right-0 c-h-full c-w-3/4 c-border-l data-[state=closed]:c-slide-out-to-right data-[state=open]:c-slide-in-from-right sm:c-max-w-sm", "c-inset-y-0 c-right-0 c-h-full c-w-3/4 c-border-l data-[disabled]:c-bg-red-300 data-[state=closed]:c-slide-out-to-right data-[state=open]:c-slide-in-from-right sm:c-max-w-sm",
}, },
}, },
defaultVariants: { defaultVariants: {