From 31f758d68063b9d606646e9c02248cc14d13fc07 Mon Sep 17 00:00:00 2001 From: randy-ar Date: Tue, 2 Dec 2025 16:25:55 +0700 Subject: [PATCH] refactor(FE): refactor UI detail from page into drawer --- .../production/project-flock/detail/page.tsx | 15 +- src/app/production/project-flock/layout.tsx | 2 +- .../detail/ProjectFlockDetail.tsx | 313 ++++++++++++++++++ src/lib/helper.ts | 8 + src/types/api/production/project-flock.d.ts | 1 + 5 files changed, 330 insertions(+), 9 deletions(-) create mode 100644 src/components/pages/production/project-flock/detail/ProjectFlockDetail.tsx 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 index fbfc5eb4..f441abad 100644 --- a/src/app/production/project-flock/layout.tsx +++ b/src/app/production/project-flock/layout.tsx @@ -51,7 +51,7 @@ export default function ProjectFlockLayout({ closeOnBackdropClick={isDetail ? true : false} onBackdropClick={handleBackdropClick} variant='right' - sidebarContent={isOpen &&
{children}
} + sidebarContent={isOpen &&
{children}
} /> ); diff --git a/src/components/pages/production/project-flock/detail/ProjectFlockDetail.tsx b/src/components/pages/production/project-flock/detail/ProjectFlockDetail.tsx new file mode 100644 index 00000000..a056ea71 --- /dev/null +++ b/src/components/pages/production/project-flock/detail/ProjectFlockDetail.tsx @@ -0,0 +1,313 @@ +import Badge from '@/components/Badge'; +import Button from '@/components/Button'; +import Card from '@/components/Card'; +import { + formatCurrency, + formatDate, + formatNumber, + formatTitleCase, +} from '@/lib/helper'; +import { ProjectFlock } from '@/types/api/production/project-flock'; +import { Icon } from '@iconify/react'; +import Link from 'next/link'; +import { useState } from 'react'; + +const ProjectFlockDetail = ({ + projectFlock, +}: { + projectFlock: ProjectFlock; +}) => { + const [openBudgets, setOpenBudget] = useState(false); + + return ( + <> +
+ {/* Header */} +
+
+ + + +
+
+ Created On {formatDate(projectFlock.created_at, 'MMM DD, YYYY')} +
+
+
+ + + + +
+
+ + {/* Informasi Umum */} +
+
+

Informasi Umum

+ {/* Badge Row */} +
+ = 3 + ? 'error' + : undefined + } + className={{ + badge: 'rounded-lg px-2', + }} + > + = 3 + ? 'error' + : undefined + } + />{' '} + {projectFlock.approval.step_name} + +
+ + + {` ${formatTitleCase(projectFlock.category)}`} + +
+ {/* Information Grid */} +
+
+ Submitted +
+
+ + {' '} + {projectFlock.created_user.name} + +
+ +
+ History +
+
+ +
+ + {/* BARIS 1 */} +
+ Area +
+
{projectFlock.area.name}
+ + {/* BARIS 2 */} +
+ Lokasi +
+
{projectFlock.location.name}
+ +
+ FCR +
+
{projectFlock.fcr.name}
+ + {/* BARIS 3 (Terakhir - TIDAK PERLU garis di bawahnya) */} +
+ {' '} + Kategori +
+
+ {formatTitleCase(projectFlock.category)} +
+
+
+
+ + {/* Kandang Aktif */} +
+
+

Kandang Aktif

+ {/* Badge Row */} +
+ + {' '} + Kandang Aktif ({projectFlock.kandangs.length}) + +
+ { + setOpenBudget(!openBudgets); + }} + > + {` ${formatCurrency( + projectFlock.project_budgets.reduce( + (acc, curr) => acc + curr.price * curr.qty, + 0 + ) + )}`} + + +
+ + {/* Card List Project Budgets */} + {openBudgets && + projectFlock.project_budgets.map((budget) => ( + +
+
+
+ {' '} + Jenis Produk +
+
+ {budget.nonstock?.name} +
+
+
+
+ {' '} + Nama Satuan +
+
+ {budget.nonstock?.uom.name} +
+
+
+
+ {' '} + Jumlah Pembelian +
+
+ {formatNumber(budget.qty)} +
+
+
+
+ {' '} + Harga Satuan +
+
+ {formatCurrency(budget.price)} +
+
+
+
+ {' '} + Total Harga +
+
+ {formatCurrency(budget.price * budget.qty)} +
+
+
+
+ ))} + + {/* Card Kandangs */} + +
+ {projectFlock.kandangs.map((kandang) => ( +
+
+ {' '} + {kandang.name} +
+
+ Created On{' '} + {formatDate(projectFlock.created_at, 'MMM DD, YYYY')} +
+
+ ))} +
+
+
+
+
+ + ); +}; + +export default ProjectFlockDetail; diff --git a/src/lib/helper.ts b/src/lib/helper.ts index 2c66e1cf..1438c98f 100644 --- a/src/lib/helper.ts +++ b/src/lib/helper.ts @@ -29,6 +29,14 @@ export const formatNumber = ( }).format(value); }; +export const formatTitleCase = (value: string) => { + return value + .toLowerCase() + .split(' ') + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); +}; + export function formatVechicleNumber(value: string): string { let result = ''; for (let i = 0; i < (value?.length ?? 0); i++) { diff --git a/src/types/api/production/project-flock.d.ts b/src/types/api/production/project-flock.d.ts index 845c5e77..81a7eebd 100644 --- a/src/types/api/production/project-flock.d.ts +++ b/src/types/api/production/project-flock.d.ts @@ -23,6 +23,7 @@ export type BaseProjectFlock = { kandangs: (Kandang & { project_flock_kandang_id: number; })[]; + project_budgets: ProjectFlockBudget[]; approval: BaseApproval; };