web-humas-fe/components/main/dashboard/chart/dynamic-bar-char.tsx

370 lines
9.3 KiB
TypeScript

import React, { useState, useCallback, useEffect, useRef } from "react";
import ReactApexChart from "react-apexcharts";
import ApexCharts from "apexcharts";
import { init } from "next/dist/compiled/webpack/webpack";
const wilayah = ["sumut", "bali", "jateng", "jabar", "metro", "papua", "riau"];
type TypeItem = {
year: number;
month: number;
user: {
list: { name: string; count: number }[];
}[];
};
interface MappedData {
result: { x: string; y: number }[] | [];
details: { name: string; count: number }[][];
}
const generateDummyData = () => {
const getDaysInMonth = (year: number, month: number) => {
return new Date(year, month, 0).getDate();
};
const dummy = [];
for (let month = 1; month <= 7; month++) {
const userData = [];
const daysInMonth = getDaysInMonth(2025, month);
for (let day = 1; day <= daysInMonth; day++) {
// Buat 2-4 wilayah acak per hari
const regionCount = Math.floor(Math.random() * 3) + 2;
const usedRegions = wilayah
.sort(() => 0.5 - Math.random())
.slice(0, regionCount);
const list = usedRegions.map((name) => ({
name,
count: Math.floor(Math.random() * 10) + 1,
}));
userData.push({ list });
}
dummy.push({
year: 2025,
month,
user: userData,
});
}
return dummy;
};
const dummy = generateDummyData();
const colors = ["#008FFB"];
export const makeDataByMonth = (
data: TypeItem[],
month: number
): MappedData => {
const result: { x: string; y: number }[] = [];
const details: { name: string; count: number }[][] = [];
const filtered = data.find((entry) => entry.month === month);
if (!filtered) return { result: [], details: [] };
filtered.user.forEach((u, idx) => {
const total = u.list.reduce((sum, item) => sum + item.count, 0);
details.push(u.list);
console.log("u.list", u.list);
result.push({
x: (idx + 1).toString(),
y: total,
});
});
return { result, details };
};
export const makeDataByRange = (
data: TypeItem[],
startMonth: number,
startDay: number,
endMonth: number,
endDay: number
) => {
const user: number[] = [];
const labels: string[] = [];
const details = [];
const result: { x: string; y: number }[] = [];
const sortedData = data.sort((a, b) => a.month - b.month);
for (const monthData of sortedData) {
const { month, user: u } = monthData;
if (month < startMonth || month > endMonth) continue;
let startIndex = 0;
let endIndex = u.length - 1;
if (month === startMonth) startIndex = startDay - 1;
if (month === endMonth) endIndex = endDay - 1;
for (let i = startIndex; i <= endIndex; i++) {
const userEntry = u[i];
if (!userEntry) continue;
const total = userEntry.list.reduce((sum, item) => sum + item.count, 0);
user.push(total);
details.push(userEntry.list);
const label = `${(i + 1).toString().padStart(2, "0")} - ${month
.toString()
.padStart(2, "0")}`;
labels.push(label);
}
}
for (let i = 0; i < user.length; i++) {
result.push({ x: labels[i], y: user[i] });
}
return { result, details };
};
const ApexChartDynamic = (props: {
type: string;
date: string;
range: { start: any; end: any };
}) => {
const { date, type, range } = props;
const [state, setState] = useState<{
series: ApexAxisChartSeries;
options: ApexCharts.ApexOptions;
seriesQuarter: ApexAxisChartSeries;
}>({
series: [{ data: [] }],
options: {
chart: {
id: "barYear",
height: 600,
width: "100%",
type: "bar",
events: {
dataPointSelection: function (e, chart, opts) {
const quarterChartEl = document.querySelector("#chart-quarter");
const yearChartEl = document.querySelector("#chart-year");
if (!quarterChartEl || !yearChartEl) return;
if (opts.selectedDataPoints[0].length === 1) {
if (quarterChartEl.classList.contains("active")) {
updateQuarterChart(chart, "barQuarter");
} else {
yearChartEl.classList.add("chart-quarter-activated");
quarterChartEl.classList.add("active");
updateQuarterChart(chart, "barQuarter");
}
} else {
updateQuarterChart(chart, "barQuarter");
}
if (opts.selectedDataPoints[0].length === 0) {
yearChartEl.classList.remove("chart-quarter-activated");
quarterChartEl.classList.remove("active");
}
},
updated: function (chart) {
updateQuarterChart(chart, "barQuarter");
},
},
},
plotOptions: {
bar: {
distributed: true,
horizontal: true,
barHeight: "100%",
dataLabels: {
position: "bottom",
},
},
},
dataLabels: {
enabled: true,
textAnchor: "start",
style: {
colors: ["#fff"],
},
formatter: function (_val, opt) {
return opt.w.globals.labels[opt.dataPointIndex];
},
offsetX: 0,
dropShadow: {
enabled: true,
},
},
colors: colors,
states: {
normal: {
filter: {
type: "desaturate",
},
},
active: {
allowMultipleDataPointsSelection: false,
filter: {
type: "darken",
value: 1,
},
},
},
legend: {
show: false,
},
tooltip: {
x: { show: false },
y: {
title: {
formatter: ((_seriesName: string, opts: any) =>
opts.w.globals.labels[opts.dataPointIndex]) as (
seriesName: string
) => string,
},
},
},
yaxis: {
labels: {
show: false,
},
},
},
seriesQuarter: [{ data: [] }],
});
const [years, setYear] = useState("");
const [datas, setDatas] = useState<any>([]);
const [details, setDetails] = useState<{ name: string; count: number }[][]>(
[]
);
const detailsRef = useRef(details);
useEffect(() => {
detailsRef.current = details;
}, [details]);
useEffect(() => {
initFetch();
}, [date, range.start, range.end, type]);
const initFetch = async () => {
const splitDate = date.split(" ");
const splitDateDaily = String(range.start.year);
const currentYear = type === "monthly" ? splitDate[1] : splitDateDaily;
let data = [];
if (currentYear === years) {
console.log("if", datas);
data = datas;
} else {
// const res = await getStatisticMonthly(
// type === "monthly" ? splitDate[1] : splitDateDaily
// );
// data = res?.data?.data;
data = dummy;
console.log("dataaa", data);
setDatas(data);
setYear(currentYear);
}
// console.log("datas", data);
if (data) {
if (type == "monthly") {
const mappedData: MappedData = makeDataByMonth(
data,
Number(splitDate[0])
);
console.log("mapped month", mappedData);
setDetails(mappedData.details);
setState((prev) => ({
...prev,
series: [{ data: mappedData.result }],
}));
}
if (type == "daily") {
const mappedData = makeDataByRange(
data,
range.start.month,
range.start.day,
range.end.month,
range.end.day
);
console.log("mmapped,", mappedData.details);
setDetails(mappedData.details);
setState((prev) => ({
...prev,
series: [{ data: mappedData.result }],
}));
}
} else {
setState((prev) => ({
...prev,
series: [{ data: [] }],
}));
}
};
const updateQuarterChart = useCallback(
(chart: any, id: string) => {
const selectedIndex = chart?.w?.config?.series[0]?.data?.findIndex(
(d: any, i: number) => {
return chart.w.globals.selectedDataPoints[0]?.includes(i);
}
);
if (selectedIndex !== -1) {
const counts = detailsRef.current[selectedIndex];
console.log("countres", counts, detailsRef);
const quarterData = [
{
name: `${selectedIndex + 1}`,
data: counts,
},
];
ApexCharts.exec(id, "updateSeries", quarterData);
setState((prev: any) => ({
...prev,
seriesQuarter: quarterData,
}));
}
},
[detailsRef]
);
return (
<div className="lg:h-[600px]">
<div id="wrap" className="flex flex-col lg:flex-row gap-2">
<div id="chart-year" className="lg:w-[80%] h-[600px]">
<ReactApexChart
options={state.options}
series={state.series}
type="bar"
height={600}
/>
</div>
<div id="chart-quarter" className="w-full lg:w-[20%]">
<div className="flex flex-col gap-1">
{state.seriesQuarter[0].data.map((list: any, index) => (
<div key={index} className="flex flex-row gap-2">
<p className="font-semibold capitalize">{list?.name} : </p>
<p>{list?.count}</p>
</div>
))}
</div>
</div>
</div>
</div>
);
};
export default ApexChartDynamic;