mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-25 15:55:48 +00:00
feat: create ReportDepreciationFilterModal component
This commit is contained in:
@@ -0,0 +1,270 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { RefObject, useMemo, useState } from 'react';
|
||||||
|
import { useFormik } from 'formik';
|
||||||
|
import * as yup from 'yup';
|
||||||
|
|
||||||
|
import { Icon } from '@iconify/react';
|
||||||
|
import Modal from '@/components/Modal';
|
||||||
|
import Button from '@/components/Button';
|
||||||
|
import DateInput from '@/components/input/DateInput';
|
||||||
|
import SelectInput, {
|
||||||
|
OptionType,
|
||||||
|
useSelect,
|
||||||
|
} from '@/components/input/SelectInput';
|
||||||
|
|
||||||
|
import { AreaApi, LocationApi } from '@/services/api/master-data';
|
||||||
|
import { ProjectFlockApi } from '@/services/api/production';
|
||||||
|
import { Area } from '@/types/api/master-data/area';
|
||||||
|
import { Location } from '@/types/api/master-data/location';
|
||||||
|
import { ProjectFlock } from '@/types/api/production/project-flock';
|
||||||
|
|
||||||
|
export type ReportDepreciationFilterValues = {
|
||||||
|
area_id: string | null;
|
||||||
|
location_id: string | null;
|
||||||
|
project_flock_id: string | null;
|
||||||
|
period: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ReportDepreciationFilterSchema = yup.object({
|
||||||
|
area_id: yup.string().nullable(),
|
||||||
|
location_id: yup.string().nullable(),
|
||||||
|
project_flock_id: yup.string().nullable(),
|
||||||
|
period: yup.string().nullable().required('Periode wajib dipilih'),
|
||||||
|
}) as yup.ObjectSchema<ReportDepreciationFilterValues>;
|
||||||
|
|
||||||
|
interface ReportDepreciationFilterModalProps {
|
||||||
|
ref: RefObject<HTMLDialogElement | null>;
|
||||||
|
initialValues?: ReportDepreciationFilterValues;
|
||||||
|
onSubmit?: (values: Partial<ReportDepreciationFilterValues>) => void;
|
||||||
|
onReset?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultInitialValues: ReportDepreciationFilterValues = {
|
||||||
|
area_id: null,
|
||||||
|
location_id: null,
|
||||||
|
project_flock_id: null,
|
||||||
|
period: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const ReportDepreciationFilterModal = ({
|
||||||
|
ref,
|
||||||
|
initialValues,
|
||||||
|
onSubmit,
|
||||||
|
onReset,
|
||||||
|
}: ReportDepreciationFilterModalProps) => {
|
||||||
|
const [selectedAreaId, setSelectedAreaId] = useState<string | undefined>(
|
||||||
|
initialValues?.area_id || undefined
|
||||||
|
);
|
||||||
|
const [selectedLocationId, setSelectedLocationId] = useState<
|
||||||
|
string | undefined
|
||||||
|
>(initialValues?.location_id || undefined);
|
||||||
|
|
||||||
|
const closeModalHandler = () => {
|
||||||
|
ref.current?.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
const {
|
||||||
|
setInputValue: setAreaInputValue,
|
||||||
|
options: areaOptions,
|
||||||
|
isLoadingOptions: isLoadingAreaOptions,
|
||||||
|
loadMore: loadMoreAreas,
|
||||||
|
} = useSelect<Area>(AreaApi.basePath, 'id', 'name', 'search');
|
||||||
|
|
||||||
|
const {
|
||||||
|
setInputValue: setLocationInputValue,
|
||||||
|
options: locationOptions,
|
||||||
|
isLoadingOptions: isLoadingLocationOptions,
|
||||||
|
loadMore: loadMoreLocations,
|
||||||
|
} = useSelect<Location>(LocationApi.basePath, 'id', 'name', 'search', {
|
||||||
|
area_id: selectedAreaId || '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
setInputValue: setProjectFlockInputValue,
|
||||||
|
options: projectFlockOptions,
|
||||||
|
isLoadingOptions: isLoadingProjectFlockOptions,
|
||||||
|
loadMore: loadMoreProjectFlocks,
|
||||||
|
} = useSelect<ProjectFlock>(
|
||||||
|
ProjectFlockApi.basePath,
|
||||||
|
'id',
|
||||||
|
'flock_name',
|
||||||
|
'search',
|
||||||
|
{
|
||||||
|
location_id: selectedLocationId || '',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const formik = useFormik<ReportDepreciationFilterValues>({
|
||||||
|
initialValues: initialValues || defaultInitialValues,
|
||||||
|
validationSchema: ReportDepreciationFilterSchema,
|
||||||
|
onSubmit: async (values) => {
|
||||||
|
onSubmit?.(values);
|
||||||
|
closeModalHandler();
|
||||||
|
},
|
||||||
|
onReset: (_) => {
|
||||||
|
onReset?.();
|
||||||
|
closeModalHandler();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const areaValue = useMemo(() => {
|
||||||
|
if (!formik.values.area_id) return null;
|
||||||
|
return (
|
||||||
|
areaOptions.find((opt) => String(opt.value) === formik.values.area_id) ||
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}, [formik.values.area_id, areaOptions]);
|
||||||
|
|
||||||
|
const locationValue = useMemo(() => {
|
||||||
|
if (!formik.values.location_id) return null;
|
||||||
|
return (
|
||||||
|
locationOptions.find(
|
||||||
|
(opt) => String(opt.value) === formik.values.location_id
|
||||||
|
) || null
|
||||||
|
);
|
||||||
|
}, [formik.values.location_id, locationOptions]);
|
||||||
|
|
||||||
|
const projectFlockValue = useMemo(() => {
|
||||||
|
if (!formik.values.project_flock_id) return null;
|
||||||
|
return (
|
||||||
|
projectFlockOptions.find(
|
||||||
|
(opt) => String(opt.value) === formik.values.project_flock_id
|
||||||
|
) || null
|
||||||
|
);
|
||||||
|
}, [formik.values.project_flock_id, projectFlockOptions]);
|
||||||
|
|
||||||
|
const areaChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||||
|
const areaId = val && !Array.isArray(val) ? String(val.value) : null;
|
||||||
|
|
||||||
|
setSelectedAreaId(areaId || undefined);
|
||||||
|
formik.setFieldValue('area_id', areaId);
|
||||||
|
formik.setFieldValue('location_id', null);
|
||||||
|
formik.setFieldValue('project_flock_id', null);
|
||||||
|
setSelectedLocationId(undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
const locationChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||||
|
const locationId = val && !Array.isArray(val) ? String(val.value) : null;
|
||||||
|
|
||||||
|
setSelectedLocationId(locationId || undefined);
|
||||||
|
formik.setFieldValue('location_id', locationId);
|
||||||
|
formik.setFieldValue('project_flock_id', null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const projectFlockChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||||
|
const projectFlockId =
|
||||||
|
val && !Array.isArray(val) ? String(val.value) : null;
|
||||||
|
|
||||||
|
formik.setFieldValue('project_flock_id', projectFlockId);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
ref={ref}
|
||||||
|
className={{
|
||||||
|
modalBox: 'p-0 rounded-xl xl:max-w-4/12 max-w-sm',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<form
|
||||||
|
onSubmit={formik.handleSubmit}
|
||||||
|
onReset={formik.handleReset}
|
||||||
|
className='w-full flex flex-col'
|
||||||
|
>
|
||||||
|
<div className='p-4 flex items-center justify-between gap-2 border-b border-base-content/10'>
|
||||||
|
<div className='flex items-center gap-2 text-primary'>
|
||||||
|
<Icon icon='heroicons:funnel' width={20} height={20} />
|
||||||
|
<h3 className='text-sm font-medium'>Filter Data</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
type='button'
|
||||||
|
variant='ghost'
|
||||||
|
color='none'
|
||||||
|
onClick={closeModalHandler}
|
||||||
|
className='p-0 text-base-content/50 hover:text-base-content'
|
||||||
|
>
|
||||||
|
<Icon icon='heroicons:x-mark' width={20} height={20} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='p-4 flex flex-col gap-1.5'>
|
||||||
|
<SelectInput
|
||||||
|
label='Area'
|
||||||
|
placeholder='Pilih Area'
|
||||||
|
options={areaOptions}
|
||||||
|
value={areaValue}
|
||||||
|
onChange={areaChangeHandler}
|
||||||
|
onInputChange={setAreaInputValue}
|
||||||
|
onMenuScrollToBottom={loadMoreAreas}
|
||||||
|
isLoading={isLoadingAreaOptions}
|
||||||
|
isClearable
|
||||||
|
isSearchable={true}
|
||||||
|
className={{ wrapper: 'w-full' }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SelectInput
|
||||||
|
label='Lokasi'
|
||||||
|
placeholder='Pilih Lokasi'
|
||||||
|
options={locationOptions}
|
||||||
|
value={locationValue}
|
||||||
|
onChange={locationChangeHandler}
|
||||||
|
onInputChange={setLocationInputValue}
|
||||||
|
onMenuScrollToBottom={loadMoreLocations}
|
||||||
|
isLoading={isLoadingLocationOptions}
|
||||||
|
isClearable
|
||||||
|
isSearchable={true}
|
||||||
|
className={{ wrapper: 'w-full' }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SelectInput
|
||||||
|
label='Project Flock'
|
||||||
|
placeholder='Pilih Project Flock'
|
||||||
|
options={projectFlockOptions}
|
||||||
|
value={projectFlockValue}
|
||||||
|
onChange={projectFlockChangeHandler}
|
||||||
|
onInputChange={setProjectFlockInputValue}
|
||||||
|
onMenuScrollToBottom={loadMoreProjectFlocks}
|
||||||
|
isLoading={isLoadingProjectFlockOptions}
|
||||||
|
isClearable
|
||||||
|
isSearchable={true}
|
||||||
|
className={{ wrapper: 'w-full' }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DateInput
|
||||||
|
label='Periode'
|
||||||
|
name='period'
|
||||||
|
placeholder='Pilih Periode'
|
||||||
|
value={formik.values.period || ''}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
onBlur={formik.handleBlur}
|
||||||
|
isError={formik.touched.period && !!formik.errors.period}
|
||||||
|
errorMessage={formik.errors.period}
|
||||||
|
required
|
||||||
|
isNestedModal
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='p-4 flex justify-between gap-4 border-t border-base-content/10 bg-gray-50'>
|
||||||
|
<Button
|
||||||
|
type='reset'
|
||||||
|
variant='soft'
|
||||||
|
className='rounded-lg text-base-content/65 bg-transparent border-none hover:bg-base-content/10 hover:text-base-content/65 transition-colors px-3 py-2'
|
||||||
|
>
|
||||||
|
Reset Filter
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
type='submit'
|
||||||
|
className='min-w-40 text-sm rounded-lg py-3 text-white font-semibold'
|
||||||
|
disabled={!formik.isValid || formik.isSubmitting}
|
||||||
|
>
|
||||||
|
Apply Filter
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ReportDepreciationFilterModal;
|
||||||
Reference in New Issue
Block a user