mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-25 15:55:48 +00:00
refactor(FE): Use chart_data for Uniformity and add week nav
This commit is contained in:
@@ -1,93 +1,86 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import Card from '@/components/Card';
|
import Card from '@/components/Card';
|
||||||
import UniformityBarChart from '@/components/pages/production/uniformity/chart/UniformityBarChart';
|
import UniformityBarChart from '@/components/pages/production/uniformity/chart/UniformityBarChart';
|
||||||
import UniformityGaugeChart from '@/components/pages/production/uniformity/chart/UniformityGaugeChart';
|
import UniformityGaugeChart from '@/components/pages/production/uniformity/chart/UniformityGaugeChart';
|
||||||
import UniformityBarChartSkeleton from '@/components/pages/production/uniformity/skeleton/UniformityBarChartSkeleton';
|
import UniformityBarChartSkeleton from '@/components/pages/production/uniformity/skeleton/UniformityBarChartSkeleton';
|
||||||
import UniformityGaugeChartSkeleton from '@/components/pages/production/uniformity/skeleton/UniformityGaugeChartSkeleton';
|
import UniformityGaugeChartSkeleton from '@/components/pages/production/uniformity/skeleton/UniformityGaugeChartSkeleton';
|
||||||
import {
|
import { Uniformity, type ChartData } from '@/types/api/production/uniformity';
|
||||||
UniformityDetailItem,
|
|
||||||
Uniformity,
|
|
||||||
} from '@/types/api/production/uniformity';
|
|
||||||
|
|
||||||
interface UniformityChartProps {
|
interface UniformityChartProps {
|
||||||
uniformityData?: Uniformity | null;
|
uniformityData?: Uniformity | null;
|
||||||
uniformityDetails?: UniformityDetailItem[];
|
isFiltered?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UniformityChart = ({
|
const UniformityChart = ({
|
||||||
uniformityData,
|
uniformityData,
|
||||||
uniformityDetails,
|
isFiltered = false,
|
||||||
}: UniformityChartProps) => {
|
}: UniformityChartProps) => {
|
||||||
const defaultUniformityDetails: UniformityDetailItem[] = [
|
const [currentWeekIndex, setCurrentWeekIndex] = useState(0);
|
||||||
{ id: 1, weight: 61, range: 'Ideal' },
|
|
||||||
{ id: 2, weight: 62, range: 'Ideal' },
|
|
||||||
{ id: 3, weight: 63, range: 'Ideal' },
|
|
||||||
{ id: 4, weight: 64, range: 'Ideal' },
|
|
||||||
{ id: 5, weight: 65, range: 'Ideal' },
|
|
||||||
{ id: 6, weight: 66, range: 'Ideal' },
|
|
||||||
{ id: 7, weight: 67, range: 'Ideal' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const detailsToUse = uniformityDetails || defaultUniformityDetails;
|
const chartData = useMemo((): ChartData | undefined => {
|
||||||
|
if (!uniformityData?.chart_data) return undefined;
|
||||||
|
return uniformityData.chart_data;
|
||||||
|
}, [uniformityData]);
|
||||||
|
|
||||||
const barChartData = useMemo(() => {
|
const barChartData = useMemo(() => {
|
||||||
if (!uniformityData) {
|
if (!chartData?.bar_chart) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!detailsToUse || detailsToUse.length === 0) {
|
const { bar_chart } = chartData;
|
||||||
|
const currentWeekStr = String(bar_chart.current_week);
|
||||||
|
const weekData = bar_chart.all_weeks[currentWeekStr];
|
||||||
|
|
||||||
|
if (!weekData || !weekData.has_data) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const weights = detailsToUse.map((d) => d.weight);
|
return weekData.weight_distribution.map((range) => ({
|
||||||
const minWeight = Math.floor(Math.min(...weights) / 5) * 5;
|
name: range.range,
|
||||||
const maxWeight = Math.ceil(Math.max(...weights) / 5) * 5;
|
uv: range.bird_count,
|
||||||
|
isIdeal: range.is_ideal_range,
|
||||||
const rangeSize = maxWeight - minWeight < 11 ? 4 : 5;
|
idealCount: range.is_ideal_range
|
||||||
const ranges: string[] = [];
|
? weekData.ideal_range.total_ideal_birds
|
||||||
|
: undefined,
|
||||||
for (let start = minWeight; start <= maxWeight; start += rangeSize) {
|
}));
|
||||||
const end = start + rangeSize;
|
}, [chartData]);
|
||||||
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 {
|
|
||||||
name: range,
|
|
||||||
uv: birdsInRange,
|
|
||||||
isIdeal: hasIdeal,
|
|
||||||
idealCount: hasIdeal ? totalIdealCount : undefined,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}, [uniformityData, detailsToUse]);
|
|
||||||
|
|
||||||
const gaugeChartData = useMemo(() => {
|
const gaugeChartData = useMemo(() => {
|
||||||
if (!uniformityData) return undefined;
|
if (!chartData?.gauge_chart || !uniformityData) return undefined;
|
||||||
|
|
||||||
|
const { gauge_chart } = chartData;
|
||||||
|
const currentWeekData = gauge_chart.available_weeks[currentWeekIndex];
|
||||||
|
|
||||||
|
if (!currentWeekData || !currentWeekData.has_data) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
value: uniformityData.uniformity,
|
value: currentWeekData.uniformity_percentage,
|
||||||
label: 'Uniformity',
|
label: 'Uniformity',
|
||||||
week: `Week ${uniformityData.week}`,
|
week: `Week ${currentWeekData.week}`,
|
||||||
currentValue: uniformityData.uniform_qty,
|
currentValue: currentWeekData.ideal_count,
|
||||||
totalValue: uniformityData.chick_qty_of_weight,
|
totalValue: currentWeekData.total_count,
|
||||||
|
hasPrevWeek: gauge_chart.week_info.has_prev_week,
|
||||||
|
hasNextWeek: gauge_chart.week_info.has_next_week,
|
||||||
};
|
};
|
||||||
}, [uniformityData]);
|
}, [chartData, currentWeekIndex, uniformityData]);
|
||||||
|
|
||||||
|
const handleWeekChange = (direction: 'prev' | 'next') => {
|
||||||
|
if (!chartData?.gauge_chart) return;
|
||||||
|
|
||||||
|
const { available_weeks, week_info } = chartData.gauge_chart;
|
||||||
|
|
||||||
|
if (direction === 'prev' && week_info.has_prev_week) {
|
||||||
|
setCurrentWeekIndex((prev) => Math.max(0, prev - 1));
|
||||||
|
} else if (direction === 'next' && week_info.has_next_week) {
|
||||||
|
setCurrentWeekIndex((prev) =>
|
||||||
|
Math.min(available_weeks.length - 1, prev + 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const shouldShowEmptyState = !isFiltered;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className='w-full grid grid-cols-1 xl:grid-cols-2 2xl:grid-cols-4 gap-4'>
|
<section className='w-full grid grid-cols-1 xl:grid-cols-2 2xl:grid-cols-4 gap-4'>
|
||||||
@@ -100,14 +93,16 @@ const UniformityChart = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className='w-full h-full flex items-center justify-center'>
|
<div className='w-full h-full flex items-center justify-center'>
|
||||||
{!uniformityData || barChartData.length === 0 ? (
|
{shouldShowEmptyState ||
|
||||||
|
!uniformityData ||
|
||||||
|
barChartData.length === 0 ? (
|
||||||
<UniformityBarChartSkeleton />
|
<UniformityBarChartSkeleton />
|
||||||
) : (
|
) : (
|
||||||
<UniformityBarChart data={barChartData} />
|
<UniformityBarChart data={barChartData} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
{!uniformityData || !gaugeChartData ? (
|
{shouldShowEmptyState || !uniformityData || !gaugeChartData ? (
|
||||||
<Card
|
<Card
|
||||||
variant='bordered'
|
variant='bordered'
|
||||||
title='Weekly Performance ⓘ'
|
title='Weekly Performance ⓘ'
|
||||||
@@ -133,6 +128,9 @@ const UniformityChart = ({
|
|||||||
week={gaugeChartData.week}
|
week={gaugeChartData.week}
|
||||||
currentValue={gaugeChartData.currentValue}
|
currentValue={gaugeChartData.currentValue}
|
||||||
totalValue={gaugeChartData.totalValue}
|
totalValue={gaugeChartData.totalValue}
|
||||||
|
onWeekChange={handleWeekChange}
|
||||||
|
hasPrevWeek={gaugeChartData.hasPrevWeek}
|
||||||
|
hasNextWeek={gaugeChartData.hasNextWeek}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -151,8 +151,10 @@ const UniformityConfirmationPreview = ({
|
|||||||
|
|
||||||
const UniformityChartWrapper = ({
|
const UniformityChartWrapper = ({
|
||||||
uniformitySwrKey,
|
uniformitySwrKey,
|
||||||
|
isFiltered,
|
||||||
}: {
|
}: {
|
||||||
uniformitySwrKey: string;
|
uniformitySwrKey: string;
|
||||||
|
isFiltered: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const { data: uniformities } = useSWR(
|
const { data: uniformities } = useSWR(
|
||||||
uniformitySwrKey,
|
uniformitySwrKey,
|
||||||
@@ -166,31 +168,8 @@ const UniformityChartWrapper = ({
|
|||||||
return null;
|
return null;
|
||||||
}, [uniformities]);
|
}, [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 (
|
return (
|
||||||
<UniformityChart
|
<UniformityChart uniformityData={uniformityData} isFiltered={isFiltered} />
|
||||||
uniformityData={uniformityData}
|
|
||||||
uniformityDetails={uniformityDetails}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -897,7 +876,10 @@ const UniformityTable = () => {
|
|||||||
<div className='my-4 divider'></div>
|
<div className='my-4 divider'></div>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<UniformityChartWrapper uniformitySwrKey={uniformitySwrKey} />
|
<UniformityChartWrapper
|
||||||
|
uniformitySwrKey={uniformitySwrKey}
|
||||||
|
isFiltered={isSubmitted}
|
||||||
|
/>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<Card
|
<Card
|
||||||
|
|||||||
+19
-13
@@ -2,26 +2,32 @@ import { BaseMetadata } from '@/types/api/api-general';
|
|||||||
import { BaseApproval } from '@/types/api/approval/approval';
|
import { BaseApproval } from '@/types/api/approval/approval';
|
||||||
|
|
||||||
// ==================== CHART DATA TYPES ====================
|
// ==================== CHART DATA TYPES ====================
|
||||||
export type WeightDistributionData = {
|
export type WeightDistributionRange = {
|
||||||
weight: number;
|
range: string;
|
||||||
count: number;
|
min_weight: number;
|
||||||
|
max_weight: number;
|
||||||
|
bird_count: number;
|
||||||
|
is_ideal_range: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type IdealRange = {
|
||||||
|
min_weight: number;
|
||||||
|
max_weight: number;
|
||||||
|
total_ideal_birds: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type StatisticsData = {
|
export type StatisticsData = {
|
||||||
mean: number;
|
min_weight: number;
|
||||||
standardDeviation: number;
|
max_weight: number;
|
||||||
min: number;
|
average_weight: number;
|
||||||
max: number;
|
total_birds_measured: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type WeekBarChartData = {
|
export type WeekBarChartData = {
|
||||||
has_data: boolean;
|
has_data: boolean;
|
||||||
weight_distribution: WeightDistributionData[];
|
weight_distribution: WeightDistributionRange[];
|
||||||
ideal_range: {
|
ideal_range: IdealRange;
|
||||||
min: number;
|
statistics: StatisticsData;
|
||||||
max: number;
|
|
||||||
} | null;
|
|
||||||
statistics: StatisticsData | null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BarChart = {
|
export type BarChart = {
|
||||||
|
|||||||
Reference in New Issue
Block a user