This commit is contained in:
rizky 2024-08-08 01:35:45 -07:00
parent 15e1e983a5
commit 5022096c06
23 changed files with 383 additions and 93 deletions

View File

@ -41,6 +41,7 @@ export const BarChart: FC<{
datasetIdKey="id" datasetIdKey="id"
options={{ options={{
responsive: true, responsive: true,
maintainAspectRatio: false,
plugins: plugins:
legend === "none" legend === "none"
? { ? {

View File

@ -43,6 +43,7 @@ interface Props {
onClickNext: () => void; onClickNext: () => void;
changeMonth: (month: number) => void; changeMonth: (month: number) => void;
changeYear: (year: number) => void; changeYear: (year: number) => void;
mode?: "monthly" | "daily";
} }
const Calendar: React.FC<Props> = ({ const Calendar: React.FC<Props> = ({
@ -53,6 +54,7 @@ const Calendar: React.FC<Props> = ({
onClickNext, onClickNext,
changeMonth, changeMonth,
changeYear, changeYear,
mode = "daily",
}) => { }) => {
// Contexts // Contexts
const { const {
@ -73,6 +75,12 @@ const Calendar: React.FC<Props> = ({
const [showMonths, setShowMonths] = useState(false); const [showMonths, setShowMonths] = useState(false);
const [showYears, setShowYears] = useState(false); const [showYears, setShowYears] = useState(false);
const [year, setYear] = useState(date.year()); const [year, setYear] = useState(date.year());
useEffect(() => {
if (mode === "monthly") {
setShowMonths(true);
hideYears();
}
}, []);
// Functions // Functions
const previous = useCallback(() => { const previous = useCallback(() => {
return getLastDaysInMonth( return getLastDaysInMonth(
@ -104,7 +112,12 @@ const Calendar: React.FC<Props> = ({
(month: number) => { (month: number) => {
setTimeout(() => { setTimeout(() => {
changeMonth(month); changeMonth(month);
setShowMonths(!showMonths); if (mode === "daily") {
setShowMonths(!showMonths);
} else {
hideDatepicker();
clickDay(1,month + 1, date.year() );
}
}, 250); }, 250);
}, },
[changeMonth, showMonths] [changeMonth, showMonths]
@ -115,6 +128,10 @@ const Calendar: React.FC<Props> = ({
setTimeout(() => { setTimeout(() => {
changeYear(year); changeYear(year);
setShowYears(!showYears); setShowYears(!showYears);
if (mode === "monthly") {
setShowMonths(true);
clickDay(1,date.month() + 1, year );
}
}, 250); }, 250);
}, },
[changeYear, showYears] [changeYear, showYears]
@ -125,7 +142,6 @@ const Calendar: React.FC<Props> = ({
const fullDay = `${year}-${month}-${day}`; const fullDay = `${year}-${month}-${day}`;
let newStart; let newStart;
let newEnd = null; let newEnd = null;
function chosePeriod(start: string, end: string) { function chosePeriod(start: string, end: string) {
const ipt = input?.current; const ipt = input?.current;
changeDatepickerValue( changeDatepickerValue(

View File

@ -51,6 +51,7 @@ const Datepicker: React.FC<DatepickerType> = ({
startWeekOn = "sun", startWeekOn = "sun",
classNames = undefined, classNames = undefined,
popoverDirection = undefined, popoverDirection = undefined,
mode="daily"
}) => { }) => {
const local = useLocal({ open: false }); const local = useLocal({ open: false });
// Ref // Ref
@ -95,6 +96,7 @@ const Datepicker: React.FC<DatepickerType> = ({
if (newDate.isSame(reformatDate) || newDate.isAfter(reformatDate)) { if (newDate.isSame(reformatDate) || newDate.isAfter(reformatDate)) {
setSecondDate(nextMonth(date)); setSecondDate(nextMonth(date));
} }
console.log(date)
setFirstDate(date); setFirstDate(date);
}, },
[secondDate] [secondDate]
@ -110,6 +112,7 @@ const Datepicker: React.FC<DatepickerType> = ({
const changeFirstMonth = useCallback( const changeFirstMonth = useCallback(
(month: number) => { (month: number) => {
console.log("HALOOO")
firstGotoDate( firstGotoDate(
dayjs(`${firstDate.year()}-${month < 10 ? "0" : ""}${month}-01`) dayjs(`${firstDate.year()}-${month < 10 ? "0" : ""}${month}-01`)
); );
@ -148,6 +151,7 @@ const Datepicker: React.FC<DatepickerType> = ({
const changeSecondMonth = useCallback( const changeSecondMonth = useCallback(
(month: number) => { (month: number) => {
console.log("ALOO")
secondGotoDate( secondGotoDate(
dayjs(`${secondDate.year()}-${month < 10 ? "0" : ""}${month}-01`) dayjs(`${secondDate.year()}-${month < 10 ? "0" : ""}${month}-01`)
); );
@ -323,8 +327,8 @@ const Datepicker: React.FC<DatepickerType> = ({
return typeof containerClassName === "function" return typeof containerClassName === "function"
? containerClassName(defaultContainerClassName) ? containerClassName(defaultContainerClassName)
: typeof containerClassName === "string" && containerClassName !== "" : typeof containerClassName === "string" && containerClassName !== ""
? containerClassName ? containerClassName
: defaultContainerClassName; : defaultContainerClassName;
}, [containerClassName]); }, [containerClassName]);
return ( return (
@ -339,7 +343,9 @@ const Datepicker: React.FC<DatepickerType> = ({
open={local.open} open={local.open}
content={ content={
<div <div
className={cx("c-text-sm 2xl:c-text-sm")} className={cx(
"c-text-sm 2xl:c-text-sm",
)}
ref={calendarContainerRef} ref={calendarContainerRef}
> >
<div className="c-flex c-flex-col lg:c-flex-row c-py-1"> <div className="c-flex c-flex-col lg:c-flex-row c-py-1">
@ -356,6 +362,7 @@ const Datepicker: React.FC<DatepickerType> = ({
onClickNext={nextMonthFirst} onClickNext={nextMonthFirst}
changeMonth={changeFirstMonth} changeMonth={changeFirstMonth}
changeYear={changeFirstYear} changeYear={changeFirstYear}
mode={mode}
minDate={minDate} minDate={minDate}
maxDate={maxDate} maxDate={maxDate}
/> />
@ -372,6 +379,7 @@ const Datepicker: React.FC<DatepickerType> = ({
onClickNext={nextMonthSecond} onClickNext={nextMonthSecond}
changeMonth={changeSecondMonth} changeMonth={changeSecondMonth}
changeYear={changeSecondYear} changeYear={changeSecondYear}
mode={mode}
minDate={minDate} minDate={minDate}
maxDate={maxDate} maxDate={maxDate}
/> />

View File

@ -82,6 +82,7 @@ export interface DatepickerType {
disabledDates?: DateRangeType[] | null; disabledDates?: DateRangeType[] | null;
startWeekOn?: string | null; startWeekOn?: string | null;
popoverDirection?: PopoverDirectionType; popoverDirection?: PopoverDirectionType;
mode?: "daily" | "monthly"
} }
export type ColorKeys = (typeof COLORS)[number]; // "blue" | "orange" export type ColorKeys = (typeof COLORS)[number]; // "blue" | "orange"

View File

@ -137,6 +137,7 @@ function PopoverArrow() {
top: arrowY != null ? `${arrowY}px` : "", top: arrowY != null ? `${arrowY}px` : "",
[staticSide]: "-4px", [staticSide]: "-4px",
transform: "rotate(45deg)", transform: "rotate(45deg)",
cursor: "pointer"
}} }}
className={cx( className={cx(
"arrow", "arrow",

View File

@ -3,14 +3,16 @@ 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 "lib/exports";
export const FilterContent: FC<{ export const FilterContent: FC<{
mode: string; mode: string;
filter: FilterLocal; filter: FilterLocal;
onSubmit?: (form: FMLocal | null) => Promise<any>;
PassProp: any; PassProp: any;
child: any; child: any;
_item: PrasiItem; _item: PrasiItem;
}> = ({ mode, filter, PassProp, child, _item }) => { }> = ({ mode, filter, PassProp, child, _item, onSubmit }) => {
const internal = useLocal({}); const internal = useLocal({});
return ( return (
<div <div
@ -97,9 +99,18 @@ export const FilterContent: FC<{
> >
<BaseForm <BaseForm
data={filter.data} data={filter.data}
on_submit={(form) => { on_submit={async (form) => {
const f = getFilter(filter.name); if (typeof onSubmit === "function" && mode === "raw") {
const data = await onSubmit(form.fm);
if(typeof form.fm?.data === "object"){
form.fm.data = {
...form.fm.data,
_where: data
}
}
}
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)) {
list.reload(); list.reload();
@ -113,7 +124,9 @@ export const FilterContent: FC<{
return ( return (
<> <>
{!!(PassProp && child) && ( {!!(PassProp && child) && (
<PassProp filter={filter}>{child}</PassProp> <PassProp filter={filter} fm={form.fm}>
{child}
</PassProp>
)} )}
</> </>
); );

View File

@ -29,7 +29,7 @@ export const FilterField: FC<{
internal.render_timeout = setTimeout(() => { internal.render_timeout = setTimeout(() => {
filter_window.prasiContext.render(); filter_window.prasiContext.render();
}, 500); }, 500);
}, [filter.form?.data[name]]); }, [filter.form]);
let show_modifier = filter.mode !== "inline"; let show_modifier = filter.mode !== "inline";

View File

@ -1,7 +1,7 @@
import { useLocal } from "@/utils/use-local"; import { useLocal } from "@/utils/use-local";
import { FC, ReactNode } from "react"; import { FC, ReactNode } from "react";
import { createPortal } from "react-dom"; import { createPortal } from "react-dom";
import { 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";
@ -16,6 +16,7 @@ type FilterProps = {
mode: FilterMode; mode: FilterMode;
children?: ReactNode; children?: ReactNode;
onClose?: () => void; onClose?: () => void;
onSubmit?: (fm: FMLocal | null) => Promise<any>;
PassProp: any; PassProp: any;
child: any; child: any;
_item: PrasiItem; _item: PrasiItem;
@ -31,6 +32,7 @@ export const MasterFilter: FC<FilterProps> = ({
child, child,
onClose, onClose,
_item, _item,
onSubmit,
}): ReactNode => { }): ReactNode => {
const filter = useLocal({ ...default_filter_local }); const filter = useLocal({ ...default_filter_local });
filter.name = name; filter.name = name;
@ -74,6 +76,7 @@ export const MasterFilter: FC<FilterProps> = ({
)} )}
> >
<FilterContent <FilterContent
onSubmit={onSubmit}
PassProp={PassProp} PassProp={PassProp}
child={child} child={child}
mode={mode} mode={mode}
@ -90,6 +93,7 @@ export const MasterFilter: FC<FilterProps> = ({
return ( return (
<> <>
<FilterContent <FilterContent
onSubmit={onSubmit}
PassProp={PassProp} PassProp={PassProp}
_item={_item} _item={_item}
child={child} child={child}

View File

@ -5,17 +5,24 @@ import { softDeleteFilter } from "./soft-delete-filter";
export const filterWhere = (filter_name: string, p: any) => { export const filterWhere = (filter_name: string, p: any) => {
const f = getFilter(filter_name); const f = getFilter(filter_name);
let where: any = {}; let where: any = {};
if (f) { if (f) {
let fields: GFCol[] = []; let fields: GFCol[] = [];
//
if (p.gen__fields) { if (p.gen__fields) {
fields = parseGenField(p.gen__fields); fields = parseGenField(p.gen__fields);
} }
for (const pf of Object.values(f.filter.ref)) { for (const pf of Object.values(f.filter.ref)) {
const w = parseSingleFilter(pf, fields); if (pf.mode === "raw") {
for (const [k, v] of Object.entries(w)) { const data = pf.data?._where ? pf.data?._where : pf.data
where[k] = v; for (const [k, v] of Object.entries(data)) {
where[k] = v;
}
} else {
const w = parseSingleFilter(pf, fields);
for (const [k, v] of Object.entries(w)) {
where[k] = v;
}
} }
} }
} }
@ -27,6 +34,6 @@ export const filterWhere = (filter_name: string, p: any) => {
type: p.sft__type, type: p.sft__type,
}); });
} }
console.log({where})
return where; return where;
}; };

View File

@ -61,9 +61,9 @@ export const BaseForm = <T extends Record<string, any>>(
return prop; return prop;
}; };
form.createFm = () => { form.createFm = useCallback(() => {
if (form.fm) { if (form.fm) {
form.fm.data = form.data; form.fm.data = data;
return form.fm; return form.fm;
} }
let size = "full"; let size = "full";
@ -72,7 +72,7 @@ export const BaseForm = <T extends Record<string, any>>(
size = "half"; size = "half";
} }
form.fm = { form.fm = {
data: form.data, data: data,
props: { label_mode: "vertical" }, props: { label_mode: "vertical" },
error: { error: {
get: () => { get: () => {
@ -84,7 +84,7 @@ export const BaseForm = <T extends Record<string, any>>(
render: form.render, render: form.render,
} as any; } as any;
return form.fm as any; return form.fm as any;
}; }, [data]);
form.fieldProps = (arg) => { form.fieldProps = (arg) => {
return { return {
@ -95,8 +95,8 @@ export const BaseForm = <T extends Record<string, any>>(
}; };
useEffect(() => { useEffect(() => {
form.data = data; // form.data = data;
form.render(); // form.render();
if (form.internal.width === 0) { if (form.internal.width === 0) {
setTimeout(() => { setTimeout(() => {
@ -142,7 +142,9 @@ export const BaseForm = <T extends Record<string, any>>(
form.render(); form.render();
}, 50); }, 50);
} }
if (!form.fm) {
form.fm = form.createFm();
}
return ( return (
<form <form
onSubmit={(e) => { onSubmit={(e) => {

View File

@ -3,7 +3,7 @@ import { FMLocal, FieldLocal, FieldProp } from "../typings";
export const default_base_form_local = { export const default_base_form_local = {
status: "init" as "init" | "submitting" | "ready", status: "init" as "init" | "submitting" | "ready",
data: {} as any, // data: {} as any,
internal: { internal: {
width: 0, width: 0,
init_render: 0, init_render: 0,
@ -23,7 +23,7 @@ type CreateFieldArg = {
}; };
export type BaseFormLocal<T> = Omit<typeof default_base_form_local, "data"> & { export type BaseFormLocal<T> = Omit<typeof default_base_form_local, "data"> & {
data: T; // data: T;
createArg: (arg: CreateFieldArg) => FieldProp; createArg: (arg: CreateFieldArg) => FieldProp;
createField: (arg: CreateFieldArg) => FieldLocal; createField: (arg: CreateFieldArg) => FieldLocal;
createFm: () => FMLocal; createFm: () => FMLocal;

View File

@ -13,7 +13,7 @@ export const Field: FC<FieldProp> = (arg) => {
const { fm } = arg; const { fm } = arg;
const field = useField(arg); const field = useField(arg);
const name = field.name; const name = field.name;
const local = useLocal({ prev_val: fm.data[name] }); const local = useLocal({ prev_val: fm.data?.[name] });
const w = field.width; const w = field.width;
@ -29,14 +29,29 @@ export const Field: FC<FieldProp> = (arg) => {
if (arg.on_change) { if (arg.on_change) {
arg.on_change({ value: fm.data[name], name, fm }); arg.on_change({ value: fm.data[name], name, fm });
} }
if(!fm.events){
fm.events = {
on_change(name, new_value) {
},
}
}
fm.events.on_change(name, fm.data[name]); fm.events.on_change(name, fm.data[name]);
fm.render(); fm.render();
} }
}, [fm.data[name]]); }, [fm.data[name]]);
useEffect(() => {
if (typeof arg.on_init === "function") {
arg.on_init({ name, field });
}
}, [field]);
if (field.status === "init" && !isEditor) return null; if (field.status === "init" && !isEditor) return null;
const errors = fm.error.get(name); let errors = fm.error.get(name);
if(field.error){
errors = [field.error]
}
const props = { ...arg.props }; const props = { ...arg.props };
let editorClassName = ""; let editorClassName = "";
@ -45,6 +60,8 @@ export const Field: FC<FieldProp> = (arg) => {
props.className.split(" ").find((e: string) => e.startsWith("s-")) || ""; props.className.split(" ").find((e: string) => e.startsWith("s-")) || "";
} }
const disabled =
typeof field.disabled === "function" ? field.disabled() : field.disabled;
if (field.hidden) return <></>; if (field.hidden) return <></>;
return ( return (
@ -72,7 +89,7 @@ export const Field: FC<FieldProp> = (arg) => {
"c-flex-col c-space-y-1", "c-flex-col c-space-y-1",
css` css`
.field-outer { .field-outer {
border: 1px solid ${field.disabled ? "#ececeb" : "#cecece"}; border: 1px solid ${disabled ? "#ececeb" : "#cecece"};
&.focused { &.focused {
border: 1px solid #1c4ed8; border: 1px solid #1c4ed8;
@ -98,7 +115,7 @@ export const Field: FC<FieldProp> = (arg) => {
{field.desc} {field.desc}
</div> </div>
)} )}
{errors.length > 0 && ( {errors.length ? (
<div <div
className={cx( className={cx(
"field-error c-p-2 c-text-xs c-text-red-600", "field-error c-p-2 c-text-xs c-text-red-600",
@ -109,7 +126,7 @@ export const Field: FC<FieldProp> = (arg) => {
return <div>{err}</div>; return <div>{err}</div>;
})} })}
</div> </div>
)} ) : <></>}
</div> </div>
</LabelDiv> </LabelDiv>
); );

View File

@ -6,13 +6,14 @@ export const Label: FC<{ field: FieldLocal; fm: FMLocal }> = ({
fm, fm,
}) => { }) => {
const errors = fm.error.get(field.name); const errors = fm.error.get(field.name);
const disabled =
typeof field.disabled === "function" ? field.disabled() : field.disabled;
return ( return (
<div className={cx("label c-text-sm c-flex c-items-center", "c-mt-3")}> <div className={cx("label c-text-sm c-flex c-items-center", "c-mt-3")}>
<span className={cx(errors.length > 0 && `c-text-red-600`)}> <span className={cx(errors.length > 0 && `c-text-red-600`)}>
{field.label} {field.label}
</span> </span>
{field.required && !field.disabled && ( {field.required && !disabled && (
<span className="c-text-red-600 c-mb-2 c-ml-1"> <span className="c-text-red-600 c-mb-2 c-ml-1">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"

View File

@ -154,15 +154,10 @@ export const TableEdit: FC<{
} }
return ( return (
<> <>
<div <div
className={cx( className={cx(
`c-w-full c-h-full c-flex c-flex-col`, `c-w-full c-h-full c-flex c-flex-col`,
css` css`
.rdg {
overflow-y: hidden !important;
height: var(--rdg-scroll-height) !important;
}
.rdg-cell > div { .rdg-cell > div {
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
@ -172,9 +167,6 @@ export const TableEdit: FC<{
padding-top: 0px; padding-top: 0px;
} }
} }
.field-error {
display: none;
}
.rdg-header-row { .rdg-header-row {
border-top-right-radius: 5px; border-top-right-radius: 5px;
border-top-left-radius: 5px; border-top-left-radius: 5px;
@ -192,6 +184,7 @@ export const TableEdit: FC<{
className={cx( className={cx(
"c-table-auto", "c-table-auto",
css` css`
height: 1px;
border-collapse: collapse; border-collapse: collapse;
table-layout: auto; /* Kolom akan menyesuaikan konten */ table-layout: auto; /* Kolom akan menyesuaikan konten */
` `
@ -241,7 +234,7 @@ export const TableEdit: FC<{
{columns.map((header) => { {columns.map((header) => {
return ( return (
<td> <td>
<div className="c-flex c-flex-row c-py-2 c-w-full"> <div className="c-flex c-flex-row c-py-2 c-w-full c-h-full">
{header.renderCell({ {header.renderCell({
props: { props: {
row: row, row: row,

View File

@ -33,6 +33,24 @@ export const TypeDropdown: FC<{
data: e.data, data: e.data,
}; };
}); });
let v = typeof arg.opt_get_value === "function"
? arg.opt_get_value({
fm,
name: field.name,
options: local.options,
type: field.type,
})
: fm.data[field.name];
let f = list.find((ex) => ex.value === v);
if(!f){
arg.opt_set_value({
fm,
name: field.name,
type: field.type,
options: local.options,
selected: [],
});
}
local.options = list; local.options = list;
} else { } else {
local.options = res; local.options = res;

View File

@ -28,7 +28,8 @@ export type PropTypeInput = {
| "file" | "file"
| "search" | "search"
| "password" | "password"
| "import"; | "import"
| "monthly";
placeholder?: string; placeholder?: string;
onFocus?: (e: FocusEvent<HTMLDivElement>) => void; onFocus?: (e: FocusEvent<HTMLDivElement>) => void;
onBlur?: (e: FocusEvent<HTMLDivElement>) => void; onBlur?: (e: FocusEvent<HTMLDivElement>) => void;
@ -168,7 +169,7 @@ export const FieldTypeInput: FC<{
case "upload": case "upload":
return ( return (
<FieldUpload <FieldUpload
arg={arg} arg={arg}
field={field} field={field}
fm={fm} fm={fm}
prop={prop} prop={prop}
@ -178,7 +179,7 @@ export const FieldTypeInput: FC<{
case "import": case "import":
return ( return (
<FieldUpload <FieldUpload
arg={arg} arg={arg}
field={field} field={field}
fm={fm} fm={fm}
prop={prop} prop={prop}
@ -199,11 +200,34 @@ export const FieldTypeInput: FC<{
value={{ startDate: value, endDate: value }} value={{ startDate: value, endDate: value }}
disabled={disabled} disabled={disabled}
displayFormat="DD MMM YYYY" displayFormat="DD MMM YYYY"
mode={"daily"}
maxDate={field.max_date instanceof Date ? field.max_date : null} maxDate={field.max_date instanceof Date ? field.max_date : null}
minDate={field.min_date instanceof Date ? field.min_date : null} minDate={field.min_date instanceof Date ? field.min_date : null}
asSingle={true} asSingle={true}
useRange={false} useRange={false}
onChange={(value) => { onChange={(value) => {
console.log({ value });
fm.data[field.name] = value?.startDate
? new Date(value?.startDate)
: null;
renderOnChange();
}}
/>
);
}
case "monthly": {
return (
<Datepicker
value={{ startDate: value, endDate: value }}
disabled={disabled}
displayFormat="MMM YYYY"
mode={"monthly"}
maxDate={field.max_date instanceof Date ? field.max_date : null}
minDate={field.min_date instanceof Date ? field.min_date : null}
asSingle={true}
useRange={false}
onChange={(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

@ -107,6 +107,10 @@ export type FieldProp = {
current: any; current: any;
options: { value: string; label: string; item?: any }[]; options: { value: string; label: string; item?: any }[];
}) => boolean; }) => boolean;
on_init: (arg: {
field: any,
name: string
}) => void;
pk: string; pk: string;
sub_type: string; sub_type: string;
placeholder: string; placeholder: string;
@ -188,6 +192,8 @@ export type FieldInternal<T extends FieldProp["type"]> = {
prop?: any; prop?: any;
max_date?: FieldProp["max_date"]; max_date?: FieldProp["max_date"];
min_date?: FieldProp["min_date"]; min_date?: FieldProp["min_date"];
error?: any;
table_fields?: any[]
}; };
export type FieldLocal = FieldInternal<any> & { export type FieldLocal = FieldInternal<any> & {
render: () => void; render: () => void;

View File

@ -16,35 +16,33 @@ export const useField = (
input: {}, input: {},
ref: null as any, ref: null as any,
} as any); } as any);
const ref = useRef(null as any); const ref = useRef(null as any)
field.ref = ref; field.ref = ref;
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 : arg.required();
const update_field = {
name: name.replace(/\s*/gi, ""),
label: label,
type: arg.type,
desc: arg.desc,
prefix: arg.prefix,
suffix: arg.suffix,
width: arg.width,
custom: arg.custom,
required: required === "y",
required_msg: arg.required_msg,
disabled: typeof arg.disabled === "function" ? arg.disabled : arg.disabled === "y",
on_change: arg.on_change,
max_date: arg.max_date,
min_date: arg.min_date,
table_fields: []
};
if (field.status === "init" || isEditor) { if (field.status === "init" || isEditor) {
for (const [k, v] of Object.entries({ for (const [k, v] of Object.entries(update_field)) {
name: name.replace(/\s*/gi, ""),
label: label,
type: arg.type,
desc: arg.desc,
prefix: arg.prefix,
suffix: arg.suffix,
width: arg.width,
custom: arg.custom,
required: required === "y",
required_msg: arg.required_msg,
disabled:
typeof arg.disabled === "function"
? arg.disabled
: arg.disabled === "y",
on_change: arg.on_change,
max_date: arg.max_date,
min_date: arg.min_date,
options: {},
reload_options: () => {},
})) {
(field as any)[k] = v; (field as any)[k] = v;
} }
} }
@ -54,6 +52,9 @@ export const useField = (
useEffect(() => { useEffect(() => {
if (field.status === "init" || !fm.fields[name]) { if (field.status === "init" || !fm.fields[name]) {
field.status = "ready"; field.status = "ready";
if(!fm.fields){
fm.fields = {}
}
fm.fields[name] = field; fm.fields[name] = field;
field.render(); field.render();
} }

View File

@ -183,7 +183,6 @@ export const TableList: FC<TableListProp> = ({
let should_set = true; let should_set = true;
const gf = JSON.stringify(gen_fields); const gf = JSON.stringify(gen_fields);
const fields = fields_map.get(gf); const fields = fields_map.get(gf);
if (fields) { if (fields) {
const rel = fields?.find((e) => e.name === columnKey); const rel = fields?.find((e) => e.name === columnKey);
if (rel && rel.checked) { if (rel && rel.checked) {
@ -196,9 +195,7 @@ export const TableList: FC<TableListProp> = ({
}, },
}; };
} else { } else {
const field = rel.checked.find( const field = rel.checked.find((e) => !e.is_pk);
(e) => !e.is_pk && !e.relation
);
if (field) { if (field) {
local.sort.orderBy = { local.sort.orderBy = {
[columnKey]: { [columnKey]: {

46
comps/ui/scroll-area.tsx Executable file
View File

@ -0,0 +1,46 @@
"use client"
import * as React from "react"
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
import { cn } from "@/utils"
const ScrollArea = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
>(({ className, children, ...props }, ref) => (
<ScrollAreaPrimitive.Root
ref={ref}
className={cn("c-relative c-overflow-hidden", className)}
{...props}
>
<ScrollAreaPrimitive.Viewport className="c-h-full c-w-full c-rounded-[inherit]">
{children}
</ScrollAreaPrimitive.Viewport>
<ScrollBar />
<ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root>
))
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
const ScrollBar = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
>(({ className, orientation = "vertical", ...props }, ref) => (
<ScrollAreaPrimitive.ScrollAreaScrollbar
ref={ref}
orientation={orientation}
className={cn(
"c-flex c-touch-none c-select-none c-transition-colors",
orientation === "vertical" &&
"c-h-full c-w-2.5 c-border-l c-border-l-transparent c-p-[1px]",
orientation === "horizontal" &&
"c-h-2.5 c-flex-col c-border-t c-border-t-transparent c-p-[1px]",
className
)}
{...props}
>
<ScrollAreaPrimitive.ScrollAreaThumb className="c-relative c-flex-1 c-rounded-full c-bg-border" />
</ScrollAreaPrimitive.ScrollAreaScrollbar>
))
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
export { ScrollArea, ScrollBar }

134
comps/ui/sheet.tsx Executable file
View File

@ -0,0 +1,134 @@
"use client";
import * as React from "react";
import * as SheetPrimitive from "@radix-ui/react-dialog";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/utils";
const Sheet = SheetPrimitive.Root;
const SheetTrigger = SheetPrimitive.Trigger;
const SheetClose = SheetPrimitive.Close;
const SheetPortal = SheetPrimitive.Portal;
const SheetOverlay = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Overlay
className={cn(
"c-fixed c-inset-0 c-z-50 c-bg-black/80 data-[state=open]:c-animate-in data-[state=closed]:c-animate-out data-[state=closed]:c-fade-out-0 data-[state=open]:c-fade-in-0",
className
)}
{...props}
ref={ref}
/>
));
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
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",
{
variants: {
side: {
top: "c-inset-x-0 c-top-0 c-border-b data-[state=closed]:c-slide-out-to-top data-[state=open]:c-slide-in-from-top",
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",
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",
},
},
defaultVariants: {
side: "right",
},
}
);
interface SheetContentProps
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
VariantProps<typeof sheetVariants> {}
const SheetContent = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Content>,
SheetContentProps
>(({ side = "right", className, children, ...props }, ref) => (
<SheetPortal>
<SheetPrimitive.Content
ref={ref}
className={cn(sheetVariants({ side }), className)}
{...props}
>
{children}
</SheetPrimitive.Content>
</SheetPortal>
));
SheetContent.displayName = SheetPrimitive.Content.displayName;
const SheetHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"c-flex c-flex-col c-space-y-2 c-text-center sm:c-text-left",
className
)}
{...props}
/>
);
SheetHeader.displayName = "SheetHeader";
const SheetFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"c-flex c-flex-col-reverse sm:c-flex-row sm:c-justify-end sm:c-space-x-2",
className
)}
{...props}
/>
);
SheetFooter.displayName = "SheetFooter";
const SheetTitle = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Title
ref={ref}
className={cn("c-text-lg c-font-semibold c-text-foreground", className)}
{...props}
/>
));
SheetTitle.displayName = SheetPrimitive.Title.displayName;
const SheetDescription = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Description
ref={ref}
className={cn("c-text-sm c-text-muted-foreground", className)}
{...props}
/>
));
SheetDescription.displayName = SheetPrimitive.Description.displayName;
export {
Sheet,
SheetPortal,
SheetOverlay,
SheetTrigger,
SheetClose,
SheetContent,
SheetHeader,
SheetFooter,
SheetTitle,
SheetDescription,
};

View File

@ -1,10 +1,10 @@
export { FieldLoading } from "@/comps/ui/field-loading"; export { FieldLoading } from "@/comps/ui/field-loading";
export { fetchLinkParams } from "./comps/form/field/type/TypeLink";
export { prasi_gen } from "./gen/prasi_gen";
export { guessLabel } from "./utils/guess-label";
import { lazify, lazifyMany } from "@/utils/lazify"; import { lazify, lazifyMany } from "@/utils/lazify";
import __get from "lodash.get"; import __get from "lodash.get";
import { sum } from "./utils/sum"; import { sum } from "./utils/sum";
export { guessLabel } from "./utils/guess-label";
export { fetchLinkParams } from "./comps/form/field/type/TypeLink";
export { prasi_gen } from "./gen/prasi_gen";
export const _sum = sum; export const _sum = sum;
export const _get = __get; export const _get = __get;
@ -80,8 +80,8 @@ export const HeaderProfile = lazify(
); );
/** charts */ /** charts */
export { PieChart } from "@/comps/charts/pie";
export { BarChart } from "@/comps/charts/bar"; export { BarChart } from "@/comps/charts/bar";
export { PieChart } from "@/comps/charts/pie";
// export { LineChart } from "@/comps/charts/line"; // export { LineChart } from "@/comps/charts/line";
/** Generator */ /** Generator */
@ -98,11 +98,9 @@ export { generateForm } from "@/comps/form/gen/gen-form";
export { validate as validateField } from "@/comps/form/utils/validate"; export { validate as validateField } from "@/comps/form/utils/validate";
export { sortTree, treePrefix } from "@/comps/list/utils/sort-tree"; export { sortTree, treePrefix } from "@/comps/list/utils/sort-tree";
export { getFilter } from "@/comps/filter/utils/get-filter";
export { export {
FMLocal, fieldType, FieldTypeCustom, FMLocal, formType
FieldTypeCustom,
fieldType,
formType,
} from "@/comps/form/typings"; } from "@/comps/form/typings";
export { TableListType } from "@/comps/list/utils/typings"; export { TableListType } from "@/comps/list/utils/typings";
export { generateTableList as generateTableList } from "@/comps/md/gen/gen-table-list"; export { generateTableList as generateTableList } from "@/comps/md/gen/gen-table-list";
@ -112,17 +110,15 @@ export { Button, FloatButton } from "@/comps/ui/button";
export { FormatValue } from "@/utils/format-value"; export { FormatValue } from "@/utils/format-value";
export { GetValue } from "@/utils/get-value"; export { GetValue } from "@/utils/get-value";
export { password } from "@/utils/password"; export { password } from "@/utils/password";
export { prasi_events, call_prasi_events } from "lib/utils/prasi-events"; export { call_prasi_events, prasi_events } from "lib/utils/prasi-events";
export { getFilter } from "@/comps/filter/utils/get-filter";
/** Session */ /** Session */
export { Login } from "@/preset/login/Login"; export { Login } from "@/preset/login/Login";
export { generateLogin } from "@/preset/login/utils/generate"; export { generateLogin } from "@/preset/login/utils/generate";
export { logout } from "@/preset/login/utils/logout"; export { logout } from "@/preset/login/utils/logout";
export { export {
RG, registerSession, RG,
UserSession, UserSession
registerSession,
} from "@/preset/login/utils/register"; } from "@/preset/login/utils/register";
export { Card } from "@/comps/custom/Card"; export { Card } from "@/comps/custom/Card";
@ -143,12 +139,15 @@ export { PanelTab } from "@/comps/tab/parts/PanelTab";
export { Popup } from "@/comps/popup/PopUp"; export { Popup } from "@/comps/popup/PopUp";
export { Detail } from "@/comps/custom/Detail"; export { Detail } from "@/comps/custom/Detail";
export * from "@/comps/ui/input";
export { ButtonUpload } from "@/preset/profile/ButtonUpload"; export { ButtonUpload } from "@/preset/profile/ButtonUpload";
export { Profile } from "@/preset/profile/Profile"; export { Profile } from "@/preset/profile/Profile";
export { generateProfile } from "@/preset/profile/utils/generate"; export { generateProfile } from "@/preset/profile/utils/generate";
export { formatTime, longDate, shortDate, timeAgo } from "@/utils/date"; export { formatTime, longDate, shortDate, timeAgo } from "@/utils/date";
export { getPathname, getBasename } from "@/utils/pathname"; export { getBasename, getPathname } from "@/utils/pathname";
export * from "@/comps/ui/input";
export { Flow } from "@/comps/ui/flow"; export { Flow } from "@/comps/ui/flow";
// format money
export { formatMoney } from "@/comps/form/field/type/TypeMoney";
export { ScrollArea } from "@/comps/ui/scroll-area";

View File

@ -49,7 +49,7 @@ export const FormatValue: FC<{
if (mode === "money") { if (mode === "money") {
if (isEmptyString(value)) return "-"; if (isEmptyString(value)) return "-";
return formatMoney(Number(value) || 0); return formatMoney(ceil_comma(Number(value) || 0));
} else if (mode === "datetime") { } else if (mode === "datetime") {
if (!value || isEmptyString(value)) return "-"; if (!value || isEmptyString(value)) return "-";
try { try {
@ -120,7 +120,7 @@ export const FormatValue: FC<{
} }
} else if (["float"].includes(field?.type as string)) { } else if (["float"].includes(field?.type as string)) {
if (!value || isEmptyString(value)) return "-"; if (!value || isEmptyString(value)) return "-";
return formatMoney(Number(value) || 0); return formatMoney(ceil_comma(Number(value) || 0));
} }
} }
@ -169,12 +169,13 @@ const timeAgo = (date: any) => {
const daysPast = Math.floor(secondsPast / 86400); const daysPast = Math.floor(secondsPast / 86400);
return `${daysPast} days ago`; return `${daysPast} days ago`;
} else { } else {
const year = date.getFullYear(); return formatDate(dayjs(date), "DD MMMM YYYY");
const month = (date.getMonth() + 1).toString().padStart(2, "0");
const day = date.getDate().toString().padStart(2, "0");
return `${day}-${month}-${year}`;
} }
} catch (e) { } catch (e) {
return null; return null;
} }
}; };
const ceil_comma = (number: number) => {
if (!number) return 0;
return Math.ceil(number * 100) / 100;
};