From 48baf47d10efdbe5687025ab7d68192468980e77 Mon Sep 17 00:00:00 2001 From: Tim <47184194+imgde@users.noreply.github.com> Date: Sun, 15 Jun 2025 23:44:47 +0200 Subject: [PATCH] StatisticsInfo --- .../webshop/model/ArticleCategory.java | 14 ++ .../repository/entities/OrderItem.java | 2 +- .../service/impl/OrderServiceImpl.java | 1 - .../service/impl/StatisticsServiceImpl.java | 14 +- .../src/helper/adminpanel/StatisticsInfo.tsx | 194 ++++++++++++------ 01-frontend/src/helper/query/Queries.tsx | 22 +- 6 files changed, 174 insertions(+), 73 deletions(-) create mode 100644 00-backend/src/main/java/de/htwsaar/webshop/model/ArticleCategory.java diff --git a/00-backend/src/main/java/de/htwsaar/webshop/model/ArticleCategory.java b/00-backend/src/main/java/de/htwsaar/webshop/model/ArticleCategory.java new file mode 100644 index 0000000..0f352c2 --- /dev/null +++ b/00-backend/src/main/java/de/htwsaar/webshop/model/ArticleCategory.java @@ -0,0 +1,14 @@ +package de.htwsaar.webshop.model; + +public enum ArticleCategory { + GARDEN_SUPPLIES("gardenSupplies"), + OTHER("other"), + SEEDS("seeds"), + TECHNICAL_COMPONENTS("technicalComponents"); + + public final String loc; + + ArticleCategory(String loc) { + this.loc = loc; + } +} diff --git a/00-backend/src/main/java/de/htwsaar/webshop/repository/entities/OrderItem.java b/00-backend/src/main/java/de/htwsaar/webshop/repository/entities/OrderItem.java index ab2ac31..7e8bb0e 100644 --- a/00-backend/src/main/java/de/htwsaar/webshop/repository/entities/OrderItem.java +++ b/00-backend/src/main/java/de/htwsaar/webshop/repository/entities/OrderItem.java @@ -38,6 +38,6 @@ public class OrderItem { } public Integer getPrice() { - return amount * article.getPrice100(); + return amount * (article.getPrice100() * (100-article.getDiscount100()))/100; } } diff --git a/00-backend/src/main/java/de/htwsaar/webshop/service/impl/OrderServiceImpl.java b/00-backend/src/main/java/de/htwsaar/webshop/service/impl/OrderServiceImpl.java index 494fd65..7316203 100644 --- a/00-backend/src/main/java/de/htwsaar/webshop/service/impl/OrderServiceImpl.java +++ b/00-backend/src/main/java/de/htwsaar/webshop/service/impl/OrderServiceImpl.java @@ -96,7 +96,6 @@ public class OrderServiceImpl implements OrderService { while (months > 0) { upper = lower + TimeUtil.monthLength(lower); List a = orderRepository.getOrdersByTimeBetween(lower, upper); - log.info("Getting Orders from {} to {}: size {}", lower, upper, a.size()); orders.put(lower, a); lower = upper; months--; diff --git a/00-backend/src/main/java/de/htwsaar/webshop/service/impl/StatisticsServiceImpl.java b/00-backend/src/main/java/de/htwsaar/webshop/service/impl/StatisticsServiceImpl.java index 094c727..d853be5 100644 --- a/00-backend/src/main/java/de/htwsaar/webshop/service/impl/StatisticsServiceImpl.java +++ b/00-backend/src/main/java/de/htwsaar/webshop/service/impl/StatisticsServiceImpl.java @@ -1,5 +1,6 @@ package de.htwsaar.webshop.service.impl; +import de.htwsaar.webshop.model.ArticleCategory; import de.htwsaar.webshop.model.CatMonthModel; import de.htwsaar.webshop.repository.entities.OrderItem; import de.htwsaar.webshop.service.OrderService; @@ -9,8 +10,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.util.HashMap; import java.util.Map; +import java.util.TreeMap; import java.util.function.BinaryOperator; import java.util.function.Function; @@ -28,12 +29,17 @@ public class StatisticsServiceImpl implements StatisticsService { private CatMonthModel getMonthCategoryMap(Function mappingFunction, BinaryOperator reduceFunction, T defaultValue) { - Map> map = new HashMap<>(); + Map> map = new TreeMap<>(); + for (ArticleCategory value : ArticleCategory.values()) { + map.put(value.loc, new TreeMap<>()); + } orderService.getTimeSortedOrders(TimeUtil.nowMonthsAgo(12), 12).forEach( (k,v) -> { - log.info("Month {} has {}", k, v.size()); + for (ArticleCategory value : ArticleCategory.values()) { + map.get(value.loc).putIfAbsent(k, defaultValue); + } v.forEach( (order) -> order.getOrderItems().forEach((item) -> { - map.putIfAbsent(item.getArticle().getCategory(), new HashMap<>()); + map.putIfAbsent(item.getArticle().getCategory(), new TreeMap<>()); map.get(item.getArticle().getCategory()).putIfAbsent(k, defaultValue); map.get(item.getArticle().getCategory()).computeIfPresent(k, (timestamp,left ) -> reduceFunction.apply(left, mappingFunction.apply(item))); }) diff --git a/01-frontend/src/helper/adminpanel/StatisticsInfo.tsx b/01-frontend/src/helper/adminpanel/StatisticsInfo.tsx index 4ad3510..ae8d647 100644 --- a/01-frontend/src/helper/adminpanel/StatisticsInfo.tsx +++ b/01-frontend/src/helper/adminpanel/StatisticsInfo.tsx @@ -1,92 +1,158 @@ import { Box, Typography, useTheme } from "@mui/material"; import { BarChart } from '@mui/x-charts/BarChart'; import { PieChart } from '@mui/x-charts/PieChart'; -import { - ArcElement, - BarElement, - CategoryScale, - Chart as ChartJS, - Legend, - LinearScale, - LineElement, - PointElement, - Title, - Tooltip, -} from "chart.js"; +import { BarSeriesType } from '@mui/x-charts' +import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; - -// Chart.js registrieren -ChartJS.register( - CategoryScale, - LinearScale, - BarElement, - Title, - Tooltip, - Legend, - ArcElement, - LineElement, - PointElement -); +import { useQuery } from "@tanstack/react-query"; +import { fetchStatisticsVolume, fetchStatisticsRevenue } from "../query/Queries.tsx"; +import { useAccount } from "../AccountProvider.tsx"; export default function StatisticsInfo() { const theme = useTheme(); - const {t} = useTranslation(); + const { t } = useTranslation(); + + const [monthlyVolume, setMonthlyVolume] = useState([]); + const [monthlyVolumeXaxis, setMonthlyVolumeXaxis] = useState([{ data: [] }]); + const [totalVolume, setTotalVolume] = useState([{ data: [] }]); + + const [monthlyRevenue, setMonthlyRevenue] = useState([]); + const [monthlyRevenueXaxis, setMonthlyRevenueXaxis] = useState([{ data: [] }]); + const [totalRevenue, setTotalRevenue] = useState([{ data: [] }]); + + + + const { user: loginData } = useAccount(); + + const { data: dataVolume } = useQuery({ + queryKey: ["fetchStatisticsVolume", loginData], + queryFn: () => fetchStatisticsVolume(loginData ? loginData : { email: "", password: "", session: "", customerId: -1, isAdmin: false }), + retry: 0, + retryDelay: 0, + }); + + const { data: dataRevenue } = useQuery({ + queryKey: ["fetchStatisticsRevenue", loginData], + queryFn: () => fetchStatisticsRevenue(loginData ? loginData : { email: "", password: "", session: "", customerId: -1, isAdmin: false }), + retry: 0, + retryDelay: 0, + }); + + useEffect(() => { + if (dataVolume) { + const cmm = [] + const cmmx = monthlyVolumeXaxis + const tv = [{data:[]}] + let i = 0 + for (const cat in dataVolume.catMonthMap) { + for (const timestamp in dataVolume.catMonthMap[cat]) { + console.log(i + "." + timestamp); + const date = new Date(parseInt(timestamp)) + const formattedDate = date.getFullYear() + "-" + String(date.getMonth() + 1).padStart(2, '0') + if (!cmmx[0].data.includes(formattedDate)) { + cmmx[0].data.push(formattedDate); + } + const datapoint = dataVolume.catMonthMap[cat][timestamp] + if(cmm.length == i) { + cmm.push({ id: i, data: [], label: t(cat), type: "bar" }) + } + cmm[i].data.push(datapoint) + + if(tv[0].data.length == i) { + tv[0].data.push({ id: i, value: 0, label: t(cat)}) + } + tv[0].data[i].value += datapoint + } + i++; + } + + setMonthlyVolume(cmm) + setMonthlyVolumeXaxis(cmmx) + setTotalVolume(tv) + } + }, [dataVolume]); + + useEffect(() => { + if (dataRevenue) { + const cmm = [] + const cmmx = monthlyRevenueXaxis + const tv = [{data:[]}] + let i = 0 + for (const cat in dataRevenue.catMonthMap) { + for (const timestamp in dataRevenue.catMonthMap[cat]) { + const date = new Date(parseInt(timestamp)) + const formattedDate = date.getFullYear() + "-" + String(date.getMonth() + 1).padStart(2, '0') + if (!cmmx[0].data.includes(formattedDate)) { + cmmx[0].data.push(formattedDate); + } + const datapoint = dataRevenue.catMonthMap[cat][timestamp] + if(cmm.length == i) { + cmm.push({ id: i, data: [], label: t(cat), type: "bar" }) + } + cmm[i].data.push(datapoint) + + if(tv[0].data.length == i) { + tv[0].data.push({ id: i, value: 0, label: t(cat)}) + } + tv[0].data[i].value += datapoint + } + i++; + } + + setMonthlyRevenue(cmm) + setMonthlyRevenueXaxis(cmmx) + setTotalRevenue(tv) + } + }, [dataRevenue]); return ( - - + + Sales Statistics - Monthly Sales in n + Monthly Sales Volume - Monthly Revenue in € + Monthly Sales Revenue in € + + + + Item Volume Distribution + + + - - - Item Sales Distribution - - + + + Item Revenue Distribution + + + ); diff --git a/01-frontend/src/helper/query/Queries.tsx b/01-frontend/src/helper/query/Queries.tsx index dad060c..e76d186 100644 --- a/01-frontend/src/helper/query/Queries.tsx +++ b/01-frontend/src/helper/query/Queries.tsx @@ -159,10 +159,26 @@ export const fetchAccounts = async (loginData: User) => { return response.json(); }; -export const fetchItems = async (loginData: User) => { - const response = await fetch("http://localhost:8085/article/all?email=" + loginData.email + "&session=" + loginData.session); +export const fetchItems = async (loginData: User) => { //TODO: remove and use above + const response = await fetch("http://localhost:8085/article/all"); if (!response.ok) { - throw new Error("Login failed"); + throw new Error("fetching items failed"); + } + return response.json(); +}; + +export const fetchStatisticsVolume = async (loginData: User) => { + const response = await fetch("http://localhost:8085/statistics/volume?email=" + loginData.email + "&session=" + loginData.session); + if (!response.ok) { + throw new Error("fetching satistics Volume failed"); + } + return response.json(); +}; + +export const fetchStatisticsRevenue = async (loginData: User) => { + const response = await fetch("http://localhost:8085/statistics/revenue?email=" + loginData.email + "&session=" + loginData.session); + if (!response.ok) { + throw new Error("fetching satistics Revenue failed"); } return response.json(); };