Added quantity editing to inventory

This commit is contained in:
keannu125 2023-03-06 22:30:35 +08:00
parent fa9931c948
commit 1bdf7356d8
5 changed files with 168 additions and 81 deletions

View file

@ -35,15 +35,20 @@ export function GetProduct(id: number) {
}); });
} }
export function UpdateProduct(note: UpdateProductParams) { export function UpdateProduct(product: UpdateProductParams) {
const token = JSON.parse(localStorage.getItem("token") || "{}"); const token = JSON.parse(localStorage.getItem("token") || "{}");
return axios return axios
.patch("http://localhost:8000/api/v1/products/" + note.id + "/", note, { .patch(
headers: { "http://localhost:8000/api/v1/products/" + product.id + "/",
Authorization: "Token " + token, product,
}, {
}) headers: {
Authorization: "Token " + token,
},
}
)
.then((response) => { .then((response) => {
console.log("Product update successful", response.data);
return response.data; return response.data;
}) })
.catch((error) => { .catch((error) => {

View file

@ -0,0 +1,35 @@
import * as React from "react";
import { ProductList } from "../../../Interfaces/Interfaces";
import styles from "../../../styles";
import { TableBody, TableRow, TableCell } from "@mui/material";
import StockRenderer from "../StockRenderer/StockRenderer";
export default function RowRenderer(props: ProductList) {
if (props.Products.length === 0) {
return (
<div style={{ ...styles.content_column, ...{ alignItems: "center" } }}>
<p style={{ ...styles.text_white, ...styles.text_L }}>
No products yet. Add one!
</p>
</div>
);
}
return (
<TableBody>
{props.Products.map((row) => (
<TableRow
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.name}
</TableCell>
{StockRenderer(row)}
</TableRow>
))}
</TableBody>
);
}

View file

@ -1,63 +1,73 @@
import { TableCell } from "@mui/material"; import { Button, TableCell } from "@mui/material";
import { useState } from "react"; import { useEffect, useState } from "react";
import styles from "../../../styles"; import styles from "../../../styles";
import IsNumber from "../IsNumber/IsNumber"; import IsNumber from "../IsNumber/IsNumber";
import { UpdateProduct } from "../../Api/Api";
import { useQueryClient, useMutation } from "react-query";
import { Product } from "../../../Interfaces/Interfaces";
export default function StockRenderer(in_stock: number) { export default function StockRenderer(product: Product) {
const [stock, setStock] = useState(in_stock); const [stock, setStock] = useState(product.quantity);
if (stock >= 0 && stock <= 3) { const [valueChanged, setValueChanged] = useState(false);
return ( const queryClient = useQueryClient();
<TableCell> const mutation = useMutation({
<input mutationFn: UpdateProduct,
style={{ onSuccess: () => {
...styles.text_red, queryClient.invalidateQueries("products");
...{ border: "none", background: "none" }, },
...styles.text_S, });
}}
value={stock} function updateQuantity() {
onChange={(e: React.ChangeEvent<HTMLInputElement>) => { mutation.mutate({
if (IsNumber(e.target.value)) { id: product.id,
setStock(parseInt(e.target.value)); name: product.name,
} quantity: stock,
}} });
/> setValueChanged(false);
</TableCell>
);
} else if (stock >= 4 && stock < 9) {
return (
<TableCell>
<input
style={{
...styles.text_orange,
...{ border: "none", background: "none" },
...styles.text_S,
}}
value={stock}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
if (IsNumber(e.target.value)) {
setStock(parseInt(e.target.value));
}
}}
/>
</TableCell>
);
} else {
return (
<TableCell>
<input
style={{
...styles.text_green,
...{ border: "none", background: "none" },
...styles.text_S,
}}
value={stock}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
if (IsNumber(e.target.value)) {
setStock(parseInt(e.target.value));
}
}}
/>
</TableCell>
);
} }
useEffect(() => {
if (stock !== product.quantity) {
setValueChanged(true);
} else if (stock === product.quantity) {
setValueChanged(false);
}
}, [stock]);
let style;
if (stock >= 0 && stock <= 3) {
style = styles.text_red;
} else if (stock >= 4 && stock < 9) {
style = styles.text_orange;
} else {
style = styles.text_green;
}
return (
<TableCell>
<div style={styles.content_row}>
<input
style={{
...style,
...{ border: "none", background: "none", width: 64 },
...styles.text_S,
}}
value={stock}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
if (IsNumber(e.target.value)) {
setStock(parseInt(e.target.value));
}
}}
/>
<div style={{ paddingRight: 64 }} />
<Button
disabled={!valueChanged}
style={{ backgroundColor: "#80b38a" }}
onClick={() => {
updateQuantity();
}}
variant="contained"
>
Confirm
</Button>
</div>
</TableCell>
);
} }

View file

@ -6,6 +6,7 @@ export interface Product {
id: number; id: number;
name: string; name: string;
date_added: string; date_added: string;
quantity: number;
} }
// Redux Interfaces // Redux Interfaces
@ -63,5 +64,5 @@ export interface AddProductParams {
export interface UpdateProductParams { export interface UpdateProductParams {
id: number; id: number;
name: string; name: string;
quantity: string; quantity: number;
} }

View file

@ -12,8 +12,59 @@ import {
import { SampleInventoryData } from "../../Components/SampleData/SampleData"; import { SampleInventoryData } from "../../Components/SampleData/SampleData";
import StockRenderer from "../../Components/InventoryPage/StockRenderer/StockRenderer"; 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 { useMutation, useQuery, useQueryClient } from "react-query";
import RowRenderer from "../../Components/InventoryPage/RowRenderer/RowRenderer";
export default function Inventory() { export default function Inventory() {
const {
data: products,
isLoading,
error,
} = useQuery("products", GetProducts, { retry: 0 });
if (isLoading) {
return (
<div>
<LoginChecker />
<div style={styles.content_row}>
<div style={{ ...styles.content_row, ...{ flex: 1 } }}>
<div style={{ display: "flex", alignItems: "center", gap: 16 }}>
<InventoryIcon size={64} color="white" />
<p style={{ ...styles.text_white, ...styles.text_XL }}>
Inventory
</p>
</div>
</div>
</div>
<div style={{ ...styles.content_column, ...{ alignItems: "center" } }}>
<p style={{ ...styles.text_white, ...styles.text_L }}>
Loading inventory...
</p>
</div>
</div>
);
} else if (error) {
return (
<div>
<div style={styles.content_row}>
<div style={{ ...styles.content_row, ...{ flex: 1 } }}>
<div style={{ display: "flex", alignItems: "center", gap: 16 }}>
<InventoryIcon size={64} color="white" />
<p style={{ ...styles.text_white, ...styles.text_XL }}>
Inventory
</p>
</div>
</div>
</div>
<div style={{ ...styles.content_column, ...{ alignItems: "center" } }}>
<p style={{ ...styles.text_red, ...styles.text_L }}>
Error loading inventory
</p>
</div>
</div>
);
}
return ( return (
<div style={{ height: "100%" }}> <div style={{ height: "100%" }}>
<LoginChecker /> <LoginChecker />
@ -41,22 +92,7 @@ export default function Inventory() {
</TableCell> </TableCell>
</TableRow> </TableRow>
</TableHead> </TableHead>
<TableBody> <RowRenderer Products={products} />
{SampleInventoryData.map((row) => (
<TableRow
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.name}
</TableCell>
{StockRenderer(row.in_stock)}
</TableRow>
))}
</TableBody>
</Table> </Table>
</TableContainer> </TableContainer>
</div> </div>