fix(FE-86-87-88) Hapus tombol edit di index, tambah tombol approve dan delete di detail, dan hit endpoint approve yang udah ada di hoppscocth

This commit is contained in:
randy-ar
2025-10-21 11:37:33 +07:00
parent 838d7277c3
commit e4a1138d8d
2 changed files with 172 additions and 47 deletions
@@ -10,11 +10,12 @@ 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';
import { ROWS_OPTIONS } from '@/config/constant'; import { ROWS_OPTIONS } from '@/config/constant';
import { isResponseSuccess } from '@/lib/api-helper'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { cn } from '@/lib/helper'; import { cn } from '@/lib/helper';
import { AreaApi, KandangApi, LocationApi } from '@/services/api/master-data'; import { AreaApi, KandangApi, LocationApi } from '@/services/api/master-data';
import { ProjectFlockApi } from '@/services/api/production'; import { ProjectFlockApi } from '@/services/api/production';
import { useTableFilter } from '@/services/hooks/useTableFilter'; import { useTableFilter } from '@/services/hooks/useTableFilter';
import { BaseApiResponse } from '@/types/api/api-general';
import { Kandang } from '@/types/api/master-data/kandang'; import { Kandang } from '@/types/api/master-data/kandang';
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';
@@ -57,7 +58,7 @@ const RowOptionsMenu = ({
<Icon icon='mdi:eye-outline' width={16} height={16} /> <Icon icon='mdi:eye-outline' width={16} height={16} />
Detail Detail
</Button> </Button>
<Button {/* <Button
href={`/production/project-flock/detail/edit?projectFlockId=${props.row.original.id}`} href={`/production/project-flock/detail/edit?projectFlockId=${props.row.original.id}`}
variant='ghost' variant='ghost'
color='warning' color='warning'
@@ -65,7 +66,7 @@ const RowOptionsMenu = ({
> >
<Icon icon='mdi:pencil-outline' width={16} height={16} /> <Icon icon='mdi:pencil-outline' width={16} height={16} />
Edit Edit
</Button> </Button> */}
<Button <Button
onClick={deleteClickHandler} onClick={deleteClickHandler}
variant='ghost' variant='ghost'
@@ -194,6 +195,7 @@ const ProjectFlockTable = () => {
const [isDeleteLoading, setIsDeleteLoading] = useState(false); const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const [selectedIds, setSelectedIds] = useState<number[]>([]); const [selectedIds, setSelectedIds] = useState<number[]>([]);
const [selectedFlocks, setSelectedFlocks] = useState<ProjectFlock[]>([]); const [selectedFlocks, setSelectedFlocks] = useState<ProjectFlock[]>([]);
const [isApproveLoading, setIsApproveLoading] = useState(false);
// Columns // Columns
const projectFlocksColumns: ColumnDef<ProjectFlock>[] = [ const projectFlocksColumns: ColumnDef<ProjectFlock>[] = [
@@ -260,7 +262,7 @@ const ProjectFlockTable = () => {
{kandangNames.length > 0 ? kandangNames.join(', ') : 'Tidak ada'} {kandangNames.length > 0 ? kandangNames.join(', ') : 'Tidak ada'}
</div> </div>
); );
}else{ } else {
return '-'; return '-';
} }
}, },
@@ -377,6 +379,30 @@ const ProjectFlockTable = () => {
} }
}; };
const confirmationModalApproveClickHandler = async () => {
setIsApproveLoading(true);
const approveProjectFlockRes = await ProjectFlockApi.customRequest<
BaseApiResponse<ProjectFlock>,
'POST'
>(`/approve`, {
method: 'POST',
payload: 'POST',
params: {
ids: selectedFlocks.map((flock) => flock.id).join(','),
},
});
if (isResponseSuccess(approveProjectFlockRes)) {
toast.success('Project Flock berhasil di-approve!');
confirmModal.closeModal();
}
if (isResponseError(approveProjectFlockRes)) {
toast.error(approveProjectFlockRes?.message as string);
confirmModal.closeModal();
}
setIsApproveLoading(false);
};
return ( return (
<> <>
<div className='w-full p-0 sm:p-4'> <div className='w-full p-0 sm:p-4'>
@@ -557,10 +583,8 @@ const ProjectFlockTable = () => {
primaryButton={{ primaryButton={{
text: 'Ya', text: 'Ya',
color: 'success', color: 'success',
onClick: async () => { onClick: confirmationModalApproveClickHandler,
toast.success('Project Flock berhasil di-approve!'); isLoading: isApproveLoading,
confirmModal.closeModal();
},
}} }}
/> />
</> </>
@@ -14,7 +14,7 @@ import {
import { Icon } from '@iconify/react'; import { Icon } from '@iconify/react';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useEffect, useMemo, useRef, useState } from 'react'; import { use, useEffect, useMemo, useRef, useState } from 'react';
import useSWR from 'swr'; import useSWR from 'swr';
import { import {
ProjectFlockFormSchema, ProjectFlockFormSchema,
@@ -35,6 +35,8 @@ import { httpClient } from '@/services/http/client';
import { BaseApiResponse } from '@/types/api/api-general'; import { BaseApiResponse } from '@/types/api/api-general';
import axios from 'axios'; import axios from 'axios';
import { FLOCK_CATEGORY_OPTIONS } from '@/config/constant'; import { FLOCK_CATEGORY_OPTIONS } from '@/config/constant';
import { useModal } from '@/components/Modal';
import ConfirmationModal from '@/components/modal/ConfirmationModal';
interface ProjectFlockFormProps { interface ProjectFlockFormProps {
formType?: 'add' | 'edit' | 'detail'; formType?: 'add' | 'edit' | 'detail';
@@ -66,6 +68,12 @@ const ProjectFlockForm = ({
initialValues?.flock?.id ?? 0 initialValues?.flock?.id ?? 0
); );
const deleteModal = useModal();
const confirmModal = useModal();
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const [isApproveLoading, setIsApproveLoading] = useState(false);
// Fetch Data // Fetch Data
const flockUrl = `${FlockApi.basePath}?${new URLSearchParams({ const flockUrl = `${FlockApi.basePath}?${new URLSearchParams({
search: '', search: '',
@@ -203,7 +211,7 @@ const ProjectFlockForm = ({
const optionChangeHandler = ( const optionChangeHandler = (
val: OptionType | OptionType[] | null, val: OptionType | OptionType[] | null,
inputName: string, inputName: string
) => { ) => {
formik.setFieldValue(inputName, val); formik.setFieldValue(inputName, val);
formik.setFieldValue( formik.setFieldValue(
@@ -218,7 +226,7 @@ const ProjectFlockForm = ({
formik.setFieldValue('category', (val as OptionType)?.value); formik.setFieldValue('category', (val as OptionType)?.value);
formik.setFieldValue('category_option', val); formik.setFieldValue('category_option', val);
formik.setFieldTouched('category', true); formik.setFieldTouched('category', true);
} };
const kandangChangeHandler = (event: React.ChangeEvent<HTMLInputElement>) => { const kandangChangeHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
const { value, checked } = event.target; const { value, checked } = event.target;
@@ -240,7 +248,11 @@ const ProjectFlockForm = ({
formik.setFieldValue( formik.setFieldValue(
'kandang_ids', 'kandang_ids',
optionsKandang optionsKandang
.filter((kandang) => (kandang.status === 'NON_ACTIVE' || formik.values.kandang_ids.includes(kandang.id))) .filter(
(kandang) =>
kandang.status === 'NON_ACTIVE' ||
formik.values.kandang_ids.includes(kandang.id)
)
.map((kandang) => kandang.id) .map((kandang) => kandang.id)
); );
} else { } else {
@@ -321,7 +333,7 @@ const ProjectFlockForm = ({
fcr_id: initialValues?.fcr?.id ?? 0, fcr_id: initialValues?.fcr?.id ?? 0,
location_id: initialValues?.location?.id ?? 0, location_id: initialValues?.location?.id ?? 0,
period: initialValues?.period ?? '', period: initialValues?.period ?? '',
kandang_ids: initialValues?.kandangs?.map((k: Kandang) => k.id) ?? [], kandang_ids: initialValues?.kandangs?.map((k: Kandang) => k.id),
}; };
}, [initialValues]); }, [initialValues]);
@@ -391,26 +403,6 @@ const ProjectFlockForm = ({
} }
}, [formType, initialValues]); }, [formType, initialValues]);
// Setelah data kandang difetch, centang otomatis kandang yang ada di initialValues
useEffect(() => {
if (formType != 'add' && isResponseSuccess(kandang)) {
if (selectedLocation) {
setOptionsKandang(kandang.data);
setOpenSelectKandangs(true);
const kandangIds =
initialValues?.kandangs?.map((k: Kandang) => k.id) ?? [];
formik.setFieldValue('kandang_ids', kandangIds);
console.log("kandangIds");
console.log(kandangIds);
} else {
setOptionsKandang([]);
setOpenSelectKandangs(false);
formik.setFieldValue('kandang_ids', []);
initialValues.kandangs = [];
}
}
}, [formType, kandang, initialValues]);
useEffect(() => { useEffect(() => {
formik.validateForm(); formik.validateForm();
}, [formik.values]); }, [formik.values]);
@@ -421,6 +413,43 @@ const ProjectFlockForm = ({
: formik.setFieldValue('period', ''); : formik.setFieldValue('period', '');
}, [periodFlocks]); }, [periodFlocks]);
// Actions handler
const confirmationModalDeleteClickHandler = async () => {
setIsDeleteLoading(true);
const deleteProjectFlockRes = await ProjectFlockApi.delete(
initialValues?.id as number
);
if (isResponseSuccess(deleteProjectFlockRes)) {
toast.success(deleteProjectFlockRes?.message as string);
router.push('/production/project-flock');
}
if (isResponseError(deleteProjectFlockRes)) {
toast.error(deleteProjectFlockRes?.message as string);
}
setIsDeleteLoading(false);
};
const confirmationModalApproveClickHandler = async () => {
setIsApproveLoading(true);
const approveProjectFlockRes = await ProjectFlockApi.customRequest<
BaseApiResponse<ProjectFlock>,
'POST'
>(`/${initialValues?.id}/approve`, {
method: 'POST',
});
if (isResponseSuccess(approveProjectFlockRes)) {
toast.success('Project Flock berhasil di-approve!');
confirmModal.closeModal();
}
if (isResponseError(approveProjectFlockRes)) {
toast.error(approveProjectFlockRes?.message as string);
confirmModal.closeModal();
}
setIsApproveLoading(false);
};
return ( return (
<> <>
<section className='w-full'> <section className='w-full'>
@@ -459,6 +488,22 @@ const ProjectFlockForm = ({
</div> </div>
</div> </div>
)} )}
<div className='w-full py-4'>
<Button
variant='outline'
color='success'
onClick={() => {
if (initialValues?.id) {
confirmModal.openModal();
}
}}
disabled={!initialValues?.id}
className='w-full sm:w-fit'
>
<Icon icon='material-symbols:check' width={24} height={24} />
Approve
</Button>
</div>
<form <form
className='w-auto h-auto' className='w-auto h-auto'
onSubmit={formik.handleSubmit} onSubmit={formik.handleSubmit}
@@ -466,7 +511,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 {formik.values.kandang_ids && formik.values.kandang_ids.join(', ')}</div>
<div className='grid sm:grid-cols-2 gap-4'> <div className='grid sm:grid-cols-2 gap-4'>
<SelectInput <SelectInput
@@ -538,8 +583,7 @@ const ProjectFlockForm = ({
onChange={categoryChangeHandler} onChange={categoryChangeHandler}
options={FLOCK_CATEGORY_OPTIONS} options={FLOCK_CATEGORY_OPTIONS}
isError={ isError={
formik.touched.category && formik.touched.category && Boolean(formik.errors.category)
Boolean(formik.errors.category)
} }
errorMessage={formik.errors.category as string} errorMessage={formik.errors.category as string}
isClearable isClearable
@@ -571,7 +615,9 @@ const ProjectFlockForm = ({
<div className='card-title'>Pilih Kandang</div> <div className='card-title'>Pilih Kandang</div>
<Button <Button
variant='link' variant='link'
className='text-primary rotate-0 transition-transform hover:text-inherit' className={`text-primary rotate-${
openSelectKandangs ? '180' : '0'
} transition-transform hover:text-inherit`}
> >
<Icon <Icon
icon='material-symbols:keyboard-arrow-down' icon='material-symbols:keyboard-arrow-down'
@@ -597,24 +643,31 @@ const ProjectFlockForm = ({
type='checkbox' type='checkbox'
checked={ checked={
optionsKandang optionsKandang
.filter((k) => (k.status === 'NON_ACTIVE' || formik.values.kandang_ids.includes(k.id))) .filter(
(k) =>
k.status === 'NON_ACTIVE' ||
formik.values.kandang_ids.includes(k.id)
)
.every((k) => .every((k) =>
formik.values.kandang_ids.includes(k.id) formik.values.kandang_ids.includes(k.id)
) && ) &&
optionsKandang.filter( optionsKandang.filter(
(k) => (k.status === 'NON_ACTIVE' || formik.values.kandang_ids.includes(k.id)) (k) =>
k.status === 'NON_ACTIVE' ||
formik.values.kandang_ids.includes(k.id)
).length > 0 ).length > 0
} }
className='checkbox' className='checkbox transition-none'
disabled={formType === 'detail' || optionsKandang.filter( disabled={
(k) => (k.status === 'NON_ACTIVE') formType === 'detail' ||
).length == 0} optionsKandang.filter(
(k) => k.status === 'NON_ACTIVE'
).length == 0
}
onChange={ onChange={
formType === 'detail' formType === 'detail'
? () => {} ? () => {}
: kandangCheckAll : kandangCheckAll
} }
/> />
</label> </label>
@@ -634,7 +687,7 @@ const ProjectFlockForm = ({
<input <input
value={kandang.id} value={kandang.id}
type='checkbox' type='checkbox'
className='checkbox' className='checkbox transition-none'
checked={formik.values.kandang_ids.includes( checked={formik.values.kandang_ids.includes(
kandang.id kandang.id
)} )}
@@ -645,7 +698,7 @@ const ProjectFlockForm = ({
} }
disabled={ disabled={
formType === 'detail' || formType === 'detail' ||
(kandang.status != 'NON_ACTIVE') kandang.status != 'NON_ACTIVE'
} }
/> />
</label> </label>
@@ -703,7 +756,55 @@ const ProjectFlockForm = ({
)} )}
</div> </div>
</form> </form>
<div className='w-full'>
<Button
onClick={() => {
if (initialValues?.id) {
deleteModal.openModal();
}
}}
color='error'
>
<Icon
icon='material-symbols:delete-outline-rounded'
width={16}
height={16}
className='justify-start text-sm'
/>
Delete
</Button>
</div>
</section> </section>
<ConfirmationModal
ref={deleteModal.ref}
type='error'
text={`Apakah anda yakin ingin menghapus data Project Flock ini (${initialValues?.flock?.name} - ${initialValues?.area?.name})?`}
secondaryButton={{
text: 'Tidak',
}}
primaryButton={{
text: 'Ya',
color: 'error',
isLoading: isDeleteLoading,
onClick: confirmationModalDeleteClickHandler,
}}
/>
<ConfirmationModal
ref={confirmModal.ref}
type='success'
text={`Apakah anda yakin ingin approve Project Flock berikut? (${initialValues?.flock?.name} - ${initialValues?.area?.name})?`}
secondaryButton={{
text: 'Tidak',
}}
primaryButton={{
text: 'Ya',
color: 'success',
isLoading: isApproveLoading,
onClick: confirmationModalApproveClickHandler,
}}
/>
</> </>
); );
}; };