This commit is contained in:
rizky 2024-11-28 00:27:23 -07:00
parent d1174741b4
commit 913f1dc942
23 changed files with 192 additions and 75 deletions

View File

@ -43,11 +43,11 @@ const Week: React.FC<Props> = ({style}) => {
> >
{style === "google" ? dayjs(`2022-11-${6 + (item + startDateModifier)}`) {style === "google" ? dayjs(`2022-11-${6 + (item + startDateModifier)}`)
.locale(i18n) .locale(i18n)
.format("dddd") : ucFirst( .format(isMobile ? "dd" :"dddd") : ucFirst(
shortString( shortString(
dayjs(`2022-11-${6 + (item + startDateModifier)}`) dayjs(`2022-11-${6 + (item + startDateModifier)}`)
.locale(i18n) .locale(i18n)
.format("dddd") .format(isMobile ? "dd" :"dddd")
), ),
)} )}

View File

@ -377,15 +377,6 @@ const Calendar: React.FC<Props> = ({
return onMark(day, date) return onMark(day, date)
} }
return <></> return <></>
if (new Date().getDate() === day)
return (
<div className="c-absolute c-inset-y-0 c-left-0 -c-translate-y-1/2 -c-translate-x-1/2">
<div className="c-w-full c-h-full c-flex c-flex-row c-items-center c-justif-center c-px-0.5">
!
</div>
</div>
);
return <></>
}} }}
/> />
</> </>

View File

