refactor(FE): refactor UI detail from page into drawer

This commit is contained in:
randy-ar
2025-12-02 16:25:55 +07:00
parent 9eba5ffeca
commit 31f758d680
5 changed files with 330 additions and 9 deletions
@@ -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 (
<div className='w-full p-4 flex flex-col justify-center'>
<div className='w-full h-full flex flex-col justify-center'>
{isLoadingProjectFlock && (
<span className='loading loading-spinner loading-xl' />
)}
{isResponseSuccess(projectFlock) && (
<ProjectFlockForm
formType='detail'
initialValues={projectFlock.data}
refreshProjectFlocks={refreshProjectFlock}
/>
<ProjectFlockDetail projectFlock={projectFlock.data} />
)}
</div>
);
};
export default ProjectFlockDetail;
export default ProjectFlockDetailPage;
ProjectFlockDetail;
ProjectFlockDetail;
+1 -1
View File
@@ -51,7 +51,7 @@ export default function ProjectFlockLayout({
closeOnBackdropClick={isDetail ? true : false}
onBackdropClick={handleBackdropClick}
variant='right'
sidebarContent={isOpen && <div className='p-4'>{children}</div>}
sidebarContent={isOpen && <div className=''>{children}</div>}
/>
</>
);
@@ -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 (
<>
<div className='h-full w-full flex flex-col gap-4'>
{/* Header */}
<div className='flex flex-row justify-between items-center px-4 pt-4'>
<div className='flex flex-row h-full gap-2'>
<Link
href={`/production/project-flock`}
className='hover:text-gray-400'
>
<Icon icon='mdi:close' width={24} height={24} />
</Link>
<div className='divider divider-horizontal p-0 m-0'></div>
<div className='text-sm text-secondary'>
Created On {formatDate(projectFlock.created_at, 'MMM DD, YYYY')}
</div>
</div>
<div className='flex flex-row gap-3 justify-end'>
<Link
href={`/production/project-flock/detail/edit?projectFlockId=${projectFlock.id}`}
>
<Icon icon='mdi:square-edit-outline' width={20} height={20} />
</Link>
<Button variant='link' className='p-0 text-error'>
<Icon icon='mdi:trash-can-outline' width={20} height={20} />
</Button>
</div>
</div>
{/* Informasi Umum */}
<div className='border-t-1 border-gray-300'>
<div className='p-4 flex flex-col gap-4'>
<h2 className='text-2xl font-semibold'>Informasi Umum</h2>
{/* Badge Row */}
<div className='flex flex-row gap-2'>
<Badge
variant='soft'
color={
projectFlock.approval.step_number == 1
? 'secondary'
: projectFlock.approval.step_number == 2
? 'success'
: projectFlock.approval.step_number >= 3
? 'error'
: undefined
}
className={{
badge: 'rounded-lg px-2',
}}
>
<Icon
icon='mdi:circle'
width={12}
height={12}
color={
projectFlock.approval.step_number == 1
? 'secondary'
: projectFlock.approval.step_number == 2
? 'success'
: projectFlock.approval.step_number >= 3
? 'error'
: undefined
}
/>{' '}
{projectFlock.approval.step_name}
</Badge>
<div className='divider divider-horizontal p-0 m-0'></div>
<Badge
color='secondary'
variant='soft'
className={{ badge: 'rounded-lg px-2' }}
>
<Icon icon='mdi:bookmark' width={12} height={12} />
{` ${formatTitleCase(projectFlock.category)}`}
</Badge>
</div>
{/* Information Grid */}
<div className='grid grid-cols-3 gap-4'>
<div className='col-span-1 flex flex-row items-center text-gray-400 font-semibold gap-2'>
<Icon width={14} height={14} icon='mdi:account' /> Submitted
</div>
<div className='col-span-2'>
<Badge
variant='soft'
color='secondary'
className={{
badge: 'rounded-lg px-2',
}}
>
<Icon icon='mdi:account-circle' width={14} height={14} />{' '}
{projectFlock.created_user.name}
</Badge>
</div>
<div className='col-span-1 flex flex-row items-center text-gray-400 font-semibold gap-2'>
<Icon width={14} height={14} icon={'mdi:clock'} /> History
</div>
<div className='col-span-2'>
<Button variant='outline' className='py-1 text-sm'>
See History{' '}
<Icon
icon='mdi:arrow-top-right-thin'
width={11}
height={11}
/>
</Button>
</div>
{/* BARIS 1 */}
<div
className='col-span-1 flex flex-row items-center text-gray-400 font-semibold gap-2
relative
before:content-[""] before:absolute before:left-[5px] before:top-[90%] before:bottom-[-100%] before:w-[1px] before:border-1 before:border-dashed before:border-gray-400'
>
<Icon width={14} height={14} icon='mdi:circle-slice-8' /> Area
</div>
<div className='col-span-2'>{projectFlock.area.name}</div>
{/* BARIS 2 */}
<div
className='col-span-1 flex flex-row items-center text-gray-400 font-semibold gap-2
relative
before:content-[""] before:absolute before:left-[5px] before:top-[90%] before:bottom-[-100%] before:w-[1px] before:border-1 before:border-dashed before:border-gray-400'
>
<Icon width={14} height={14} icon='mdi:circle-slice-8' /> Lokasi
</div>
<div className='col-span-2'>{projectFlock.location.name}</div>
<div
className='col-span-1 flex flex-row items-center text-gray-400 font-semibold gap-2
relative
before:content-[""] before:absolute before:left-[5px] before:top-[90%] before:bottom-[-100%] before:w-[1px] before:border-1 before:border-dashed before:border-gray-400'
>
<Icon width={14} height={14} icon='mdi:circle-slice-8' /> FCR
</div>
<div className='col-span-2'>{projectFlock.fcr.name}</div>
{/* BARIS 3 (Terakhir - TIDAK PERLU garis di bawahnya) */}
<div className='col-span-1 flex flex-row items-center text-gray-400 font-semibold gap-2'>
<Icon width={14} height={14} icon='mdi:circle-slice-8' />{' '}
Kategori
</div>
<div className='col-span-2'>
{formatTitleCase(projectFlock.category)}
</div>
</div>
</div>
</div>
{/* Kandang Aktif */}
<div className='border-t-1 border-gray-300'>
<div className='p-4 flex flex-col gap-4'>
<h2 className='text-2xl font-semibold'>Kandang Aktif</h2>
{/* Badge Row */}
<div className='flex flex-row gap-2'>
<Badge
variant='soft'
color={'success'}
className={{
badge: 'rounded-lg px-2',
}}
>
<Icon
icon='mdi:circle'
width={12}
height={12}
color={'success'}
/>{' '}
Kandang Aktif ({projectFlock.kandangs.length})
</Badge>
<div className='divider divider-horizontal p-0 m-0'></div>
<Badge
color='secondary'
variant='soft'
className={{ badge: 'rounded-lg px-2 cursor-pointer' }}
onClick={() => {
setOpenBudget(!openBudgets);
}}
>
{` ${formatCurrency(
projectFlock.project_budgets.reduce(
(acc, curr) => acc + curr.price * curr.qty,
0
)
)}`}
<Icon
icon={`mdi:${openBudgets ? 'eye' : 'eye-off'}`}
width={12}
height={12}
/>
</Badge>
</div>
{/* Card List Project Budgets */}
{openBudgets &&
projectFlock.project_budgets.map((budget) => (
<Card
key={budget.id}
variant='bordered'
className={{
wrapper: 'w-full',
body: 'p-3',
}}
>
<div className='flex flex-col gap-6'>
<div className='flex flex-row justify-between items-center'>
<div className='flex flex-row gap-2 items-center text-gray-400'>
<Icon icon={'mdi:tag'} width={14} height={14} />{' '}
<span>Jenis Produk</span>
</div>
<div className='text-end text-gray-500'>
{budget.nonstock?.name}
</div>
</div>
<div className='flex flex-row justify-between items-center'>
<div className='flex flex-row gap-2 items-center text-gray-400'>
<Icon icon={'mdi:tag'} width={14} height={14} />{' '}
<span>Nama Satuan</span>
</div>
<div className='text-end text-gray-500'>
{budget.nonstock?.uom.name}
</div>
</div>
<div className='flex flex-row justify-between items-center'>
<div className='flex flex-row gap-2 items-center text-gray-400'>
<Icon
icon={'mdi:file-multiple'}
width={14}
height={14}
/>{' '}
<span>Jumlah Pembelian</span>
</div>
<div className='text-end text-gray-500'>
{formatNumber(budget.qty)}
</div>
</div>
<div className='flex flex-row justify-between items-center'>
<div className='flex flex-row gap-2 items-center text-gray-400'>
<Icon icon={'mdi:file'} width={14} height={14} />{' '}
<span>Harga Satuan</span>
</div>
<div className='text-end text-gray-500'>
{formatCurrency(budget.price)}
</div>
</div>
<div className='flex flex-row justify-between items-center'>
<div className='flex flex-row gap-2 items-center text-gray-400'>
<Icon icon={'mdi:calculator'} width={14} height={14} />{' '}
<span>Total Harga</span>
</div>
<div className='text-end text-gray-500'>
{formatCurrency(budget.price * budget.qty)}
</div>
</div>
</div>
</Card>
))}
{/* Card Kandangs */}
<Card
variant='bordered'
className={{
wrapper: 'w-full',
body: 'p-3',
}}
>
<div className='flex flex-col gap-6'>
{projectFlock.kandangs.map((kandang) => (
<div
key={kandang.id}
className='flex flex-row justify-between items-center'
>
<div className='flex flex-row gap-2 items-center cursor-pointer text-gray-400'>
<Icon icon={'mdi:drag'} width={21} height={21} />{' '}
<span className='font-semibold'>{kandang.name}</span>
</div>
<div className='text-end text-gray-400'>
Created On{' '}
{formatDate(projectFlock.created_at, 'MMM DD, YYYY')}
</div>
</div>
))}
</div>
</Card>
</div>
</div>
</div>
</>
);
};
export default ProjectFlockDetail;
+8
View File
@@ -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++) {
+1
View File
@@ -23,6 +23,7 @@ export type BaseProjectFlock = {
kandangs: (Kandang & {
project_flock_kandang_id: number;
})[];
project_budgets: ProjectFlockBudget[];
approval: BaseApproval;
};