fix: adjust ProjectFlockDetail styling and content

This commit is contained in:
ValdiANS
2026-02-06 09:46:00 +07:00
parent 6e2e9da1be
commit 7b9ba48204
@@ -4,12 +4,7 @@ import Card from '@/components/Card';
import { RadioGroup, RadioGroupItem } from '@/components/input/RadioInput'; import { RadioGroup, RadioGroupItem } from '@/components/input/RadioInput';
import Tooltip from '@/components/Tooltip'; import Tooltip from '@/components/Tooltip';
import DrawerHeader from '@/components/helper/drawer/DrawerHeader'; import DrawerHeader from '@/components/helper/drawer/DrawerHeader';
import { import { cn, formatCurrency, formatDate, formatNumber } from '@/lib/helper';
formatCurrency,
formatDate,
formatNumber,
formatTitleCase,
} from '@/lib/helper';
import { ProjectFlock } from '@/types/api/production/project-flock'; import { ProjectFlock } from '@/types/api/production/project-flock';
import { Icon } from '@iconify/react'; import { Icon } from '@iconify/react';
import Link from 'next/link'; import Link from 'next/link';
@@ -20,16 +15,15 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import { ProjectFlockApi } from '@/services/api/production/project-flock'; import { ProjectFlockApi } from '@/services/api/production/project-flock';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import ApprovalSteps, {
useApprovalSteps,
} from '@/components/pages/ApprovalSteps';
import {
PROJECT_FLOCK_APPROVAL_LINE,
PROJECT_FLOCK_KANDANGS_APPROVAL_LINE,
} from '@/config/approval-line';
import useSWR from 'swr'; import useSWR from 'swr';
import { ProjectFlockKandangApi } from '@/services/api/production';
import RequirePermission from '@/components/helper/RequirePermission'; import RequirePermission from '@/components/helper/RequirePermission';
import ApprovalStepsV2 from '@/components/helper/ApprovalStepsV2';
import { APPROVAL_WORKFLOWS } from '@/config/constant';
import Table from '@/components/Table';
import { ProjectFlockFormConfirmationTableType } from '../form/ProjectFlockForm';
import { ColumnDef } from '@tanstack/react-table';
import StatusBadge from '@/components/helper/StatusBadge';
import { ProjectFlockKandangApi } from '@/services/api/production/project-flock-kandang';
const ProjectFlockDetail = ({ const ProjectFlockDetail = ({
projectFlock, projectFlock,
@@ -40,7 +34,7 @@ const ProjectFlockDetail = ({
const deleteModal = useModal(); const deleteModal = useModal();
const [isDeleteLoading, setIsDeleteLoading] = useState(false); const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const [openBudgets, setOpenBudget] = useState(false); const [openBudgets, setOpenBudget] = useState(false);
const [selectedKandangId, setSelectedKamdangId] = useState<string | null>( const [selectedKandangId, setSelectedKandangId] = useState<string | null>(
null null
); );
@@ -61,30 +55,94 @@ const ProjectFlockDetail = ({
: null : null
); );
const { const { data: projectFlockApprovalResponse } = useSWR(
approvals, projectFlock.id ? ['approval-project-flock', projectFlock.id] : undefined,
isLoading: approvalsLoading, ([, id]) => ProjectFlockApi.getApprovalLineHistory(Number(id))
refresh: refreshApprovals, );
} = useApprovalSteps({
latestApproval: projectFlock?.approval,
approvalLines: PROJECT_FLOCK_APPROVAL_LINE,
moduleName: 'PROJECT_FLOCKS',
moduleId: projectFlock?.id?.toString() ?? '',
});
const { approvals: kandangApprovals, isLoading: kandangApprovalsLoading } = const projectFlockApproval = isResponseSuccess(projectFlockApprovalResponse)
useApprovalSteps({ ? projectFlockApprovalResponse.data
latestApproval: : undefined;
selectedKandangId && isResponseSuccess(projectFlockKandang)
? projectFlockKandang?.data?.approval const { data: projectFlockKandangApprovalResponse } = useSWR(
: undefined, selectedKandang?.project_flock_kandang_id
approvalLines: PROJECT_FLOCK_KANDANGS_APPROVAL_LINE, ? [
moduleName: 'PROJECT_FLOCK_KANDANGS', 'approval-project-flock-kandang',
moduleId: selectedKandang?.project_flock_kandang_id,
selectedKandangId && isResponseSuccess(projectFlockKandang) ]
? projectFlockKandang?.data?.id?.toString() : undefined,
: '', ([, id]) => ProjectFlockKandangApi.getApprovalLineHistory(Number(id))
}); );
const projectFlockKandangApproval = isResponseSuccess(
projectFlockKandangApprovalResponse
)
? projectFlockKandangApprovalResponse.data
: undefined;
const confirmationTableColumns: ColumnDef<ProjectFlockFormConfirmationTableType>[] =
[
{
header: 'Label',
accessorKey: 'label',
enableSorting: false,
cell: ({ row }) => {
const isSubRow = row.depth > 0;
return (
<>
{!isSubRow && row.original.label}
{isSubRow && (
<div
className={cn('w-full min-h-full flex items-stretch gap-0')}
>
<div className='w-px mx-4 bg-base-content/10' />
<span className='p-3'>{row.original.label}</span>
</div>
)}
</>
);
},
},
{
header: 'Value',
accessorKey: 'value',
enableSorting: false,
cell: ({ row }) => row.original.value,
},
];
const confirmationTableData: ProjectFlockFormConfirmationTableType[] = [
{
label: 'Tanggal',
value: formatDate(projectFlock.created_at, 'DD MMMM YYYY'),
},
{
label: 'Area',
value: projectFlock.area.name ?? '-',
},
{
label: 'Lokasi',
value: projectFlock.location.name ?? '-',
},
{
label: 'Flock',
value: projectFlock.flock_name ?? '-',
},
{
label: 'Kategori',
value: projectFlock.category ?? '-',
},
{
label: 'Standar Produksi',
value: projectFlock.production_standard.name ?? '-',
},
{
label: 'Periode',
value: projectFlock.period ?? '-',
},
];
const confirmationModalDeleteClickHandler = async () => { const confirmationModalDeleteClickHandler = async () => {
setIsDeleteLoading(true); setIsDeleteLoading(true);
@@ -104,12 +162,14 @@ const ProjectFlockDetail = ({
return ( return (
<> <>
<div className='h-full w-full flex flex-col gap-4'> <div className='h-full w-full flex flex-col overflow-x-hidden overflow-y-auto'>
{/* Header */} {/* Header */}
<DrawerHeader <DrawerHeader
leftIcon='mdi:close' leftIcon='heroicons:chevron-left'
leftIconHref='/production/project-flock' leftIconHref='/production/project-flock'
subtitle={`Created On ${formatDate(projectFlock.created_at, 'MMM DD, YYYY')}`} leftIconClassName='hover:text-gray-400'
subtitle='Detail Flock'
className='sticky top-0 z-10 bg-base-100'
> >
<RequirePermission permissions='lti.production.project_flocks.update'> <RequirePermission permissions='lti.production.project_flocks.update'>
<Link <Link
@@ -118,7 +178,7 @@ const ProjectFlockDetail = ({
> >
<Tooltip content='Edit' position='bottom'> <Tooltip content='Edit' position='bottom'>
<Button variant='link' className='p-0 text-neutral'> <Button variant='link' className='p-0 text-neutral'>
<Icon icon='mdi:square-edit-outline' width={20} height={20} /> <Icon icon='heroicons:pencil-square' width={20} height={20} />
</Button> </Button>
</Tooltip> </Tooltip>
</Link> </Link>
@@ -132,332 +192,224 @@ const ProjectFlockDetail = ({
}} }}
> >
<Tooltip content='Hapus' position='bottom'> <Tooltip content='Hapus' position='bottom'>
<Icon icon='mdi:trash-can-outline' width={20} height={20} /> <Icon icon='heroicons:trash' width={20} height={20} />
</Tooltip> </Tooltip>
</Button> </Button>
</RequirePermission> </RequirePermission>
</DrawerHeader> </DrawerHeader>
{/* Informasi Umum */} <ApprovalStepsV2
<div className='border-t-1 border-gray-300'> approvals={projectFlockApproval}
<div className='p-4 flex flex-col gap-4'> steps={APPROVAL_WORKFLOWS.PROJECT_FLOCKS}
<h2 className='text-2xl font-semibold'>Informasi Umum</h2> />
{/* Status Approval */}
{approvals && !approvalsLoading && (
<div className='text-sm my-3'>
<ApprovalSteps approvals={approvals} />
</div>
)}
{/* Badge Row */}
<div className='flex flex-row gap-2'>
<Badge
variant='soft'
color={
projectFlock.approval?.step_number == 1
? 'neutral'
: projectFlock.approval?.step_number == 2
? 'primary'
: projectFlock.approval?.step_number == 3
? 'success'
: undefined
}
className={{
badge: 'rounded-lg px-2',
}}
>
<Icon
icon='mdi:circle'
width={12}
height={12}
color={
projectFlock.approval?.step_number == 1
? 'neutral'
: projectFlock.approval?.step_number == 2
? 'primary'
: projectFlock.approval?.step_number == 3
? 'success'
: undefined
}
/>{' '}
{projectFlock.approval?.step_name}
</Badge>
<div className='divider divider-horizontal p-0 m-0'></div>
<Badge
color='neutral'
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='neutral'
className={{
badge: 'rounded-lg px-2',
}}
>
<Icon icon='mdi:account-circle' width={14} height={14} />{' '}
{projectFlock.created_user?.name}
</Badge>
</div>
{/* BARIS 1 */} <div className='w-full p-4 flex flex-col gap-3 border-b border-base-content/10'>
<div <h4 className='text-base font-medium text-base-content/50 font-roboto'>
className='col-span-1 flex flex-row items-center text-gray-400 font-semibold gap-2 Informasi Umum
relative </h4>
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 */} <Table<ProjectFlockFormConfirmationTableType>
<div columns={confirmationTableColumns}
className='col-span-1 flex flex-row items-center text-gray-400 font-semibold gap-2 data={confirmationTableData}
relative withPagination={false}
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' pageSize={10000}
> expanded={true}
<Icon width={14} height={14} icon='mdi:circle-slice-8' /> Lokasi getSubRows={(row) => row.subRows}
</div> className={{
<div className='col-span-2'>{projectFlock?.location?.name}</div> headerRowClassName: 'border-b border-base-content/10',
bodyRowClassName: 'border-none',
<div bodySubRowClassName: () => 'border-none',
className='col-span-1 flex flex-row items-center text-gray-400 font-semibold gap-2 bodySubRowColumnClassName: () => 'first:p-0',
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>
<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' />{' '}
Standard
</div>
<div className='col-span-2'>
{projectFlock?.production_standard?.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> </div>
{/* Kandang Aktif */} <div className='w-full p-4 flex flex-col gap-3 border-b border-base-content/10'>
<div className='border-t-1 border-gray-300'> <h4 className='text-base font-medium text-base-content/50 font-roboto'>
<div className='p-4 flex flex-col gap-4'> Kandang Aktif
<h2 className='text-2xl font-semibold'>Kandang Aktif</h2> </h4>
{kandangApprovals && !kandangApprovalsLoading && (
<ApprovalSteps approvals={kandangApprovals} />
)}
{/* Badge Row */}
<div className='flex flex-row gap-2'>
<Badge
variant='soft'
color={'primary'}
className={{
badge: 'rounded-lg px-2',
}}
>
<Icon
icon='mdi:circle'
width={12}
height={12}
color={'primary'}
/>{' '}
Kandang Aktif ({projectFlock.kandangs?.length})
</Badge>
<div className='divider divider-horizontal p-0 m-0'></div>
<Badge
color='neutral'
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 */} <div className='flex flex-row gap-2'>
{openBudgets && <StatusBadge
(projectFlock.project_budgets ?? []).map((budget) => ( color='info'
<Card text={`Kandang Aktif (${projectFlock.kandangs?.length})`}
key={budget.id} className={{ badge: 'w-fit text-nowrap' }}
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 */} <StatusBadge
<Card color='neutral'
variant='bordered' onClick={() => {
className={{ setOpenBudget(!openBudgets);
wrapper: 'w-full',
body: 'p-3',
}} }}
> text={
<RadioGroup <>
name='gender' {` ${formatCurrency(
(projectFlock.project_budgets ?? []).reduce(
(acc, curr) => acc + curr.price * curr.qty,
0
)
)}`}
<Icon
icon={`mdi:${openBudgets ? 'eye' : 'eye-off'}`}
width={12}
height={12}
/>
</>
}
className={{ badge: 'w-fit text-nowrap cursor-pointer' }}
/>
</div>
{/* Card List Project Budgets */}
{openBudgets &&
(projectFlock.project_budgets ?? []).map((budget) => (
<Card
key={budget.id}
variant='bordered'
className={{ className={{
radioWrapper: 'grid grid-cols-1 gap-6', wrapper: 'w-full rounded-lg',
body: 'p-3',
}} }}
onChange={(e) => setSelectedKamdangId(e.target.value)}
value={selectedKandangId?.toString()}
size='md'
color='neutral'
disabled={projectFlock?.approval?.step_number == 1}
> >
{projectFlock.kandangs?.map((kandang) => ( <div className='flex flex-col gap-6'>
<div <div className='flex flex-row justify-between items-center'>
key={kandang.id} <div className='flex flex-row gap-2 items-center text-gray-400'>
className={`grid grid-cols-2 gap-6 cursor-pointer hover:text-gray-800`} <Icon icon={'mdi:tag'} width={14} height={14} />{' '}
onClick={() => <span>Jenis Produk</span>
projectFlock?.approval?.step_number > 1 && </div>
setSelectedKamdangId(kandang?.id?.toString()) <div className='text-end text-gray-500'>
} {budget?.nonstock?.name}
>
<RadioGroupItem
value={kandang?.id?.toString()}
label={kandang?.name}
disabled={projectFlock?.approval?.step_number == 1}
/>
<div className='text-end'>
<Badge
className={{
badge: 'rounded-lg',
}}
>
Kapasitas {kandang?.capacity} Ekor
</Badge>
</div> </div>
</div> </div>
))} <div className='flex flex-row justify-between items-center'>
</RadioGroup> <div className='flex flex-row gap-2 items-center text-gray-400'>
</Card> <Icon icon={'mdi:tag'} width={14} height={14} />{' '}
<div className='grid grid-cols-4 gap-3'> <span>Nama Satuan</span>
<RequirePermission permissions='lti.production.chickins.detail'> </div>
<Link <div className='text-end text-gray-500'>
href={`/production/project-flock/chickin/add/kandang?projectFlockKandangId=${selectedKandang?.project_flock_kandang_id}&projectFlockId=${projectFlock.id}`} {budget?.nonstock?.uom?.name}
className='m-0 p-0' </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 rounded-lg',
body: 'p-3',
}}
>
<RadioGroup
name='gender'
className={{
radioWrapper: 'grid grid-cols-1 gap-6',
}}
onChange={(e) => setSelectedKandangId(e.target.value)}
value={selectedKandangId?.toString()}
size='md'
color='neutral'
disabled={projectFlock?.approval?.step_number == 1}
>
{projectFlock.kandangs?.map((kandang) => (
<div
key={kandang.id}
className={`grid grid-cols-2 gap-6 cursor-pointer hover:text-gray-800`}
onClick={() =>
projectFlock?.approval?.step_number > 1 &&
setSelectedKandangId(kandang?.id?.toString())
}
> >
<Button <RadioGroupItem
className='w-full px-2 py-1 text-sm' value={kandang?.id?.toString()}
variant='outline' label={kandang?.name}
color='success' disabled={projectFlock?.approval?.step_number == 1}
disabled={ />
!selectedKandangId || <div className='text-end'>
projectFlock?.approval?.step_number == 1 <Badge
} className={{
> badge: 'rounded-lg',
Chickin <Icon icon='mdi:checkbox-marked-outline' /> }}
</Button> >
</Link> Kapasitas {kandang?.capacity} Ekor
</RequirePermission> </Badge>
<RequirePermission permissions='lti.production.project_flock_kandangs.closing.detail'> </div>
<Link </div>
href={`/production/project-flock/closing?projectFlockId=${projectFlock.id}&projectFlockKandangId=${selectedKandang?.project_flock_kandang_id}`} ))}
className='m-0 p-0' </RadioGroup>
</Card>
<ApprovalStepsV2
approvals={projectFlockKandangApproval}
steps={APPROVAL_WORKFLOWS.PROJECT_FLOCK_KANDANGS}
/>
<div className='grid grid-cols-4 gap-3'>
<RequirePermission permissions='lti.production.chickins.detail'>
<Link
href={`/production/project-flock/chickin/add/kandang?projectFlockKandangId=${selectedKandang?.project_flock_kandang_id}&projectFlockId=${projectFlock.id}`}
className='m-0 p-0'
>
<Button
className='w-full px-2 py-1 text-sm'
variant='outline'
color='success'
disabled={
!selectedKandangId ||
projectFlock?.approval?.step_number == 1
}
> >
<Button Chickin <Icon icon='mdi:checkbox-marked-outline' />
className='w-full px-2 py-1 text-sm' </Button>
variant='outline' </Link>
color='error' </RequirePermission>
disabled={ <RequirePermission permissions='lti.production.project_flock_kandangs.closing.detail'>
!selectedKandangId || <Link
projectFlock?.approval?.step_number == 1 href={`/production/project-flock/closing?projectFlockId=${projectFlock.id}&projectFlockKandangId=${selectedKandang?.project_flock_kandang_id}`}
} className='m-0 p-0'
> >
Close <Icon icon='mdi:checkbox-marked-circle-outline' /> <Button
</Button> className='w-full px-2 py-1 text-sm'
</Link> variant='outline'
</RequirePermission> color='error'
</div> disabled={
!selectedKandangId ||
projectFlock?.approval?.step_number == 1
}
>
Close <Icon icon='mdi:checkbox-marked-circle-outline' />
</Button>
</Link>
</RequirePermission>
</div> </div>
</div> </div>
</div> </div>