mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
feat(FE-316): Add filter modal and query params for Uniformity
This commit is contained in:
@@ -23,6 +23,18 @@ import UniformityTableSkeleton from '@/components/pages/uniformity/skeleton/Unif
|
||||
import RequirePermission from '@/components/helper/RequirePermission';
|
||||
import { useUniformityStore } from '@/stores/uniformity/uniformity.store';
|
||||
import FloatingActionsButton from '@/components/FloatingActionsButton';
|
||||
import Modal from '@/components/Modal';
|
||||
import SelectInput, {
|
||||
OptionType,
|
||||
useSelect,
|
||||
} from '@/components/input/SelectInput';
|
||||
import DateInput from '@/components/input/DateInput';
|
||||
import { LocationApi } from '@/services/api/master-data';
|
||||
import {
|
||||
ProjectFlockApi,
|
||||
ProjectFlockKandangApi,
|
||||
} from '@/services/api/production';
|
||||
import { Kandang } from '@/types/api/master-data/kandang';
|
||||
import {
|
||||
getStatusColor,
|
||||
getStatusIndicatorColor,
|
||||
@@ -170,16 +182,170 @@ const UniformityTable = ({ refresh }: { refresh?: () => void }) => {
|
||||
const singleRejectModal = useModal();
|
||||
const bulkApproveModal = useModal();
|
||||
const bulkRejectModal = useModal();
|
||||
const filterModal = useModal();
|
||||
|
||||
// ===== FILTER STATE =====
|
||||
const [filterLocation, setFilterLocation] = useState<OptionType | null>(null);
|
||||
const [filterProjectFlock, setFilterProjectFlock] =
|
||||
useState<OptionType | null>(null);
|
||||
const [filterKandang, setFilterKandang] = useState<OptionType | null>(null);
|
||||
const [filterStartDate, setFilterStartDate] = useState('');
|
||||
const [filterEndDate, setFilterEndDate] = useState('');
|
||||
const [projectFlockSearchValue, setProjectFlockSearchValue] = useState('');
|
||||
|
||||
const {
|
||||
setInputValue: setFilterLocationInputValue,
|
||||
options: filterLocationOptions,
|
||||
isLoadingOptions: isLoadingFilterLocations,
|
||||
} = useSelect(LocationApi.basePath, 'id', 'name', 'search');
|
||||
|
||||
// ===== FETCH PROJECT FLOCKS DATA FOR FILTER =====
|
||||
const filterProjectFlocksUrl = useMemo(() => {
|
||||
const params = new URLSearchParams({
|
||||
search: projectFlockSearchValue || '',
|
||||
limit: '100',
|
||||
});
|
||||
if (filterLocation) {
|
||||
params.append('location_id', filterLocation.value.toString());
|
||||
}
|
||||
return `${ProjectFlockApi.basePath}?${params.toString()}`;
|
||||
}, [projectFlockSearchValue, filterLocation]);
|
||||
|
||||
const {
|
||||
data: filterProjectFlocksData,
|
||||
isLoading: isLoadingFilterProjectFlocks,
|
||||
} = useSWR(filterProjectFlocksUrl, ProjectFlockApi.getAllFetcher);
|
||||
|
||||
const filterProjectFlocksDataList = useMemo(
|
||||
() =>
|
||||
isResponseSuccess(filterProjectFlocksData)
|
||||
? filterProjectFlocksData.data
|
||||
: undefined,
|
||||
[filterProjectFlocksData]
|
||||
);
|
||||
|
||||
const filterProjectFlockOptions = useMemo(() => {
|
||||
let options: OptionType[] = [];
|
||||
|
||||
if (isResponseSuccess(filterProjectFlocksData)) {
|
||||
const flockOptions =
|
||||
filterProjectFlocksData?.data.map((projectFlock) => ({
|
||||
value: projectFlock.id,
|
||||
label: projectFlock.flock_name || '',
|
||||
})) || [];
|
||||
options = options.concat(flockOptions);
|
||||
}
|
||||
|
||||
return options;
|
||||
}, [filterProjectFlocksData]);
|
||||
|
||||
// ===== KANDANG OPTIONS FOR FILTER =====
|
||||
const filterKandangOptions = useMemo(() => {
|
||||
let options: OptionType[] = [];
|
||||
|
||||
if (filterProjectFlock && filterProjectFlocksDataList) {
|
||||
const selectedProjectFlockData = filterProjectFlocksDataList.find(
|
||||
(pf) => pf.id === filterProjectFlock.value
|
||||
);
|
||||
|
||||
if (selectedProjectFlockData?.kandangs) {
|
||||
const kandangOpts = selectedProjectFlockData.kandangs.map(
|
||||
(kandang: Kandang) => ({
|
||||
value: kandang.id,
|
||||
label: kandang.name || '',
|
||||
})
|
||||
);
|
||||
options = options.concat(kandangOpts);
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}, [filterProjectFlock, filterProjectFlocksDataList]);
|
||||
|
||||
// ===== BUILD SWR KEY WITH FILTERS =====
|
||||
const uniformitySwrKey = useMemo(() => {
|
||||
const basePath = UniformityApi.basePath;
|
||||
const queryParams = new URLSearchParams();
|
||||
|
||||
if (filterLocation) {
|
||||
queryParams.append('location_id', filterLocation.value.toString());
|
||||
}
|
||||
if (filterProjectFlock) {
|
||||
queryParams.append(
|
||||
'project_flock_id',
|
||||
filterProjectFlock.value.toString()
|
||||
);
|
||||
}
|
||||
if (filterKandang) {
|
||||
queryParams.append('kandang_id', filterKandang.value.toString());
|
||||
}
|
||||
if (filterStartDate) {
|
||||
queryParams.append('start_date', filterStartDate);
|
||||
}
|
||||
if (filterEndDate) {
|
||||
queryParams.append('end_date', filterEndDate);
|
||||
}
|
||||
|
||||
const tableQueryString = getTableFilterQueryString();
|
||||
const tableParams = new URLSearchParams(
|
||||
tableQueryString.split('?')[1] || ''
|
||||
);
|
||||
|
||||
tableParams.forEach((value, key) => {
|
||||
queryParams.append(key, value);
|
||||
});
|
||||
|
||||
const queryString = queryParams.toString();
|
||||
return queryString ? `${basePath}?${queryString}` : basePath;
|
||||
}, [
|
||||
filterLocation,
|
||||
filterProjectFlock,
|
||||
filterKandang,
|
||||
filterStartDate,
|
||||
filterEndDate,
|
||||
getTableFilterQueryString,
|
||||
]);
|
||||
|
||||
const {
|
||||
data: uniformities,
|
||||
isLoading,
|
||||
mutate: refreshUniformities,
|
||||
} = useSWR(
|
||||
`${UniformityApi.basePath}${getTableFilterQueryString()}`,
|
||||
UniformityApi.getAllFetcher
|
||||
} = useSWR(uniformitySwrKey, UniformityApi.getAllFetcher);
|
||||
|
||||
// ===== FILTER HANDLERS =====
|
||||
const handleFilterLocationChange = useCallback(
|
||||
(val: OptionType | OptionType[] | null) => {
|
||||
setFilterLocation(val as OptionType | null);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const handleFilterProjectFlockChange = useCallback(
|
||||
(val: OptionType | OptionType[] | null) => {
|
||||
setFilterProjectFlock(val as OptionType | null);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const handleFilterKandangChange = useCallback(
|
||||
(val: OptionType | OptionType[] | null) => {
|
||||
setFilterKandang(val as OptionType | null);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const handleResetFilters = useCallback(() => {
|
||||
setFilterLocation(null);
|
||||
setFilterProjectFlock(null);
|
||||
setFilterKandang(null);
|
||||
setFilterStartDate('');
|
||||
setFilterEndDate('');
|
||||
}, []);
|
||||
|
||||
const handleApplyFilters = useCallback(() => {
|
||||
filterModal.closeModal();
|
||||
}, [filterModal]);
|
||||
|
||||
const selectedRowIds = useMemo(() => {
|
||||
return Object.keys(rowSelection)
|
||||
.filter((key) => rowSelection[key])
|
||||
@@ -485,7 +651,7 @@ const UniformityTable = ({ refresh }: { refresh?: () => void }) => {
|
||||
</div>
|
||||
|
||||
<div className='sm:flex gap-2'>
|
||||
<Button variant='outline'>
|
||||
<Button variant='outline' onClick={filterModal.openModal}>
|
||||
<Icon icon='heroicons:funnel' width={18} height={18} />
|
||||
Filter
|
||||
</Button>
|
||||
@@ -683,6 +849,97 @@ const UniformityTable = ({ refresh }: { refresh?: () => void }) => {
|
||||
</div>
|
||||
</ConfirmationModal>
|
||||
|
||||
{/* Filter Modal */}
|
||||
<Modal
|
||||
ref={filterModal.ref}
|
||||
className={{ modalBox: 'rounded-2xl max-w-2xl' }}
|
||||
>
|
||||
<div className='flex flex-col gap-6'>
|
||||
<div className='flex items-center justify-between'>
|
||||
<h3 className='text-lg font-semibold'>Filter Data</h3>
|
||||
<button
|
||||
onClick={filterModal.closeModal}
|
||||
className='p-1 hover:bg-gray-100 rounded-lg transition-colors'
|
||||
>
|
||||
<Icon icon='mdi:close' width={20} height={20} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className='flex flex-col gap-4'>
|
||||
<div className='grid grid-cols-2 gap-4'>
|
||||
<DateInput
|
||||
label='Tanggal Mulai'
|
||||
name='start_date'
|
||||
value={filterStartDate}
|
||||
onChange={(e) => setFilterStartDate(e.target.value)}
|
||||
className={{ wrapper: 'w-full' }}
|
||||
/>
|
||||
|
||||
<DateInput
|
||||
label='Tanggal Akhir'
|
||||
name='end_date'
|
||||
value={filterEndDate}
|
||||
onChange={(e) => setFilterEndDate(e.target.value)}
|
||||
className={{ wrapper: 'w-full' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<SelectInput
|
||||
label='Lokasi'
|
||||
placeholder='Pilih Lokasi...'
|
||||
value={filterLocation}
|
||||
onChange={handleFilterLocationChange}
|
||||
options={filterLocationOptions}
|
||||
onInputChange={setFilterLocationInputValue}
|
||||
isLoading={isLoadingFilterLocations}
|
||||
isClearable
|
||||
className={{ wrapper: 'w-full' }}
|
||||
/>
|
||||
|
||||
<SelectInput
|
||||
label='Project Flock'
|
||||
placeholder='Pilih Project Flock...'
|
||||
value={filterProjectFlock}
|
||||
onChange={handleFilterProjectFlockChange}
|
||||
options={filterProjectFlockOptions}
|
||||
onInputChange={setProjectFlockSearchValue}
|
||||
isLoading={isLoadingFilterProjectFlocks}
|
||||
isDisabled={!filterLocation}
|
||||
isClearable
|
||||
className={{ wrapper: 'w-full' }}
|
||||
/>
|
||||
|
||||
<SelectInput
|
||||
label='Kandang'
|
||||
placeholder='Pilih Kandang...'
|
||||
value={filterKandang}
|
||||
onChange={handleFilterKandangChange}
|
||||
options={filterKandangOptions}
|
||||
isDisabled={!filterProjectFlock}
|
||||
isClearable
|
||||
className={{ wrapper: 'w-full' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-2'>
|
||||
<Button
|
||||
variant='outline'
|
||||
className='grow'
|
||||
onClick={handleResetFilters}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
<Button
|
||||
color='primary'
|
||||
className='grow'
|
||||
onClick={handleApplyFilters}
|
||||
>
|
||||
Terapkan
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
{/* Floating Actions Button */}
|
||||
<FloatingActionsButton
|
||||
actions={[
|
||||
|
||||
@@ -17,8 +17,25 @@ export class UniformityApiService extends BaseApiService<
|
||||
super(basePath);
|
||||
}
|
||||
|
||||
async getUniformity(): Promise<BaseApiResponse<Uniformity> | undefined> {
|
||||
return await this.customRequest<BaseApiResponse<Uniformity>>('');
|
||||
async getUniformity(
|
||||
location_id?: number,
|
||||
project_flock_id?: number,
|
||||
kandang_id?: number,
|
||||
project_flock_kandang_id?: number,
|
||||
start_date?: string,
|
||||
end_date?: string
|
||||
): Promise<BaseApiResponse<Uniformity> | undefined> {
|
||||
return await this.customRequest<BaseApiResponse<Uniformity>>('', {
|
||||
method: 'GET',
|
||||
params: {
|
||||
location_id: location_id,
|
||||
project_flock_id: project_flock_id,
|
||||
kandang_id: kandang_id,
|
||||
project_flock_kandang_id: project_flock_kandang_id,
|
||||
start_date: start_date,
|
||||
end_date: end_date,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async getUniformityDetail(
|
||||
|
||||
Reference in New Issue
Block a user