checkpoint

This commit is contained in:
rizky 2024-11-03 21:01:36 -07:00
parent fff47ee40a
commit 61845dc62b
43 changed files with 1592 additions and 616 deletions

View File

@ -27,6 +27,7 @@ interface Props {
onClickDay: (day: number) => void; onClickDay: (day: number) => void;
onClickNextDays: (day: number) => void; onClickNextDays: (day: number) => void;
onIcon?: (day: number, date: Date) => any; onIcon?: (day: number, date: Date) => any;
style?: string;
} }
const Days: React.FC<Props> = ({ const Days: React.FC<Props> = ({
@ -35,6 +36,7 @@ const Days: React.FC<Props> = ({
onClickDay, onClickDay,
onClickNextDays, onClickNextDays,
onIcon, onIcon,
style,
}) => { }) => {
// Contexts // Contexts
const { const {
@ -241,13 +243,18 @@ const Days: React.FC<Props> = ({
const buttonClass = useCallback( const buttonClass = useCallback(
(day: number, type: "current" | "next" | "previous") => { (day: number, type: "current" | "next" | "previous") => {
const baseClass = let baseClass = `calender-day c-flex c-items-center c-justify-center ${
"c-flex c-items-center c-justify-center c-w-12 c-h-12 lg:c-w-10 lg:c-h-10 c-relative"; style === "google"
? " c-w-6 c-h-6 c-m-1"
: "c-w-12 c-h-12 lg:c-w-10 lg:c-h-10"
} c-relative`;
if (type === "current") { if (type === "current") {
return cn( return cn(
baseClass, baseClass,
!activeDateData(day).active !activeDateData(day).active
? hoverClassByDay(day) ? hoverClassByDay(day)
: style === "google"
? ""
: activeDateData(day).className, : activeDateData(day).className,
isDateDisabled(day, type) && "c-text-gray-400 c-cursor-not-allowed" isDateDisabled(day, type) && "c-text-gray-400 c-cursor-not-allowed"
); );
@ -403,62 +410,171 @@ const Days: React.FC<Props> = ({
return typeof onIcon === "function" ? onIcon(day, res) : null; return typeof onIcon === "function" ? onIcon(day, res) : null;
}; };
return ( return (
<div className="c-grid c-grid-cols-7 c-gap-y-0.5 c-my-1"> <div
className={cx(
"calender-days c-grid c-grid-cols-7 c-gap-y-0.5 c-my-1",
css`
grid-template-columns: repeat(
auto-fit,
minmax(0px, 1fr)
); /* Grid fleksibel */
.calender-grid {
aspect-ratio: 1 / 1;
}
`
)}
>
{calendarData.days.previous.map((item, index) => ( {calendarData.days.previous.map((item, index) => (
<button <div
type="button" className={cx(
key={index} "calender-grid c-flex c-flex-row",
disabled={isDateDisabled(item, "previous")} style === "google" ? "hover:c-bg-gray-100 c-cursor-pointer" : ""
className={`${buttonClass(item, "previous")}`} )}
onClick={() => handleClickDay(item, "previous")} onClick={() => {
onMouseOver={() => { if (style === "google") handleClickDay(item, "previous");
hoverDay(item, "previous");
}} }}
> >
<span className="c-relative"> <div className="c-flex c-flex-col c-flex-grow calender-day-wrap">
{item} {style === "google" ? (
{load_marker(item, "previous")} <>
</span> <button
</button> type="button"
key={index}
disabled={isDateDisabled(item, "previous")}
className={`${buttonClass(item, "previous")}`}
onMouseOver={() => {
hoverDay(item, "previous");
}}
>
<span className="c-relative">{item}</span>
</button>
<div>{load_marker(item, "previous")}</div>
</>
) : (
<>
<button
type="button"
key={index}
disabled={isDateDisabled(item, "previous")}
className={`${buttonClass(item, "previous")}`}
onClick={() => handleClickDay(item, "previous")}
onMouseOver={() => {
hoverDay(item, "previous");
}}
>
<span className="c-relative">
{item}
{load_marker(item, "previous")}
</span>
</button>
</>
)}
</div>
</div>
))} ))}
{calendarData.days.current.map((item, index) => ( {calendarData.days.current.map((item, index) => (
<button <div
type="button"
key={index}
disabled={isDateDisabled(item, "current")}
className={cx( className={cx(
`${buttonClass(item, "current")}`, "calender-grid c-flex c-flex-row",
item === 1 && "highlight" style === "google"
? activeDateData(item).active
? "c-bg-blue-200/75 c-ring-1 c-cursor-pointer"
: "hover:c-bg-gray-100 c-cursor-pointer"
: ""
)} )}
onClick={() => handleClickDay(item, "current")} onClick={() => {
onMouseOver={() => { if (style === "google") handleClickDay(item, "current");
hoverDay(item, "current");
}} }}
> >
<span className="c-relative"> <div className="c-flex c-flex-col c-flex-grow calender-day-wrap">
{item} {style === "google" ? (
{load_marker(item, "current")} <>
</span> <button
</button> type="button"
key={index}
disabled={isDateDisabled(item, "current")}
className={`${buttonClass(item, "current")}`}
// onClick={() => handleClickDay(item, "current")}
onMouseOver={() => {
hoverDay(item, "current");
}}
>
<span className="c-relative">{item}</span>
</button>
<div>{load_marker(item, "current")}</div>
</>
) : (
<>
<button
type="button"
key={index}
disabled={isDateDisabled(item, "current")}
className={`${buttonClass(item, "current")}`}
onClick={() => handleClickDay(item, "current")}
onMouseOver={() => {
hoverDay(item, "current");
}}
>
<span className="c-relative">
{item}
{load_marker(item, "current")}
</span>
</button>
</>
)}
</div>
</div>
))} ))}
{calendarData.days.next.map((item, index) => ( {calendarData.days.next.map((item, index) => (
<button <div
type="button" className={cx(
key={index} "calender-grid c-flex c-flex-row",
disabled={isDateDisabled(item, "next")} style === "google" ? "hover:c-bg-gray-100 c-cursor-pointer" : ""
className={`${buttonClass(item, "next")}`} )}
onClick={() => handleClickDay(item, "next")} onClick={() => {
onMouseOver={() => { if (style === "google") handleClickDay(item, "next");
hoverDay(item, "next");
}} }}
> >
<span className="c-relative"> <div className="c-flex c-flex-col c-flex-grow calender-day-wrap">
{item} {style === "google" ? (
{load_marker(item, "next")} <>
</span> <button
</button> type="button"
key={index}
disabled={isDateDisabled(item, "next")}
className={`${buttonClass(item, "next")}`}
onMouseOver={() => {
hoverDay(item, "next");
}}
>
<span className="c-relative">{item}</span>
</button>
<div>{load_marker(item, "next")}</div>
</>
) : (
<>
<button
type="button"
key={index}
disabled={isDateDisabled(item, "next")}
className={`${buttonClass(item, "next")}`}
onClick={() => handleClickDay(item, "next")}
onMouseOver={() => {
hoverDay(item, "next");
}}
>
<span className="c-relative">
{item}
{load_marker(item, "next")}
</span>
</button>
</>
)}
</div>
</div>
))} ))}
</div> </div>
); );

View File

@ -9,9 +9,10 @@ import { RoundedButton } from "../utils";
interface Props { interface Props {
currentMonth: number; currentMonth: number;
clickMonth: (month: number) => void; clickMonth: (month: number) => void;
style?: string
} }
const Months: React.FC<Props> = ({ currentMonth, clickMonth }) => { const Months: React.FC<Props> = ({ currentMonth, clickMonth, style }) => {
const { i18n } = useContext(DatepickerContext); const { i18n } = useContext(DatepickerContext);
loadLanguageModule(i18n); loadLanguageModule(i18n);
return ( return (
@ -25,7 +26,7 @@ const Months: React.FC<Props> = ({ currentMonth, clickMonth }) => {
}} }}
active={currentMonth === item} active={currentMonth === item}
> >
<>{dayjs(`2022-${item}-01`).locale(i18n).format("MMM")}</> <>{dayjs(`2022-${item}-01`).locale(i18n).format(style === "google" ? "MMMM" :"MMM")}</>
</RoundedButton> </RoundedButton>
))} ))}
</div> </div>

View File

@ -4,11 +4,15 @@ import React, { useContext, useMemo } from "react";
import { DAYS } from "../../constants"; import { DAYS } from "../../constants";
import DatepickerContext from "../../contexts/DatepickerContext"; import DatepickerContext from "../../contexts/DatepickerContext";
import { loadLanguageModule, shortString, ucFirst } from "../../helpers"; import { loadLanguageModule, shortString, ucFirst } from "../../helpers";
interface Props {
const Week: React.FC = () => { style?: string
}
const Week: React.FC<Props> = ({style}) => {
const { i18n, startWeekOn } = useContext(DatepickerContext); const { i18n, startWeekOn } = useContext(DatepickerContext);
loadLanguageModule(i18n); loadLanguageModule(i18n);
const startDateModifier = useMemo(() => { const startDateModifier = useMemo(() => {
console.log(startWeekOn);
if (startWeekOn) { if (startWeekOn) {
switch (startWeekOn) { switch (startWeekOn) {
case "mon": case "mon":
@ -33,19 +37,22 @@ const Week: React.FC = () => {
}, [startWeekOn]); }, [startWeekOn]);
return ( return (
<div className=" c-grid c-grid-cols-7 c-border-b c-border-gray-300 dark:c-border-gray-700 c-py-2"> <div className="calender-week c-grid c-grid-cols-7 c-border-b c-border-gray-300 dark:c-border-gray-700 c-py-2">
{DAYS.map((item) => ( {DAYS.map((item) => (
<div <div
key={item} key={item}
className="c-tracking-wide c-text-gray-500 c-text-center" className="c-tracking-wide c-text-gray-500 c-text-center"
> >
{ucFirst( {style === "google" ? dayjs(`2022-11-${6 + (item + startDateModifier)}`)
shortString(
dayjs(`2022-11-${6 + (item + startDateModifier)}`)
.locale(i18n) .locale(i18n)
.format("ddd") .format("dddd") : ucFirst(
) shortString(
)} dayjs(`2022-11-${6 + (item + startDateModifier)}`)
.locale(i18n)
.format("dddd")
),
)}
</div> </div>
))} ))}
</div> </div>

View File

@ -45,6 +45,7 @@ interface Props {
changeYear: (year: number) => void; changeYear: (year: number) => void;
mode?: "monthly" | "daily"; mode?: "monthly" | "daily";
onMark?: (day: number, date: Date) => any; onMark?: (day: number, date: Date) => any;
style?: string
} }
const Calendar: React.FC<Props> = ({ const Calendar: React.FC<Props> = ({
@ -57,6 +58,7 @@ const Calendar: React.FC<Props> = ({
changeYear, changeYear,
onMark, onMark,
mode = "daily", mode = "daily",
style = "prasi"
}) => { }) => {
// Contexts // Contexts
const { const {
@ -266,7 +268,7 @@ const Calendar: React.FC<Props> = ({
); );
return ( return (
<div className="c-w-full md:c-w-[296px] md:c-min-w-[296px]"> <div className="c-w-full md:c-w-[296px] md:c-min-w-[296px] calender">
<div <div
className={cx( className={cx(
"c-flex c-items-stretch c-space-x-1.5 c-px-2 c-py-1.5", "c-flex c-items-stretch c-space-x-1.5 c-px-2 c-py-1.5",
@ -304,7 +306,7 @@ const Calendar: React.FC<Props> = ({
hideYears(); hideYears();
}} }}
> >
<>{calendarData.date.locale(i18n).format("MMM")}</> <>{calendarData.date.locale(i18n).format(style === "google" ? "MMMM" :"MMM")}</>
</RoundedButton> </RoundedButton>
</div> </div>
@ -341,11 +343,12 @@ const Calendar: React.FC<Props> = ({
</div> </div>
)} )}
</div> </div>
<div className={cx("c-mt-0.5 c-min-h-[285px]")}> <div className={cx("c-mt-0.5 c-min-h-[285px] calender-body")}>
{showMonths && ( {showMonths && (
<Months <Months
currentMonth={calendarData.date.month() + 1} currentMonth={calendarData.date.month() + 1}
clickMonth={clickMonth} clickMonth={clickMonth}
style={style}
/> />
)} )}
@ -361,13 +364,14 @@ const Calendar: React.FC<Props> = ({
{!showMonths && !showYears && ( {!showMonths && !showYears && (
<> <>
<Week /> <Week style={style}/>
<Days <Days
calendarData={calendarData} calendarData={calendarData}
onClickPreviousDays={clickPreviousDays} onClickPreviousDays={clickPreviousDays}
onClickDay={clickDay} onClickDay={clickDay}
onClickNextDays={clickNextDays} onClickNextDays={clickNextDays}
style={style}
onIcon={(day, date) => { onIcon={(day, date) => {
if(typeof onMark === "function"){ if(typeof onMark === "function"){
return onMark(day, date) return onMark(day, date)

View File

@ -174,7 +174,15 @@ export function getNumberOfDay(
} }
} }
[ isMobile ? [
"S",
"M",
"T",
"W",
"T",
"F",
"S",
]: [
"Sunday", "Sunday",
"Monday", "Monday",
"Tuesday", "Tuesday",

View File

@ -84,7 +84,8 @@ export interface DatepickerType {
popoverDirection?: PopoverDirectionType; popoverDirection?: PopoverDirectionType;
mode?: "daily" | "monthly"; mode?: "daily" | "monthly";
onMark?: (day: number, date: Date) => any; onMark?: (day: number, date: Date) => any;
onLoad?: () => Promise<void> onLoad?: (day: any) => Promise<void>;
style?: "google" | "prasi"
} }

View File

@ -7,6 +7,7 @@ export const QrLabel: FC<{
fgcolor: string, fgcolor: string,
size: number size: number
}> = ({ value, bgcolor, fgcolor, size }) => { }> = ({ value, bgcolor, fgcolor, size }) => {
console.log(value);
return ( return (
<div> <div>
<QRCode value={value} size={size} <QRCode value={value} size={size}

107
comps/custom/QrScanner.tsx Executable file
View File

@ -0,0 +1,107 @@
import { FC, useEffect, useRef, useState } from "react";
import { default as ScanQr } from "qr-scanner";
import { QrReaderType } from "./typings";
export const QrScanner: FC<{ onSuccess: (result: any) => {} }> = ({ onSuccess }) => {
const w = window as any;
const scanner = useRef<ScanQr>();
const videoEl = useRef<HTMLVideoElement>(null);
const qrBoxEl = useRef<HTMLDivElement>(null);
const [qrOn, setQrOn] = useState<boolean>(true);
const [scannedResult, setScannedResult] = useState<string | undefined>("");
const [hasScanned, setHasScanned] = useState<boolean>(false);
const onScanSuccess = (result: ScanQr.ScanResult) => {
if (!hasScanned) {
console.log(result);
setScannedResult(result?.data);
setHasScanned(true);
scanner.current?.stop();
setTimeout(() => {
onSuccess(result?.data);
}, 1000);
}
};
const onScanFail = (err: string | Error) => {
console.log("failed", err);
};
const openCameraScanner = () => {
if (w.AndroidBridge && w.AndroidBridge.openCameraScanner) {
w.AndroidBridge.openCameraScanner();
} else {
console.error("AndroidBridge or openCameraScanner function is not available.");
}
};
if (w.AndroidBridge && w.AndroidBridge.openCameraScanner) {
useEffect(() => {
openCameraScanner();
}, []);
useEffect(() => {
w.onScannerResult = (scannedData: string) => {
console.log('Scanned Data:', scannedData);
onSuccess(scannedData);
};
}, []);
return (
<div>
<h1>QR/Barcode Scanner</h1>
</div>
);
} else {
console.log("Bridge not found");
useEffect(() => {
if (videoEl?.current && !scanner.current) {
scanner.current = new ScanQr(videoEl?.current, onScanSuccess, {
onDecodeError: onScanFail,
preferredCamera: "environment",
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(() => {
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

@ -3,6 +3,7 @@ import { Button } from "lib/comps/ui/button";
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
DialogDescription,
DialogHeader, DialogHeader,
DialogOverlay, DialogOverlay,
DialogTitle, DialogTitle,
@ -73,6 +74,7 @@ export const Pop: FC<{
</DialogPrimitive.Close> </DialogPrimitive.Close>
<DialogHeader className="c-hidden"> <DialogHeader className="c-hidden">
<DialogTitle></DialogTitle> <DialogTitle></DialogTitle>
<DialogDescription></DialogDescription>
</DialogHeader> </DialogHeader>
<PassProp <PassProp
pop={{ pop={{

View File

@ -115,6 +115,13 @@ export const FilterContent: FC<{
} catch (ex) {} } catch (ex) {}
if (mode === "raw" && fm) { if (mode === "raw" && fm) {
if (form && form.fm) {
Object.keys(form.fm.data).map((e) => {
if (!form?.fm.data?.[e]) {
delete form.fm?.data[e];
}
});
}
const submit = async (fm: FMLocal) => { const submit = async (fm: FMLocal) => {
fm.render(); fm.render();
if (typeof onSubmit === "function") { if (typeof onSubmit === "function") {

View File

@ -43,11 +43,12 @@ export const MasterFilter: FC<FilterProps> = ({
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;
filter.raw_status = "ready"; filter.raw_status = "ready";
filter.render(); filter.render();
} else { } else {
filter.data = {};
if (mode === "raw" && onLoad) { if (mode === "raw" && onLoad) {
if (filter.raw_status === "init") { if (filter.raw_status === "init") {
filter.raw_status = "loading"; filter.raw_status = "loading";
@ -55,8 +56,24 @@ export const MasterFilter: FC<FilterProps> = ({
if (data instanceof Promise) { if (data instanceof Promise) {
data.then((e) => { data.then((e) => {
filter.data = e; filter.data = e;
if (typeof onSubmit === "function") {
const data = onSubmit({ data: e } as any);
if (data instanceof Promise) {
data.then((ex) => {
filter.data = {
__status: "submit",
...e,
_where: ex,
};
filter.render();
});
}
console.log({ data });
}
filter.raw_status = "ready"; filter.raw_status = "ready";
filter.render(); filter.render();
}); });
} else { } else {
filter.raw_status = "ready"; filter.raw_status = "ready";
@ -69,6 +86,9 @@ export const MasterFilter: FC<FilterProps> = ({
wf.filter.ref[_item.id] = filter; wf.filter.ref[_item.id] = filter;
wf.list.render(); wf.list.render();
} }
} else {
filter.raw_status = "ready";
filter.render();
} }
}, []); }, []);

View File

@ -1,5 +1,411 @@
export const generateFilter = ( export const generateFilter = (data: any, item: PrasiItem, commit: boolean) => {
data: any, console.log("log", { data, item, commit });
item: PrasiItem, };
commit: boolean
) => {}; const frameFilter = {
dim: { h: "full", w: "full", hUnit: "px", wUnit: "px" },
name: "Wrapped",
type: "item",
childs: [
{
bg: { pos: "center", size: "cover", color: "" },
dim: { h: "full", w: "full", hUnit: "px", wUnit: "px" },
name: "Wrapped",
type: "item",
childs: [
{
id: "r9u5j1gxxbuv73x8magw7smp",
adv: {
js: '<ScrollArea className={cx(props.className, "")} orientation={orientation}>{child}</ScrollArea>',
css: "",
jsBuilt:
'const _jsxFileName = "[item: scroll-area - ehqmu8gsesbvqyxmiw8l580t]";render (React.createElement(ScrollArea, { className: cx(props.className, ""), orientation: orientation, __self: this, __source: {fileName: _jsxFileName, lineNumber: 1}}, child))',
},
dim: { h: "full", w: "full" },
name: "scroll-area",
type: "item",
childs: [
{
name: "jsx:child",
id: "c7nakvqt2ih6zbbrnq59omup",
},
],
script: {
props: {
orientation: { value: '"vertical"', valueBuilt: '"vertical"' },
},
},
component: {
id: "bb74d83b-5fd5-45a5-902d-f2f2ec8a48a7",
props: {
child: {
idx: 1,
meta: { type: "content-element" },
name: "new_prop_1",
type: "string",
value: '"hello"',
content: {
bg: { pos: "center", size: "cover", color: "" },
id: "vpvydv6rjhrtw6j9xkgl2t8f",
adv: { js: "", css: "", jsBuilt: "render ()" },
dim: { h: "full", w: "full", hUnit: "px", wUnit: "px" },
name: "child",
type: "item",
childs: [
{
bg: { pos: "center", size: "cover", color: "" },
id: "j1jtff4t8hvk4c2zn2spseww",
adv: {
js: '<div {...props} className={cx(props.className, "")} id="cek">\n {children}\n</div>',
jsBuilt:
'const _jsxFileName = "[item: absolute - bs6mzs6otarkqrqciv6hd0to]";render (React.createElement(\'div\', { ...props, className: cx(props.className, ""), id: "cek", __self: this, __source: {fileName: _jsxFileName, lineNumber: 1}}\n , children\n))',
},
dim: { h: "full", w: "full", hUnit: "px", wUnit: "px" },
name: "absolute",
type: "item",
childs: [
{
id: "tlfpdpv1g6y7f7klumtbecet",
dim: {
h: "fit",
w: "full",
hUnit: "px",
wUnit: "px",
},
name: "container",
type: "item",
childs: [
{
id: "epmv1gh58uso1iyi747oqlpf",
adv: {
js: '<div {...props} className={cx(props.className, "form-fields")}>\n {children}\n</div>',
jsBuilt:
'render(/* @__PURE__ */ React.createElement("div", { ...props, className: cx(props.className, "form-fields") }, children));\n',
},
dim: {
h: "full",
w: "full",
hUnit: "px",
wUnit: "px",
},
name: "fields-filter",
type: "item",
childs: [],
layout: {
dir: "row",
gap: 0,
wrap: "flex-wrap",
align: "top-left",
},
script: {},
},
],
hidden: false,
},
],
hidden: false,
script: {},
padding: { b: 0, l: 0, r: 5, t: 0 },
},
],
script: {},
},
valueBuilt: '"hello"',
jsxCalledBy: [
"yych4cm4do7t4jt0d42dasrk",
"ehqmu8gsesbvqyxmiw8l580t",
],
},
orientation: {
idx: 1,
meta: {
type: "option",
options: '["horizontal", "vertical"]',
optionsBuilt: '["horizontal", "vertical"]',
},
name: "new_prop_1",
type: "string",
value: '"vertical"',
valueBuilt: '"vertical"',
},
},
ref_ids: {},
useStyle: true,
},
hidden: false,
},
],
hidden: false,
},
{
id: "s9kb580m1bjmpe4xj9dtkgnz",
dim: { h: "fit", w: "full", hUnit: "px", wUnit: "px" },
name: "Wrapped",
type: "item",
childs: [
{
id: "hf0pxf4slu2xqhcuoropnkc6",
dim: { h: "fit", w: "full", hUnit: "px", wUnit: "px" },
name: "Reset",
type: "item",
childs: [
{
id: "jvnbd14f1sgcnmdhy4dcfuo4",
adv: {
js: '<Button\n {...props}\n onClick={(e) => {\n if (!isEditor) on_click(e);\n }}\n variant={variant !== "primary" ? variant : undefined}\n size={size !== "default" ? size : undefined}\n>\n {label}\n</Button>',
css: "& {\n display: flex;\n\n &:hover {\n cursor: pointer;\n\n\n\n\n\n // &.mobile {}\n // &.desktop {}\n // &:hover {}\n }\n}",
jsBuilt:
'render(/* @__PURE__ */ React.createElement(\n Button,\n {\n ...props,\n onClick: (e) => {\n if (!isEditor)\n on_click(e);\n },\n variant: variant !== "primary" ? variant : void 0,\n size: size !== "default" ? size : void 0\n },\n label\n));\n',
},
dim: { h: "full", w: "full" },
name: "button",
type: "item",
childs: [
{
name: "jsx: label",
id: "k59ppuxhzf4tnkb4b4enyxxe",
},
],
mobile: { linktag: {} },
script: {
props: {
size: { value: ' "default";\n', valueBuilt: ' "default";\n' },
variant: { value: '"ghost"', valueBuilt: '"ghost"' },
on_click: {
value:
'async (e) => {\r\n fm.data = {};\r\n filter.data = {};\r\n filter.render();\r\n fm.render();\r\n await fm.submit();\r\n sheet.close();\r\n // const res = getFilter("root");\r\n // res.list.reload();\r\n}',
valueBuilt:
'async (e) => {\r\n fm.data = {};\r\n filter.data = {};\r\n filter.render();\r\n fm.render();\r\n await fm.submit();\r\n sheet.close();\r\n // const res = getFilter("root");\r\n // res.list.reload();\r\n}',
},
},
},
component: {
id: "a15d152d-0118-408f-89f1-f6b2dfbd2e05",
props: {
size: {
idx: 5,
meta: {
type: "option",
options:
'["default", "xs", "sm", "lg", "icon", "nosize"]',
optionsBuilt:
' ["default", "xs", "sm", "lg", "icon", "nosize"];\n',
},
name: "prop_5",
type: "string",
value: '"default"',
valueBuilt: ' "default";\n',
},
label: {
idx: 1,
meta: { type: "content-element" },
name: "prop_1",
type: "string",
value: '"hello"',
content: {
id: "bb91ryll6mtk827zpibo6k65",
adv: {
js: '<div {...props} className={cx(props.className, "")}>\n {children}\n</div>',
css: "",
jsBuilt:
'render(/* @__PURE__ */ React.createElement("div", { ...props, className: cx(props.className, "") }, children));\n',
},
dim: { h: "full", w: "full" },
name: "label",
type: "item",
childs: [
{
id: "zhmnkaup9uxpou42lyn4splr",
adv: {
js: '<div {...props} className={cx(props.className, "")}>\n Reset\n</div>',
css: "",
jsBuilt:
'const _jsxFileName = "[item: new_text - feo7cgson0fo2zjjujwqkr8l]";render (React.createElement(\'div\', { ...props, className: cx(props.className, ""), __self: this, __source: {fileName: _jsxFileName, lineNumber: 1}}, "Reset"\n\n))',
},
dim: { h: "full", w: "full" },
html: "submit",
name: "new_text",
text: "",
type: "text",
layout: { dir: "col", gap: 0, align: "center" },
script: {},
},
],
script: {},
},
valueBuilt: '"hello"',
jsxCalledBy: [
"frq12uxc65i9zn8myzan4huk",
"q278u9p4cejubvfwqc72ey73",
],
},
variant: {
idx: 3,
meta: {
type: "option",
options:
'["primary", "secondary", "outline", "ghost", "link", "destructive","no-style"]',
option_mode: "button",
optionsBuilt:
' ["primary", "secondary", "outline", "ghost", "link", "destructive", "no-style"];\n',
},
name: "prop_3",
type: "string",
value: '"ghost"',
valueBuilt: '"ghost"',
},
on_click: {
idx: 1,
meta: { type: "text" },
name: "prop_1",
type: "string",
value:
'async (e: React.MouseEvent<HTMLDivElement>) => {\r\n fm.data = {};\r\n filter.data = {};\r\n filter.render();\r\n fm.render();\r\n await fm.submit();\r\n sheet.close();\r\n // const res = getFilter("root");\r\n // res.list.reload();\r\n}',
valueBuilt:
'async (e) => {\r\n fm.data = {};\r\n filter.data = {};\r\n filter.render();\r\n fm.render();\r\n await fm.submit();\r\n sheet.close();\r\n // const res = getFilter("root");\r\n // res.list.reload();\r\n}',
},
},
},
originalId: "q278u9p4cejubvfwqc72ey73",
},
],
padding: { b: 10, l: 10, r: 0, t: 10 },
},
{
id: "zidglf2tal6pgxe6c1ey7i5x",
dim: { h: "fit", w: "full", hUnit: "px", wUnit: "px" },
name: "Apply",
type: "item",
childs: [
{
id: "hjaeik76bqh3ufszscmzw0bm",
adv: {
js: '<Button\n {...props}\n onClick={(e) => {\n if (!isEditor) on_click(e);\n }}\n variant={variant !== "primary" ? variant : undefined}\n size={size !== "default" ? size : undefined}\n>\n {label}\n</Button>',
css: "& {\n display: flex;\n\n &:hover {\n cursor: pointer;\n\n\n\n\n\n // &.mobile {}\n // &.desktop {}\n // &:hover {}\n }\n}",
jsBuilt:
'render(/* @__PURE__ */ React.createElement(\n Button,\n {\n ...props,\n onClick: (e) => {\n if (!isEditor)\n on_click(e);\n },\n variant: variant !== "primary" ? variant : void 0,\n size: size !== "default" ? size : void 0\n },\n label\n));\n',
},
dim: { h: "full", w: "full" },
name: "button",
type: "item",
childs: [
{
name: "jsx: label",
id: "w8i2j4uq3a186ozueanxisx9",
originalId: "hglwmigw09h2b8a9vlz2cme5",
},
],
mobile: { linktag: {} },
script: {
props: {
size: { value: ' "default";\n', valueBuilt: ' "default";\n' },
variant: {
value: ' "primary";\n',
valueBuilt: ' "primary";\n',
},
on_click: {
value:
'async (e) => {\r\n await fm.submit();\r\n const res = getFilter("root");\r\n res.list.reload();\r\n sheet.close();\r\n}',
valueBuilt:
'async (e) => {\r\n await fm.submit();\r\n const res = getFilter("root");\r\n res.list.reload();\r\n sheet.close();\r\n}',
},
},
},
component: {
id: "a15d152d-0118-408f-89f1-f6b2dfbd2e05",
props: {
size: {
idx: 5,
meta: {
type: "option",
options:
'["default", "xs", "sm", "lg", "icon", "nosize"]',
optionsBuilt:
' ["default", "xs", "sm", "lg", "icon", "nosize"];\n',
},
name: "prop_5",
type: "string",
value: '"default"',
valueBuilt: ' "default";\n',
},
label: {
idx: 1,
meta: { type: "content-element" },
name: "prop_1",
type: "string",
value: '"hello"',
content: {
id: "dlq5kemqyq8d0os6uu34l4wd",
adv: {
js: '<div {...props} className={cx(props.className, "")}>\n {children}\n</div>',
css: "",
jsBuilt:
'render(/* @__PURE__ */ React.createElement("div", { ...props, className: cx(props.className, "") }, children));\n',
},
dim: { h: "full", w: "full" },
name: "label",
type: "item",
childs: [
{
id: "nbho83c6uggcc4mxjn0e2tr9",
adv: {
js: '<div {...props} className={cx(props.className, "")}>\n Apply\n</div>',
css: "",
jsBuilt:
'const _jsxFileName = "[item: new_text - to0gkiai6fbns9utxj6re1pw]";render (React.createElement(\'div\', { ...props, className: cx(props.className, ""), __self: this, __source: {fileName: _jsxFileName, lineNumber: 1}}, "Apply"\n\n))',
},
dim: { h: "full", w: "full" },
html: "submit",
name: "new_text",
text: "",
type: "text",
layout: { dir: "col", gap: 0, align: "center" },
script: {},
},
],
script: {},
},
valueBuilt: '"hello"',
jsxCalledBy: [
"frq12uxc65i9zn8myzan4huk",
"q278u9p4cejubvfwqc72ey73",
],
},
variant: {
idx: 3,
meta: {
type: "option",
options:
'["primary", "secondary", "outline", "ghost", "link", "destructive","no-style"]',
option_mode: "button",
optionsBuilt:
' ["primary", "secondary", "outline", "ghost", "link", "destructive", "no-style"];\n',
},
name: "prop_3",
type: "string",
value: '"primary"',
valueBuilt: ' "primary";\n',
},
on_click: {
idx: 1,
meta: { type: "text" },
name: "prop_1",
type: "string",
value:
'async (e: React.MouseEvent<HTMLDivElement>) => {\r\n await fm.submit();\r\n const res = getFilter("root");\r\n res.list.reload();\r\n sheet.close();\r\n}',
valueBuilt:
'async (e) => {\r\n await fm.submit();\r\n const res = getFilter("root");\r\n res.list.reload();\r\n sheet.close();\r\n}',
},
},
},
originalId: "q278u9p4cejubvfwqc72ey73",
},
],
padding: { b: 10, l: 10, r: 0, t: 10 },
},
],
layout: { dir: "row", gap: 0, wrap: "flex-nowrap", align: "top-left" },
padding: { b: 0, l: 5, r: 5, t: 0 },
},
],
};

View File

@ -56,7 +56,7 @@ export const BaseField = (prop: {
"filled" "filled"
)} )}
> >
{arg.show_label !== "n" && <Label field={field} fm={fm} />} {arg.show_label !== "n" && <Label field={field} fm={fm} arg={arg}/>}
<div className="field-input c-flex c-flex-1 c-flex-col"> <div className="field-input c-flex c-flex-1 c-flex-col">
<div <div
className={cx( className={cx(

View File

@ -104,7 +104,15 @@ export const Field: FC<FieldProp> = (arg) => {
const disabled = const disabled =
typeof field.disabled === "function" ? field.disabled() : field.disabled; typeof field.disabled === "function" ? field.disabled() : field.disabled;
if (field.hidden) return <></>; const show =
typeof field.hidden === "function"
? field.hidden()
: typeof field.hidden === "string"
? field.hidden === "n"
? false
: true
: typeof field.hidden === "boolean"? field.hidden : true;
if (!show) return <></>;
return ( return (
<LabelDiv <LabelDiv
@ -145,7 +153,7 @@ export const Field: FC<FieldProp> = (arg) => {
)} )}
ref={typeof arg.field_ref === "function" ? arg.field_ref : undefined} ref={typeof arg.field_ref === "function" ? arg.field_ref : undefined}
> >
{showlabel !== "n" && field.label && <Label field={field} fm={fm} />} {showlabel !== "n" && field.label && <Label field={field} fm={fm} arg={arg}/>}
<div className={cx("field-input c-flex c-flex-1 c-flex-col")}> <div className={cx("field-input c-flex c-flex-1 c-flex-col")}>
<FieldInput <FieldInput
field={field} field={field}

View File

@ -7,6 +7,7 @@ import { FieldTypeInput, PropTypeInput } from "./type/TypeInput";
import { FieldLink } from "./type/TypeLink"; import { FieldLink } from "./type/TypeLink";
import { MultiOption } from "./type/TypeMultiOption"; import { MultiOption } from "./type/TypeMultiOption";
import { SingleOption } from "./type/TypeSingleOption"; import { SingleOption } from "./type/TypeSingleOption";
import { TableListEdit } from "app/comps/table-list-edit/TableListEdit";
export const FieldInput: FC<{ export const FieldInput: FC<{
field: FieldLocal; field: FieldLocal;
@ -58,6 +59,7 @@ export const FieldInput: FC<{
child: get(_item, "edit.props.child.value") as PrasiItem, child: get(_item, "edit.props.child.value") as PrasiItem,
bottom: childsTableEdit.find((e) => e.name === "bottom") as PrasiItem, bottom: childsTableEdit.find((e) => e.name === "bottom") as PrasiItem,
}; };
console.log({ tableEdit });
table_edit = ( table_edit = (
<TableEdit <TableEdit
on_init={() => { on_init={() => {
@ -74,6 +76,35 @@ export const FieldInput: FC<{
); );
} }
let table_list_edit = null;
if (type_field === "multi-option" && arg.sub_type === "table-list-edit") {
const childsTableEdit = get(
_item,
"edit.props.child.value.childs"
) as unknown as Array<PrasiItem>;
const tableListEdit = {
child: get(_item, "edit.props.child.value") as PrasiItem,
bottom: childsTableEdit.find((e) => e.name === "bottom") as PrasiItem,
};
table_list_edit = (
<TableListEdit
on_init={() => {
return fm;
}}
field = {field}
arg={arg}
show_header={arg.tbl_show_header}
name={arg.name}
child={child}
PassProp={PassProp}
item={_item}
bottom={tableListEdit.bottom}
body={tableListEdit.child}
fm={fm}
/>
);
}
let not_ready: any = false; let not_ready: any = false;
if ( if (
arg.type === "multi-option" && arg.type === "multi-option" &&
@ -184,6 +215,8 @@ export const FieldInput: FC<{
) : ["multi-option"].includes(type_field) ? ( ) : ["multi-option"].includes(type_field) ? (
arg.sub_type === "table-edit" ? ( arg.sub_type === "table-edit" ? (
table_edit table_edit
) : arg.sub_type === "table-list-edit" ? (
table_list_edit
) : ( ) : (
<MultiOption arg={arg} field={field} fm={fm} /> <MultiOption arg={arg} field={field} fm={fm} />
) )

View File

@ -1,36 +1,62 @@
import { FC } from "react"; import { FC, useEffect } from "react";
import { FMLocal, FieldLocal } from "../typings"; import { FMLocal, FieldLocal, FieldProp } from "../typings";
export const Label: FC<{ field: FieldLocal; fm: FMLocal }> = ({ export const Label: FC<{ field: FieldLocal; fm: FMLocal; arg: FieldProp }> = ({
field, field,
fm, fm,
arg,
}) => { }) => {
const errors = fm.error.get(field.name); const errors = fm.error.get(field.name);
const disabled = const disabled =
typeof field.disabled === "function" ? field.disabled() : field.disabled; typeof field.disabled === "function" ? field.disabled() : field.disabled;
useEffect(() => {
if (field.name === "complete_description")
console.log("log", field.required);
}, []);
const required =
typeof arg.required === "string"
? arg.required === "y"
: typeof arg.required === "function"
? arg.required()
: false;
if (typeof required === "boolean" && field.required !== required) {
field.required = required;
field.render();
}
return ( return (
<div className={cx("label c-text-sm c-flex c-items-center", "c-mt-3")}> <div
<span className={cx(errors.length > 0 && `c-text-red-600`)}> className={cx(
{field.label} "label c-text-sm c-flex c-items-center",
</span> "c-mt-3 c-w-full c-justify-between"
{field.required && !disabled && ( )}
<span className="c-text-red-600 c-mb-2 c-ml-1"> >
<svg <div>
xmlns="http://www.w3.org/2000/svg" <span className={cx(errors.length > 0 && `c-text-red-600`)}>
width="12" {field.label}
height="12"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M12 6v12" />
<path d="M17.196 9 6.804 15" />
<path d="m6.804 9 10.392 6" />
</svg>
</span> </span>
{required && !disabled && (
<span className="c-text-red-600 c-mb-2 c-ml-1">
<svg
xmlns="http://www.w3.org/2000/svg"
width="12"
height="12"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M12 6v12" />
<path d="M17.196 9 6.804 15" />
<path d="m6.804 9 10.392 6" />
</svg>
</span>
)}
</div>
{field.label_action && (
<div className="c-self-end">{field.label_action}</div>
)} )}
</div> </div>
); );

View File

@ -71,96 +71,106 @@ export const TableEdit: FC<{
const label = getProp(child, "title", ""); const label = getProp(child, "title", "");
const type = getProp(child, "type", ""); const type = getProp(child, "type", "");
const width = parseInt(getProp(child, "width", {})); const width = parseInt(getProp(child, "width", {}));
if (type === "checkbox") { const show = getProp(child, "show", {});
const on_click = getProp(child, "opt__on_click", ""); let is_show =
columns.push({ typeof show === "function"
key, ? show({ data: fm.data })
name, : typeof show === "string"
width: 35, ? show === "n"
minWidth: 45, ? false
resizable: true, : true
sortable: true, : true;
frozen: true, if (is_show)
renderCell(arg: any) { if (type === "checkbox") {
const { props, tbl } = arg; const on_click = getProp(child, "opt__on_click", "");
local.tbl = tbl; columns.push({
const key = props.column.key; key,
return ( name,
<PassProp width: 35,
idx={props.rowIdx} minWidth: 45,
row={props.row} resizable: true,
col={{ sortable: true,
name: key, frozen: true,
value: props.row[props.column.key], renderCell(arg: any) {
depth: props.row.__depth || 0, const { props, tbl } = arg;
}} local.tbl = tbl;
rows={tbl.data} const key = props.column.key;
fm={props.fm} return (
fm_parent={parent} <PassProp
ext_fm={{ idx={props.rowIdx}
idx: props.rowIdx, row={props.row}
change: () => {}, col={{
remove: () => { name: key,
const data = value: props.row[props.column.key],
tbl.data.filter((e: any) => e !== props.row) || []; depth: props.row.__depth || 0,
fm.data[name] = data; }}
fm.render(); rows={tbl.data}
}, fm={props.fm}
add: (e: any) => { fm_parent={parent}
tbl.data.push(e ? e : {}); ext_fm={{
fm.render(); idx: props.rowIdx,
}, change: () => {},
}} remove: () => {
> const data =
{child} tbl.data.filter((e: any) => e !== props.row) || [];
</PassProp> fm.data[name] = data;
); fm.render();
}, },
}); add: (e: any) => {
} else { tbl.data.push(e ? e : {});
columns.push({ fm.render();
key, },
label, }}
width: width > 0 ? width : undefined, >
resizable: true, {child}
sortable: true, </PassProp>
renderCell(arg: any) { );
const { props, tbl } = arg; },
local.tbl = tbl; });
const key = props.column.key; } else {
return ( columns.push({
<PassProp key,
idx={props.rowIdx} label,
row={props.row} width: width > 0 ? width : undefined,
col={{ resizable: true,
name: key, sortable: true,
value: props.row[props.column.key], renderCell(arg: any) {
depth: props.row.__depth || 0, const { props, tbl } = arg;
}} local.tbl = tbl;
rows={tbl.data} const key = props.column.key;
fm={props.fm} return (
fm_parent={parent} <PassProp
ext_fm={{ idx={props.rowIdx}
idx: props.rowIdx, row={props.row}
change: () => {}, col={{
remove: () => { name: key,
const data = value: props.row[props.column.key],
tbl.data.filter((e: any) => e !== props.row) || []; depth: props.row.__depth || 0,
fm.data[name] = data; }}
fm.render(); rows={tbl.data}
}, fm={props.fm}
add: (e: any) => { fm_parent={parent}
tbl.data.push(e ? e : {}); ext_fm={{
fm.render(); idx: props.rowIdx,
}, change: () => {},
}} remove: () => {
> const data =
{child} tbl.data.filter((e: any) => e !== props.row) || [];
</PassProp> fm.data[name] = data;
); fm.render();
}, },
}); add: (e: any) => {
} tbl.data.push(e ? e : {});
fm.render();
},
}}
>
{child}
</PassProp>
);
},
});
}
} }
return ( return (
<> <>
@ -190,6 +200,7 @@ export const TableEdit: FC<{
` `
)} )}
> >
{/* {JSON.stringify(value.map((e) => e.id))} */}
<table <table
className={cx( className={cx(
"c-table-auto", "c-table-auto",
@ -255,15 +266,7 @@ export const TableEdit: FC<{
<> <>
{value.map((row: any, idx: number) => { {value.map((row: any, idx: number) => {
return ( return (
<BaseForm <BaseForm is_form={false} data={row}>
is_form={false}
data={row}
on_change={(_fm) => {
fm.data[name][idx] = _fm.data;
fm.data[name] = [...fm.data[name]];
fm.render();
}}
>
{(form) => { {(form) => {
return ( return (
<tr> <tr>
@ -289,7 +292,10 @@ export const TableEdit: FC<{
row: row, row: row,
rowIdx: idx, rowIdx: idx,
column: header, column: header,
fm: form.fm, fm: {
...form.fm,
data: row,
},
}, },
tbl: { tbl: {
data: value, data: value,

View File

@ -29,10 +29,10 @@ export const FieldButton: FC<{
if (arg.type === "multi-option") { if (arg.type === "multi-option") {
return ( return (
<> <>
<div className={cx("c-flex c-items-center c-w-full c-flex-row")}> <div className={cx("c-flex c-items-center c-w-full c-flex-row c-max-w-full")}>
<div <div
className={cx( className={cx(
`c-flex`, `c-flex c-flex-wrap`,
css` css`
gap: 0.5rem; gap: 0.5rem;
` `
@ -58,10 +58,11 @@ export const FieldButton: FC<{
} else { } else {
selected.push(item); selected.push(item);
} }
if(selected.length) selected = selected.filter((e) => e)
arg.opt_set_value({ arg.opt_set_value({
fm, fm,
name: field.name, name: field.name,
selected: selected.map((e) => e.value), selected: selected.length ? selected.map((e) => e.value) : [],
options: local.list, options: local.list,
type: field.type, type: field.type,
}); });

View File

@ -9,6 +9,7 @@ export const TypeDropdown: FC<{
fm: FMLocal; fm: FMLocal;
arg: FieldProp; arg: FieldProp;
}> = ({ field, fm, arg }) => { }> = ({ field, fm, arg }) => {
const local = useLocal({ const local = useLocal({
loaded: false, loaded: false,
options: [] as { value: string; label: string; data: any }[], options: [] as { value: string; label: string; data: any }[],
@ -164,7 +165,14 @@ export const TypeDropdown: FC<{
} }
const disabled = const disabled =
typeof field.disabled === "function" ? field.disabled() : field.disabled; typeof field.disabled === "function" ? field.disabled() : field.disabled;
const disabled_search =
typeof field.disabled_search === "function"
? field.disabled_search()
: typeof field.disabled_search === "string"
? field.disabled_search === "n"
? true
: false
: field.disabled_search;
if (!local.loaded) return <FieldLoading />; if (!local.loaded) return <FieldLoading />;
if (field.type === "single-option") { if (field.type === "single-option") {
@ -179,6 +187,7 @@ export const TypeDropdown: FC<{
<> <>
<Typeahead <Typeahead
value={typeahead_val} value={typeahead_val}
disabledSearch={disabled_search ? true : false}
popupClassName={popupClassName} popupClassName={popupClassName}
onSelect={({ search, item }) => { onSelect={({ search, item }) => {
if (item) { if (item) {

View File

@ -117,7 +117,6 @@ export const FieldTypeInput: FC<{
case "toggle": case "toggle":
return ( return (
<div className="c-px-2"> <div className="c-px-2">
ad
<div <div
className={cx( className={cx(
"c-relative", "c-relative",
@ -222,6 +221,10 @@ export const FieldTypeInput: FC<{
? new Date(value?.startDate) ? new Date(value?.startDate)
: null; : null;
renderOnChange(); renderOnChange();
if (prop.onChange) {
prop.onChange(fm.data[field.name]);
}
}} }}
/> />
); );

View File

@ -80,6 +80,11 @@ export const FieldLink: FC<{
if (!(await navigateLink(arg.link, field, fm.deps.md))) { if (!(await navigateLink(arg.link, field, fm.deps.md))) {
link_local.navigating = false; link_local.navigating = false;
link_local.render(); link_local.render();
setTimeout(() => {
local.init = true;
local.render();
}, 1000)
} }
} }
}} }}
@ -168,10 +173,11 @@ export type LinkParam = {
}[]; }[];
}; };
const navigateLink = async ( export const navigateLink = async (
link: FieldProp["link"], link: FieldProp["link"],
field: FieldLocal, field: FieldLocal,
md?: MDLocal md?: MDLocal,
prm? :any
) => { ) => {
let params = link.params(field) as { where: any; create: any; update: any }; let params = link.params(field) as { where: any; create: any; update: any };
@ -217,20 +223,26 @@ const navigateLink = async (
values.hash = vhash; values.hash = vhash;
link_cache[vhash] = values; link_cache[vhash] = values;
if (typeof link.url === "function") {
if (!link.url) { if (md) {
alert("No URL defined!"); link.url(md.selected[md.pk?.name || ""]);
}
return false; return false;
} } else {
if (!link.url) {
alert("No URL defined!");
return false;
}
await api._kv("set", vhash, values); await api._kv("set", vhash, values);
const lnk = location.hash.split("#").find((e) => e.startsWith("lnk=")); const lnk = location.hash.split("#").find((e) => e.startsWith("lnk="));
let prev_link = ""; let prev_link = "";
if (lnk) { if (lnk) {
prev_link = lnk.split("=").pop() || ""; prev_link = lnk.split("=").pop() || "";
if (prev_link) prev_link = prev_link + "+"; if (prev_link) prev_link = prev_link + "+";
} }
navigate(`${link.url}#lnk=${prev_link + vhash}`); navigate(`${link.url}#lnk=${prev_link + vhash}${prm}`);
return true; return true;
}
}; };

View File

@ -58,7 +58,7 @@ export const FieldMoney: FC<{
input.render(); input.render();
} }
}} }}
value={formatCurrency(input.value)} value={formatCurrency(input.value) || 0}
disabled={disabled} disabled={disabled}
className={cx( className={cx(
"c-flex-1 c-bg-transparent c-outline-none c-px-2 c-text-sm c-w-full" "c-flex-1 c-bg-transparent c-outline-none c-px-2 c-text-sm c-w-full"

View File

@ -46,6 +46,7 @@ type FieldType =
export type FieldProp = { export type FieldProp = {
name: string; name: string;
label: string; label: string;
label_action: ReactNode;
desc?: string; desc?: string;
props?: any; props?: any;
mask?: string; mask?: string;
@ -63,7 +64,7 @@ export type FieldProp = {
field: FieldLocal; field: FieldLocal;
Link: FC<{ children: any }>; Link: FC<{ children: any }>;
}) => Promise<string> | string); }) => Promise<string> | string);
url: string; url: string | ((e?: any) => void);
params: (field: FieldLocal) => { where: any } | Promise<{ where: any }>; params: (field: FieldLocal) => { where: any } | Promise<{ where: any }>;
}; };
fm: FMLocal; fm: FMLocal;
@ -127,6 +128,8 @@ export type FieldProp = {
max_date?: any; max_date?: any;
min_date?: any; min_date?: any;
upload_style?: "inline" | "full"; upload_style?: "inline" | "full";
disabled_search?: (() => Promise<boolean> | boolean) | boolean | "y" | "n";
show?: (() => boolean) | boolean | "y" | "n";
}; };
export type FMInternal = { export type FMInternal = {
@ -170,13 +173,14 @@ export type FieldInternal<T extends FieldProp["type"]> = {
name: FieldProp["name"]; name: FieldProp["name"];
type: T | (() => T); type: T | (() => T);
label: FieldProp["label"]; label: FieldProp["label"];
label_action: FieldProp["label_action"];
desc: FieldProp["desc"]; desc: FieldProp["desc"];
prefix: FieldProp["prefix"]; prefix: FieldProp["prefix"];
suffix: FieldProp["suffix"]; suffix: FieldProp["suffix"];
width: FieldProp["width"]; width: FieldProp["width"];
required: boolean; required: boolean;
focused: boolean; focused: boolean;
hidden: boolean; hidden: (() => boolean) | boolean | "y" | "n";
disabled: boolean | (() => boolean); disabled: boolean | (() => boolean);
required_msg: FieldProp["required_msg"]; required_msg: FieldProp["required_msg"];
col?: GFCol; col?: GFCol;
@ -200,6 +204,7 @@ export type FieldInternal<T extends FieldProp["type"]> = {
min_date?: FieldProp["min_date"]; min_date?: FieldProp["min_date"];
error?: any; error?: any;
table_fields?: any[]; table_fields?: any[];
disabled_search?: (() => Promise<boolean> | boolean) | boolean | "y" | "n";
}; };
export type FieldLocal = FieldInternal<any> & { export type FieldLocal = FieldInternal<any> & {
render: () => void; render: () => void;

View File

@ -25,7 +25,6 @@ export const formInit = (fm: FMLocal, props: FMProps) => {
const toastSuccess = (opt: { addNewText: string }) => { const toastSuccess = (opt: { addNewText: string }) => {
const md = fm.deps.md as MDLocal; const md = fm.deps.md as MDLocal;
fm.save_status = "saved"; fm.save_status = "saved";
if (md) { if (md) {
toast.success( toast.success(
<div <div
@ -110,7 +109,7 @@ export const formInit = (fm: FMLocal, props: FMProps) => {
</div> </div>
</div>, </div>,
{ {
duration: 60 * 1000, duration:3000,
className: css` className: css`
background: #e4ffed; background: #e4ffed;
border: 2px solid green; border: 2px solid green;
@ -135,7 +134,7 @@ export const formInit = (fm: FMLocal, props: FMProps) => {
</div> </div>
</div>, </div>,
{ {
duration: 60 * 1000, duration:3000,
className: css` className: css`
background: #e4ffed; background: #e4ffed;
border: 2px solid green; border: 2px solid green;
@ -233,7 +232,6 @@ export const formInit = (fm: FMLocal, props: FMProps) => {
</> </>
); );
} }
const form = JSON.parse(JSON.stringify(fm.data)); const form = JSON.parse(JSON.stringify(fm.data));
if (fm.deps.md) { if (fm.deps.md) {
@ -291,6 +289,7 @@ export const formInit = (fm: FMLocal, props: FMProps) => {
} }
); );
} else { } else {
toastSuccess({ addNewText: lang.t("Add New") }); toastSuccess({ addNewText: lang.t("Add New") });
} }
} }

View File

@ -21,18 +21,20 @@ export const useField = (
const name = typeof arg.name === "string" ? arg.name : arg.name(); const name = typeof arg.name === "string" ? arg.name : arg.name();
const label = typeof arg.label === "string" ? arg.label : arg.label(); const label = typeof arg.label === "string" ? arg.label : arg.label();
const required = const required =
typeof arg.required === "string" ? arg.required : arg.required(); typeof arg.required === "string" ? arg.required === "y" : arg.required();
const update_field = { const update_field = {
name: name.replace(/\s*/gi, ""), name: name.replace(/\s*/gi, ""),
label: label, label: label,
label_action: arg.label_action,
type: arg.type, type: arg.type,
desc: arg.desc, desc: arg.desc,
prefix: arg.prefix, prefix: arg.prefix,
suffix: arg.suffix, suffix: arg.suffix,
width: arg.width, width: arg.width,
custom: arg.custom, custom: arg.custom,
required: required === "y", required: typeof required === "string" ? required === "y" : required,
required_msg: arg.required_msg, required_msg: arg.required_msg,
disabled: disabled:
typeof arg.disabled === "function" ? arg.disabled : arg.disabled === "y", typeof arg.disabled === "function" ? arg.disabled : arg.disabled === "y",
@ -40,8 +42,11 @@ export const useField = (
max_date: arg.max_date, max_date: arg.max_date,
min_date: arg.min_date, min_date: arg.min_date,
table_fields: [], table_fields: [],
disabled_search: arg.disabled_search,
hidden: arg.show
}; };
if (field.status === "init" || isEditor) { if (field.status === "init" || isEditor) {
for (const [k, v] of Object.entries(update_field)) { for (const [k, v] of Object.entries(update_field)) {
(field as any)[k] = v; (field as any)[k] = v;
@ -49,7 +54,6 @@ export const useField = (
} }
const fm = arg.fm; const fm = arg.fm;
useEffect(() => { useEffect(() => {
if (field.status === "init" || !fm.fields[name]) { if (field.status === "init" || !fm.fields[name]) {
field.status = "ready"; field.status = "ready";
@ -60,7 +64,10 @@ export const useField = (
field.render(); field.render();
} }
}, []); }, []);
field.prop = arg as any; field.prop = arg as any;
if(field.name === "complete_description") console.log(update_field);
if(field.name === "complete_description") console.log(field);
return field; return field;
}; };

View File

@ -84,7 +84,7 @@ export const Import: FC<{
task, task,
}: { }: {
list: any[]; list: any[];
task: (e: any) => Promise<void>; task: (e: any, index: number) => Promise<void>;
}) => { }) => {
let n = 0; let n = 0;
while (n < list.length) { while (n < list.length) {
@ -99,7 +99,7 @@ export const Import: FC<{
}); });
} }
await task(list[n]); await task(list[n], n);
await sleep(100); await sleep(100);
n++; n++;
} }
@ -108,9 +108,9 @@ export const Import: FC<{
if (local.progress <= 0) { if (local.progress <= 0) {
const res = startImport({ const res = startImport({
list: local.data, list: local.data,
task: async (e) => { task: async (e, index) => {
if (typeof task === "function") { if (typeof task === "function") {
const result = await task({data: e, excel}); const result = await task({data: e, excel, index, list: local.data, render: local.render});
if (typeof result === "boolean") { if (typeof result === "boolean") {
if (!result) { if (!result) {
local.recap.failed.push(e); local.recap.failed.push(e);

View File

@ -183,6 +183,7 @@ export const TableList: FC<TableListProp> = ({
reloading: null as any, reloading: null as any,
reload: (arg?: { toast: boolean }) => { reload: (arg?: { toast: boolean }) => {
if (local.reloading) return local.reloading; if (local.reloading) return local.reloading;
local.reloading = new Promise<void>(async (done) => { local.reloading = new Promise<void>(async (done) => {
let should_toast = true; let should_toast = true;
if (arg?.toast === false) should_toast = false; if (arg?.toast === false) should_toast = false;
@ -217,101 +218,99 @@ export const TableList: FC<TableListProp> = ({
} }
if (md) { if (md) {
if (md.header.loading) { await new Promise<void>((resolve) => {
await new Promise<void>((resolve) => { const ival = setInterval(() => {
const ival = setInterval(() => { if (!md.header.loading) {
if (!md.header.loading) { clearInterval(ival);
clearInterval(ival); resolve();
resolve(); }
} }, 10);
}, 10); });
}); if (Array.isArray(md?.params?.links) && md?.params?.links?.length) {
} const last = md.params.links[md.params.links.length - 1];
const last = md.params.links[md.params.links.length - 1];
if (last && last.where) { if (last && last.where) {
if ((last.name && last.name === md.name) || !last.name) { if ((last.name && last.name === md.name) || !last.name) {
for (const [k, v] of Object.entries(last.where)) { for (const [k, v] of Object.entries(last.where)) {
where[k] = v; where[k] = v;
}
} }
} }
} }
} }
call_prasi_events("tablelist", "where", [ call_prasi_events("tablelist", "where", [__props?.gen__table, where]);
__props?.gen__table,
where,
]);
const load_args: any = { const load_args: any = {
async reload() {}, async reload() {},
orderBy, orderBy,
where, where,
paging: { paging: {
take: local.paging.take > 0 ? local.paging.take : undefined, take: local.paging.take > 0 ? local.paging.take : undefined,
skip: local.paging.skip, skip: local.paging.skip,
}, },
}; };
if (id_parent) { if (id_parent) {
load_args.paging = {}; 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);
} }
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; return local.reloading;
@ -557,7 +556,7 @@ export const TableList: FC<TableListProp> = ({
if (typeof local.data === "string") { if (typeof local.data === "string") {
console.error(local.data); console.error(local.data);
local.data = [] local.data = [];
} }
if (isEditor) { if (isEditor) {
@ -579,163 +578,49 @@ export const TableList: FC<TableListProp> = ({
return true; return true;
}); });
} }
if (childs.length && isCheckbox) {
columns.push({
key: SELECT_COLUMN_KEY,
name: "",
width: 35,
minWidth: 35,
maxWidth: 35,
resizable: false,
sortable: false,
frozen: true,
renderHeaderCell(props) {
return <input type="checkbox" onChange={headerCheckboxClick} />;
},
renderCell(props) {
// digunakan untuk mengecek apakah local selected rows memiliki pk dari props.row.id
const isChecked = local.selectedRows.some(
(checked) => checked.pk === props.row.id
);
return (
<div
onClick={checkboxClick(props.row.id)}
className={cx(
css`
width: 100%;
height: 100%;
`,
"c-flex c-items-center c-justify-center"
)}
>
<input
className="c-pointer-events-none"
type="checkbox"
checked={isChecked}
/>
</div>
);
},
headerCellClass: selectCellClassname,
cellClass: selectCellClassname,
});
}
let first = true; let first = true;
for (const child of childs) { for (const child of childs) {
let key = getProp(child, "name", {}); let key = getProp(child, "name", {});
const name = getProp(child, "title", ""); const name = getProp(child, "title", "");
const type = getProp(child, "type", ""); const sort = getProp(child, "sortir", "");
const width = parseInt(getProp(child, "width", {})); let show = getProp(child, "show", "");
if (type === "checkbox") { if (typeof show === "function") {
columns.push({ show = show();
key, if (typeof show === "object" && show instanceof Promise) {
name, show.then((e) => {
width: 35, show = e;
minWidth: 45, });
resizable: true, }
sortable: true, }
frozen: true, show = show === "n" ? false : show;
renderHeaderCell(props) { if (show) {
return ( const type = getProp(child, "type", "");
<div> const width = parseInt(getProp(child, "width", {}));
{/* <CheckboxList value={false} on_click={on_click} /> */} if (type === "checkbox") {
</div> columns.push({
); key,
}, name,
renderCell(props) { width: 35,
if (typeof render_col === "function") minWidth: 45,
return render_col({ resizable: true,
props, sortable: sort === "n" ? false : true,
tbl: local, frozen: true,
child, renderHeaderCell(props) {
}); return (
<div>
{/* <CheckboxList value={false} on_click={on_click} /> */}
</div>
);
},
renderCell(props) {
if (typeof render_col === "function")
return render_col({
props,
tbl: local,
child,
});
return ( return (
<PassProp
idx={props.rowIdx}
row={props.row}
col={{
name: props.column.key,
value: get(props.row, props.column.key),
depth: props.row.__depth || 0,
}}
rows={local.data}
>
{child}
</PassProp>
);
},
});
} else {
columns.push({
key,
name,
width: width > 0 ? width : undefined,
resizable: true,
sortable: true,
renderCell(props) {
if (typeof render_col === "function")
return render_col({
props,
tbl: local,
child,
});
return (
<>
{isTree && local.firstKey === key && local.pk && (
<div
className={cx(
css`
padding-left: ${3 + props.row.__depth * 8}px;
`,
"c-flex c-items-center c-cursor-pointer"
)}
onClick={(e) => {
if (!local.pk) return;
if (props?.row?.__children?.length > 0) {
e.stopPropagation();
if (!local.collapsed.has(props.row?.[local.pk.name])) {
local.collapsed.add(props.row?.[local.pk.name]);
} else {
local.collapsed.delete(props.row?.[local.pk.name]);
}
local.render();
}
}}
>
<div
className={cx(
css`
width: 16px;
`
)}
>
{props.row?.__children?.length > 0 && (
<>
{local.collapsed.has(props.row?.[local.pk.name]) ? (
<ChevronRight size={16} />
) : (
<ChevronDown size={16} />
)}
</>
)}
</div>
{props.row?.__parent &&
(props.row?.__children || []).length === 0 && (
<div
className={cx(
" c-border-l c-border-b c-border-black c-w-[10px] c-h-[15px]",
css`
margin-top: -10px;
`
)}
></div>
)}
</div>
)}
<PassProp <PassProp
idx={props.rowIdx} idx={props.rowIdx}
row={props.row} row={props.row}
@ -748,14 +633,160 @@ export const TableList: FC<TableListProp> = ({
> >
{child} {child}
</PassProp> </PassProp>
</> );
); },
}, });
}); } else {
columns.push({
key,
name,
width: width > 0 ? width : undefined,
resizable: true,
sortable: sort === "n" ? false : true,
renderHeaderCell(props) {
return (
<div
className="flex flex-row flex-grow items-center"
onClick={() => {
const msg = `The ${props?.column?.name} column cannot be sorted!`;
if (!props?.column?.sortable) {
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" />
{msg}
</div>,
{
dismissible: true,
className: css`
background: #ffecec;
border: 2px solid red;
`,
}
);
}
}}
>
{props?.column?.name}
{props.sortDirection ? (
<>
{" "}
<div className="px-1">
{props.sortDirection === "ASC" ? (
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M12.53 7.97a.75.75 0 0 0-1.06 0l-7 7A.75.75 0 0 0 5 16.25h14a.75.75 0 0 0 .53-1.28z"
/>
</svg>
) : (
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M5 7.75a.75.75 0 0 0-.53 1.28l7 7a.75.75 0 0 0 1.06 0l7-7A.75.75 0 0 0 19 7.75z"
/>
</svg>
)}
</div>
</>
) : (
<></>
)}
</div>
);
},
renderCell(props) {
if (typeof render_col === "function")
return render_col({
props,
tbl: local,
child,
});
if (first) { return (
first = false; <>
local.firstKey = key; {isTree && local.firstKey === key && local.pk && (
<div
className={cx(
css`
padding-left: ${3 + props.row.__depth * 8}px;
`,
"c-flex c-items-center c-cursor-pointer"
)}
onClick={(e) => {
if (!local.pk) return;
if (props?.row?.__children?.length > 0) {
e.stopPropagation();
if (!local.collapsed.has(props.row?.[local.pk.name])) {
local.collapsed.add(props.row?.[local.pk.name]);
} else {
local.collapsed.delete(props.row?.[local.pk.name]);
}
local.render();
}
}}
>
<div
className={cx(
css`
width: 16px;
`
)}
>
{props.row?.__children?.length > 0 && (
<>
{local.collapsed.has(props.row?.[local.pk.name]) ? (
<ChevronRight size={16} />
) : (
<ChevronDown size={16} />
)}
</>
)}
</div>
{props.row?.__parent &&
(props.row?.__children || []).length === 0 && (
<div
className={cx(
" c-border-l c-border-b c-border-black c-w-[10px] c-h-[15px] rows",
css`
margin-top: -10px;
`
)}
></div>
)}
</div>
)}
<PassProp
idx={props.rowIdx}
row={props.row}
col={{
name: props.column.key,
value: get(props.row, props.column.key),
depth: props.row.__depth || 0,
}}
rows={local.data}
>
{child}
</PassProp>
</>
);
},
});
if (first) {
first = false;
local.firstKey = key;
}
} }
} }
} }

View File

@ -25,9 +25,12 @@ export const generateTableList = async (
)[]; )[];
let pk = ""; let pk = "";
let pks: Record<string, string> = {}; let pks: Record<string, string> = {};
// console.log(raw_fields)
const fields = parseGenField(raw_fields); const fields = parseGenField(raw_fields);
// convert ke bahasa prisma untuk select // convert ke bahasa prisma untuk select
const res = generateSelect(fields); const res = generateSelect(fields);
console.log({res})
console.log({fields})
pk = res.pk; pk = res.pk;
const select = res.select as any; const select = res.select as any;
if (!pk) { if (!pk) {

View File

@ -117,7 +117,6 @@ idx: any;
} }
`, `,
}); });
tab_master?.edit.setChilds([ tab_master?.edit.setChilds([
{ {
type: "item", type: "item",

View File

@ -22,9 +22,11 @@ export const SheetCn: FC<{
header: string; header: string;
open: string; open: string;
close?: string; close?: string;
mode?: "full" | "normal"; mode?: "full" | "normal" | "1/3";
direction?: "left" | "right" | "bottom" | "top"; direction?: "left" | "right" | "bottom" | "top";
deps?: any; deps?: any;
_item?: PrasiItem;
onInit?: (e: any) => void;
}> = ({ }> = ({
child, child,
PassProp, PassProp,
@ -35,10 +37,13 @@ export const SheetCn: FC<{
close, close,
mode, mode,
direction, direction,
deps deps,
_item,
onInit,
}) => { }) => {
const local = useLocal({ const local = useLocal({
open: false, open: false,
data: null as any,
}); });
useEffect(() => { useEffect(() => {
if (isEditor) { if (isEditor) {
@ -47,6 +52,25 @@ export const SheetCn: FC<{
local.open = op; local.open = op;
local.render(); local.render();
} }
} else {
console.log(typeof onInit)
if (typeof onInit === "function") {
onInit({
data: local,
deps,
open: () => {
local.open = true;
local.render();
setTimeout(() => {
document.body.style.pointerEvents = "auto";
}, 1000);
},
close: () => {
local.open = false;
local.render();
},
});
}
} }
}, [open]); }, [open]);
return ( return (
@ -55,6 +79,7 @@ export const SheetCn: FC<{
<div {...props} className={cx(props.className, "")}> <div {...props} className={cx(props.className, "")}>
<PassProp <PassProp
sheet={{ sheet={{
data: local,
deps, deps,
open: () => { open: () => {
local.open = true; local.open = true;
@ -89,57 +114,79 @@ export const SheetCn: FC<{
<SheetContent <SheetContent
side={direction} side={direction}
className={cx( className={cx(
mode === "full" ? "c-w-screen" : "sm:s-max-w-[425px]", mode === "1/3"
? css`
max-width: 500px !important;
`
: mode === "full"
? "c-w-screen"
: "sm:s-max-w-[425px]",
css` css`
z-index: 2; z-index: 2;
` `
)} )}
> >
{close !== "n" ? ( <div className="c-relative">
<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-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`
z-index: 99; right: 1rem;
` z-index: 99;
)} transform: translateY(50%);
> `
<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" />
) : ( <span className="c-sr-only">Close</span>
<></> </SheetPrimitive.Close>
)} ) : (
{header !== "" && header ? ( <></>
<SheetHeader )}
className={cx( {header !== "" && header ? (
css` <SheetHeader
padding: 0px 0px 0px 10px; className={cx(
` "c-text-base",
)} css`
> padding: 0px 0px 0px 10px;
<SheetTitle>{header}</SheetTitle> `
</SheetHeader> )}
) : ( >
<> <SheetTitle
<SheetHeader className={cx(
className={cx( "c-text-base",
css` css`
padding: 0px; font-size: 1rem !important;
` line-height: 1.5rem !important;
)} `
> )}
<SheetTitle></SheetTitle> >
</SheetHeader></> {header}
)} </SheetTitle>
</SheetHeader>
) : (
<>
<SheetHeader
className={cx(
css`
padding: 0px;
`
)}
>
<SheetTitle></SheetTitle>
</SheetHeader>
</>
)}
</div>
<PassProp <PassProp
sheet={{ sheet={{
data: local,
deps, deps,
open: () => { open: () => {
local.open = true; local.open = true;

44
comps/slide/Slide.tsx Executable file
View File

@ -0,0 +1,44 @@
import { useLocal } from "lib/utils/use-local";
import * as SheetPrimitive from "@radix-ui/react-dialog";
import { X } from "lucide-react";
import { FC, useEffect } from "react";
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "lib/comps/ui/carousel";
export const Slide: FC<{
child: any;
PassProp: any;
props: any;
mode?: "full" | "normal";
direction?: "vertical" | "horizontal";
deps?: any;
_item?: PrasiItem;
data: any;
}> = ({ child, PassProp, props, mode, direction, deps, _item, data }) => {
return (
<Carousel className="c-flex c-flex-row c-flex-grow c-flex-1 ">
<CarouselContent>
{Array.isArray(data) ? (
<>
{data.map((e, idx) => {
return (
<CarouselItem>
<PassProp item={e} idx={idx} slide={{ deps }}>
{child}
</PassProp>
</CarouselItem>
);
})}
</>
) : (
<></>
)}
</CarouselContent>
</Carousel>
);
};

View File

@ -2,9 +2,10 @@ import { useLocal } from "lib/utils/use-local";
import get from "lodash.get"; import get from "lodash.get";
import { FC, ReactNode, useEffect } from "react"; import { FC, ReactNode, useEffect } from "react";
import { PTLocalInternal, PTProp } from "../utils/typings"; import { PTLocalInternal, PTProp } from "../utils/typings";
export const PanelTab: FC<PTProp> = ({ header, body, tab, PassProp, item }) => { export const PanelTab: FC<PTProp> = ({ header, body, tab, PassProp, item, deps }) => {
const local = useLocal<PTLocalInternal>({ const local = useLocal<PTLocalInternal>({
mode: "" as any, mode: "" as any,
deps
}); });
useEffect(() => { useEffect(() => {
local.mode = tab; local.mode = tab;

View File

@ -10,9 +10,11 @@ export type PTProp = {
PassProp: any; PassProp: any;
item: PrasiItem; item: PrasiItem;
pt: PTLocalInternal; pt: PTLocalInternal;
deps?: any;
}; };
export type PTLocalInternal = { export type PTLocalInternal = {
mode: string; mode: string;
deps?: any
}; };
export type PTLocal = PTLocalInternal & { render: (force?: boolean) => void }; export type PTLocal = PTLocalInternal & { render: (force?: boolean) => void };

7
comps/ui/aspect-ratio.tsx Executable file
View File

@ -0,0 +1,7 @@
"use client"
import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"
const AspectRatio = AspectRatioPrimitive.Root
export { AspectRatio }

View File

@ -155,7 +155,7 @@ const CarouselContent = React.forwardRef<
const { carouselRef, orientation } = useCarousel() const { carouselRef, orientation } = useCarousel()
return ( return (
<div ref={carouselRef} className="c-overflow-hidden"> <div ref={carouselRef} className="c-overflow-hidden c-w-full c-flex-grow c-flex-1">
<div <div
ref={ref} ref={ref}
className={cn( className={cn(

View File

@ -59,7 +59,7 @@ const SheetContent = React.forwardRef<
<SheetPortal> <SheetPortal>
<SheetPrimitive.Content <SheetPrimitive.Content
ref={ref} ref={ref}
className={cn(sheetVariants({ side }), className)} className={cn(sheetVariants({ side }), className, "sheet-content")}
{...props} {...props}
> >
{children} {children}
@ -102,7 +102,7 @@ const SheetTitle = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<SheetPrimitive.Title <SheetPrimitive.Title
ref={ref} ref={ref}
className={cn("c-text-lg c-font-semibold c-text-foreground", className)} className={cn("c-py-2 c-text-lg c-font-semibold c-text-foreground", className)}
{...props} {...props}
/> />
)); ));

View File

@ -25,6 +25,7 @@ export const Typeahead: FC<{
disabled?: boolean; disabled?: boolean;
mode?: "multi" | "single"; mode?: "multi" | "single";
note?: string; note?: string;
disabledSearch?: boolean;
}> = ({ }> = ({
value, value,
note, note,
@ -41,6 +42,7 @@ export const Typeahead: FC<{
onChange, onChange,
className, className,
popupClassName, popupClassName,
disabledSearch
}) => { }) => {
const local = useLocal({ const local = useLocal({
value: [] as string[], value: [] as string[],
@ -440,13 +442,8 @@ export const Typeahead: FC<{
return false; return false;
}} }}
> >
<input <div
placeholder={ className="single c-flex-1 c-flex-grow c-flex c-flex-row c-cursor-pointer c-z-99"
local.mode === "multi" ? placeholder : valueLabel[0]?.label
}
type="text"
ref={input}
value={inputval}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
if (!disabled) { if (!disabled) {
@ -459,75 +456,44 @@ export const Typeahead: FC<{
} }
if (local.mode === "single") { if (local.mode === "single") {
e.currentTarget.select(); if (input && input.current) input.current.select();
} }
} }
}} }}
onChange={async (e) => { >
const val = e.currentTarget.value; <input
if (!local.open) { placeholder={
local.open = true; local.mode === "multi" ? placeholder : valueLabel[0]?.label
} }
type="text"
ref={input}
value={inputval}
onChange={async (e) => {
const val = e.currentTarget.value;
if (!local.open) {
local.open = true;
}
local.search.input = val; local.search.input = val;
local.render(); local.render();
if (local.search.promise) { if (local.search.promise) {
await local.search.promise; await local.search.promise;
} }
local.search.searching = true; local.search.searching = true;
local.render(); local.render();
if (local.search.searching) { if (local.search.searching) {
if (local.local_search) { if (local.local_search) {
if (!local.loaded) { if (!local.loaded) {
await loadOptions(); await loadOptions();
}
const search = local.search.input.toLowerCase();
if (search) {
local.search.result = options.filter((e) =>
e.label.toLowerCase().includes(search)
);
if (
local.search.result.length > 0 &&
!local.search.result.find(
(e) => e.value === local.select?.value
)
) {
local.select = local.search.result[0];
} }
} else { const search = local.search.input.toLowerCase();
local.search.result = null; if (search) {
} local.search.result = options.filter((e) =>
local.search.searching = false; e.label.toLowerCase().includes(search)
local.render(); );
} else {
clearTimeout(local.search.timeout);
local.search.timeout = setTimeout(async () => {
const result = options_fn?.({
search: local.search.input,
existing: options,
});
if (result) {
if (result instanceof Promise) {
local.search.promise = result;
local.search.result = (await result).map((item) => {
if (typeof item === "string")
return { value: item, label: item };
return item;
});
local.search.searching = false;
local.search.promise = null;
} else {
local.search.result = result.map((item) => {
if (typeof item === "string")
return { value: item, label: item };
return item;
});
local.search.searching = false;
}
if ( if (
local.search.result.length > 0 && local.search.result.length > 0 &&
@ -537,21 +503,64 @@ export const Typeahead: FC<{
) { ) {
local.select = local.search.result[0]; local.select = local.search.result[0];
} }
} else {
local.render(); local.search.result = null;
} }
}, 100); local.search.searching = false;
local.render();
} else {
clearTimeout(local.search.timeout);
local.search.timeout = setTimeout(async () => {
const result = options_fn?.({
search: local.search.input,
existing: options,
});
if (result) {
if (result instanceof Promise) {
local.search.promise = result;
local.search.result = (await result).map((item) => {
if (typeof item === "string")
return { value: item, label: item };
return item;
});
local.search.searching = false;
local.search.promise = null;
} else {
local.search.result = result.map((item) => {
if (typeof item === "string")
return { value: item, label: item };
return item;
});
local.search.searching = false;
}
if (
local.search.result.length > 0 &&
!local.search.result.find(
(e) => e.value === local.select?.value
)
) {
local.select = local.search.result[0];
}
local.render();
}
}, 100);
}
} }
} }}
}} disabled={!disabled ? disabledSearch : disabled}
disabled={disabled} spellCheck={false}
spellCheck={false} className={cx(
className={cx( "c-flex-1 c-mb-2 c-outline-none c-bg-transparent",
"c-flex-1 c-mb-2 c-outline-none c-bg-transparent", local.mode === "single" ? "c-cursor-pointer" : ""
local.mode === "single" ? "c-cursor-pointer" : "" )}
)} style={{
onKeyDown={keydown} pointerEvents: disabledSearch ? "none" : "auto", // Mencegah input menangkap klik saat disabled
/> }}
onKeyDown={keydown}
/>
</div>
</TypeaheadOptions> </TypeaheadOptions>
{local.mode === "single" && ( {local.mode === "single" && (
<div <div

View File

@ -20,9 +20,9 @@ export const Typeahead = lazify(
); );
/** Master - Detail - List - Form */ /** Master - Detail - List - Form */
export const MasterDetail = lazify( // export const MasterDetail = lazify(
async () => (await import("lib/comps/md/MasterDetail")).MasterDetail // async () => (await import("lib/comps/md/MasterDetail")).MasterDetail
); // );
export const MDRenderMaster = lazify( export const MDRenderMaster = lazify(
async () => (await import("lib/comps/md/parts/MDMaster")).MDRenderMaster async () => (await import("lib/comps/md/parts/MDMaster")).MDRenderMaster
); );
@ -107,6 +107,13 @@ export const Pop = lazify(
async () => (await import("lib/comps/dialog/Dialog")).Pop async () => (await import("lib/comps/dialog/Dialog")).Pop
); );
export const Slide = lazify(
async () => (await import("lib/comps/slide/Slide")).Slide
);
export const AspectRatio = lazify(
async () => (await import("lib/comps/ui/aspect-ratio")).AspectRatio
);
export const Import = lazify( export const Import = lazify(
async () => (await import("lib/comps/import/Import")).Import async () => (await import("lib/comps/import/Import")).Import
); );
@ -134,11 +141,12 @@ export { kvToJSON } from "./utils/kv-to-json";
export { overrideNav } from "./utils/override-nav"; export { overrideNav } from "./utils/override-nav";
export { bulk_query } from "./utils/bulk-query"; export { bulk_query } from "./utils/bulk-query";
export { get_user } from "./utils/get_user"; export { get_user } from "./utils/get_user";
export { range_date } from "./utils/ranged_date";
export const _sum = sum; export const _sum = sum;
export const _get = __get; export const _get = __get;
/** Generator */ /** Generator */
export { generateFilter as genereteFilter } from "lib/comps/filter/gen/gen-filter"; export { generateFilter as generateFilter } from "lib/comps/filter/gen/gen-filter";
export { generateRelation } from "lib/comps/form/gen/gen-rel"; export { generateRelation } from "lib/comps/form/gen/gen-rel";
export { genTableEdit } from "lib/comps/form/gen/gen-table-edit"; export { genTableEdit } from "lib/comps/form/gen/gen-table-edit";
export { generateMasterDetail } from "lib/comps/md/gen/md-gen"; export { generateMasterDetail } from "lib/comps/md/gen/md-gen";
@ -146,7 +154,7 @@ export { parseGenField } from "lib/gen/utils";
/** ETC */ /** ETC */
export { filterModifier } from "lib/comps/filter/utils/filter-modifier"; export { filterModifier } from "lib/comps/filter/utils/filter-modifier";
export { generateField } from "lib/comps/form/gen/gen-field"; // export { generateField } from "lib/comps/form/gen/gen-field";
export { generateForm } from "lib/comps/form/gen/gen-form"; export { generateForm } from "lib/comps/form/gen/gen-form";
export { validate as validateField } from "lib/comps/form/utils/validate"; export { validate as validateField } from "lib/comps/form/utils/validate";
export { sortTree, treePrefix } from "lib/comps/list/utils/sort-tree"; export { sortTree, treePrefix } from "lib/comps/list/utils/sort-tree";

View File

@ -11,6 +11,7 @@ export const logout = (url_login?: string) => {
if (isEditor) return; if (isEditor) return;
if (typeof get(w, "user") === "object") { if (typeof get(w, "user") === "object") {
w.user = null; w.user = null;
localStorage.removeItem("expiry_date");
} }
let id_site = ""; let id_site = "";

View File

@ -31,6 +31,10 @@ export const registerSession = (session: RG) => {
} }
localStorage.setItem("user" + id_site, JSON.stringify(data)); localStorage.setItem("user" + id_site, JSON.stringify(data));
localStorage.setItem(
"expiry_date",
data.expired ? data.expired?.format("YYYY-MM-DD HH:mm:ss") : ""
);
w.user = session.data; w.user = session.data;
w.user.role = session.role; w.user.role = session.role;
}; };

View File

@ -37,7 +37,7 @@ type LYTChild = {
tablet?: ReactNode; tablet?: ReactNode;
child?: ReactNode; child?: ReactNode;
default_layout: ReactNode; default_layout: ReactNode;
exception?: Array<string>; exception?: ((path: string) => boolean) | Array<string>;
blank_layout: ReactNode; blank_layout: ReactNode;
login_url: string; login_url: string;
PassProp: any; PassProp: any;
@ -66,28 +66,32 @@ export const Layout: FC<LYTChild> = (props) => {
}, []); }, []);
initResponsive(); initResponsive();
const path = getPathname(); const path = getPathname({ hash: true });
const no_layout = props.exception; const no_layout = props.exception;
overrideNav({ local }); overrideNav({ local });
if ( if (
!isEditor && !isEditor &&
Array.isArray(no_layout) && (typeof no_layout === "function"
no_layout.find((rule) => wildcardMatch(path, rule)) ? no_layout!(path)
: Array.isArray(no_layout) &&
no_layout.find((rule) => wildcardMatch(path, rule)))
) { ) {
return <>{props.blank_layout}</>; return <>{props.blank_layout}</>;
} else { } else {
if (!w.user) { if (!w.user) {
local.loading = true; local.loading = true;
loadSession(props.login_url || "/auth/login"); isMobile
? loadSession("/m/auth/login")
: loadSession(props.login_url || "/auth/login");
local.loading = false; local.loading = false;
} }
} }
if (path === props.login_url) return props.blank_layout; if (path === props.login_url) return props.blank_layout;
if (!w.user && !isEditor) { if (!w.user && !isEditor && !isMobile) {
return ( return (
<div <div
className={cx( className={cx(

27
utils/ranged_date.ts Executable file
View File

@ -0,0 +1,27 @@
export const range_date = (param: { data: any, name: any }) => {
const {data, name} = param;
let result = null as any;
Object.keys(data).map((e) => {
if (["lte", "gte", "gt", "lt"].includes(e)) {
if (data?.[e] instanceof Date) {
const today = data?.[e];
const day = new Date(today);
if (["lte", "lt"].includes(e)) {
day.setHours(23, 59, 59, 999); // Mengatur waktu ke 23:59:59
} else if (["gte", "gt"].includes(e)) {
day.setHours(0, 0, 0, 0); // Mengatur waktu ke 00:00:
}
result = {
...result,
[e]: day,
};
}
}
});
if (result)
return {
[name]: result,
};
return result;
};