diff --git a/src/app/master-data/fcr/add/page.tsx b/src/app/master-data/fcr/add/page.tsx deleted file mode 100644 index 9a74034d..00000000 --- a/src/app/master-data/fcr/add/page.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import FcrForm from '@/components/pages/master-data/fcr/form/FcrForm'; - -const AddFcr = () => { - return ( -
- -
- ); -}; - -export default AddFcr; diff --git a/src/app/master-data/fcr/detail/edit/page.tsx b/src/app/master-data/fcr/detail/edit/page.tsx deleted file mode 100644 index 54277e8a..00000000 --- a/src/app/master-data/fcr/detail/edit/page.tsx +++ /dev/null @@ -1,52 +0,0 @@ -'use client'; - -import { useRouter, useSearchParams } from 'next/navigation'; -import useSWR from 'swr'; - -import FcrForm from '@/components/pages/master-data/fcr/form/FcrForm'; - -import { FcrApi } from '@/services/api/master-data'; -import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; -import { BaseApiResponse } from '@/types/api/api-general'; -import { FcrWithStandards } from '@/types/api/master-data/fcr'; - -const FcrEdit = () => { - const router = useRouter(); - const searchParams = useSearchParams(); - - const fcrId = searchParams.get('fcrId'); - - const { data: fcr, isLoading: isLoadingFcr } = useSWR( - fcrId, - (id: number) => - FcrApi.getSingle(id) as Promise< - BaseApiResponse | undefined - > - ); - - if (!fcrId) { - router.back(); - - return ( -
- -
- ); - } - - if (!isLoadingFcr && (!fcr || isResponseError(fcr))) { - router.replace('/404'); - return; - } - - return ( -
- {isLoadingFcr && } - {!isLoadingFcr && isResponseSuccess(fcr) && ( - - )} -
- ); -}; - -export default FcrEdit; diff --git a/src/app/master-data/fcr/detail/layout.tsx b/src/app/master-data/fcr/detail/layout.tsx deleted file mode 100644 index 7220dfa1..00000000 --- a/src/app/master-data/fcr/detail/layout.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import SuspenseHelper from '@/components/helper/SuspenseHelper'; - -const Layout = ({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) => { - return {children}; -}; - -export default Layout; diff --git a/src/app/master-data/fcr/detail/page.tsx b/src/app/master-data/fcr/detail/page.tsx deleted file mode 100644 index 5db1ab32..00000000 --- a/src/app/master-data/fcr/detail/page.tsx +++ /dev/null @@ -1,52 +0,0 @@ -'use client'; - -import { useRouter, useSearchParams } from 'next/navigation'; -import useSWR from 'swr'; - -import FcrForm from '@/components/pages/master-data/fcr/form/FcrForm'; - -import { FcrApi } from '@/services/api/master-data'; -import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; -import { FcrWithStandards } from '@/types/api/master-data/fcr'; -import { BaseApiResponse } from '@/types/api/api-general'; - -const FcrDetail = () => { - const router = useRouter(); - const searchParams = useSearchParams(); - - const fcrId = searchParams.get('fcrId'); - - const { data: fcr, isLoading: isLoadingFcr } = useSWR( - fcrId, - (id: number) => - FcrApi.getSingle(id) as Promise< - BaseApiResponse | undefined - > - ); - - if (!fcrId) { - router.back(); - - return ( -
- -
- ); - } - - if (!isLoadingFcr && (!fcr || isResponseError(fcr))) { - router.replace('/404'); - return; - } - - return ( -
- {isLoadingFcr && } - {!isLoadingFcr && isResponseSuccess(fcr) && ( - - )} -
- ); -}; - -export default FcrDetail; diff --git a/src/app/master-data/fcr/page.tsx b/src/app/master-data/fcr/page.tsx deleted file mode 100644 index 9ca9c55d..00000000 --- a/src/app/master-data/fcr/page.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import FcrsTable from '@/components/pages/master-data/fcr/FcrsTable'; - -const Fcr = () => { - return ( -
- -
- ); -}; - -export default Fcr; diff --git a/src/components/pages/master-data/fcr/FcrsTable.tsx b/src/components/pages/master-data/fcr/FcrsTable.tsx deleted file mode 100644 index 2eb8d8da..00000000 --- a/src/components/pages/master-data/fcr/FcrsTable.tsx +++ /dev/null @@ -1,289 +0,0 @@ -'use client'; - -import { ChangeEventHandler, useEffect, useState } from 'react'; -import useSWR from 'swr'; -import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table'; -import toast from 'react-hot-toast'; - -import { Icon } from '@iconify/react'; -import Table from '@/components/Table'; -import DebouncedTextInput from '@/components/input/DebouncedTextInput'; -import Button from '@/components/Button'; -import { useModal } from '@/components/Modal'; -import ConfirmationModal from '@/components/modal/ConfirmationModal'; -import SelectInput, { OptionType } from '@/components/input/SelectInput'; -import RowDropdownOptions from '@/components/table/RowDropdownOptions'; -import RowCollapseOptions from '@/components/table/RowCollapseOptions'; -import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper'; -import RequirePermission from '@/components/helper/RequirePermission'; - -import { Fcr } from '@/types/api/master-data/fcr'; -import { FcrApi } from '@/services/api/master-data'; -import { cn } from '@/lib/helper'; -import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; -import { useTableFilter } from '@/services/hooks/useTableFilter'; -import { ROWS_OPTIONS } from '@/config/constant'; - -const RowOptionsMenu = ({ - type = 'dropdown', - props, - deleteClickHandler, -}: { - type: 'dropdown' | 'collapse'; - props: CellContext; - deleteClickHandler: () => void; -}) => { - return ( - - - - - - - - - - - - - - ); -}; - -const FcrsTable = () => { - const { - state: tableFilterState, - updateFilter, - setPage, - setPageSize, - toQueryString: getTableFilterQueryString, - } = useTableFilter({ - initial: { search: '', nameSort: '' }, - paramMap: { page: 'page', pageSize: 'limit', nameSort: 'sort_name' }, - }); - - const { - data: fcrs, - isLoading, - mutate: refreshFcrs, - } = useSWR( - `${FcrApi.basePath}${getTableFilterQueryString()}`, - FcrApi.getAllFetcher - ); - - const deleteModal = useModal(); - - const [selectedFcr, setSelectedFcr] = useState(undefined); - const [isDeleteLoading, setIsDeleteLoading] = useState(false); - - const [sorting, setSorting] = useState([]); - - const fcrsColumns: ColumnDef[] = [ - { - header: '#', - cell: (props) => - tableFilterState.pageSize * (tableFilterState.page - 1) + - props.row.index + - 1, - }, - { - accessorKey: 'name', - header: 'Nama', - }, - { - header: 'Aksi', - cell: (props) => { - const currentPageSize = props.table.getPaginationRowModel().rows.length; - const currentPageRows = props.table.getPaginationRowModel().flatRows; - const currentRowRelativeIndex = - currentPageRows.findIndex((r) => r.id === props.row.id) + 1; - - const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; - - const deleteClickHandler = () => { - setSelectedFcr(props.row.original); - deleteModal.openModal(); - }; - - return ( - <> - {currentPageSize > 2 && ( - - - - )} - - {currentPageSize <= 2 && ( - - - - )} - - ); - }, - }, - ]; - - const confirmationModalDeleteClickHandler = async () => { - setIsDeleteLoading(true); - - const deleteResponse = await FcrApi.delete(selectedFcr?.id as number); - - if (isResponseError(deleteResponse)) { - toast.error(deleteResponse.message); - setIsDeleteLoading(false); - return; - } - - refreshFcrs(); - - deleteModal.closeModal(); - toast.success('Successfully delete FCR!'); - setIsDeleteLoading(false); - }; - - const searchChangeHandler: ChangeEventHandler = (e) => { - updateFilter('search', e.target.value); - }; - - const pageSizeChangeHandler = (val: OptionType | OptionType[] | null) => { - const newVal = val as OptionType; - - setPageSize(newVal.value as number); - }; - - // track sorting - useEffect(() => { - const isNameSorted = sorting.find((sortItem) => sortItem.id === 'name'); - - if (!isNameSorted) { - updateFilter('nameSort', ''); - } else { - updateFilter('nameSort', isNameSorted.desc ? 'desc' : 'asc'); - } - }, [sorting]); - - return ( - <> -
-
-
-
- - - -
- - -
- -
- -
-
- - - data={isResponseSuccess(fcrs) ? fcrs?.data : []} - columns={fcrsColumns} - pageSize={tableFilterState.pageSize} - page={isResponseSuccess(fcrs) ? fcrs?.meta?.page : 0} - totalItems={isResponseSuccess(fcrs) ? fcrs?.meta?.total_results : 0} - onPageChange={setPage} - isLoading={isLoading} - sorting={sorting} - setSorting={setSorting} - className={{ - containerClassName: cn({ - 'mb-20': isResponseSuccess(fcrs) && fcrs?.data?.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', - }} - /> -
- - - - ); -}; - -export default FcrsTable; diff --git a/src/components/pages/master-data/fcr/form/FcrForm.schema.ts b/src/components/pages/master-data/fcr/form/FcrForm.schema.ts deleted file mode 100644 index 21b0b9ee..00000000 --- a/src/components/pages/master-data/fcr/form/FcrForm.schema.ts +++ /dev/null @@ -1,26 +0,0 @@ -import * as Yup from 'yup'; - -const FcrStandardSchema: Yup.ObjectSchema<{ - weight: number | string; - fcr_number: number | string; - mortality: number | string; -}> = Yup.object({ - weight: Yup.number().nullable().required('Bobot wajib diisi!'), - fcr_number: Yup.number() - .nullable() - .typeError('FCR harus angka!') - .required('FCR harus diisi!'), - mortality: Yup.number().nullable().required('Mortalitas wajib diisi!'), -}); - -export const FcrFormSchema = Yup.object({ - name: Yup.string().required('Nama wajib diisi!'), - fcrStandards: Yup.array() - .of(FcrStandardSchema) - .min(1, 'Minimal 1 FCR Standard diisi1') - .required('FCR wajib diisi!'), -}); - -export const UpdateFcrFormSchema = FcrFormSchema; - -export type FcrFormValues = Yup.InferType; diff --git a/src/components/pages/master-data/fcr/form/FcrForm.tsx b/src/components/pages/master-data/fcr/form/FcrForm.tsx deleted file mode 100644 index 807e7e45..00000000 --- a/src/components/pages/master-data/fcr/form/FcrForm.tsx +++ /dev/null @@ -1,401 +0,0 @@ -'use client'; - -import { useCallback, useEffect, useMemo, useState } from 'react'; -import { useRouter } from 'next/navigation'; -import { useFormik } from 'formik'; -import { toast } from 'react-hot-toast'; - -import { Icon } from '@iconify/react'; -import Button from '@/components/Button'; -import TextInput from '@/components/input/TextInput'; -import { useModal } from '@/components/Modal'; -import ConfirmationModal from '@/components/modal/ConfirmationModal'; -import RequirePermission from '@/components/helper/RequirePermission'; - -import { - FcrFormSchema, - FcrFormValues, - UpdateFcrFormSchema, -} from '@/components/pages/master-data/fcr/form/FcrForm.schema'; -import { isResponseError } from '@/lib/api-helper'; -import { - CreateFcrPayload, - Fcr, - FcrWithStandards, - UpdateFcrPayload, -} from '@/types/api/master-data/fcr'; -import { FcrApi } from '@/services/api/master-data'; -import { cn } from '@/lib/helper'; -import AlertErrorList from '@/components/helper/form/FormErrors'; -import { useFormikErrorList } from '@/services/hooks/useFormikErrorList'; - -interface FcrFormProps { - type?: 'add' | 'edit' | 'detail'; - initialValues?: FcrWithStandards; -} - -const FcrForm = ({ type = 'add', initialValues }: FcrFormProps) => { - const router = useRouter(); - const deleteModal = useModal(); - - const [fcrFormErrorMessage, setFcrFormErrorMessage] = useState(''); - const [isDeleteLoading, setIsDeleteLoading] = useState(false); - - const createFcrHandler = useCallback( - async (payload: CreateFcrPayload) => { - const createFcrRes = await FcrApi.create(payload); - - if (isResponseError(createFcrRes)) { - setFcrFormErrorMessage(createFcrRes.message); - return; - } - - toast.success(createFcrRes?.message as string); - router.push('/master-data/fcr'); - }, - [router] - ); - - const updateFcrHandler = useCallback( - async (fcrId: number, payload: UpdateFcrPayload) => { - const updateFcrRes = await FcrApi.update(fcrId, payload); - - if (updateFcrRes?.status === 'error') { - setFcrFormErrorMessage(updateFcrRes.message); - return; - } - - toast.success(updateFcrRes?.message as string); - router.refresh(); - router.push('/master-data/fcr'); - }, - [router] - ); - - const formikInitialValues = useMemo(() => { - return { - name: initialValues?.name ?? '', - fcrStandards: initialValues?.fcr_standards - ? initialValues?.fcr_standards - : [ - { - weight: '', - fcr_number: '', - mortality: '', - }, - ], - }; - }, [initialValues]); - - const formik = useFormik({ - initialValues: formikInitialValues, - validationSchema: type === 'edit' ? UpdateFcrFormSchema : FcrFormSchema, - onSubmit: async (values) => { - setFcrFormErrorMessage(''); - - const fcrPayload: CreateFcrPayload = { - name: values.name, - fcr_standards: values.fcrStandards as CreateFcrPayload['fcr_standards'], - }; - - switch (type) { - case 'add': - await createFcrHandler(fcrPayload); - break; - - case 'edit': - await updateFcrHandler(initialValues?.id as number, fcrPayload); - break; - } - }, - }); - - const { setValues: formikSetValues } = formik; - - const addFcrStandard = () => - formik.setFieldValue('fcrStandards', [ - ...formik.values.fcrStandards, - { - weight: '', - fcr_number: '', - mortality: '', - }, - ]); - - const removeFcrStandard = (i: number) => - formik.setFieldValue( - 'fcrStandards', - formik.values.fcrStandards.filter((_, idx) => idx !== i) - ); - - const deleteFcrClickHandler = () => { - deleteModal.openModal(); - }; - - const confirmationModalDeleteClickHandler = async () => { - setIsDeleteLoading(true); - - await FcrApi.delete(initialValues?.id as number); - - deleteModal.closeModal(); - toast.success('Successfully delete FCR!'); - setIsDeleteLoading(false); - router.push('/master-data/fcr'); - }; - - const isRepeaterInputError = ( - column: keyof CreateFcrPayload['fcr_standards'][0], - idx: number - ) => { - return ( - formik.touched.fcrStandards?.[idx]?.[column] && - Boolean( - formik.errors.fcrStandards?.[idx] instanceof Object && - formik.errors.fcrStandards?.[idx]?.[column] - ) - ); - }; - - useEffect(() => { - formikSetValues(formikInitialValues); - }, [formikSetValues, formikInitialValues]); - - // ===== Formik Error List ===== - const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik); - - return ( - <> -
-
- - -

- {type === 'add' && 'Tambah FCR'} - {type === 'edit' && 'Edit FCR'} - {type === 'detail' && 'Detail FCR'} -

-
- -
-
- - -
-
- - - - - - - {type !== 'detail' && } - - - - - {formik.values.fcrStandards.map((fcrStandard, idx) => ( - - - - - {type !== 'detail' && ( - - )} - - ))} - -
BobotFCRMortalitasAksi
- - - - - - - -
-
-
- - {type !== 'detail' && ( - - )} -
- - - -
- {type !== 'add' && ( -
- - - - - {type !== 'edit' && ( - - - - )} -
- )} - - {type !== 'detail' && ( -
- - - -
- )} -
- - {fcrFormErrorMessage && ( -
- - {fcrFormErrorMessage} -
- )} - -
- - {type !== 'add' && ( - - )} - - ); -}; - -export default FcrForm; diff --git a/src/config/constant.ts b/src/config/constant.ts index fc89763b..66d0af7d 100644 --- a/src/config/constant.ts +++ b/src/config/constant.ts @@ -220,7 +220,6 @@ export const MAIN_DRAWER_LINKS: SidebarMenuItem[] = [ 'lti.master.area.list', 'lti.master.banks.list', 'lti.master.customer.list', - 'lti.master.fcr.list', 'lti.master.flocks.list', 'lti.master.kandangs.list', 'lti.master.locations.list', @@ -283,11 +282,6 @@ export const MAIN_DRAWER_LINKS: SidebarMenuItem[] = [ link: '/master-data/nonstock', permission: ['lti.master.nonstocks.list'], }, - { - text: 'FCR', - link: '/master-data/fcr', - permission: ['lti.master.fcr.list'], - }, { text: 'Supplier', link: '/master-data/supplier', diff --git a/src/config/route-permission.ts b/src/config/route-permission.ts index 20ee5292..dc638b29 100644 --- a/src/config/route-permission.ts +++ b/src/config/route-permission.ts @@ -195,11 +195,6 @@ export const ROUTE_PERMISSIONS: Record = { '/master-data/nonstock/detail/': ['lti.master.nonstocks.detail'], '/master-data/nonstock/detail/edit/': ['lti.master.nonstocks.update'], - '/master-data/fcr/': ['lti.master.fcr.list'], - '/master-data/fcr/add/': ['lti.master.fcr.create'], - '/master-data/fcr/detail/': ['lti.master.fcr.detail'], - '/master-data/fcr/detail/edit/': ['lti.master.fcr.update'], - '/master-data/supplier/': ['lti.master.suppliers.list'], '/master-data/supplier/add/': ['lti.master.suppliers.create'], '/master-data/supplier/detail/': ['lti.master.suppliers.detail'], diff --git a/src/services/api/master-data.ts b/src/services/api/master-data.ts index f15de21d..ea33ba70 100644 --- a/src/services/api/master-data.ts +++ b/src/services/api/master-data.ts @@ -54,11 +54,7 @@ import { CreateBankPayload, UpdateBankPayload, } from '@/types/api/master-data/bank'; -import { - CreateFcrPayload, - Fcr, - UpdateFcrPayload, -} from '@/types/api/master-data/fcr'; + import { CreateFlockPayload, Flock, @@ -131,12 +127,6 @@ export const BankApi = new BaseApiService< UpdateBankPayload >('/master-data/banks'); -export const FcrApi = new BaseApiService< - Fcr, - CreateFcrPayload, - UpdateFcrPayload ->('/master-data/fcrs'); - export const FlockApi = new BaseApiService< Flock, CreateFlockPayload, diff --git a/src/types/api/closing.d.ts b/src/types/api/closing.d.ts index 31a0248d..6bf8c7c6 100644 --- a/src/types/api/closing.d.ts +++ b/src/types/api/closing.d.ts @@ -1,5 +1,4 @@ import { Area } from '@/types/api/master-data/area'; -import { Fcr } from '@/types/api/master-data/fcr'; import { Flock } from '@/types/api/master-data/flock'; import { Location } from '@/types/api/master-data/location'; import { Kandang } from '@/types/api/master-data/kandang'; diff --git a/src/types/api/master-data/fcr.d.ts b/src/types/api/master-data/fcr.d.ts deleted file mode 100644 index 45ad25e5..00000000 --- a/src/types/api/master-data/fcr.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { BaseMetadata } from '@/types/api/api-general'; - -export type BaseFcr = { - id: number; - name: string; -}; - -export type FcrStandard = { - id: number; - weight: number; - fcr_number: number; - mortality: number; -}; - -export type Fcr = BaseMetadata & BaseFcr; - -export type FcrWithStandards = Fcr & { - fcr_standards: FcrStandard[]; -}; - -export type CreateFcrPayload = { - name: string; - fcr_standards: { - weight: number; - fcr_number: number; - mortality: number; - }[]; -}; - -export type UpdateFcrPayload = CreateFcrPayload;