mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
Merge branch 'development' into 'production'
Development See merge request mbugroup/lti-web-client!456
This commit is contained in:
@@ -11,7 +11,6 @@ import { useModal } from '@/components/Modal';
|
||||
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
||||
import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes';
|
||||
import Table from '@/components/Table';
|
||||
import Dropdown from '@/components/Dropdown';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
import { cn, formatDate } from '@/lib/helper';
|
||||
import { AreaApi, KandangApi, LocationApi } from '@/services/api/master-data';
|
||||
@@ -44,6 +43,7 @@ import {
|
||||
import Modal from '@/components/Modal';
|
||||
import SelectInputRadio from '@/components/input/SelectInputRadio';
|
||||
import ButtonFilter from '@/components/helper/ButtonFilter';
|
||||
import NumberInput from '@/components/input/NumberInput';
|
||||
|
||||
const RowOptionsMenu = ({
|
||||
props,
|
||||
@@ -211,8 +211,7 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
||||
);
|
||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||
const [isApproveLoading, setIsApproveLoading] = useState(false);
|
||||
const [isLoadingExportingToExcel, setIsLoadingExportingToExcel] =
|
||||
useState(false);
|
||||
|
||||
const {
|
||||
isChickinApproveModalOpen,
|
||||
isChickinApproveLoading,
|
||||
@@ -327,14 +326,6 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
||||
[]
|
||||
);
|
||||
|
||||
const periodOptions = useMemo(
|
||||
() => [
|
||||
{ value: '1', label: 'Periode 1' },
|
||||
{ value: '2', label: 'Periode 2' },
|
||||
],
|
||||
[]
|
||||
);
|
||||
|
||||
// ===== FILTER HELPERS =====
|
||||
const areaValue = useMemo(() => {
|
||||
if (!formik.values.area_id) return null;
|
||||
@@ -393,13 +384,6 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
||||
);
|
||||
}, [formik.values.category, categoryOptions]);
|
||||
|
||||
const periodValue = useMemo(() => {
|
||||
if (!formik.values.period) return null;
|
||||
return (
|
||||
periodOptions.find((opt) => opt.value === formik.values.period) || null
|
||||
);
|
||||
}, [formik.values.period, periodOptions]);
|
||||
|
||||
// ===== FILTER DEPENDENCY HANDLERS =====
|
||||
const handleFilterAreaChange = (area: OptionType | null) => {
|
||||
const areaId = area?.value ? String(area.value) : undefined;
|
||||
@@ -813,14 +797,6 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
||||
[]
|
||||
);
|
||||
|
||||
const exportToExcelHandler = async () => {
|
||||
setIsLoadingExportingToExcel(true);
|
||||
|
||||
toast.error('Not implemented yet!');
|
||||
|
||||
setIsLoadingExportingToExcel(false);
|
||||
};
|
||||
|
||||
const bulkApproveClickHandler = () => {
|
||||
setApprovalAction('APPROVED');
|
||||
confirmModal.openModal();
|
||||
@@ -1020,51 +996,6 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
||||
onClick={handleFilterModalOpen}
|
||||
className='px-3 py-2.5'
|
||||
/>
|
||||
|
||||
<Dropdown
|
||||
align='end'
|
||||
direction='bottom'
|
||||
className={{
|
||||
content:
|
||||
'mt-1 rounded-xl border border-base-content/5 shadow-sm overflow-hidden',
|
||||
}}
|
||||
trigger={
|
||||
<Button
|
||||
variant='outline'
|
||||
color='none'
|
||||
className='px-3 py-2.5 text-sm text-base-content/50 border border-base-content/10 rounded-xl shadow-button-soft'
|
||||
>
|
||||
<div className='flex flex-row items-center gap-1.5'>
|
||||
<Icon
|
||||
icon='heroicons:cloud-arrow-down'
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
|
||||
<span>Export</span>
|
||||
|
||||
<div className='w-px self-stretch bg-base-content/10' />
|
||||
|
||||
<Icon
|
||||
icon='heroicons:chevron-down'
|
||||
width={14}
|
||||
height={14}
|
||||
/>
|
||||
</div>
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
variant='ghost'
|
||||
color='none'
|
||||
onClick={exportToExcelHandler}
|
||||
isLoading={isLoadingExportingToExcel}
|
||||
className='w-full p-3 justify-start text-sm text-base-content/50 font-semibold text-nowrap'
|
||||
>
|
||||
<Icon icon='heroicons:table-cells' width={20} height={20} />
|
||||
Export to Excel
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1393,18 +1324,14 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
||||
isClearable={true}
|
||||
/>
|
||||
|
||||
<SelectInputRadio
|
||||
<NumberInput
|
||||
label='Periode'
|
||||
placeholder='Pilih Periode'
|
||||
options={periodOptions}
|
||||
value={periodValue}
|
||||
onChange={(val) => {
|
||||
if (!Array.isArray(val)) {
|
||||
formik.setFieldValue('period', val?.value || null);
|
||||
}
|
||||
}}
|
||||
name='period'
|
||||
placeholder='Masukkan Periode'
|
||||
value={formik.values.period ?? ''}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
className={{ wrapper: 'w-full' }}
|
||||
isClearable
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
CreateLayingRecordingPayload,
|
||||
CreateEggPayload,
|
||||
} from '@/types/api/production/recording';
|
||||
import { getProductWarehouseOptionLabel } from '@/lib/product-warehouse';
|
||||
|
||||
type RecordingGrowingFormSchemaType = {
|
||||
record_date: string;
|
||||
@@ -29,11 +30,19 @@ type RecordingGrowingFormSchemaType = {
|
||||
} | null;
|
||||
project_flock_kandang_id: number;
|
||||
stocks: {
|
||||
product_warehouse_id: number;
|
||||
product_warehouse_id:
|
||||
| {
|
||||
value: number;
|
||||
label: string;
|
||||
}
|
||||
| undefined;
|
||||
qty: number | string;
|
||||
}[];
|
||||
depletions: {
|
||||
product_warehouse_id?: number;
|
||||
product_warehouse_id?: {
|
||||
value: number;
|
||||
label: string;
|
||||
};
|
||||
source_product_warehouse_id?: number;
|
||||
qty?: number | string;
|
||||
}[];
|
||||
@@ -41,34 +50,48 @@ type RecordingGrowingFormSchemaType = {
|
||||
|
||||
type RecordingLayingFormSchemaType = RecordingGrowingFormSchemaType & {
|
||||
eggs: {
|
||||
product_warehouse_id?: number;
|
||||
product_warehouse_id?: {
|
||||
value: number;
|
||||
label: string;
|
||||
};
|
||||
qty?: number | string;
|
||||
weight?: number | string;
|
||||
}[];
|
||||
};
|
||||
|
||||
export type StockSchema = {
|
||||
product_warehouse_id: number;
|
||||
product_warehouse_id: {
|
||||
value: number;
|
||||
label: string;
|
||||
};
|
||||
qty: number | string;
|
||||
};
|
||||
|
||||
export type DepletionSchema = {
|
||||
product_warehouse_id?: number;
|
||||
product_warehouse_id?: {
|
||||
value: number;
|
||||
label: string;
|
||||
};
|
||||
source_product_warehouse_id?: number;
|
||||
qty?: number | string;
|
||||
};
|
||||
|
||||
export type EggSchema = {
|
||||
product_warehouse_id?: number;
|
||||
product_warehouse_id?: {
|
||||
value: number;
|
||||
label: string;
|
||||
};
|
||||
qty?: number | string;
|
||||
weight?: number | string;
|
||||
};
|
||||
|
||||
const StockObjectSchema: Yup.ObjectSchema<StockSchema> = Yup.object({
|
||||
product_warehouse_id: Yup.number()
|
||||
product_warehouse_id: Yup.object({
|
||||
value: Yup.number().min(1).required(),
|
||||
label: Yup.string().required(),
|
||||
})
|
||||
.required('Produk wajib diisi!')
|
||||
.min(1, 'Produk wajib diisi!')
|
||||
.typeError('Produk harus berupa angka!'),
|
||||
.typeError('Produk wajib diisi!'),
|
||||
qty: Yup.number()
|
||||
.required('Jumlah penggunaan wajib diisi!')
|
||||
.moreThan(0, 'Jumlah penggunaan harus lebih dari 0!')
|
||||
@@ -76,7 +99,10 @@ const StockObjectSchema: Yup.ObjectSchema<StockSchema> = Yup.object({
|
||||
});
|
||||
|
||||
const DepletionObjectSchema: Yup.ObjectSchema<DepletionSchema> = Yup.object({
|
||||
product_warehouse_id: Yup.number()
|
||||
product_warehouse_id: Yup.object({
|
||||
value: Yup.number().min(1).required(),
|
||||
label: Yup.string().required(),
|
||||
})
|
||||
.optional()
|
||||
.typeError('Depletions harus berupa angka!'),
|
||||
source_product_warehouse_id: Yup.number()
|
||||
@@ -88,7 +114,10 @@ const DepletionObjectSchema: Yup.ObjectSchema<DepletionSchema> = Yup.object({
|
||||
});
|
||||
|
||||
const EggObjectSchema: Yup.ObjectSchema<EggSchema> = Yup.object({
|
||||
product_warehouse_id: Yup.number()
|
||||
product_warehouse_id: Yup.object({
|
||||
value: Yup.number().min(1).required(),
|
||||
label: Yup.string().required(),
|
||||
})
|
||||
.optional()
|
||||
.typeError('Kondisi telur harus berupa angka!'),
|
||||
qty: Yup.number().optional().typeError('Jumlah telur harus berupa angka!'),
|
||||
@@ -248,14 +277,17 @@ export const getRecordingGrowingFormInitialValues = (
|
||||
initialValues?.project_flock?.project_flock_kandang_id ??
|
||||
0,
|
||||
stocks: initialValues?.stocks?.map((stock) => ({
|
||||
product_warehouse_id: stock.product_warehouse_id,
|
||||
product_warehouse_id: {
|
||||
value: stock.product_warehouse_id,
|
||||
label: getProductWarehouseOptionLabel(stock.product_warehouse),
|
||||
},
|
||||
qty:
|
||||
(stock as { qty?: number; usage_amount?: number }).qty ||
|
||||
(stock as { qty?: number; usage_amount?: number }).usage_amount ||
|
||||
'',
|
||||
})) ?? [
|
||||
{
|
||||
product_warehouse_id: 0,
|
||||
product_warehouse_id: undefined,
|
||||
qty: '',
|
||||
},
|
||||
],
|
||||
@@ -263,13 +295,16 @@ export const getRecordingGrowingFormInitialValues = (
|
||||
(
|
||||
depletion: NonNullable<CreateGrowingRecordingPayload['depletions']>[0]
|
||||
) => ({
|
||||
product_warehouse_id: depletion.product_warehouse_id,
|
||||
product_warehouse_id: {
|
||||
value: Number(depletion.product_warehouse_id ?? 0),
|
||||
label: getProductWarehouseOptionLabel(depletion.product_warehouse),
|
||||
},
|
||||
source_product_warehouse_id: depletion.source_product_warehouse_id,
|
||||
qty: depletion.qty,
|
||||
})
|
||||
) ?? [
|
||||
{
|
||||
product_warehouse_id: 0,
|
||||
product_warehouse_id: undefined,
|
||||
qty: '',
|
||||
},
|
||||
],
|
||||
@@ -281,12 +316,15 @@ export const getRecordingLayingFormInitialValues = (
|
||||
...getRecordingGrowingFormInitialValues(initialValues),
|
||||
|
||||
eggs: initialValues?.eggs?.map((egg: CreateEggPayload) => ({
|
||||
product_warehouse_id: egg.product_warehouse_id,
|
||||
product_warehouse_id: {
|
||||
value: Number(egg.product_warehouse_id ?? 0),
|
||||
label: getProductWarehouseOptionLabel(egg.product_warehouse),
|
||||
},
|
||||
qty: egg.qty,
|
||||
weight: egg.weight,
|
||||
})) ?? [
|
||||
{
|
||||
product_warehouse_id: 0,
|
||||
product_warehouse_id: undefined,
|
||||
qty: '',
|
||||
weight: '',
|
||||
},
|
||||
|
||||
@@ -522,7 +522,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
? values.depletions
|
||||
?.filter((d) => d.product_warehouse_id && d.qty)
|
||||
.map((depletion) => ({
|
||||
product_warehouse_id: depletion.product_warehouse_id!,
|
||||
product_warehouse_id: depletion.product_warehouse_id?.value ?? 0,
|
||||
...(depletion.source_product_warehouse_id && {
|
||||
source_product_warehouse_id:
|
||||
depletion.source_product_warehouse_id,
|
||||
@@ -533,13 +533,13 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
|
||||
const stocks = recordingRestriction.canEditStock
|
||||
? (values.stocks ?? [])
|
||||
.filter((s) => s.product_warehouse_id && s.qty)
|
||||
.filter((s) => s.product_warehouse_id?.value && s.qty)
|
||||
.map((stock) => ({
|
||||
// In migration mode, product_warehouse_id field holds product.id;
|
||||
// send it as product_id so the backend auto-creates the warehouse entry.
|
||||
...(isMigrationMode
|
||||
? { product_id: stock.product_warehouse_id }
|
||||
: { product_warehouse_id: stock.product_warehouse_id }),
|
||||
? { product_id: stock.product_warehouse_id?.value }
|
||||
: { product_warehouse_id: stock.product_warehouse_id?.value }),
|
||||
qty: Number(stock.qty) || 0,
|
||||
}))
|
||||
: [];
|
||||
@@ -561,9 +561,9 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
const createLayingPayload = useCallback(
|
||||
(values: RecordingLayingFormValues) => {
|
||||
const depletions = values.depletions
|
||||
?.filter((d) => d.product_warehouse_id && d.qty)
|
||||
?.filter((d) => d.product_warehouse_id?.value && d.qty)
|
||||
.map((depletion) => ({
|
||||
product_warehouse_id: depletion.product_warehouse_id!,
|
||||
product_warehouse_id: depletion.product_warehouse_id?.value ?? 0,
|
||||
...(depletion.source_product_warehouse_id && {
|
||||
source_product_warehouse_id: depletion.source_product_warehouse_id,
|
||||
}),
|
||||
@@ -573,7 +573,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
const eggs = values.eggs
|
||||
?.filter((e) => e.product_warehouse_id && e.qty && e.weight)
|
||||
.map((egg) => ({
|
||||
product_warehouse_id: egg.product_warehouse_id!,
|
||||
product_warehouse_id: egg.product_warehouse_id?.value ?? 0,
|
||||
qty: Number(egg.qty) || 0,
|
||||
weight:
|
||||
typeof egg.weight === 'number'
|
||||
@@ -583,11 +583,11 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
|
||||
const stocks = recordingRestriction.canEditStock
|
||||
? values.stocks
|
||||
.filter((s) => s.product_warehouse_id && s.qty)
|
||||
.filter((s) => s.product_warehouse_id?.value && s.qty)
|
||||
.map((stock) => ({
|
||||
...(isMigrationMode
|
||||
? { product_id: stock.product_warehouse_id }
|
||||
: { product_warehouse_id: stock.product_warehouse_id }),
|
||||
? { product_id: stock.product_warehouse_id?.value }
|
||||
: { product_warehouse_id: stock.product_warehouse_id?.value }),
|
||||
qty: Number(stock.qty) || 0,
|
||||
}))
|
||||
: [];
|
||||
@@ -636,21 +636,13 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
rawData: stockProductsPW,
|
||||
isLoadingOptions: isLoadingStockProductsPW,
|
||||
loadMore: loadMoreStockProductsPW,
|
||||
} = useSelect(
|
||||
isMigrationMode ? null : ProductWarehouseApi.basePath,
|
||||
'id',
|
||||
'product.name',
|
||||
'search',
|
||||
{
|
||||
flags: 'PAKAN,OVK',
|
||||
limit: '100',
|
||||
available_only: 'false',
|
||||
location_id: stockProductsLocationId,
|
||||
...(selectedKandangId
|
||||
? { kandang_id: selectedKandangId.toString() }
|
||||
: {}),
|
||||
}
|
||||
);
|
||||
} = useSelect(ProductWarehouseApi.basePath, 'id', 'product.name', 'search', {
|
||||
flags: 'PAKAN,OVK',
|
||||
limit: '100',
|
||||
available_only: 'false',
|
||||
location_id: stockProductsLocationId,
|
||||
...(selectedKandangId ? { kandang_id: selectedKandangId.toString() } : {}),
|
||||
});
|
||||
|
||||
const {
|
||||
setInputValue: setStockMasterInputValue,
|
||||
@@ -1283,8 +1275,12 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
// product_warehouse object returned by the API.
|
||||
if (isMigrationMode && type === 'edit' && initialValues?.stocks?.length) {
|
||||
baseValues.stocks = initialValues.stocks.map((stock) => ({
|
||||
product_warehouse_id:
|
||||
stock.product_warehouse?.product_id ?? stock.product_warehouse_id,
|
||||
product_warehouse_id: {
|
||||
value: Number(
|
||||
stock.product_warehouse?.product_id ?? stock.product_warehouse_id
|
||||
),
|
||||
label: getProductWarehouseOptionLabel(stock.product_warehouse),
|
||||
},
|
||||
qty: stock.usage_amount ?? '',
|
||||
}));
|
||||
}
|
||||
@@ -1438,8 +1434,12 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
formik.setFieldValue(
|
||||
'stocks',
|
||||
initialValues.stocks.map((stock) => ({
|
||||
product_warehouse_id:
|
||||
stock.product_warehouse?.product_id ?? stock.product_warehouse_id,
|
||||
product_warehouse_id: {
|
||||
value: Number(
|
||||
stock.product_warehouse?.product_id ?? stock.product_warehouse_id
|
||||
),
|
||||
label: getProductWarehouseOptionLabel(stock.product_warehouse),
|
||||
},
|
||||
qty: stock.usage_amount ?? '',
|
||||
}))
|
||||
);
|
||||
@@ -1462,7 +1462,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
(stockIdx: number) => {
|
||||
if ((type as 'add' | 'edit' | 'detail') === 'detail') return null;
|
||||
const stock = formik.values.stocks?.[stockIdx];
|
||||
if (!stock || !stock.product_warehouse_id) return null;
|
||||
if (!stock || !stock.product_warehouse_id?.value) return null;
|
||||
return null;
|
||||
},
|
||||
[formik.values.stocks, type]
|
||||
@@ -1492,13 +1492,17 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
const getStockUsageAdornment = useCallback(
|
||||
(stockIdx: number) => {
|
||||
const stock = formik.values.stocks?.[stockIdx];
|
||||
if (!stock || !stock.product_warehouse_id) return null;
|
||||
if (!stock || !stock.product_warehouse_id?.value) return null;
|
||||
|
||||
const isDetail = (type as 'add' | 'edit' | 'detail') === 'detail';
|
||||
const availableStock = getAvailableStock(stock.product_warehouse_id);
|
||||
const availableStock = getAvailableStock(
|
||||
stock.product_warehouse_id.value
|
||||
);
|
||||
const requestedUsage = Number(stock.qty) || 0;
|
||||
const remainingStock = availableStock - requestedUsage;
|
||||
const { pendingQty } = getStockPendingInfo(stock.product_warehouse_id);
|
||||
const { pendingQty } = getStockPendingInfo(
|
||||
stock.product_warehouse_id.value
|
||||
);
|
||||
|
||||
if (isDetail) {
|
||||
if (pendingQty > 0) {
|
||||
@@ -1605,10 +1609,10 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
return (
|
||||
idx !== currentIdx &&
|
||||
s.product_warehouse_id &&
|
||||
s.product_warehouse_id !== 0
|
||||
s.product_warehouse_id.value !== 0
|
||||
);
|
||||
})
|
||||
.map((s) => s.product_warehouse_id) || [];
|
||||
.map((s) => s.product_warehouse_id?.value) || [];
|
||||
|
||||
return unifiedStockProducts.filter(
|
||||
(opt) => !selectedProductIds.includes(Number(opt.value))
|
||||
@@ -1625,10 +1629,10 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
return (
|
||||
idx !== currentIdx &&
|
||||
d.product_warehouse_id &&
|
||||
d.product_warehouse_id !== 0
|
||||
d.product_warehouse_id.value !== 0
|
||||
);
|
||||
})
|
||||
.map((d) => d.product_warehouse_id) || [];
|
||||
.map((d) => d.product_warehouse_id?.value) || [];
|
||||
|
||||
return depletionProducts.filter(
|
||||
(opt) => !selectedProductIds.includes(Number(opt.value))
|
||||
@@ -1645,10 +1649,10 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
return (
|
||||
idx !== currentIdx &&
|
||||
e.product_warehouse_id &&
|
||||
e.product_warehouse_id !== 0
|
||||
e.product_warehouse_id.value !== 0
|
||||
);
|
||||
})
|
||||
.map((e) => e.product_warehouse_id) || [];
|
||||
.map((e) => e.product_warehouse_id?.value) || [];
|
||||
|
||||
return eggProducts.filter(
|
||||
(opt) => !selectedProductIds.includes(Number(opt.value))
|
||||
@@ -1694,7 +1698,9 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
isError: touchedField && Boolean(errorField?.[column]),
|
||||
errorMessage:
|
||||
touchedField && errorField?.[column]
|
||||
? (errorField[column] as string)
|
||||
? errorField[column] instanceof Object
|
||||
? (errorField[column] as OptionType)?.label
|
||||
: (errorField[column] as string)
|
||||
: '',
|
||||
};
|
||||
};
|
||||
@@ -2901,20 +2907,15 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
<td>
|
||||
<SelectInput
|
||||
required
|
||||
key={`stock-product-${idx}-${stock.product_warehouse_id}`}
|
||||
value={
|
||||
unifiedStockProducts.find(
|
||||
(product) =>
|
||||
product.value === stock.product_warehouse_id
|
||||
) || null
|
||||
}
|
||||
key={`stock-product-${idx}-${stock.product_warehouse_id?.value}`}
|
||||
value={stock.product_warehouse_id}
|
||||
onInputChange={setStockInputValue}
|
||||
onChange={(selectedOption) => {
|
||||
const option =
|
||||
selectedOption as OptionType | null;
|
||||
formik.setFieldValue(
|
||||
`stocks.${idx}.product_warehouse_id`,
|
||||
option?.value || 0
|
||||
option
|
||||
);
|
||||
}}
|
||||
options={getAvailableStockProductOptions(idx)}
|
||||
@@ -2950,9 +2951,9 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
}
|
||||
isClearable={type !== 'detail'}
|
||||
inputPrefix={
|
||||
stock.product_warehouse_id
|
||||
stock.product_warehouse_id?.value
|
||||
? getProductFlagBadgeAdornment(
|
||||
stock.product_warehouse_id
|
||||
stock.product_warehouse_id.value
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
@@ -2988,7 +2989,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
inputSuffix={
|
||||
stock.product_warehouse_id
|
||||
? getProductUomSuffix(
|
||||
stock.product_warehouse_id,
|
||||
stock.product_warehouse_id.value,
|
||||
'stock'
|
||||
)
|
||||
: null
|
||||
@@ -3181,19 +3182,13 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
)}
|
||||
<td>
|
||||
<SelectInput
|
||||
value={
|
||||
depletionProducts.find(
|
||||
(product) =>
|
||||
product.value ===
|
||||
depletion.product_warehouse_id
|
||||
) || null
|
||||
}
|
||||
value={depletion.product_warehouse_id}
|
||||
onChange={(selectedOption) => {
|
||||
const option =
|
||||
selectedOption as OptionType | null;
|
||||
formik.setFieldValue(
|
||||
`depletions.${idx}.product_warehouse_id`,
|
||||
option?.value || 0
|
||||
option
|
||||
);
|
||||
}}
|
||||
options={getAvailableDepletionProductOptions(idx)}
|
||||
@@ -3256,7 +3251,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
inputSuffix={
|
||||
depletion.product_warehouse_id
|
||||
? getProductUomSuffix(
|
||||
depletion.product_warehouse_id,
|
||||
depletion.product_warehouse_id.value,
|
||||
'depletion'
|
||||
)
|
||||
: null
|
||||
@@ -3434,18 +3429,13 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
)}
|
||||
<td>
|
||||
<SelectInput
|
||||
value={
|
||||
eggProducts.find(
|
||||
(product) =>
|
||||
product.value === egg.product_warehouse_id
|
||||
) || null
|
||||
}
|
||||
value={egg.product_warehouse_id}
|
||||
onChange={(selectedOption) => {
|
||||
const option =
|
||||
selectedOption as OptionType | null;
|
||||
formik.setFieldValue(
|
||||
`eggs.${idx}.product_warehouse_id`,
|
||||
option?.value || 0
|
||||
option
|
||||
);
|
||||
}}
|
||||
options={getAvailableEggProductOptions(idx)}
|
||||
|
||||
@@ -223,6 +223,8 @@ const TransferToLayingFormModal = () => {
|
||||
},
|
||||
});
|
||||
|
||||
const { flockSource: formikFlockSource } = formik.values;
|
||||
|
||||
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
|
||||
|
||||
const [selectedFlockSourceRawData, setSelectedFlockSourceRawData] = useState<
|
||||
@@ -455,13 +457,13 @@ const TransferToLayingFormModal = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (isResponseSuccess(flockSourceRawData)) {
|
||||
const selectedFlockSourceRawData = flockSourceRawData.data.find(
|
||||
const currentSelectedFlockSourceRawData = flockSourceRawData.data.find(
|
||||
(item) => item.id === formik.values.flockSource?.value
|
||||
);
|
||||
|
||||
setSelectedFlockSourceRawData(selectedFlockSourceRawData);
|
||||
setSelectedFlockSourceRawData(currentSelectedFlockSourceRawData);
|
||||
}
|
||||
}, [flockSourceRawData]);
|
||||
}, [flockSourceRawData, formikFlockSource]);
|
||||
|
||||
useEffect(() => {
|
||||
formik.setFieldValue('totalQuantity', totalTransferedChicken);
|
||||
@@ -625,6 +627,7 @@ const TransferToLayingFormModal = () => {
|
||||
>
|
||||
<div className='flex flex-row items-center gap-3'>
|
||||
<input
|
||||
id={`flock-source-kandang-${item.project_flock_kandang_id}`}
|
||||
type='radio'
|
||||
name='flockSourceKandang'
|
||||
value={item.project_flock_kandang_id}
|
||||
@@ -637,13 +640,14 @@ const TransferToLayingFormModal = () => {
|
||||
/>
|
||||
|
||||
<label
|
||||
htmlFor={`flock-source-kandang-${item.project_flock_kandang_id}`}
|
||||
className={cn('text-sm text-base-content/50', {
|
||||
'cursor-pointer': isAvailable,
|
||||
'cursor-not-allowed opacity-50': !isAvailable,
|
||||
})}
|
||||
>
|
||||
{item.kandang_name}{' '}
|
||||
<span className='text-base-content/20'>{`(Max: ${item.available_qty})`}</span>
|
||||
<span className='text-base-content/20'>{`(Max: ${item.available_qty ?? '-'})`}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -409,10 +409,17 @@ const PurchaseTable = () => {
|
||||
setIsDeleteLoading(true);
|
||||
|
||||
try {
|
||||
await PurchaseApi.delete(selectedPurchase?.id as number);
|
||||
refreshPurchaseRequests();
|
||||
deleteModal.closeModal();
|
||||
toast.success('Berhasil menghapus data permintaan pembelian!');
|
||||
const deleteResponse = await PurchaseApi.delete(
|
||||
selectedPurchase?.id as number
|
||||
);
|
||||
|
||||
if (isResponseSuccess(deleteResponse)) {
|
||||
refreshPurchaseRequests();
|
||||
deleteModal.closeModal();
|
||||
toast.success('Berhasil menghapus data permintaan pembelian!');
|
||||
} else {
|
||||
toast.error(deleteResponse?.message ?? 'Gagal menghapus data!');
|
||||
}
|
||||
} catch {
|
||||
toast.error('Gagal menghapus data permintaan pembelian!');
|
||||
}
|
||||
|
||||
@@ -89,7 +89,10 @@ export function Dashboard() {
|
||||
options: kandangOptions,
|
||||
loadMore: loadMoreKandang,
|
||||
isLoadingMore: isLoadingMoreKandang,
|
||||
} = useSelect(DailyChecklistKandangApi.basePath, 'id', 'name');
|
||||
} = useSelect(DailyChecklistKandangApi.basePath, 'id', 'name', 'search', {
|
||||
order_by: 'asc',
|
||||
sort_by: 'name',
|
||||
});
|
||||
|
||||
const handleKandangScroll = (e: React.UIEvent<HTMLDivElement>) => {
|
||||
const target = e.target as HTMLDivElement;
|
||||
|
||||
@@ -110,7 +110,10 @@ export function ListDailyChecklistContent() {
|
||||
options: kandangOptions,
|
||||
isLoadingMore: isLoadingMoreKandang,
|
||||
loadMore: loadMoreKandang,
|
||||
} = useSelect(DailyChecklistKandangApi.basePath, 'id', 'name');
|
||||
} = useSelect(DailyChecklistKandangApi.basePath, 'id', 'name', 'search', {
|
||||
order_by: 'asc',
|
||||
sort_by: 'name',
|
||||
});
|
||||
|
||||
const checklistList = isResponseSuccess(checklistListRes)
|
||||
? checklistListRes.data || []
|
||||
|
||||
@@ -96,7 +96,10 @@ export function MasterEmployeeContent() {
|
||||
options: kandangOptions,
|
||||
loadMore: loadMoreKandang,
|
||||
isLoadingMore: isLoadingMoreKandang,
|
||||
} = useSelect(DailyChecklistKandangApi.basePath, 'id', 'name');
|
||||
} = useSelect(DailyChecklistKandangApi.basePath, 'id', 'name', 'search', {
|
||||
order_by: 'asc',
|
||||
sort_by: 'name',
|
||||
});
|
||||
|
||||
const handleKandangScroll = (e: React.UIEvent<HTMLDivElement>) => {
|
||||
const target = e.target as HTMLDivElement;
|
||||
|
||||
@@ -368,7 +368,9 @@ export function MasterKandangContent() {
|
||||
name='search'
|
||||
placeholder='Cari kandang...'
|
||||
value={tableFilterState.search}
|
||||
onChange={(e) => updateFilter('search', e.target.value)}
|
||||
onChange={(e) =>
|
||||
updateFilter('search', e.target.value, true)
|
||||
}
|
||||
className={{
|
||||
wrapper: 'w-full sm:w-[280px] border-gray-200',
|
||||
inputWrapper: 'px-3 py-2 h-fit rounded-md',
|
||||
@@ -383,7 +385,11 @@ export function MasterKandangContent() {
|
||||
<Select
|
||||
value={tableFilterState.location_id}
|
||||
onValueChange={(value) =>
|
||||
updateFilter('location_id', value === 'all' ? '' : value)
|
||||
updateFilter(
|
||||
'location_id',
|
||||
value === 'all' ? '' : value,
|
||||
true
|
||||
)
|
||||
}
|
||||
>
|
||||
<SelectTrigger className='w-[180px] border-gray-200'>
|
||||
|
||||
@@ -137,6 +137,8 @@ export function DailyChecklistReportsContent() {
|
||||
} = useSelect(DailyChecklistKandangApi.basePath, 'id', 'name', 'search', {
|
||||
area_id: tableFilterState.area_id,
|
||||
location_id: tableFilterState.location_id,
|
||||
order_by: 'asc',
|
||||
sort_by: 'name',
|
||||
});
|
||||
|
||||
const handleKandangScroll = (e: React.UIEvent<HTMLDivElement>) => {
|
||||
@@ -159,17 +161,24 @@ export function DailyChecklistReportsContent() {
|
||||
}
|
||||
);
|
||||
|
||||
const { options: employeeOptions } = useSelect(
|
||||
EmployeeApi.basePath,
|
||||
'id',
|
||||
'name',
|
||||
'search',
|
||||
{
|
||||
page: '1',
|
||||
limit: '500',
|
||||
kandang_id: tableFilterState.kandang_id,
|
||||
const {
|
||||
options: employeeOptions,
|
||||
loadMore: loadMoreEmployee,
|
||||
isLoadingMore: isLoadingMoreEmployee,
|
||||
} = useSelect(EmployeeApi.basePath, 'id', 'name', 'search', {
|
||||
order_by: 'asc',
|
||||
sort_by: 'name',
|
||||
kandang_id: tableFilterState.kandang_id,
|
||||
});
|
||||
|
||||
const handleEmployeeScroll = (e: React.UIEvent<HTMLDivElement>) => {
|
||||
const target = e.target as HTMLDivElement;
|
||||
if (target.scrollHeight - target.scrollTop <= target.clientHeight + 10) {
|
||||
if (!isLoadingMoreEmployee) {
|
||||
loadMoreEmployee();
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const currentMonthMaxDay = new Date(
|
||||
Number(tableFilterState.tahun),
|
||||
@@ -493,7 +502,7 @@ export function DailyChecklistReportsContent() {
|
||||
>
|
||||
<SelectValue placeholder='Semua ABK' />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectContent onScroll={handleEmployeeScroll}>
|
||||
<SelectItem value='ALL'>Semua ABK</SelectItem>
|
||||
{employeeOptions.map((employee) => (
|
||||
<SelectItem
|
||||
@@ -503,6 +512,11 @@ export function DailyChecklistReportsContent() {
|
||||
{employee.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
{isLoadingMoreEmployee && (
|
||||
<div className='flex justify-center p-2'>
|
||||
<Loader2 className='h-4 w-4 animate-spin text-gray-500' />
|
||||
</div>
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { RequestOptions } from '@/services/http/base';
|
||||
import { redirectToSSO } from '@/lib/auth-helper';
|
||||
|
||||
const BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL ?? '';
|
||||
const axiosClient = axios.create({ baseURL: BASE_URL, timeout: 30_000 });
|
||||
const axiosClient = axios.create({ baseURL: BASE_URL, timeout: 60_000 });
|
||||
|
||||
axiosClient.interceptors.response.use(
|
||||
(response) => response,
|
||||
@@ -38,7 +38,7 @@ export async function httpClient<T, B = unknown>(
|
||||
method: opts.method ?? 'GET',
|
||||
params: opts.query,
|
||||
data: opts.body,
|
||||
timeout: opts.timeoutMs ?? 30_000,
|
||||
timeout: opts.timeoutMs ?? 60_000,
|
||||
withCredentials: isCookieAuth && !isBearerAuth,
|
||||
responseType: opts.responseType,
|
||||
headers: {
|
||||
|
||||
+2
@@ -117,6 +117,7 @@ export type CreateGrowingRecordingPayload = {
|
||||
product_warehouse_id?: number;
|
||||
source_product_warehouse_id?: number;
|
||||
qty?: number;
|
||||
product_warehouse?: ProductWarehouse;
|
||||
}[];
|
||||
};
|
||||
|
||||
@@ -124,6 +125,7 @@ export type CreateEggPayload = {
|
||||
product_warehouse_id?: number;
|
||||
qty?: number;
|
||||
weight?: number;
|
||||
product_warehouse?: ProductWarehouse;
|
||||
};
|
||||
|
||||
export type CreateLayingRecordingPayload = CreateGrowingRecordingPayload & {
|
||||
|
||||
Reference in New Issue
Block a user