diff --git a/package-lock.json b/package-lock.json
index 535bb986..f960d1c5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -36,9 +36,9 @@
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
- "daisyui": "^5.1.12",
+ "daisyui": "^5.5.8",
"eslint": "^9",
- "eslint-config-next": "15.5.3",
+ "eslint-config-next": "^15.5.7",
"husky": "^9.1.7",
"prettier": "^3.6.2",
"tailwindcss": "^4",
@@ -1088,9 +1088,9 @@
"license": "MIT"
},
"node_modules/@next/eslint-plugin-next": {
- "version": "15.5.3",
- "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.3.tgz",
- "integrity": "sha512-SdhaKdko6dpsSr0DldkESItVrnPYB1NS2NpShCSX5lc7SSQmLZt5Mug6t2xbiuVWEVDLZSuIAoQyYVBYp0dR5g==",
+ "version": "15.5.7",
+ "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.7.tgz",
+ "integrity": "sha512-DtRU2N7BkGr8r+pExfuWHwMEPX5SD57FeA6pxdgCHODo+b/UgIgjE+rgWKtJAbEbGhVZ2jtHn4g3wNhWFoNBQQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3067,9 +3067,9 @@
"peer": true
},
"node_modules/daisyui": {
- "version": "5.3.10",
- "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.3.10.tgz",
- "integrity": "sha512-vmjyPmm0hvFhA95KB6uiGmWakziB2pBv6CUcs5Ka/3iMBMn9S+C3SZYx9G9l2JrgTZ1EFn61F/HrPcwaUm2kLQ==",
+ "version": "5.5.8",
+ "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.5.8.tgz",
+ "integrity": "sha512-6psL9jIEOFOw68V10j/BKCWcRgx8dh81mmNxShr+g7HDM6UHNoPharlp9zq/PQkHNuGU1ZQsajR3HgpvavbRKQ==",
"dev": true,
"license": "MIT",
"funding": {
@@ -3576,13 +3576,13 @@
}
},
"node_modules/eslint-config-next": {
- "version": "15.5.3",
- "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.5.3.tgz",
- "integrity": "sha512-e6j+QhQFOr5pfsc8VJbuTD9xTXJaRvMHYjEeLPA2pFkheNlgPLCkxdvhxhfuM4KGcqSZj2qEnpHisdTVs3BxuQ==",
+ "version": "15.5.7",
+ "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.5.7.tgz",
+ "integrity": "sha512-nU/TRGHHeG81NeLW5DeQT5t6BDUqbpsNQTvef1ld/tqHT+/zTx60/TIhKnmPISTTe++DVo+DLxDmk4rnwHaZVw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@next/eslint-plugin-next": "15.5.3",
+ "@next/eslint-plugin-next": "15.5.7",
"@rushstack/eslint-patch": "^1.10.3",
"@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0",
"@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0",
diff --git a/package.json b/package.json
index 85485ee3..e1f92aaf 100644
--- a/package.json
+++ b/package.json
@@ -39,9 +39,9 @@
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
- "daisyui": "^5.1.12",
+ "daisyui": "^5.5.8",
"eslint": "^9",
- "eslint-config-next": "15.5.3",
+ "eslint-config-next": "^15.5.7",
"husky": "^9.1.7",
"prettier": "^3.6.2",
"tailwindcss": "^4",
diff --git a/src/app/closing/detail/layout.tsx b/src/app/closing/detail/layout.tsx
new file mode 100644
index 00000000..7220dfa1
--- /dev/null
+++ b/src/app/closing/detail/layout.tsx
@@ -0,0 +1,11 @@
+import SuspenseHelper from '@/components/helper/SuspenseHelper';
+
+const Layout = ({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) => {
+ return {children};
+};
+
+export default Layout;
diff --git a/src/app/closing/detail/page.tsx b/src/app/closing/detail/page.tsx
new file mode 100644
index 00000000..6225b8dd
--- /dev/null
+++ b/src/app/closing/detail/page.tsx
@@ -0,0 +1,50 @@
+'use client';
+
+import { useRouter, useSearchParams } from 'next/navigation';
+import useSWR from 'swr';
+
+import ClosingDetail from '@/components/pages/closing/ClosingDetail';
+
+import { ClosingApi } from '@/services/api/closing';
+import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
+
+const ClosingDetailPage = () => {
+ const router = useRouter();
+ const searchParams = useSearchParams();
+
+ const closingId = searchParams.get('closingId');
+
+ const { data: closing, isLoading: isLoadingClosing } = useSWR(
+ closingId,
+ (id: number) => ClosingApi.getGeneralInfo(id)
+ );
+
+ if (!closingId) {
+ router.back();
+
+ return (
+
+
+
+ );
+ }
+
+ if (!isLoadingClosing && (!closing || isResponseError(closing))) {
+ router.replace('/404');
+ return;
+ }
+
+ return (
+
+ {isLoadingClosing && (
+
+ )}
+
+ {!isLoadingClosing && isResponseSuccess(closing) && (
+
+ )}
+
+ );
+};
+
+export default ClosingDetailPage;
diff --git a/src/app/closing/page.tsx b/src/app/closing/page.tsx
new file mode 100644
index 00000000..acaa3ee8
--- /dev/null
+++ b/src/app/closing/page.tsx
@@ -0,0 +1,11 @@
+import ClosingsTable from '@/components/pages/closing/ClosingsTable';
+
+const Closing = () => {
+ return (
+
+ );
+};
+
+export default Closing;
diff --git a/src/app/globals.css b/src/app/globals.css
index e50e020d..10b48ad5 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -7,26 +7,39 @@
default: false;
prefersdark: false;
color-scheme: 'light';
- --color-base-100: oklch(98% 0.001 106.423);
- --color-base-200: oklch(97% 0.001 106.424);
- --color-base-300: oklch(92% 0.003 48.717);
- --color-base-content: oklch(22.389% 0.031 278.072);
- --color-primary: oklch(60% 0.126 221.723);
- --color-primary-content: oklch(100% 0 0);
- --color-secondary: oklch(52% 0.105 223.128);
- --color-secondary-content: oklch(100% 0 0);
- --color-accent: oklch(45% 0.085 224.283);
- --color-accent-content: oklch(100% 0 0);
- --color-neutral: oklch(39% 0.07 227.392);
- --color-neutral-content: oklch(100% 0 0);
- --color-info: oklch(58% 0.158 241.966);
- --color-info-content: oklch(100% 0 0);
- --color-success: oklch(62% 0.194 149.214);
- --color-success-content: oklch(100% 0 0);
- --color-warning: oklch(85% 0.199 91.936);
- --color-warning-content: oklch(0% 0 0);
- --color-error: oklch(57% 0.245 27.325);
- --color-error-content: oklch(100% 0 0);
+
+ /* Primary Colors */
+ --color-primary: oklch(39.4% 0.177 301.9);
+ --color-primary-content: oklch(87.5% 0.038 274.5);
+
+ /* Secondary Colors */
+ --color-secondary: oklch(60.1% 0.258 335.7);
+ --color-secondary-content: oklch(99.4% 0.007 337.8);
+
+ /* Accent Colors */
+ --color-accent: oklch(76.2% 0.155 170.8);
+ --color-accent-content: oklch(7.2% 0.007 167.6);
+
+ /* Neutral Colors */
+ --color-neutral: oklch(22.4% 0.032 258.8);
+ --color-neutral-content: oklch(87.7% 0.016 257);
+
+ /* Base Colors */
+ --color-base-100: oklch(100% 0 0); /* #ffffff */
+ --color-base-200: oklch(97.2% 0 0); /* #f2f2f2 */
+ --color-base-300: oklch(93.1% 0.002 249.7); /* #e5e6e6 */
+ --color-base-content: oklch(18.6% 0.024 257.7); /* #1f2937 */
+
+ /* Status/Utility Colors */
+ --color-info: oklch(67.4% 0.176 238.9);
+ --color-info-content: oklch(0% 0 0); /* #000000 */
+ --color-success: oklch(62.3% 0.147 149);
+ --color-success-content: oklch(100% 0 0); /* #ffffff */
+ --color-warning: oklch(82.2% 0.165 91.9);
+ --color-warning-content: oklch(0% 0 0); /* #000000 */
+ --color-error: oklch(61.8% 0.203 27.8);
+ --color-error-content: oklch(100% 0 0); /* #fffffff */
+
--radius-selector: 0rem;
--radius-field: 0.25rem;
--radius-box: 0.25rem;
@@ -43,6 +56,12 @@
@theme {
--font-inter: var(--font-inter);
+
+ --container-sm: 40rem;
+ --container-md: 48rem;
+ --container-lg: 64rem;
+ --container-xl: 80rem;
+ --container-2xl: 96rem;
}
html {
diff --git a/src/app/inventory/adjustment/detail/page.tsx b/src/app/inventory/adjustment/detail/page.tsx
index acb9f8db..eb13647d 100644
--- a/src/app/inventory/adjustment/detail/page.tsx
+++ b/src/app/inventory/adjustment/detail/page.tsx
@@ -12,8 +12,6 @@ const DetailInventoryAdjustment = () => {
// Ambil data dari router state
useEffect(() => {
- console.log('Router State');
- console.log(window.history.state);
const state = window.history.state?.usr as
| { inventoryAdjustment?: InventoryAdjustment }
| undefined;
@@ -26,9 +24,6 @@ const DetailInventoryAdjustment = () => {
const finalData = inventoryAdjustment;
- console.log('Final Data');
- console.log(finalData);
-
if (!finalData) {
return (
diff --git a/src/app/inventory/product/detail/layout.tsx b/src/app/inventory/product/detail/layout.tsx
new file mode 100644
index 00000000..7220dfa1
--- /dev/null
+++ b/src/app/inventory/product/detail/layout.tsx
@@ -0,0 +1,11 @@
+import SuspenseHelper from '@/components/helper/SuspenseHelper';
+
+const Layout = ({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) => {
+ return
{children};
+};
+
+export default Layout;
diff --git a/src/app/inventory/product/detail/page.tsx b/src/app/inventory/product/detail/page.tsx
new file mode 100644
index 00000000..6daa7a86
--- /dev/null
+++ b/src/app/inventory/product/detail/page.tsx
@@ -0,0 +1,50 @@
+'use client';
+
+import InventoryProductDetail from '@/components/pages/inventory/product/detail/InventoryProductDetail';
+import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
+import { InventoryProductApi } from '@/services/api/inventory';
+import { useRouter, useSearchParams } from 'next/navigation';
+import useSWR from 'swr';
+
+const InventoryProductDetailPage = () => {
+ const router = useRouter();
+ const searchParams = useSearchParams();
+
+ const inventoryProductId = searchParams.get('inventoryProductId');
+
+ const { data: inventoryProduct, isLoading: isLoadingInventoryProduct } =
+ useSWR(inventoryProductId, (id: number) =>
+ InventoryProductApi.getSingle(id)
+ );
+
+ if (!inventoryProductId) {
+ router.back();
+
+ return (
+
+
+
+ );
+ }
+
+ if (
+ !isLoadingInventoryProduct &&
+ (!inventoryProduct || isResponseError(inventoryProduct))
+ ) {
+ router.replace('/404');
+ return;
+ }
+
+ return (
+
+ {isLoadingInventoryProduct && (
+
+ )}
+ {!isLoadingInventoryProduct && isResponseSuccess(inventoryProduct) && (
+
+ )}
+
+ );
+};
+
+export default InventoryProductDetailPage;
diff --git a/src/app/inventory/product/page.tsx b/src/app/inventory/product/page.tsx
new file mode 100644
index 00000000..4815b8a1
--- /dev/null
+++ b/src/app/inventory/product/page.tsx
@@ -0,0 +1,11 @@
+import InventoryProductTable from '@/components/pages/inventory/product/InventoryProductTable';
+
+const InventoryProductPage = () => {
+ return (
+
+
+
+ );
+};
+
+export default InventoryProductPage;
diff --git a/src/app/production/project-flock/add/page.tsx b/src/app/production/project-flock/add/page.tsx
index b323b5f3..2eb2c090 100644
--- a/src/app/production/project-flock/add/page.tsx
+++ b/src/app/production/project-flock/add/page.tsx
@@ -1,10 +1,18 @@
'use client';
import ProjectFlockForm from '@/components/pages/production/project-flock/form/ProjectFlockForm';
+import React, { useImperativeHandle } from 'react';
+import toast from 'react-hot-toast';
const AddProjectFlock = () => {
+ // useImperativeHandle(ref, () => ({
+ // validate() {
+ // toast.success('Validating');
+ // return false;
+ // },
+ // }));
return (
-
+
);
diff --git a/src/app/production/project-flock/chickin/add/kandang/page.tsx b/src/app/production/project-flock/chickin/add/kandang/page.tsx
index a22039d1..c3a93a80 100644
--- a/src/app/production/project-flock/chickin/add/kandang/page.tsx
+++ b/src/app/production/project-flock/chickin/add/kandang/page.tsx
@@ -44,7 +44,7 @@ export default function AddChickinKandang() {
return (
<>
-
+
{isLoading && }
{!isLoading &&
isResponseSuccess(projectFlockKandang) &&
diff --git a/src/app/production/project-flock/chickin/add/page.tsx b/src/app/production/project-flock/chickin/add/page.tsx
index bcb4d612..831979cb 100644
--- a/src/app/production/project-flock/chickin/add/page.tsx
+++ b/src/app/production/project-flock/chickin/add/page.tsx
@@ -10,7 +10,7 @@ const AddChickin = () => {
return (
<>
-
+
>
diff --git a/src/app/production/project-flock/chickin/page.tsx b/src/app/production/project-flock/chickin/page.tsx
index 5d105aab..d40c39a3 100644
--- a/src/app/production/project-flock/chickin/page.tsx
+++ b/src/app/production/project-flock/chickin/page.tsx
@@ -2,7 +2,7 @@ import ChickinTable from '@/components/pages/production/chickin/ChickinTable';
const Chickin = () => {
return (
-
+
);
diff --git a/src/app/production/project-flock/closing/layout.tsx b/src/app/production/project-flock/closing/layout.tsx
new file mode 100644
index 00000000..7220dfa1
--- /dev/null
+++ b/src/app/production/project-flock/closing/layout.tsx
@@ -0,0 +1,11 @@
+import SuspenseHelper from '@/components/helper/SuspenseHelper';
+
+const Layout = ({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) => {
+ return {children};
+};
+
+export default Layout;
diff --git a/src/app/production/project-flock/closing/page.tsx b/src/app/production/project-flock/closing/page.tsx
new file mode 100644
index 00000000..d734f669
--- /dev/null
+++ b/src/app/production/project-flock/closing/page.tsx
@@ -0,0 +1,63 @@
+'use client';
+import ProjectFlockClosingForm from '@/components/pages/production/project-flock/closing/ProjectFlockClosingForm';
+import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
+import { ProjectFlockKandangApi } from '@/services/api/production';
+import { ProjectFlockApi } from '@/services/api/production/project-flock';
+import { useRouter, useSearchParams } from 'next/navigation';
+import useSWR from 'swr';
+
+const ProjectFlockClosingPage = () => {
+ const router = useRouter();
+ const searchParams = useSearchParams();
+
+ const projectFlockId = searchParams.get('projectFlockId');
+ const projectFlockKandangId = searchParams.get('projectFlockKandangId');
+
+ const { data: projectFlockKandang, isLoading: isLoadingProjectFlockKandang } =
+ useSWR(projectFlockKandangId, (id: number) =>
+ ProjectFlockKandangApi.getSingle(id)
+ );
+
+ const { data: projectFlock, isLoading: isLoadingProjectFlock } = useSWR(
+ projectFlockId,
+ (id: number) => ProjectFlockApi.getSingle(id)
+ );
+
+ if (!projectFlockId || !projectFlockKandangId) {
+ router.back();
+
+ return (
+
+
+
+ );
+ }
+
+ if (
+ !isLoadingProjectFlock &&
+ (!projectFlock || isResponseError(projectFlock)) &&
+ !isLoadingProjectFlockKandang &&
+ (!projectFlockKandang || isResponseError(projectFlockKandang))
+ ) {
+ router.replace('/404');
+ return;
+ }
+
+ return (
+
+ {isLoadingProjectFlock ||
+ (isLoadingProjectFlockKandang && (
+
+ ))}
+ {isResponseSuccess(projectFlock) &&
+ isResponseSuccess(projectFlockKandang) && (
+
+ )}
+
+ );
+};
+
+export default ProjectFlockClosingPage;
diff --git a/src/app/production/project-flock/detail/edit/page.tsx b/src/app/production/project-flock/detail/edit/page.tsx
index f55ce601..e5f88f19 100644
--- a/src/app/production/project-flock/detail/edit/page.tsx
+++ b/src/app/production/project-flock/detail/edit/page.tsx
@@ -37,7 +37,7 @@ const ProjectFlockEdit = () => {
}
return (
-
+
{isLoadingProjectFlock && (
)}
diff --git a/src/app/production/project-flock/detail/page.tsx b/src/app/production/project-flock/detail/page.tsx
index 91d4dfd5..29a078dd 100644
--- a/src/app/production/project-flock/detail/page.tsx
+++ b/src/app/production/project-flock/detail/page.tsx
@@ -1,12 +1,13 @@
'use client';
+import ProjectFlockDetail from '@/components/pages/production/project-flock/detail/ProjectFlockDetail';
import ProjectFlockForm from '@/components/pages/production/project-flock/form/ProjectFlockForm';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { ProjectFlockApi } from '@/services/api/production/project-flock';
import { useRouter, useSearchParams } from 'next/navigation';
import useSWR from 'swr';
-const ProjectFlockDetail = () => {
+const ProjectFlockDetailPage = () => {
const router = useRouter();
const searchParams = useSearchParams();
@@ -37,19 +38,17 @@ const ProjectFlockDetail = () => {
}
return (
-
+
{isLoadingProjectFlock && (
)}
{isResponseSuccess(projectFlock) && (
-
+
)}
);
};
-export default ProjectFlockDetail;
+export default ProjectFlockDetailPage;
+ProjectFlockDetail;
+ProjectFlockDetail;
diff --git a/src/app/production/project-flock/layout.tsx b/src/app/production/project-flock/layout.tsx
new file mode 100644
index 00000000..698064cf
--- /dev/null
+++ b/src/app/production/project-flock/layout.tsx
@@ -0,0 +1,59 @@
+'use client';
+
+import { usePathname, useRouter } from 'next/navigation';
+import Drawer from '@/components/Drawer';
+import React, { ReactNode } from 'react';
+import ProjectFlockTable from '@/components/pages/production/project-flock/ProjectFlockTable';
+import { useUiStore } from '@/stores/ui/ui.store';
+
+export default function ProjectFlockLayout({
+ children,
+}: {
+ children: ReactNode;
+}) {
+ const pathname = usePathname();
+ const router = useRouter();
+ const toggleValidate = useUiStore((s) => s.toggleValidate);
+
+ const isAdd = pathname.endsWith('/add');
+ const isEdit = pathname.includes('/detail/edit');
+ const isDetail = pathname.includes('/detail');
+ const isChickin = pathname.includes('/chickin/add/kandang');
+ const isClosing = pathname.includes('/closing');
+
+ const isOpen = isAdd || isEdit || isDetail || isChickin || isClosing;
+
+ const handleBackdropClick = () => {
+ const unsub = useUiStore.getState().subscribeIsValid((isValid) => {
+ if (isValid) {
+ unsub(); // berhenti listen
+ router.push('/production/project-flock');
+ }
+ });
+
+ toggleValidate();
+ };
+
+ return (
+ <>
+ {/* List page always rendered */}
+
+
!isOpen && router.push('/production/project-flock')}
+ />
+
+
+ {/* Render Drawer only on /add */}
+
{
+ if (!v) router.push('/production/project-flock');
+ }}
+ closeOnBackdropClick={isDetail ? true : false}
+ onBackdropClick={handleBackdropClick}
+ variant='right'
+ sidebarContent={isOpen && {children}
}
+ />
+ >
+ );
+}
diff --git a/src/app/production/project-flock/page.tsx b/src/app/production/project-flock/page.tsx
index 79feb41f..e93c6bc4 100644
--- a/src/app/production/project-flock/page.tsx
+++ b/src/app/production/project-flock/page.tsx
@@ -2,7 +2,7 @@ import ProjectFlockTable from '@/components/pages/production/project-flock/Proje
const ProjectFlock = () => {
return (
-
+
);
diff --git a/src/components/Drawer.tsx b/src/components/Drawer.tsx
index f0efb417..17b8a56f 100644
--- a/src/components/Drawer.tsx
+++ b/src/components/Drawer.tsx
@@ -10,28 +10,102 @@ interface DrawerProps {
open: boolean;
setOpen: (newOpenState: boolean) => void;
openOnLarge?: boolean;
+ variant?: 'sidebar' | 'left' | 'right';
+ zIndex?: string;
+ className?: DrawerClassName;
+ onBackdropClick?: () => void;
+ closeOnBackdropClick?: boolean;
}
+type DrawerClassName = {
+ drawer?: string;
+ drawerContent?: string;
+ drawerSide?: string;
+ drawerOverlay?: string;
+ drawerSidebarContent?: string;
+};
+
const Drawer = ({
children,
sidebarContent,
open,
setOpen,
openOnLarge,
+ variant = 'sidebar',
+ zIndex = '20',
+ className,
+ onBackdropClick,
+ closeOnBackdropClick = true,
}: DrawerProps) => {
+ const getDrawerClassNames = (): DrawerClassName => {
+ const baseClassNames = {
+ drawer: 'drawer',
+ drawerContent: 'drawer-content',
+ drawerSide: 'drawer-side',
+ drawerOverlay: 'drawer-overlay',
+ drawerSidebarContent: 'min-h-full bg-base-100',
+ };
+
+ if (variant === 'sidebar') {
+ return {
+ ...baseClassNames,
+ drawerSidebarContent: cn(
+ baseClassNames.drawerSidebarContent,
+ 'w-full max-w-[300px] lg:w-[300px]'
+ ),
+ };
+ } else if (variant === 'right') {
+ return {
+ ...baseClassNames,
+ drawer: cn(baseClassNames.drawer, 'drawer-end'),
+ drawerSide: cn(
+ baseClassNames.drawerSide,
+ 'border-l border-solid border-gray-200 drawer-side w-screen top-0 right-0 fixed z-21'
+ ),
+ drawerSidebarContent: cn(
+ baseClassNames.drawerSidebarContent,
+ 'w-full min-w-120 sm:w-fit'
+ ),
+ };
+ } else if (variant === 'left') {
+ return {
+ ...baseClassNames,
+ drawerSide: cn(
+ baseClassNames.drawerSide,
+ 'border-l border-solid border-gray-200 drawer-side w-screen top-0 right-0 fixed z-21'
+ ),
+ drawerSidebarContent: cn(
+ baseClassNames.drawerSidebarContent,
+ 'w-full min-w-120 sm:w-fit'
+ ),
+ };
+ }
+ return baseClassNames; // Fallback for default or unknown variant
+ };
+
+ const varianClassName = getDrawerClassNames();
+
const toggleDrawer = () => {
setOpen(!open);
};
const closeDrawer = () => {
- setOpen(false);
+ if (closeOnBackdropClick) {
+ setOpen(false);
+ }
+ onBackdropClick && onBackdropClick();
};
return (
-
{children}
+ {/* Drawer Content */}
+
+ {children}
+
-
+ {/* Drawer Side */}
+
-
+ {/* Sidebar Content */}
+
{sidebarContent}
diff --git a/src/components/FloatingActionsButton.tsx b/src/components/FloatingActionsButton.tsx
new file mode 100644
index 00000000..c0033d72
--- /dev/null
+++ b/src/components/FloatingActionsButton.tsx
@@ -0,0 +1,141 @@
+'use client';
+
+import Button from '@/components/Button';
+import Tooltip from '@/components/Tooltip';
+import { cn } from '@/lib/helper';
+import { Icon } from '@iconify/react';
+
+type FloatingActionsButtonProps = {
+ actions: {
+ action: 'DETAIL' | 'EDIT' | 'DELETE';
+ icon: string;
+ label?: string;
+ onClick?: () => void;
+ hidden?: boolean;
+ disabled?: boolean;
+ }[];
+ approvals: {
+ action: 'APPROVED' | 'REJECTED';
+ icon: string;
+ label?: string;
+ onClick?: () => void;
+ disabled?: boolean;
+ }[];
+ selectedRowIds: number[];
+ onClose: () => void;
+};
+
+const FloatingActionsButton = ({
+ actions,
+ approvals,
+ selectedRowIds,
+ onClose,
+}: FloatingActionsButtonProps) => {
+ // Jika tidak ada baris yang dipilih, jangan tampilkan FAB
+ const positionStyles =
+ selectedRowIds.length > 0 ? 'bottom-[10%]' : 'bottom-[-100%]';
+
+ // Helper untuk menentukan gaya warna tombol approval
+ const getApprovalColor = (action: 'APPROVED' | 'REJECTED') => {
+ if (action === 'APPROVED') return 'success';
+ if (action === 'REJECTED') return 'error';
+ return 'primary';
+ };
+
+ const getActionColor = (action: 'DETAIL' | 'EDIT' | 'DELETE') => {
+ if (action === 'DETAIL') return 'white';
+ if (action === 'EDIT') return 'warning';
+ if (action === 'DELETE') return 'error';
+ return 'primary';
+ };
+
+ return (
+ // Container utama FAB
+
+
+ {/* === BARIS ATAS: Status Seleksi dan Actions (Termasuk Close) === */}
+
+
+ {selectedRowIds.length} Selected
+
+
+
+
+ {/* Render Aksi dari props.actions */}
+ {actions
+ .filter((action) => !action.hidden)
+ .map((action, index) => {
+ return (
+
+ );
+ })}
+
+
+
+ {/* Tombol Close */}
+
+
+
+
+
+ {/* === BARIS BAWAH: Approval Buttons (Approve/Reject) === */}
+
+ {approvals.map((approval, index) => (
+
+ ))}
+
+
+
+ );
+};
+
+export default FloatingActionsButton;
diff --git a/src/components/MainDrawer.tsx b/src/components/MainDrawer.tsx
index 4a3b44b0..3a09c0b1 100644
--- a/src/components/MainDrawer.tsx
+++ b/src/components/MainDrawer.tsx
@@ -1,161 +1,21 @@
'use client';
-import { useCallback, useState } from 'react';
+import { useCallback } from 'react';
import { usePathname } from 'next/navigation';
import Image from 'next/image';
import { Icon } from '@iconify/react';
import Drawer from '@/components/Drawer';
-import Menu from '@/components/menu/Menu';
-import MenuItem from '@/components/menu/MenuItem';
import Navbar from '@/components/Navbar';
-import Collapse from '@/components/Collapse';
import Button from '@/components/Button';
+import SidebarMenu from '@/components/molecules/SidebarMenu';
import { useUiStore } from '@/stores/ui/ui.store';
import { MAIN_DRAWER_LINKS } from '@/config/constant';
-import { cn } from '@/lib/helper';
-
-type CollapseMenuProps = {
- title: string;
- link: string;
- icon: string;
- submenu?: CollapseMenuProps[];
- depth?: number;
-};
-
-const isPathActive = (pathname: string, link?: string) => {
- if (!link) return false;
-
- const splittedPathname = pathname.split('/');
- const splittedLink = link.split('/');
-
- const isActiveLinkValid = splittedLink.every((linkChunk, idx) => {
- return linkChunk === splittedPathname[idx];
- });
-
- return pathname.startsWith(link) && isActiveLinkValid;
-};
-
-const CollapseMenu = ({
- title,
- link,
- icon,
- submenu,
- depth = 0,
-}: CollapseMenuProps) => {
- const pathname = usePathname();
- const isActive = isPathActive(pathname, link);
- const [open, setOpen] = useState(isActive);
-
- const menuCollapseTitle = (
-
- );
-
- return (
-
-
-
- );
-};
-
-const MainDrawerMenu = () => {
- const pathname = usePathname();
-
- return (
-
- );
-};
+import { isPathActive } from '@/lib/helper';
const MainDrawerContent = () => {
+ const pathname = usePathname();
const { setMainDrawerOpen } = useUiStore();
const closeMainDrawerHandler = () => {
@@ -191,7 +51,7 @@ const MainDrawerContent = () => {
-
+
);
};
@@ -216,9 +76,9 @@ const MainDrawer = ({
const hasSubmenu = menu?.submenu && menu?.submenu.length > 0;
if (!title) {
- title += menu?.title;
+ title += menu?.text;
} else {
- title += ' - ' + menu?.title;
+ title += ' - ' + menu?.text;
}
if (!hasSubmenu || !menu.submenu) return;
diff --git a/src/components/Pagination.tsx b/src/components/Pagination.tsx
index e47e480d..43b26d90 100644
--- a/src/components/Pagination.tsx
+++ b/src/components/Pagination.tsx
@@ -1,7 +1,9 @@
'use client';
-import { ReactNode } from 'react';
+import { ChangeEventHandler, ReactNode } from 'react';
+
import { Icon } from '@iconify/react';
+import Button from '@/components/Button';
import { cn } from '@/lib/helper';
@@ -17,16 +19,18 @@ const PaginationButton = ({
disabled?: boolean;
onClick?: () => void;
}) => (
-
+
);
const EtcPaginationButton = ({
@@ -48,7 +52,7 @@ const EtcPaginationButton = ({
tabIndex={0}
role='button'
className={cn(
- 'join-item btn btn-ghost p-2.5 rounded-lg text-sm font-medium text-gray-500 aspect-square'
+ 'join-item btn btn-ghost p-2.5 rounded-lg! text-sm font-medium text-gray-500 aspect-square'
)}
>
...
@@ -57,7 +61,7 @@ const EtcPaginationButton = ({
{pages.map((pageNumber) => (
-
@@ -76,7 +80,7 @@ const EtcPaginationButton = ({