but i had a system built in this highcharts version and that error wasnt there at all
'use client';
import React from "react";
import Highcharts from "highcharts";
import HighchartsReact from "highcharts-react-official";
import moment from "moment";
// Import required Highcharts modules
import "highcharts/modules/exporting";
import "highcharts/modules/export-data";
import "highcharts/modules/full-screen";
import "highcharts/modules/no-data-to-display";
import "highcharts/modules/accessibility";
// these move hand in hand
import "highcharts/highcharts-more";
import "highcharts/modules/dumbbell";
import "highcharts/modules/lollipop";
// // ensure this comes last
// import "highcharts/modules/drilldown";
interface Product {
id: string;
name: string;
sply_sales: number;
actual_sales: number;
target_sales: number;
}
interface SubCategory {
id: string;
name: string;
sply_sales: number;
actual_sales: number;
target_sales: number;
products: Product[];
}
interface Category {
id: string;
name: string;
sply_sales: number;
actual_sales: number;
target_sales: number;
subCategories: SubCategory[];
}
interface Agent {
id: string;
name: string;
sply_sales: number;
actual_sales: number;
target_sales: number;
categories: Category[];
}
interface AgentData {
agents: Agent[];
}
interface AgentDrillChartProps {
data: AgentData;
requestParams: any;
measure: "sales_value" | "quantity";
}
const AgentDrillChart: React.FC<AgentDrillChartProps> = ({
data,
requestParams,
measure,
}) => {
const formatArray = (arr: any[], key: string): string => {
if (!Array.isArray(arr) || arr.length === 0) return "";
return arr.map((item) => item[key]).join(", ");
};
const formatFilters = (dataFilters: any): string => {
const startDate = dataFilters?.start_date ? moment(dataFilters.start_date).format("MMMM Do, YYYY") : null;
const endDate = dataFilters?.end_date ? moment(dataFilters.end_date).format("MMMM Do, YYYY") : null;
const agents = formatArray(dataFilters?.agents, "name");
const salesAssociates = formatArray(dataFilters?.salesAssociates, "name");
const regions = formatArray(dataFilters?.regions, "name");
const territories = formatArray(dataFilters?.territories, "name");
const routes = formatArray(dataFilters?.routes, "name");
const statuses = formatArray(dataFilters?.statuses, "label");
const orderBy = dataFilters?.orderBy?.label;
const dataLimit = dataFilters?.dataLimit?.label;
const dataLimitNumber = dataFilters?.dataLimitNumber;
const viewInterval = dataFilters?.viewInterval?.label;
const productCategories = formatArray(dataFilters?.categories, "name");
const productSubCategories = formatArray(dataFilters?.subcategories, "name");
const products = formatArray(dataFilters?.products, "name");
let sentence = "Filters: ";
if (agents) sentence += `Distributors: ${agents}. `;
if (salesAssociates) sentence += `Sales Associates: ${salesAssociates}. `;
if (regions) sentence += `Regions: ${regions}. `;
if (territories) sentence += `Territories: ${territories}. `;
if (routes) sentence += `Routes: ${routes}. `;
if (statuses) sentence += `Statuses: ${statuses}. `;
if (orderBy) sentence += `Order By: ${orderBy}. `;
if (dataLimit) sentence += `Data Limit: ${dataLimit}. `;
if (dataLimitNumber) sentence += `Data Limit No: ${dataLimitNumber}. `;
if (viewInterval) sentence += `View Interval: ${viewInterval}. `;
if (productCategories) sentence += `Product Categories: ${productCategories}. `;
if (productSubCategories) sentence += `Product Sub Categories: ${productSubCategories}. `;
if (products) sentence += `Products: ${products}. `;
if (startDate) sentence += `Start Date: ${startDate}. `;
if (endDate) sentence += `End Date: ${endDate}.`;
return sentence.trim();
};
const calculatePercentageChange = (actual: number, comparison: number): number => {
return ((actual - comparison) / comparison) * 100;
};
const formatData = (items: any[], level: string) => {
return items.map((item) => ({
name: item.name,
sply: item.sply_sales,
actual: item.actual_sales,
target: item.target_sales,
drilldown: level !== "products" ? item.id : undefined,
}));
};
const createDrilldownSeries = (items: any[], level: string) => {
return items.map((item) => {
const nextLevel = level === "agents" ? "categories" : level === "categories" ? "subCategories" : "products";
const hasNextLevel = item[nextLevel] && item[nextLevel].length > 0;
const drilldownData = hasNextLevel ? formatData(item[nextLevel], nextLevel) : [];
return {
id: item.id,
data: drilldownData,
drilldown: hasNextLevel,
};
});
};
const createPercentageChangeSeries = (items: any[], comparisonType: string): any => {
return {
type: "lollipop",
name: `Percentage Change Actual vs. ${comparisonType}`,
data: items.map((item) => {
const percentageChange = calculatePercentageChange(item.actual_sales, item[`${comparisonType.toLowerCase()}_sales`]);
return {
name: item.name,
y: percentageChange,
color: percentageChange < 90 ? "#FF0000" : percentageChange > 100 ? "#008000" : "#FFBF00",
};
}),
yAxis: 1,
tooltip: {
pointFormat: "{series.name}: <b>{point.y:,.0f}%</b>",
},
marker: {
enabled: true,
lineColor: undefined,
symbol: "circle",
},
dataLabels: {
enabled: true,
format: "{point.y:,.0f}%",
},
showInLegend: false,
};
};
const chartData = data?.agents || [];
const chartTitle = formatFilters(requestParams);
const drilldownSeries = [
...createDrilldownSeries(chartData, "agents"),
...chartData.flatMap((item) => createDrilldownSeries(item.categories, "categories")),
...chartData.flatMap((item) => item.categories.flatMap((category) => createDrilldownSeries(category.subCategories, "subCategories"))),
];
const options: Highcharts.Options = {
chart: {
type: "bar",
height: 800,
scrollablePlotArea: {
minHeight: 500,
scrollPositionX: 1,
},
events: {
drilldown: function (e: any) {
if (!e.seriesOptions) {
const chart: any = this;
const series = chart.userOptions.drilldown?.series?.find((s: any) => s.id === e.point.drilldown);
if (series && series.data && series.data.length > 0) {
const splyData = series?.data.map((item: any) => ({ name: item.name, y: item.sply, drilldown: item.drilldown }));
const actualData = series?.data.map((item: any) => ({ name: item.name, y: item.actual, drilldown: item.drilldown }));
const targetData = series?.data.map((item: any) => ({ name: item.name, y: item.target, drilldown: item.drilldown }));
chart.addSingleSeriesAsDrilldown(e.point, {
name: "SPLY Sales",
data: splyData,
color: Highcharts.getOptions().colors?.[0],
});
chart.addSingleSeriesAsDrilldown(e.point, {
name: "Actual Sales",
data: actualData,
color: Highcharts.getOptions().colors?.[1],
});
chart.addSingleSeriesAsDrilldown(e.point, {
name: "Target Sales",
data: targetData,
color: Highcharts.getOptions().colors?.[2],
});
chart.addSingleSeriesAsDrilldown(e.point, {
type: "lollipop",
name: "Percentage Change Actual vs. SPLY",
data: series.data.map((item: any) => {
const percentageChange = calculatePercentageChange(item.actual, item.sply);
return {
name: item.name,
y: percentageChange,
color: percentageChange < 90 ? "#FF0000" : percentageChange > 100 ? "#008000" : "#FFBF00",
};
}),
yAxis: 1,
tooltip: {
pointFormat: "{series.name}: <b>{point.y:,.0f}%</b>",
},
marker: {
enabled: true,
lineColor: undefined,
symbol: "circle",
},
dataLabels: {
enabled: true,
format: "{point.y:,.0f}%",
},
showInLegend: false,
});
chart.addSingleSeriesAsDrilldown(e.point, {
type: "lollipop",
name: "Percentage Change Actual vs. Target",
data: series.data.map((item: any) => {
const percentageChange = calculatePercentageChange(item.actual, item.target);
return {
name: item.name,
y: percentageChange,
color: percentageChange < 90 ? "#FF0000" : percentageChange > 100 ? "#008000" : "#FFBF00",
};
}),
yAxis: 1,
tooltip: {
pointFormat: "{series.name}: <b>{point.y:,.0f}%</b>",
},
marker: {
enabled: true,
lineColor: undefined,
symbol: "circle",
},
dataLabels: {
enabled: true,
format: "{point.y:,.0f}%",
},
showInLegend: false,
});
chart.applyDrilldown();
} else {
e.preventDefault();
}
}
},
},
},
accessibility: {
enabled: true,
keyboardNavigation: {
enabled: false,
},
},
title: {
text: `Agents Sales Performance <br/> ${chartTitle}`,
},
xAxis: {
type: "category",
uniqueNames: false,
},
yAxis: [
{
title: {
text: "Sales",
},
width: "60%",
lineWidth: 2,
offset: 0,
labels: {
formatter: function () {
const value = this.value as number;
if (value >= 1000000000) {
return value / 1000000000 + "b";
}
if (value >= 1000000) {
return value / 1000000 + "m";
}
if (value >= 1000) {
return value / 1000 + "k";
}
return String(value);
},
},
},
{
title: {
text: "Percentage Change",
},
left: "65%",
width: "30%",
offset: 0,
lineWidth: 2,
},
],
legend: {
enabled: true,
},
plotOptions: {
series: {
borderWidth: 0,
dataLabels: {
enabled: true,
format: "{point.y:,.0f}",
style: {
color: "#000000",
},
},
point: {
events: {
click: function () {
if (!(this as any).drilldown) {
return false;
}
},
},
},
},
},
credits: {
enabled: false,
},
noData: {
style: {
fontWeight: "bold",
fontSize: "16px",
color: "#303030",
},
position: {
align: "center",
verticalAlign: "middle",
},
},
series: [
{
type: "bar",
name: "SPLY Sales",
data: chartData.map((item) => ({
name: item.name,
y: item.sply_sales,
drilldown: item.id,
color: Highcharts.getOptions().colors?.[0],
})),
tooltip: {
pointFormat: "{series.name}: <b>{point.y:,.0f}</b>",
},
},
{
type: "bar",
name: "Actual Sales",
data: chartData.map((item) => ({
name: item.name,
y: item.actual_sales,
drilldown: item.id,
color: Highcharts.getOptions().colors?.[1],
})),
tooltip: {
pointFormat: "{series.name}: <b>{point.y:,.0f}</b>",
},
},
{
type: "bar",
name: "Target Sales",
data: chartData.map((item) => ({
name: item.name,
y: item.target_sales,
drilldown: item.id,
color: Highcharts.getOptions().colors?.[2],
})),
tooltip: {
pointFormat: "{series.name}: <b>{point.y:,.0f}</b>",
},
},
createPercentageChangeSeries(chartData, "SPLY"),
createPercentageChangeSeries(chartData, "Target"),
],
drilldown: {
allowPointDrilldown: false,
breadcrumbs: {
position: {
align: "right",
},
},
activeDataLabelStyle: {
color: "#000000",
textDecoration: "none",
textOutline: "1px contrast",
},
series: drilldownSeries as any,
},
};
return (
<div>
<HighchartsReact highcharts={Highcharts} options={options} immutable={true} />
</div>
);
};
export default AgentDrillChart;
"use client";
import React, { useState } from "react";
import { Tooltip } from "primereact/tooltip";
import { Button } from "primereact/button";
import { useQuery } from "@tanstack/react-query";
import moment from "moment";
import SelloutFiltersButton, { selectedFiltersType, } from "@/components/admin-panel/filters/sellout/SelloutFiltersButton";
import RobotProcessorLottie from "@/assets/nice-lotties/robot-processor-lottie.json";
import MaterialUiLoaderLottie from "@/assets/nice-lotties/material-ui-loading-lottie.json";
import SnailErrorLottie from "@/assets/nice-lotties/snail-error-lottie.json";
import useHandleQueryError from "@/hooks/useHandleQueryError";
import { getSelloutAgentPdtDrillSVSAVsTChart } from "@/services/dashboard/sellout/barcharts-service";
import dynamic from "next/dynamic";
const Lottie = dynamic(() => import("lottie-react"), { ssr: false });
const AgentDrillChart = dynamic(() => import("./charts/AgentDrillChart"), {
ssr: false,
loading: () => (
<div className="flex justify-center items-center h-96">
<span className="text-gray-500 dark:text-gray-400">Loading chart...</span>
</div>
)
});
interface AgentWithProductDrillPerformanceChartProps {
measure?: "sales_value" | "quantity";
}
const AgentWithProductDrillPerformanceChart: React.FC<AgentWithProductDrillPerformanceChartProps> = ({
measure = "sales_value",
}) => {
const [selectedFilters, setSelectedFilters] = useState<selectedFiltersType>({
start_date: moment().startOf("year").toDate(),
end_date: moment().toDate(),
year: null,
months: [],
categories: [],
subcategories: [],
products: [],
channels: [],
regions: [],
territories: [],
routes: [],
order_by: "Ascending",
data_limit: 5,
view_interval: "Default",
});
const getSelloutAgentPdtDrillSVSAVsTChartQuery = useQuery({
queryKey: [
"sellout-agent-drill-performance",
measure,
...Object.values(selectedFilters),
],
queryFn: () =>
getSelloutAgentPdtDrillSVSAVsTChart({
measure: measure,
...selectedFilters,
}),
});
useHandleQueryError(getSelloutAgentPdtDrillSVSAVsTChartQuery);
if (getSelloutAgentPdtDrillSVSAVsTChartQuery?.isPending) {
return (
<div className="col-span-12">
<div className="w-full flex items-center justify-center">
<div style={{ maxWidth: "400px" }}>
<Lottie animationData={RobotProcessorLottie} style={{ height: "300px" }} loop={true} />
<Lottie
animationData={MaterialUiLoaderLottie}
style={{ height: "50px" }}
loop={true}
/>
</div>
</div>
</div>
);
}
if (getSelloutAgentPdtDrillSVSAVsTChartQuery?.isError) {
return (
<div className="w-full flex items-center justify-center">
<div style={{ maxWidth: "400px" }}>
<Lottie animationData={SnailErrorLottie} loop={true} />
</div>
</div>
);
}
const seriesData = getSelloutAgentPdtDrillSVSAVsTChartQuery?.data?.data?.data;
const requestParams = getSelloutAgentPdtDrillSVSAVsTChartQuery?.data?.data?.requestParams;
return (
<>
<div className="col-span-12 text-right mb-3">
<SelloutFiltersButton
selectedFilters={selectedFilters}
setSelectedFilters={setSelectedFilters}
sectionsToShow={{
dateRange: true,
products: true,
administrative: true,
dataLimits: true,
}}
/>
</div>
<div className="overflow-y-auto" style={{ height: "400px" }}>
<AgentDrillChart
data={seriesData}
requestParams={requestParams}
measure={measure}
/>
</div>
</>
);
};
export default AgentWithProductDrillPerformanceChart;
these are the packages am using
im facing this error when am working with highcharts in next js
but i realised its related to the drill down
and what i realised the moment i leave this line uncomented the error happens if i comment it it the error nolonger happens
but i had a system built in this highcharts version and that error wasnt there at all
here is my full code
Here is how i use the component
any advice or clarity on this issue will highly be appreciated