@ -145,7 +145,6 @@ export function getNumberOfDay(
let number = 0; let number = 0;
let startDateModifier = 0; let startDateModifier = 0;
if (startWeekOn) { if (startWeekOn) {
switch (startWeekOn) { switch (startWeekOn) {
case "mon": case "mon":
@ -173,16 +172,7 @@ export function getNumberOfDay(
break; break;
} }
} }
const days = [
isMobile ? [
"S",
"M",
"T",
"W",
"T",
"F",
"S",
]: [
"Sunday", "Sunday",
"Monday", "Monday",
"Tuesday", "Tuesday",
@ -190,7 +180,8 @@ export function getNumberOfDay(
"Thursday", "Thursday",
"Friday", "Friday",
"Saturday", "Saturday",
].forEach((item, index) => { ]
days.forEach((item, index) => {
if (item.includes(dayString)) { if (item.includes(dayString)) {
number = (index + startDateModifier) % 7; number = (index + startDateModifier) % 7;
} }

View File

@ -1,10 +1,10 @@
import { Toaster } from "lib/exports";
import { ReactNode, useEffect, useRef, useState } from "react"; import { ReactNode, useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom"; import { createPortal } from "react-dom";
import { editorFormWidth } from "../Form";
import { FMLocal } from "../typings"; import { FMLocal } from "../typings";
import { createFm } from "./utils/create-fm"; import { createFm } from "./utils/create-fm";
import { DivForm } from "./utils/DivForm"; import { DivForm } from "./utils/DivForm";
import { Toaster } from "lib/exports"; import { editorFormWidth } from "../Form";
export type BaseFormProps<T> = { export type BaseFormProps<T> = {
name: string; name: string;

View File

@ -60,7 +60,6 @@ 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={() => {

View File

@ -9,10 +9,6 @@ export const Label: FC<{ field: FieldLocal; fm: FMLocal; arg: FieldProp }> = ({
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 = const required =
typeof arg.required === "string" typeof arg.required === "string"
? arg.required === "y" ? arg.required === "y"
@ -30,12 +26,12 @@ export const Label: FC<{ field: FieldLocal; fm: FMLocal; arg: FieldProp }> = ({
"c-mt-3 c-w-full c-justify-between" "c-mt-3 c-w-full c-justify-between"
)} )}
> >
<div className="c-flex c-items-center"> <div className="c-flex c-flex-row c-items-center">
<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>
{required && !disabled && ( {required && !disabled && (
<span className="c-text-red-600 c-mb-2 c-ml-1"> <span className="c-text-red-600 c-ml-1">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="12" width="12"

View File

@ -269,7 +269,7 @@ export const TableEdit: FC<{
<BaseForm is_form={false} data={row}> <BaseForm is_form={false} data={row}>
{(form) => { {(form) => {
return ( return (
<tr> <tr className={cx("c-border-b ", idx !== value.length -1 ? "c-border-gray-300" : "" )}>
{columns.map((header) => { {columns.map((header) => {
return ( return (
<td <td
@ -322,10 +322,11 @@ export const TableEdit: FC<{
ext_fm={{ ext_fm={{
add: (e: any) => { add: (e: any) => {
if (Array.isArray(fm.data[name])) { if (Array.isArray(fm.data[name])) {
fm.data[name].push({}); fm.data[name].push({...e});
} else { } else {
fm.data[name] = [{}]; fm.data[name] = [{...e}];
} }
console.log("CEKK")
fm.render(); fm.render();
setTimeout(() => { setTimeout(() => {
const last = Array.from( const last = Array.from(

View File

@ -0,0 +1,36 @@
import { FC } from "react"
import { FilePreview } from "./FilePreview";
import { Trash2 } from "lucide-react";
import { FMLocal } from "lib/exports";
export const PreviewAfterUpload: FC<{
fm: FMLocal, fieldName: string, local: any
}> = ({ fm, fieldName, local }) => {
return (
<div className="c-flex-grow c-flex-row c-flex c-w-full c-h-full c-items-stretch">
<div className="c-flex c-justify-between c-flex-1 c-p-1">
<FilePreview url={fm.data[fieldName] || ""} />
<div
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
if (confirm("Clear this file ?")) {
fm.data[fieldName] = null;
local.attachment = "";
local.render();
fm.render();
}
}}
className={cx(
"c-flex c-flex-row c-items-center c-border c-px-2 c-rounded c-cursor-pointer hover:c-bg-red-100"
)}
>
<Trash2 className="c-text-red-500 c-h-4 c-w-4" />
</div>
</div>
</div>
)
}

View File

@ -242,7 +242,7 @@ export const navigateLink = async (
if (prev_link) prev_link = prev_link + "+"; if (prev_link) prev_link = prev_link + "+";
} }
navigate(`${link.url}#lnk=${prev_link + vhash}${prm}`); navigate(`${link.url}#lnk=${prev_link + vhash}${prm ? prm : ""}`);
return true; return true;
} }
}; };

View File

@ -12,6 +12,8 @@ export const FieldRadio: FC<{
list: [] as any[], list: [] as any[],
value: [] as any[], value: [] as any[],
}); });
const disabled =
typeof field.disabled === "function" ? field.disabled() : field.disabled;
useEffect(() => { useEffect(() => {
const callback = (res: any[]) => { const callback = (res: any[]) => {
local.list = res; local.list = res;
@ -40,6 +42,7 @@ export const FieldRadio: FC<{
<div <div
className="flex items-center mb-4" className="flex items-center mb-4"
onClick={() => { onClick={() => {
if(!disabled)
arg.opt_set_value({ arg.opt_set_value({
fm, fm,
name: field.name, name: field.name,

View File

@ -109,7 +109,7 @@ export const formInit = (fm: FMLocal, props: FMProps) => {
</div> </div>
</div>, </div>,
{ {
duration:3000, duration: 3000,
className: css` className: css`
background: #e4ffed; background: #e4ffed;
border: 2px solid green; border: 2px solid green;
@ -134,7 +134,7 @@ export const formInit = (fm: FMLocal, props: FMProps) => {
</div> </div>
</div>, </div>,
{ {
duration:3000, duration: 3000,
className: css` className: css`
background: #e4ffed; background: #e4ffed;
border: 2px solid green; border: 2px solid green;
@ -147,10 +147,10 @@ export const formInit = (fm: FMLocal, props: FMProps) => {
fm.reload = async () => { fm.reload = async () => {
fm.status = isEditor ? "ready" : "loading"; fm.status = isEditor ? "ready" : "loading";
fm.render(); fm.render();
let toastId = null as any;
if (sonar === "on" && !isEditor) { if (sonar === "on" && !isEditor) {
toast.dismiss(); toast.dismiss();
toast.loading( toastId = toast.loading(
<> <>
<Loader2 className="c-h-4 c-w-4 c-animate-spin" /> <Loader2 className="c-h-4 c-w-4 c-animate-spin" />
Loading data... Loading data...
@ -158,7 +158,6 @@ export const formInit = (fm: FMLocal, props: FMProps) => {
{ dismissible: true } { dismissible: true }
); );
} }
let should_load = true; let should_load = true;
if (isEditor) { if (isEditor) {
const item_id = props.item.id; const item_id = props.item.id;
@ -204,7 +203,6 @@ export const formInit = (fm: FMLocal, props: FMProps) => {
} }
} }
} }
toast.dismiss(); toast.dismiss();
if (fm.is_newly_created) { if (fm.is_newly_created) {
@ -214,6 +212,11 @@ export const formInit = (fm: FMLocal, props: FMProps) => {
fm.status = "ready"; fm.status = "ready";
fm.render(); fm.render();
setTimeout(() => {
toast.dismiss();
}, 2000)
}; };
fm.submit = async () => { fm.submit = async () => {
@ -289,7 +292,6 @@ export const formInit = (fm: FMLocal, props: FMProps) => {
} }
); );
} else { } else {
toastSuccess({ addNewText: lang.t("Add New") }); toastSuccess({ addNewText: lang.t("Add New") });
} }
} }

View File

@ -67,6 +67,5 @@ export const useField = (
}, []); }, []);
field.prop = arg as any; field.prop = arg as any;
return field; return field;
}; };

View File

@ -34,7 +34,7 @@ import {
Plus, Plus,
QrCode, QrCode,
Rocket, Rocket,
Settings, Settings,
Ship, Ship,
Siren, Siren,
StickyNote, StickyNote,

View File

@ -95,12 +95,12 @@ export const Import: FC<{
clearInterval(interval); clearInterval(interval);
resolve([]); resolve([]);
} }
}, 100); }, 50);
}); });
} }
await task(list[n], n); await task(list[n], n);
await sleep(100); await sleep(50);
n++; n++;
} }
}; };

View File

@ -35,7 +35,7 @@ export const TLList: FC<{
) : ( ) : (
<div <div
className={cx( className={cx(
"c-absolute c-inset-0", "list-inner c-absolute c-inset-0",
!isEditor && !isEditor &&
css` css`
@keyframes flasher { @keyframes flasher {

View File

@ -40,7 +40,13 @@ import { OnRowClick } from "./utils/type";
let EMPTY_SET = new Set() as ReadonlySet<any>; let EMPTY_SET = new Set() as ReadonlySet<any>;
type SelectedRow = (arg: { row: any; rows: any[]; idx: any }) => boolean; type SelectedRow = (arg: {
row: any;
rows: any[];
idx: any;
select?: boolean;
data?: any[];
}) => boolean;
type TableListProp = { type TableListProp = {
child: any; child: any;
PassProp: any; PassProp: any;
@ -137,6 +143,7 @@ export const TableList: FC<TableListProp> = ({
el: null as null | HTMLDivElement, el: null as null | HTMLDivElement,
width: 0, width: 0,
height: 0, height: 0,
selectedAllRows: false as boolean,
selectedRowIds: [] as (string | number)[], selectedRowIds: [] as (string | number)[],
pk: null as null | GFCol, pk: null as null | GFCol,
scrolled: false, scrolled: false,
@ -468,9 +475,11 @@ export const TableList: FC<TableListProp> = ({
rows: data, rows: data,
}); });
}); });
local.selectedAllRows = true;
local.render(); local.render();
} else { } else {
// jika tidak, maka local selected rows akan dikosongkan // jika tidak, maka local selected rows akan dikosongkan
local.selectedAllRows = false;
local.selectedRows = []; local.selectedRows = [];
local.render(); local.render();
} }
@ -494,6 +503,7 @@ export const TableList: FC<TableListProp> = ({
local.selectedRows = local.selectedRows.filter( local.selectedRows = local.selectedRows.filter(
(data) => data.pk !== rowId (data) => data.pk !== rowId
); );
local.selectedAllRows = false;
local.render(); local.render();
} }
}; };
@ -610,8 +620,24 @@ export const TableList: FC<TableListProp> = ({
frozen: true, frozen: true,
renderHeaderCell(props) { renderHeaderCell(props) {
return ( return (
<div> <div
{/* <CheckboxList value={false} on_click={on_click} /> */} className={cx(
css`
width: 100%;
height: 100%;
`,
"c-flex c-items-center c-justify-center"
)}
>
{isCheckbox ? (
<input
type="checkbox"
checked={local.selectedAllRows}
onChange={headerCheckboxClick}
/>
) : (
<></>
)}
</div> </div>
); );
}, },
@ -622,7 +648,29 @@ export const TableList: FC<TableListProp> = ({
tbl: local, tbl: local,
child, child,
}); });
if (isCheckbox) {
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={local.selectedAllRows ? true : isChecked}
/>
</div>
);
}
return ( return (
<PassProp <PassProp
idx={props.rowIdx} idx={props.rowIdx}
@ -908,10 +956,20 @@ export const TableList: FC<TableListProp> = ({
) { ) {
return local.cached_row.get(props.row); return local.cached_row.get(props.row);
} }
const isSelect = selected({ const isSelect = local.selectedAllRows
? true
: selected({
idx: props.rowIdx, idx: props.rowIdx,
row: props.row, row: props.row,
rows: local.data, rows: local.data,
select: local.selectedAllRows
? true
: local.selectedRows.some(
(checked) => checked.pk === props.row.id
),
data: local.selectedAllRows
? local.data
: local.selectedRows,
}); });
const child_row = ( const child_row = (
<Row <Row
@ -1008,23 +1066,29 @@ export const TableList: FC<TableListProp> = ({
}; };
const CheckboxList: FC<{ const CheckboxList: FC<{
on_click: (e: any) => void; on_click: (e: any) => void;
checked?: boolean;
value?: boolean; value?: boolean;
}> = ({ value, on_click }) => { }> = ({ value, checked, on_click }) => {
const local = useLocal({ const local = useLocal({
checked: false as any,
value: false as boolean, value: false as boolean,
}); });
useEffect(() => {
local.checked = checked;
local.render();
}, []);
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")}>
<div className={cx(`c-flex c-flex-col c-space-y-1 c-p-0.5`)}> <div className={cx(`c-flex c-flex-col c-space-y-1 c-p-0.5`)}>
<div <div
onClick={() => { onClick={() => {
local.value = !local.value; local.checked = !local.checked;
on_click(local.value); on_click(typeof value === "boolean" ? !local.checked : value);
local.render(); local.render();
}} }}
className="c-flex c-flex-row c-space-x-1 cursor-pointer c-items-center rounded-full p-0.5" className="c-flex c-flex-row c-space-x-1 cursor-pointer c-items-center rounded-full p-0.5"
> >
{local.value ? ( {local.checked ? (
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="24" width="24"

View File

@ -17,6 +17,7 @@ export const toast = {
sonner.dismiss(t.id); sonner.dismiss(t.id);
} }
} }
sonner.dismiss()
} else { } else {
clearTimeout(timer.timeout); clearTimeout(timer.timeout);
timer.timeout = null; timer.timeout = null;

View File

@ -144,7 +144,6 @@ export { get_user } from "./utils/get_user";
export { range_date } from "./utils/ranged_date"; 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 generateFilter } 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";
@ -197,7 +196,7 @@ export * from "lib/comps/ui/input";
export { ButtonUpload } from "lib/preset/profile/ButtonUpload"; export { ButtonUpload } from "lib/preset/profile/ButtonUpload";
export { Profile } from "lib/preset/profile/Profile"; export { Profile } from "lib/preset/profile/Profile";
export { generateProfile } from "lib/preset/profile/utils/generate"; export { generateProfile } from "lib/preset/profile/utils/generate";
export { formatTime, longDate, shortDate, timeAgo } from "lib/utils/date"; export { formatTime, longDate, shortDate, timeAgo, formatDay } from "lib/utils/date";
export { getBasename, getPathname } from "lib/utils/pathname"; export { getBasename, getPathname } from "lib/utils/pathname";
export { formatMoney } from "lib/comps/form/field/type/TypeMoney"; export { formatMoney } from "lib/comps/form/field/type/TypeMoney";

View File

@ -3,6 +3,8 @@ import { useLocal } from "lib/utils/use-local";
import get from "lodash.get"; import get from "lodash.get";
import { FC, useEffect, useRef } from "react"; import { FC, useEffect, useRef } from "react";
import { IMenu, MenuProp } from "./utils/type-menu"; import { IMenu, MenuProp } from "./utils/type-menu";
import { icons } from "app/icons";
import { FieldLoading } from "lib/exports";
// import { icon } from "../../.."; // import { icon } from "../../..";
const local_default = { const local_default = {
@ -14,19 +16,19 @@ const local_default = {
pathname: "", pathname: "",
loading: false, loading: false,
init: false, init: false,
ready: false,
menu: [] as any[],
}; };
type MLocal = typeof local_default & { render: () => void }; type MLocal = typeof local_default & { render: () => void };
export const Menu: FC<MenuProp> = (props) => { export const Menu: FC<MenuProp> = (props) => {
const imenu = props.menu;
let role = props.role;
role = props.on_init();
let menu = get(imenu, role) || [];
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
const local = useLocal({ ...local_default }, ({ setDelayedRender }) => { const local = useLocal({ ...local_default }, ({ setDelayedRender }) => {
setDelayedRender(true); setDelayedRender(true);
}); });
const imenu = props.menu;
// useEffect(() => {
// }, []);
if (local.pathname !== getPathname()) { if (local.pathname !== getPathname()) {
local.pathname = getPathname(); local.pathname = getPathname();
} }
@ -37,9 +39,30 @@ export const Menu: FC<MenuProp> = (props) => {
useEffect(() => { useEffect(() => {
local.mode = props.mode; local.mode = props.mode;
local.ready = false;
local.render();
if (typeof imenu === "function") {
const res = imenu();
if (res instanceof Promise) {
res.then((e) => {
local.menu = e;
local.render();
})
}else{
local.menu = res;
local.render();
}
} else {
let role = props.role;
role = props.on_init();
let menu = get(imenu, role) || [];
local.menu = menu;
}
local.ready = true;
local.render(); local.render();
}, [props.mode]); }, [props.mode]);
if (!local.ready) return <FieldLoading />;
return ( return (
<div <div
className={cx("c-overflow-y-auto c-relative c-h-full c-w-full c-flex-1")} className={cx("c-overflow-y-auto c-relative c-h-full c-w-full c-flex-1")}
@ -47,7 +70,7 @@ export const Menu: FC<MenuProp> = (props) => {
> >
<div className="sidebar-menu c-absolute c-inset-0 c-flex c-flex-col c-flex-grow c-px-3 c-py-4 "> <div className="sidebar-menu c-absolute c-inset-0 c-flex c-flex-col c-flex-grow c-px-3 c-py-4 ">
<SideBar <SideBar
data={menu} data={local.menu}
local={local} local={local}
pm={props} pm={props}
depth={0} depth={0}
@ -88,6 +111,9 @@ export const SideBar: FC<{
pm, pm,
}; };
const data: IMenu[] = (typeof _data[0] === "string" ? [_data] : _data) as any; const data: IMenu[] = (typeof _data[0] === "string" ? [_data] : _data) as any;
if (!data.length) {
return <></>;
}
useEffect(() => { useEffect(() => {
data.map((item) => { data.map((item) => {
const menu = { const menu = {

View File

@ -4,7 +4,7 @@ export type IMenu = [string, any, string | IMenu[]];
export type MenuProp = { export type MenuProp = {
role: string; role: string;
on_init: () => string; on_init: () => string;
menu: Array<Record<string, IMenu[]>>; menu: Array<Record<string, IMenu[]>> | (() => Promise<any>);
PassProp: any; PassProp: any;
child: ReactNode; child: ReactNode;
mode: "full" | "mini"; mode: "full" | "mini";
@ -12,7 +12,9 @@ export type MenuProp = {
style: "navbar" | "sidebar"; style: "navbar" | "sidebar";
layout?: { current_menu: string; render: () => void }; layout?: { current_menu: string; render: () => void };
on_load?: (on_done: (exec: () => void) => void) => void; on_load?: (on_done: (exec: () => void) => void) => void;
get_menu?: (mn: Array<Record<string, IMenu[]>>) => Array<Record<string, IMenu[]>> get_menu?: (
mn: Array<Record<string, IMenu[]>>
) => Array<Record<string, IMenu[]>>;
}; };
export type MenuActive = { export type MenuActive = {
data: any; data: any;

View File

@ -73,7 +73,6 @@ export const useServerRouter = <T extends ReturnType<typeof newServerRouter>>(
async handle(arg: ServerContext | SessionContext<any>) { async handle(arg: ServerContext | SessionContext<any>) {
const { url, req, handle } = arg; const { url, req, handle } = arg;
const found = findRoute(rou, undefined, url.pathname); const found = findRoute(rou, undefined, url.pathname);
if (found) { if (found) {
const route = found.data; const route = found.data;

View File

@ -3,6 +3,13 @@ import relative from "dayjs/plugin/relativeTime";
day.extend(relative); day.extend(relative);
export const formatDay = (date: string | Date, mode: string) => {
if (date instanceof Date || typeof date === "string") {
return day(date).format(mode);
}
return "-";
};
export const longDate = (date: string | Date) => { export const longDate = (date: string | Date) => {
if (date instanceof Date || typeof date === "string") { if (date instanceof Date || typeof date === "string") {
return day(date).format("DD MMM YYYY - hh:mm"); return day(date).format("DD MMM YYYY - hh:mm");
@ -23,6 +30,7 @@ export const timeAgo = (date: string | Date) => {
} }
return "-"; return "-";
}; };
export const formatTime = (date: string | Date) => { export const formatTime = (date: string | Date) => {
if (date instanceof Date || typeof date === "string") { if (date instanceof Date || typeof date === "string") {
return day(date).format("hh:mm"); return day(date).format("hh:mm");