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/components/FloatingActionsButton.tsx b/src/components/FloatingActionsButton.tsx index 1ee5e6c0..c0033d72 100644 --- a/src/components/FloatingActionsButton.tsx +++ b/src/components/FloatingActionsButton.tsx @@ -1,5 +1,6 @@ 'use client'; +import Button from '@/components/Button'; import Tooltip from '@/components/Tooltip'; import { cn } from '@/lib/helper'; import { Icon } from '@iconify/react'; @@ -11,12 +12,14 @@ type FloatingActionsButtonProps = { label?: string; onClick?: () => void; hidden?: boolean; + disabled?: boolean; }[]; approvals: { action: 'APPROVED' | 'REJECTED'; icon: string; label?: string; onClick?: () => void; + disabled?: boolean; }[]; selectedRowIds: number[]; onClose: () => void; @@ -69,10 +72,12 @@ const FloatingActionsButton = ({ .filter((action) => !action.hidden) .map((action, index) => { return ( - + ); })}
{/* Tombol Close */} - + @@ -104,14 +110,18 @@ const FloatingActionsButton = ({ {/* === BARIS BAWAH: Approval Buttons (Approve/Reject) === */}
{approvals.map((approval, index) => ( - + ))}
diff --git a/src/components/pages/production/chickin/form/ChickinForm.tsx b/src/components/pages/production/chickin/form/ChickinForm.tsx index eadc9e66..d7210c8b 100644 --- a/src/components/pages/production/chickin/form/ChickinForm.tsx +++ b/src/components/pages/production/chickin/form/ChickinForm.tsx @@ -14,6 +14,9 @@ import ApprovalSteps, { import { PROJECT_FLOCK_KANDANG_APPROVAL_LINE } from '@/config/approval-line'; import ChickinFormView from '@/components/pages/production/chickin/form/tabs/ChickinFormView'; import ChickinLogsView from '@/components/pages/production/chickin/form/tabs/ChickLogsView'; +import DrawerHeader from '@/components/helper/drawer/DrawerHeader'; +import { Icon } from '@iconify/react'; +import Badge from '@/components/Badge'; const ChickinFormKandang = ({ formType = 'add', initialValues, @@ -24,6 +27,7 @@ const ChickinFormKandang = ({ afterSubmit?: () => void; }) => { const [activeTabId, setActiveTabId] = useState('formChickIn'); + const [openChickin, setOpenChickin] = useState(false); const { approvals, @@ -43,102 +47,142 @@ const ChickinFormKandang = ({ }; return ( -
- + - {approvals && !approvalsLoading && ( - - )} + {/* Informasi Kandang */} +
+
+

Informasi Kandang

- - - emptyContent={ -
- - Informasi Kandang belum tersedia... - -
- } - data={[initialValues?.kandang]} - columns={[ - { - header: 'Area', - accessorFn: () => initialValues?.project_flock?.area.name || '-', - }, - { - header: 'Lokasi', - accessorFn: () => - initialValues?.project_flock?.location.name || '-', - }, - { - header: 'Flock', - accessorFn: () => initialValues?.project_flock?.flock_name || '-', - }, - { - header: 'Kandang', - accessorFn: (row) => row?.name || '-', - }, - { - header: 'Kapasitas', - accessorFn: (row) => - (row?.capacity && formatNumber(row?.capacity)) || '-', - }, - { - header: 'Penanggung Jawab', - accessorFn: (row) => row?.pic?.name || '-', - }, - ]} - className={{ - tableWrapperClassName: 'overflow-x-auto min-h-full!', - tableClassName: 'font-inter w-full table-auto min-h-full!', - headerRowClassName: 'border-b border-b-gray-200', - headerColumnClassName: - 'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end', - bodyRowClassName: 'border-b border-b-gray-200', - bodyColumnClassName: - 'px-6 py-3 last:flex last:flex-row last:justify-end', - paginationClassName: 'hidden', - }} + {approvals && !approvalsLoading && ( +
+ +
+ )} + + {/* Badge Row */} +
+ + {' '} + Aktif + +
+ + + {` Kapasitas ${formatNumber(initialValues.kandang.capacity)} Ekor`} + +
+ + {/* Information Grid */} +
+ {/* Area */} +
+ Area +
+
+ {initialValues.project_flock.area.name} +
+ + {/* Lokasi */} +
+ Lokasi +
+
+ {initialValues.project_flock?.location.name} +
+ + {/* Kandang */} +
+ Kandang +
+
{initialValues.kandang.name}
+ + {/* Jumlah DOC */} +
+ Jumlah DOC +
+
+ {formatNumber( + initialValues.chickins?.reduce( + (total, chickin) => total + chickin.usage_qty, + 0 + ) ?? 0 + )}{' '} + Ekor +
+
+
+ +
+
+

Informasi Chick In

+ {/* Badge Row */} +
+ + {' '} + Perlu Chick In ({initialValues.available_qtys?.length ?? 0}) + +
+ setOpenChickin(!openChickin)} + > + {`Riwayat Chick In ${formatNumber(initialValues.chickins?.length ?? 0)}`} + + +
+
+ {openChickin && ( + - - - ), - }, - { - content: ( - - ), - id: 'logsChickIn', - label: 'Riwayat Chick In', - }, - ]} - variant='lifted' + )} + -
+ ); }; diff --git a/src/components/pages/production/chickin/form/tabs/ChickLogsView.tsx b/src/components/pages/production/chickin/form/tabs/ChickLogsView.tsx index 8accf9ae..865091d7 100644 --- a/src/components/pages/production/chickin/form/tabs/ChickLogsView.tsx +++ b/src/components/pages/production/chickin/form/tabs/ChickLogsView.tsx @@ -2,17 +2,12 @@ import Alert from '@/components/Alert'; import Button from '@/components/Button'; import Card from '@/components/Card'; import { useModal } from '@/components/Modal'; -import ConfirmationModal from '@/components/modal/ConfirmationModal'; import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes'; import PillBadge from '@/components/PillBadge'; -import Table from '@/components/Table'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; -import { cn, formatDate, formatNumber } from '@/lib/helper'; +import { formatDate, formatNumber } from '@/lib/helper'; import { ChickinApi } from '@/services/api/production/chickin'; -import { - Chickin, - ProjectFlockKandang, -} from '@/types/api/production/project-flock-kandang'; +import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang'; import { Icon } from '@iconify/react'; import { useState } from 'react'; import toast from 'react-hot-toast'; @@ -54,105 +49,120 @@ const ChickinLogsView = ({ return ( <> - -
- {initialValues?.approval?.step_number == 1 && ( - - )} -
- - data={initialValues?.chickins || []} - columns={[ - { - header: '#', - cell: (props) => props.row.index + 1, - }, - { - accessorFn: (row) => row.chick_in_date, - header: 'Tanggal Chick In', - cell: (props) => { - return formatDate(props.getValue() as string, 'DD MMM YYYY'); - }, - }, - { - accessorFn: (row) => row.product_warehouse?.warehouse?.name, - header: 'Kandang', - }, - { - accessorFn: (row) => row.product_warehouse?.product?.name, - header: 'Produk', - }, - { - accessorFn: (row) => row.usage_qty ?? row.pending_usage_qty, - header: 'Jumlah Chick In', - cell: (props) => { - if (props.row.original.usage_qty != 0) { - return formatNumber(props.row.original.usage_qty); - } else if (props.row.original.pending_usage_qty != 0) { - return formatNumber(props.row.original.pending_usage_qty); - } else { - return '-'; - } - }, - }, - { - accessorFn: (row) => row.pending_usage_qty, - header: 'Status', - cell: (props) => { - return ( - - ); - }, - }, - ]} - className={{ - containerClassName: cn({ - 'mb-20': initialValues?.chickins?.length === 0, - }), - tableWrapperClassName: 'overflow-x-auto min-h-full!', - tableClassName: 'font-inter w-full table-auto min-h-full!', - headerRowClassName: 'border-b border-b-gray-200', - headerColumnClassName: - 'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end', - bodyRowClassName: 'border-b border-b-gray-200', - bodyColumnClassName: - 'px-6 py-3 last:flex last:flex-row last:justify-end', - paginationClassName: 'hidden', - }} - /> +
+ {/* Card List Chickin Logs */} + {(initialValues?.chickins || []).length === 0 ? ( +
+ + Belum ada riwayat Chick In... + +
+ ) : ( + (initialValues?.chickins || []).map((chickin, index) => { + const isApproved = chickin.usage_qty !== 0; + const isPending = chickin.pending_usage_qty !== 0; + const quantity = isApproved + ? chickin.usage_qty + : isPending + ? chickin.pending_usage_qty + : 0; + + return ( + +
+ {/* Header with Status Badge */} +
+
+ Chick In #{index + 1} +
+ +
+ + {/* Tanggal Chick In */} +
+
+ {' '} + Tanggal Chick In +
+
+ {formatDate(chickin.chick_in_date, 'DD MMM YYYY')} +
+
+ + {/* Kandang */} +
+
+ {' '} + Kandang +
+
+ {chickin.product_warehouse?.warehouse?.name || '-'} +
+
+ + {/* Produk */} +
+
+ {' '} + Produk +
+
+ {chickin.product_warehouse?.product?.name || '-'} +
+
+ + {/* Jumlah Chick In */} +
+
+ {' '} + Jumlah Chick In +
+
+ {quantity > 0 ? `${formatNumber(quantity)} Ekor` : '-'} +
+
+
+
+ ); + }) + )} + + {initialValues?.approval?.step_number == 1 && ( + + )} + {chickinErrorMessage && (
setChickinErrorMessage('')}> {chickinErrorMessage}
)} - +
+ { handleReset(); }} onSubmit={formik.handleSubmit} > - - - data={formik.values.chickin_requests || []} - columns={[ - { - accessorFn: (row) => row.chick_in_date, - header: 'Tanggal Chick In', - cell(props) { - return ( - - ); - }, - }, - { - accessorFn: (row) => row.product_warehouse_id, - header: 'Produk', - cell(props) { - const availableQty = initialValues?.available_qtys?.find( - (availableQty) => - availableQty.product_warehouse.id === - props.row.original.product_warehouse_id - ); - return ( -
{availableQty?.product_warehouse?.product?.name}
- ); - }, - }, - { - accessorFn: (row) => row.product_warehouse_id, - header: 'Jumlah (ekor)', - cell(props) { - const availableQty = initialValues?.available_qtys?.find( - (availableQty) => - availableQty.product_warehouse.id === - props.row.original.product_warehouse_id - ); - return ( -
- {availableQty?.available_qty - ? formatNumber(availableQty?.available_qty) - : '-'} -
- ); - }, - }, - ]} - className={{ - tableWrapperClassName: 'overflow-x-auto min-h-full!', - tableClassName: 'font-inter w-full table-auto min-h-full!', - headerRowClassName: 'border-b border-b-gray-200', - headerColumnClassName: - 'px-2 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end', - bodyRowClassName: 'border-b border-b-gray-200', - bodyColumnClassName: - 'px-2 py-2 last:flex last:flex-row last:justify-end', - paginationClassName: 'hidden', - }} - emptyContent={ -
- - Isi persediaan DOC untuk kandang belum tersedia... - + {(formik.values.chickin_requests || []).map((chickinRequest, index) => { + const availableQty = initialValues?.available_qtys?.find( + (availableQty) => + availableQty.product_warehouse.id === + chickinRequest.product_warehouse_id + ); + return ( + +
+
+ {formatNumber(availableQty?.available_qty ?? 0)} Ekor -{' '} + {availableQty?.product_warehouse?.product?.name} +
+ {chickinRequest.chick_in_date && ( + + )}
- } - /> -
-
- + + + ); + })} + {/* + data={formik.values.chickin_requests || []} + columns={[ + { + accessorFn: (row) => row.chick_in_date, + header: 'Tanggal Chick In', + cell(props) { + return ( + + ); + }, + }, + { + accessorFn: (row) => row.product_warehouse_id, + header: 'Produk', + cell(props) { + const availableQty = initialValues?.available_qtys?.find( + (availableQty) => + availableQty.product_warehouse.id === + props.row.original.product_warehouse_id + ); + return ( +
{availableQty?.product_warehouse?.product?.name}
+ ); + }, + }, + { + accessorFn: (row) => row.product_warehouse_id, + header: 'Jumlah (ekor)', + cell(props) { + const availableQty = initialValues?.available_qtys?.find( + (availableQty) => + availableQty.product_warehouse.id === + props.row.original.product_warehouse_id + ); + return ( +
+ {availableQty?.available_qty + ? formatNumber(availableQty?.available_qty) + : '-'} +
+ ); + }, + }, + ]} + className={{ + tableWrapperClassName: 'overflow-x-auto min-h-full!', + tableClassName: 'font-inter w-full table-auto min-h-full!', + headerRowClassName: 'border-b border-b-gray-200', + headerColumnClassName: + 'px-2 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end', + bodyRowClassName: 'border-b border-b-gray-200', + bodyColumnClassName: + 'px-2 py-2 last:flex last:flex-row last:justify-end', + paginationClassName: 'hidden', + }} + emptyContent={ +
+ + Isi persediaan DOC untuk kandang belum tersedia... + +
+ } + /> */} + {formik.values.chickin_requests?.length > 0 && ( -
+ )} {chickinErrorMessage && (
setChickinErrorMessage('')}> {chickinErrorMessage} diff --git a/src/components/pages/production/project-flock/ProjectFlockTable.tsx b/src/components/pages/production/project-flock/ProjectFlockTable.tsx index 613ea5fc..4be30f7a 100644 --- a/src/components/pages/production/project-flock/ProjectFlockTable.tsx +++ b/src/components/pages/production/project-flock/ProjectFlockTable.tsx @@ -10,8 +10,6 @@ import { useModal } from '@/components/Modal'; import ConfirmationModal from '@/components/modal/ConfirmationModal'; import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes'; import Table from '@/components/Table'; -import RowCollapseOptions from '@/components/table/RowCollapseOptions'; -import RowDropdownOptions from '@/components/table/RowDropdownOptions'; import { ROWS_OPTIONS } from '@/config/constant'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { cn, formatDate } from '@/lib/helper'; @@ -23,7 +21,7 @@ import { ProjectFlock } from '@/types/api/production/project-flock'; import { Icon } from '@iconify/react'; import { CellContext, SortingState } from '@tanstack/react-table'; import { useRouter } from 'next/navigation'; -import { ChangeEventHandler, useEffect, useState } from 'react'; +import { ChangeEventHandler, useEffect, useMemo, useState } from 'react'; import toast from 'react-hot-toast'; import useSWR from 'swr'; @@ -124,7 +122,7 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => { }); const router = useRouter(); - // State + // ===== State ===== const [rowSelection, setRowSelection] = useState>({}); const selectedRowIds = Object.keys(rowSelection) .filter((id) => rowSelection[id]) @@ -151,7 +149,7 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => { const [isDeleteLoading, setIsDeleteLoading] = useState(false); const [isApproveLoading, setIsApproveLoading] = useState(false); - // Fetch Data + // ===== Fetch Data ===== const { data: projectFlocks, isLoading, @@ -192,7 +190,7 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => { KandangApi.getAllFetcher ); - // Data to Options Mapping + // ===== Data to Options Mapping ====== const optionsArea = isResponseSuccess(areas) ? areas?.data.map((area) => ({ value: area.id, @@ -212,7 +210,7 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => { })) : []; - // Handler + // ====== HANDLER ====== const pageSizeChangeHandler = (val: OptionType | OptionType[] | null) => { const newVal = val as OptionType; setPageSize(newVal.value as number); @@ -220,17 +218,17 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => { const confirmationModalDeleteClickHandler = async () => { setIsDeleteLoading(true); - await ProjectFlockApi.delete(selectedProjectFlock?.id as number); + await ProjectFlockApi.delete(selectedSingleRow?.id as number); refreshProjectFlocks(); deleteModal.closeModal(); toast.success('Successfully delete Project Flock!'); setIsDeleteLoading(false); + setRowSelection({}); }; const searchChangeHandler: ChangeEventHandler = (e) => { updateFilter('search', e.target.value); }; - const confirmApprovalHandler = async ( notes: string, approvalAction: 'APPROVED' | 'REJECTED' @@ -260,10 +258,29 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => { setIsApproveLoading(false); }; + // ====== EFFECT ====== useEffect(() => { refreshProjectFlocks(); }, [refresh]); + // ====== MEMO ====== + const selectedSingleRow: ProjectFlock | null | undefined = useMemo(() => { + return selectedRowIds.length === 1 + ? isResponseSuccess(projectFlocks) + ? projectFlocks?.data.find((row) => row.id === selectedRowIds[0]) + : null + : null; + }, [rowSelection]); + + const canApprove = useMemo(() => { + if (!selectedSingleRow || isApproveLoading) return false; + + const isPengajuan = selectedSingleRow.approval.step_number == 1; + const isNotRejected = selectedSingleRow.approval.action != 'REJECTED'; + + return isPengajuan && isNotRejected; + }, [selectedSingleRow, isApproveLoading]); + return ( <>
@@ -617,9 +634,10 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => { { action: 'DELETE', icon: 'material-symbols:delete-outline-rounded', - label: `Hapus ${selectedRowIds.length} data`, + label: `Hapus data`, + hidden: selectedRowIds.length !== 1, onClick: () => { - toast.error(`Konfirmasi hapus ${selectedRowIds.length} data.`); + deleteModal.openModal(); }, }, ]} @@ -632,6 +650,7 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => { setApprovalAction('APPROVED'); confirmModal.openModal(); }, + disabled: !canApprove, }, { icon: 'mdi:times', diff --git a/src/components/pages/production/project-flock/closing/ProjectFlockClosingForm.tsx b/src/components/pages/production/project-flock/closing/ProjectFlockClosingForm.tsx index a078ed85..a12e7369 100644 --- a/src/components/pages/production/project-flock/closing/ProjectFlockClosingForm.tsx +++ b/src/components/pages/production/project-flock/closing/ProjectFlockClosingForm.tsx @@ -102,7 +102,7 @@ const ProjectFlockClosingForm = ({ className={{ badge: 'rounded-lg px-2' }} > - {` Kapasitas ${formatNumber(projectFlockKandang.kandang.capacity)} Ekor`} + {` Kapasitas ${formatNumber(projectFlockKandang.kandang?.capacity)} Ekor`}
diff --git a/src/components/pages/production/project-flock/detail/ProjectFlockDetail.tsx b/src/components/pages/production/project-flock/detail/ProjectFlockDetail.tsx index 17272d20..e2d8018f 100644 --- a/src/components/pages/production/project-flock/detail/ProjectFlockDetail.tsx +++ b/src/components/pages/production/project-flock/detail/ProjectFlockDetail.tsx @@ -34,6 +34,10 @@ const ProjectFlockDetail = ({ null ); + const selectedKandang = projectFlock.kandangs.find( + (kandang) => kandang.id === Number(selectedKandangId) + ); + const confirmationModalDeleteClickHandler = async () => { setIsDeleteLoading(true); const deleteProjectFlockRes = await ProjectFlockApi.delete( @@ -328,16 +332,21 @@ const ProjectFlockDetail = ({ value={selectedKandangId?.toString()} size='md' color='neutral' + disabled={projectFlock.approval.step_number == 1} > {projectFlock.kandangs.map((kandang) => (
setSelectedKamdangId(kandang.id.toString())} + onClick={() => + projectFlock.approval.step_number > 1 && + setSelectedKamdangId(kandang.id.toString()) + } >
@@ -374,7 +385,9 @@ const ProjectFlockDetail = ({ className='w-full px-2 py-1 text-sm' variant='outline' color='error' - disabled={!selectedKandangId} + disabled={ + !selectedKandangId || projectFlock.approval.step_number == 1 + } > Close