Merge pull request #8 from lemeow125/feature/logs_daily

Feature/logs daily
This commit is contained in:
lemeow125 2023-03-25 12:28:38 +08:00 committed by GitHub
commit fdfeef3589
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 489 additions and 276 deletions

20
package-lock.json generated
View file

@ -28,6 +28,7 @@
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-fast-marquee": "^1.3.5", "react-fast-marquee": "^1.3.5",
"react-moment": "^1.1.3",
"react-query": "^3.39.3", "react-query": "^3.39.3",
"react-redux": "^8.0.5", "react-redux": "^8.0.5",
"react-router-dom": "^6.8.1", "react-router-dom": "^6.8.1",
@ -12592,6 +12593,15 @@
"mkdirp": "bin/cmd.js" "mkdirp": "bin/cmd.js"
} }
}, },
"node_modules/moment": {
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
"peer": true,
"engines": {
"node": "*"
}
},
"node_modules/ms": { "node_modules/ms": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@ -14881,6 +14891,16 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
}, },
"node_modules/react-moment": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/react-moment/-/react-moment-1.1.3.tgz",
"integrity": "sha512-8EPvlUL8u6EknPp1ISF5MQ3wx2OHJVXIP/iZc4wRh3iV3XozftZERDv9ANZeAtMlhNNQHdFoqcZHFUkBSTONfA==",
"peerDependencies": {
"moment": "^2.29.0",
"prop-types": "^15.7.0",
"react": "^16.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-query": { "node_modules/react-query": {
"version": "3.39.3", "version": "3.39.3",
"resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz", "resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz",

View file

@ -24,6 +24,7 @@
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-fast-marquee": "^1.3.5", "react-fast-marquee": "^1.3.5",
"react-moment": "^1.1.3",
"react-query": "^3.39.3", "react-query": "^3.39.3",
"react-redux": "^8.0.5", "react-redux": "^8.0.5",
"react-router-dom": "^6.8.1", "react-router-dom": "^6.8.1",

View file

@ -22,6 +22,7 @@ import Activation from "./Routes/Activation/Activation";
import { QueryClient, QueryClientProvider } from "react-query"; import { QueryClient, QueryClientProvider } from "react-query";
import NewProduct from "./Routes/NewProduct/NewProduct"; import NewProduct from "./Routes/NewProduct/NewProduct";
import Register from "./Routes/Register/Register"; import Register from "./Routes/Register/Register";
import DailyView from "./Routes/DailyView/DailyView";
const queryClient = new QueryClient(); const queryClient = new QueryClient();
@ -103,6 +104,14 @@ const router = createHashRouter([
</Container> </Container>
), ),
}, },
{
path: "/ActivityToday",
element: (
<Container>
<DailyView />
</Container>
),
},
]); ]);
export default function App() { export default function App() {

View file

@ -8,11 +8,14 @@ import {
} from "../../Interfaces/Interfaces"; } from "../../Interfaces/Interfaces";
// Product APIs // Product APIs
const instance = axios.create({
baseURL: "https://keannu125.pythonanywhere.com",
});
export function GetProducts() { export function GetProducts() {
const token = JSON.parse(localStorage.getItem("token") || "{}"); const token = JSON.parse(localStorage.getItem("token") || "{}");
return axios return instance
.get("https://keannu125.pythonanywhere.com/api/v1/products/", { .get("api/v1/products/", {
headers: { headers: {
Authorization: "Token " + token, Authorization: "Token " + token,
}, },
@ -24,8 +27,8 @@ export function GetProducts() {
export function GetProduct(id: number) { export function GetProduct(id: number) {
const token = JSON.parse(localStorage.getItem("token") || "{}"); const token = JSON.parse(localStorage.getItem("token") || "{}");
return axios return instance
.get("https://keannu125.pythonanywhere.com/api/v1/products/" + id + "/", { .get("api/v1/products/" + id + "/", {
headers: { headers: {
Authorization: "Token " + token, Authorization: "Token " + token,
}, },
@ -37,18 +40,12 @@ export function GetProduct(id: number) {
export function UpdateProduct(product: UpdateProductParams) { export function UpdateProduct(product: UpdateProductParams) {
const token = JSON.parse(localStorage.getItem("token") || "{}"); const token = JSON.parse(localStorage.getItem("token") || "{}");
return axios return instance
.patch( .patch("api/v1/products/" + product.id + "/", product, {
"https://keannu125.pythonanywhere.com/api/v1/products/" +
product.id +
"/",
product,
{
headers: { headers: {
Authorization: "Token " + token, Authorization: "Token " + token,
}, },
} })
)
.then((response) => { .then((response) => {
console.log("Product update successful"); console.log("Product update successful");
return response.data; return response.data;
@ -61,8 +58,8 @@ export function UpdateProduct(product: UpdateProductParams) {
export function GetLowestStockedProduct() { export function GetLowestStockedProduct() {
const token = JSON.parse(localStorage.getItem("token") || "{}"); const token = JSON.parse(localStorage.getItem("token") || "{}");
return axios return instance
.get("https://keannu125.pythonanywhere.com/api/v1/lowest_stock_product/", { .get("api/v1/lowest_stock_product/", {
headers: { headers: {
Authorization: "Token " + token, Authorization: "Token " + token,
}, },
@ -74,8 +71,8 @@ export function GetLowestStockedProduct() {
export function GetLogs() { export function GetLogs() {
const token = JSON.parse(localStorage.getItem("token") || "{}"); const token = JSON.parse(localStorage.getItem("token") || "{}");
return axios return instance
.get("https://keannu125.pythonanywhere.com/api/v1/logs/", { .get("api/v1/logs/", {
headers: { headers: {
Authorization: "Token " + token, Authorization: "Token " + token,
}, },
@ -87,8 +84,8 @@ export function GetLogs() {
export function AddProduct(note: AddProductParams) { export function AddProduct(note: AddProductParams) {
const token = JSON.parse(localStorage.getItem("token") || "{}"); const token = JSON.parse(localStorage.getItem("token") || "{}");
return axios return instance
.post("https://keannu125.pythonanywhere.com/api/v1/products/", note, { .post("api/v1/products/", note, {
headers: { headers: {
Authorization: "Token " + token, Authorization: "Token " + token,
}, },
@ -104,15 +101,12 @@ export function AddProduct(note: AddProductParams) {
export function DeleteProduct(id: number) { export function DeleteProduct(id: number) {
const token = JSON.parse(localStorage.getItem("token") || "{}"); const token = JSON.parse(localStorage.getItem("token") || "{}");
return axios return instance
.delete( .delete("api/v1/products/" + id + "/", {
"https://keannu125.pythonanywhere.com/api/v1/products/" + id + "/",
{
headers: { headers: {
Authorization: "Token " + token, Authorization: "Token " + token,
}, },
} })
)
.catch((error) => { .catch((error) => {
console.log("Error deleting product"); console.log("Error deleting product");
return error; return error;
@ -122,11 +116,8 @@ export function DeleteProduct(id: number) {
// User APIs // User APIs
export function UserRegister(register: RegistrationParams) { export function UserRegister(register: RegistrationParams) {
return axios return instance
.post( .post("api/v1/accounts/users/", register)
"https://keannu125.pythonanywhere.com/api/v1/accounts/users/",
register
)
.then(async (response) => { .then(async (response) => {
console.log(response.data); console.log(response.data);
return true; return true;
@ -138,11 +129,8 @@ export function UserRegister(register: RegistrationParams) {
} }
export function UserLogin(user: LoginParams) { export function UserLogin(user: LoginParams) {
return axios return instance
.post( .post("api/v1/accounts/token/login/", user)
"https://keannu125.pythonanywhere.com/api/v1/accounts/token/login/",
user
)
.then(async (response) => { .then(async (response) => {
localStorage.setItem("token", JSON.stringify(response.data.auth_token)); localStorage.setItem("token", JSON.stringify(response.data.auth_token));
console.log("Login Success "); console.log("Login Success ");
@ -156,8 +144,8 @@ export function UserLogin(user: LoginParams) {
export function UserInfo() { export function UserInfo() {
const token = JSON.parse(localStorage.getItem("token") || "{}"); const token = JSON.parse(localStorage.getItem("token") || "{}");
return axios return instance
.get("https://keannu125.pythonanywhere.com/api/v1/accounts/users/me/", { .get("api/v1/accounts/users/me/", {
headers: { headers: {
Authorization: "Token " + token, Authorization: "Token " + token,
}, },
@ -174,8 +162,8 @@ export function UserInfo() {
export function QueryUser(id: number) { export function QueryUser(id: number) {
const token = JSON.parse(localStorage.getItem("token") || "{}"); const token = JSON.parse(localStorage.getItem("token") || "{}");
return axios return instance
.get("https://keannu125.pythonanywhere.com/api/v1/user_list/" + id, { .get("api/v1/user_list/" + id, {
headers: { headers: {
Authorization: "Token " + token, Authorization: "Token " + token,
}, },
@ -191,11 +179,8 @@ export function QueryUser(id: number) {
} }
export function UserActivate(activation: ActivationParams) { export function UserActivate(activation: ActivationParams) {
return axios return instance
.post( .post("api/v1/accounts/users/activation/", activation)
"https://keannu125.pythonanywhere.com/api/v1/accounts/users/activation/",
activation
)
.then(async (response) => { .then(async (response) => {
console.log("Activation Success"); console.log("Activation Success");
return true; return true;

View file

@ -0,0 +1,42 @@
import * as React from "react";
import { ProductLog, ProductLogList } from "../../../Interfaces/Interfaces";
import styles from "../../../styles";
import LogsIcon from "../../Icons/LogsIcon/LogsIcon";
import Moment from "react-moment";
export default function RecentTransactionsWidget(props: ProductLogList) {
return (
<div style={{ flex: 3 }}>
<div style={styles.widget}>
<div style={styles.content_row}>
<LogsIcon size={64} color="white" />
<div style={styles.wrapper_column}>
<p style={{ ...styles.text_white, ...styles.text_L }}>Recent</p>
<p style={{ ...styles.text_white, ...styles.text_L }}>
Transactions
</p>
</div>
</div>
{props.ProductLogs.slice(0, 5).map((log: ProductLog, index: number) => {
return (
<div key={index}>
<div style={{ marginBottom: "8px" }} />
<p style={{ ...styles.text_white, ...styles.text_M }}>
{log.name}
</p>
<p style={{ ...styles.text_white, ...styles.text_S }}>
Quantity: {log.quantity}
</p>
<p style={{ ...styles.text_white, ...styles.text_S }}>
{log.history_date}
</p>
<p style={{ ...styles.text_white, ...styles.text_XS }}>
Transaction ID: {log.history_id}
</p>
</div>
);
})}
</div>
</div>
);
}

View file

@ -3,16 +3,16 @@ import styles from "../../../styles";
import ColoredCube from "../../ColoredCube/ColoredCube"; import ColoredCube from "../../ColoredCube/ColoredCube";
import StatsIcon from "../../Icons/StatsIcon/StatsIcon"; import StatsIcon from "../../Icons/StatsIcon/StatsIcon";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { SessionTransactions } from "../../../Interfaces/Interfaces"; import { RootState } from "../../../Plugins/Redux/Store/Store";
export interface props {} export interface props {}
export default function SessionStatsWidget() { export default function SessionStatsWidget() {
const session_added = useSelector( const session_added = useSelector(
(state: SessionTransactions) => state.session_transactions.added (state: RootState) => state.session_transactions.added
); );
const session_removed = useSelector( const session_removed = useSelector(
(state: SessionTransactions) => state.session_transactions.removed (state: RootState) => state.session_transactions.removed
); );
return ( return (
<div <div

View file

@ -0,0 +1,11 @@
export default function GetToday() {
const current = new Date();
const date =
("0" + (current.getMonth() + 1)).slice(-2) +
"-" +
("0" + current.getDate()).slice(-2) +
"-" +
current.getFullYear();
console.log("Today is " + date);
return date;
}

View file

@ -0,0 +1,27 @@
import React from "react";
export interface props {
size: number;
color: string;
}
export default function TodayIcon(props: props) {
return (
<svg
width={props.size}
height={props.size}
viewBox="0 0 24 24"
strokeWidth="2"
stroke={props.color}
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4"></path>
<path d="M4 13a8.094 8.094 0 0 0 3 5.24"></path>
<path d="M11 15h2a1 1 0 0 1 1 1v1a1 1 0 0 1 -1 1h-1a1 1 0 0 0 -1 1v1a1 1 0 0 0 1 1h2"></path>
<path d="M17 15v2a1 1 0 0 0 1 1h1"></path>
<path d="M20 15v6"></path>
</svg>
);
}

View file

@ -2,13 +2,12 @@ import { useSelector } from "react-redux";
import { Button } from "@mui/material"; import { Button } from "@mui/material";
import styles from "../../styles"; import styles from "../../styles";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { LoggedInUserState } from "../../Interfaces/Interfaces"; import { RootState } from "../../Plugins/Redux/Store/Store";
import { LoginState } from "../../Interfaces/Interfaces";
export default function Login() { export default function Login() {
const logged_in = useSelector((state: LoginState) => state.logged_in.value); const logged_in = useSelector((state: RootState) => state.logged_in.value);
const logged_in_user = useSelector( const logged_in_user = useSelector(
(state: LoggedInUserState) => state.logged_in_user.value (state: RootState) => state.logged_in_user.value
); );
const navigate = useNavigate(); const navigate = useNavigate();
if (logged_in) { if (logged_in) {

View file

@ -2,14 +2,14 @@ import * as React from "react";
import { Navigate } from "react-router-dom"; import { Navigate } from "react-router-dom";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { LoginState, OldSessionState } from "../../Interfaces/Interfaces"; import { RootState } from "../../Plugins/Redux/Store/Store";
export interface props {} export interface props {}
export default function LoginChecker() { export default function LoginChecker() {
const logged_in = useSelector((state: LoginState) => state.logged_in.value); const logged_in = useSelector((state: RootState) => state.logged_in.value);
const old_session_checked = useSelector( const old_session_checked = useSelector(
(state: OldSessionState) => state.old_session_checked.value (state: RootState) => state.old_session_checked.value
); );
if (!logged_in && !old_session_checked) { if (!logged_in && !old_session_checked) {
} else if (!logged_in && old_session_checked) { } else if (!logged_in && old_session_checked) {

View file

@ -1,17 +1,16 @@
import * as React from "react"; import * as React from "react";
import { useEffect } from "react"; import { useEffect, useCallback } from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { UserInfo } from "../Api/Api"; import { UserInfo } from "../Api/Api";
import { toggle_login } from "../../Features/Redux/Slices/Login/LoginSlice"; import { toggle_login } from "../../Features/Redux/Slices/Login/LoginSlice";
import { SetUser } from "../../Features/Redux/Slices/LoggedInUserSlice/LoggedInUserSlice"; import { SetUser } from "../../Features/Redux/Slices/LoggedInUserSlice/LoggedInUserSlice";
import { LoginState } from "../../Interfaces/Interfaces";
import { set_checked } from "../../Features/Redux/Slices/OldSession/OldSessionSlice"; import { set_checked } from "../../Features/Redux/Slices/OldSession/OldSessionSlice";
import { RootState } from "../../Plugins/Redux/Store/Store";
export default function PreviousSessionChecker() { export default function PreviousSessionChecker() {
const dispatch = useDispatch(); const dispatch = useDispatch();
const logged_in = useSelector((state: LoginState) => state.logged_in.value); const logged_in = useSelector((state: RootState) => state.logged_in.value);
// Function to check for previous login session // Function to check for previous login session
useEffect(() => { const check = useCallback(async () => {
async function check() {
if (await UserInfo()) { if (await UserInfo()) {
if (logged_in !== true) { if (logged_in !== true) {
console.log("Previous session found. Restoring"); console.log("Previous session found. Restoring");
@ -23,8 +22,11 @@ export default function PreviousSessionChecker() {
localStorage.removeItem("token"); localStorage.removeItem("token");
} }
await dispatch(set_checked()); await dispatch(set_checked());
} }, [dispatch, logged_in]);
useEffect(() => {
if (!logged_in) {
check(); check();
}, []); }
}, [check, logged_in]);
return <div />; return <div />;
} }

View file

@ -21,7 +21,7 @@ export default function BlobView({ Products }: ProductList) {
padding: 16, padding: 16,
}} }}
> >
<Button <button
style={{ style={{
lineHeight: 0, lineHeight: 0,
display: "flex", display: "flex",
@ -29,6 +29,8 @@ export default function BlobView({ Products }: ProductList) {
justifySelf: "flex-start", justifySelf: "flex-start",
alignItems: "flex-start", alignItems: "flex-start",
width: "100%", width: "100%",
border: "none",
background: "none",
}} }}
onClick={() => navigate("/Product/" + row.id)} onClick={() => navigate("/Product/" + row.id)}
> >
@ -45,7 +47,7 @@ export default function BlobView({ Products }: ProductList) {
<p style={{ ...styles.text_white, ...styles.text_S }}> <p style={{ ...styles.text_white, ...styles.text_S }}>
Date Added: {row.date_added} Date Added: {row.date_added}
</p> </p>
</Button> </button>
</div> </div>
))} ))}
</div> </div>

View file

@ -9,97 +9,64 @@ import Logout from "../Logout/Logout";
import InventoryIcon from "../Icons/InventoryIcon/InventoryIcon"; import InventoryIcon from "../Icons/InventoryIcon/InventoryIcon";
import LogsIcon from "../Icons/LogsIcon/LogsIcon"; import LogsIcon from "../Icons/LogsIcon/LogsIcon";
import LogoutIcon from "../Icons/LogoutIcon/LogoutIcon"; import LogoutIcon from "../Icons/LogoutIcon/LogoutIcon";
import TodayIcon from "../Icons/TodayIcon/TodayIcon";
import { RootState } from "../../Plugins/Redux/Store/Store";
export interface state {
logged_in: {
value: boolean;
};
}
export default function Sidebar() { export default function Sidebar() {
const navigate = useNavigate(); const navigate = useNavigate();
const logged_in = useSelector((state: state) => state.logged_in.value); const logged_in = useSelector((state: RootState) => state.logged_in.value);
function LogoutButton() {
if (!logged_in) { if (!logged_in) {
return ( return <div />;
<div style={styles.sidebar_container}>
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "flex-end",
}}
>
<div
style={{
position: "relative",
display: "flex",
flexDirection: "column",
justifyContent: "flex-start",
}}
>
<SidebarButton onClick={() => navigate("/")} name="Dashboard">
<HomeIcon size={48} color="white" />
</SidebarButton>
<SidebarButton
onClick={() => navigate("/Products")}
name="Products"
>
<ProductsIcon size={48} color="white" />
</SidebarButton>
<SidebarButton
onClick={() => navigate("/Inventory")}
name="Inventory"
>
<InventoryIcon size={48} color="white" />
</SidebarButton>
<SidebarButton onClick={() => navigate("/Logs")} name="Logs">
<LogsIcon size={48} color="white" />
</SidebarButton>
</div>
</div>
</div>
);
} else { } else {
return ( return (
<div style={styles.sidebar_container}>
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "flex-end",
}}
>
<div
style={{
position: "relative",
display: "flex",
flexDirection: "column",
justifyContent: "flex-start",
}}
>
<SidebarButton onClick={() => navigate("/")} name="Dashboard">
<HomeIcon size={48} color="white" />
</SidebarButton>
<SidebarButton
onClick={() => navigate("/Products")}
name="Products"
>
<ProductsIcon size={48} color="white" />
</SidebarButton>
<SidebarButton
onClick={() => navigate("/Inventory")}
name="Inventory"
>
<InventoryIcon size={48} color="white" />
</SidebarButton>
<SidebarButton onClick={() => navigate("/Logs")} name="Logs">
<LogsIcon size={48} color="white" />
</SidebarButton>
<Logout> <Logout>
<LogoutIcon size={48} color="white" /> <LogoutIcon size={48} color="white" />
</Logout> </Logout>
);
}
}
return (
<div style={styles.sidebar_container}>
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "flex-end",
}}
>
<div
style={{
position: "relative",
display: "flex",
flexDirection: "column",
justifyContent: "flex-start",
}}
>
<SidebarButton onClick={() => navigate("/")} name="Dashboard">
<HomeIcon size={48} color="white" />
</SidebarButton>
<SidebarButton onClick={() => navigate("/Products")} name="Products">
<ProductsIcon size={48} color="white" />
</SidebarButton>
<SidebarButton
onClick={() => navigate("/Inventory")}
name="Inventory"
>
<InventoryIcon size={48} color="white" />
</SidebarButton>
<SidebarButton
onClick={() => navigate("/ActivityToday")}
name="Summary"
>
<TodayIcon size={48} color="white" />
</SidebarButton>
<SidebarButton onClick={() => navigate("/Logs")} name="Logs">
<LogsIcon size={48} color="white" />
</SidebarButton>
<LogoutButton />
</div> </div>
</div> </div>
</div> </div>
); );
}
} }

View file

@ -7,6 +7,12 @@ export interface Product {
name: string; name: string;
date_added: string; date_added: string;
quantity: number; quantity: number;
history: ProductHistoryEntry[];
}
export interface ProductHistoryEntry {
quantity: number;
history_date: string;
} }
export interface ProductLogList { export interface ProductLogList {
@ -33,36 +39,6 @@ export interface ProductLog {
history_user: string; history_user: string;
} }
// Redux Interfaces
export interface LoginState {
logged_in: {
value: boolean;
};
}
export interface OldSessionState {
old_session_checked: {
value: boolean;
};
}
export interface LoggedInUserState {
logged_in_user: {
value: {
email: string;
id: number;
username: string;
};
};
}
export interface SessionTransactions {
session_transactions: {
added: number;
removed: number;
};
}
// Component Props Interfaces // Component Props Interfaces
export interface IconProps { export interface IconProps {

View file

@ -4,7 +4,7 @@ import LoggedInUserReducer from "../../../Features/Redux/Slices/LoggedInUserSlic
import OldSessionReducer from "../../../Features/Redux/Slices/OldSession/OldSessionSlice"; import OldSessionReducer from "../../../Features/Redux/Slices/OldSession/OldSessionSlice";
import TransactionsThisSessionReducer from "../../../Features/Redux/Slices/TransactionsThisSessionSlice/TransactionsThisSessionSlice"; import TransactionsThisSessionReducer from "../../../Features/Redux/Slices/TransactionsThisSessionSlice/TransactionsThisSessionSlice";
export default configureStore({ const store = configureStore({
reducer: { reducer: {
logged_in: LoginReducer, logged_in: LoginReducer,
logged_in_user: LoggedInUserReducer, logged_in_user: LoggedInUserReducer,
@ -12,3 +12,10 @@ export default configureStore({
session_transactions: TransactionsThisSessionReducer, session_transactions: TransactionsThisSessionReducer,
}, },
}); });
export default store;
// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>;
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch;

View file

@ -0,0 +1,167 @@
import React from "react";
import "../../index.css";
import styles from "../../styles";
import LoginChecker from "../../Components/LoginChecker/LoginChecker";
import { useQuery } from "react-query";
import {
GetLogs,
GetLowestStockedProduct,
GetProducts,
} from "../../Components/Api/Api";
import { Product } from "../../Interfaces/Interfaces";
import { useSelector } from "react-redux";
import TodayIcon from "../../Components/Icons/TodayIcon/TodayIcon";
import moment from "moment";
import GetToday from "../../Components/GetToday/GetToday";
import Moment from "react-moment";
import { useNavigate } from "react-router-dom";
import {
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
} from "@mui/material";
import { RootState } from "../../Plugins/Redux/Store/Store";
export default function DailyView() {
const logs = useQuery("logs", GetLogs, { retry: 0 });
const products = useQuery("products", GetProducts, { retry: 0 });
const lowest_stock_product = useQuery(
"lowest_stock_product",
GetLowestStockedProduct,
{
retry: 0,
}
);
const old_session_checked = useSelector(
(state: RootState) => state.old_session_checked.value
);
const navigate = useNavigate();
if (
logs.isLoading ||
products.isLoading ||
lowest_stock_product.isLoading ||
!old_session_checked
) {
return (
<div>
<LoginChecker />
<div style={styles.flex_row}>
<TodayIcon size={64} color="white" />
<p style={{ ...styles.text_white, ...styles.text_XL }}>Daily View</p>
</div>
<div style={{ ...styles.content_column, ...{ alignItems: "center" } }}>
<p style={{ ...styles.text_white, ...styles.text_L }}>
Loading today's summary...
</p>
</div>
</div>
);
}
if (logs.error || products.error || lowest_stock_product.isError) {
<div>
<LoginChecker />
<div style={styles.flex_row}>
<TodayIcon size={64} color="white" />
<p style={{ ...styles.text_white, ...styles.text_XL }}>Daily View</p>
</div>
<div style={{ ...styles.content_column, ...{ alignItems: "center" } }}>
<p style={{ ...styles.text_red, ...styles.text_L }}>
Error loading today's summary
</p>
</div>
</div>;
}
return (
<div>
<LoginChecker />
<div style={styles.flex_row}>
<TodayIcon size={64} color="white" />
<p style={{ ...styles.text_white, ...styles.text_XL }}>
Products Modified Today
</p>
</div>
<div style={styles.content_column}>
{products.data
.filter(
(Product: Product) =>
moment(Product.history[0].history_date).format("MM-DD-YYYY") ===
GetToday()
)
.map((Product: Product) => (
<button
style={{
...styles.widget,
...{ border: "None" },
}}
onClick={() => navigate("/Product/" + Product.id)}
>
<p style={{ ...styles.text_white, ...styles.text_XL }}>
Product: {Product.name}
</p>
<p style={{ ...styles.text_white, ...styles.text_XL }}>
Last Modified:{" "}
<Moment date={Product.history[0].history_date} fromNow />
</p>
<p style={{ ...styles.text_white, ...styles.text_XL }}>
Current Stock: {Product.history[0].quantity}
</p>
<p style={{ ...styles.text_white, ...styles.text_XL }}>
Daily Summary
</p>
<div style={{ alignSelf: "center" }}>
{" "}
<TableContainer>
<TableHead>
<TableRow>
<TableCell
style={{ ...styles.text_white, ...styles.text_M }}
>
Time
</TableCell>
<TableCell
style={{ ...styles.text_white, ...styles.text_M }}
>
Quantity
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{Product.history
.filter(
(history_entry) =>
moment(history_entry.history_date).format(
"MM-DD-YYYY"
) === GetToday()
)
.map((history_entry, index: number) => (
<TableRow
key={index}
sx={{
"&:last-child td, &:last-child th": { border: 0 },
}}
>
<TableCell
style={{ ...styles.text_white, ...styles.text_S }}
>
{moment(history_entry.history_date).format(
"hh:mm A"
)}
</TableCell>
<TableCell
style={{ ...styles.text_white, ...styles.text_S }}
>
{history_entry.quantity}
</TableCell>
</TableRow>
))}
</TableBody>
</TableContainer>
</div>
</button>
))}
</div>
</div>
);
}

View file

@ -1,13 +1,7 @@
import React, { useState } from "react"; import React from "react";
import TotalProductsIcon from "../../Components/Icons/TotalProductsIcon/TotalProductsIcon";
import LowStockIcon from "../../Components/Icons/LowStockIcon/LowStockIcon";
import StatsIcon from "../../Components/Icons/StatsIcon/StatsIcon";
import LogsIcon from "../../Components/Icons/LogsIcon/LogsIcon";
import "../../index.css"; import "../../index.css";
import styles from "../../styles"; import styles from "../../styles";
import HomeIcon from "../../Components/Icons/HomeIcon/HomeIcon"; import HomeIcon from "../../Components/Icons/HomeIcon/HomeIcon";
import ColoredCube from "../../Components/ColoredCube/ColoredCube";
import RecentlyAddedIcon from "../../Components/Icons/RecentlyAddedIcon/RecentlyAddedIcon";
import LoginChecker from "../../Components/LoginChecker/LoginChecker"; import LoginChecker from "../../Components/LoginChecker/LoginChecker";
import { useQuery } from "react-query"; import { useQuery } from "react-query";
import { import {
@ -15,16 +9,13 @@ import {
GetLowestStockedProduct, GetLowestStockedProduct,
GetProducts, GetProducts,
} from "../../Components/Api/Api"; } from "../../Components/Api/Api";
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 LowestStockWidget from "../../Components/DashboardPage/LowestStockWidget/LowestStockWidget";
import RecentlyAddedWidget from "../../Components/DashboardPage/RecentlyAddedWidget/RecentlyAddedWidget"; import RecentlyAddedWidget from "../../Components/DashboardPage/RecentlyAddedWidget/RecentlyAddedWidget";
import TotalProductsWidget from "../../Components/DashboardPage/TotalProductsWidget/TotalProductsWidget"; import TotalProductsWidget from "../../Components/DashboardPage/TotalProductsWidget/TotalProductsWidget";
import SessionStatsWidget from "../../Components/DashboardPage/SessionStatsWidget/SessionStatsWidget"; import SessionStatsWidget from "../../Components/DashboardPage/SessionStatsWidget/SessionStatsWidget";
import RecentTransactionsWidget from "../../Components/DashboardPage/RecentTransactionsWidget/RecentTransactionsWidget";
import { RootState } from "../../Plugins/Redux/Store/Store";
export default function Dashboard() { export default function Dashboard() {
const logs = useQuery("logs", GetLogs, { retry: 0 }); const logs = useQuery("logs", GetLogs, { retry: 0 });
@ -37,7 +28,7 @@ export default function Dashboard() {
} }
); );
const old_session_checked = useSelector( const old_session_checked = useSelector(
(state: OldSessionState) => state.old_session_checked.value (state: RootState) => state.old_session_checked.value
); );
if ( if (
logs.isLoading || logs.isLoading ||
@ -107,38 +98,7 @@ export default function Dashboard() {
</div> </div>
</div> </div>
</div> </div>
<div style={{ flex: 3 }}> <RecentTransactionsWidget ProductLogs={logs.data} />
<div style={styles.widget}>
<div style={styles.content_row}>
<LogsIcon size={64} color="white" />
<div style={styles.wrapper_column}>
<p style={{ ...styles.text_white, ...styles.text_L }}>Recent</p>
<p style={{ ...styles.text_white, ...styles.text_L }}>
Transactions
</p>
</div>
</div>
{logs.data.slice(0, 5).map((log: ProductLog, index: number) => {
return (
<div key={index}>
<div style={{ marginBottom: "8px" }} />
<p style={{ ...styles.text_white, ...styles.text_M }}>
{log.name}
</p>
<p style={{ ...styles.text_white, ...styles.text_S }}>
Quantity: {log.quantity}
</p>
<p style={{ ...styles.text_white, ...styles.text_S }}>
Date: {log.history_date}
</p>
<p style={{ ...styles.text_white, ...styles.text_XS }}>
Transaction ID: {log.history_id}
</p>
</div>
);
})}
</div>
</div>
</div> </div>
</div> </div>
); );

View file

@ -10,23 +10,19 @@ import {
TableHead, TableHead,
TableRow, TableRow,
} from "@mui/material"; } from "@mui/material";
import { SampleLogData } from "../../Components/SampleData/SampleData";
import LoginChecker from "../../Components/LoginChecker/LoginChecker"; import LoginChecker from "../../Components/LoginChecker/LoginChecker";
import { useQuery } from "react-query"; import { useQuery } from "react-query";
import { GetLogs, UserInfo } from "../../Components/Api/Api"; import { GetLogs } from "../../Components/Api/Api";
import { import { ProductLog } from "../../Interfaces/Interfaces";
OldSessionState,
ProductLog,
ProductLogEntry,
} from "../../Interfaces/Interfaces";
import { useState } from "react"; import { useState } from "react";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import GetToday from "../../Components/LogsPage/GetToday/GetToday"; import GetToday from "../../Components/LogsPage/GetToday/GetToday";
import { RootState } from "../../Plugins/Redux/Store/Store";
export default function Logs() { export default function Logs() {
const logs = useQuery("logs", GetLogs, { retry: 0 }); const logs = useQuery("logs", GetLogs, { retry: 0 });
const old_session_checked = useSelector( const old_session_checked = useSelector(
(state: OldSessionState) => state.old_session_checked.value (state: RootState) => state.old_session_checked.value
); );
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState("");
const [searchToday, setSearchToday] = useState(""); const [searchToday, setSearchToday] = useState("");

View file

@ -1,13 +1,22 @@
import * as React from "react"; import * as React from "react";
import styles from "../../styles"; import styles from "../../styles";
import { Button } from "@mui/material"; import {
Button,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
} from "@mui/material";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import LoginChecker from "../../Components/LoginChecker/LoginChecker"; 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 { useSelector } from "react-redux";
import { OldSessionState } from "../../Interfaces/Interfaces"; import { ProductHistoryEntry } from "../../Interfaces/Interfaces";
import moment from "moment";
import { RootState } from "../../Plugins/Redux/Store/Store";
export default function Product() { export default function Product() {
const navigate = useNavigate(); const navigate = useNavigate();
@ -30,7 +39,7 @@ export default function Product() {
}, },
}); });
const old_session_checked = useSelector( const old_session_checked = useSelector(
(state: OldSessionState) => state.old_session_checked.value (state: RootState) => state.old_session_checked.value
); );
if (isLoading || !old_session_checked) { if (isLoading || !old_session_checked) {
return ( return (
@ -91,6 +100,48 @@ export default function Product() {
> >
Delete Product Delete Product
</Button> </Button>
<p style={{ ...styles.text_white, ...styles.text_XL }}>
Individual Transaction Log
</p>
<div style={{ alignSelf: "center" }}>
<TableContainer>
<TableHead>
<TableRow>
<TableCell style={{ ...styles.text_white, ...styles.text_M }}>
Time
</TableCell>
<TableCell style={{ ...styles.text_white, ...styles.text_M }}>
Quantity
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{product.history.map(
(history_entry: ProductHistoryEntry, index: number) => (
<TableRow
key={index}
sx={{
"&:last-child td, &:last-child th": { border: 0 },
}}
>
<TableCell
style={{ ...styles.text_white, ...styles.text_S }}
>
{moment(history_entry.history_date).format(
"MMMM DD, YYYY hh:mm A"
)}
</TableCell>
<TableCell
style={{ ...styles.text_white, ...styles.text_S }}
>
{history_entry.quantity}
</TableCell>
</TableRow>
)
)}
</TableBody>
</TableContainer>
</div>
</div> </div>
</div> </div>
); );

View file

@ -1,16 +1,15 @@
import React, { useState } from "react"; import React from "react";
import styles from "../../styles"; import styles from "../../styles";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import ProductsIcon from "../../Components/Icons/ProductsIcon/ProductsIcon"; import ProductsIcon from "../../Components/Icons/ProductsIcon/ProductsIcon";
import AddIcon from "../../Components/Icons/AddIcon/AddIcon"; import AddIcon from "../../Components/Icons/AddIcon/AddIcon";
import { Button } from "@mui/material"; import { Button } from "@mui/material";
import { SampleProducts } from "../../Components/SampleData/SampleData";
import ViewManager from "../../Components/ProductsPage/ViewManager"; 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 { useSelector } from "react-redux";
import { OldSessionState } from "../../Interfaces/Interfaces"; import { RootState } from "../../Plugins/Redux/Store/Store";
export default function Products() { export default function Products() {
const navigate = useNavigate(); const navigate = useNavigate();
@ -20,7 +19,7 @@ export default function Products() {
error, error,
} = useQuery("products", GetProducts, { retry: 0 }); } = useQuery("products", GetProducts, { retry: 0 });
const old_session_checked = useSelector( const old_session_checked = useSelector(
(state: OldSessionState) => state.old_session_checked.value (state: RootState) => state.old_session_checked.value
); );
if (isLoading || !old_session_checked) { if (isLoading || !old_session_checked) {
return ( return (

View file

@ -1,14 +1,9 @@
import * as React from "react"; import * as React from "react";
import styles from "../../styles"; import styles from "../../styles";
import LoginIcon from "../../Components/Icons/LoginIcon/LoginIcon";
import { Button } from "@mui/material"; import { Button } from "@mui/material";
import { useDispatch } from "react-redux";
import { useNavigate } from "react-router-dom";
import { useState } from "react"; import { useState } from "react";
import { UserRegister } from "../../Components/Api/Api"; import { UserRegister } from "../../Components/Api/Api";
export default function Register() { export default function Register() {
const navigate = useNavigate();
const dispatch = useDispatch();
const [user, setUser] = useState({ const [user, setUser] = useState({
email: "", email: "",
username: "", username: "",
@ -65,11 +60,6 @@ export default function Register() {
style={styles.login_button} style={styles.login_button}
variant="contained" variant="contained"
onClick={async () => { onClick={async () => {
setUser({
email: "",
username: "",
password: "",
});
if (await UserRegister(user)) { if (await UserRegister(user)) {
setFeedback( setFeedback(
"Registration success. Please check your email address for activation" "Registration success. Please check your email address for activation"
@ -77,6 +67,11 @@ export default function Register() {
} else { } else {
setFeedback("Invalid credentials specified"); setFeedback("Invalid credentials specified");
} }
setUser({
email: "",
username: "",
password: "",
});
}} }}
> >
Register Register

View file

@ -4,22 +4,19 @@ import InventoryIcon from "../../Components/Icons/InventoryIcon/InventoryIcon";
import { import {
Button, Button,
Table, Table,
TableBody,
TableCell, TableCell,
TableContainer, TableContainer,
TableHead, TableHead,
TableRow, TableRow,
} from "@mui/material"; } from "@mui/material";
import { SampleInventoryData } from "../../Components/SampleData/SampleData";
import StockRenderer from "../../Components/InventoryPage/StockRenderer/StockRenderer";
import LoginChecker from "../../Components/LoginChecker/LoginChecker"; import LoginChecker from "../../Components/LoginChecker/LoginChecker";
import { GetProducts, UpdateProduct } from "../../Components/Api/Api"; import { GetProducts } from "../../Components/Api/Api";
import { useMutation, useQuery, useQueryClient } from "react-query"; import { useQuery } 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 { useSelector } from "react-redux";
import { OldSessionState } from "../../Interfaces/Interfaces"; import { RootState } from "../../Plugins/Redux/Store/Store";
export default function Inventory() { export default function Inventory() {
const { const {
@ -29,7 +26,7 @@ export default function Inventory() {
} = useQuery("products", GetProducts, { retry: 0 }); } = useQuery("products", GetProducts, { retry: 0 });
const navigate = useNavigate(); const navigate = useNavigate();
const old_session_checked = useSelector( const old_session_checked = useSelector(
(state: OldSessionState) => state.old_session_checked.value (state: RootState) => state.old_session_checked.value
); );
if (isLoading || !old_session_checked) { if (isLoading || !old_session_checked) {
return ( return (