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