Separated all the widgets in the dashboard into their own components

This commit is contained in:
keannu125 2023-03-07 21:17:22 +08:00
parent 6619163abe
commit 3b5fb87099
12 changed files with 405 additions and 133 deletions

View file

@ -164,6 +164,24 @@ export function UserInfo() {
}); });
} }
export function QueryUser(id: number) {
const token = JSON.parse(localStorage.getItem("token") || "{}");
return axios
.get("http://localhost:8000/api/v1/user_list/" + id, {
headers: {
Authorization: "Token " + token,
},
})
.then((response) => {
console.log("Querying one user...", response.data);
return response.data;
})
.catch((error) => {
console.log("Error retrieving single user data", error.response);
return false;
});
}
export function UserActivate(activation: ActivationParams) { export function UserActivate(activation: ActivationParams) {
return axios return axios
.post("http://localhost:8000/api/v1/accounts/users/activation/", activation) .post("http://localhost:8000/api/v1/accounts/users/activation/", activation)

View file

@ -0,0 +1,49 @@
import * as React from "react";
import styles from "../../../styles";
import LowStockIcon from "../../Icons/LowStockIcon/LowStockIcon";
import { ProductList } from "../../../Interfaces/Interfaces";
export interface props {}
export default function LowestStockWidget(props: ProductList) {
if (!props.Products[0]) {
return (
<div
style={{
...styles.widget,
...{ flex: 1 },
}}
>
<div style={styles.content_row}>
<LowStockIcon size={64} color="white" />
<p style={{ ...styles.text_white, ...styles.text_L }}>Lowest Stock</p>
</div>
<p style={{ ...styles.text_white, ...styles.text_S }}>
There are no products yet...
</p>
<p style={{ ...styles.text_white, ...styles.text_S }}>
No worries on running out!
</p>
</div>
);
}
return (
<div
style={{
...styles.widget,
...{ flex: 1 },
}}
>
<div style={styles.content_row}>
<LowStockIcon size={64} color="white" />
<p style={{ ...styles.text_white, ...styles.text_L }}>Lowest Stock</p>
</div>
<p style={{ ...styles.text_white, ...styles.text_S }}>
{props.Products[0].name}
</p>
<p style={{ ...styles.text_white, ...styles.text_S }}>
In Stock: {props.Products[0].quantity}
</p>
</div>
);
}

View file

@ -0,0 +1,58 @@
import * as React from "react";
import styles from "../../../styles";
import { ProductList } from "../../../Interfaces/Interfaces";
import RecentlyAddedIcon from "../../Icons/RecentlyAddedIcon/RecentlyAddedIcon";
export default function RecentlyAddedWidget(props: ProductList) {
if (!props.Products[0]) {
return (
<div
style={{
...styles.widget,
...{ flex: 1 },
}}
>
<div style={styles.content_row}>
<RecentlyAddedIcon size={64} color="white" />
<p
style={{
...styles.text_white,
...styles.text_L,
}}
>
Recently Added
</p>
</div>
<p style={{ ...styles.text_white, ...styles.text_M }}>
Nothing recently added...
</p>
</div>
);
}
return (
<div
style={{
...styles.widget,
...{ flex: 1 },
}}
>
<div style={styles.content_row}>
<RecentlyAddedIcon size={64} color="white" />
<p
style={{
...styles.text_white,
...styles.text_L,
}}
>
Recently Added
</p>
</div>
<p style={{ ...styles.text_white, ...styles.text_M }}>
{props.Products[0].name}
</p>
<p style={{ ...styles.text_white, ...styles.text_S }}>
{props.Products[0].date_added}
</p>
</div>
);
}

View file

@ -0,0 +1,44 @@
import * as React from "react";
import styles from "../../../styles";
import ColoredCube from "../../ColoredCube/ColoredCube";
import StatsIcon from "../../Icons/StatsIcon/StatsIcon";
import { useSelector } from "react-redux";
import { SessionTransactions } from "../../../Interfaces/Interfaces";
export interface props {}
export default function SessionStatsWidget() {
const session_added = useSelector(
(state: SessionTransactions) => state.session_transactions.added
);
const session_removed = useSelector(
(state: SessionTransactions) => state.session_transactions.removed
);
return (
<div
style={{
...styles.widget,
...{ flex: 5 },
}}
>
<div style={styles.content_row}>
<StatsIcon size={64} color="white" />
<p style={{ ...styles.text_white, ...styles.text_XL }}>
Current Session
</p>
</div>
<div style={styles.content_row}>
<ColoredCube size={32} color="#a48e41" />
<p style={{ ...styles.text_white, ...styles.text_L }}>Added</p>
</div>
<p style={{ ...styles.text_white, ...styles.text_L }}>{session_added}</p>
<div style={styles.content_row}>
<ColoredCube size={32} color="#a44141" />
<p style={{ ...styles.text_white, ...styles.text_L }}>Removed</p>
</div>
<p style={{ ...styles.text_white, ...styles.text_XL }}>
{session_removed}
</p>
</div>
);
}

View file

@ -0,0 +1,37 @@
import * as React from "react";
import styles from "../../../styles";
import TotalProductsIcon from "../../Icons/TotalProductsIcon/TotalProductsIcon";
import { Product, ProductList } from "../../../Interfaces/Interfaces";
import { useEffect, useState } from "react";
export interface props {}
export default function TotalProductsWidget(props: ProductList) {
const [items, setItems] = useState("Loading...");
useEffect(() => {
if (props.Products.length === 0) {
setItems("No products");
} else if (props.Products.length === 1) {
setItems("1 product");
} else {
setItems(props.Products.length + " Unique Items");
}
}, []);
return (
<div
style={{
...styles.widget,
...{ flex: 5 },
}}
>
<div style={styles.content_row}>
<TotalProductsIcon size={64} color="white" />
<p style={{ ...styles.text_white, ...styles.text_XL }}>
Total Products
</p>
</div>
<p style={{ ...styles.text_white, ...styles.text_L }}>{items}</p>
<p style={{ ...styles.text_white, ...styles.text_L }}>In inventory</p>
</div>
);
}

View file

@ -0,0 +1,99 @@
import * as React from "react";
import {
OldSessionState,
ProductLogEntry,
} from "../../../Interfaces/Interfaces";
import styles from "../../../styles";
import { TableBody, TableRow, TableCell } from "@mui/material";
import { GetProduct, QueryUser } from "../../Api/Api";
import { useState } from "react";
import { useSelector } from "react-redux";
import { useQuery } from "react-query";
export default function RowRenderer(props: ProductLogEntry) {
const old_session_checked = useSelector(
(state: OldSessionState) => state.old_session_checked.value
);
const {
data: user,
isLoading,
error,
} = useQuery({
queryKey: ["user_select_id_" + props.Product.id, props.Product.id],
queryFn: () => QueryUser(props.Product.id),
});
if (isLoading || !old_session_checked) {
<TableRow
key={props.Product.id}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell style={{ ...styles.text_white, ...styles.text_S }}>
{props.Product.history_id}
</TableCell>
<TableCell style={{ ...styles.text_white, ...styles.text_S }}>
{props.Product.id}
</TableCell>
<TableCell style={{ ...styles.text_white, ...styles.text_S }}>
{props.Product.name}
</TableCell>
<TableCell style={{ ...styles.text_white, ...styles.text_S }}>
{props.Product.quantity}
</TableCell>
<TableCell style={{ ...styles.text_white, ...styles.text_S }}>
Loading...
</TableCell>
<TableCell style={{ ...styles.text_white, ...styles.text_S }}>
{props.Product.history_date}
</TableCell>
</TableRow>;
} else if (error) {
<TableRow
key={props.Product.id}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell style={{ ...styles.text_white, ...styles.text_S }}>
{props.Product.history_id}
</TableCell>
<TableCell style={{ ...styles.text_white, ...styles.text_S }}>
{props.Product.id}
</TableCell>
<TableCell style={{ ...styles.text_white, ...styles.text_S }}>
{props.Product.name}
</TableCell>
<TableCell style={{ ...styles.text_white, ...styles.text_S }}>
{props.Product.quantity}
</TableCell>
<TableCell style={{ ...styles.text_white, ...styles.text_S }}>
Loading...
</TableCell>
<TableCell style={{ ...styles.text_white, ...styles.text_S }}>
{props.Product.history_date}
</TableCell>
</TableRow>;
}
return (
<TableRow
key={props.Product.id}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell style={{ ...styles.text_white, ...styles.text_S }}>
{props.Product.history_id}
</TableCell>
<TableCell style={{ ...styles.text_white, ...styles.text_S }}>
{props.Product.id}
</TableCell>
<TableCell style={{ ...styles.text_white, ...styles.text_S }}>
{props.Product.name}
</TableCell>
<TableCell style={{ ...styles.text_white, ...styles.text_S }}>
{props.Product.quantity}
</TableCell>
<TableCell style={{ ...styles.text_white, ...styles.text_S }}>
{isLoading || user.username}
</TableCell>
<TableCell style={{ ...styles.text_white, ...styles.text_S }}>
{props.Product.history_date}
</TableCell>
</TableRow>
);
}

View file

@ -13,11 +13,24 @@ export interface ProductLogList {
ProductLogs: ProductLog[]; ProductLogs: ProductLog[];
} }
export interface ProductLogEntry {
Product: {
history_id: number;
id: number;
name: string;
quantity: string;
history_date: string;
history_user_id: number;
};
}
export interface ProductLog { export interface ProductLog {
history_id: number; history_id: number;
id: number;
name: string; name: string;
quantity: string; quantity: string;
history_date: string; history_date: string;
history_user_id: number;
} }
// Redux Interfaces // Redux Interfaces

View file

@ -15,8 +15,16 @@ import {
GetLowestStockedProduct, GetLowestStockedProduct,
GetProducts, GetProducts,
} from "../../Components/Api/Api"; } from "../../Components/Api/Api";
import { ProductLog, SessionTransactions } from "../../Interfaces/Interfaces"; import {
OldSessionState,
ProductLog,
SessionTransactions,
} from "../../Interfaces/Interfaces";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import LowestStockWidget from "../../Components/DashboardPage/LowestStockWidget/LowestStockWidget";
import RecentlyAddedWidget from "../../Components/DashboardPage/RecentlyAddedWidget/RecentlyAddedWidget";
import TotalProductsWidget from "../../Components/DashboardPage/TotalProductsWidget/TotalProductsWidget";
import SessionStatsWidget from "../../Components/DashboardPage/SessionStatsWidget/SessionStatsWidget";
export default function Dashboard() { export default function Dashboard() {
const logs = useQuery("logs", GetLogs, { retry: 0 }); const logs = useQuery("logs", GetLogs, { retry: 0 });
@ -28,13 +36,15 @@ export default function Dashboard() {
retry: 0, retry: 0,
} }
); );
const session_added = useSelector( const old_session_checked = useSelector(
(state: SessionTransactions) => state.session_transactions.added (state: OldSessionState) => state.old_session_checked.value
); );
const session_removed = useSelector( if (
(state: SessionTransactions) => state.session_transactions.removed logs.isLoading ||
); products.isLoading ||
if (logs.isLoading || products.isLoading || lowest_stock_product.isLoading) { lowest_stock_product.isLoading ||
!old_session_checked
) {
return ( return (
<div> <div>
<LoginChecker /> <LoginChecker />
@ -79,108 +89,20 @@ export default function Dashboard() {
> >
<div style={{ flex: 7 }}> <div style={{ flex: 7 }}>
<div style={styles.flex_column}> <div style={styles.flex_column}>
<div <TotalProductsWidget Products={products.data} />
style={{
...styles.widget,
...{ flex: 5 },
}}
>
<div style={styles.content_row}>
<TotalProductsIcon size={64} color="white" />
<p style={{ ...styles.text_white, ...styles.text_XL }}>
Total Products
</p>
</div>
<p style={{ ...styles.text_white, ...styles.text_L }}>
{products.data.length} Unique Item/s
</p>
<p style={{ ...styles.text_white, ...styles.text_L }}>
In inventory
</p>
</div>
<div style={styles.flex_row}> <div style={styles.flex_row}>
<div <SessionStatsWidget />
style={{
...styles.widget,
...{ flex: 5 },
}}
>
<div style={styles.content_row}>
<StatsIcon size={64} color="white" />
<p style={{ ...styles.text_white, ...styles.text_XL }}>
Current Session
</p>
</div>
<div style={styles.content_row}>
<ColoredCube size={32} color="#a48e41" />
<p style={{ ...styles.text_white, ...styles.text_L }}>
Added
</p>
</div>
<p style={{ ...styles.text_white, ...styles.text_L }}>
{session_added}
</p>
<div style={styles.content_row}>
<ColoredCube size={32} color="#a44141" />
<p style={{ ...styles.text_white, ...styles.text_L }}>
Removed
</p>
</div>
<p style={{ ...styles.text_white, ...styles.text_XL }}>
{session_removed}
</p>
</div>
<div <div
style={{ style={{
...styles.flex_column, ...styles.flex_column,
...{ ...{
flex: 5, flex: 5,
alignSelf: "stretch",
}, },
}} }}
> >
<div <LowestStockWidget Products={lowest_stock_product.data} />
style={{ <RecentlyAddedWidget Products={products.data} />
...styles.widget,
...{ flex: 1 },
}}
>
<div style={styles.content_row}>
<LowStockIcon size={64} color="white" />
<p style={{ ...styles.text_white, ...styles.text_L }}>
Lowest Stock
</p>
</div>
<p style={{ ...styles.text_white, ...styles.text_S }}>
{lowest_stock_product.data[0].name}
</p>
<p style={{ ...styles.text_white, ...styles.text_S }}>
In Stock: {lowest_stock_product.data[0].quantity}
</p>
</div>
<div
style={{
...styles.widget,
...{ flex: 1 },
}}
>
<div style={styles.content_row}>
<RecentlyAddedIcon size={64} color="white" />
<p
style={{
...styles.text_white,
...styles.text_L,
}}
>
Recently Added
</p>
</div>
<p style={{ ...styles.text_white, ...styles.text_M }}>
{products.data[0].name}
</p>
<p style={{ ...styles.text_white, ...styles.text_S }}>
{products.data[0].date_added}
</p>
</div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -11,8 +11,49 @@ import {
} from "@mui/material"; } from "@mui/material";
import { SampleLogData } from "../../Components/SampleData/SampleData"; import { SampleLogData } from "../../Components/SampleData/SampleData";
import LoginChecker from "../../Components/LoginChecker/LoginChecker"; import LoginChecker from "../../Components/LoginChecker/LoginChecker";
import { useQuery } from "react-query";
import { GetLogs, UserInfo } from "../../Components/Api/Api";
import { OldSessionState, ProductLog } from "../../Interfaces/Interfaces";
import { useState } from "react";
import RowRenderer from "../../Components/LogsPage/RowRenderer/RowRenderer";
import { useSelector } from "react-redux";
export default function Logs() { export default function Logs() {
const logs = useQuery("logs", GetLogs, { retry: 0 });
const old_session_checked = useSelector(
(state: OldSessionState) => state.old_session_checked.value
);
if (logs.isLoading || !old_session_checked) {
return (
<div>
<LoginChecker />
<div style={styles.flex_row}>
<LogsIcon size={64} color="white" />
<p style={{ ...styles.text_white, ...styles.text_XL }}>Logs</p>
</div>
<div style={{ ...styles.content_column, ...{ alignItems: "center" } }}>
<p style={{ ...styles.text_white, ...styles.text_L }}>
Loading logs...
</p>
</div>
</div>
);
} else if (logs.error) {
return (
<div>
<LoginChecker />
<div style={styles.flex_row}>
<LogsIcon size={64} color="white" />
<p style={{ ...styles.text_white, ...styles.text_XL }}>Logs</p>
</div>
<div style={{ ...styles.content_column, ...{ alignItems: "center" } }}>
<p style={{ ...styles.text_red, ...styles.text_L }}>
Error loading logs
</p>
</div>
</div>
);
}
return ( return (
<div> <div>
<LoginChecker /> <LoginChecker />
@ -39,7 +80,10 @@ export default function Logs() {
Product Product
</TableCell> </TableCell>
<TableCell style={{ ...styles.text_white, ...styles.text_M }}> <TableCell style={{ ...styles.text_white, ...styles.text_M }}>
Amount Change Quantity
</TableCell>
<TableCell style={{ ...styles.text_white, ...styles.text_M }}>
User
</TableCell> </TableCell>
<TableCell style={{ ...styles.text_white, ...styles.text_M }}> <TableCell style={{ ...styles.text_white, ...styles.text_M }}>
Timestamp Timestamp
@ -47,34 +91,8 @@ export default function Logs() {
</TableRow> </TableRow>
</TableHead> </TableHead>
<TableBody> <TableBody>
{SampleLogData.map((row) => ( {logs.data.map((row: ProductLog, index: number) => (
<TableRow <RowRenderer key={index} Product={row} />
key={row.id}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell style={{ ...styles.text_white, ...styles.text_S }}>
{row.id}
</TableCell>
<TableCell style={{ ...styles.text_white, ...styles.text_S }}>
{row.p_id}
</TableCell>
<TableCell style={{ ...styles.text_white, ...styles.text_S }}>
{row.p_name}
</TableCell>
<TableCell
style={{
...{
color: row.amount_changed < 0 ? "#a44141" : "#80b28a",
},
...styles.text_S,
}}
>
{row.amount_changed}
</TableCell>
<TableCell style={{ ...styles.text_white, ...styles.text_S }}>
{row.timestamp}
</TableCell>
</TableRow>
))} ))}
</TableBody> </TableBody>
</Table> </Table>

View file

@ -6,6 +6,8 @@ import LoginChecker from "../../Components/LoginChecker/LoginChecker";
import { DeleteProduct, GetProduct } from "../../Components/Api/Api"; import { DeleteProduct, GetProduct } from "../../Components/Api/Api";
import { useMutation, useQuery, useQueryClient } from "react-query"; import { useMutation, useQuery, useQueryClient } from "react-query";
import ProductIcon from "../../Components/Icons/ProductIcon/ProductIcon"; import ProductIcon from "../../Components/Icons/ProductIcon/ProductIcon";
import { useSelector } from "react-redux";
import { OldSessionState } from "../../Interfaces/Interfaces";
export default function Product() { export default function Product() {
const navigate = useNavigate(); const navigate = useNavigate();
@ -25,7 +27,10 @@ export default function Product() {
queryClient.invalidateQueries("products"); queryClient.invalidateQueries("products");
}, },
}); });
if (isLoading) { const old_session_checked = useSelector(
(state: OldSessionState) => state.old_session_checked.value
);
if (isLoading || !old_session_checked) {
return ( return (
<div> <div>
<LoginChecker /> <LoginChecker />

View file

@ -9,6 +9,8 @@ import ViewManager from "../../Components/ProductsPage/ViewManager";
import { useQuery } from "react-query"; import { useQuery } from "react-query";
import { GetProducts } from "../../Components/Api/Api"; import { GetProducts } from "../../Components/Api/Api";
import LoginChecker from "../../Components/LoginChecker/LoginChecker"; import LoginChecker from "../../Components/LoginChecker/LoginChecker";
import { useSelector } from "react-redux";
import { OldSessionState } from "../../Interfaces/Interfaces";
export default function Products() { export default function Products() {
const navigate = useNavigate(); const navigate = useNavigate();
@ -17,7 +19,10 @@ export default function Products() {
isLoading, isLoading,
error, error,
} = useQuery("products", GetProducts, { retry: 0 }); } = useQuery("products", GetProducts, { retry: 0 });
if (isLoading) { const old_session_checked = useSelector(
(state: OldSessionState) => state.old_session_checked.value
);
if (isLoading || !old_session_checked) {
return ( return (
<div> <div>
<LoginChecker /> <LoginChecker />

View file

@ -18,6 +18,8 @@ import { useMutation, useQuery, useQueryClient } from "react-query";
import RowRenderer from "../../Components/InventoryPage/RowRenderer/RowRenderer"; import RowRenderer from "../../Components/InventoryPage/RowRenderer/RowRenderer";
import AddIcon from "../../Components/Icons/AddIcon/AddIcon"; import AddIcon from "../../Components/Icons/AddIcon/AddIcon";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { useSelector } from "react-redux";
import { OldSessionState } from "../../Interfaces/Interfaces";
export default function Inventory() { export default function Inventory() {
const { const {
@ -26,8 +28,10 @@ export default function Inventory() {
error, error,
} = useQuery("products", GetProducts, { retry: 0 }); } = useQuery("products", GetProducts, { retry: 0 });
const navigate = useNavigate(); const navigate = useNavigate();
const old_session_checked = useSelector(
if (isLoading) { (state: OldSessionState) => state.old_session_checked.value
);
if (isLoading || !old_session_checked) {
return ( return (
<div> <div>
<LoginChecker /> <LoginChecker />