Skip to content

Commit

Permalink
web: adjust response on small device + allow pause hourly chart auto …
Browse files Browse the repository at this point in the history
…update
  • Loading branch information
hoang-rio committed Nov 27, 2024
1 parent f4cc81b commit a9966ef
Show file tree
Hide file tree
Showing 15 changed files with 193 additions and 165 deletions.
4 changes: 4 additions & 0 deletions web_viewer/fe_src/src/components/HourlyChart.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@

.hourly-chart-content {
margin-top: 10px;
}

.hourly-chart-buttons button+button {
margin-left: 10px;
}
324 changes: 172 additions & 152 deletions web_viewer/fe_src/src/components/HourlyChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,167 +15,186 @@ import { IClassNameProps, IUpdateChart, SeriesItem } from "../Intefaces";

const BATTERY_SERIE_NAME = "Battery";

const HourlyChart = forwardRef(({ className }: IClassNameProps, ref: ForwardedRef<IUpdateChart>) => {
const [chartData, setChartData] = useState<never[][]>([]);
const [isDark, setIsDark] = useState(false);
const isFetchingRef = useRef<boolean>(false);
const HourlyChart = forwardRef(
({ className }: IClassNameProps, ref: ForwardedRef<IUpdateChart>) => {
const [chartData, setChartData] = useState<never[][]>([]);
const [isDark, setIsDark] = useState(false);
const isFetchingRef = useRef<boolean>(false);
const [isAutoUpdate, setIsAutoUpdate] = useState(false);

const series = useMemo(() => {
const pvSeries: SeriesItem[] = [];
const batterySeries: SeriesItem[] = [];
const gridSeries: SeriesItem[] = [];
const consumptionSeries: SeriesItem[] = [];
const socSeries: SeriesItem[] = [];
const series = useMemo(() => {
const pvSeries: SeriesItem[] = [];
const batterySeries: SeriesItem[] = [];
const gridSeries: SeriesItem[] = [];
const consumptionSeries: SeriesItem[] = [];
const socSeries: SeriesItem[] = [];

chartData.forEach((item) => {
const time = new Date(item[1]).getTime();
pvSeries.push({ x: time, y: item[2] });
batterySeries.push({ x: time, y: item[3] });
gridSeries.push({ x: time, y: item[4] });
consumptionSeries.push({ x: time, y: item[5] });
socSeries.push({ x: time, y: item[6] });
});
return [
{
name: "PV",
data: pvSeries,
},
{
name: BATTERY_SERIE_NAME,
data: batterySeries,
},
{
name: "Grid",
data: gridSeries,
},
{
name: "Consumption",
data: consumptionSeries,
},
{
name: "SOC",
data: socSeries,
},
];
}, [chartData]);
chartData.forEach((item) => {
const time = new Date(item[1]).getTime();
pvSeries.push({ x: time, y: item[2] });
batterySeries.push({ x: time, y: item[3] });
gridSeries.push({ x: time, y: item[4] });
consumptionSeries.push({ x: time, y: item[5] });
socSeries.push({ x: time, y: item[6] });
});
return [
{
name: "PV",
data: pvSeries,
},
{
name: BATTERY_SERIE_NAME,
data: batterySeries,
},
{
name: "Grid",
data: gridSeries,
},
{
name: "Consumption",
data: consumptionSeries,
},
{
name: "SOC",
data: socSeries,
},
];
}, [chartData]);

const fetchChart = useCallback(async () => {
if (isFetchingRef.current) {
return;
}
isFetchingRef.current = true;
const res = await fetch(
`${import.meta.env.VITE_API_BASE_URL}/hourly-chart`
);
const json = await res.json();
setChartData(json);
isFetchingRef.current = false;
}, [setChartData]);

useImperativeHandle(ref, (): IUpdateChart => ({
updateItem(hourlyItem) {
const lastItem = chartData[chartData.length - 1];
if (JSON.stringify(lastItem) === JSON.stringify(hourlyItem)) {
const fetchChart = useCallback(async () => {
if (isFetchingRef.current) {
return;
}
const newChartData = [...chartData];
if (lastItem[0] === hourlyItem[0]) {
newChartData.splice(chartData.length - 1, 1, hourlyItem);
} else {
newChartData.push(hourlyItem);
}
setChartData(newChartData);
},
}));
isFetchingRef.current = true;
const res = await fetch(
`${import.meta.env.VITE_API_BASE_URL}/hourly-chart`
);
const json = await res.json();
setChartData(json);
isFetchingRef.current = false;
}, [setChartData]);

useEffect(() => {
fetchChart();
document.addEventListener("visibilitychange", () => {
if (!document.hidden) {
fetchChart();
}
});
}, [fetchChart, ref]);
useImperativeHandle(
ref,
(): IUpdateChart => ({
updateItem(hourlyItem) {
if (!isAutoUpdate) {
return;
}
const lastItem = chartData[chartData.length - 1];
if (JSON.stringify(lastItem) === JSON.stringify(hourlyItem)) {
return;
}
const newChartData = [...chartData];
if (lastItem[0] === hourlyItem[0]) {
newChartData.splice(chartData.length - 1, 1, hourlyItem);
} else {
newChartData.push(hourlyItem);
}
setChartData(newChartData);
},
})
);

useEffect(() => {
const mq = window.matchMedia("(prefers-color-scheme: dark)");
useEffect(() => {
fetchChart();
document.addEventListener("visibilitychange", () => {
if (!document.hidden) {
fetchChart();
}
});
}, [fetchChart, ref]);

if (mq.matches) {
setIsDark(true);
}
useEffect(() => {
const mq = window.matchMedia("(prefers-color-scheme: dark)");

// This callback will fire if the perferred color scheme changes without a reload
mq.addEventListener("change", (evt) => setIsDark(evt.matches));
}, []);
if (mq.matches) {
setIsDark(true);
}

// This callback will fire if the perferred color scheme changes without a reload
mq.addEventListener("change", (evt) => setIsDark(evt.matches));
}, []);

const toggleAutoUpdate = useCallback(() => {
if (!isAutoUpdate) {
fetchChart();
}
setIsAutoUpdate(!isAutoUpdate);
}, [isAutoUpdate, fetchChart]);

return (
<div className={`card hourly-chart ${className || ""}`}>
<div className="row justify-space-between">
<div className="hourly-chart-title">Hourly Chart</div>
<div className="row">
<button onClick={() => fetchChart()}>Update</button>
return (
<div className={`card hourly-chart ${className || ""}`}>
<div className="row justify-space-between">
<div className="hourly-chart-title">Hourly Chart</div>
<div className="row hourly-chart-buttons">
<button onClick={toggleAutoUpdate}>
{!isAutoUpdate ? "Allow auto update" : "Pause auto update"}
</button>
<button disabled={!isAutoUpdate} onClick={() => fetchChart()}>
Update
</button>
</div>
</div>
</div>
<div className="hourly-chart-content">
<Chart
type="line"
series={series}
options={{
chart: {
toolbar: {
show: false,
<div className="hourly-chart-content">
<Chart
type="line"
series={series}
options={{
chart: {
toolbar: {
show: false,
},
height: 300,
},
height: 300,
},
colors: [
"rgb(112, 173, 70)",
"rgb(90, 155, 213)",
"rgb(246, 104, 103)",
"rgb(255, 164, 97)",
"rgb(128, 0, 128)",
],
stroke: {
width: 3,
},
theme: {
mode: isDark ? "dark" : "light",
},
xaxis: {
type: "datetime",
labels: {
datetimeUTC: false,
colors: [
"rgb(112, 173, 70)",
"rgb(90, 155, 213)",
"rgb(246, 104, 103)",
"rgb(255, 164, 97)",
"rgb(128, 0, 128)",
],
stroke: {
width: 3,
},
},
yaxis: [
{ seriesName: "PV", title: { text: "Power (W)" } },
{ seriesName: "PV", show: false },
{ seriesName: "PV", show: false },
{ seriesName: "PV", show: false },
{
seriesName: "SOC",
opposite: true,
tickAmount: 10,
min: 0,
max: 100,
title: {
text: "SOC (%)",
},
theme: {
mode: isDark ? "dark" : "light",
},
],
tooltip: {
x: {
format: "HH:mm:ss",
xaxis: {
type: "datetime",
labels: {
datetimeUTC: false,
},
},
y: {
formatter(val, opts) {
if (opts.seriesIndex === 4) {
return `${val}%`;
}
return `${Math.abs(val)} W`;
yaxis: [
{ seriesName: "PV", title: { text: "Power (W)" } },
{ seriesName: "PV", show: false },
{ seriesName: "PV", show: false },
{ seriesName: "PV", show: false },
{
seriesName: "SOC",
opposite: true,
tickAmount: 10,
min: 0,
max: 100,
title: {
text: "SOC (%)",
},
},
title: {
formatter(seriesName, opts) {
console.log(opts);
],
tooltip: {
x: {
format: "HH:mm:ss",
},
y: {
formatter(val, opts) {
if (opts.seriesIndex === 4) {
return `${val}%`;
}
return `${Math.abs(val)} W`;
},
title: {
formatter(seriesName, opts) {
if (seriesName === BATTERY_SERIE_NAME) {
const batteryValue =
opts.series[1][opts.dataPointIndex];
Expand All @@ -185,16 +204,17 @@ const HourlyChart = forwardRef(({ className }: IClassNameProps, ref: ForwardedRe
return `${seriesName} discharge:`;
}
return `${seriesName}:`;
},
},
}
},
},
},
}}
/>
}}
/>
</div>
</div>
</div>
);
});
);
}
);

HourlyChart.displayName = "HourlyChart";

Expand Down
8 changes: 6 additions & 2 deletions web_viewer/fe_src/src/components/SystemInformation.css
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@
}

@media screen and (max-width: 900px) {
.consumption-icon img {
margin-top: 0px;
}

.power {
font-size: 16px;
margin-left: 10px;
Expand Down Expand Up @@ -284,7 +288,7 @@

.eps {
position: relative;
top: -25px;
top: -12px;
}

.grid {
Expand Down Expand Up @@ -380,7 +384,7 @@
}

.eps {
top: -22px;
top: -8px;
}

.eps-status {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit a9966ef

Please sign in to comment.