Skip to content

Commit

Permalink
belinda-nextjs-03-231-modify-add-components-product-page (#232)
Browse files Browse the repository at this point in the history
* refactor and add buttons

* add store jwt and update buttons
  • Loading branch information
tinpham5614 authored Feb 23, 2024
1 parent f3e075b commit d239499
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 109 deletions.
1 change: 1 addition & 0 deletions app/auth/sign-in/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const Signin = () => {
throw new Error(await res.text());
}
const { token } = await res.json();
localStorage.setItem("token", token); // store token in local storage
const userRole = JSON.parse(atob(token.split(".")[1])).role; // decode token to get user role
// Redirect to user page
if (userRole === "admin") {
Expand Down
54 changes: 0 additions & 54 deletions app/category-page/[categoryId]/page.module.css

This file was deleted.

81 changes: 51 additions & 30 deletions app/category-page/[categoryId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,35 @@
"use client";

import styles from './page.module.css';
import React, { useState, useEffect, Dispatch, SetStateAction, createContext, Context } from "react";
import Link from 'next/link';
import React, { useState, useEffect, Dispatch, SetStateAction } from "react";
import ProductCard from "@/components/ProductCard";
import google_play from "../../google_play.png";
;

const placeholderImg = google_play
import logo from "../../logo.png";
import { Container, Grid, Typography } from "@mui/material";
const placeholderImg = logo;
interface Product {
_id: string;
productImage: typeof placeholderImg;
productType: string[];
productGender: string;
productSizeShoe: string;
productSize: string;
productSizePantsWaist: string;
productSizePantsInseam: string;
productDescription: string;
// more product fields can be added
}

async function fetchData(categoryId: string, setProducts: Dispatch<SetStateAction<Product[]>>) {
const apiUrl = 'http://localhost:3000/api/products/findByType/';
async function fetchData(
categoryId: string,
setProducts: Dispatch<SetStateAction<Product[]>>
) {
const apiUrl = "http://localhost:3000/api/products/findByType/";
const queryParam = encodeURIComponent(categoryId);
const fetchUrl = `${apiUrl}${queryParam}`;

try {
const res = await fetch(fetchUrl, {
method: 'GET',
method: "GET",
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
},
});
if (!res.ok) {
Expand All @@ -36,7 +40,7 @@ async function fetchData(categoryId: string, setProducts: Dispatch<SetStateActio
console.log(data);
}
} catch (error) {
console.error('Error getting product:', error);
console.error("Error getting product:", error);
}
}

Expand All @@ -45,32 +49,49 @@ const ViewProduct = ({ categoryId }: { categoryId: string }) => {

useEffect(() => {
fetchData(categoryId, setProducts); // Pass categoryId to fetchData
}, [categoryId]);
}, [categoryId]);

return (
<div className={styles.displaySection}>
<h1>Found {products.length} products in {categoryId}</h1>
<div className={styles.productContainer}>
<Container sx={{ py: 4 }} maxWidth="lg">
<Typography
variant="h4"
gutterBottom
justifyContent={"center"}
align={"center"}
>
Found {products.length} products in {categoryId}
</Typography>
<Grid container spacing={2}>
{products.map((product, index) => (
<ProductCard
image={google_play}
categories={product.productType}
description={product.productDescription}
href={`/category-page/${categoryId}/products/${product._id}`} // Construct the URL
key={index}
/>
<Grid item key={index} xs={12} sm={6} md={4}>
<ProductCard
image={logo}
categories={product.productType}
gender={product.productGender}
sizeShoe={product.productSizeShoe}
size={product.productSize}
sizePantsWaist={product.productSizePantsWaist}
sizePantsInseam={product.productSizePantsInseam}
description={product.productDescription}
href={`/category-page/${categoryId}/products/${product._id}`} // Construct the URL
/>
</Grid>
))}
</div>
</div>
</Grid>
</Container>
);
};

export default function ProductList({ params }: { params: { categoryId: string } }) {
export default function ProductList({
params,
}: {
params: { categoryId: string };
}) {
const decodedCategoryId = decodeURIComponent(params.categoryId);

return (
<div className={styles.container}>
<Container>
<ViewProduct categoryId={decodedCategoryId} />
</div>
</Container>
);
}
}
135 changes: 110 additions & 25 deletions components/ProductCard.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,115 @@
import React from "react";
import Image from "next/image";
import * as React from "react";
import Grid from "@mui/material/Grid";
import Paper from "@mui/material/Paper";
import Typography from "@mui/material/Typography";
import ButtonBase from "@mui/material/ButtonBase";
import { StaticImageData } from "next/image";
import Link from 'next/link';
import styles from '../app/category-page/[categoryId]/page.module.css'; // Import style from category page
import { Stack, Button, Link } from "@mui/material";
import Image from "next/image";
import DeleteIcon from "@mui/icons-material/Delete";
import ArchiveIcon from "@mui/icons-material/Archive";

type ProductCardProps = {
image: StaticImageData;
categories: string[];
description: string;
href: string;
};

const ProductCard: React.FC<ProductCardProps> = ({ image, categories, description, href }) => {
return (
<Link href={href} passHref>
<div tabIndex={0} className={`product-card-container cursor-pointer px-4 py-2 border-4 border-gray-300 rounded-md hover:border-blue-500 focus:outline-none focus:border-blue-500 w-1/4 ${styles.product}`}>
<Image src={image} alt={`Image for ${categories.join(', ')}`} width={200} height={200} layout="responsive" />
<div className="py-4" style={{ flexGrow: 1 }}>
<p className="font-bold text-l mb-2 text-white h-8 truncate text-center">{categories.join(', ')}</p>
<p className="text-sm text-gray-200 text-center" style={{ overflow: 'hidden', textOverflow: 'ellipsis', display: '-webkit-box', WebkitLineClamp: 3, WebkitBoxOrient: 'vertical' }}>
{description}
</p>
</div>
</div>
</Link>
);
image: StaticImageData;
categories: string[];
gender: string;
sizeShoe: string;
sizePantsWaist: string;
sizePantsInseam: string;
size: string;
description: string;
href: string;
};
export default function ProductCard({
image,
categories,
gender,
sizeShoe,
size,
sizePantsWaist,
sizePantsInseam,
description,
href,
}: ProductCardProps) {
const [userRole, setUserRole] = React.useState("");

export default ProductCard;
// Get user role from token
React.useEffect(() => {
const token = localStorage.getItem("token");
if (token) {
const role = JSON.parse(atob(token.split(".")[1])).role;
setUserRole(role);
}
}, []);
return (
<Paper
sx={{
p: 2,
margin: "auto",
maxWidth: 500,
flexGrow: 1,
backgroundColor: (theme) =>
theme.palette.mode === "dark" ? "#1A2027" : "#fff",
}}
>
<Grid container spacing={2}>
<Grid item>
<ButtonBase>
<Link href={href}>
<Image
src={image}
alt="product image"
style={{ width: 128, height: 128 }}
/>
</Link>
</ButtonBase>
</Grid>
<Grid item xs={12} sm container>
<Grid item xs container direction="column" spacing={2}>
<Grid item xs>
<Typography gutterBottom variant="subtitle1" component="div">
{categories}
</Typography>
<Typography variant="body2" color="text.secondary">
{gender}
</Typography>
<Typography variant="body2" color="text.secondary">
{sizeShoe}
</Typography>
<Typography variant="body2" color="text.secondary">
{size}
</Typography>
<Typography variant="body2" color="text.secondary">
{sizePantsWaist}
</Typography>
<Typography variant="body2" color="text.secondary">
{sizePantsInseam}
</Typography>
<Typography variant="body2" gutterBottom>
{description}
</Typography>
</Grid>
</Grid>
</Grid>
</Grid>
<Stack direction="row" spacing={2} justifyContent="flex-end" mt={2}>
<Button variant="contained" href={href} color="primary">
View
</Button>
{userRole === "admin" ||
(userRole === "creator" && (
<Stack direction="row" spacing={2}>
{/* TODO: Add delete function to this button */}
<Button variant="contained" startIcon={<DeleteIcon />} color="error">
Delete
</Button>
{/* TODO: Add archive function to this button */}
<Button variant="contained" startIcon={<ArchiveIcon />} color="warning">
Archive
</Button>
</Stack>
))}
</Stack>
</Paper>
);
}

0 comments on commit d239499

Please sign in to comment.