fix(FE-270): adjust flock_name in select input and fixing project flock approval

This commit is contained in:
randy-ar
2025-11-21 00:31:25 +07:00
parent af939ee225
commit d523a01e34
8 changed files with 118 additions and 78 deletions
@@ -11,10 +11,6 @@ const AddChickin = () => {
return ( return (
<> <>
<section className='w-full p-4'> <section className='w-full p-4'>
<FormHeader
title='Daftar Kandang Project Flock'
backUrl='/production/project-flock'
/>
<ProjectFlockChickinDetail projectFlockId={Number(projectFlockId)} /> <ProjectFlockChickinDetail projectFlockId={Number(projectFlockId)} />
</section> </section>
</> </>
@@ -45,7 +45,7 @@ const ChickinFormKandang = ({
return ( return (
<div className='flex flex-col gap-4'> <div className='flex flex-col gap-4'>
<FormHeader <FormHeader
title='Chick In DOC' title={`Chick In ${initialValues.kandang?.name ?? 'Kandang'}`}
backUrl={`/production/project-flock/chickin/add?projectFlockId=${initialValues?.project_flock?.id}`} backUrl={`/production/project-flock/chickin/add?projectFlockId=${initialValues?.project_flock?.id}`}
/> />
@@ -3,6 +3,7 @@ import Button from '@/components/Button';
import Card from '@/components/Card'; import Card from '@/components/Card';
import { useModal } from '@/components/Modal'; import { useModal } from '@/components/Modal';
import ConfirmationModal from '@/components/modal/ConfirmationModal'; import ConfirmationModal from '@/components/modal/ConfirmationModal';
import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes';
import PillBadge from '@/components/PillBadge'; import PillBadge from '@/components/PillBadge';
import Table from '@/components/Table'; import Table from '@/components/Table';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
@@ -31,12 +32,13 @@ const ChickinLogsView = ({
confirmModal.openModal(); confirmModal.openModal();
}; };
const confirmationModalApproveClickHandler = async () => { const confirmationModalApproveClickHandler = async (notes?: string) => {
setChickinErrorMessage(''); setChickinErrorMessage('');
setIsApproveLoading(true); setIsApproveLoading(true);
const approveChickinRes = await ChickinApi.singleApproval( const approveChickinRes = await ChickinApi.singleApproval(
initialValues?.id as number, initialValues?.id as number,
'APPROVED' 'APPROVED',
notes
); );
if (isResponseSuccess(approveChickinRes)) { if (isResponseSuccess(approveChickinRes)) {
toast.success(approveChickinRes?.message as string); toast.success(approveChickinRes?.message as string);
@@ -151,7 +153,7 @@ const ChickinLogsView = ({
</div> </div>
)} )}
</Card> </Card>
<ConfirmationModal <ConfirmationModalWithNotes
ref={confirmModal.ref} ref={confirmModal.ref}
type='success' type='success'
text={`Apakah anda yakin ingin approve data Chickin yang Pending?`} text={`Apakah anda yakin ingin approve data Chickin yang Pending?`}
@@ -161,7 +163,9 @@ const ChickinLogsView = ({
primaryButton={{ primaryButton={{
text: 'Ya', text: 'Ya',
color: 'success', color: 'success',
onClick: confirmationModalApproveClickHandler, onClick: (notes) => {
confirmationModalApproveClickHandler(notes);
},
isLoading: isApproveLoading, isLoading: isApproveLoading,
}} }}
/> />
@@ -6,6 +6,7 @@ import DebouncedTextInput from '@/components/input/DebouncedTextInput';
import SelectInput, { OptionType } from '@/components/input/SelectInput'; import SelectInput, { OptionType } from '@/components/input/SelectInput';
import { useModal } from '@/components/Modal'; import { useModal } from '@/components/Modal';
import ConfirmationModal from '@/components/modal/ConfirmationModal'; import ConfirmationModal from '@/components/modal/ConfirmationModal';
import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes';
import Table from '@/components/Table'; import Table from '@/components/Table';
import RowCollapseOptions from '@/components/table/RowCollapseOptions'; import RowCollapseOptions from '@/components/table/RowCollapseOptions';
import RowDropdownOptions from '@/components/table/RowDropdownOptions'; import RowDropdownOptions from '@/components/table/RowDropdownOptions';
@@ -144,6 +145,9 @@ const ProjectFlockTable = () => {
useState<ProjectFlock>(); useState<ProjectFlock>();
const deleteModal = useModal(); const deleteModal = useModal();
const confirmModal = useModal(); const confirmModal = useModal();
const [approvalAction, setApprovalAction] = useState<'APPROVED' | 'REJECTED'>(
'APPROVED'
);
const [isDeleteLoading, setIsDeleteLoading] = useState(false); const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const [isApproveLoading, setIsApproveLoading] = useState(false); const [isApproveLoading, setIsApproveLoading] = useState(false);
@@ -226,18 +230,21 @@ const ProjectFlockTable = () => {
updateFilter('search', e.target.value); updateFilter('search', e.target.value);
}; };
const confirmationModalApproveClickHandler = async () => { const confirmApprovalHandler = async (
notes: string,
approvalAction: 'APPROVED' | 'REJECTED'
) => {
setIsApproveLoading(true); setIsApproveLoading(true);
const approveProjectFlockRes = await ProjectFlockApi.customRequest< const approveProjectFlockRes =
BaseApiResponse<ProjectFlock>, approvalAction === 'APPROVED'
ProjectFlockApprovalPayload ? await ProjectFlockApi.bulkApprove(
>(`/approvals`, { selectedRowIds.map((id) => id),
method: 'POST', notes
payload: { )
action: 'APPROVED', : await ProjectFlockApi.bulkReject(
approvable_ids: selectedRowIds.map((id) => id), selectedRowIds.map((id) => id),
}, notes
}); );
if (isResponseSuccess(approveProjectFlockRes)) { if (isResponseSuccess(approveProjectFlockRes)) {
toast.success('Project Flock berhasil di-approve!'); toast.success('Project Flock berhasil di-approve!');
@@ -271,6 +278,7 @@ const ProjectFlockTable = () => {
variant='outline' variant='outline'
color='success' color='success'
onClick={() => { onClick={() => {
setApprovalAction('APPROVED');
confirmModal.openModal(); confirmModal.openModal();
}} }}
disabled={selectedRowIds.length === 0} disabled={selectedRowIds.length === 0}
@@ -279,6 +287,19 @@ const ProjectFlockTable = () => {
<Icon icon='material-symbols:check' width={24} height={24} /> <Icon icon='material-symbols:check' width={24} height={24} />
Approve Approve
</Button> </Button>
<Button
variant='outline'
color='error'
onClick={() => {
setApprovalAction('REJECTED');
confirmModal.openModal();
}}
disabled={selectedRowIds.length === 0}
className='w-full sm:w-fit'
>
<Icon icon='mdi:times' width={24} height={24} />
Reject
</Button>
<div className='ms-auto w-full sm:w-auto'> <div className='ms-auto w-full sm:w-auto'>
<DebouncedTextInput <DebouncedTextInput
name='search' name='search'
@@ -570,17 +591,19 @@ const ProjectFlockTable = () => {
}} }}
/> />
<ConfirmationModal <ConfirmationModalWithNotes
ref={confirmModal.ref} ref={confirmModal.ref}
type='success' type={approvalAction == 'APPROVED' ? 'success' : 'error'}
text={`Apakah anda yakin ingin approve data Project Flock ini (${selectedRowIds.length} data)?`} text={`Apakah anda yakin ingin ${approvalAction == 'APPROVED' ? 'approve' : 'reject'} data Project Flock ini (${selectedRowIds.length} data)?`}
secondaryButton={{ secondaryButton={{
text: 'Tidak', text: 'Tidak',
}} }}
primaryButton={{ primaryButton={{
text: 'Ya', text: 'Ya',
color: 'success', color: approvalAction == 'APPROVED' ? 'success' : 'error',
onClick: confirmationModalApproveClickHandler, onClick: (notes) => {
confirmApprovalHandler(notes, approvalAction);
},
isLoading: isApproveLoading, isLoading: isApproveLoading,
}} }}
/> />
@@ -20,6 +20,7 @@ import { Icon } from '@iconify/react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import useSWR from 'swr'; import useSWR from 'swr';
import { FormHeader } from '@/components/helper/form/FormHeader';
const ProjectFlockChickinDetail = ({ const ProjectFlockChickinDetail = ({
projectFlockId, projectFlockId,
@@ -100,6 +101,10 @@ const ProjectFlockChickinDetail = ({
}, [projectFlockId, listProjectFlock]); }, [projectFlockId, listProjectFlock]);
return ( return (
<> <>
<FormHeader
title={`Chick In ${projectFlock?.flock_name ?? 'Project Flock'}`}
backUrl='/production/project-flock'
/>
<div className='flex flex-col gap-4 w-full my-4'> <div className='flex flex-col gap-4 w-full my-4'>
<div className='max-w-full sm:max-w-1/2 md:max-w-3/5 lg:max-w-2/5'> <div className='max-w-full sm:max-w-1/2 md:max-w-3/5 lg:max-w-2/5'>
<SelectInput <SelectInput
@@ -114,7 +119,7 @@ const ProjectFlockChickinDetail = ({
value={ value={
projectFlock projectFlock
? { ? {
label: `${projectFlock?.flock?.name}`, label: `${projectFlock?.flock_name}`,
value: projectFlock?.id, value: projectFlock?.id,
} }
: null : null
@@ -171,7 +176,7 @@ const ProjectFlockChickinDetail = ({
}, },
{ {
header: 'Nama Flock', header: 'Nama Flock',
accessorKey: 'flock.name', accessorKey: 'flock_name',
}, },
{ {
header: 'Kategori', header: 'Kategori',
@@ -205,10 +210,6 @@ const ProjectFlockChickinDetail = ({
); );
}, },
}, },
{
header: 'Periode',
accessorKey: 'period',
},
{ {
header: 'FCR Layer', header: 'FCR Layer',
accessorKey: 'fcr.name', accessorKey: 'fcr.name',
@@ -274,6 +275,10 @@ const ProjectFlockChickinDetail = ({
accessorKey: 'kandang.capacity', accessorKey: 'kandang.capacity',
header: 'Kapasitas', header: 'Kapasitas',
}, },
{
accessorFn: () => projectFlock?.period,
header: 'Periode',
},
{ {
accessorKey: 'approval.step_name', accessorKey: 'approval.step_name',
header: 'Status', header: 'Status',
@@ -41,6 +41,7 @@ import ApprovalSteps, {
useApprovalSteps, useApprovalSteps,
} from '@/components/pages/ApprovalSteps'; } from '@/components/pages/ApprovalSteps';
import { PROJECT_FLOCK_APPROVAL_LINE } from '@/config/approval-line'; import { PROJECT_FLOCK_APPROVAL_LINE } from '@/config/approval-line';
import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes';
interface ProjectFlockFormProps { interface ProjectFlockFormProps {
formType?: 'add' | 'edit' | 'detail'; formType?: 'add' | 'edit' | 'detail';
@@ -71,8 +72,11 @@ const ProjectFlockForm = ({
const [optionsKandang, setOptionsKandang] = useState<Kandang[]>( const [optionsKandang, setOptionsKandang] = useState<Kandang[]>(
initialValues?.kandangs ?? [] initialValues?.kandangs ?? []
); );
const [selectedFlock, setSelectedFlock] = useState<number | undefined>( const [selectedFlock, setSelectedFlock] = useState<string | undefined>(
initialValues?.flock?.id ?? 0 initialValues?.flock_name?.slice(
0,
initialValues?.flock_name?.lastIndexOf(' ')
) ?? ''
); );
const deleteModal = useModal(); const deleteModal = useModal();
@@ -101,9 +105,13 @@ const ProjectFlockForm = ({
useEffect(() => { useEffect(() => {
if (initialValues?.approval?.step_name) { if (initialValues?.approval?.step_name) {
const approvedDisabled = initialValues.approval.step_name !== 'Pengajuan'; const pengajuanRejected =
initialValues.approval.step_number == 1 &&
initialValues.approval.action == 'REJECTED';
const approvedDisabled =
initialValues.approval.step_number !== 1 || pengajuanRejected;
setIsApprovedDisabled(approvedDisabled); setIsApprovedDisabled(approvedDisabled);
setIsRejectedDisabled(!approvedDisabled); setIsRejectedDisabled(!approvedDisabled || pengajuanRejected);
setApprovalAction(!approvedDisabled ? 'APPROVED' : 'REJECTED'); setApprovalAction(!approvedDisabled ? 'APPROVED' : 'REJECTED');
} }
}, [initialValues]); }, [initialValues]);
@@ -470,32 +478,23 @@ const ProjectFlockForm = ({
setIsDeleteLoading(false); setIsDeleteLoading(false);
}; };
const confirmationModalClickHandler = async ({ const confirmApprovalHandler = async (
action = 'APPROVED', notes: string,
}: { approvalAction: 'REJECTED' | 'APPROVED'
action: 'APPROVED' | 'REJECTED'; ) => {
}) => {
if (initialValues?.id === undefined) return; if (initialValues?.id === undefined) return;
setIsApproveLoading(true); setIsApproveLoading(true);
const approveProjectFlockRes = await ProjectFlockApi.customRequest<
BaseApiResponse<ProjectFlock>,
ProjectFlockApprovalPayload
>(`/approvals`, {
method: 'POST',
payload: {
action: action,
approvable_ids: [initialValues?.id],
},
});
if (isResponseSuccess(approveProjectFlockRes)) { const approvalRes =
if (refreshProjectFlocks) { approvalAction == 'APPROVED'
await refreshProjectFlocks(); ? await ProjectFlockApi.approve(initialValues?.id, notes)
: await ProjectFlockApi.reject(initialValues?.id, notes);
if (isResponseSuccess(approvalRes)) {
refreshProjectFlocks?.();
toast.success(approvalRes.message as string);
} }
toast.success(approveProjectFlockRes.message as string); if (isResponseError(approvalRes)) {
} toast.error(approvalRes?.message as string);
if (isResponseError(approveProjectFlockRes)) {
toast.error(approveProjectFlockRes?.message as string);
} }
refreshApprovals(); refreshApprovals();
confirmModal.closeModal(); confirmModal.closeModal();
@@ -600,7 +599,7 @@ const ProjectFlockForm = ({
<div className='card bg-base-100 shadow w-full mb-6'> <div className='card bg-base-100 shadow w-full mb-6'>
<div className='card-body'> <div className='card-body'>
<div className='card-title mb-4'>Informasi Umum</div> <div className='card-title mb-4'>Informasi Umum</div>
{selectedFlock}
<div className='grid sm:grid-cols-2 gap-4'> <div className='grid sm:grid-cols-2 gap-4'>
<SelectInput <SelectInput
required required
@@ -619,10 +618,19 @@ const ProjectFlockForm = ({
<SelectInput <SelectInput
required required
label='Flock' label='Flock'
value={formik.values.flock as OptionType} value={
formik.values.flock_name
? ({
label: selectedFlock,
value: optionsFlock.find((flock) => {
return flock.label === selectedFlock;
})?.value,
} as OptionType)
: undefined
}
onChange={(val) => { onChange={(val) => {
optionChangeHandler(val, 'flock'); optionChangeHandler(val, 'flock');
setSelectedFlock((val as OptionType)?.value as number); setSelectedFlock((val as OptionType)?.label as string);
formik.setFieldValue( formik.setFieldValue(
'flock_name', 'flock_name',
(val as OptionType)?.label (val as OptionType)?.label
@@ -816,7 +824,7 @@ const ProjectFlockForm = ({
}} }}
/> />
<ConfirmationModal <ConfirmationModalWithNotes
ref={confirmModal.ref} ref={confirmModal.ref}
type={approvalAction == 'APPROVED' ? 'success' : 'error'} type={approvalAction == 'APPROVED' ? 'success' : 'error'}
text={`Apakah anda yakin ingin ${ text={`Apakah anda yakin ingin ${
@@ -831,10 +839,8 @@ const ProjectFlockForm = ({
text: 'Ya', text: 'Ya',
color: approvalAction == 'APPROVED' ? 'success' : 'error', color: approvalAction == 'APPROVED' ? 'success' : 'error',
isLoading: isApproveLoading, isLoading: isApproveLoading,
onClick: () => { onClick: (notes) => {
confirmationModalClickHandler({ confirmApprovalHandler(notes, approvalAction);
action: approvalAction,
});
}, },
}} }}
/> />
+3 -2
View File
@@ -21,7 +21,8 @@ export class ChickinService extends BaseApiService<
*/ */
async singleApproval( async singleApproval(
id: number, id: number,
action: 'APPROVED' | 'REJECTED' action: 'APPROVED' | 'REJECTED',
notes?: string
): Promise<BaseApiResponse<{ message: string }> | undefined> { ): Promise<BaseApiResponse<{ message: string }> | undefined> {
try { try {
const path = `${this.basePath}/approvals`; const path = `${this.basePath}/approvals`;
@@ -30,7 +31,7 @@ export class ChickinService extends BaseApiService<
body: { body: {
action: action, action: action,
approvable_ids: [id], approvable_ids: [id],
notes: `${action} chickin ${id}`, notes: notes ?? `${action} chickin ${id}`,
}, },
}); });
} catch (error) { } catch (error) {
+16 -11
View File
@@ -120,7 +120,7 @@ export class ProjectFlockService extends BaseApiService<
| undefined | undefined
> { > {
try { try {
const path = `${this.basePath}/kandangs/${locationId.toString()}/periods`; const path = `${this.basePath}/location/${locationId.toString()}/periods`;
return await httpClient< return await httpClient<
SuccessApiResponse< SuccessApiResponse<
{ {
@@ -145,36 +145,40 @@ export class ProjectFlockService extends BaseApiService<
* Approve single Project Flock * Approve single Project Flock
*/ */
async approve( async approve(
id: number id: number,
notes?: string
): Promise<BaseApiResponse<{ message: string }> | undefined> { ): Promise<BaseApiResponse<{ message: string }> | undefined> {
return await this.bulkApprovalAction([id], 'APPROVED'); return await this.bulkApprovalAction([id], 'APPROVED', notes);
} }
/** /**
* Reject single Project Flock * Reject single Project Flock
*/ */
async reject( async reject(
id: number id: number,
notes?: string
): Promise<BaseApiResponse<{ message: string }> | undefined> { ): Promise<BaseApiResponse<{ message: string }> | undefined> {
return await this.bulkApprovalAction([id], 'REJECTED'); return await this.bulkApprovalAction([id], 'REJECTED', notes);
} }
/** /**
* Approve Bulk Project Flock * Approve Bulk Project Flock
*/ */
async bulkApprove( async bulkApprove(
ids: number[] ids: number[],
notes?: string
): Promise<BaseApiResponse<{ message: string }> | undefined> { ): Promise<BaseApiResponse<{ message: string }> | undefined> {
return await this.bulkApprovalAction(ids, 'APPROVED'); return await this.bulkApprovalAction(ids, 'APPROVED', notes);
} }
/** /**
* Reject Bulk Project Flock * Reject Bulk Project Flock
*/ */
async bulkReject( async bulkReject(
ids: number[] ids: number[],
notes?: string
): Promise<BaseApiResponse<{ message: string }> | undefined> { ): Promise<BaseApiResponse<{ message: string }> | undefined> {
return await this.bulkApprovalAction(ids, 'REJECTED'); return await this.bulkApprovalAction(ids, 'REJECTED', notes);
} }
/** /**
@@ -182,7 +186,8 @@ export class ProjectFlockService extends BaseApiService<
*/ */
async bulkApprovalAction( async bulkApprovalAction(
ids: number[], ids: number[],
action: 'APPROVED' | 'REJECTED' action: 'APPROVED' | 'REJECTED',
notes?: string
): Promise<BaseApiResponse<{ message: string }> | undefined> { ): Promise<BaseApiResponse<{ message: string }> | undefined> {
try { try {
const path = `${this.basePath}/approvals`; const path = `${this.basePath}/approvals`;
@@ -191,7 +196,7 @@ export class ProjectFlockService extends BaseApiService<
body: { body: {
action: action, action: action,
approvable_ids: ids, approvable_ids: ids,
notes: `Bulk ${action} Project Flock ${ids.join(', ')}`, notes: notes ?? `Bulk ${action} Project Flock ${ids.join(', ')}`,
}, },
}); });
} catch (error) { } catch (error) {