mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
refactor(FE): Refactor Uniformity charts to use API data
This commit is contained in:
@@ -4,89 +4,20 @@ import UniformityBarChart from '@/components/pages/production/uniformity/chart/U
|
||||
import UniformityGaugeChart from '@/components/pages/production/uniformity/chart/UniformityGaugeChart';
|
||||
import UniformityBarChartSkeleton from '@/components/pages/production/uniformity/skeleton/UniformityBarChartSkeleton';
|
||||
import UniformityGaugeChartSkeleton from '@/components/pages/production/uniformity/skeleton/UniformityGaugeChartSkeleton';
|
||||
import { UniformityDetailItem } from '@/types/api/production/uniformity';
|
||||
|
||||
interface BarChartData {
|
||||
name: string;
|
||||
uv: number;
|
||||
isIdeal?: boolean;
|
||||
}
|
||||
|
||||
interface GaugeChartData {
|
||||
value: number;
|
||||
label: string;
|
||||
kandang?: string;
|
||||
week?: string;
|
||||
currentValue?: number;
|
||||
totalValue?: number;
|
||||
}
|
||||
import {
|
||||
UniformityDetailItem,
|
||||
Uniformity,
|
||||
} from '@/types/api/production/uniformity';
|
||||
|
||||
interface UniformityChartProps {
|
||||
barChartData?: BarChartData[];
|
||||
gaugeChartData?: GaugeChartData;
|
||||
uniformityData?: Uniformity | null;
|
||||
uniformityDetails?: UniformityDetailItem[];
|
||||
}
|
||||
|
||||
const UniformityChart = ({
|
||||
barChartData: initialBarChartData,
|
||||
gaugeChartData: initialGaugeChartData,
|
||||
uniformityData,
|
||||
uniformityDetails,
|
||||
}: UniformityChartProps) => {
|
||||
const defaultBarChartData: BarChartData[] = [
|
||||
{
|
||||
name: '48-52',
|
||||
uv: 80,
|
||||
},
|
||||
{
|
||||
name: '52-56',
|
||||
uv: 120,
|
||||
},
|
||||
{
|
||||
name: '56-60',
|
||||
uv: 160,
|
||||
},
|
||||
{
|
||||
name: '60-64',
|
||||
uv: 200,
|
||||
},
|
||||
{
|
||||
name: '64-68',
|
||||
uv: 160,
|
||||
},
|
||||
{
|
||||
name: '68-72',
|
||||
uv: 120,
|
||||
},
|
||||
{
|
||||
name: '72-76',
|
||||
uv: 80,
|
||||
},
|
||||
{
|
||||
name: '76-80',
|
||||
uv: 120,
|
||||
},
|
||||
{
|
||||
name: '84-88',
|
||||
uv: 160,
|
||||
},
|
||||
{
|
||||
name: '88-92',
|
||||
uv: 200,
|
||||
},
|
||||
{
|
||||
name: '92-96',
|
||||
uv: 160,
|
||||
},
|
||||
];
|
||||
|
||||
const defaultGaugeChartData: GaugeChartData = {
|
||||
value: 52,
|
||||
label: 'Uniformity',
|
||||
week: 'Week 2',
|
||||
currentValue: 512,
|
||||
totalValue: 1024,
|
||||
};
|
||||
|
||||
const defaultUniformityDetails: UniformityDetailItem[] = [
|
||||
{ id: 1, weight: 61, range: 'Ideal' },
|
||||
{ id: 2, weight: 62, range: 'Ideal' },
|
||||
@@ -100,34 +31,63 @@ const UniformityChart = ({
|
||||
const detailsToUse = uniformityDetails || defaultUniformityDetails;
|
||||
|
||||
const barChartData = useMemo(() => {
|
||||
const dataToProcess = initialBarChartData || defaultBarChartData;
|
||||
|
||||
if (!detailsToUse || detailsToUse.length === 0) {
|
||||
return dataToProcess;
|
||||
if (!uniformityData) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return dataToProcess.map((bar) => {
|
||||
const rangeMatch = bar.name.match(/(\d+)-(\d+)/);
|
||||
if (!rangeMatch) return bar;
|
||||
if (!detailsToUse || detailsToUse.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const minWeight = parseInt(rangeMatch[1], 10);
|
||||
const maxWeight = parseInt(rangeMatch[2], 10);
|
||||
const weights = detailsToUse.map((d) => d.weight);
|
||||
const minWeight = Math.floor(Math.min(...weights) / 5) * 5;
|
||||
const maxWeight = Math.ceil(Math.max(...weights) / 5) * 5;
|
||||
|
||||
const hasIdealWeight = detailsToUse.some((detail) => {
|
||||
const weight = detail.weight;
|
||||
return (
|
||||
detail.range === 'Ideal' && weight >= minWeight && weight <= maxWeight
|
||||
);
|
||||
});
|
||||
const rangeSize = maxWeight - minWeight < 11 ? 4 : 5;
|
||||
const ranges: string[] = [];
|
||||
|
||||
for (let start = minWeight; start <= maxWeight; start += rangeSize) {
|
||||
const end = start + rangeSize;
|
||||
ranges.push(`${start}-${end}`);
|
||||
}
|
||||
|
||||
const totalIdealCount = detailsToUse.filter(
|
||||
(d) => d.range === 'Ideal'
|
||||
).length;
|
||||
|
||||
return ranges.map((range) => {
|
||||
const [minStr, maxStr] = range.split('-').map(Number);
|
||||
const min = minStr;
|
||||
const max = maxStr;
|
||||
|
||||
const birdsInRange = detailsToUse.filter(
|
||||
(d) => d.weight >= min && d.weight < max
|
||||
).length;
|
||||
|
||||
const hasIdeal = detailsToUse.some(
|
||||
(d) => d.range === 'Ideal' && d.weight >= min && d.weight < max
|
||||
);
|
||||
|
||||
return {
|
||||
...bar,
|
||||
isIdeal: hasIdealWeight,
|
||||
name: range,
|
||||
uv: birdsInRange,
|
||||
isIdeal: hasIdeal,
|
||||
idealCount: hasIdeal ? totalIdealCount : undefined,
|
||||
};
|
||||
});
|
||||
}, [initialBarChartData, detailsToUse]);
|
||||
}, [uniformityData, detailsToUse]);
|
||||
|
||||
const gaugeChartData = initialGaugeChartData || defaultGaugeChartData;
|
||||
const gaugeChartData = useMemo(() => {
|
||||
if (!uniformityData) return undefined;
|
||||
|
||||
return {
|
||||
value: uniformityData.uniformity,
|
||||
label: 'Uniformity',
|
||||
week: `Week ${uniformityData.week}`,
|
||||
currentValue: uniformityData.uniform_qty,
|
||||
totalValue: uniformityData.chick_qty_of_weight,
|
||||
};
|
||||
}, [uniformityData]);
|
||||
|
||||
return (
|
||||
<section className='w-full grid grid-cols-1 xl:grid-cols-2 2xl:grid-cols-4 gap-4'>
|
||||
@@ -140,14 +100,14 @@ const UniformityChart = ({
|
||||
}}
|
||||
>
|
||||
<div className='w-full h-full flex items-center justify-center'>
|
||||
{barChartData.length === 0 ? (
|
||||
{!uniformityData || barChartData.length === 0 ? (
|
||||
<UniformityBarChartSkeleton />
|
||||
) : (
|
||||
<UniformityBarChart data={barChartData} />
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
{gaugeChartData && gaugeChartData.value === 0 ? (
|
||||
{!uniformityData || !gaugeChartData ? (
|
||||
<Card
|
||||
variant='bordered'
|
||||
title='Weekly Performance ⓘ'
|
||||
@@ -158,7 +118,7 @@ const UniformityChart = ({
|
||||
>
|
||||
<UniformityGaugeChartSkeleton />
|
||||
</Card>
|
||||
) : gaugeChartData ? (
|
||||
) : (
|
||||
<Card
|
||||
variant='bordered'
|
||||
title='Weekly Performance ⓘ'
|
||||
@@ -175,7 +135,7 @@ const UniformityChart = ({
|
||||
totalValue={gaugeChartData.totalValue}
|
||||
/>
|
||||
</Card>
|
||||
) : null}
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@ import { UniformityApi } from '@/services/api/uniformity';
|
||||
import {
|
||||
DetailOptionType,
|
||||
type Uniformity,
|
||||
type UniformityDetail,
|
||||
} from '@/types/api/production/uniformity';
|
||||
import { isResponseSuccess } from '@/lib/api-helper';
|
||||
import { type BaseApiResponse } from '@/types/api/api-general';
|
||||
@@ -79,7 +80,7 @@ const UniformityConfirmationPreview = ({
|
||||
{
|
||||
id: 'file-uniformity',
|
||||
label: 'File Uniformity',
|
||||
value: '-', // File name tidak tersedia di GET ALL response
|
||||
value: '-',
|
||||
},
|
||||
{
|
||||
id: 'status',
|
||||
@@ -136,6 +137,51 @@ const UniformityConfirmationPreview = ({
|
||||
);
|
||||
};
|
||||
|
||||
const UniformityChartWrapper = ({
|
||||
uniformitySwrKey,
|
||||
}: {
|
||||
uniformitySwrKey: string;
|
||||
}) => {
|
||||
const { data: uniformities } = useSWR(
|
||||
uniformitySwrKey,
|
||||
UniformityApi.getAllFetcher
|
||||
);
|
||||
|
||||
const uniformityData = useMemo(() => {
|
||||
if (isResponseSuccess(uniformities) && uniformities?.data?.length > 0) {
|
||||
return uniformities.data[0];
|
||||
}
|
||||
return null;
|
||||
}, [uniformities]);
|
||||
|
||||
const shouldFetchDetails = !!uniformityData;
|
||||
const uniformityDetailSwrKey = useMemo(() => {
|
||||
if (!uniformityData) return null;
|
||||
return `${UniformityApi.basePath}/${uniformityData.id}?with_details=true`;
|
||||
}, [uniformityData]);
|
||||
|
||||
const { data: uniformityDetailResponse } = useSWR(
|
||||
uniformityDetailSwrKey,
|
||||
shouldFetchDetails ? UniformityApi.getAllFetcher : null
|
||||
);
|
||||
|
||||
const uniformityDetails = useMemo(() => {
|
||||
if (shouldFetchDetails && isResponseSuccess(uniformityDetailResponse)) {
|
||||
const detailData =
|
||||
uniformityDetailResponse.data as unknown as UniformityDetail;
|
||||
return detailData.uniformity_details;
|
||||
}
|
||||
return undefined;
|
||||
}, [shouldFetchDetails, uniformityDetailResponse]);
|
||||
|
||||
return (
|
||||
<UniformityChart
|
||||
uniformityData={uniformityData}
|
||||
uniformityDetails={uniformityDetails}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const UniformityTable = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
@@ -830,7 +876,7 @@ const UniformityTable = () => {
|
||||
<div className='my-4 divider'></div>
|
||||
|
||||
<section>
|
||||
<UniformityChart />
|
||||
<UniformityChartWrapper uniformitySwrKey={uniformitySwrKey} />
|
||||
</section>
|
||||
|
||||
<Card
|
||||
|
||||
@@ -27,6 +27,7 @@ interface BarChartData {
|
||||
name: string;
|
||||
uv: number;
|
||||
isIdeal?: boolean;
|
||||
idealCount?: number;
|
||||
}
|
||||
|
||||
interface UniformityBarChartProps {
|
||||
@@ -35,7 +36,25 @@ interface UniformityBarChartProps {
|
||||
|
||||
function CustomTooltip({ payload, label, active }: CustomTooltipProps) {
|
||||
if (active && payload && payload.length && label !== undefined) {
|
||||
const data = payload[0] as unknown as { payload: BarChartData };
|
||||
const chartData = data.payload as BarChartData;
|
||||
const labelStr = String(label);
|
||||
|
||||
if (chartData.isIdeal && chartData.idealCount !== undefined) {
|
||||
return (
|
||||
<div className='bg-[#18181B] p-2.5 shadow-sm text-white rounded-2xl rounded-bl-none'>
|
||||
<p className='m-0 font-bold text-white/50'>Uniformity 2025</p>
|
||||
<div className='flex items-center gap-2 mt-2 justify-between'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<div className='w-5 h-5 bg-[#0069E0] rounded-md'></div>
|
||||
{chartData.idealCount} of Birds
|
||||
</div>
|
||||
<span>{labelStr}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='bg-[#18181B] p-2.5 shadow-sm text-white rounded-2xl rounded-bl-none'>
|
||||
<p className='m-0 font-bold text-white/50'>Uniformity 2025</p>
|
||||
|
||||
Reference in New Issue
Block a user