mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
Merge branch 'fix/transfer-to-laying' into 'development'
[FIX/FE] Transfer to Laying See merge request mbugroup/lti-web-client!265
This commit is contained in:
@@ -176,24 +176,26 @@ const ApprovalStepsV2 = ({
|
||||
})}
|
||||
</div>
|
||||
|
||||
<Button
|
||||
variant='outline'
|
||||
color='none'
|
||||
onClick={seeMoreClickHandler}
|
||||
className={cn(
|
||||
'px-3 py-2 gap-2.5 text-sm text-base-content/50 border border-base-content/10 rounded-lg transition-all'
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
icon='heroicons-outline:chevron-double-down'
|
||||
width={20}
|
||||
height={20}
|
||||
className={cn('transition-all duration-300', {
|
||||
'-rotate-180': isSeeAll,
|
||||
})}
|
||||
/>
|
||||
See {isSeeAll ? 'Less' : 'More'}
|
||||
</Button>
|
||||
{formattedApprovals.length > maxVisibleSteps && (
|
||||
<Button
|
||||
variant='outline'
|
||||
color='none'
|
||||
onClick={seeMoreClickHandler}
|
||||
className={cn(
|
||||
'px-3 py-2 gap-2.5 text-sm text-base-content/50 border border-base-content/10 rounded-lg transition-all'
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
icon='heroicons-outline:chevron-double-down'
|
||||
width={20}
|
||||
height={20}
|
||||
className={cn('transition-all duration-300', {
|
||||
'-rotate-180': isSeeAll,
|
||||
})}
|
||||
/>
|
||||
See {isSeeAll ? 'Less' : 'More'}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -118,7 +118,7 @@ const TextInput = ({
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
'input h-fit px-3 py-2.5 text-sm font-normal leading-6 flex-1 rounded-lg! outline-none! transition-all duration-200 flex items-center bg-white border-base-content/10',
|
||||
'input h-fit px-3 py-2.5 gap-1.5 text-sm font-normal leading-6 flex-1 rounded-lg! outline-none! transition-all duration-200 flex items-center bg-white border-base-content/10',
|
||||
{
|
||||
'border-error': isError,
|
||||
'border-success!': isValid,
|
||||
@@ -182,7 +182,7 @@ const TextInput = ({
|
||||
) : (
|
||||
<div
|
||||
className={cn(
|
||||
'input h-fit px-3 py-2.5 text-sm font-normal leading-6 w-full rounded-lg! outline-none! transition-all duration-200 bg-white border-base-content/10',
|
||||
'input h-fit px-3 py-2.5 gap-1.5 text-sm font-normal leading-6 w-full rounded-lg! outline-none! transition-all duration-200 bg-white border-base-content/10',
|
||||
{
|
||||
'border-error': isError,
|
||||
'border-success!': isValid,
|
||||
|
||||
@@ -288,6 +288,48 @@ const TransferToLayingFormModal = () => {
|
||||
return { available: countAvailable, unavailable: countUnavailable };
|
||||
}, [mappedFlockSourceKandangsAvailability]);
|
||||
|
||||
const {
|
||||
data: flockDestinationKandangsMaxTargetQty,
|
||||
isLoading: isLoadingFlockDestinationKandangsMaxTargetQty,
|
||||
} = useSWR(
|
||||
formik.values.flockDestination
|
||||
? [
|
||||
'transfer-to-laying',
|
||||
'max-target-qty',
|
||||
String(formik.values.flockDestination.value),
|
||||
]
|
||||
: undefined,
|
||||
([, , id]: string[]) =>
|
||||
TransferToLayingApi.getMappedFlockKandangsMaxTargetQty(Number(id))
|
||||
);
|
||||
|
||||
const mappedFlockDestinationKandangsMaxTargetQty: {
|
||||
kandang_name: string;
|
||||
max_target_qty: number;
|
||||
project_flock_kandang_id: number;
|
||||
}[] = useMemo(() => {
|
||||
if (
|
||||
!flockDestinationKandangsMaxTargetQty ||
|
||||
!selectedFlockDestinationRawData
|
||||
)
|
||||
return [];
|
||||
|
||||
return selectedFlockDestinationRawData
|
||||
? selectedFlockDestinationRawData.kandangs.map((kandang) => {
|
||||
const maxQty =
|
||||
flockDestinationKandangsMaxTargetQty[
|
||||
kandang.project_flock_kandang_id
|
||||
]?.max_target_qty;
|
||||
|
||||
return {
|
||||
kandang_name: kandang.name,
|
||||
max_target_qty: maxQty,
|
||||
project_flock_kandang_id: kandang.project_flock_kandang_id,
|
||||
};
|
||||
})
|
||||
: [];
|
||||
}, [flockDestinationKandangsMaxTargetQty, selectedFlockDestinationRawData]);
|
||||
|
||||
const mappedFlockDestinationKandangsAvailabilityInfo: {
|
||||
available: number;
|
||||
unavailable: number;
|
||||
@@ -298,9 +340,8 @@ const TransferToLayingFormModal = () => {
|
||||
let countAvailable = 0;
|
||||
let countUnavailable = 0;
|
||||
|
||||
selectedFlockDestinationRawData?.kandangs.forEach((item) => {
|
||||
// TODO: change this to real available quota later
|
||||
if (item.capacity > 0) {
|
||||
mappedFlockDestinationKandangsMaxTargetQty.forEach((item) => {
|
||||
if (item.max_target_qty > 0) {
|
||||
countAvailable += 1;
|
||||
} else {
|
||||
countUnavailable += 1;
|
||||
@@ -308,7 +349,7 @@ const TransferToLayingFormModal = () => {
|
||||
});
|
||||
|
||||
return { available: countAvailable, unavailable: countUnavailable };
|
||||
}, [selectedFlockDestinationRawData]);
|
||||
}, [mappedFlockDestinationKandangsMaxTargetQty]);
|
||||
|
||||
const totalEnteredChickenForTransfer =
|
||||
formik.values.flockSourceKandangs.reduce(
|
||||
@@ -648,10 +689,9 @@ const TransferToLayingFormModal = () => {
|
||||
</div>
|
||||
|
||||
<div className='w-full rounded-xl border border-base-content/10'>
|
||||
{selectedFlockDestinationRawData?.kandangs.map(
|
||||
{mappedFlockDestinationKandangsMaxTargetQty.map(
|
||||
(item, itemIdx) => {
|
||||
// TODO: change this to real available quota later
|
||||
const isAvailable = item.capacity > 0;
|
||||
const isAvailable = item.max_target_qty > 0;
|
||||
const isChecked =
|
||||
formik.values.flockDestinationKandangs.some(
|
||||
(k) =>
|
||||
@@ -669,11 +709,10 @@ const TransferToLayingFormModal = () => {
|
||||
{
|
||||
kandang: {
|
||||
value: item.project_flock_kandang_id,
|
||||
label: item.name,
|
||||
label: item.kandang_name,
|
||||
},
|
||||
quantity: '',
|
||||
// TODO: change this to real available quota later
|
||||
maxQuantity: item.capacity,
|
||||
maxQuantity: item.max_target_qty,
|
||||
},
|
||||
]);
|
||||
} else {
|
||||
@@ -718,9 +757,8 @@ const TransferToLayingFormModal = () => {
|
||||
'cursor-not-allowed': !isAvailable,
|
||||
})}
|
||||
>
|
||||
{item.name}{' '}
|
||||
{/* TODO: change this to real available quota later */}
|
||||
<span className='text-base-content/20'>{`(Max: ${item.capacity})`}</span>
|
||||
{item.kandang_name}{' '}
|
||||
<span className='text-base-content/20'>{`(Max: ${item.max_target_qty})`}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { ChangeEventHandler, useEffect, useMemo, useState } from 'react';
|
||||
import useSWR from 'swr';
|
||||
import {
|
||||
CellContext,
|
||||
@@ -33,6 +33,7 @@ import { cn, formatDate, formatNumber } from '@/lib/helper';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||
import { Color } from '@/types/theme';
|
||||
import DebouncedTextInput from '@/components/input/DebouncedTextInput';
|
||||
|
||||
const RowOptionsMenu = ({
|
||||
props,
|
||||
@@ -432,6 +433,10 @@ const TransferToLayingsTable = () => {
|
||||
setIsRejectLoading(false);
|
||||
};
|
||||
|
||||
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||
updateFilter('search', e.target.value);
|
||||
};
|
||||
|
||||
const filterSubmitHandler = (values: TransferToLayingFilter) => {
|
||||
updateFilter('startDate', values.startDate);
|
||||
updateFilter('endDate', values.endDate);
|
||||
@@ -527,7 +532,27 @@ const TransferToLayingsTable = () => {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='flex flex-row justify-center items-center gap-3'>
|
||||
<div className='flex flex-1 flex-row justify-start sm:justify-end items-center gap-3 flex-wrap'>
|
||||
<DebouncedTextInput
|
||||
name='search'
|
||||
placeholder='Search'
|
||||
value={tableFilterState.search ?? ''}
|
||||
onChange={searchChangeHandler}
|
||||
startAdornment={
|
||||
<Icon
|
||||
icon='heroicons:magnifying-glass'
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
}
|
||||
className={{
|
||||
wrapper: 'w-full min-w-24 max-w-3xs',
|
||||
inputWrapper: 'rounded-xl! shadow-button-soft',
|
||||
input:
|
||||
'placeholder:font-semibold placeholder:text-base-content/50',
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button
|
||||
variant='outline'
|
||||
color='none'
|
||||
|
||||
+11
-13
@@ -176,6 +176,11 @@ export const getFilledTransferToLayingFormInitialValues = async (
|
||||
initialValues?.from_project_flock.id as number
|
||||
);
|
||||
|
||||
const mappedFlockDestinationKandangsMaxTargetQty =
|
||||
await TransferToLayingApi.getMappedFlockKandangsMaxTargetQty(
|
||||
initialValues?.to_project_flock.id as number
|
||||
);
|
||||
|
||||
const formattedFlockSourceKandangs = initialValues?.sources
|
||||
? initialValues.sources.map((sourceKandang) => ({
|
||||
kandang: {
|
||||
@@ -197,20 +202,8 @@ export const getFilledTransferToLayingFormInitialValues = async (
|
||||
maxTotalQuantity += item.quantity;
|
||||
});
|
||||
|
||||
const flockDestination = await ProjectFlockApi.getSingle(
|
||||
initialValues?.to_project_flock.id as number
|
||||
);
|
||||
|
||||
const formattedFlockDestinationKandangs = initialValues?.targets
|
||||
? initialValues.targets.map((targetKandang) => {
|
||||
const kandang = isResponseSuccess(flockDestination)
|
||||
? flockDestination?.data?.kandangs.find(
|
||||
(kandang) =>
|
||||
String(kandang.project_flock_kandang_id) ===
|
||||
String(targetKandang.target_project_flock_kandang.id)
|
||||
)
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
kandang: {
|
||||
value: targetKandang.target_project_flock_kandang.id,
|
||||
@@ -218,7 +211,12 @@ export const getFilledTransferToLayingFormInitialValues = async (
|
||||
},
|
||||
quantity: targetKandang.qty,
|
||||
|
||||
maxQuantity: kandang?.capacity ?? 0,
|
||||
maxQuantity:
|
||||
(mappedFlockDestinationKandangsMaxTargetQty &&
|
||||
mappedFlockDestinationKandangsMaxTargetQty[
|
||||
targetKandang.target_project_flock_kandang.id
|
||||
].max_target_qty) ??
|
||||
0,
|
||||
};
|
||||
})
|
||||
: [];
|
||||
|
||||
@@ -12,7 +12,10 @@ import {
|
||||
UpdateTransferToLayingPayload,
|
||||
} from '@/types/api/production/transfer-to-laying';
|
||||
import { httpClient } from '@/services/http/client';
|
||||
import { ProjectFlockAvailableQuantity } from '@/types/api/production/project-flock';
|
||||
import {
|
||||
ProjectFlockAvailableQuantity,
|
||||
ProjectFlockMaxQuantity,
|
||||
} from '@/types/api/production/project-flock';
|
||||
import { isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
export class TransferToLayingService extends BaseApiService<
|
||||
@@ -132,7 +135,7 @@ export class TransferToLayingService extends BaseApiService<
|
||||
}
|
||||
}
|
||||
|
||||
async getAvailabelQty(projectFlockId: number) {
|
||||
async getAvailableQty(projectFlockId: number) {
|
||||
try {
|
||||
const availableQtyRes = await httpClient<
|
||||
BaseApiResponse<ProjectFlockAvailableQuantity>
|
||||
@@ -154,7 +157,7 @@ export class TransferToLayingService extends BaseApiService<
|
||||
|
||||
async getMappedFlockKandangsAvailability(projectFlockId: number) {
|
||||
try {
|
||||
const flockAvailableQty = await this.getAvailabelQty(projectFlockId);
|
||||
const flockAvailableQty = await this.getAvailableQty(projectFlockId);
|
||||
|
||||
const flockKandangsAvailableQty = isResponseSuccess(flockAvailableQty)
|
||||
? flockAvailableQty.data.kandangs
|
||||
@@ -177,6 +180,47 @@ export class TransferToLayingService extends BaseApiService<
|
||||
}
|
||||
}
|
||||
|
||||
async getMaxTargetQty(projectFlockId: number) {
|
||||
try {
|
||||
const availableQtyRes = await httpClient<
|
||||
BaseApiResponse<ProjectFlockMaxQuantity>
|
||||
>(`${this.basePath}/project-flocks/${projectFlockId}/max-target-qty`);
|
||||
|
||||
return availableQtyRes;
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError<BaseApiResponse<ProjectFlockMaxQuantity>>(error)) {
|
||||
return error.response?.data;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
async getMappedFlockKandangsMaxTargetQty(projectFlockId: number) {
|
||||
try {
|
||||
const flockMaxTargetQty = await this.getMaxTargetQty(projectFlockId);
|
||||
|
||||
const flockKandangsMaxTargetQty = isResponseSuccess(flockMaxTargetQty)
|
||||
? flockMaxTargetQty.data.project_flock_kandangs
|
||||
: [];
|
||||
|
||||
const mappedFlockKandangsMaxTargetQty: Record<
|
||||
number,
|
||||
(typeof flockKandangsMaxTargetQty)[0]
|
||||
> = {};
|
||||
|
||||
flockKandangsMaxTargetQty.forEach((item) => {
|
||||
if (!mappedFlockKandangsMaxTargetQty[item.project_flock_kandang_id]) {
|
||||
mappedFlockKandangsMaxTargetQty[item.project_flock_kandang_id] = item;
|
||||
}
|
||||
});
|
||||
|
||||
return mappedFlockKandangsMaxTargetQty;
|
||||
} catch (error) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
async getApprovalHistory(
|
||||
transferToLayingId: number,
|
||||
group: boolean = true,
|
||||
|
||||
+8
@@ -89,6 +89,14 @@ export type ProjectFlockAvailableQuantity = {
|
||||
}[];
|
||||
};
|
||||
|
||||
export type ProjectFlockMaxQuantity = {
|
||||
project_flock_id: number;
|
||||
project_flock_kandangs: {
|
||||
project_flock_kandang_id: number;
|
||||
max_target_qty: number;
|
||||
}[];
|
||||
};
|
||||
|
||||
export type ProjectFlockPeriods = {
|
||||
id: number;
|
||||
name: string;
|
||||
|
||||
Reference in New Issue
Block a user