wip fix
This commit is contained in:
parent
4eebe5bb99
commit
ab80962b47
|
|
@ -0,0 +1,92 @@
|
||||||
|
import { useLocal } from "@/utils/use-local";
|
||||||
|
import { FC, ReactNode, useEffect } from "react";
|
||||||
|
import { Skeleton } from "../ui/skeleton";
|
||||||
|
|
||||||
|
type BreadcrumbProps = {
|
||||||
|
on_load: () => Promise<any[]>;
|
||||||
|
props: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Breadcrumb: FC<BreadcrumbProps> = (_arg) => {
|
||||||
|
const { on_load } = _arg;
|
||||||
|
|
||||||
|
const local = useLocal({
|
||||||
|
list: [] as { label: string; url: string }[],
|
||||||
|
status: "init" as "init" | "loading" | "ready",
|
||||||
|
params: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
if (local.status === "init") {
|
||||||
|
local.status = "loading";
|
||||||
|
local.render();
|
||||||
|
|
||||||
|
local.list = await on_load();
|
||||||
|
|
||||||
|
local.status = "ready";
|
||||||
|
local.render();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}, [on_load]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="c-w-full c-flex c-items-center c-px-4 c-flex-wrap c-py-2 c-border-b">
|
||||||
|
{local.status !== "ready" ? (
|
||||||
|
<Skeleton className="c-h-4 c-w-[80%]" />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{local.list === null ? (
|
||||||
|
<>
|
||||||
|
<h1 className="c-font-semibold c-text-xs md:c-text-base">
|
||||||
|
Dummy
|
||||||
|
</h1>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
(local.list || []).map((item, index): ReactNode => {
|
||||||
|
const lastIndex = local.list.length - 1;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{index === lastIndex ? (
|
||||||
|
<h1 className="c-font-semibold c-text-xs md:c-text-base">
|
||||||
|
{item?.label}
|
||||||
|
</h1>
|
||||||
|
) : (
|
||||||
|
<h1
|
||||||
|
className="c-font-normal c-text-xs md:c-text-base hover:c-cursor-pointer"
|
||||||
|
onClick={() => {
|
||||||
|
navigate(item?.url);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item?.label}
|
||||||
|
</h1>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{index !== lastIndex && (
|
||||||
|
<div>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
className="lucide lucide-chevron-right"
|
||||||
|
>
|
||||||
|
<path d="m9 18 6-6-6-6" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -6,7 +6,9 @@ import { Skeleton } from "../ui/skeleton";
|
||||||
|
|
||||||
export const Card: FC<{
|
export const Card: FC<{
|
||||||
title: { left: ReactNode; right: ReactNode };
|
title: { left: ReactNode; right: ReactNode };
|
||||||
desc: (() => Promise<ReactNode>) | ReactNode;
|
desc:
|
||||||
|
| ((arg: { setOnClick: (fn: any) => void }) => Promise<ReactNode>)
|
||||||
|
| ReactNode;
|
||||||
value: (() => Promise<ReactNode>) | ReactNode;
|
value: (() => Promise<ReactNode>) | ReactNode;
|
||||||
}> = ({ title, desc, value }) => {
|
}> = ({ title, desc, value }) => {
|
||||||
const local = useLocal({
|
const local = useLocal({
|
||||||
|
|
@ -14,6 +16,7 @@ export const Card: FC<{
|
||||||
desc: "" as any,
|
desc: "" as any,
|
||||||
status_value: "init" as "init" | "loading" | "ready",
|
status_value: "init" as "init" | "loading" | "ready",
|
||||||
status_desc: "init" as "init" | "loading" | "ready",
|
status_desc: "init" as "init" | "loading" | "ready",
|
||||||
|
onClick: null as null | (() => void),
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -38,7 +41,12 @@ export const Card: FC<{
|
||||||
if (!!desc && typeof desc === "function") {
|
if (!!desc && typeof desc === "function") {
|
||||||
local.status_desc = "loading";
|
local.status_desc = "loading";
|
||||||
local.render();
|
local.render();
|
||||||
const result = desc();
|
const result = desc({
|
||||||
|
setOnClick: (fn) => {
|
||||||
|
local.onClick = fn;
|
||||||
|
local.render();
|
||||||
|
},
|
||||||
|
});
|
||||||
if (typeof result === "object" && result instanceof Promise) {
|
if (typeof result === "object" && result instanceof Promise) {
|
||||||
result.then((val) => {
|
result.then((val) => {
|
||||||
local.desc = val;
|
local.desc = val;
|
||||||
|
|
@ -71,7 +79,14 @@ export const Card: FC<{
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<card.Card className="c-flex c-flex-1 c-items-center">
|
<card.Card
|
||||||
|
className="c-flex c-flex-1 c-items-center"
|
||||||
|
onClick={() => {
|
||||||
|
if (local.onClick) {
|
||||||
|
local.onClick();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div className={cn("c-p-3 c-text-[14px] c-flex-1")}>
|
<div className={cn("c-p-3 c-text-[14px] c-flex-1")}>
|
||||||
{!!title && (title.left || title.right) && (
|
{!!title && (title.left || title.right) && (
|
||||||
<div className="c-tracking-tight c-text-sm c-font-medium c-flex c-justify-between c-space-x-1 mb-1 c-items-center">
|
<div className="c-tracking-tight c-text-sm c-font-medium c-flex c-justify-between c-space-x-1 mb-1 c-items-center">
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,10 @@ import { Skeleton } from "../ui/skeleton";
|
||||||
|
|
||||||
export const Detail: FC<{
|
export const Detail: FC<{
|
||||||
detail: (item: any) => Record<string, [string, string, string]>;
|
detail: (item: any) => Record<string, [string, string, string]>;
|
||||||
on_load: (arg: { params: any }) => Promise<any>;
|
on_load: (arg: {
|
||||||
|
params: any;
|
||||||
|
bind: (fn: (on_load: any) => void) => void;
|
||||||
|
}) => Promise<any>;
|
||||||
mode: "standard" | "compact" | "inline";
|
mode: "standard" | "compact" | "inline";
|
||||||
}> = ({ detail, mode, on_load }) => {
|
}> = ({ detail, mode, on_load }) => {
|
||||||
const local = useLocal({
|
const local = useLocal({
|
||||||
|
|
@ -15,6 +18,7 @@ export const Detail: FC<{
|
||||||
pathname: "",
|
pathname: "",
|
||||||
mode: mode,
|
mode: mode,
|
||||||
on_load,
|
on_load,
|
||||||
|
bound: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!isEditor) {
|
if (!isEditor) {
|
||||||
|
|
@ -39,7 +43,24 @@ export const Detail: FC<{
|
||||||
}
|
}
|
||||||
local.render();
|
local.render();
|
||||||
|
|
||||||
const res = on_load({ params: {} });
|
const res = on_load({
|
||||||
|
params: {},
|
||||||
|
bind: (fn) => {
|
||||||
|
if (!local.bound) {
|
||||||
|
local.bound = true;
|
||||||
|
local.render();
|
||||||
|
|
||||||
|
fn(async () => {
|
||||||
|
local.status = "loading";
|
||||||
|
local.render();
|
||||||
|
const item = await on_load({} as any);
|
||||||
|
local.detail = detail(item);
|
||||||
|
local.status = "ready";
|
||||||
|
local.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
if (typeof res === "object" && res instanceof Promise) {
|
if (typeof res === "object" && res instanceof Promise) {
|
||||||
res.then((item) => {
|
res.then((item) => {
|
||||||
local.detail = detail(item);
|
local.detail = detail(item);
|
||||||
|
|
@ -70,7 +91,34 @@ export const Detail: FC<{
|
||||||
"c-flex c-relative items-stretch",
|
"c-flex c-relative items-stretch",
|
||||||
mode === "inline"
|
mode === "inline"
|
||||||
? "c-flex-row c-my-2"
|
? "c-flex-row c-my-2"
|
||||||
: "c-flex-col c-flex-1 c-w-full c-h-full "
|
: "c-flex-col c-flex-1 c-w-full c-h-full ",
|
||||||
|
isDesktop &&
|
||||||
|
entries.length > 3 &&
|
||||||
|
mode === "compact" &&
|
||||||
|
css`
|
||||||
|
flex-direction: row !important;
|
||||||
|
flex-wrap: wrap !important;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: " ";
|
||||||
|
position: absolute;
|
||||||
|
left: 49%;
|
||||||
|
bottom: 0px;
|
||||||
|
top: 0px;
|
||||||
|
border-right: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
> div {
|
||||||
|
border-top: 0px;
|
||||||
|
padding-right: 10px;
|
||||||
|
padding-left: 10px;
|
||||||
|
width: 49% !important;
|
||||||
|
}
|
||||||
|
`
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{entries.map(([name, data], idx) => {
|
{entries.map(([name, data], idx) => {
|
||||||
|
|
@ -90,7 +138,7 @@ export const Detail: FC<{
|
||||||
|
|
||||||
if (mode === "standard") {
|
if (mode === "standard") {
|
||||||
return (
|
return (
|
||||||
<div key={idx} className="c-flex c-flex-col c-items-stretch">
|
<div key={idx} className="c-flex c-flex-col c-items-stretch c-pt-3">
|
||||||
<div className="c-flex c-font-bold">{label}</div>
|
<div className="c-flex c-font-bold">{label}</div>
|
||||||
<div className="c-flex">
|
<div className="c-flex">
|
||||||
<Linkable
|
<Linkable
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,166 @@
|
||||||
|
import { useLocal } from "@/utils/use-local";
|
||||||
|
import { FC, ReactNode, useEffect } from "react";
|
||||||
|
import { Skeleton } from "../ui/skeleton";
|
||||||
|
|
||||||
|
export const Header: FC<{
|
||||||
|
text: string;
|
||||||
|
back_url: string | (() => void);
|
||||||
|
props: any;
|
||||||
|
actions: () => Promise<[string, string | (() => void), ReactNode][]>;
|
||||||
|
action_mode: "auto" | "buttons" | "sub-header" | "dropdown";
|
||||||
|
edit_url: string;
|
||||||
|
}> = ({ text, back_url, props, edit_url, actions, action_mode }) => {
|
||||||
|
const local = useLocal({
|
||||||
|
loading: false,
|
||||||
|
actions: [] as [string, string | (() => void), ReactNode][],
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
if (typeof actions === "function") {
|
||||||
|
local.loading = true;
|
||||||
|
local.render();
|
||||||
|
const res_actions = await actions();
|
||||||
|
if (Array.isArray(res_actions)) {
|
||||||
|
local.actions = res_actions;
|
||||||
|
preload(
|
||||||
|
res_actions
|
||||||
|
.filter((e) => typeof e[1] === "string")
|
||||||
|
.map((e) => e[1]) as string[]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
local.loading = false;
|
||||||
|
local.render();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}, [actions, action_mode]);
|
||||||
|
|
||||||
|
const back = () => {
|
||||||
|
if (isEditor) return;
|
||||||
|
if (typeof back_url === "function") {
|
||||||
|
const url = back_url();
|
||||||
|
if (typeof url === "string") {
|
||||||
|
if (url === "back") {
|
||||||
|
history.back();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
navigate(url);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (back_url === "back") {
|
||||||
|
history.back();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
navigate(back_url);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
{...props}
|
||||||
|
className={cx(
|
||||||
|
props.className,
|
||||||
|
"whitespace-pre-wrap",
|
||||||
|
css`
|
||||||
|
min-height: 50px !important;
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{typeof back_url === "string" && preload([back_url])}
|
||||||
|
{back_url && (
|
||||||
|
<div className="mr-2" onClick={back}>
|
||||||
|
<svg
|
||||||
|
width="25"
|
||||||
|
height="25"
|
||||||
|
viewBox="0 0 15 15"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M8.84182 3.13514C9.04327 3.32401 9.05348 3.64042 8.86462 3.84188L5.43521 7.49991L8.86462 11.1579C9.05348 11.3594 9.04327 11.6758 8.84182 11.8647C8.64036 12.0535 8.32394 12.0433 8.13508 11.8419L4.38508 7.84188C4.20477 7.64955 4.20477 7.35027 4.38508 7.15794L8.13508 3.15794C8.32394 2.95648 8.64036 2.94628 8.84182 3.13514Z"
|
||||||
|
fill="currentColor"
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="w-full flex flex-row justify-between items-center">
|
||||||
|
<div className="c-whitespace-nowrap" onClick={back}>
|
||||||
|
{text}
|
||||||
|
</div>
|
||||||
|
<div className="c-flex">
|
||||||
|
{local.loading && (
|
||||||
|
<Skeleton
|
||||||
|
className={cx(
|
||||||
|
css`
|
||||||
|
height: 10px;
|
||||||
|
min-width: 40px;
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{!local.loading && (
|
||||||
|
<>
|
||||||
|
{local.actions.map((e) => {
|
||||||
|
const [label, to, icon] = e;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="c-bg-primary hover:c-bg-primary/90 c-p-1 c-rounded-md c-text-sm c-flex c-text-white c-items-center c-space-x-1 c-px-2 c-ml-2"
|
||||||
|
onClick={() => {
|
||||||
|
if (isEditor) return;
|
||||||
|
if (typeof to === "string") {
|
||||||
|
navigate(to);
|
||||||
|
} else if (typeof to === "function") {
|
||||||
|
to();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={css`
|
||||||
|
svg {
|
||||||
|
max-width: 15px;
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{icon}
|
||||||
|
</div>
|
||||||
|
<div>{label}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* {edit_url && edit_url !== "" && params.id !== "_" && (
|
||||||
|
<div
|
||||||
|
className="c-bg-primary hover:c-bg-primary/90 c-p-1 c-rounded-md c-ml-2"
|
||||||
|
onClick={() => {
|
||||||
|
if (isEditor) return;
|
||||||
|
if (typeof edit_url === "string") {
|
||||||
|
navigate(edit_url);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="white"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
className="lucide lucide-pencil-line"
|
||||||
|
>
|
||||||
|
<path d="M12 20h9" />
|
||||||
|
<path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z" />
|
||||||
|
<path d="m15 5 3 3" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
)} */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { FC } from "react";
|
import { FC, useEffect } from "react";
|
||||||
import { Tabs, TabsList, TabsTrigger } from "../ui/tabs";
|
import { Tabs, TabsList, TabsTrigger } from "../ui/tabs";
|
||||||
import { useLocal } from "@/utils/use-local";
|
import { useLocal } from "@/utils/use-local";
|
||||||
|
|
||||||
|
|
@ -64,6 +64,17 @@ export const Tab: FC<{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isEditor) {
|
||||||
|
useEffect(() => {
|
||||||
|
if (local.mode === "hash") {
|
||||||
|
local.activeIndex = location.hash.substring(1);
|
||||||
|
if (!parseInt(local.activeIndex)) {
|
||||||
|
local.activeIndex = "0";
|
||||||
|
}
|
||||||
|
local.render();
|
||||||
|
}
|
||||||
|
}, [location.hash]);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className="c-flex c-flex-1 c-w-full c-flex-col c-items-stretch">
|
<div className="c-flex c-flex-1 c-w-full c-flex-col c-items-stretch">
|
||||||
<Tabs
|
<Tabs
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
import { useLocal } from "@/utils/use-local";
|
||||||
|
import { FC, useEffect } from "react";
|
||||||
|
import { icon } from "../icon";
|
||||||
|
|
||||||
|
type TableProps = {
|
||||||
|
map_val: Array<{ name: string }>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Table: FC<TableProps> = (_args) => {
|
||||||
|
const { map_val } = _args;
|
||||||
|
|
||||||
|
const local = useLocal({
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
name: "test1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test2",
|
||||||
|
},
|
||||||
|
] as { name: string }[],
|
||||||
|
status: "init" as "init" | "loading" | "ready",
|
||||||
|
});
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// (async () => {
|
||||||
|
// if (local.status === "init") {
|
||||||
|
// local.status = "loading";
|
||||||
|
// local.render();
|
||||||
|
|
||||||
|
// local.list = await map_val;
|
||||||
|
// local.render();
|
||||||
|
|
||||||
|
// local.status = "ready";
|
||||||
|
// local.render();
|
||||||
|
// }
|
||||||
|
// })();
|
||||||
|
// }, [map_val]);
|
||||||
|
|
||||||
|
console.log(local.list, "tes");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="c-overflow-x-auto c-w-full">
|
||||||
|
<table className="c-table-auto c-w-full c-border-collapse c-rounded-lg c-border c-border-gray-300">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th className="c-px-4 c-py-2 c-text-center">Nomor</th>
|
||||||
|
<th className="c-px-4 c-py-2 c-text-center">Header 1</th>
|
||||||
|
<th className="c-px-4 c-py-2 c-text-center">Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{!!local.list &&
|
||||||
|
local.list.map((item, index) => (
|
||||||
|
<tr
|
||||||
|
key={index}
|
||||||
|
className={index % 2 === 0 ? "c-bg-gray-100" : ""}
|
||||||
|
>
|
||||||
|
<td className="c-border c-px-4 c-py-2 c-text-center">
|
||||||
|
{index + 1}
|
||||||
|
</td>
|
||||||
|
<td className="c-border c-px-4 c-py-2">{item.name}</td>
|
||||||
|
<td className="c-border c-px-4 c-py-2 c-flex c-flex-col c-justify-center c-items-center c-space-y-2">
|
||||||
|
<button className="c-w-[50px] c-rounded c-flex c-justify-center c-bg-blue-300">
|
||||||
|
<span className="c-p-2">{icon.update}</span>
|
||||||
|
</button>
|
||||||
|
<button className="c-w-[50px] c-rounded c-flex c-justify-center c-bg-red-300">
|
||||||
|
<span className="c-p-2">{icon.delete}</span>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -6,31 +6,25 @@ import {
|
||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from "@/comps/ui/form";
|
} from "@/comps/ui/form";
|
||||||
|
import { useLocal } from "@/utils/use-local";
|
||||||
|
import autosize from "autosize";
|
||||||
import { FC, useEffect, useRef } from "react";
|
import { FC, useEffect, useRef } from "react";
|
||||||
import { UseFormReturn } from "react-hook-form";
|
import { UseFormReturn } from "react-hook-form";
|
||||||
import { Input } from "../ui/input";
|
|
||||||
import { useLocal } from "@/utils/use-local";
|
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from "@/comps/ui/popover";
|
import { Input } from "../ui/input";
|
||||||
import { format } from "date-fns";
|
|
||||||
import { Calendar } from "@/comps/ui/calendar";
|
|
||||||
import { Calendar as CalendarIcon } from "lucide-react";
|
|
||||||
import { PopUpDropdown } from "./PopUpDropdown";
|
|
||||||
import { ButtonOptions } from "./ButtonOptions";
|
|
||||||
import { Textarea } from "../ui/textarea";
|
import { Textarea } from "../ui/textarea";
|
||||||
import autosize from "autosize";
|
import { ButtonOptions } from "./ButtonOptions";
|
||||||
import { InputMoney } from "./InputMoney";
|
|
||||||
import { Date } from "./Date";
|
import { Date } from "./Date";
|
||||||
import { Datetime } from "./Datetime";
|
import { Datetime } from "./Datetime";
|
||||||
import { Slider } from "@radix-ui/react-slider";
|
import { InputMoney } from "./InputMoney";
|
||||||
|
import { PopUpDropdown } from "./PopUpDropdown";
|
||||||
import { SliderOptions } from "./Slider/types";
|
import { SliderOptions } from "./Slider/types";
|
||||||
import { cn } from "@/utils";
|
|
||||||
|
|
||||||
export const Field: FC<{
|
export const Field: FC<{
|
||||||
name: string;
|
name: string;
|
||||||
label: string;
|
label: string;
|
||||||
desc?: string;
|
desc?: string;
|
||||||
form: { hook: UseFormReturn<any, any, undefined>; render: () => void };
|
form?: { hook: UseFormReturn<any, any, undefined>; render: () => void };
|
||||||
type:
|
type:
|
||||||
| "text"
|
| "text"
|
||||||
| "textarea"
|
| "textarea"
|
||||||
|
|
@ -46,7 +40,7 @@ export const Field: FC<{
|
||||||
options: () => Promise<{ value: string; label: string }[]>;
|
options: () => Promise<{ value: string; label: string }[]>;
|
||||||
slider_options: () => Promise<SliderOptions>;
|
slider_options: () => Promise<SliderOptions>;
|
||||||
}> = ({ name, form, desc, label, type, required, options, slider_options }) => {
|
}> = ({ name, form, desc, label, type, required, options, slider_options }) => {
|
||||||
const value = form.hook.getValues()[name];
|
const value = form?.hook.getValues()[name];
|
||||||
const local = useLocal({
|
const local = useLocal({
|
||||||
dropdown: {
|
dropdown: {
|
||||||
popup: false,
|
popup: false,
|
||||||
|
|
@ -104,14 +98,14 @@ export const Field: FC<{
|
||||||
local.render();
|
local.render();
|
||||||
}}
|
}}
|
||||||
on_select={(value: any) => {
|
on_select={(value: any) => {
|
||||||
form.hook.setValue(name, value);
|
form?.hook.setValue(name, value);
|
||||||
}}
|
}}
|
||||||
title={label}
|
title={label}
|
||||||
options={options}
|
options={options}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<FormField
|
<FormField
|
||||||
control={form.hook.control}
|
control={form?.hook.control || {} as any}
|
||||||
name={name}
|
name={name}
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="c-flex c-flex-1 c-flex-col">
|
<FormItem className="c-flex c-flex-1 c-flex-col">
|
||||||
|
|
@ -138,7 +132,7 @@ export const Field: FC<{
|
||||||
value,
|
value,
|
||||||
local.slider.opt
|
local.slider.opt
|
||||||
);
|
);
|
||||||
form.hook.setValue(name, value);
|
form?.hook.setValue(name, value);
|
||||||
local.render();
|
local.render();
|
||||||
}}
|
}}
|
||||||
value={local.slider.value}
|
value={local.slider.value}
|
||||||
|
|
@ -180,7 +174,7 @@ export const Field: FC<{
|
||||||
{type === "date" && (
|
{type === "date" && (
|
||||||
<Date
|
<Date
|
||||||
on_select={(value: any) => {
|
on_select={(value: any) => {
|
||||||
form.hook.setValue(name, value);
|
form?.hook.setValue(name, value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
@ -188,7 +182,7 @@ export const Field: FC<{
|
||||||
{type === "datetime" && (
|
{type === "datetime" && (
|
||||||
<Datetime
|
<Datetime
|
||||||
on_select={(value: any) => {
|
on_select={(value: any) => {
|
||||||
form.hook.setValue(name, value);
|
form?.hook.setValue(name, value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
@ -198,7 +192,7 @@ export const Field: FC<{
|
||||||
options={options}
|
options={options}
|
||||||
value={field.value}
|
value={field.value}
|
||||||
on_select={(value: any) => {
|
on_select={(value: any) => {
|
||||||
form.hook.setValue(name, value);
|
form?.hook.setValue(name, value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
@ -207,7 +201,7 @@ export const Field: FC<{
|
||||||
<InputMoney
|
<InputMoney
|
||||||
value={field.value}
|
value={field.value}
|
||||||
on_select={(value: any) => {
|
on_select={(value: any) => {
|
||||||
form.hook.setValue(name, value);
|
form?.hook.setValue(name, value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { Form as FForm } from "@/comps/ui/form";
|
import { Form as FForm } from "@/comps/ui/form";
|
||||||
|
import { useLocal } from "@/utils/use-local";
|
||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
|
||||||
|
|
@ -8,13 +9,22 @@ export const Form: FC<{
|
||||||
body: any;
|
body: any;
|
||||||
form: { hook: any; render: () => void };
|
form: { hook: any; render: () => void };
|
||||||
PassProp: any;
|
PassProp: any;
|
||||||
}> = ({ on_load, body, form, PassProp, on_submit }) => {
|
layout: "auto" | "1-col" | "2-col";
|
||||||
|
}> = ({ on_load, body, form, PassProp, on_submit, layout: _layout }) => {
|
||||||
const form_hook = useForm<any>({
|
const form_hook = useForm<any>({
|
||||||
defaultValues: on_load,
|
defaultValues: on_load,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const local = useLocal({
|
||||||
|
el: null as any,
|
||||||
|
layout: "unknown" as "unknown" | "2-col" | "1-col",
|
||||||
|
});
|
||||||
|
|
||||||
form.hook = form_hook;
|
form.hook = form_hook;
|
||||||
|
|
||||||
|
let layout = _layout || "auto";
|
||||||
|
if (layout !== "auto") local.layout = layout;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FForm {...form_hook}>
|
<FForm {...form_hook}>
|
||||||
<form
|
<form
|
||||||
|
|
@ -27,7 +37,44 @@ export const Form: FC<{
|
||||||
on_submit({ form: form.hook.getValues(), error: {} });
|
on_submit({ form: form.hook.getValues(), error: {} });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="absolute inset-0">
|
<div
|
||||||
|
ref={(el) => {
|
||||||
|
if (el && layout === "auto" && local.layout === "unknown") {
|
||||||
|
let cur: any = el;
|
||||||
|
let i = 0;
|
||||||
|
while (
|
||||||
|
cur.parentNode &&
|
||||||
|
cur.getBoundingClientRect().width === 0
|
||||||
|
) {
|
||||||
|
cur = cur.parentNode;
|
||||||
|
i++;
|
||||||
|
if (i > 10) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cur.getBoundingClientRect().width < 500) {
|
||||||
|
local.layout = "1-col";
|
||||||
|
} else {
|
||||||
|
local.layout = "2-col";
|
||||||
|
}
|
||||||
|
local.render();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className={cx(
|
||||||
|
local.layout === "unknown" && "c-hidden",
|
||||||
|
local.layout === "2-col" &&
|
||||||
|
css`
|
||||||
|
> div {
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
> div {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
>
|
||||||
<PassProp
|
<PassProp
|
||||||
submit={() => {
|
submit={() => {
|
||||||
on_submit({ form: form.hook.getValues(), error: {} });
|
on_submit({ form: form.hook.getValues(), error: {} });
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,48 @@
|
||||||
import { Bell, Calendar, ChevronRight, UsersRound, ChevronDown, Contact2, Drill, HelpCircle, Home, LayoutList, ListChecks, ListTodo, PenBoxIcon, QrCode, UserCog, Workflow, Settings, Newspaper, PanelRightOpen, PanelLeftOpen, LayoutDashboard, Building, Box, Package, Blocks, Megaphone, MousePointerSquare, Siren, Ship, HeartHandshake, StickyNote, Volume2, Wallet, FileDown, Menu, X, LogOut, Rocket, BookOpen } from "lucide-react";
|
import {
|
||||||
|
Bell,
|
||||||
|
Calendar,
|
||||||
|
ChevronRight,
|
||||||
|
UsersRound,
|
||||||
|
ChevronDown,
|
||||||
|
Contact2,
|
||||||
|
Drill,
|
||||||
|
HelpCircle,
|
||||||
|
Home,
|
||||||
|
LayoutList,
|
||||||
|
ListChecks,
|
||||||
|
ListTodo,
|
||||||
|
PenBoxIcon,
|
||||||
|
QrCode,
|
||||||
|
UserCog,
|
||||||
|
Workflow,
|
||||||
|
Settings,
|
||||||
|
Newspaper,
|
||||||
|
PanelRightOpen,
|
||||||
|
PanelLeftOpen,
|
||||||
|
LayoutDashboard,
|
||||||
|
Building,
|
||||||
|
Box,
|
||||||
|
Package,
|
||||||
|
Blocks,
|
||||||
|
Megaphone,
|
||||||
|
MousePointerSquare,
|
||||||
|
Siren,
|
||||||
|
Ship,
|
||||||
|
HeartHandshake,
|
||||||
|
StickyNote,
|
||||||
|
Volume2,
|
||||||
|
Wallet,
|
||||||
|
FileDown,
|
||||||
|
Menu,
|
||||||
|
X,
|
||||||
|
LogOut,
|
||||||
|
Rocket,
|
||||||
|
BookOpen,
|
||||||
|
Pencil,
|
||||||
|
Plus,
|
||||||
|
Delete,
|
||||||
|
Edit,
|
||||||
|
} from "lucide-react";
|
||||||
|
|
||||||
export const icon = {
|
export const icon = {
|
||||||
home: <Home />,
|
home: <Home />,
|
||||||
|
|
@ -14,8 +58,10 @@ export const icon = {
|
||||||
manage_user: <UserCog />,
|
manage_user: <UserCog />,
|
||||||
notification: <Bell />,
|
notification: <Bell />,
|
||||||
profile: <Contact2 />,
|
profile: <Contact2 />,
|
||||||
|
pencil: <Pencil />,
|
||||||
|
plus: <Plus />,
|
||||||
bell: <Bell />,
|
bell: <Bell />,
|
||||||
bellSmall: <Bell className={`c-w-4`} fill="currentColor" />,
|
bellSmall: <Bell className={`c-w-4`} fill="currentColor" />,
|
||||||
bellSmallTransparent: <Bell className={`c-w-4`} />,
|
bellSmallTransparent: <Bell className={`c-w-4`} />,
|
||||||
help: <HelpCircle />,
|
help: <HelpCircle />,
|
||||||
user: <UsersRound />,
|
user: <UsersRound />,
|
||||||
|
|
@ -53,6 +99,20 @@ export const icon = {
|
||||||
releaseSmallTransparent: <Rocket className={`c-w-4`} />,
|
releaseSmallTransparent: <Rocket className={`c-w-4`} />,
|
||||||
newsSmall: <BookOpen className={`c-w-4`} fill="currentColor" />,
|
newsSmall: <BookOpen className={`c-w-4`} fill="currentColor" />,
|
||||||
newsSmallTransparent: <BookOpen className={`c-w-4`} />,
|
newsSmallTransparent: <BookOpen className={`c-w-4`} />,
|
||||||
|
scan: (
|
||||||
|
<svg
|
||||||
|
width="28"
|
||||||
|
height="28"
|
||||||
|
viewBox="0 0 28 28"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M27.5 20V27.5H0.5V20H3.5V24.5H24.5V20H27.5ZM0.5 12.5H27.5V15.5H0.5V12.5ZM27.5 8H24.5V3.5H3.5V8H0.5V0.5H27.5V8Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
delete: <Delete />,
|
||||||
|
update: <Edit />
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ export const List: FC<ListProp> = (_arg) => {
|
||||||
list: [] as any[],
|
list: [] as any[],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isEditor) {
|
if (isEditor || typeof on_load !== 'function') {
|
||||||
return <ListDummy {..._arg} />;
|
return <ListDummy {..._arg} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,14 +35,16 @@ export const List: FC<ListProp> = (_arg) => {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
if (local.status === "init") {
|
if (typeof on_load === "function") {
|
||||||
local.status = "loading";
|
if (local.status === "init") {
|
||||||
local.render();
|
local.status = "loading";
|
||||||
|
local.render();
|
||||||
|
|
||||||
local.list = await on_load({ params: local.params });
|
local.list = await on_load({ params: local.params });
|
||||||
|
|
||||||
local.status = "ready";
|
local.status = "ready";
|
||||||
local.render();
|
local.render();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}, [on_load]);
|
}, [on_load]);
|
||||||
|
|
@ -61,13 +63,16 @@ export const List: FC<ListProp> = (_arg) => {
|
||||||
<ListDummy {..._arg} />
|
<ListDummy {..._arg} />
|
||||||
) : (
|
) : (
|
||||||
(local.list || []).map((item, idx) => {
|
(local.list || []).map((item, idx) => {
|
||||||
|
const mapped = map_val(item);
|
||||||
const val = (...arg: any[]) => {
|
const val = (...arg: any[]) => {
|
||||||
const value = get(map_val(item), `${arg.join("")}`);
|
const value = get(mapped, `${arg.join("")}`);
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div key={item} className="c-border-b">
|
<div key={item} className="c-border-b">
|
||||||
<PassProp item={val}>{row}</PassProp>
|
<PassProp item={val} row={mapped}>
|
||||||
|
{row}
|
||||||
|
</PassProp>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|
@ -76,8 +81,8 @@ export const List: FC<ListProp> = (_arg) => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ListDummy = ({ props, row, PassProp, map_val, mode }: ListProp) => {
|
const ListDummy = ({ props, row, PassProp, map_val, mode }: ListProp) => {
|
||||||
const item = (...arg: string[]) => {
|
const item = (...arg: string[]) => {
|
||||||
|
|
|
||||||
|
|
@ -56,4 +56,21 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||||
);
|
);
|
||||||
Button.displayName = "Button";
|
Button.displayName = "Button";
|
||||||
|
|
||||||
export { Button, buttonVariants };
|
const FloatButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||||
|
({ variant, className, ...props }, ref) => {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={cn(
|
||||||
|
buttonVariants({ variant, className }),
|
||||||
|
`btn-${
|
||||||
|
variant || "default"
|
||||||
|
} btn c-transition-all c-duration-300 c-w-10 c-h-10 c-rounded-full c-z-50 c-absolute c-bottom-7 c-right-6 c-shadow-sm`
|
||||||
|
)}
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export { Button, buttonVariants, FloatButton };
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,9 @@ export { formatMoney } from "@/comps/form/InputMoney";
|
||||||
export { icon } from "@/comps/icon";
|
export { icon } from "@/comps/icon";
|
||||||
export { List } from "@/comps/list/List";
|
export { List } from "@/comps/list/List";
|
||||||
export { Slider } from "@/comps/ui/slider";
|
export { Slider } from "@/comps/ui/slider";
|
||||||
export { longDate, shortDate } from "@/utils/date";
|
export * from "@/utils/date";
|
||||||
export { Button } from "@/comps/ui/button";
|
export { Button, FloatButton } from "@/comps/ui/button";
|
||||||
export { getPathname } from "@/utils/pathname";
|
export { getPathname } from "@/utils/pathname";
|
||||||
|
export { Breadcrumb } from "./comps/custom/Breadcrumb";
|
||||||
|
export { Header } from "./comps/custom/Header";
|
||||||
|
export { Table } from "./comps/custom/Table";
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { format } from "date-fns";
|
import { format, formatDistanceToNow } from "date-fns";
|
||||||
|
|
||||||
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") {
|
||||||
|
|
@ -13,3 +13,10 @@ export const shortDate = (date: string | Date) => {
|
||||||
}
|
}
|
||||||
return "-";
|
return "-";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const timeAgo = (date: string | Date) => {
|
||||||
|
if (date instanceof Date || typeof date === "string") {
|
||||||
|
return formatDistanceToNow(date, { addSuffix: true });
|
||||||
|
}
|
||||||
|
return "-";
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,13 @@ export const getPathname = () => {
|
||||||
location.pathname.startsWith("/prod") ||
|
location.pathname.startsWith("/prod") ||
|
||||||
location.pathname.startsWith("/deploy")
|
location.pathname.startsWith("/deploy")
|
||||||
) {
|
) {
|
||||||
return "/" + location.pathname.split("/").slice(3).join("/");
|
const hash = location.hash;
|
||||||
|
|
||||||
|
if (hash !== "") {
|
||||||
|
return "/" + location.pathname.split("/").slice(3).join("/") + hash;
|
||||||
|
} else {
|
||||||
|
return "/" + location.pathname.split("/").slice(3).join("/");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return location.pathname;
|
return location.pathname;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue