Product detail modal
This commit is contained in:
@ -1,5 +1,4 @@
|
||||
import axios from "axios";
|
||||
import { API_URL } from "../const";
|
||||
|
||||
export class ApiBase {}
|
||||
|
||||
@ -12,7 +11,6 @@ const commonHeaders = {
|
||||
};
|
||||
|
||||
export const axiosUnauthorizedInstance = axios.create({
|
||||
baseURL: API_URL,
|
||||
timeout: 5000,
|
||||
headers: commonHeaders,
|
||||
});
|
||||
@ -25,7 +23,6 @@ axiosUnauthorizedInstance.interceptors.response.use(
|
||||
);
|
||||
|
||||
export const axiosInstance = axios.create({
|
||||
baseURL: API_URL,
|
||||
timeout: 5000,
|
||||
headers: {
|
||||
...commonHeaders,
|
||||
|
||||
@ -1 +1,2 @@
|
||||
export { machinesApi } from "./machines";
|
||||
export { productsApi } from "./products";
|
||||
|
||||
@ -1,14 +1,17 @@
|
||||
import { axiosInstance, ApiBase } from "./common";
|
||||
import { MACHINES_API_URL } from "../const";
|
||||
|
||||
const baseUrl = `${MACHINES_API_URL}/machines`;
|
||||
|
||||
class MachinesApi extends ApiBase {
|
||||
list = async () => {
|
||||
return axiosInstance.get(`/machines`, {});
|
||||
return axiosInstance.get(`${baseUrl}`, {});
|
||||
};
|
||||
get = async (machineId) => {
|
||||
return axiosInstance.get(`/machines/${machineId}`, {});
|
||||
return axiosInstance.get(`${baseUrl}/${machineId}`, {});
|
||||
};
|
||||
listProducts = async (machineId) => {
|
||||
return axiosInstance.get(`/machines/${machineId}/products`, {});
|
||||
return axiosInstance.get(`${baseUrl}/${machineId}/products`, {});
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
12
frontend/src/api/products.js
Normal file
12
frontend/src/api/products.js
Normal file
@ -0,0 +1,12 @@
|
||||
import { axiosInstance, ApiBase } from "./common";
|
||||
import { PRODUCTS_API_URL } from "../const";
|
||||
|
||||
const baseUrl = `${PRODUCTS_API_URL}/products`;
|
||||
|
||||
class ProductsApi extends ApiBase {
|
||||
get = async (productId) => {
|
||||
return axiosInstance.get(`${baseUrl}/${productId}`, {});
|
||||
};
|
||||
}
|
||||
|
||||
export const productsApi = new ProductsApi();
|
||||
@ -6,20 +6,21 @@ import Typography from "@mui/material/Typography";
|
||||
import { CardActionArea } from "@mui/material";
|
||||
import { PRODUCT_IMAGE_DIR } from "../const";
|
||||
|
||||
function ProductCard({ product }) {
|
||||
const productImg = `${PRODUCT_IMAGE_DIR}${product.image}`;
|
||||
function ProductCard({ product, onClick }) {
|
||||
const productImg = `${PRODUCT_IMAGE_DIR}/${product.image}`;
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardActionArea>
|
||||
<CardMedia component="img" height="140" image={productImg} alt={product.name} />
|
||||
<Card sx={{ width: "100%" }}>
|
||||
<CardActionArea
|
||||
onClick={() => {
|
||||
onClick(product.id);
|
||||
}}
|
||||
>
|
||||
<CardMedia component="img" height="200" image={productImg} alt={product.name} />
|
||||
<CardContent>
|
||||
<Typography gutterBottom variant="h5" component="div">
|
||||
<Typography gutterBottom variant="h5" component="div" sx={{ marginBottom: 0 }}>
|
||||
{product.name}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{product.description}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
</Card>
|
||||
|
||||
36
frontend/src/components/ProductModal.js
Normal file
36
frontend/src/components/ProductModal.js
Normal file
@ -0,0 +1,36 @@
|
||||
import * as React from "react";
|
||||
import Button from "@mui/material/Button";
|
||||
import Dialog from "@mui/material/Dialog";
|
||||
import DialogActions from "@mui/material/DialogActions";
|
||||
import DialogContent from "@mui/material/DialogContent";
|
||||
import DialogContentText from "@mui/material/DialogContentText";
|
||||
import DialogTitle from "@mui/material/DialogTitle";
|
||||
import CardMedia from "@mui/material/CardMedia";
|
||||
import { PRODUCT_IMAGE_DIR } from "../const";
|
||||
|
||||
function ProductModal({ product, onClose }) {
|
||||
const productImg = `${PRODUCT_IMAGE_DIR}/${product.image}`;
|
||||
|
||||
return (
|
||||
<Dialog open={true} onClose={onClose}>
|
||||
<DialogTitle>{product.name}</DialogTitle>
|
||||
<DialogContent>
|
||||
<CardMedia
|
||||
component="img"
|
||||
height="300"
|
||||
image={productImg}
|
||||
alt={product.name}
|
||||
sx={{ marginBottom: "1rem" }}
|
||||
/>
|
||||
<DialogContentText>{product.description}</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onClose} variant="contained" autoFocus>
|
||||
Close
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export { ProductModal };
|
||||
@ -2,12 +2,32 @@ import * as React from "react";
|
||||
import Grid from "@mui/material/Unstable_Grid2";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { ProductCard } from "./ProductCard";
|
||||
import { ProductModal } from "./ProductModal";
|
||||
import { productsApi } from "../api";
|
||||
|
||||
function Products({ machineName, products, onSelect }) {
|
||||
const [productModal, setProductModal] = React.useState({ isOpen: false, product: null });
|
||||
|
||||
const onProductSelect = (productId) => {
|
||||
productsApi.get(productId).then((response) => {
|
||||
setProductModal({
|
||||
isOpen: true,
|
||||
product: response.data,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const onProductModalClose = () => {
|
||||
setProductModal({
|
||||
isOpen: false,
|
||||
productId: null,
|
||||
});
|
||||
};
|
||||
|
||||
const productItems = products.map((product) => {
|
||||
return (
|
||||
<Grid md={6} key={product.id} sx={{ display: "flex" }}>
|
||||
<ProductCard product={product} />
|
||||
<ProductCard product={product} onClick={onProductSelect} />
|
||||
</Grid>
|
||||
);
|
||||
});
|
||||
@ -21,6 +41,8 @@ function Products({ machineName, products, onSelect }) {
|
||||
<Grid container spacing={2}>
|
||||
{productItems}
|
||||
</Grid>
|
||||
|
||||
{productModal.isOpen && <ProductModal product={productModal.product} onClose={onProductModalClose} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
export const API_URL = process.env.REACT_APP_BACKEND_API_URL || "http://localhost:10000";
|
||||
export const MACHINES_API_URL = process.env.REACT_APP_MACHINES_API_URL || "http://localhost:4000";
|
||||
export const PRODUCTS_API_URL = process.env.REACT_APP_PRODUCTS_API_URL || "http://localhost:4001";
|
||||
export const PRODUCT_IMAGE_DIR = "/static/products/";
|
||||
|
||||
@ -18,9 +18,7 @@ function Home() {
|
||||
}, []);
|
||||
|
||||
const onMachineSelect = (machineName, machineId) => {
|
||||
console.log("selected:", machineName);
|
||||
machinesApi.listProducts(machineId).then((response) => {
|
||||
console.log(response.data.products);
|
||||
setProductsData({
|
||||
machineName,
|
||||
products: response.data.products,
|
||||
|
||||
Reference in New Issue
Block a user