From 167270546411c148ba5c791028b31d69c269ef1f Mon Sep 17 00:00:00 2001 From: randy-ar Date: Tue, 21 Oct 2025 10:14:17 +0700 Subject: [PATCH] fix(FE-88-89) adjust category flock dengan API backend & set disabled input period --- .../inventory/adjustment/detail/layout.tsx | 11 ++ .../master-data/customer/detail/layout.tsx | 11 ++ src/app/master-data/flock/detail/layout.tsx | 11 ++ .../master-data/supplier/detail/layout.tsx | 11 ++ .../project-flock/detail/layout.tsx | 11 ++ .../project-flock/ProjectFlockTable.tsx | 168 ++++++++++++++++-- .../form/ProjectFlockForm.schema.ts | 13 +- .../project-flock/form/ProjectFlockForm.tsx | 52 +++--- src/config/constant.ts | 11 ++ src/types/api/production/project-flock.d.ts | 5 +- 10 files changed, 262 insertions(+), 42 deletions(-) create mode 100644 src/app/inventory/adjustment/detail/layout.tsx create mode 100644 src/app/master-data/customer/detail/layout.tsx create mode 100644 src/app/master-data/flock/detail/layout.tsx create mode 100644 src/app/master-data/supplier/detail/layout.tsx create mode 100644 src/app/production/project-flock/detail/layout.tsx diff --git a/src/app/inventory/adjustment/detail/layout.tsx b/src/app/inventory/adjustment/detail/layout.tsx new file mode 100644 index 00000000..b41c70f9 --- /dev/null +++ b/src/app/inventory/adjustment/detail/layout.tsx @@ -0,0 +1,11 @@ +import SuspenseHelper from "@/components/helper/SuspenseHelper" + +const Layout = ({ + children +}: Readonly<{ + children: React.ReactNode +}>) => { + return {children} +} + +export default Layout; \ No newline at end of file diff --git a/src/app/master-data/customer/detail/layout.tsx b/src/app/master-data/customer/detail/layout.tsx new file mode 100644 index 00000000..b41c70f9 --- /dev/null +++ b/src/app/master-data/customer/detail/layout.tsx @@ -0,0 +1,11 @@ +import SuspenseHelper from "@/components/helper/SuspenseHelper" + +const Layout = ({ + children +}: Readonly<{ + children: React.ReactNode +}>) => { + return {children} +} + +export default Layout; \ No newline at end of file diff --git a/src/app/master-data/flock/detail/layout.tsx b/src/app/master-data/flock/detail/layout.tsx new file mode 100644 index 00000000..b41c70f9 --- /dev/null +++ b/src/app/master-data/flock/detail/layout.tsx @@ -0,0 +1,11 @@ +import SuspenseHelper from "@/components/helper/SuspenseHelper" + +const Layout = ({ + children +}: Readonly<{ + children: React.ReactNode +}>) => { + return {children} +} + +export default Layout; \ No newline at end of file diff --git a/src/app/master-data/supplier/detail/layout.tsx b/src/app/master-data/supplier/detail/layout.tsx new file mode 100644 index 00000000..b41c70f9 --- /dev/null +++ b/src/app/master-data/supplier/detail/layout.tsx @@ -0,0 +1,11 @@ +import SuspenseHelper from "@/components/helper/SuspenseHelper" + +const Layout = ({ + children +}: Readonly<{ + children: React.ReactNode +}>) => { + return {children} +} + +export default Layout; \ No newline at end of file diff --git a/src/app/production/project-flock/detail/layout.tsx b/src/app/production/project-flock/detail/layout.tsx new file mode 100644 index 00000000..b41c70f9 --- /dev/null +++ b/src/app/production/project-flock/detail/layout.tsx @@ -0,0 +1,11 @@ +import SuspenseHelper from "@/components/helper/SuspenseHelper" + +const Layout = ({ + children +}: Readonly<{ + children: React.ReactNode +}>) => { + return {children} +} + +export default Layout; \ No newline at end of file diff --git a/src/components/pages/production/project-flock/ProjectFlockTable.tsx b/src/components/pages/production/project-flock/ProjectFlockTable.tsx index 8fe3ff7f..d283e46d 100644 --- a/src/components/pages/production/project-flock/ProjectFlockTable.tsx +++ b/src/components/pages/production/project-flock/ProjectFlockTable.tsx @@ -3,6 +3,7 @@ import Button from '@/components/Button'; import DebouncedTextInput from '@/components/input/DebouncedTextInput'; import SelectInput, { OptionType } from '@/components/input/SelectInput'; +import TextInput from '@/components/input/TextInput'; import { useModal } from '@/components/Modal'; import ConfirmationModal from '@/components/modal/ConfirmationModal'; import Table from '@/components/Table'; @@ -11,6 +12,7 @@ import RowDropdownOptions from '@/components/table/RowDropdownOptions'; import { ROWS_OPTIONS } from '@/config/constant'; import { isResponseSuccess } from '@/lib/api-helper'; import { cn } from '@/lib/helper'; +import { AreaApi, KandangApi, LocationApi } from '@/services/api/master-data'; import { ProjectFlockApi } from '@/services/api/production'; import { useTableFilter } from '@/services/hooks/useTableFilter'; import { Kandang } from '@/types/api/master-data/kandang'; @@ -92,12 +94,33 @@ const ProjectFlockTable = () => { } = useTableFilter({ initial: { search: '', + areaFilter: '', + locationFilter: '', + kandangFilter: '', + periodFilter: '', }, paramMap: { page: 'page', pageSize: 'limit', + search: 'search', + areaFilter: 'area_id', + locationFilter: 'location_id', + kandangFilter: 'kandang_id', + periodFilter: 'period', }, }); + const [locationSelectInputValue, setLocationSelectInputValue] = useState(''); + const [areaSelectInputValue, setAreaSelectInputValue] = useState(''); + const [kandangSelectInputValue, setKandangSelectInputValue] = useState(''); + + const [selectedArea, setSelectedArea] = useState(null); + const [selectedLocation, setSelectedLocation] = useState( + null + ); + const [selectedKandang, setSelectedKandang] = useState( + null + ); + const [periodInputValue, setPeriodInputValue] = useState(null); // Fetch Data const { @@ -109,6 +132,59 @@ const ProjectFlockTable = () => { ProjectFlockApi.getAllFetcher ); + const areaUrl = `${AreaApi.basePath}?${new URLSearchParams({ + search: areaSelectInputValue, + limit: '100', + }).toString()}`; + const { + data: areas, + isLoading: isLoadingAreas, + mutate: refreshAreas, + } = useSWR(areaUrl, AreaApi.getAllFetcher); + + const locationUrl = `${LocationApi.basePath}?${new URLSearchParams({ + search: locationSelectInputValue, + area_id: selectedArea != null ? selectedArea.value.toString() : '', + limit: '100', + }).toString()}`; + const { + data: locations, + isLoading: isLoadingLocations, + mutate: refreshLocations, + } = useSWR(locationUrl, LocationApi.getAllFetcher); + + const kandangUrl = `${KandangApi.basePath}?${new URLSearchParams({ + search: kandangSelectInputValue, + location_id: + selectedLocation != null ? selectedLocation.value.toString() : '', + limit: '100', + }).toString()}`; + const { + data: kandangs, + isLoading: isLoadingKandang, + mutate: refreshKandang, + } = useSWR(kandangUrl, KandangApi.getAllFetcher); + + // Data to Options Mapping + const optionsArea = isResponseSuccess(areas) + ? areas?.data.map((area) => ({ + value: area.id, + label: area.name, + })) + : []; + const optionsKandang = isResponseSuccess(kandangs) + ? kandangs?.data.map((kandang) => ({ + value: kandang.id, + label: kandang.name, + })) + : []; + const optionsLocation = isResponseSuccess(locations) + ? locations?.data.map((location) => ({ + value: location.id, + label: location.name, + })) + : []; + // State const [sorting, setSorting] = useState([]); const [selectedProjectFlock, setSelectedProjectFlock] = @@ -170,21 +246,23 @@ const ProjectFlockTable = () => { header: 'FCR', }, { - accessorKey: 'product_category.name', - header: 'Kategori Produk', + accessorKey: 'category', + header: 'Kategori', }, { header: 'Kandang', cell: (props) => { const kandang = props.row.original.kandangs; - const kandangNames = kandang.map((k: Kandang) => k.name); - console.log('kandang'); - console.log(kandang); - return ( -
- {kandangNames.length > 0 ? kandangNames.join(', ') : 'Tidak ada'} -
- ); + if (kandang) { + const kandangNames = kandang.map((k: Kandang) => k.name); + return ( +
+ {kandangNames.length > 0 ? kandangNames.join(', ') : 'Tidak ada'} +
+ ); + }else{ + return '-'; + } }, }, { @@ -341,7 +419,60 @@ const ProjectFlockTable = () => { /> -
+
+ { + setSelectedArea(val as OptionType); + updateFilter( + 'areaFilter', + (val as OptionType)?.value.toString() + ); + }} + isClearable + /> + { + setSelectedLocation(val as OptionType); + updateFilter( + 'locationFilter', + (val as OptionType)?.value.toString() + ); + }} + isClearable + /> + { + setSelectedKandang(val as OptionType); + updateFilter( + 'kandangFilter', + (val as OptionType)?.value.toString() + ); + }} + isClearable + /> + { + setPeriodInputValue(parseInt(e.target.value)); + updateFilter('periodFilter', e.target.value); + }} + /> {
+ + { formik.setFieldValue(inputName, val); formik.setFieldValue( @@ -213,6 +214,12 @@ const ProjectFlockForm = ({ formik.setFieldTouched(`${inputName}_id`, true); }; + const categoryChangeHandler = (val: OptionType | OptionType[] | null) => { + formik.setFieldValue('category', (val as OptionType)?.value); + formik.setFieldValue('category_option', val); + formik.setFieldTouched('category', true); + } + const kandangChangeHandler = (event: React.ChangeEvent) => { const { value, checked } = event.target; if (checked) { @@ -233,7 +240,7 @@ const ProjectFlockForm = ({ formik.setFieldValue( 'kandang_ids', optionsKandang - .filter((kandang) => kandang.status === 'NON_ACTIVE') + .filter((kandang) => (kandang.status === 'NON_ACTIVE' || formik.values.kandang_ids.includes(kandang.id))) .map((kandang) => kandang.id) ); } else { @@ -290,10 +297,10 @@ const ProjectFlockForm = ({ label: initialValues.area.name, } : null, - product_category: initialValues?.product_category + category_option: initialValues?.category ? { - value: initialValues.product_category.id, - label: initialValues.product_category.name, + value: initialValues.category, + label: initialValues.category, } : null, fcr: initialValues?.fcr @@ -310,11 +317,11 @@ const ProjectFlockForm = ({ : null, flock_id: initialValues?.flock?.id ?? 0, area_id: initialValues?.area?.id ?? 0, - product_category_id: initialValues?.product_category?.id ?? 0, + category: initialValues?.category, fcr_id: initialValues?.fcr?.id ?? 0, location_id: initialValues?.location?.id ?? 0, period: initialValues?.period ?? '', - kandang_ids: [], + kandang_ids: initialValues?.kandangs?.map((k: Kandang) => k.id) ?? [], }; }, [initialValues]); @@ -332,7 +339,7 @@ const ProjectFlockForm = ({ const payload: CreateProjectFlockPayload = { flock_id: values.flock_id as number, area_id: values.area_id as number, - product_category_id: values.product_category_id as number, + category: values.category as string, fcr_id: values.fcr_id as number, location_id: values.location_id as number, period: values.period as number, @@ -526,18 +533,15 @@ const ProjectFlockForm = ({ /> { - optionChangeHandler(val, 'product_category'); - }} - options={optionsProductCategory} - isLoading={isLoadingProductCategories} + label='Kategori' + value={formik.values.category_option as OptionType} + onChange={categoryChangeHandler} + options={FLOCK_CATEGORY_OPTIONS} isError={ - formik.touched.product_category && - Boolean(formik.errors.product_category) + formik.touched.category && + Boolean(formik.errors.category) } - errorMessage={formik.errors.product_category as string} + errorMessage={formik.errors.category as string} isClearable isDisabled={formType === 'detail'} /> @@ -554,6 +558,7 @@ const ProjectFlockForm = ({ } errorMessage={formik.errors.period as string} readOnly={formType === 'detail'} + disabled={true} /> @@ -592,19 +597,24 @@ const ProjectFlockForm = ({ type='checkbox' checked={ optionsKandang - .filter((k) => k.status === 'NON_ACTIVE') + .filter((k) => (k.status === 'NON_ACTIVE' || formik.values.kandang_ids.includes(k.id))) .every((k) => formik.values.kandang_ids.includes(k.id) ) && optionsKandang.filter( - (k) => k.status === 'NON_ACTIVE' + (k) => (k.status === 'NON_ACTIVE' || formik.values.kandang_ids.includes(k.id)) ).length > 0 } className='checkbox' + disabled={formType === 'detail' || optionsKandang.filter( + (k) => (k.status === 'NON_ACTIVE') + ).length == 0} onChange={ formType === 'detail' ? () => {} : kandangCheckAll + + } /> @@ -635,7 +645,7 @@ const ProjectFlockForm = ({ } disabled={ formType === 'detail' || - kandang.status != 'NON_ACTIVE' + (kandang.status != 'NON_ACTIVE') } /> diff --git a/src/config/constant.ts b/src/config/constant.ts index ed4386a9..053a50cc 100644 --- a/src/config/constant.ts +++ b/src/config/constant.ts @@ -189,6 +189,17 @@ export const CATEGORY_OPTIONS = [ }, ]; +export const FLOCK_CATEGORY_OPTIONS = [ + { + label: 'GROWING', + value: 'GROWING', + }, + { + label: 'LAYING', + value: 'LAYING', + }, +]; + export const PRODUCT_FLAG_OPTIONS = [ { label: 'DOC', value: 'DOC' }, { label: 'PAKAN', value: 'PAKAN' }, diff --git a/src/types/api/production/project-flock.d.ts b/src/types/api/production/project-flock.d.ts index caaf1844..07eb5082 100644 --- a/src/types/api/production/project-flock.d.ts +++ b/src/types/api/production/project-flock.d.ts @@ -12,8 +12,7 @@ export type BaseProjectFlock = { flock_id: number; area: Area; area_id: number; - product_category: ProductCategory; - product_category_id: number; + category: string; fcr: Fcr; fcr_id: number; location: Location; @@ -34,7 +33,7 @@ export type ProjectFlock = BaseMetadata & BaseProjectFlock export type CreateProjectFlockPayload = { flock_id: number; area_id: number; - product_category_id: number; + category: string; fcr_id: number; location_id: number; period: number;