add OrderStatus statistic, stock fullfilment dummy
This commit is contained in:
Binary file not shown.
@@ -49,5 +49,6 @@ public class ControllerPathConfig {
|
|||||||
private static final String STATISTICS_BASE = "/statistics";
|
private static final String STATISTICS_BASE = "/statistics";
|
||||||
public static final String STATISTICS_VOLUME = STATISTICS_BASE + "/volume";
|
public static final String STATISTICS_VOLUME = STATISTICS_BASE + "/volume";
|
||||||
public static final String STATISTICS_REVENUE = STATISTICS_BASE + "/revenue";
|
public static final String STATISTICS_REVENUE = STATISTICS_BASE + "/revenue";
|
||||||
|
public static final String STATISTICS_ORDERSTATUS = STATISTICS_BASE + "/orderstatus";
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package de.htwsaar.webshop.controller;
|
package de.htwsaar.webshop.controller;
|
||||||
|
|
||||||
import de.htwsaar.webshop.model.CatMonthModel;
|
import de.htwsaar.webshop.model.CatMonthModel;
|
||||||
|
import de.htwsaar.webshop.model.OrderStatus;
|
||||||
import de.htwsaar.webshop.service.SessionService;
|
import de.htwsaar.webshop.service.SessionService;
|
||||||
import de.htwsaar.webshop.service.StatisticsService;
|
import de.htwsaar.webshop.service.StatisticsService;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
@@ -11,10 +12,10 @@ import org.springframework.web.bind.annotation.RequestMethod;
|
|||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static de.htwsaar.webshop.config.ControllerPathConfig.STATISTICS_REVENUE;
|
import static de.htwsaar.webshop.config.ControllerPathConfig.*;
|
||||||
import static de.htwsaar.webshop.config.ControllerPathConfig.STATISTICS_VOLUME;
|
|
||||||
import static de.htwsaar.webshop.config.ParameterConfig.PARAM_EMAIL;
|
import static de.htwsaar.webshop.config.ParameterConfig.PARAM_EMAIL;
|
||||||
import static de.htwsaar.webshop.config.ParameterConfig.PARAM_SESSION;
|
import static de.htwsaar.webshop.config.ParameterConfig.PARAM_SESSION;
|
||||||
import static de.htwsaar.webshop.util.LoggerUtil.logRequest;
|
import static de.htwsaar.webshop.util.LoggerUtil.logRequest;
|
||||||
@@ -55,4 +56,16 @@ public class StatisticsController {
|
|||||||
return ResponseEntity.ok(statisticsService.getSalesRevenue());
|
return ResponseEntity.ok(statisticsService.getSalesRevenue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = STATISTICS_ORDERSTATUS, method = RequestMethod.GET, produces = "application/json")
|
||||||
|
public ResponseEntity<Map<OrderStatus, Integer>> getOrderStatus(HttpServletRequest request,
|
||||||
|
@RequestParam(value = PARAM_SESSION) UUID token,
|
||||||
|
@RequestParam(value = PARAM_EMAIL) String email) {
|
||||||
|
logRequest(request);
|
||||||
|
if (!sessionService.isAdmin(token, email)) {
|
||||||
|
log.warn("Invalid session requesting Admin {}", token);
|
||||||
|
return ResponseEntity.status(403).build();
|
||||||
|
}
|
||||||
|
return ResponseEntity.ok(statisticsService.getOrderStatus());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
package de.htwsaar.webshop.service;
|
package de.htwsaar.webshop.service;
|
||||||
|
|
||||||
import de.htwsaar.webshop.model.CatMonthModel;
|
import de.htwsaar.webshop.model.CatMonthModel;
|
||||||
|
import de.htwsaar.webshop.model.OrderStatus;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public interface StatisticsService {
|
public interface StatisticsService {
|
||||||
CatMonthModel<Integer> getSalesVolume();
|
CatMonthModel<Integer> getSalesVolume();
|
||||||
|
|
||||||
CatMonthModel<Integer> getSalesRevenue();
|
CatMonthModel<Integer> getSalesRevenue();
|
||||||
|
|
||||||
|
Map<OrderStatus, Integer> getOrderStatus();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package de.htwsaar.webshop.service.impl;
|
|||||||
|
|
||||||
import de.htwsaar.webshop.model.ArticleCategory;
|
import de.htwsaar.webshop.model.ArticleCategory;
|
||||||
import de.htwsaar.webshop.model.CatMonthModel;
|
import de.htwsaar.webshop.model.CatMonthModel;
|
||||||
|
import de.htwsaar.webshop.model.OrderStatus;
|
||||||
import de.htwsaar.webshop.repository.entities.OrderItem;
|
import de.htwsaar.webshop.repository.entities.OrderItem;
|
||||||
import de.htwsaar.webshop.service.OrderService;
|
import de.htwsaar.webshop.service.OrderService;
|
||||||
import de.htwsaar.webshop.service.StatisticsService;
|
import de.htwsaar.webshop.service.StatisticsService;
|
||||||
@@ -57,4 +58,14 @@ public class StatisticsServiceImpl implements StatisticsService {
|
|||||||
public CatMonthModel<Integer> getSalesRevenue() {
|
public CatMonthModel<Integer> getSalesRevenue() {
|
||||||
return getMonthCategoryMap(item -> item.getArticle().getPrice100(), Integer::sum, 0);
|
return getMonthCategoryMap(item -> item.getArticle().getPrice100(), Integer::sum, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<OrderStatus, Integer> getOrderStatus() {
|
||||||
|
Map<OrderStatus, Integer> map = new TreeMap<>();
|
||||||
|
orderService.findAll().forEach(order -> {
|
||||||
|
map.putIfAbsent(order.getStatus(), 0);
|
||||||
|
map.computeIfPresent(order.getStatus(), (k,v) -> ++v);
|
||||||
|
});
|
||||||
|
return map;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ import { BarSeriesType } from '@mui/x-charts'
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { fetchStatisticsVolume, fetchStatisticsRevenue } from "../query/Queries.tsx";
|
import { fetchStatisticsVolume, fetchStatisticsRevenue, fetchOrderStatus } from "../query/Queries.tsx";
|
||||||
import { useAccount } from "../AccountProvider.tsx";
|
import { useAccount } from "../AccountProvider.tsx";
|
||||||
|
import { data } from "react-router-dom";
|
||||||
|
|
||||||
export default function StatisticsInfo() {
|
export default function StatisticsInfo() {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@@ -14,16 +15,17 @@ export default function StatisticsInfo() {
|
|||||||
|
|
||||||
const [monthlyVolume, setMonthlyVolume] = useState<BarSeriesType[]>([]);
|
const [monthlyVolume, setMonthlyVolume] = useState<BarSeriesType[]>([]);
|
||||||
const [monthlyVolumeXaxis, setMonthlyVolumeXaxis] = useState([{ data: [] }]);
|
const [monthlyVolumeXaxis, setMonthlyVolumeXaxis] = useState([{ data: [] }]);
|
||||||
const [totalVolume, setTotalVolume] = useState([{ data: [] }]);
|
const [totalVolume, setTotalVolume] = useState([]);
|
||||||
|
|
||||||
const [monthlyRevenue, setMonthlyRevenue] = useState<BarSeriesType[]>([]);
|
const [monthlyRevenue, setMonthlyRevenue] = useState<BarSeriesType[]>([]);
|
||||||
const [monthlyRevenueXaxis, setMonthlyRevenueXaxis] = useState([{ data: [] }]);
|
const [monthlyRevenueXaxis, setMonthlyRevenueXaxis] = useState([{ data: [] }]);
|
||||||
const [totalRevenue, setTotalRevenue] = useState([{ data: [] }]);
|
const [totalRevenue, setTotalRevenue] = useState([]);
|
||||||
|
|
||||||
|
|
||||||
|
const [orderStatus, setOrderStatus] = useState([]);
|
||||||
|
|
||||||
const { user: loginData } = useAccount();
|
const { user: loginData } = useAccount();
|
||||||
|
|
||||||
|
|
||||||
const { data: dataVolume } = useQuery({
|
const { data: dataVolume } = useQuery({
|
||||||
queryKey: ["fetchStatisticsVolume", loginData],
|
queryKey: ["fetchStatisticsVolume", loginData],
|
||||||
queryFn: () => fetchStatisticsVolume(loginData ? loginData : { email: "", password: "", session: "", customerId: -1, isAdmin: false }),
|
queryFn: () => fetchStatisticsVolume(loginData ? loginData : { email: "", password: "", session: "", customerId: -1, isAdmin: false }),
|
||||||
@@ -38,15 +40,23 @@ export default function StatisticsInfo() {
|
|||||||
retryDelay: 0,
|
retryDelay: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { data: dataOrderStatus } = useQuery({
|
||||||
|
queryKey: ["fetchOrderStatus", loginData],
|
||||||
|
queryFn: () => fetchOrderStatus(loginData ? loginData : { email: "", password: "", session: "", customerId: -1, isAdmin: false }),
|
||||||
|
retry: 0,
|
||||||
|
retryDelay: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (dataVolume) {
|
if (dataVolume) {
|
||||||
const cmm = []
|
const cmm = []
|
||||||
const cmmx = monthlyVolumeXaxis
|
const cmmx = monthlyVolumeXaxis
|
||||||
const tv = [{data:[]}]
|
const tv = []
|
||||||
let i = 0
|
let i = 0
|
||||||
for (const cat in dataVolume.catMonthMap) {
|
for (const cat in dataVolume.catMonthMap) {
|
||||||
for (const timestamp in dataVolume.catMonthMap[cat]) {
|
for (const timestamp in dataVolume.catMonthMap[cat]) {
|
||||||
console.log(i + "." + timestamp);
|
|
||||||
const date = new Date(parseInt(timestamp))
|
const date = new Date(parseInt(timestamp))
|
||||||
const formattedDate = date.getFullYear() + "-" + String(date.getMonth() + 1).padStart(2, '0')
|
const formattedDate = date.getFullYear() + "-" + String(date.getMonth() + 1).padStart(2, '0')
|
||||||
if (!cmmx[0].data.includes(formattedDate)) {
|
if (!cmmx[0].data.includes(formattedDate)) {
|
||||||
@@ -58,10 +68,10 @@ export default function StatisticsInfo() {
|
|||||||
}
|
}
|
||||||
cmm[i].data.push(datapoint)
|
cmm[i].data.push(datapoint)
|
||||||
|
|
||||||
if(tv[0].data.length == i) {
|
if(tv.length == i) {
|
||||||
tv[0].data.push({ id: i, value: 0, label: t(cat)})
|
tv.push({ id: i, value: 0, label: t(cat)})
|
||||||
}
|
}
|
||||||
tv[0].data[i].value += datapoint
|
tv[i].value += datapoint
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@@ -76,7 +86,7 @@ export default function StatisticsInfo() {
|
|||||||
if (dataRevenue) {
|
if (dataRevenue) {
|
||||||
const cmm = []
|
const cmm = []
|
||||||
const cmmx = monthlyRevenueXaxis
|
const cmmx = monthlyRevenueXaxis
|
||||||
const tv = [{data:[]}]
|
const tv = []
|
||||||
let i = 0
|
let i = 0
|
||||||
for (const cat in dataRevenue.catMonthMap) {
|
for (const cat in dataRevenue.catMonthMap) {
|
||||||
for (const timestamp in dataRevenue.catMonthMap[cat]) {
|
for (const timestamp in dataRevenue.catMonthMap[cat]) {
|
||||||
@@ -85,16 +95,16 @@ export default function StatisticsInfo() {
|
|||||||
if (!cmmx[0].data.includes(formattedDate)) {
|
if (!cmmx[0].data.includes(formattedDate)) {
|
||||||
cmmx[0].data.push(formattedDate);
|
cmmx[0].data.push(formattedDate);
|
||||||
}
|
}
|
||||||
const datapoint = dataRevenue.catMonthMap[cat][timestamp]
|
const datapoint = dataRevenue.catMonthMap[cat][timestamp] / 100
|
||||||
if(cmm.length == i) {
|
if(cmm.length == i) {
|
||||||
cmm.push({ id: i, data: [], label: t(cat), type: "bar" })
|
cmm.push({ id: i, data: [], label: t(cat), type: "bar" })
|
||||||
}
|
}
|
||||||
cmm[i].data.push(datapoint)
|
cmm[i].data.push(datapoint)
|
||||||
|
|
||||||
if(tv[0].data.length == i) {
|
if(tv.length == i) {
|
||||||
tv[0].data.push({ id: i, value: 0, label: t(cat)})
|
tv.push({ id: i, value: 0, label: t(cat)})
|
||||||
}
|
}
|
||||||
tv[0].data[i].value += datapoint
|
tv[i].value += datapoint
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@@ -105,6 +115,16 @@ export default function StatisticsInfo() {
|
|||||||
}
|
}
|
||||||
}, [dataRevenue]);
|
}, [dataRevenue]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(dataOrderStatus) {
|
||||||
|
const orderStatus = []
|
||||||
|
for(var status in dataOrderStatus) {
|
||||||
|
orderStatus.push({value: dataOrderStatus[status], label: t(status)})
|
||||||
|
}
|
||||||
|
setOrderStatus(orderStatus)
|
||||||
|
}
|
||||||
|
}, [dataOrderStatus])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box className="" sx={{ color: theme.palette.text.primary }}>
|
<Box className="" sx={{ color: theme.palette.text.primary }}>
|
||||||
<Typography mt={4} variant="h4" align="center" gutterBottom>
|
<Typography mt={4} variant="h4" align="center" gutterBottom>
|
||||||
@@ -132,27 +152,64 @@ export default function StatisticsInfo() {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box display={"flex"} mb={9}>
|
<Box display={"flex"} mb={9}>
|
||||||
<Box sx={{ m: 2 }}>
|
<Box className="vw20" sx={{ m: 2 }}>
|
||||||
<Typography variant="h6" align="center" gutterBottom>
|
<Typography variant="h6" align="center" gutterBottom>
|
||||||
Item Volume Distribution
|
Item Volume Distribution
|
||||||
</Typography>
|
</Typography>
|
||||||
<PieChart
|
<PieChart
|
||||||
series={totalVolume}
|
series={[{
|
||||||
|
data: totalVolume,
|
||||||
|
highlightScope: { fade: 'global', highlight: 'item' },
|
||||||
|
faded: { innerRadius: 30, additionalRadius: -30, color: 'gray' },
|
||||||
|
}]}
|
||||||
width={200}
|
width={200}
|
||||||
height={200}
|
height={200}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box sx={{ m: 2 }}>
|
<Box className="vw20" sx={{ m: 2 }}>
|
||||||
<Typography variant="h6" align="center" gutterBottom>
|
<Typography variant="h6" align="center" gutterBottom>
|
||||||
Item Revenue Distribution
|
Item Revenue Distribution
|
||||||
</Typography>
|
</Typography>
|
||||||
<PieChart
|
<PieChart
|
||||||
series={totalRevenue}
|
series={[{
|
||||||
|
data: totalRevenue,
|
||||||
|
highlightScope: { fade: 'global', highlight: 'item' },
|
||||||
|
}]}
|
||||||
width={200}
|
width={200}
|
||||||
height={200}
|
height={200}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
<Box className="vw20" sx={{ m: 2 }}>
|
||||||
|
<Typography variant="h6" align="center" gutterBottom>
|
||||||
|
Stock fulfillment
|
||||||
|
</Typography>
|
||||||
|
<PieChart
|
||||||
|
series={[{
|
||||||
|
data: totalRevenue,
|
||||||
|
}]}
|
||||||
|
width={200}
|
||||||
|
height={200}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box className="vw20" sx={{ m: 2 }}>
|
||||||
|
<Typography variant="h6" align="center" gutterBottom>
|
||||||
|
Order Status
|
||||||
|
</Typography>
|
||||||
|
<PieChart
|
||||||
|
series={[{
|
||||||
|
data: orderStatus,
|
||||||
|
innerRadius: 20,
|
||||||
|
outerRadius: 70,
|
||||||
|
cornerRadius: 5,
|
||||||
|
paddingAngle: 1,
|
||||||
|
highlightScope: { fade: 'global', highlight: 'item' },
|
||||||
|
faded: { innerRadius: 30, additionalRadius: -10, color: 'gray' },
|
||||||
|
}]}
|
||||||
|
height={200}
|
||||||
|
width={200}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -29,4 +29,8 @@
|
|||||||
|
|
||||||
.rating-text-field{
|
.rating-text-field{
|
||||||
background-color: whitesmoke;
|
background-color: whitesmoke;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vw20 {
|
||||||
|
width: 20vw;
|
||||||
}
|
}
|
||||||
@@ -206,4 +206,12 @@ export const orderPatch = async (order: OrderPatch) => {
|
|||||||
throw new Error("Order patch failed");
|
throw new Error("Order patch failed");
|
||||||
}
|
}
|
||||||
return response.json();
|
return response.json();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchOrderStatus = async (loginData: User) => {
|
||||||
|
const response = await fetch("http://localhost:8085/statistics/orderstatus?email=" + loginData.email + "&session=" + loginData.session);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("fetching satistics Revenue failed");
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user