mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-25 15:55:48 +00:00
Merge branch 'development' into 'production'
refactor(FE-load-more-option): Add infinite scroll to location and See merge request mbugroup/lti-web-client!396
This commit is contained in:
@@ -5,12 +5,12 @@ import { Icon } from '@iconify/react';
|
|||||||
import Modal, { useModal } from '@/components/Modal';
|
import Modal, { useModal } from '@/components/Modal';
|
||||||
import DateInput from '@/components/input/DateInput';
|
import DateInput from '@/components/input/DateInput';
|
||||||
import { OptionType, useSelect } from '@/components/input/SelectInput';
|
import { OptionType, useSelect } from '@/components/input/SelectInput';
|
||||||
import { useState, useEffect, useRef, useCallback } from 'react';
|
import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { DashboardApi } from '@/services/api/dashboard';
|
import { DashboardApi } from '@/services/api/dashboard';
|
||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
import { ProjectFlockApi } from '@/services/api/production';
|
import { ProjectFlockApi } from '@/services/api/production';
|
||||||
import { KandangApi, LocationApi } from '@/services/api/master-data';
|
import { LocationApi } from '@/services/api/master-data';
|
||||||
import { generateDashboardPDF } from '@/components/pages/dashboard/export/DashboardPDF';
|
import { generateDashboardPDF } from '@/components/pages/dashboard/export/DashboardPDF';
|
||||||
import {
|
import {
|
||||||
DashboardFilterType,
|
DashboardFilterType,
|
||||||
@@ -42,6 +42,7 @@ import { cn } from '@/lib/helper';
|
|||||||
import DashboardExportStats, {
|
import DashboardExportStats, {
|
||||||
DashboardExportStatsRef,
|
DashboardExportStatsRef,
|
||||||
} from '@/components/pages/dashboard/export/DashboardExportStats';
|
} from '@/components/pages/dashboard/export/DashboardExportStats';
|
||||||
|
import { ProjectFlock } from '@/types/api/production/project-flock';
|
||||||
|
|
||||||
// Helper function to normalize values to array
|
// Helper function to normalize values to array
|
||||||
const normalizeToArray = (
|
const normalizeToArray = (
|
||||||
@@ -71,6 +72,7 @@ const DashboardProduction = () => {
|
|||||||
const [selectedLocationIds, setSelectedLocationIds] = useState<number[]>(
|
const [selectedLocationIds, setSelectedLocationIds] = useState<number[]>(
|
||||||
normalizeToArray(filterValues.location)
|
normalizeToArray(filterValues.location)
|
||||||
);
|
);
|
||||||
|
const [kandangInputValue, setRawKandangInputValue] = useState('');
|
||||||
const [exporting, setExporting] = useState(false);
|
const [exporting, setExporting] = useState(false);
|
||||||
const allChartsRef = useRef<DashboardExportChartsRef>(null);
|
const allChartsRef = useRef<DashboardExportChartsRef>(null);
|
||||||
const allStatsRef = useRef<DashboardExportStatsRef>(null);
|
const allStatsRef = useRef<DashboardExportStatsRef>(null);
|
||||||
@@ -114,23 +116,25 @@ const DashboardProduction = () => {
|
|||||||
options: flockOptions,
|
options: flockOptions,
|
||||||
isLoadingOptions: isLoadingFlockOptions,
|
isLoadingOptions: isLoadingFlockOptions,
|
||||||
loadMore: loadMoreFlock,
|
loadMore: loadMoreFlock,
|
||||||
} = useSelect(ProjectFlockApi.basePath, 'id', 'flock_name', '', {
|
rawData: projectFlocksRawData,
|
||||||
location_id: selectedLocationIds ? selectedLocationIds.toString() : '',
|
} = useSelect<ProjectFlock>(
|
||||||
});
|
ProjectFlockApi.basePath,
|
||||||
|
'id',
|
||||||
|
'flock_name',
|
||||||
|
'',
|
||||||
|
{
|
||||||
|
location_id:
|
||||||
|
selectedLocationIds.length > 0 ? selectedLocationIds.toString() : '',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
setInputValue: setInputValueLocation,
|
setInputValue: setInputValueLocation,
|
||||||
options: locationOptions,
|
options: locationOptions,
|
||||||
isLoadingOptions: isLoadingLocationOptions,
|
isLoadingOptions: isLoadingLocationOptions,
|
||||||
loadMore: loadMoreLocation,
|
loadMore: loadMoreLocation,
|
||||||
} = useSelect(LocationApi.basePath, 'id', 'name');
|
} = useSelect(LocationApi.basePath, 'id', 'name');
|
||||||
const {
|
|
||||||
setInputValue: setInputValueKandang,
|
|
||||||
options: kandangOptions,
|
|
||||||
isLoadingOptions: isLoadingKandangOptions,
|
|
||||||
loadMore: loadMoreKandang,
|
|
||||||
} = useSelect(KandangApi.basePath, 'id', 'name', '', {
|
|
||||||
location_id: selectedLocationIds ? selectedLocationIds.toString() : '',
|
|
||||||
});
|
|
||||||
const comparisonTypeOptions = [
|
const comparisonTypeOptions = [
|
||||||
{ value: 'FARM', label: 'Farm' },
|
{ value: 'FARM', label: 'Farm' },
|
||||||
{ value: 'FLOCK', label: 'Flock' },
|
{ value: 'FLOCK', label: 'Flock' },
|
||||||
@@ -161,12 +165,68 @@ const DashboardProduction = () => {
|
|||||||
|
|
||||||
const { resetForm } = formik;
|
const { resetForm } = formik;
|
||||||
|
|
||||||
|
const selectedFlockIds = useMemo(
|
||||||
|
() => normalizeToArray(formik.values.flock),
|
||||||
|
[formik.values.flock]
|
||||||
|
);
|
||||||
|
|
||||||
|
const derivedKandangOptions = useMemo(() => {
|
||||||
|
if (!isResponseSuccess(projectFlocksRawData)) return [];
|
||||||
|
|
||||||
|
const availableProjectFlocks = projectFlocksRawData.data.filter(
|
||||||
|
(projectFlock) =>
|
||||||
|
selectedFlockIds.length === 0 ||
|
||||||
|
selectedFlockIds.includes(projectFlock.id)
|
||||||
|
);
|
||||||
|
|
||||||
|
const kandangMap = new Map<number, OptionType<number>>();
|
||||||
|
|
||||||
|
availableProjectFlocks.forEach((projectFlock) => {
|
||||||
|
projectFlock.kandangs?.forEach((kandang) => {
|
||||||
|
if (!kandangMap.has(kandang.id)) {
|
||||||
|
kandangMap.set(kandang.id, {
|
||||||
|
value: kandang.id,
|
||||||
|
label: kandang.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const normalizedSearch = kandangInputValue.trim().toLowerCase();
|
||||||
|
const allOptions = Array.from(kandangMap.values());
|
||||||
|
|
||||||
|
if (!normalizedSearch) return allOptions;
|
||||||
|
|
||||||
|
return allOptions.filter((option) =>
|
||||||
|
option.label.toLowerCase().includes(normalizedSearch)
|
||||||
|
);
|
||||||
|
}, [projectFlocksRawData, selectedFlockIds, kandangInputValue]);
|
||||||
|
|
||||||
|
const kandangSelect = useMemo(
|
||||||
|
() => ({
|
||||||
|
setInputValue: setRawKandangInputValue,
|
||||||
|
options: derivedKandangOptions,
|
||||||
|
isLoadingOptions: isLoadingFlockOptions,
|
||||||
|
loadMore: loadMoreFlock,
|
||||||
|
}),
|
||||||
|
[derivedKandangOptions, isLoadingFlockOptions, loadMoreFlock]
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
setInputValue: setInputValueKandang,
|
||||||
|
options: kandangOptions,
|
||||||
|
isLoadingOptions: isLoadingKandangOptions,
|
||||||
|
loadMore: loadMoreKandang,
|
||||||
|
} = kandangSelect;
|
||||||
|
|
||||||
const handleResetFilter = useCallback(() => {
|
const handleResetFilter = useCallback(() => {
|
||||||
resetForm();
|
resetForm();
|
||||||
resetFilterValues(); // Clear stored filter values
|
resetFilterValues(); // Clear stored filter values
|
||||||
setAnalysisMode('OVERVIEW');
|
setAnalysisMode('OVERVIEW');
|
||||||
setSelectedLocationIds([]);
|
setSelectedLocationIds([]);
|
||||||
}, [resetForm, resetFilterValues]);
|
setRawKandangInputValue('');
|
||||||
|
filterModal.closeModal();
|
||||||
|
}, [filterModal, resetForm, resetFilterValues]);
|
||||||
|
|
||||||
// ===== Formik Error List =====
|
// ===== Formik Error List =====
|
||||||
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
|
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
|
||||||
@@ -460,6 +520,7 @@ const DashboardProduction = () => {
|
|||||||
formik.setFieldValue('kandang', []);
|
formik.setFieldValue('kandang', []);
|
||||||
formik.setFieldValue('comparisonType', '');
|
formik.setFieldValue('comparisonType', '');
|
||||||
setSelectedLocationIds([]);
|
setSelectedLocationIds([]);
|
||||||
|
setRawKandangInputValue('');
|
||||||
}}
|
}}
|
||||||
color='primary'
|
color='primary'
|
||||||
className={{
|
className={{
|
||||||
@@ -505,6 +566,7 @@ const DashboardProduction = () => {
|
|||||||
className={{
|
className={{
|
||||||
select: 'rounded-lg text-sm border-base-content/10',
|
select: 'rounded-lg text-sm border-base-content/10',
|
||||||
}}
|
}}
|
||||||
|
isClearable={true}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -530,6 +592,7 @@ const DashboardProduction = () => {
|
|||||||
// Reset dependent fields when location changes
|
// Reset dependent fields when location changes
|
||||||
formik.setFieldValue('flock', []);
|
formik.setFieldValue('flock', []);
|
||||||
formik.setFieldValue('kandang', []);
|
formik.setFieldValue('kandang', []);
|
||||||
|
setRawKandangInputValue('');
|
||||||
}}
|
}}
|
||||||
errorMessage={formik.errors.location as string}
|
errorMessage={formik.errors.location as string}
|
||||||
options={locationOptions}
|
options={locationOptions}
|
||||||
@@ -541,6 +604,7 @@ const DashboardProduction = () => {
|
|||||||
className={{
|
className={{
|
||||||
select: 'rounded-lg text-sm border-base-content/10',
|
select: 'rounded-lg text-sm border-base-content/10',
|
||||||
}}
|
}}
|
||||||
|
isClearable={true}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<SelectInputRadio
|
<SelectInputRadio
|
||||||
@@ -561,6 +625,7 @@ const DashboardProduction = () => {
|
|||||||
// Reset dependent fields when location changes
|
// Reset dependent fields when location changes
|
||||||
formik.setFieldValue('flock', []);
|
formik.setFieldValue('flock', []);
|
||||||
formik.setFieldValue('kandang', []);
|
formik.setFieldValue('kandang', []);
|
||||||
|
setRawKandangInputValue('');
|
||||||
}}
|
}}
|
||||||
errorMessage={formik.errors.location as string}
|
errorMessage={formik.errors.location as string}
|
||||||
options={locationOptions}
|
options={locationOptions}
|
||||||
@@ -572,6 +637,7 @@ const DashboardProduction = () => {
|
|||||||
className={{
|
className={{
|
||||||
select: 'rounded-lg text-sm border-base-content/10',
|
select: 'rounded-lg text-sm border-base-content/10',
|
||||||
}}
|
}}
|
||||||
|
isClearable={true}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -596,9 +662,11 @@ const DashboardProduction = () => {
|
|||||||
| null
|
| null
|
||||||
| undefined
|
| undefined
|
||||||
}
|
}
|
||||||
onChange={(selected) =>
|
onChange={(selected) => {
|
||||||
formik.setFieldValue('flock', selected)
|
formik.setFieldValue('flock', selected);
|
||||||
}
|
formik.setFieldValue('kandang', []);
|
||||||
|
setInputValueKandang('');
|
||||||
|
}}
|
||||||
errorMessage={formik.errors.flock as string}
|
errorMessage={formik.errors.flock as string}
|
||||||
onInputChange={setInputValueFlock}
|
onInputChange={setInputValueFlock}
|
||||||
onMenuScrollToBottom={loadMoreFlock}
|
onMenuScrollToBottom={loadMoreFlock}
|
||||||
@@ -611,6 +679,7 @@ const DashboardProduction = () => {
|
|||||||
className={{
|
className={{
|
||||||
select: 'rounded-lg text-sm border-base-content/10',
|
select: 'rounded-lg text-sm border-base-content/10',
|
||||||
}}
|
}}
|
||||||
|
isClearable={true}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<SelectInputRadio
|
<SelectInputRadio
|
||||||
@@ -622,9 +691,11 @@ const DashboardProduction = () => {
|
|||||||
| null
|
| null
|
||||||
| undefined
|
| undefined
|
||||||
}
|
}
|
||||||
onChange={(selected) =>
|
onChange={(selected) => {
|
||||||
formik.setFieldValue('flock', selected)
|
formik.setFieldValue('flock', selected);
|
||||||
}
|
formik.setFieldValue('kandang', []);
|
||||||
|
setInputValueKandang('');
|
||||||
|
}}
|
||||||
errorMessage={formik.errors.flock as string}
|
errorMessage={formik.errors.flock as string}
|
||||||
onInputChange={setInputValueFlock}
|
onInputChange={setInputValueFlock}
|
||||||
onMenuScrollToBottom={loadMoreFlock}
|
onMenuScrollToBottom={loadMoreFlock}
|
||||||
@@ -637,6 +708,7 @@ const DashboardProduction = () => {
|
|||||||
className={{
|
className={{
|
||||||
select: 'rounded-lg text-sm border-base-content/10',
|
select: 'rounded-lg text-sm border-base-content/10',
|
||||||
}}
|
}}
|
||||||
|
isClearable={true}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
@@ -675,6 +747,7 @@ const DashboardProduction = () => {
|
|||||||
className={{
|
className={{
|
||||||
select: 'rounded-lg text-sm border-base-content/10',
|
select: 'rounded-lg text-sm border-base-content/10',
|
||||||
}}
|
}}
|
||||||
|
isClearable={true}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<SelectInputRadio
|
<SelectInputRadio
|
||||||
@@ -701,6 +774,7 @@ const DashboardProduction = () => {
|
|||||||
className={{
|
className={{
|
||||||
select: 'rounded-lg text-sm border-base-content/10',
|
select: 'rounded-lg text-sm border-base-content/10',
|
||||||
}}
|
}}
|
||||||
|
isClearable={true}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -178,12 +178,14 @@ const ExpenseRequestForm = ({
|
|||||||
setInputValue: setLocationInputValue,
|
setInputValue: setLocationInputValue,
|
||||||
options: locationOptions,
|
options: locationOptions,
|
||||||
isLoadingOptions: isLoadingLocationOptions,
|
isLoadingOptions: isLoadingLocationOptions,
|
||||||
|
loadMore: loadMoreLocations,
|
||||||
} = useSelect<Location>(LocationApi.basePath, 'id', 'name');
|
} = useSelect<Location>(LocationApi.basePath, 'id', 'name');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
setInputValue: setVendorInputValue,
|
setInputValue: setVendorInputValue,
|
||||||
options: supplierOptions,
|
options: supplierOptions,
|
||||||
isLoadingOptions: isLoadingVendorOptions,
|
isLoadingOptions: isLoadingVendorOptions,
|
||||||
|
loadMore: loadMoreSuppliers,
|
||||||
} = useSelect<Supplier>(SupplierApi.basePath, 'id', 'name');
|
} = useSelect<Supplier>(SupplierApi.basePath, 'id', 'name');
|
||||||
|
|
||||||
const categoryChangeHandler = (val: OptionType | OptionType[] | null) => {
|
const categoryChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||||
@@ -408,6 +410,7 @@ const ExpenseRequestForm = ({
|
|||||||
options={locationOptions}
|
options={locationOptions}
|
||||||
onInputChange={setLocationInputValue}
|
onInputChange={setLocationInputValue}
|
||||||
isLoading={isLoadingLocationOptions}
|
isLoading={isLoadingLocationOptions}
|
||||||
|
onMenuScrollToBottom={loadMoreLocations}
|
||||||
isError={
|
isError={
|
||||||
formik.touched.location_id && Boolean(formik.errors.location_id)
|
formik.touched.location_id && Boolean(formik.errors.location_id)
|
||||||
}
|
}
|
||||||
@@ -452,6 +455,7 @@ const ExpenseRequestForm = ({
|
|||||||
options={supplierOptions}
|
options={supplierOptions}
|
||||||
onInputChange={setVendorInputValue}
|
onInputChange={setVendorInputValue}
|
||||||
isLoading={isLoadingVendorOptions}
|
isLoading={isLoadingVendorOptions}
|
||||||
|
onMenuScrollToBottom={loadMoreSuppliers}
|
||||||
isError={
|
isError={
|
||||||
formik.touched.supplier_id && Boolean(formik.errors.supplier_id)
|
formik.touched.supplier_id && Boolean(formik.errors.supplier_id)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ import SelectInput, {
|
|||||||
useSelect,
|
useSelect,
|
||||||
} from '@/components/input/SelectInput';
|
} from '@/components/input/SelectInput';
|
||||||
import { MARKETING_APPROVAL_LINE } from '@/config/approval-line';
|
import { MARKETING_APPROVAL_LINE } from '@/config/approval-line';
|
||||||
|
import {
|
||||||
|
MarketingFilterFormValues,
|
||||||
|
MarketingFilterSchema,
|
||||||
|
} from '@/components/pages/marketing/filter/MarketingFilter';
|
||||||
import { MarketingFilter } from '@/types/api/marketing/marketing';
|
import { MarketingFilter } from '@/types/api/marketing/marketing';
|
||||||
import SelectInputCheckbox from '@/components/input/SelectInputCheckbox';
|
import SelectInputCheckbox from '@/components/input/SelectInputCheckbox';
|
||||||
import { MarketingApi } from '@/services/api/marketing/marketing';
|
import { MarketingApi } from '@/services/api/marketing/marketing';
|
||||||
@@ -70,8 +74,8 @@ const MarketingFilterModal = ({
|
|||||||
limit: 'limit',
|
limit: 'limit',
|
||||||
});
|
});
|
||||||
|
|
||||||
const uniqueCustomersOptions = useMemo(() => {
|
const salesCustomerOptions = useMemo(() => {
|
||||||
const seen = new Set();
|
const seen = new Set<string | number>();
|
||||||
return customersOptions.filter((customer) => {
|
return customersOptions.filter((customer) => {
|
||||||
if (seen.has(customer.value)) return false;
|
if (seen.has(customer.value)) return false;
|
||||||
seen.add(customer.value);
|
seen.add(customer.value);
|
||||||
@@ -87,23 +91,19 @@ const MarketingFilterModal = ({
|
|||||||
{ value: 'DITOLAK', label: 'Ditolak' },
|
{ value: 'DITOLAK', label: 'Ditolak' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const formik = useFormik<{
|
const formik = useFormik<MarketingFilterFormValues>({
|
||||||
product_ids: OptionType[];
|
|
||||||
status: OptionType | null;
|
|
||||||
customer_id: OptionType | null;
|
|
||||||
}>({
|
|
||||||
initialValues: {
|
initialValues: {
|
||||||
product_ids: [],
|
product_ids: [],
|
||||||
status: null,
|
status: null,
|
||||||
customer_id: null,
|
customer: null,
|
||||||
},
|
},
|
||||||
|
validationSchema: MarketingFilterSchema,
|
||||||
|
|
||||||
onSubmit: async (values) => {
|
onSubmit: async (values) => {
|
||||||
const formattedValues = {
|
const formattedValues: MarketingFilter = {
|
||||||
...values,
|
|
||||||
product_ids: values.product_ids.map((item) => Number(item.value)),
|
product_ids: values.product_ids.map((item) => Number(item.value)),
|
||||||
status: values.status?.value.toString() || '',
|
status: values.status?.value.toString() || '',
|
||||||
customer_id: Number(values.customer_id?.value),
|
customer_id: Number(values.customer?.value),
|
||||||
};
|
};
|
||||||
|
|
||||||
onSubmit?.(formattedValues);
|
onSubmit?.(formattedValues);
|
||||||
@@ -121,7 +121,10 @@ const MarketingFilterModal = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const customerChangeHandler = (val: OptionType | OptionType[] | null) => {
|
const customerChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||||
formik.setFieldValue('customer_id', val as OptionType);
|
formik.setFieldValue(
|
||||||
|
'customer',
|
||||||
|
!Array.isArray(val) ? (val as OptionType<number> | null) : null
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const statusChangeHandler = (val: OptionType | OptionType[] | null) => {
|
const statusChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||||
@@ -187,9 +190,9 @@ const MarketingFilterModal = ({
|
|||||||
label='Customer'
|
label='Customer'
|
||||||
isClearable
|
isClearable
|
||||||
placeholder='Pilih customer'
|
placeholder='Pilih customer'
|
||||||
options={uniqueCustomersOptions}
|
options={salesCustomerOptions}
|
||||||
isLoading={isLoadingCustomersOptions}
|
isLoading={isLoadingCustomersOptions}
|
||||||
value={formik.values.customer_id}
|
value={formik.values.customer}
|
||||||
onChange={customerChangeHandler}
|
onChange={customerChangeHandler}
|
||||||
onInputChange={setCustomersInputValue}
|
onInputChange={setCustomersInputValue}
|
||||||
onMenuScrollToBottom={loadMoreCustomers}
|
onMenuScrollToBottom={loadMoreCustomers}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { array, mixed, object } from 'yup';
|
||||||
|
import { OptionType } from '@/components/input/SelectInput';
|
||||||
|
|
||||||
|
export const MarketingFilterSchema = object({
|
||||||
|
product_ids: array().of(mixed<OptionType<number>>().required()).required(),
|
||||||
|
status: mixed<OptionType<string>>().nullable(),
|
||||||
|
customer: mixed<OptionType<number>>().nullable(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type MarketingFilterFormValues = {
|
||||||
|
product_ids: OptionType<number>[];
|
||||||
|
status: OptionType<string> | null;
|
||||||
|
customer: OptionType<number> | null;
|
||||||
|
};
|
||||||
@@ -33,18 +33,18 @@ import { generateReportExpensePDF } from '../export/ReportExpenseExportPDF';
|
|||||||
import { generateReportExpenseExcel } from '../export/ReportExpenseExportXLSX';
|
import { generateReportExpenseExcel } from '../export/ReportExpenseExportXLSX';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import {
|
import {
|
||||||
KandangApi,
|
|
||||||
LocationApi,
|
LocationApi,
|
||||||
NonstockApi,
|
NonstockApi,
|
||||||
SupplierApi,
|
SupplierApi,
|
||||||
} from '@/services/api/master-data';
|
} from '@/services/api/master-data';
|
||||||
import { Supplier } from '@/types/api/master-data/supplier';
|
import { Supplier } from '@/types/api/master-data/supplier';
|
||||||
import { Kandang } from '@/types/api/master-data/kandang';
|
|
||||||
import { Nonstock } from '@/types/api/master-data/nonstock';
|
import { Nonstock } from '@/types/api/master-data/nonstock';
|
||||||
import { ColumnDef } from '@tanstack/react-table';
|
import { ColumnDef } from '@tanstack/react-table';
|
||||||
import { httpClient } from '@/services/http/client';
|
import { httpClient } from '@/services/http/client';
|
||||||
import { BaseApiResponse } from '@/types/api/api-general';
|
import { BaseApiResponse } from '@/types/api/api-general';
|
||||||
import ButtonFilter from '@/components/helper/ButtonFilter';
|
import ButtonFilter from '@/components/helper/ButtonFilter';
|
||||||
|
import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang';
|
||||||
|
import { ProjectFlockKandangApi } from '@/services/api/production/project-flock-kandang';
|
||||||
|
|
||||||
interface ReportExpenseTabProps {
|
interface ReportExpenseTabProps {
|
||||||
tabId: string;
|
tabId: string;
|
||||||
@@ -136,7 +136,7 @@ const ReportExpenseTab = ({ tabId }: ReportExpenseTabProps) => {
|
|||||||
options: locationOptions,
|
options: locationOptions,
|
||||||
isLoadingOptions: isLoadingLocations,
|
isLoadingOptions: isLoadingLocations,
|
||||||
loadMore: loadMoreLocations,
|
loadMore: loadMoreLocations,
|
||||||
} = useSelect<Kandang>(LocationApi.basePath, 'id', 'name', 'search');
|
} = useSelect<Location>(LocationApi.basePath, 'id', 'name', 'search');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
setInputValue: setSupplierInputValue,
|
setInputValue: setSupplierInputValue,
|
||||||
@@ -146,14 +146,14 @@ const ReportExpenseTab = ({ tabId }: ReportExpenseTabProps) => {
|
|||||||
} = useSelect<Supplier>(SupplierApi.basePath, 'id', 'name', 'search');
|
} = useSelect<Supplier>(SupplierApi.basePath, 'id', 'name', 'search');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
setInputValue: setKandangInputValue,
|
setInputValue: setProjectFlockKandangInputValue,
|
||||||
options: kandangOptions,
|
options: projectFlockKandangOptions,
|
||||||
isLoadingOptions: isLoadingKandangs,
|
isLoadingOptions: isLoadingProjectFlockKandangs,
|
||||||
loadMore: loadMoreKandangs,
|
loadMore: loadMoreProjectFlockKandangs,
|
||||||
} = useSelect<Kandang>(
|
} = useSelect<ProjectFlockKandang>(
|
||||||
KandangApi.basePath,
|
ProjectFlockKandangApi.basePath,
|
||||||
'id',
|
'id',
|
||||||
'name',
|
'name_with_period',
|
||||||
'search',
|
'search',
|
||||||
formik.values.location_id?.value
|
formik.values.location_id?.value
|
||||||
? { location_id: String(formik.values.location_id.value) }
|
? { location_id: String(formik.values.location_id.value) }
|
||||||
@@ -643,14 +643,14 @@ const ReportExpenseTab = ({ tabId }: ReportExpenseTabProps) => {
|
|||||||
<SelectInput
|
<SelectInput
|
||||||
label='Kandang'
|
label='Kandang'
|
||||||
placeholder='Pilih Kandang'
|
placeholder='Pilih Kandang'
|
||||||
options={kandangOptions}
|
options={projectFlockKandangOptions}
|
||||||
isLoading={isLoadingKandangs}
|
isLoading={isLoadingProjectFlockKandangs}
|
||||||
value={kandangValue}
|
value={kandangValue}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
formik.setFieldValue('kandang_id', val);
|
formik.setFieldValue('kandang_id', val);
|
||||||
}}
|
}}
|
||||||
onInputChange={setKandangInputValue}
|
onInputChange={setProjectFlockKandangInputValue}
|
||||||
onMenuScrollToBottom={loadMoreKandangs}
|
onMenuScrollToBottom={loadMoreProjectFlockKandangs}
|
||||||
isClearable
|
isClearable
|
||||||
isDisabled={!formik.values.location_id}
|
isDisabled={!formik.values.location_id}
|
||||||
className={{ wrapper: 'w-full' }}
|
className={{ wrapper: 'w-full' }}
|
||||||
|
|||||||
@@ -43,15 +43,7 @@ export const ProductionResultFilterSchema = yup.object({
|
|||||||
}
|
}
|
||||||
return !!value;
|
return !!value;
|
||||||
}),
|
}),
|
||||||
kandang_id: yup
|
kandang_id: yup.mixed<OptionType>().nullable(),
|
||||||
.mixed<OptionType>()
|
|
||||||
.required('Kandang wajib dipilih')
|
|
||||||
.test('is-not-empty', 'Kandang wajib dipilih', (value) => {
|
|
||||||
if (Array.isArray(value)) {
|
|
||||||
return value.length > 0;
|
|
||||||
}
|
|
||||||
return !!value;
|
|
||||||
}),
|
|
||||||
}) as yup.ObjectSchema<ProductionResultFilterFormType>;
|
}) as yup.ObjectSchema<ProductionResultFilterFormType>;
|
||||||
|
|
||||||
export type ProductionResultFilterValues = yup.InferType<
|
export type ProductionResultFilterValues = yup.InferType<
|
||||||
|
|||||||
+27
-10
@@ -46,6 +46,7 @@ import Modal, { useModal } from '@/components/Modal';
|
|||||||
import { formatNumber } from '@/lib/helper';
|
import { formatNumber } from '@/lib/helper';
|
||||||
import Pagination from '@/components/Pagination';
|
import Pagination from '@/components/Pagination';
|
||||||
import ProductionResultSkeleton from '@/components/pages/report/production-result/skeleton/ProductionResultSkeleton';
|
import ProductionResultSkeleton from '@/components/pages/report/production-result/skeleton/ProductionResultSkeleton';
|
||||||
|
import { ProjectFlock } from '@/types/api/production/project-flock';
|
||||||
|
|
||||||
interface ProductionResultTabProps {
|
interface ProductionResultTabProps {
|
||||||
tabId: string;
|
tabId: string;
|
||||||
@@ -238,6 +239,17 @@ const ProductionResultContent = ({ tabId }: ProductionResultTabProps) => {
|
|||||||
? String(values.kandang_id.value)
|
? String(values.kandang_id.value)
|
||||||
: undefined,
|
: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const selectedProjectFlockKandangRawData = isResponseSuccess(
|
||||||
|
projectFlockKandangsRawData
|
||||||
|
)
|
||||||
|
? projectFlockKandangsRawData.data.find(
|
||||||
|
(item) => item.id === values.kandang_id?.value
|
||||||
|
)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
setSelectedProjectFlockKandang(selectedProjectFlockKandangRawData);
|
||||||
|
|
||||||
filterModal.closeModal();
|
filterModal.closeModal();
|
||||||
setIsSubmitted(true);
|
setIsSubmitted(true);
|
||||||
setPage(1);
|
setPage(1);
|
||||||
@@ -255,6 +267,9 @@ const ProductionResultContent = ({ tabId }: ProductionResultTabProps) => {
|
|||||||
formik.validateForm();
|
formik.validateForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const [selectedProjectFlockKandang, setSelectedProjectFlockKandang] =
|
||||||
|
useState<ProjectFlockKandang | undefined>();
|
||||||
|
|
||||||
// ===== OPTIONS =====
|
// ===== OPTIONS =====
|
||||||
const {
|
const {
|
||||||
setInputValue: setAreaInputValue,
|
setInputValue: setAreaInputValue,
|
||||||
@@ -279,7 +294,7 @@ const ProductionResultContent = ({ tabId }: ProductionResultTabProps) => {
|
|||||||
options: projectFlockOptions,
|
options: projectFlockOptions,
|
||||||
isLoadingOptions: isLoadingProjectFlocks,
|
isLoadingOptions: isLoadingProjectFlocks,
|
||||||
loadMore: loadMoreProjectFlocks,
|
loadMore: loadMoreProjectFlocks,
|
||||||
} = useSelect<BaseKandang>(
|
} = useSelect<ProjectFlock>(
|
||||||
ProjectFlockApi.basePath,
|
ProjectFlockApi.basePath,
|
||||||
'id',
|
'id',
|
||||||
'flock_name',
|
'flock_name',
|
||||||
@@ -300,10 +315,11 @@ const ProductionResultContent = ({ tabId }: ProductionResultTabProps) => {
|
|||||||
options: projectFlockKandangOptions,
|
options: projectFlockKandangOptions,
|
||||||
isLoadingOptions: isLoadingProjectFlockKandangs,
|
isLoadingOptions: isLoadingProjectFlockKandangs,
|
||||||
loadMore: loadMoreProjectFlockKandangs,
|
loadMore: loadMoreProjectFlockKandangs,
|
||||||
} = useSelect<BaseKandang>(
|
rawData: projectFlockKandangsRawData,
|
||||||
|
} = useSelect<ProjectFlockKandang>(
|
||||||
ProjectFlockKandangApi.basePath,
|
ProjectFlockKandangApi.basePath,
|
||||||
'id',
|
'id',
|
||||||
'kandang.name',
|
'name_with_period',
|
||||||
'search',
|
'search',
|
||||||
{
|
{
|
||||||
area_id: formik.values.area_id?.value
|
area_id: formik.values.area_id?.value
|
||||||
@@ -359,13 +375,15 @@ const ProductionResultContent = ({ tabId }: ProductionResultTabProps) => {
|
|||||||
([url]: string[]) => httpClient<BaseApiResponse<ProjectFlockKandang[]>>(url)
|
([url]: string[]) => httpClient<BaseApiResponse<ProjectFlockKandang[]>>(url)
|
||||||
);
|
);
|
||||||
|
|
||||||
const projectFlockKandangs = useMemo(
|
const projectFlockKandangs = useMemo(() => {
|
||||||
() =>
|
if (selectedProjectFlockKandang) {
|
||||||
isResponseSuccess(projectFlockKandangsData)
|
return [selectedProjectFlockKandang];
|
||||||
|
}
|
||||||
|
|
||||||
|
return isResponseSuccess(projectFlockKandangsData)
|
||||||
? projectFlockKandangsData.data
|
? projectFlockKandangsData.data
|
||||||
: null,
|
: null;
|
||||||
[projectFlockKandangsData]
|
}, [projectFlockKandangsData, selectedProjectFlockKandang]);
|
||||||
);
|
|
||||||
|
|
||||||
const projectFlockKandangMetadata = useMemo(
|
const projectFlockKandangMetadata = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@@ -804,7 +822,6 @@ const ProductionResultContent = ({ tabId }: ProductionResultTabProps) => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<SelectInput
|
<SelectInput
|
||||||
required
|
|
||||||
label='Kandang'
|
label='Kandang'
|
||||||
placeholder='Pilih Kandang'
|
placeholder='Pilih Kandang'
|
||||||
options={projectFlockKandangOptions}
|
options={projectFlockKandangOptions}
|
||||||
|
|||||||
Reference in New Issue
Block a user