Skip to content

Commit

Permalink
edit product functionality added in admin route
Browse files Browse the repository at this point in the history
  • Loading branch information
murtazatankiwala456 committed Jun 28, 2024
1 parent 1a5a4b2 commit 418d60e
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 91 deletions.
45 changes: 24 additions & 21 deletions data.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
"products": [
{
"id": "1",
"title": "Essence Mascara Lash Princess",
"title": "Essence Mascara Lash",
"description": "The Essence Mascara Lash Princess is a popular mascara known for its volumizing and lengthening effects. Achieve dramatic lashes with this long-lasting and cruelty-free formula.",
"category": "beauty",
"price": 9.99,
"discountPercentage": 7.17,
"rating": 4.94,
"rating": 0,
"stock": 5,
"tags": [
"beauty",
Expand Down Expand Up @@ -56,9 +56,12 @@
"qrCode": "https://dummyjson.com/public/qr-code.png"
},
"images": [
"https://cdn.dummyjson.com/products/images/beauty/Essence%20Mascara%20Lash%20Princess/1.png",
"https://cdn.dummyjson.com/products/images/beauty/Essence%20Mascara%20Lash%20Princess/1.png"
],
"thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Essence%20Mascara%20Lash%20Princess/thumbnail.png"
"thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Essence%20Mascara%20Lash%20Princess/1.png",
"discount": null,
"deleted": true
},
{
"id": "2",
Expand Down Expand Up @@ -125,9 +128,9 @@
"title": "Powder Canister",
"description": "The Powder Canister is a finely milled setting powder designed to set makeup and control shine. With a lightweight and translucent formula, it provides a smooth and matte finish.",
"category": "beauty",
"price": 14.99,
"price": 500,
"discountPercentage": 18.14,
"rating": 3.82,
"rating": 0,
"stock": 59,
"tags": [
"beauty",
Expand Down Expand Up @@ -176,9 +179,11 @@
"qrCode": "https://dummyjson.com/public/qr-code.png"
},
"images": [
"https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/1.png"
"https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/1.png",
"https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png"
],
"thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png"
"thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png",
"discount": null
},
{
"id": "4",
Expand Down Expand Up @@ -1797,22 +1802,20 @@
"thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Kiwi/thumbnail.png"
},
{
"id": "ac1d",
"title": "Raj Furniture",
"description": "Raj Furniture",
"brand": "Furniture Co.",
"category": "furniture",
"price": "520",
"discountPercentage": "5",
"stock": "20",
"thumbnail": "https://m.media-amazon.com/images/I/71u3F2NZ9gL.jpg",
"id": "caeb",
"title": "Gucci Belt",
"description": "Gucci Belt",
"brand": "Gucci",
"category": "beauty",
"price": 45,
"discountPercentage": 35,
"stock": 9,
"thumbnail": "https://images-cdn.ubuy.co.in/6340119aa00848332523d5b9-gucci-belt-u-07-gu-24186.jpg",
"images": [
"https://m.media-amazon.com/images/I/71u3F2NZ9gL.jpg",
"https://m.media-amazon.com/images/I/71u3F2NZ9gL.jpg",
"https://m.media-amazon.com/images/I/71u3F2NZ9gL.jpg",
"https://m.media-amazon.com/images/I/71u3F2NZ9gL.jpg"
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3GFf5cstnkS6xmvA5ySarmKkhtYsHquDC_A&s",
"https://images-cdn.ubuy.co.in/6340119aa00848332523d5b9-gucci-belt-u-07-gu-24186.jpg"
],
"rating": 0
"discount": null
}
],
"brands": [
Expand Down
8 changes: 8 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ const router = createBrowserRouter([
</ProtectedAdmin>
),
},
{
path: "/admin/product-form/edit/:id",
element: (
<ProtectedAdmin>
<ProductFormPage></ProductFormPage>
</ProtectedAdmin>
),
},
{
path: "/order-success/:id",
element: <OrderSuccessPage></OrderSuccessPage>,
Expand Down
4 changes: 2 additions & 2 deletions src/features/admin/components/AdminProductDetail.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { StarIcon } from "@heroicons/react/20/solid";
import { RadioGroup } from "@headlessui/react";
import { useSelector, useDispatch } from "react-redux";
import {
fetchAllProductsIdAsync,
fetchProductByIdAsync,
selectProductById,
} from "../../product/productSlice";
import { useParams } from "react-router-dom";
Expand Down Expand Up @@ -54,7 +54,7 @@ export default function AdminProductDetail() {
};

useEffect(() => {
dispatch(fetchAllProductsIdAsync(params.id)); // :id from path in app.js
dispatch(fetchProductByIdAsync(params.id)); // :id from path in app.js
}, [dispatch, params.id]);
return (
<div className="bg-white">
Expand Down
16 changes: 12 additions & 4 deletions src/features/admin/components/AdminProductList.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ export default function AdminProductList() {
{/* This is our products list */}
<Link
to="/admin/product-form"
className="rounded-md mx-10 bg-green-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
className="rounded-md mx-10 bg-green-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
>
Add Product
</Link>
Expand Down Expand Up @@ -448,13 +448,21 @@ function ProductGrid({ products }) {
</p>
</div>
</div>
{product.deleted && (
<div>
<p className="text-sm text-red-400">Product Deleted</p>
</div>
)}
</div>
</div>
</Link>
<div>
<button className="rounded-md my-2 bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">
<div className="mt-5">
<Link
to={`/admin/product-form/edit/${product.id}`}
className="rounded-md my-2 bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
>
Edit Product
</button>
</Link>
</div>
</div>
))}
Expand Down
123 changes: 68 additions & 55 deletions src/features/admin/components/ProductForm.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,84 @@
import { useDispatch, useSelector } from "react-redux";
import {
clearSelectedProduct,
createProductAsync,
fetchProductByIdAsync,
selectBrands,
selectCategories,
selectProductById,
updateProductAsync,
} from "../../product/productSlice";
import { PhotoIcon, UserCircleIcon } from "@heroicons/react/24/solid";
import { useForm } from "react-hook-form";
import { useEffect } from "react";
import { useParams } from "react-router-dom";
function ProductForm() {
const categories = useSelector(selectCategories);
const brands = useSelector(selectBrands);
const dispatch = useDispatch();
const params = useParams();
const selectedProduct = useSelector(selectProductById);
const {
register,
handleSubmit,
setValue,
reset,
formState: { errors },
} = useForm();

useEffect(() => {
if (params.id) {
dispatch(fetchProductByIdAsync(params.id));
} else {
dispatch(clearSelectedProduct());
}
}, [params.id, dispatch]);

useEffect(() => {
if (selectedProduct && params.id) {
setValue("title", selectedProduct.title);
setValue("description", selectedProduct.description);
setValue("brand", selectedProduct.brand);
setValue("category", selectedProduct.category);
setValue("price", selectedProduct.price);
setValue("discountPercentage", selectedProduct.discountPercentage);
setValue("stock", selectedProduct.stock);
setValue("thumbnail", selectedProduct.thumbnail);
setValue("image", selectedProduct.images[0]);
}
}, [setValue, params.id, selectedProduct]);

const handleDelete = () => {
const product = { ...selectedProduct };
product.deleted = true;
dispatch(updateProductAsync(product));
};
return (
<form
noValidate
onSubmit={handleSubmit((data) => {
console.log(data);
const product = { ...data };
product.images = [
product.image1,
product.image2,
product.image3,
product.thumbnail,
];
product.images = [product.image, product.thumbnail];
delete product["image"];
product.rating = 0;
delete product["image1"];
delete product["image2"];
delete product["image3"];

product.price = +product.price;

product.discountPercentage = +product.discountPercentage;
product.stock = +product.stock;
product.discount = +product.discount;
console.log(product);
dispatch(createProductAsync(product));

if (params.id) {
product.id = params.id;
product.rating = selectedProduct.rating || 0;
dispatch(updateProductAsync(product));
reset();
} else {
dispatch(createProductAsync(product));
reset();
}
})}
>
<div className="space-y-12 bg-white p-12">
Expand Down Expand Up @@ -214,59 +259,19 @@ function ProductForm() {

<div className="sm:col-span-6">
<label
htmlFor="image1"
className="block text-sm font-medium leading-6 text-gray-900"
>
Image1
</label>
<div className="mt-2">
<div className="flex rounded-md shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-inset focus-within:ring-indigo-600">
<input
type="text"
{...register("image1", {
required: "image1 is required",
})}
id="image1"
className="block flex-1 border-0 bg-transparent py-1.5 pl-1 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6"
/>
</div>
</div>
</div>
<div className="sm:col-span-6">
<label
htmlFor="image2"
className="block text-sm font-medium leading-6 text-gray-900"
>
Image2
</label>
<div className="mt-2">
<div className="flex rounded-md shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-inset focus-within:ring-indigo-600">
<input
type="text"
{...register("image2", {
required: "image2 is required",
})}
id="image2"
className="block flex-1 border-0 bg-transparent py-1.5 pl-1 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6"
/>
</div>
</div>
</div>
<div className="sm:col-span-6">
<label
htmlFor="image3"
htmlFor="image"
className="block text-sm font-medium leading-6 text-gray-900"
>
Image3
Image
</label>
<div className="mt-2">
<div className="flex rounded-md shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-inset focus-within:ring-indigo-600">
<input
type="text"
{...register("image3", {
required: "image3 is required",
{...register("image", {
required: "image is required",
})}
id="image3"
id="image"
className="block flex-1 border-0 bg-transparent py-1.5 pl-1 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6"
/>
</div>
Expand Down Expand Up @@ -360,6 +365,14 @@ function ProductForm() {
>
Cancel
</button>
{selectedProduct && (
<button
onClick={handleDelete}
className="rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
>
Delete
</button>
)}
<button
type="submit"
className="rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
Expand Down
4 changes: 2 additions & 2 deletions src/features/product/components/ProductDetail.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useEffect, useState } from "react";
import { StarIcon } from "@heroicons/react/20/solid";
import { RadioGroup } from "@headlessui/react";
import { useSelector, useDispatch } from "react-redux";
import { fetchAllProductsIdAsync, selectProductById } from "../productSlice";
import { fetchProductByIdAsync, selectProductById } from "../productSlice";
import { useParams } from "react-router-dom";
import { addToCartAsync } from "../../cart/cartSlice";
import { selectLoggedInUser } from "../../auth/authSlice";
Expand Down Expand Up @@ -51,7 +51,7 @@ export default function ProductDetail() {
};

useEffect(() => {
dispatch(fetchAllProductsIdAsync(params.id)); // :id from path in app.js
dispatch(fetchProductByIdAsync(params.id)); // :id from path in app.js
}, [dispatch, params.id]);
return (
<div className="bg-white">
Expand Down
1 change: 1 addition & 0 deletions src/features/product/components/ProductList.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export default function ProductList() {
useEffect(() => {
const pagination = { _page: page, _per_page: ITEMS_PER_PAGE };
dispatch(fetchProductsByFiltersAsync({ filter, sort, pagination }));
// TODO:server will filter the deleted products
}, [dispatch, filter, sort, page]);

useEffect(() => {
Expand Down
17 changes: 17 additions & 0 deletions src/features/product/productAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export function fetchAllProducts() {
return new Promise(async (resolve) => {
// TODO: we will not hard coded server url here...
const response = await fetch("http://localhost:8080/products");

const data = await response.json();
resolve({ data });
});
Expand All @@ -26,9 +27,25 @@ export function createProduct(product) {
resolve({ data });
});
}
export function updateProduct(update) {
return new Promise(async (resolve) => {
const response = await fetch(
"http://localhost:8080/products/" + update.id,
{
method: "PATCH",
body: JSON.stringify(update),
headers: { "content-type": "application/json" },
}
);
const data = await response.json();

resolve({ data });
});
}
export function fetchProductsByFilters(filter, sort, pagination) {
// filter ={"brand":"Essence"}
// TODO:we will on server support mutilple value
// TODO:server will filter the deleted products in case of non-admin

// reference for sorting functionality
// JSON Server's behavior for sorting changed with different versions.
Expand Down
Loading

0 comments on commit 418d60e

Please sign in to comment.