Product detail modal

This commit is contained in:
Eden Kirin
2024-01-23 22:32:11 +01:00
parent 7b213dd33a
commit 5db0026aa7
10 changed files with 97 additions and 18 deletions

View File

@ -0,0 +1,8 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View File

@ -1,5 +1,4 @@
import axios from "axios"; import axios from "axios";
import { API_URL } from "../const";
export class ApiBase {} export class ApiBase {}
@ -12,7 +11,6 @@ const commonHeaders = {
}; };
export const axiosUnauthorizedInstance = axios.create({ export const axiosUnauthorizedInstance = axios.create({
baseURL: API_URL,
timeout: 5000, timeout: 5000,
headers: commonHeaders, headers: commonHeaders,
}); });
@ -25,7 +23,6 @@ axiosUnauthorizedInstance.interceptors.response.use(
); );
export const axiosInstance = axios.create({ export const axiosInstance = axios.create({
baseURL: API_URL,
timeout: 5000, timeout: 5000,
headers: { headers: {
...commonHeaders, ...commonHeaders,

View File

@ -1 +1,2 @@
export { machinesApi } from "./machines"; export { machinesApi } from "./machines";
export { productsApi } from "./products";

View File

@ -1,14 +1,17 @@
import { axiosInstance, ApiBase } from "./common"; import { axiosInstance, ApiBase } from "./common";
import { MACHINES_API_URL } from "../const";
const baseUrl = `${MACHINES_API_URL}/machines`;
class MachinesApi extends ApiBase { class MachinesApi extends ApiBase {
list = async () => { list = async () => {
return axiosInstance.get(`/machines`, {}); return axiosInstance.get(`${baseUrl}`, {});
}; };
get = async (machineId) => { get = async (machineId) => {
return axiosInstance.get(`/machines/${machineId}`, {}); return axiosInstance.get(`${baseUrl}/${machineId}`, {});
}; };
listProducts = async (machineId) => { listProducts = async (machineId) => {
return axiosInstance.get(`/machines/${machineId}/products`, {}); return axiosInstance.get(`${baseUrl}/${machineId}/products`, {});
}; };
} }

View 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();

View File

@ -6,20 +6,21 @@ import Typography from "@mui/material/Typography";
import { CardActionArea } from "@mui/material"; import { CardActionArea } from "@mui/material";
import { PRODUCT_IMAGE_DIR } from "../const"; import { PRODUCT_IMAGE_DIR } from "../const";
function ProductCard({ product }) { function ProductCard({ product, onClick }) {
const productImg = `${PRODUCT_IMAGE_DIR}/${product.image}`; const productImg = `${PRODUCT_IMAGE_DIR}/${product.image}`;
return ( return (
<Card> <Card sx={{ width: "100%" }}>
<CardActionArea> <CardActionArea
<CardMedia component="img" height="140" image={productImg} alt={product.name} /> onClick={() => {
onClick(product.id);
}}
>
<CardMedia component="img" height="200" image={productImg} alt={product.name} />
<CardContent> <CardContent>
<Typography gutterBottom variant="h5" component="div"> <Typography gutterBottom variant="h5" component="div" sx={{ marginBottom: 0 }}>
{product.name} {product.name}
</Typography> </Typography>
<Typography variant="body2" color="text.secondary">
{product.description}
</Typography>
</CardContent> </CardContent>
</CardActionArea> </CardActionArea>
</Card> </Card>

View 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 };

View File

@ -2,12 +2,32 @@ import * as React from "react";
import Grid from "@mui/material/Unstable_Grid2"; import Grid from "@mui/material/Unstable_Grid2";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { ProductCard } from "./ProductCard"; import { ProductCard } from "./ProductCard";
import { ProductModal } from "./ProductModal";
import { productsApi } from "../api";
function Products({ machineName, products, onSelect }) { 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) => { const productItems = products.map((product) => {
return ( return (
<Grid md={6} key={product.id} sx={{ display: "flex" }}> <Grid md={6} key={product.id} sx={{ display: "flex" }}>
<ProductCard product={product} /> <ProductCard product={product} onClick={onProductSelect} />
</Grid> </Grid>
); );
}); });
@ -21,6 +41,8 @@ function Products({ machineName, products, onSelect }) {
<Grid container spacing={2}> <Grid container spacing={2}>
{productItems} {productItems}
</Grid> </Grid>
{productModal.isOpen && <ProductModal product={productModal.product} onClose={onProductModalClose} />}
</> </>
); );
} }

View File

@ -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/"; export const PRODUCT_IMAGE_DIR = "/static/products/";

View File

@ -18,9 +18,7 @@ function Home() {
}, []); }, []);
const onMachineSelect = (machineName, machineId) => { const onMachineSelect = (machineName, machineId) => {
console.log("selected:", machineName);
machinesApi.listProducts(machineId).then((response) => { machinesApi.listProducts(machineId).then((response) => {
console.log(response.data.products);
setProductsData({ setProductsData({
machineName, machineName,
products: response.data.products, products: response.data.products,