refactor(FE): export charts with hidden html renderer and adding package html-to-image

This commit is contained in:
randy-ar
2026-01-18 18:57:10 +07:00
parent c576933ba2
commit a9c22d778b
4 changed files with 768 additions and 44 deletions
@@ -1,8 +1,10 @@
import Button from '@/components/Button';
import Card from '@/components/Card';
import Dropdown from '@/components/Dropdown';
import { OptionType } from '@/components/input/SelectInput';
import Menu from '@/components/menu/Menu';
import MenuItem from '@/components/menu/MenuItem';
import { formatNumber } from '@/lib/helper';
import {
Dashboard,
DashboardOverviewCharts,
@@ -25,13 +27,20 @@ import {
type DashboardLineChartProps = {
analysisMode: 'OVERVIEW' | 'COMPARISON';
data: Dashboard;
selectedKandang?: OptionType;
};
// Type guard to check if charts is DashboardOverviewCharts
function isOverviewCharts(
charts: DashboardOverviewCharts | DashboardComparisonCharts
): charts is DashboardOverviewCharts {
return 'deplesi' in charts;
return (
'deplesi' in charts ||
'body_weight' in charts ||
'fcr' in charts ||
'performance' in charts ||
'quality_control' in charts
);
}
// Type guard to check if charts is DashboardComparisonCharts
@@ -94,6 +103,7 @@ const getLineColor = (
const DashboardLineChart = ({
analysisMode,
data,
selectedKandang,
}: DashboardLineChartProps) => {
const [chartData, setChartData] =
useState<keyof DashboardOverviewCharts>('body_weight');
@@ -452,11 +462,84 @@ const DashboardLineChart = ({
labelStyle={{ color: 'white', marginBottom: '4px' }}
itemStyle={{ color: 'white', fontSize: '12px' }}
labelFormatter={(value) => `Week ${value}`}
content={(props) => {
return (
<div className='flex flex-col gap-2 rounded-lg bg-neutral-950 p-4 text-white'>
<p className='text-neutral-300 text-xs font-semibold text-start'>
{analysisMode === 'OVERVIEW'
? selectedKandang
? selectedKandang.label || 'Overview Performance'
: 'Overview Performance'
: 'Comparison Performance'}
</p>
<ul className='flex flex-col gap-1'>
{props.payload.map((item, index) => {
if (item.name.startsWith('STD. ')) return null;
// Get series data to find the unit
let seriesData: DashboardChartsSeries[] = [];
if (
analysisMode === 'OVERVIEW' &&
isOverviewCharts(data.charts)
) {
seriesData = data.charts[chartData]?.series || [];
} else if (
analysisMode === 'COMPARISON' &&
isComparisonCharts(data.charts)
) {
const comparisonChart =
data.charts.farm ||
data.charts.flock ||
data.charts.kandang;
seriesData = comparisonChart?.series || [];
}
// Find the series that matches this line's name
const series = seriesData.find(
(s) => s.label === item.name
);
const color = series?.id
? getLineColor(series.id, index, analysisMode)
: '#9ca3af';
const unit = series?.unit;
return (
<li
key={item.name}
className='flex w-full justify-between items-center flex-row gap-6 p-0'
>
<span className='flex flex-row gap-1 items-center'>
<div
className='h-4 w-4 m-0 rounded-md'
style={{
backgroundColor: color,
}}
></div>
<div className='m-0'>
{formatNumber(item.value)}
{unit}
</div>
</span>
<span className='m-0'>{item.name}</span>
</li>
);
})}
</ul>
<p className='text-neutral-300 text-xs text-start'>
Week {props.label}
</p>
</div>
);
}}
formatter={(
value: number | undefined,
name: string | undefined
) => {
if (value === undefined || name === undefined) return ['', ''];
if (
value === undefined ||
name === undefined ||
name.startsWith('STD. ')
)
return [undefined, undefined];
// Get series data to find the unit
let seriesData: DashboardChartsSeries[] = [];
@@ -478,9 +561,9 @@ const DashboardLineChart = ({
// Find the series that matches this line's name
const series = seriesData.find((s) => s.label === name);
const unit = series?.unit || '';
const id = series?.id || '';
return [`${value} ${unit}`, name];
return [value, id];
}}
/>
{/* Dynamic Line rendering based on visible series */}