From e70320e593b4e7731578a5ae50161ca1e2ff3be5 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Tue, 11 Jun 2024 14:55:16 +0530 Subject: [PATCH 01/47] copy data.json from main branch --- data.json | 3115 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3115 insertions(+) create mode 100644 data.json diff --git a/data.json b/data.json new file mode 100644 index 0000000..500b5f3 --- /dev/null +++ b/data.json @@ -0,0 +1,3115 @@ +{ + "products": [ + { + "id": 1, + "title": "iPhone 9", + "description": "An apple mobile which is nothing like apple", + "price": 993, + "discountPercentage": 12.96, + "rating": 0, + "stock": 0, + "brand": "Apple", + "category": "smartphones", + "thumbnail": "https://i.dummyjson.com/data/products/1/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/1/1.jpg", + "https://i.dummyjson.com/data/products/1/2.jpg", + "https://i.dummyjson.com/data/products/1/3.jpg", + "https://i.dummyjson.com/data/products/1/thumbnail.jpg" + ], + "deleted": true + }, + { + "id": 2, + "title": "iPhone X", + "description": "SIM-Free, Model A19211 6.5-inch Super Retina HD display with OLED technology A12 Bionic chip with ...", + "price": 1111, + "discountPercentage": 17.94, + "rating": 0, + "stock": 34, + "brand": "Apple", + "category": "smartphones", + "thumbnail": "https://i.dummyjson.com/data/products/2/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/2/1.jpg", + "https://i.dummyjson.com/data/products/2/2.jpg", + "https://i.dummyjson.com/data/products/2/3.jpg", + "https://i.dummyjson.com/data/products/2/thumbnail.jpg" + ], + "deleted": true + }, + { + "id": 3, + "title": "Samsung Universe 9", + "description": "Samsung's new variant which goes beyond Galaxy to the Universe", + "price": 1249, + "discountPercentage": 15.46, + "rating": 4.09, + "stock": 36, + "brand": "Samsung", + "category": "smartphones", + "thumbnail": "https://i.dummyjson.com/data/products/3/thumbnail.jpg", + "images": ["https://i.dummyjson.com/data/products/3/1.jpg"], + "deleted": true + }, + { + "id": 4, + "title": "OPPOF19", + "description": "OPPO F19 is officially announced on April 2021.", + "price": 300, + "discountPercentage": 17.91, + "rating": 0, + "stock": 123, + "brand": "OPPO", + "category": "smartphones", + "thumbnail": "https://i.dummyjson.com/data/products/4/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/4/1.jpg", + "https://i.dummyjson.com/data/products/4/2.jpg", + "https://i.dummyjson.com/data/products/4/3.jpg", + "https://i.dummyjson.com/data/products/4/thumbnail.jpg" + ] + }, + { + "id": 5, + "title": "Huawei P30", + "description": "Huawei’s re-badged P30 Pro New Edition was officially unveiled yesterday in Germany and now the device has made its way to the UK.", + "price": 499, + "discountPercentage": 10.58, + "rating": 4.09, + "stock": 32, + "brand": "Huawei", + "category": "smartphones", + "thumbnail": "https://i.dummyjson.com/data/products/5/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/5/1.jpg", + "https://i.dummyjson.com/data/products/5/2.jpg", + "https://i.dummyjson.com/data/products/5/3.jpg" + ] + }, + { + "id": 6, + "title": "MacBook Pro", + "description": "MacBook Pro 2021 with mini-LED display may launch between September, November", + "price": 1999, + "discountPercentage": 11.02, + "rating": 0, + "stock": 83, + "brand": "Apple", + "category": "laptops", + "thumbnail": "https://i.dummyjson.com/data/products/6/thumbnail.png", + "images": [ + "https://i.dummyjson.com/data/products/6/1.png", + "https://i.dummyjson.com/data/products/6/2.jpg", + "https://i.dummyjson.com/data/products/6/3.png", + "https://i.dummyjson.com/data/products/6/thumbnail.png" + ] + }, + { + "id": 7, + "title": "Samsung Galaxy Book", + "description": "Samsung Galaxy Book S (2020) Laptop With Intel Lakefield Chip, 8GB of RAM Launched", + "price": 1499, + "discountPercentage": 4.15, + "rating": 4.25, + "stock": 50, + "brand": "Samsung", + "category": "laptops", + "thumbnail": "https://i.dummyjson.com/data/products/7/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/7/1.jpg", + "https://i.dummyjson.com/data/products/7/2.jpg", + "https://i.dummyjson.com/data/products/7/3.jpg", + "https://i.dummyjson.com/data/products/7/thumbnail.jpg" + ] + }, + { + "id": 8, + "title": "Microsoft Surface Laptop 4", + "description": "Style and speed. Stand out on HD video calls backed by Studio Mics. Capture ideas on the vibrant touchscreen.", + "price": 1499, + "discountPercentage": 10.23, + "rating": 4.43, + "stock": 68, + "brand": "Microsoft Surface", + "category": "laptops", + "thumbnail": "https://i.dummyjson.com/data/products/8/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/8/1.jpg", + "https://i.dummyjson.com/data/products/8/2.jpg", + "https://i.dummyjson.com/data/products/8/3.jpg", + "https://i.dummyjson.com/data/products/8/4.jpg", + "https://i.dummyjson.com/data/products/8/thumbnail.jpg" + ] + }, + { + "id": 9, + "title": "Infinix INBOOK", + "description": "Infinix Inbook X1 Ci3 10th 8GB 256GB 14 Win10 Grey – 1 Year Warranty", + "price": 1099, + "discountPercentage": 11.83, + "rating": 4.54, + "stock": 96, + "brand": "Infinix", + "category": "laptops", + "thumbnail": "https://i.dummyjson.com/data/products/9/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/9/1.jpg", + "https://i.dummyjson.com/data/products/9/2.png", + "https://i.dummyjson.com/data/products/9/3.png", + "https://i.dummyjson.com/data/products/9/4.jpg", + "https://i.dummyjson.com/data/products/9/thumbnail.jpg" + ] + }, + { + "id": 10, + "title": "HP Pavilion 15-DK1056WM", + "description": "HP Pavilion 15-DK1056WM Gaming Laptop 10th Gen Core i5, 8GB, 256GB SSD, GTX 1650 4GB, Windows 10", + "price": 1099, + "discountPercentage": 6.18, + "rating": 4.43, + "stock": 89, + "brand": "HP Pavilion", + "category": "laptops", + "thumbnail": "https://i.dummyjson.com/data/products/10/thumbnail.jpeg", + "images": [ + "https://i.dummyjson.com/data/products/10/1.jpg", + "https://i.dummyjson.com/data/products/10/2.jpg", + "https://i.dummyjson.com/data/products/10/3.jpg", + "https://i.dummyjson.com/data/products/10/thumbnail.jpeg" + ] + }, + { + "id": 11, + "title": "perfume Oil", + "description": "Mega Discount, Impression of Acqua Di Gio by GiorgioArmani concentrated attar perfume Oil", + "price": 13, + "discountPercentage": 8.4, + "rating": 4.26, + "stock": 65, + "brand": "Impression of Acqua Di Gio", + "category": "fragrances", + "thumbnail": "https://i.dummyjson.com/data/products/11/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/11/1.jpg", + "https://i.dummyjson.com/data/products/11/2.jpg", + "https://i.dummyjson.com/data/products/11/3.jpg", + "https://i.dummyjson.com/data/products/11/thumbnail.jpg" + ] + }, + { + "id": 12, + "title": "Brown Perfume", + "description": "Royal_Mirage Sport Brown Perfume for Men & Women - 120ml", + "price": 40, + "discountPercentage": 15.66, + "rating": 4, + "stock": 52, + "brand": "Royal_Mirage", + "category": "fragrances", + "thumbnail": "https://i.dummyjson.com/data/products/12/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/12/1.jpg", + "https://i.dummyjson.com/data/products/12/2.jpg", + "https://i.dummyjson.com/data/products/12/3.png", + "https://i.dummyjson.com/data/products/12/4.jpg", + "https://i.dummyjson.com/data/products/12/thumbnail.jpg" + ] + }, + { + "id": 13, + "title": "Fog Scent Xpressio Perfume", + "description": "Product details of Best Fog Scent Xpressio Perfume 100ml For Men cool long lasting perfumes for Men", + "price": 13, + "discountPercentage": 8.14, + "rating": 4.59, + "stock": 61, + "brand": "Fog Scent Xpressio", + "category": "fragrances", + "thumbnail": "https://i.dummyjson.com/data/products/13/thumbnail.webp", + "images": [ + "https://i.dummyjson.com/data/products/13/1.jpg", + "https://i.dummyjson.com/data/products/13/2.png", + "https://i.dummyjson.com/data/products/13/3.jpg", + "https://i.dummyjson.com/data/products/13/4.jpg", + "https://i.dummyjson.com/data/products/13/thumbnail.webp" + ] + }, + { + "id": 14, + "title": "Non-Alcoholic Concentrated Perfume Oil", + "description": "Original Al Munakh® by Mahal Al Musk | Our Impression of Climate | 6ml Non-Alcoholic Concentrated Perfume Oil", + "price": 120, + "discountPercentage": 15.6, + "rating": 4.21, + "stock": 114, + "brand": "Al Munakh", + "category": "fragrances", + "thumbnail": "https://i.dummyjson.com/data/products/14/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/14/1.jpg", + "https://i.dummyjson.com/data/products/14/2.jpg", + "https://i.dummyjson.com/data/products/14/3.jpg", + "https://i.dummyjson.com/data/products/14/thumbnail.jpg" + ] + }, + { + "id": 15, + "title": "Eau De Perfume Spray", + "description": "Genuine Al-Rehab spray perfume from UAE/Saudi Arabia/Yemen High Quality", + "price": 30, + "discountPercentage": 10.99, + "rating": 4.7, + "stock": 105, + "brand": "Lord - Al-Rehab", + "category": "fragrances", + "thumbnail": "https://i.dummyjson.com/data/products/15/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/15/1.jpg", + "https://i.dummyjson.com/data/products/15/2.jpg", + "https://i.dummyjson.com/data/products/15/3.jpg", + "https://i.dummyjson.com/data/products/15/4.jpg", + "https://i.dummyjson.com/data/products/15/thumbnail.jpg" + ] + }, + { + "id": 16, + "title": "Hyaluronic Acid Serum", + "description": "L'Oréal Paris introduces Hyaluron Expert Replumping Serum formulated with 1.5% Hyaluronic Acid", + "price": 19, + "discountPercentage": 13.31, + "rating": 4.83, + "stock": 110, + "brand": "L'Oreal Paris", + "category": "skincare", + "thumbnail": "https://i.dummyjson.com/data/products/16/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/16/1.png", + "https://i.dummyjson.com/data/products/16/2.webp", + "https://i.dummyjson.com/data/products/16/3.jpg", + "https://i.dummyjson.com/data/products/16/4.jpg", + "https://i.dummyjson.com/data/products/16/thumbnail.jpg" + ] + }, + { + "id": 17, + "title": "Tree Oil 30ml", + "description": "Tea tree oil contains a number of compounds, including terpinen-4-ol, that have been shown to kill certain bacteria,", + "price": 12, + "discountPercentage": 4.09, + "rating": 4.52, + "stock": 78, + "brand": "Hemani Tea", + "category": "skincare", + "thumbnail": "https://i.dummyjson.com/data/products/17/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/17/1.jpg", + "https://i.dummyjson.com/data/products/17/2.jpg", + "https://i.dummyjson.com/data/products/17/3.jpg", + "https://i.dummyjson.com/data/products/17/thumbnail.jpg" + ] + }, + { + "id": 18, + "title": "Oil Free Moisturizer 100ml", + "description": "Dermive Oil Free Moisturizer with SPF 20 is specifically formulated with ceramides, hyaluronic acid & sunscreen.", + "price": 40, + "discountPercentage": 13.1, + "rating": 4.56, + "stock": 88, + "brand": "Dermive", + "category": "skincare", + "thumbnail": "https://i.dummyjson.com/data/products/18/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/18/1.jpg", + "https://i.dummyjson.com/data/products/18/2.jpg", + "https://i.dummyjson.com/data/products/18/3.jpg", + "https://i.dummyjson.com/data/products/18/4.jpg", + "https://i.dummyjson.com/data/products/18/thumbnail.jpg" + ] + }, + { + "id": 19, + "title": "Skin Beauty Serum.", + "description": "Product name: rorec collagen hyaluronic acid white face serum riceNet weight: 15 m", + "price": 46, + "discountPercentage": 10.68, + "rating": 4.42, + "stock": 54, + "brand": "ROREC White Rice", + "category": "skincare", + "thumbnail": "https://i.dummyjson.com/data/products/19/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/19/1.jpg", + "https://i.dummyjson.com/data/products/19/2.jpg", + "https://i.dummyjson.com/data/products/19/3.png", + "https://i.dummyjson.com/data/products/19/thumbnail.jpg" + ] + }, + { + "id": 20, + "title": "Freckle Treatment Cream- 15gm", + "description": "Fair & Clear is Pakistan's only pure Freckle cream which helpsfade Freckles, Darkspots and pigments. Mercury level is 0%, so there are no side effects.", + "price": 70, + "discountPercentage": 16.99, + "rating": 4.06, + "stock": 140, + "brand": "Fair & Clear", + "category": "skincare", + "thumbnail": "https://i.dummyjson.com/data/products/20/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/20/1.jpg", + "https://i.dummyjson.com/data/products/20/2.jpg", + "https://i.dummyjson.com/data/products/20/3.jpg", + "https://i.dummyjson.com/data/products/20/4.jpg", + "https://i.dummyjson.com/data/products/20/thumbnail.jpg" + ] + }, + { + "id": 21, + "title": "- Daal Masoor 500 grams", + "description": "Fine quality Branded Product Keep in a cool and dry place", + "price": 20, + "discountPercentage": 4.81, + "rating": 4.44, + "stock": 133, + "brand": "Saaf & Khaas", + "category": "groceries", + "thumbnail": "https://i.dummyjson.com/data/products/21/thumbnail.png", + "images": [ + "https://i.dummyjson.com/data/products/21/1.png", + "https://i.dummyjson.com/data/products/21/2.jpg", + "https://i.dummyjson.com/data/products/21/3.jpg" + ] + }, + { + "id": 22, + "title": "Elbow Macaroni - 400 gm", + "description": "Product details of Bake Parlor Big Elbow Macaroni - 400 gm", + "price": 14, + "discountPercentage": 15.58, + "rating": 4.57, + "stock": 146, + "brand": "Bake Parlor Big", + "category": "groceries", + "thumbnail": "https://i.dummyjson.com/data/products/22/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/22/1.jpg", + "https://i.dummyjson.com/data/products/22/2.jpg", + "https://i.dummyjson.com/data/products/22/3.jpg" + ] + }, + { + "id": 23, + "title": "Orange Essence Food Flavou", + "description": "Specifications of Orange Essence Food Flavour For Cakes and Baking Food Item", + "price": 14, + "discountPercentage": 8.04, + "rating": 4.85, + "stock": 26, + "brand": "Baking Food Items", + "category": "groceries", + "thumbnail": "https://i.dummyjson.com/data/products/23/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/23/1.jpg", + "https://i.dummyjson.com/data/products/23/2.jpg", + "https://i.dummyjson.com/data/products/23/3.jpg", + "https://i.dummyjson.com/data/products/23/4.jpg", + "https://i.dummyjson.com/data/products/23/thumbnail.jpg" + ] + }, + { + "id": 24, + "title": "cereals muesli fruit nuts", + "description": "original fauji cereal muesli 250gm box pack original fauji cereals muesli fruit nuts flakes breakfast cereal break fast faujicereals cerels cerel foji fouji", + "price": 46, + "discountPercentage": 16.8, + "rating": 4.94, + "stock": 113, + "brand": "fauji", + "category": "groceries", + "thumbnail": "https://i.dummyjson.com/data/products/24/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/24/1.jpg", + "https://i.dummyjson.com/data/products/24/2.jpg", + "https://i.dummyjson.com/data/products/24/3.jpg", + "https://i.dummyjson.com/data/products/24/4.jpg", + "https://i.dummyjson.com/data/products/24/thumbnail.jpg" + ] + }, + { + "id": 25, + "title": "Gulab Powder 50 Gram", + "description": "Dry Rose Flower Powder Gulab Powder 50 Gram • Treats Wounds", + "price": 70, + "discountPercentage": 13.58, + "rating": 4.87, + "stock": 47, + "brand": "Dry Rose", + "category": "groceries", + "thumbnail": "https://i.dummyjson.com/data/products/25/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/25/1.png", + "https://i.dummyjson.com/data/products/25/2.jpg", + "https://i.dummyjson.com/data/products/25/3.png", + "https://i.dummyjson.com/data/products/25/4.jpg", + "https://i.dummyjson.com/data/products/25/thumbnail.jpg" + ] + }, + { + "id": 26, + "title": "Plant Hanger For Home", + "description": "Boho Decor Plant Hanger For Home Wall Decoration Macrame Wall Hanging Shelf", + "price": 41, + "discountPercentage": 17.86, + "rating": 4.08, + "stock": 131, + "brand": "Boho Decor", + "category": "home-decoration", + "thumbnail": "https://i.dummyjson.com/data/products/26/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/26/1.jpg", + "https://i.dummyjson.com/data/products/26/2.jpg", + "https://i.dummyjson.com/data/products/26/3.jpg", + "https://i.dummyjson.com/data/products/26/4.jpg", + "https://i.dummyjson.com/data/products/26/5.jpg", + "https://i.dummyjson.com/data/products/26/thumbnail.jpg" + ] + }, + { + "id": 27, + "title": "Flying Wooden Bird", + "description": "Package Include 6 Birds with Adhesive Tape Shape: 3D Shaped Wooden Birds Material: Wooden MDF, Laminated 3.5mm", + "price": 51, + "discountPercentage": 15.58, + "rating": 4.41, + "stock": 17, + "brand": "Flying Wooden", + "category": "home-decoration", + "thumbnail": "https://i.dummyjson.com/data/products/27/thumbnail.webp", + "images": [ + "https://i.dummyjson.com/data/products/27/1.jpg", + "https://i.dummyjson.com/data/products/27/2.jpg", + "https://i.dummyjson.com/data/products/27/3.jpg", + "https://i.dummyjson.com/data/products/27/4.jpg", + "https://i.dummyjson.com/data/products/27/thumbnail.webp" + ] + }, + { + "id": 28, + "title": "3D Embellishment Art Lamp", + "description": "3D led lamp sticker Wall sticker 3d wall art light on/off button cell operated (included)", + "price": 20, + "discountPercentage": 16.49, + "rating": 4.82, + "stock": 54, + "brand": "LED Lights", + "category": "home-decoration", + "thumbnail": "https://i.dummyjson.com/data/products/28/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/28/1.jpg", + "https://i.dummyjson.com/data/products/28/2.jpg", + "https://i.dummyjson.com/data/products/28/3.png", + "https://i.dummyjson.com/data/products/28/4.jpg", + "https://i.dummyjson.com/data/products/28/thumbnail.jpg" + ] + }, + { + "id": 29, + "title": "Handcraft Chinese style", + "description": "Handcraft Chinese style art luxury palace hotel villa mansion home decor ceramic vase with brass fruit plate", + "price": 60, + "discountPercentage": 15.34, + "rating": 4.44, + "stock": 7, + "brand": "luxury palace", + "category": "home-decoration", + "thumbnail": "https://i.dummyjson.com/data/products/29/thumbnail.webp", + "images": [ + "https://i.dummyjson.com/data/products/29/1.jpg", + "https://i.dummyjson.com/data/products/29/2.jpg", + "https://i.dummyjson.com/data/products/29/3.webp", + "https://i.dummyjson.com/data/products/29/4.webp", + "https://i.dummyjson.com/data/products/29/thumbnail.webp" + ] + }, + { + "id": 30, + "title": "Key Holder", + "description": "Attractive DesignMetallic materialFour key hooksReliable & DurablePremium Quality", + "price": 30, + "discountPercentage": 2.92, + "rating": 4.92, + "stock": 54, + "brand": "Golden", + "category": "home-decoration", + "thumbnail": "https://i.dummyjson.com/data/products/30/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/30/1.jpg", + "https://i.dummyjson.com/data/products/30/2.jpg", + "https://i.dummyjson.com/data/products/30/3.jpg", + "https://i.dummyjson.com/data/products/30/thumbnail.jpg" + ] + }, + { + "id": 31, + "title": "Mornadi Velvet Bed", + "description": "Mornadi Velvet Bed Base with Headboard Slats Support Classic Style Bedroom Furniture Bed Set", + "price": 40, + "discountPercentage": 17, + "rating": 4.16, + "stock": 140, + "brand": "Furniture Bed Set", + "category": "furniture", + "thumbnail": "https://i.dummyjson.com/data/products/31/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/31/1.jpg", + "https://i.dummyjson.com/data/products/31/2.jpg", + "https://i.dummyjson.com/data/products/31/3.jpg", + "https://i.dummyjson.com/data/products/31/4.jpg", + "https://i.dummyjson.com/data/products/31/thumbnail.jpg" + ] + }, + { + "id": 32, + "title": "Sofa for Coffe Cafe", + "description": "Ratttan Outdoor furniture Set Waterproof Rattan Sofa for Coffe Cafe", + "price": 50, + "discountPercentage": 15.59, + "rating": 4.74, + "stock": 30, + "brand": "Ratttan Outdoor", + "category": "furniture", + "thumbnail": "https://i.dummyjson.com/data/products/32/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/32/1.jpg", + "https://i.dummyjson.com/data/products/32/2.jpg", + "https://i.dummyjson.com/data/products/32/3.jpg", + "https://i.dummyjson.com/data/products/32/thumbnail.jpg" + ] + }, + { + "id": 33, + "title": "3 Tier Corner Shelves", + "description": "3 Tier Corner Shelves | 3 PCs Wall Mount Kitchen Shelf | Floating Bedroom Shelf", + "price": 700, + "discountPercentage": 17, + "rating": 4.31, + "stock": 106, + "brand": "Kitchen Shelf", + "category": "furniture", + "thumbnail": "https://i.dummyjson.com/data/products/33/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/33/1.jpg", + "https://i.dummyjson.com/data/products/33/2.jpg", + "https://i.dummyjson.com/data/products/33/3.jpg", + "https://i.dummyjson.com/data/products/33/4.jpg", + "https://i.dummyjson.com/data/products/33/thumbnail.jpg" + ] + }, + { + "id": 34, + "title": "Plastic Table", + "description": "Very good quality plastic table for multi purpose now in reasonable price", + "price": 50, + "discountPercentage": 4, + "rating": 4.01, + "stock": 136, + "brand": "Multi Purpose", + "category": "furniture", + "thumbnail": "https://i.dummyjson.com/data/products/34/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/34/1.jpg", + "https://i.dummyjson.com/data/products/34/2.jpg", + "https://i.dummyjson.com/data/products/34/3.jpg", + "https://i.dummyjson.com/data/products/34/4.jpg", + "https://i.dummyjson.com/data/products/34/thumbnail.jpg" + ] + }, + { + "id": 35, + "title": "3 DOOR PORTABLE", + "description": "Material: Stainless Steel and Fabric Item Size: 110 cm x 45 cm x 175 cm Package Contents: 1 Storage Wardrobe", + "price": 41, + "discountPercentage": 7.98, + "rating": 4.06, + "stock": 68, + "brand": "AmnaMart", + "category": "furniture", + "thumbnail": "https://i.dummyjson.com/data/products/35/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/35/1.jpg", + "https://i.dummyjson.com/data/products/35/2.jpg", + "https://i.dummyjson.com/data/products/35/3.jpg", + "https://i.dummyjson.com/data/products/35/4.jpg", + "https://i.dummyjson.com/data/products/35/thumbnail.jpg" + ] + }, + { + "id": 36, + "title": "Sleeve Shirt Womens", + "description": "Cotton Solid Color Professional Wear Sleeve Shirt Womens Work Blouses Wholesale Clothing Casual Plain Custom Top OEM Customized", + "price": 90, + "discountPercentage": 10.89, + "rating": 4.26, + "stock": 39, + "brand": "Professional Wear", + "category": "tops", + "thumbnail": "https://i.dummyjson.com/data/products/36/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/36/1.jpg", + "https://i.dummyjson.com/data/products/36/2.webp", + "https://i.dummyjson.com/data/products/36/3.webp", + "https://i.dummyjson.com/data/products/36/4.jpg", + "https://i.dummyjson.com/data/products/36/thumbnail.jpg" + ] + }, + { + "id": 37, + "title": "ank Tops for Womens/Girls", + "description": "PACK OF 3 CAMISOLES ,VERY COMFORTABLE SOFT COTTON STUFF, COMFORTABLE IN ALL FOUR SEASONS", + "price": 50, + "discountPercentage": 12.05, + "rating": 4.52, + "stock": 107, + "brand": "Soft Cotton", + "category": "tops", + "thumbnail": "https://i.dummyjson.com/data/products/37/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/37/1.jpg", + "https://i.dummyjson.com/data/products/37/2.jpg", + "https://i.dummyjson.com/data/products/37/3.jpg", + "https://i.dummyjson.com/data/products/37/4.jpg", + "https://i.dummyjson.com/data/products/37/thumbnail.jpg" + ] + }, + { + "id": 38, + "title": "sublimation plain kids tank", + "description": "sublimation plain kids tank tops wholesale", + "price": 100, + "discountPercentage": 11.12, + "rating": 4.8, + "stock": 20, + "brand": "Soft Cotton", + "category": "tops", + "thumbnail": "https://i.dummyjson.com/data/products/38/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/38/1.png", + "https://i.dummyjson.com/data/products/38/2.jpg", + "https://i.dummyjson.com/data/products/38/3.jpg", + "https://i.dummyjson.com/data/products/38/4.jpg" + ] + }, + { + "id": 39, + "title": "Women Sweaters Wool", + "description": "2021 Custom Winter Fall Zebra Knit Crop Top Women Sweaters Wool Mohair Cos Customize Crew Neck Women' S Crop Top Sweater", + "price": 600, + "discountPercentage": 17.2, + "rating": 4.55, + "stock": 55, + "brand": "Top Sweater", + "category": "tops", + "thumbnail": "https://i.dummyjson.com/data/products/39/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/39/1.jpg", + "https://i.dummyjson.com/data/products/39/2.jpg", + "https://i.dummyjson.com/data/products/39/3.jpg", + "https://i.dummyjson.com/data/products/39/4.jpg", + "https://i.dummyjson.com/data/products/39/thumbnail.jpg" + ] + }, + { + "id": 40, + "title": "women winter clothes", + "description": "women winter clothes thick fleece hoodie top with sweat pantjogger women sweatsuit set joggers pants two piece pants set", + "price": 57, + "discountPercentage": 13.39, + "rating": 4.91, + "stock": 84, + "brand": "Top Sweater", + "category": "tops", + "thumbnail": "https://i.dummyjson.com/data/products/40/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/40/1.jpg", + "https://i.dummyjson.com/data/products/40/2.jpg" + ] + }, + { + "id": 41, + "title": "NIGHT SUIT", + "description": "NIGHT SUIT RED MICKY MOUSE.. For Girls. Fantastic Suits.", + "price": 55, + "discountPercentage": 15.05, + "rating": 4.65, + "stock": 21, + "brand": "RED MICKY MOUSE..", + "category": "womens-dresses", + "thumbnail": "https://i.dummyjson.com/data/products/41/thumbnail.webp", + "images": [ + "https://i.dummyjson.com/data/products/41/1.jpg", + "https://i.dummyjson.com/data/products/41/2.webp", + "https://i.dummyjson.com/data/products/41/3.jpg", + "https://i.dummyjson.com/data/products/41/4.jpg", + "https://i.dummyjson.com/data/products/41/thumbnail.webp" + ] + }, + { + "id": 42, + "title": "Stiched Kurta plus trouser", + "description": "FABRIC: LILEIN CHEST: 21 LENGHT: 37 TROUSER: (38) :ARABIC LILEIN", + "price": 80, + "discountPercentage": 15.37, + "rating": 4.05, + "stock": 148, + "brand": "Digital Printed", + "category": "womens-dresses", + "thumbnail": "https://i.dummyjson.com/data/products/42/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/42/1.png", + "https://i.dummyjson.com/data/products/42/2.png", + "https://i.dummyjson.com/data/products/42/3.png", + "https://i.dummyjson.com/data/products/42/4.jpg", + "https://i.dummyjson.com/data/products/42/thumbnail.jpg" + ] + }, + { + "id": 43, + "title": "frock gold printed", + "description": "Ghazi fabric long frock gold printed ready to wear stitched collection (G992)", + "price": 600, + "discountPercentage": 15.55, + "rating": 4.31, + "stock": 150, + "brand": "Ghazi Fabric", + "category": "womens-dresses", + "thumbnail": "https://i.dummyjson.com/data/products/43/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/43/1.jpg", + "https://i.dummyjson.com/data/products/43/2.jpg", + "https://i.dummyjson.com/data/products/43/3.jpg", + "https://i.dummyjson.com/data/products/43/4.jpg", + "https://i.dummyjson.com/data/products/43/thumbnail.jpg" + ] + }, + { + "id": 44, + "title": "Ladies Multicolored Dress", + "description": "This classy shirt for women gives you a gorgeous look on everyday wear and specially for semi-casual wears.", + "price": 79, + "discountPercentage": 16.88, + "rating": 4.03, + "stock": 2, + "brand": "Ghazi Fabric", + "category": "womens-dresses", + "thumbnail": "https://i.dummyjson.com/data/products/44/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/44/1.jpg", + "https://i.dummyjson.com/data/products/44/2.jpg", + "https://i.dummyjson.com/data/products/44/3.jpg", + "https://i.dummyjson.com/data/products/44/4.jpg", + "https://i.dummyjson.com/data/products/44/thumbnail.jpg" + ] + }, + { + "id": 45, + "title": "Malai Maxi Dress", + "description": "Ready to wear, Unique design according to modern standard fashion, Best fitting ,Imported stuff", + "price": 50, + "discountPercentage": 5.07, + "rating": 4.67, + "stock": 96, + "brand": "IELGY", + "category": "womens-dresses", + "thumbnail": "https://i.dummyjson.com/data/products/45/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/45/1.jpg", + "https://i.dummyjson.com/data/products/45/2.webp", + "https://i.dummyjson.com/data/products/45/3.jpg", + "https://i.dummyjson.com/data/products/45/4.jpg", + "https://i.dummyjson.com/data/products/45/thumbnail.jpg" + ] + }, + { + "id": 46, + "title": "women's shoes", + "description": "Close: Lace, Style with bottom: Increased inside, Sole Material: Rubber", + "price": 40, + "discountPercentage": 16.96, + "rating": 4.14, + "stock": 72, + "brand": "IELGY fashion", + "category": "womens-shoes", + "thumbnail": "https://i.dummyjson.com/data/products/46/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/46/1.webp", + "https://i.dummyjson.com/data/products/46/2.jpg", + "https://i.dummyjson.com/data/products/46/3.jpg", + "https://i.dummyjson.com/data/products/46/4.jpg", + "https://i.dummyjson.com/data/products/46/thumbnail.jpg" + ] + }, + { + "id": 47, + "title": "Sneaker shoes", + "description": "Synthetic Leather Casual Sneaker shoes for Women/girls Sneakers For Women", + "price": 120, + "discountPercentage": 10.37, + "rating": 4.19, + "stock": 50, + "brand": "Synthetic Leather", + "category": "womens-shoes", + "thumbnail": "https://i.dummyjson.com/data/products/47/thumbnail.jpeg", + "images": [ + "https://i.dummyjson.com/data/products/47/1.jpg", + "https://i.dummyjson.com/data/products/47/2.jpg", + "https://i.dummyjson.com/data/products/47/3.jpg", + "https://i.dummyjson.com/data/products/47/thumbnail.jpeg" + ] + }, + { + "id": 48, + "title": "Women Strip Heel", + "description": "Features: Flip-flops, Mid Heel, Comfortable, Striped Heel, Antiskid, Striped", + "price": 40, + "discountPercentage": 10.83, + "rating": 4.02, + "stock": 25, + "brand": "Sandals Flip Flops", + "category": "womens-shoes", + "thumbnail": "https://i.dummyjson.com/data/products/48/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/48/1.jpg", + "https://i.dummyjson.com/data/products/48/2.jpg", + "https://i.dummyjson.com/data/products/48/3.jpg", + "https://i.dummyjson.com/data/products/48/4.jpg", + "https://i.dummyjson.com/data/products/48/thumbnail.jpg" + ] + }, + { + "id": 49, + "title": "Chappals & Shoe Ladies Metallic", + "description": "Womens Chappals & Shoe Ladies Metallic Tong Thong Sandal Flat Summer 2020 Maasai Sandals", + "price": 23, + "discountPercentage": 2.62, + "rating": 4.72, + "stock": 107, + "brand": "Maasai Sandals", + "category": "womens-shoes", + "thumbnail": "https://i.dummyjson.com/data/products/49/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/49/1.jpg", + "https://i.dummyjson.com/data/products/49/2.jpg", + "https://i.dummyjson.com/data/products/49/3.webp", + "https://i.dummyjson.com/data/products/49/thumbnail.jpg" + ] + }, + { + "id": 50, + "title": "Women Shoes", + "description": "2020 New Arrivals Genuine Leather Fashion Trend Platform Summer Women Shoes", + "price": 36, + "discountPercentage": 16.87, + "rating": 4.33, + "stock": 46, + "brand": "Arrivals Genuine", + "category": "womens-shoes", + "thumbnail": "https://i.dummyjson.com/data/products/50/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/50/1.jpeg", + "https://i.dummyjson.com/data/products/50/2.jpg", + "https://i.dummyjson.com/data/products/50/3.jpg" + ] + }, + { + "id": 51, + "title": "half sleeves T shirts", + "description": "Many store is creating new designs and trend every month and every year. Daraz.pk have a beautiful range of men fashion brands", + "price": 23, + "discountPercentage": 12.76, + "rating": 4.26, + "stock": 132, + "brand": "Vintage Apparel", + "category": "mens-shirts", + "thumbnail": "https://i.dummyjson.com/data/products/51/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/51/1.png", + "https://i.dummyjson.com/data/products/51/2.jpg", + "https://i.dummyjson.com/data/products/51/3.jpg", + "https://i.dummyjson.com/data/products/51/thumbnail.jpg" + ] + }, + { + "id": 52, + "title": "FREE FIRE T Shirt", + "description": "quality and professional print - It doesn't just look high quality, it is high quality.", + "price": 10, + "discountPercentage": 14.72, + "rating": 4.52, + "stock": 128, + "brand": "FREE FIRE", + "category": "mens-shirts", + "thumbnail": "https://i.dummyjson.com/data/products/52/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/52/1.png", + "https://i.dummyjson.com/data/products/52/2.png", + "https://i.dummyjson.com/data/products/52/3.jpg", + "https://i.dummyjson.com/data/products/52/4.jpg", + "https://i.dummyjson.com/data/products/52/thumbnail.jpg" + ] + }, + { + "id": 53, + "title": "printed high quality T shirts", + "description": "Brand: vintage Apparel ,Export quality", + "price": 35, + "discountPercentage": 7.54, + "rating": 4.89, + "stock": 6, + "brand": "Vintage Apparel", + "category": "mens-shirts", + "thumbnail": "https://i.dummyjson.com/data/products/53/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/53/1.webp", + "https://i.dummyjson.com/data/products/53/2.jpg", + "https://i.dummyjson.com/data/products/53/3.jpg", + "https://i.dummyjson.com/data/products/53/4.jpg", + "https://i.dummyjson.com/data/products/53/thumbnail.jpg" + ] + }, + { + "id": 54, + "title": "Pubg Printed Graphic T-Shirt", + "description": "Product Description Features: 100% Ultra soft Polyester Jersey. Vibrant & colorful printing on front. Feels soft as cotton without ever cracking", + "price": 46, + "discountPercentage": 16.44, + "rating": 4.62, + "stock": 136, + "brand": "The Warehouse", + "category": "mens-shirts", + "thumbnail": "https://i.dummyjson.com/data/products/54/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/54/1.jpg", + "https://i.dummyjson.com/data/products/54/2.jpg", + "https://i.dummyjson.com/data/products/54/3.jpg", + "https://i.dummyjson.com/data/products/54/4.jpg", + "https://i.dummyjson.com/data/products/54/thumbnail.jpg" + ] + }, + { + "id": 55, + "title": "Money Heist Printed Summer T Shirts", + "description": "Fabric Jercy, Size: M & L Wear Stylish Dual Stiched", + "price": 66, + "discountPercentage": 15.97, + "rating": 4.9, + "stock": 122, + "brand": "The Warehouse", + "category": "mens-shirts", + "thumbnail": "https://i.dummyjson.com/data/products/55/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/55/1.jpg", + "https://i.dummyjson.com/data/products/55/2.webp", + "https://i.dummyjson.com/data/products/55/3.jpg", + "https://i.dummyjson.com/data/products/55/4.jpg", + "https://i.dummyjson.com/data/products/55/thumbnail.jpg" + ] + }, + { + "id": 56, + "title": "Sneakers Joggers Shoes", + "description": "Gender: Men , Colors: Same as DisplayedCondition: 100% Brand New", + "price": 40, + "discountPercentage": 12.57, + "rating": 4.38, + "stock": 6, + "brand": "Sneakers", + "category": "mens-shoes", + "thumbnail": "https://i.dummyjson.com/data/products/56/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/56/1.jpg", + "https://i.dummyjson.com/data/products/56/2.jpg", + "https://i.dummyjson.com/data/products/56/3.jpg", + "https://i.dummyjson.com/data/products/56/4.jpg", + "https://i.dummyjson.com/data/products/56/5.jpg", + "https://i.dummyjson.com/data/products/56/thumbnail.jpg" + ] + }, + { + "id": 57, + "title": "Loafers for men", + "description": "Men Shoes - Loafers for men - Rubber Shoes - Nylon Shoes - Shoes for men - Moccassion - Pure Nylon (Rubber) Expot Quality.", + "price": 47, + "discountPercentage": 10.91, + "rating": 4.91, + "stock": 20, + "brand": "Rubber", + "category": "mens-shoes", + "thumbnail": "https://i.dummyjson.com/data/products/57/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/57/1.jpg", + "https://i.dummyjson.com/data/products/57/2.jpg", + "https://i.dummyjson.com/data/products/57/3.jpg", + "https://i.dummyjson.com/data/products/57/4.jpg", + "https://i.dummyjson.com/data/products/57/thumbnail.jpg" + ] + }, + { + "id": 58, + "title": "formal offices shoes", + "description": "Pattern Type: Solid, Material: PU, Toe Shape: Pointed Toe ,Outsole Material: Rubber", + "price": 57, + "discountPercentage": 12, + "rating": 4.41, + "stock": 68, + "brand": "The Warehouse", + "category": "mens-shoes", + "thumbnail": "https://i.dummyjson.com/data/products/58/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/58/1.jpg", + "https://i.dummyjson.com/data/products/58/2.jpg", + "https://i.dummyjson.com/data/products/58/3.jpg", + "https://i.dummyjson.com/data/products/58/4.jpg", + "https://i.dummyjson.com/data/products/58/thumbnail.jpg" + ] + }, + { + "id": 59, + "title": "Spring and summershoes", + "description": "Comfortable stretch cloth, lightweight body; ,rubber sole, anti-skid wear;", + "price": 20, + "discountPercentage": 8.71, + "rating": 4.33, + "stock": 137, + "brand": "Sneakers", + "category": "mens-shoes", + "thumbnail": "https://i.dummyjson.com/data/products/59/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/59/1.jpg", + "https://i.dummyjson.com/data/products/59/2.jpg", + "https://i.dummyjson.com/data/products/59/3.jpg", + "https://i.dummyjson.com/data/products/59/4.jpg", + "https://i.dummyjson.com/data/products/59/thumbnail.jpg" + ] + }, + { + "id": 60, + "title": "Stylish Casual Jeans Shoes", + "description": "High Quality ,Stylish design ,Comfortable wear ,FAshion ,Durable", + "price": 58, + "discountPercentage": 7.55, + "rating": 4.55, + "stock": 129, + "brand": "Sneakers", + "category": "mens-shoes", + "thumbnail": "https://i.dummyjson.com/data/products/60/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/60/1.jpg", + "https://i.dummyjson.com/data/products/60/2.jpg", + "https://i.dummyjson.com/data/products/60/3.jpg", + "https://i.dummyjson.com/data/products/60/thumbnail.jpg" + ] + }, + { + "id": 61, + "title": "Leather Straps Wristwatch", + "description": "Style:Sport ,Clasp:Buckles ,Water Resistance Depth:3Bar", + "price": 120, + "discountPercentage": 7.14, + "rating": 4.63, + "stock": 91, + "brand": "Naviforce", + "category": "mens-watches", + "thumbnail": "https://i.dummyjson.com/data/products/61/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/61/1.jpg", + "https://i.dummyjson.com/data/products/61/2.png", + "https://i.dummyjson.com/data/products/61/3.jpg" + ] + }, + { + "id": 62, + "title": "Waterproof Leather Brand Watch", + "description": "Watch Crown With Environmental IPS Bronze Electroplating; Display system of 12 hours", + "price": 46, + "discountPercentage": 3.15, + "rating": 4.05, + "stock": 95, + "brand": "SKMEI 9117", + "category": "mens-watches", + "thumbnail": "https://i.dummyjson.com/data/products/62/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/62/1.jpg", + "https://i.dummyjson.com/data/products/62/2.jpg" + ] + }, + { + "id": 63, + "title": "Royal Blue Premium Watch", + "description": "Men Silver Chain Royal Blue Premium Watch Latest Analog Watch", + "price": 50, + "discountPercentage": 2.56, + "rating": 4.89, + "stock": 142, + "brand": "SKMEI 9117", + "category": "mens-watches", + "thumbnail": "https://i.dummyjson.com/data/products/63/thumbnail.webp", + "images": [ + "https://i.dummyjson.com/data/products/63/1.jpg", + "https://i.dummyjson.com/data/products/63/2.jpg", + "https://i.dummyjson.com/data/products/63/3.png", + "https://i.dummyjson.com/data/products/63/4.jpeg" + ] + }, + { + "id": 64, + "title": "Leather Strap Skeleton Watch", + "description": "Leather Strap Skeleton Watch for Men - Stylish and Latest Design", + "price": 46, + "discountPercentage": 10.2, + "rating": 4.98, + "stock": 61, + "brand": "Strap Skeleton", + "category": "mens-watches", + "thumbnail": "https://i.dummyjson.com/data/products/64/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/64/1.jpg", + "https://i.dummyjson.com/data/products/64/2.webp", + "https://i.dummyjson.com/data/products/64/3.jpg", + "https://i.dummyjson.com/data/products/64/thumbnail.jpg" + ] + }, + { + "id": 65, + "title": "Stainless Steel Wrist Watch", + "description": "Stylish Watch For Man (Luxury) Classy Men's Stainless Steel Wrist Watch - Box Packed", + "price": 47, + "discountPercentage": 17.79, + "rating": 4.79, + "stock": 94, + "brand": "Stainless", + "category": "mens-watches", + "thumbnail": "https://i.dummyjson.com/data/products/65/thumbnail.webp", + "images": [ + "https://i.dummyjson.com/data/products/65/1.jpg", + "https://i.dummyjson.com/data/products/65/2.webp", + "https://i.dummyjson.com/data/products/65/3.jpg", + "https://i.dummyjson.com/data/products/65/4.webp", + "https://i.dummyjson.com/data/products/65/thumbnail.webp" + ] + }, + { + "id": 66, + "title": "Steel Analog Couple Watches", + "description": "Elegant design, Stylish ,Unique & Trendy,Comfortable wear", + "price": 35, + "discountPercentage": 3.23, + "rating": 4.79, + "stock": 24, + "brand": "Eastern Watches", + "category": "womens-watches", + "thumbnail": "https://i.dummyjson.com/data/products/66/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/66/1.jpg", + "https://i.dummyjson.com/data/products/66/2.jpg", + "https://i.dummyjson.com/data/products/66/3.jpg", + "https://i.dummyjson.com/data/products/66/4.JPG", + "https://i.dummyjson.com/data/products/66/thumbnail.jpg" + ] + }, + { + "id": 67, + "title": "Fashion Magnetic Wrist Watch", + "description": "Buy this awesome The product is originally manufactured by the company and it's a top selling product with a very reasonable", + "price": 60, + "discountPercentage": 16.69, + "rating": 4.03, + "stock": 46, + "brand": "Eastern Watches", + "category": "womens-watches", + "thumbnail": "https://i.dummyjson.com/data/products/67/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/67/1.jpg", + "https://i.dummyjson.com/data/products/67/2.jpg", + "https://i.dummyjson.com/data/products/67/3.jpg", + "https://i.dummyjson.com/data/products/67/4.jpg", + "https://i.dummyjson.com/data/products/67/thumbnail.jpg" + ] + }, + { + "id": 68, + "title": "Stylish Luxury Digital Watch", + "description": "Stylish Luxury Digital Watch For Girls / Women - Led Smart Ladies Watches For Girls", + "price": 57, + "discountPercentage": 9.03, + "rating": 4.55, + "stock": 77, + "brand": "Luxury Digital", + "category": "womens-watches", + "thumbnail": "https://i.dummyjson.com/data/products/68/thumbnail.webp", + "images": [ + "https://i.dummyjson.com/data/products/68/1.jpg", + "https://i.dummyjson.com/data/products/68/2.jpg" + ] + }, + { + "id": 69, + "title": "Golden Watch Pearls Bracelet Watch", + "description": "Product details of Golden Watch Pearls Bracelet Watch For Girls - Golden Chain Ladies Bracelate Watch for Women", + "price": 47, + "discountPercentage": 17.55, + "rating": 4.77, + "stock": 89, + "brand": "Watch Pearls", + "category": "womens-watches", + "thumbnail": "https://i.dummyjson.com/data/products/69/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/69/1.jpg", + "https://i.dummyjson.com/data/products/69/2.jpg", + "https://i.dummyjson.com/data/products/69/3.webp", + "https://i.dummyjson.com/data/products/69/4.jpg", + "https://i.dummyjson.com/data/products/69/thumbnail.jpg" + ] + }, + { + "id": 70, + "title": "Stainless Steel Women", + "description": "Fashion Skmei 1830 Shell Dial Stainless Steel Women Wrist Watch Lady Bracelet Watch Quartz Watches Ladies", + "price": 35, + "discountPercentage": 8.98, + "rating": 4.08, + "stock": 111, + "brand": "Bracelet", + "category": "womens-watches", + "thumbnail": "https://i.dummyjson.com/data/products/70/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/70/1.jpg", + "https://i.dummyjson.com/data/products/70/2.jpg", + "https://i.dummyjson.com/data/products/70/thumbnail.jpg" + ] + }, + { + "id": 71, + "title": "Women Shoulder Bags", + "description": "LouisWill Women Shoulder Bags Long Clutches Cross Body Bags Phone Bags PU Leather Hand Bags Large Capacity Card Holders Zipper Coin Purses Fashion Crossbody Bags for Girls Ladies", + "price": 46, + "discountPercentage": 14.65, + "rating": 4.71, + "stock": 17, + "brand": "LouisWill", + "category": "womens-bags", + "thumbnail": "https://i.dummyjson.com/data/products/71/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/71/1.jpg", + "https://i.dummyjson.com/data/products/71/2.jpg", + "https://i.dummyjson.com/data/products/71/3.webp", + "https://i.dummyjson.com/data/products/71/thumbnail.jpg" + ] + }, + { + "id": 72, + "title": "Handbag For Girls", + "description": "This fashion is designed to add a charming effect to your casual outfit. This Bag is made of synthetic leather.", + "price": 23, + "discountPercentage": 17.5, + "rating": 4.91, + "stock": 27, + "brand": "LouisWill", + "category": "womens-bags", + "thumbnail": "https://i.dummyjson.com/data/products/72/thumbnail.webp", + "images": [ + "https://i.dummyjson.com/data/products/72/1.jpg", + "https://i.dummyjson.com/data/products/72/2.png", + "https://i.dummyjson.com/data/products/72/3.webp", + "https://i.dummyjson.com/data/products/72/4.jpg", + "https://i.dummyjson.com/data/products/72/thumbnail.webp" + ] + }, + { + "id": 73, + "title": "Fancy hand clutch", + "description": "This fashion is designed to add a charming effect to your casual outfit. This Bag is made of synthetic leather.", + "price": 44, + "discountPercentage": 10.39, + "rating": 4.18, + "stock": 101, + "brand": "Bracelet", + "category": "womens-bags", + "thumbnail": "https://i.dummyjson.com/data/products/73/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/73/1.jpg", + "https://i.dummyjson.com/data/products/73/2.webp", + "https://i.dummyjson.com/data/products/73/3.jpg", + "https://i.dummyjson.com/data/products/73/thumbnail.jpg" + ] + }, + { + "id": 74, + "title": "Leather Hand Bag", + "description": "It features an attractive design that makes it a must have accessory in your collection. We sell different kind of bags for boys, kids, women, girls and also for unisex.", + "price": 57, + "discountPercentage": 11.19, + "rating": 4.01, + "stock": 43, + "brand": "Copenhagen Luxe", + "category": "womens-bags", + "thumbnail": "https://i.dummyjson.com/data/products/74/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/74/1.jpg", + "https://i.dummyjson.com/data/products/74/2.jpg", + "https://i.dummyjson.com/data/products/74/3.jpg", + "https://i.dummyjson.com/data/products/74/4.jpg", + "https://i.dummyjson.com/data/products/74/thumbnail.jpg" + ] + }, + { + "id": 75, + "title": "Seven Pocket Women Bag", + "description": "Seven Pocket Women Bag Handbags Lady Shoulder Crossbody Bag Female Purse Seven Pocket Bag", + "price": 68, + "discountPercentage": 14.87, + "rating": 4.93, + "stock": 13, + "brand": "Steal Frame", + "category": "womens-bags", + "thumbnail": "https://i.dummyjson.com/data/products/75/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/75/1.jpg", + "https://i.dummyjson.com/data/products/75/2.jpg", + "https://i.dummyjson.com/data/products/75/3.jpg", + "https://i.dummyjson.com/data/products/75/thumbnail.jpg" + ] + }, + { + "id": 76, + "title": "Silver Ring Set Women", + "description": "Jewelry Type:RingsCertificate Type:NonePlating:Silver PlatedShapeattern:noneStyle:CLASSICReligious", + "price": 70, + "discountPercentage": 13.57, + "rating": 4.61, + "stock": 51, + "brand": "Darojay", + "category": "womens-jewellery", + "thumbnail": "https://i.dummyjson.com/data/products/76/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/76/1.jpg", + "https://i.dummyjson.com/data/products/76/2.jpg", + "https://i.dummyjson.com/data/products/76/thumbnail.jpg" + ] + }, + { + "id": 77, + "title": "Rose Ring", + "description": "Brand: The Greetings Flower Colour: RedRing Colour: GoldenSize: Adjustable", + "price": 100, + "discountPercentage": 3.22, + "rating": 4.21, + "stock": 149, + "brand": "Copenhagen Luxe", + "category": "womens-jewellery", + "thumbnail": "https://i.dummyjson.com/data/products/77/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/77/1.jpg", + "https://i.dummyjson.com/data/products/77/2.jpg", + "https://i.dummyjson.com/data/products/77/3.jpg", + "https://i.dummyjson.com/data/products/77/thumbnail.jpg" + ] + }, + { + "id": 78, + "title": "Rhinestone Korean Style Open Rings", + "description": "Fashion Jewellery 3Pcs Adjustable Pearl Rhinestone Korean Style Open Rings For Women", + "price": 30, + "discountPercentage": 8.02, + "rating": 4.69, + "stock": 9, + "brand": "Fashion Jewellery", + "category": "womens-jewellery", + "thumbnail": "https://i.dummyjson.com/data/products/78/thumbnail.jpg", + "images": ["https://i.dummyjson.com/data/products/78/thumbnail.jpg"] + }, + { + "id": 79, + "title": "Elegant Female Pearl Earrings", + "description": "Elegant Female Pearl Earrings Set Zircon Pearl Earings Women Party Accessories 9 Pairs/Set", + "price": 30, + "discountPercentage": 12.8, + "rating": 4.74, + "stock": 16, + "brand": "Fashion Jewellery", + "category": "womens-jewellery", + "thumbnail": "https://i.dummyjson.com/data/products/79/thumbnail.jpg", + "images": ["https://i.dummyjson.com/data/products/79/1.jpg"] + }, + { + "id": 80, + "title": "Chain Pin Tassel Earrings", + "description": "Pair Of Ear Cuff Butterfly Long Chain Pin Tassel Earrings - Silver ( Long Life Quality Product)", + "price": 45, + "discountPercentage": 17.75, + "rating": 4.59, + "stock": 9, + "brand": "Cuff Butterfly", + "category": "womens-jewellery", + "thumbnail": "https://i.dummyjson.com/data/products/80/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/80/1.jpg", + "https://i.dummyjson.com/data/products/80/2.jpg", + "https://i.dummyjson.com/data/products/80/3.png", + "https://i.dummyjson.com/data/products/80/4.jpg", + "https://i.dummyjson.com/data/products/80/thumbnail.jpg" + ] + }, + { + "id": 81, + "title": "Round Silver Frame Sun Glasses", + "description": "A pair of sunglasses can protect your eyes from being hurt. For car driving, vacation travel, outdoor activities, social gatherings,", + "price": 19, + "discountPercentage": 10.1, + "rating": 4.94, + "stock": 78, + "brand": "Designer Sun Glasses", + "category": "sunglasses", + "thumbnail": "https://i.dummyjson.com/data/products/81/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/81/1.jpg", + "https://i.dummyjson.com/data/products/81/2.jpg", + "https://i.dummyjson.com/data/products/81/3.jpg", + "https://i.dummyjson.com/data/products/81/4.webp", + "https://i.dummyjson.com/data/products/81/thumbnail.jpg" + ] + }, + { + "id": 82, + "title": "Kabir Singh Square Sunglass", + "description": "Orignal Metal Kabir Singh design 2020 Sunglasses Men Brand Designer Sun Glasses Kabir Singh Square Sunglass", + "price": 50, + "discountPercentage": 15.6, + "rating": 4.62, + "stock": 78, + "brand": "Designer Sun Glasses", + "category": "sunglasses", + "thumbnail": "https://i.dummyjson.com/data/products/82/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/82/1.jpg", + "https://i.dummyjson.com/data/products/82/2.webp", + "https://i.dummyjson.com/data/products/82/3.jpg", + "https://i.dummyjson.com/data/products/82/4.jpg", + "https://i.dummyjson.com/data/products/82/thumbnail.jpg" + ] + }, + { + "id": 83, + "title": "Wiley X Night Vision Yellow Glasses", + "description": "Wiley X Night Vision Yellow Glasses for Riders - Night Vision Anti Fog Driving Glasses - Free Night Glass Cover - Shield Eyes From Dust and Virus- For Night Sport Matches", + "price": 30, + "discountPercentage": 6.33, + "rating": 4.97, + "stock": 115, + "brand": "mastar watch", + "category": "sunglasses", + "thumbnail": "https://i.dummyjson.com/data/products/83/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/83/1.jpg", + "https://i.dummyjson.com/data/products/83/2.jpg", + "https://i.dummyjson.com/data/products/83/3.jpg", + "https://i.dummyjson.com/data/products/83/4.jpg", + "https://i.dummyjson.com/data/products/83/thumbnail.jpg" + ] + }, + { + "id": 84, + "title": "Square Sunglasses", + "description": "Fashion Oversized Square Sunglasses Retro Gradient Big Frame Sunglasses For Women One Piece Gafas Shade Mirror Clear Lens 17059", + "price": 28, + "discountPercentage": 13.89, + "rating": 4.64, + "stock": 64, + "brand": "mastar watch", + "category": "sunglasses", + "thumbnail": "https://i.dummyjson.com/data/products/84/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/84/1.jpg", + "https://i.dummyjson.com/data/products/84/2.jpg", + "https://i.dummyjson.com/data/products/84/thumbnail.jpg" + ] + }, + { + "id": 85, + "title": "LouisWill Men Sunglasses", + "description": "LouisWill Men Sunglasses Polarized Sunglasses UV400 Sunglasses Day Night Dual Use Safety Driving Night Vision Eyewear AL-MG Frame Sun Glasses with Free Box for Drivers", + "price": 50, + "discountPercentage": 11.27, + "rating": 4.98, + "stock": 92, + "brand": "LouisWill", + "category": "sunglasses", + "thumbnail": "https://i.dummyjson.com/data/products/85/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/85/1.jpg", + "https://i.dummyjson.com/data/products/85/2.jpg", + "https://i.dummyjson.com/data/products/85/thumbnail.jpg" + ] + }, + { + "id": 86, + "title": "Bluetooth Aux", + "description": "Bluetooth Aux Bluetooth Car Aux Car Bluetooth Transmitter Aux Audio Receiver Handfree Car Bluetooth Music Receiver Universal 3.5mm Streaming A2DP Wireless Auto AUX Audio Adapter With Mic For Phone MP3", + "price": 25, + "discountPercentage": 10.56, + "rating": 4.57, + "stock": 22, + "brand": "Car Aux", + "category": "automotive", + "thumbnail": "https://i.dummyjson.com/data/products/86/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/86/1.jpg", + "https://i.dummyjson.com/data/products/86/2.webp", + "https://i.dummyjson.com/data/products/86/3.jpg", + "https://i.dummyjson.com/data/products/86/4.jpg", + "https://i.dummyjson.com/data/products/86/thumbnail.jpg" + ] + }, + { + "id": 87, + "title": "t Temperature Controller Incubator Controller", + "description": "Both Heat and Cool Purpose, Temperature control range; -50 to +110, Temperature measurement accuracy; 0.1, Control accuracy; 0.1", + "price": 40, + "discountPercentage": 11.3, + "rating": 4.54, + "stock": 37, + "brand": "W1209 DC12V", + "category": "automotive", + "thumbnail": "https://i.dummyjson.com/data/products/87/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/87/1.jpg", + "https://i.dummyjson.com/data/products/87/2.jpg", + "https://i.dummyjson.com/data/products/87/3.jpg", + "https://i.dummyjson.com/data/products/87/4.jpg", + "https://i.dummyjson.com/data/products/87/thumbnail.jpg" + ] + }, + { + "id": 88, + "title": "TC Reusable Silicone Magic Washing Gloves", + "description": "TC Reusable Silicone Magic Washing Gloves with Scrubber, Cleaning Brush Scrubber Gloves Heat Resistant Pair for Cleaning of Kitchen, Dishes, Vegetables and Fruits, Bathroom, Car Wash, Pet Care and Multipurpose", + "price": 29, + "discountPercentage": 3.19, + "rating": 4.98, + "stock": 42, + "brand": "TC Reusable", + "category": "automotive", + "thumbnail": "https://i.dummyjson.com/data/products/88/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/88/1.jpg", + "https://i.dummyjson.com/data/products/88/2.jpg", + "https://i.dummyjson.com/data/products/88/3.jpg", + "https://i.dummyjson.com/data/products/88/4.webp", + "https://i.dummyjson.com/data/products/88/thumbnail.jpg" + ] + }, + { + "id": 89, + "title": "Qualcomm original Car Charger", + "description": "best Quality CHarger , Highly Recommended to all best Quality CHarger , Highly Recommended to all", + "price": 40, + "discountPercentage": 17.53, + "rating": 4.2, + "stock": 79, + "brand": "TC Reusable", + "category": "automotive", + "thumbnail": "https://i.dummyjson.com/data/products/89/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/89/1.jpg", + "https://i.dummyjson.com/data/products/89/2.jpg", + "https://i.dummyjson.com/data/products/89/3.jpg", + "https://i.dummyjson.com/data/products/89/4.jpg", + "https://i.dummyjson.com/data/products/89/thumbnail.jpg" + ] + }, + { + "id": 90, + "title": "Cycle Bike Glow", + "description": "Universal fitment and easy to install no special wires, can be easily installed and removed. Fits most standard tyre air stem valves of road, mountain bicycles, motocycles and cars.Bright led will turn on w", + "price": 35, + "discountPercentage": 11.08, + "rating": 4.1, + "stock": 63, + "brand": "Neon LED Light", + "category": "automotive", + "thumbnail": "https://i.dummyjson.com/data/products/90/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/90/1.jpg", + "https://i.dummyjson.com/data/products/90/2.jpg", + "https://i.dummyjson.com/data/products/90/3.jpg", + "https://i.dummyjson.com/data/products/90/4.jpg", + "https://i.dummyjson.com/data/products/90/thumbnail.jpg" + ] + }, + { + "id": 91, + "title": "Black Motorbike", + "description": "Engine Type:Wet sump, Single Cylinder, Four Stroke, Two Valves, Air Cooled with SOHC (Single Over Head Cam) Chain Drive Bore & Stroke:47.0 x 49.5 MM", + "price": 569, + "discountPercentage": 13.63, + "rating": 4.04, + "stock": 115, + "brand": "METRO 70cc Motorcycle - MR70", + "category": "motorcycle", + "thumbnail": "https://i.dummyjson.com/data/products/91/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/91/1.jpg", + "https://i.dummyjson.com/data/products/91/2.jpg", + "https://i.dummyjson.com/data/products/91/3.jpg", + "https://i.dummyjson.com/data/products/91/4.jpg", + "https://i.dummyjson.com/data/products/91/thumbnail.jpg" + ] + }, + { + "id": 92, + "title": "HOT SALE IN EUROPE electric racing motorcycle", + "description": "HOT SALE IN EUROPE electric racing motorcycle electric motorcycle for sale adult electric motorcycles", + "price": 920, + "discountPercentage": 14.4, + "rating": 4.19, + "stock": 22, + "brand": "BRAVE BULL", + "category": "motorcycle", + "thumbnail": "https://i.dummyjson.com/data/products/92/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/92/1.jpg", + "https://i.dummyjson.com/data/products/92/2.jpg", + "https://i.dummyjson.com/data/products/92/3.jpg", + "https://i.dummyjson.com/data/products/92/4.jpg" + ] + }, + { + "id": 93, + "title": "Automatic Motor Gas Motorcycles", + "description": "150cc 4-Stroke Motorcycle Automatic Motor Gas Motorcycles Scooter motorcycles 150cc scooter", + "price": 1050, + "discountPercentage": 3.34, + "rating": 4.84, + "stock": 127, + "brand": "shock absorber", + "category": "motorcycle", + "thumbnail": "https://i.dummyjson.com/data/products/93/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/93/1.jpg", + "https://i.dummyjson.com/data/products/93/2.jpg", + "https://i.dummyjson.com/data/products/93/3.jpg", + "https://i.dummyjson.com/data/products/93/4.jpg", + "https://i.dummyjson.com/data/products/93/thumbnail.jpg" + ] + }, + { + "id": 94, + "title": "new arrivals Fashion motocross goggles", + "description": "new arrivals Fashion motocross goggles motorcycle motocross racing motorcycle", + "price": 900, + "discountPercentage": 3.85, + "rating": 4.06, + "stock": 109, + "brand": "JIEPOLLY", + "category": "motorcycle", + "thumbnail": "https://i.dummyjson.com/data/products/94/thumbnail.webp", + "images": [ + "https://i.dummyjson.com/data/products/94/1.webp", + "https://i.dummyjson.com/data/products/94/2.jpg", + "https://i.dummyjson.com/data/products/94/3.jpg", + "https://i.dummyjson.com/data/products/94/thumbnail.webp" + ] + }, + { + "id": 95, + "title": "Wholesale cargo lashing Belt", + "description": "Wholesale cargo lashing Belt Tie Down end Ratchet strap customized strap 25mm motorcycle 1500kgs with rubber handle", + "price": 930, + "discountPercentage": 17.67, + "rating": 4.21, + "stock": 144, + "brand": "Xiangle", + "category": "motorcycle", + "thumbnail": "https://i.dummyjson.com/data/products/95/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/95/1.jpg", + "https://i.dummyjson.com/data/products/95/2.jpg", + "https://i.dummyjson.com/data/products/95/3.jpg", + "https://i.dummyjson.com/data/products/95/4.jpg", + "https://i.dummyjson.com/data/products/95/thumbnail.jpg" + ] + }, + { + "id": 96, + "title": "lighting ceiling kitchen", + "description": "Wholesale slim hanging decorative kid room lighting ceiling kitchen chandeliers pendant light modern", + "price": 30, + "discountPercentage": 14.89, + "rating": 4.83, + "stock": 96, + "brand": "lightingbrilliance", + "category": "lighting", + "thumbnail": "https://i.dummyjson.com/data/products/96/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/96/1.jpg", + "https://i.dummyjson.com/data/products/96/2.jpg", + "https://i.dummyjson.com/data/products/96/3.jpg", + "https://i.dummyjson.com/data/products/96/4.jpg", + "https://i.dummyjson.com/data/products/96/thumbnail.jpg" + ] + }, + { + "id": 97, + "title": "Metal Ceramic Flower", + "description": "Metal Ceramic Flower Chandelier Home Lighting American Vintage Hanging Lighting Pendant Lamp", + "price": 35, + "discountPercentage": 10.94, + "rating": 4.93, + "stock": 146, + "brand": "Ifei Home", + "category": "lighting", + "thumbnail": "https://i.dummyjson.com/data/products/97/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/97/1.jpg", + "https://i.dummyjson.com/data/products/97/2.jpg", + "https://i.dummyjson.com/data/products/97/3.jpg", + "https://i.dummyjson.com/data/products/97/4.webp", + "https://i.dummyjson.com/data/products/97/thumbnail.jpg" + ] + }, + { + "id": 98, + "title": "3 lights lndenpant kitchen islang", + "description": "3 lights lndenpant kitchen islang dining room pendant rice paper chandelier contemporary led pendant light modern chandelier", + "price": 34, + "discountPercentage": 5.92, + "rating": 4.99, + "stock": 44, + "brand": "DADAWU", + "category": "lighting", + "thumbnail": "https://i.dummyjson.com/data/products/98/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/98/1.jpg", + "https://i.dummyjson.com/data/products/98/2.jpg", + "https://i.dummyjson.com/data/products/98/3.jpg", + "https://i.dummyjson.com/data/products/98/4.jpg", + "https://i.dummyjson.com/data/products/98/thumbnail.jpg" + ] + }, + { + "id": 99, + "title": "American Vintage Wood Pendant Light", + "description": "American Vintage Wood Pendant Light Farmhouse Antique Hanging Lamp Lampara Colgante", + "price": 46, + "discountPercentage": 8.84, + "rating": 4.32, + "stock": 138, + "brand": "Ifei Home", + "category": "lighting", + "thumbnail": "https://i.dummyjson.com/data/products/99/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/99/1.jpg", + "https://i.dummyjson.com/data/products/99/2.jpg", + "https://i.dummyjson.com/data/products/99/3.jpg", + "https://i.dummyjson.com/data/products/99/4.jpg", + "https://i.dummyjson.com/data/products/99/thumbnail.jpg" + ] + }, + { + "id": 100, + "title": "Crystal chandelier maria theresa for 12 light", + "description": "Crystal chandelier maria theresa for 12 light", + "price": 47, + "discountPercentage": 16, + "rating": 4.74, + "stock": 133, + "brand": "YIOSI", + "category": "lighting", + "thumbnail": "https://i.dummyjson.com/data/products/100/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/100/1.jpg", + "https://i.dummyjson.com/data/products/100/2.jpg", + "https://i.dummyjson.com/data/products/100/3.jpg", + "https://i.dummyjson.com/data/products/100/thumbnail.jpg" + ] + }, + { + "title": "Mac Book Air ", + "description": "Mac Book Air ", + "brand": "Apple", + "category": "laptops", + "price": 1111, + "discountPercentage": 11, + "stock": 22, + "thumbnail": "https://i.dummyjson.com/data/products/6/thumbnail.png", + "images": [ + "https://i.dummyjson.com/data/products/6/thumbnail.png", + "https://i.dummyjson.com/data/products/6/thumbnail.png", + "https://i.dummyjson.com/data/products/6/thumbnail.png", + "https://i.dummyjson.com/data/products/6/thumbnail.png" + ], + "id": 101 + } + ], + "brands": [ + { + "value": "Apple", + "label": "Apple", + "checked": false + }, + { + "value": "Samsung", + "label": "Samsung", + "checked": false + }, + { + "value": "OPPO", + "label": "OPPO", + "checked": false + }, + { + "value": "Huawei", + "label": "Huawei", + "checked": false + }, + { + "value": "Microsoft Surface", + "label": "Microsoft Surface", + "checked": false + }, + { + "value": "Infinix", + "label": "Infinix", + "checked": false + }, + { + "value": "HP Pavilion", + "label": "HP Pavilion", + "checked": false + }, + { + "value": "Impression of Acqua Di Gio", + "label": "Impression of Acqua Di Gio", + "checked": false + }, + { + "value": "Royal_Mirage", + "label": "Royal_Mirage", + "checked": false + }, + { + "value": "Fog Scent Xpressio", + "label": "Fog Scent Xpressio", + "checked": false + }, + { + "value": "Al Munakh", + "label": "Al Munakh", + "checked": false + }, + { + "value": "Lord - Al-Rehab", + "label": "Lord Al Rehab", + "checked": false + }, + { + "value": "L'Oreal Paris", + "label": "L'Oreal Paris", + "checked": false + }, + { + "value": "Hemani Tea", + "label": "Hemani Tea", + "checked": false + }, + { + "value": "Dermive", + "label": "Dermive", + "checked": false + }, + { + "value": "ROREC White Rice", + "label": "ROREC White Rice", + "checked": false + }, + { + "value": "Fair & Clear", + "label": "Fair & Clear", + "checked": false + }, + { + "value": "Saaf & Khaas", + "label": "Saaf & Khaas", + "checked": false + }, + { + "value": "Bake Parlor Big", + "label": "Bake Parlor Big", + "checked": false + }, + { + "value": "Baking Food Items", + "label": "Baking Food Items", + "checked": false + }, + { + "value": "fauji", + "label": "fauji", + "checked": false + }, + { + "value": "Dry Rose", + "label": "Dry Rose", + "checked": false + }, + { + "value": "Boho Decor", + "label": "Boho Decor", + "checked": false + }, + { + "value": "Flying Wooden", + "label": "Flying Wooden", + "checked": false + }, + { + "value": "LED Lights", + "label": "LED Lights", + "checked": false + }, + { + "value": "luxury palace", + "label": "luxury palace", + "checked": false + }, + { + "value": "Golden", + "label": "Golden", + "checked": false + }, + { + "value": "Furniture Bed Set", + "label": "Furniture Bed Set", + "checked": false + }, + { + "value": "Ratttan Outdoor", + "label": "Ratttan Outdoor", + "checked": false + }, + { + "value": "Kitchen Shelf", + "label": "Kitchen Shelf", + "checked": false + }, + { + "value": "Multi Purpose", + "label": "Multi Purpose", + "checked": false + }, + { + "value": "AmnaMart", + "label": "AmnaMart", + "checked": false + }, + { + "value": "Professional Wear", + "label": "Professional Wear", + "checked": false + }, + { + "value": "Soft Cotton", + "label": "Soft Cotton", + "checked": false + }, + { + "value": "Top Sweater", + "label": "Top Sweater", + "checked": false + }, + { + "value": "RED MICKY MOUSE..", + "label": "RED MICKY MOUSE..", + "checked": false + }, + { + "value": "Digital Printed", + "label": "Digital Printed", + "checked": false + }, + { + "value": "Ghazi Fabric", + "label": "Ghazi Fabric", + "checked": false + }, + { + "value": "IELGY", + "label": "IELGY", + "checked": false + }, + { + "value": "IELGY fashion", + "label": "IELGY fashion", + "checked": false + }, + { + "value": "Synthetic Leather", + "label": "Synthetic Leather", + "checked": false + }, + { + "value": "Sandals Flip Flops", + "label": "Sandals Flip Flops", + "checked": false + }, + { + "value": "Maasai Sandals", + "label": "Maasai Sandals", + "checked": false + }, + { + "value": "Arrivals Genuine", + "label": "Arrivals Genuine", + "checked": false + }, + { + "value": "Vintage Apparel", + "label": "Vintage Apparel", + "checked": false + }, + { + "value": "FREE FIRE", + "label": "FREE FIRE", + "checked": false + }, + { + "value": "The Warehouse", + "label": "The Warehouse", + "checked": false + }, + { + "value": "Sneakers", + "label": "Sneakers", + "checked": false + }, + { + "value": "Rubber", + "label": "Rubber", + "checked": false + }, + { + "value": "Naviforce", + "label": "Naviforce", + "checked": false + }, + { + "value": "SKMEI 9117", + "label": "SKMEI 9117", + "checked": false + }, + { + "value": "Strap Skeleton", + "label": "Strap Skeleton", + "checked": false + }, + { + "value": "Stainless", + "label": "Stainless", + "checked": false + }, + { + "value": "Eastern Watches", + "label": "Eastern Watches", + "checked": false + }, + { + "value": "Luxury Digital", + "label": "Luxury Digital", + "checked": false + }, + { + "value": "Watch Pearls", + "label": "Watch Pearls", + "checked": false + }, + { + "value": "Bracelet", + "label": "Bracelet", + "checked": false + }, + { + "value": "LouisWill", + "label": "LouisWill", + "checked": false + }, + { + "value": "Copenhagen Luxe", + "label": "Copenhagen Luxe", + "checked": false + }, + { + "value": "Steal Frame", + "label": "Steal Frame", + "checked": false + }, + { + "value": "Darojay", + "label": "Darojay", + "checked": false + }, + { + "value": "Fashion Jewellery", + "label": "Fashion Jewellery", + "checked": false + }, + { + "value": "Cuff Butterfly", + "label": "Cuff Butterfly", + "checked": false + }, + { + "value": "Designer Sun Glasses", + "label": "Designer Sun Glasses", + "checked": false + }, + { + "value": "mastar watch", + "label": "mastar watch", + "checked": false + }, + { + "value": "Car Aux", + "label": "Car Aux", + "checked": false + }, + { + "value": "W1209 DC12V", + "label": "W1209 DC12V", + "checked": false + }, + { + "value": "TC Reusable", + "label": "TC Reusable", + "checked": false + }, + { + "value": "Neon LED Light", + "label": "Neon LED Light", + "checked": false + }, + { + "value": "METRO 70cc Motorcycle - MR70", + "label": "METRO 70cc Motorcycle MR70", + "checked": false + }, + { + "value": "BRAVE BULL", + "label": "BRAVE BULL", + "checked": false + }, + { + "value": "shock absorber", + "label": "shock absorber", + "checked": false + }, + { + "value": "JIEPOLLY", + "label": "JIEPOLLY", + "checked": false + }, + { + "value": "Xiangle", + "label": "Xiangle", + "checked": false + }, + { + "value": "lightingbrilliance", + "label": "lightingbrilliance", + "checked": false + }, + { + "value": "Ifei Home", + "label": "Ifei Home", + "checked": false + }, + { + "value": "DADAWU", + "label": "DADAWU", + "checked": false + }, + { + "value": "YIOSI", + "label": "YIOSI", + "checked": false + } + ], + "categories": [ + { + "value": "smartphones", + "label": "smartphones", + "checked": false + }, + { + "value": "laptops", + "label": "laptops", + "checked": false + }, + { + "value": "fragrances", + "label": "fragrances", + "checked": false + }, + { + "value": "skincare", + "label": "skincare", + "checked": false + }, + { + "value": "groceries", + "label": "groceries", + "checked": false + }, + { + "value": "home-decoration", + "label": "home decoration", + "checked": false + }, + { + "value": "furniture", + "label": "furniture", + "checked": false + }, + { + "value": "tops", + "label": "tops", + "checked": false + }, + { + "value": "womens-dresses", + "label": "womens dresses", + "checked": false + }, + { + "value": "womens-shoes", + "label": "womens shoes", + "checked": false + }, + { + "value": "mens-shirts", + "label": "mens shirts", + "checked": false + }, + { + "value": "mens-shoes", + "label": "mens shoes", + "checked": false + }, + { + "value": "mens-watches", + "label": "mens watches", + "checked": false + }, + { + "value": "womens-watches", + "label": "womens watches", + "checked": false + }, + { + "value": "womens-bags", + "label": "womens bags", + "checked": false + }, + { + "value": "womens-jewellery", + "label": "womens jewellery", + "checked": false + }, + { + "value": "sunglasses", + "label": "sunglasses", + "checked": false + }, + { + "value": "automotive", + "label": "automotive", + "checked": false + }, + { + "value": "motorcycle", + "label": "motorcycle", + "checked": false + }, + { + "value": "lighting", + "label": "lighting", + "checked": false + } + ], + "users": [ + { + "email": "test@gmail.com", + "password": "Qwerty123", + "role": "user", + "addresses": [ + { + "name": "Abhishek Rathore", + "email": "test@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Banaglore", + "state": "Karnataka", + "pinCode": "560034" + }, + { + "name": "Jack Morris", + "email": "jack@gmail.com", + "city": "Delhi", + "state": "Delhi", + "pinCode": "110006", + "phone": "12312331232", + "street": "12th cross" + } + ], + "id": 1 + }, + { + "email": "demo@gmail.com", + "password": "Qwerty123", + "role": "user", + "addresses": [ + { + "name": "Demo user", + "email": "demo@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Mumbai", + "state": "Maharastra", + "pinCode": "220001" + } + ], + "id": 2 + }, + { + "email": "admin@gmail.com", + "password": "Qwerty123", + "addresses": [], + "role": "admin", + "id": 3 + } + ], + "cart": [ + { + "title": "Huawei P30", + "description": "Huawei’s re-badged P30 Pro New Edition was officially unveiled yesterday in Germany and now the device has made its way to the UK.", + "price": 499, + "discountPercentage": 10.58, + "rating": 4.09, + "stock": 32, + "brand": "Huawei", + "category": "smartphones", + "thumbnail": "https://i.dummyjson.com/data/products/5/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/5/1.jpg", + "https://i.dummyjson.com/data/products/5/2.jpg", + "https://i.dummyjson.com/data/products/5/3.jpg" + ], + "productId": 5, + "quantity": 1, + "user": 2, + "id": 1 + } + ], + "orders": [ + { + "items": [ + { + "title": "iPhone X", + "description": "SIM-Free, Model A19211 6.5-inch Super Retina HD display with OLED technology A12 Bionic chip with ...", + "price": 899, + "discountPercentage": 17.94, + "rating": 4.44, + "stock": 34, + "brand": "Apple", + "category": "smartphones", + "thumbnail": "https://i.dummyjson.com/data/products/2/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/2/1.jpg", + "https://i.dummyjson.com/data/products/2/2.jpg", + "https://i.dummyjson.com/data/products/2/3.jpg", + "https://i.dummyjson.com/data/products/2/thumbnail.jpg" + ], + "quantity": 2, + "user": 1, + "id": 6 + } + ], + "totalAmount": 1798, + "totalItems": 2, + "user": { + "email": "test@gmail.com", + "password": "Qwerty123", + "addresses": [ + { + "name": "Abhishek R", + "email": "test@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Banaglore", + "state": "Karnataka", + "pinCode": "560034" + }, + { + "name": "Abhishek R", + "email": "test@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Banaglore", + "state": "Karnataka", + "pinCode": "560034" + } + ], + "id": 1 + }, + "paymentMethod": "cash", + "selectedAddress": { + "name": "Abhishek R", + "email": "test@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Banaglore", + "state": "Karnataka", + "pinCode": "560034" + }, + "status": "dispatched", + "id": 1 + }, + { + "items": [ + { + "title": "iPhone X", + "description": "SIM-Free, Model A19211 6.5-inch Super Retina HD display with OLED technology A12 Bionic chip with ...", + "price": 899, + "discountPercentage": 17.94, + "rating": 4.44, + "stock": 34, + "brand": "Apple", + "category": "smartphones", + "thumbnail": "https://i.dummyjson.com/data/products/2/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/2/1.jpg", + "https://i.dummyjson.com/data/products/2/2.jpg", + "https://i.dummyjson.com/data/products/2/3.jpg", + "https://i.dummyjson.com/data/products/2/thumbnail.jpg" + ], + "quantity": 2, + "user": 1, + "id": 6 + } + ], + "totalAmount": 1798, + "totalItems": 2, + "user": { + "email": "test@gmail.com", + "password": "Qwerty123", + "addresses": [ + { + "name": "Abhishek R", + "email": "test@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Banaglore", + "state": "Karnataka", + "pinCode": "560034" + }, + { + "name": "Abhishek R", + "email": "test@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Banaglore", + "state": "Karnataka", + "pinCode": "560034" + } + ], + "id": 1 + }, + "paymentMethod": "cash", + "selectedAddress": { + "name": "Abhishek R", + "email": "test@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Banaglore", + "state": "Karnataka", + "pinCode": "560034" + }, + "status": "delivered", + "id": 2 + }, + { + "items": [ + { + "id": 3, + "title": "Samsung Universe 9", + "description": "Samsung's new variant which goes beyond Galaxy to the Universe", + "price": 1249, + "discountPercentage": 15.46, + "rating": 4.09, + "stock": 36, + "brand": "Samsung", + "category": "smartphones", + "thumbnail": "https://i.dummyjson.com/data/products/3/thumbnail.jpg", + "images": ["https://i.dummyjson.com/data/products/3/1.jpg"], + "quantity": 2, + "user": 2 + }, + { + "id": 5, + "title": "Huawei P30", + "description": "Huawei’s re-badged P30 Pro New Edition was officially unveiled yesterday in Germany and now the device has made its way to the UK.", + "price": 499, + "discountPercentage": 10.58, + "rating": 4.09, + "stock": 32, + "brand": "Huawei", + "category": "smartphones", + "thumbnail": "https://i.dummyjson.com/data/products/5/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/5/1.jpg", + "https://i.dummyjson.com/data/products/5/2.jpg", + "https://i.dummyjson.com/data/products/5/3.jpg" + ], + "quantity": 1, + "user": 2 + } + ], + "totalAmount": 2997, + "totalItems": 3, + "user": { + "email": "demo@gmail.com", + "password": "Qwerty123", + "addresses": [ + { + "name": "Demo user", + "email": "demo@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Mumbai", + "state": "Maharastra", + "pinCode": "220001" + } + ], + "id": 2 + }, + "paymentMethod": "card", + "selectedAddress": { + "name": "Demo user", + "email": "demo@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Mumbai", + "state": "Maharastra", + "pinCode": "220001" + }, + "status": "cancelled", + "id": 3 + }, + { + "items": [ + { + "title": "iPhone 9", + "description": "An apple mobile which is nothing like apple", + "price": 549, + "discountPercentage": 12.96, + "rating": 4.69, + "stock": 94, + "brand": "Apple", + "category": "smartphones", + "thumbnail": "https://i.dummyjson.com/data/products/1/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/1/1.jpg", + "https://i.dummyjson.com/data/products/1/2.jpg", + "https://i.dummyjson.com/data/products/1/3.jpg", + "https://i.dummyjson.com/data/products/1/4.jpg", + "https://i.dummyjson.com/data/products/1/thumbnail.jpg" + ], + "quantity": 2, + "user": 1, + "id": 1 + } + ], + "totalAmount": 1098, + "totalItems": 2, + "user": { + "email": "test@gmail.com", + "password": "Qwerty123", + "addresses": [ + { + "name": "Abhishek R", + "email": "test@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Banaglore", + "state": "Karnataka", + "pinCode": "560034" + }, + { + "name": "Abhishek R", + "email": "test@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Banaglore", + "state": "Karnataka", + "pinCode": "560034" + } + ], + "id": 1 + }, + "paymentMethod": "cash", + "selectedAddress": { + "name": "Abhishek R", + "email": "test@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Banaglore", + "state": "Karnataka", + "pinCode": "560034" + }, + "status": "pending", + "id": 4 + }, + { + "items": [ + { + "title": "iPhone X", + "description": "SIM-Free, Model A19211 6.5-inch Super Retina HD display with OLED technology A12 Bionic chip with ...", + "price": 899, + "discountPercentage": 17.94, + "rating": 4.44, + "stock": 34, + "brand": "Apple", + "category": "smartphones", + "thumbnail": "https://i.dummyjson.com/data/products/2/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/2/1.jpg", + "https://i.dummyjson.com/data/products/2/2.jpg", + "https://i.dummyjson.com/data/products/2/3.jpg", + "https://i.dummyjson.com/data/products/2/thumbnail.jpg" + ], + "quantity": 1, + "user": 1, + "id": 1 + } + ], + "totalAmount": 899, + "totalItems": 1, + "user": { + "email": "test@gmail.com", + "password": "Qwerty123", + "addresses": [ + { + "name": "Abhishek Rathore", + "email": "test@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Banaglore", + "state": "Karnataka", + "pinCode": "560034" + }, + { + "name": "Jack Morris", + "email": "jack@gmail.com", + "city": "Delhi", + "state": "Delhi", + "pinCode": "110006", + "phone": "12312331232", + "street": "12th cross" + } + ], + "id": 1 + }, + "paymentMethod": "cash", + "selectedAddress": { + "name": "Abhishek Rathore", + "email": "test@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Banaglore", + "state": "Karnataka", + "pinCode": "560034" + }, + "status": "pending", + "id": 5 + }, + { + "items": [ + { + "title": "iPhone X", + "description": "SIM-Free, Model A19211 6.5-inch Super Retina HD display with OLED technology A12 Bionic chip with ...", + "price": 1111, + "discountPercentage": 17.94, + "rating": 0, + "stock": 34, + "brand": "Apple", + "category": "smartphones", + "thumbnail": "https://i.dummyjson.com/data/products/2/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/2/1.jpg", + "https://i.dummyjson.com/data/products/2/2.jpg", + "https://i.dummyjson.com/data/products/2/3.jpg", + "https://i.dummyjson.com/data/products/2/thumbnail.jpg" + ], + "quantity": 2, + "user": 1, + "id": 1 + } + ], + "totalAmount": 1824, + "totalItems": 2, + "user": { + "email": "test@gmail.com", + "password": "Qwerty123", + "role": "user", + "addresses": [ + { + "name": "Abhishek Rathore", + "email": "test@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Banaglore", + "state": "Karnataka", + "pinCode": "560034" + }, + { + "name": "Jack Morris", + "email": "jack@gmail.com", + "city": "Delhi", + "state": "Delhi", + "pinCode": "110006", + "phone": "12312331232", + "street": "12th cross" + } + ], + "id": 1 + }, + "paymentMethod": "cash", + "selectedAddress": { + "name": "Jack Morris", + "email": "jack@gmail.com", + "city": "Delhi", + "state": "Delhi", + "pinCode": "110006", + "phone": "12312331232", + "street": "12th cross" + }, + "status": "pending", + "id": 6 + }, + { + "items": [ + { + "title": "Samsung Universe 9", + "description": "Samsung's new variant which goes beyond Galaxy to the Universe", + "price": 1249, + "discountPercentage": 15.46, + "rating": 4.09, + "stock": 36, + "brand": "Samsung", + "category": "smartphones", + "thumbnail": "https://i.dummyjson.com/data/products/3/thumbnail.jpg", + "images": ["https://i.dummyjson.com/data/products/3/1.jpg"], + "quantity": 1, + "user": 2, + "id": 1 + }, + { + "title": "Huawei P30", + "description": "Huawei’s re-badged P30 Pro New Edition was officially unveiled yesterday in Germany and now the device has made its way to the UK.", + "price": 499, + "discountPercentage": 10.58, + "rating": 4.09, + "stock": 32, + "brand": "Huawei", + "category": "smartphones", + "thumbnail": "https://i.dummyjson.com/data/products/5/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/5/1.jpg", + "https://i.dummyjson.com/data/products/5/2.jpg", + "https://i.dummyjson.com/data/products/5/3.jpg" + ], + "quantity": 1, + "user": 2, + "id": 2 + } + ], + "totalAmount": 1502, + "totalItems": 2, + "user": { + "email": "demo@gmail.com", + "password": "Qwerty123", + "role": "user", + "addresses": [ + { + "name": "Demo user", + "email": "demo@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Mumbai", + "state": "Maharastra", + "pinCode": "220001" + } + ], + "id": 2 + }, + "paymentMethod": "cash", + "selectedAddress": { + "name": "Demo user", + "email": "demo@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Mumbai", + "state": "Maharastra", + "pinCode": "220001" + }, + "status": "pending", + "id": 7 + }, + { + "items": [ + { + "title": "Samsung Universe 9", + "description": "Samsung's new variant which goes beyond Galaxy to the Universe", + "price": 1249, + "discountPercentage": 15.46, + "rating": 4.09, + "stock": 36, + "brand": "Samsung", + "category": "smartphones", + "thumbnail": "https://i.dummyjson.com/data/products/3/thumbnail.jpg", + "images": ["https://i.dummyjson.com/data/products/3/1.jpg"], + "quantity": 1, + "user": 2, + "id": 1 + }, + { + "title": "Huawei P30", + "description": "Huawei’s re-badged P30 Pro New Edition was officially unveiled yesterday in Germany and now the device has made its way to the UK.", + "price": 499, + "discountPercentage": 10.58, + "rating": 4.09, + "stock": 32, + "brand": "Huawei", + "category": "smartphones", + "thumbnail": "https://i.dummyjson.com/data/products/5/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/5/1.jpg", + "https://i.dummyjson.com/data/products/5/2.jpg", + "https://i.dummyjson.com/data/products/5/3.jpg" + ], + "quantity": 1, + "user": 2, + "id": 2 + } + ], + "totalAmount": 1502, + "totalItems": 2, + "user": { + "email": "demo@gmail.com", + "password": "Qwerty123", + "role": "user", + "addresses": [ + { + "name": "Demo user", + "email": "demo@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Mumbai", + "state": "Maharastra", + "pinCode": "220001" + } + ], + "id": 2 + }, + "paymentMethod": "cash", + "selectedAddress": { + "name": "Demo user", + "email": "demo@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Mumbai", + "state": "Maharastra", + "pinCode": "220001" + }, + "status": "pending", + "id": 8 + }, + { + "items": [ + { + "title": "MacBook Pro", + "description": "MacBook Pro 2021 with mini-LED display may launch between September, November", + "price": 1999, + "discountPercentage": 11.02, + "rating": 0, + "stock": 83, + "brand": "Apple", + "category": "laptops", + "thumbnail": "https://i.dummyjson.com/data/products/6/thumbnail.png", + "images": [ + "https://i.dummyjson.com/data/products/6/1.png", + "https://i.dummyjson.com/data/products/6/2.jpg", + "https://i.dummyjson.com/data/products/6/3.png", + "https://i.dummyjson.com/data/products/6/thumbnail.png" + ], + "quantity": 1, + "user": 2, + "id": 1 + } + ], + "totalAmount": 1779, + "totalItems": 1, + "user": { + "email": "demo@gmail.com", + "password": "Qwerty123", + "role": "user", + "addresses": [ + { + "name": "Demo user", + "email": "demo@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Mumbai", + "state": "Maharastra", + "pinCode": "220001" + } + ], + "id": 2 + }, + "paymentMethod": "card", + "selectedAddress": { + "name": "Demo user", + "email": "demo@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Mumbai", + "state": "Maharastra", + "pinCode": "220001" + }, + "status": "pending", + "id": 9 + }, + { + "items": [ + { + "title": "Microsoft Surface Laptop 4", + "description": "Style and speed. Stand out on HD video calls backed by Studio Mics. Capture ideas on the vibrant touchscreen.", + "price": 1499, + "discountPercentage": 10.23, + "rating": 4.43, + "stock": 68, + "brand": "Microsoft Surface", + "category": "laptops", + "thumbnail": "https://i.dummyjson.com/data/products/8/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/8/1.jpg", + "https://i.dummyjson.com/data/products/8/2.jpg", + "https://i.dummyjson.com/data/products/8/3.jpg", + "https://i.dummyjson.com/data/products/8/4.jpg", + "https://i.dummyjson.com/data/products/8/thumbnail.jpg" + ], + "quantity": 1, + "user": 2, + "id": 1 + } + ], + "totalAmount": 1346, + "totalItems": 1, + "user": { + "email": "demo@gmail.com", + "password": "Qwerty123", + "role": "user", + "addresses": [ + { + "name": "Demo user", + "email": "demo@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Mumbai", + "state": "Maharastra", + "pinCode": "220001" + } + ], + "id": 2 + }, + "paymentMethod": "card", + "selectedAddress": { + "name": "Demo user", + "email": "demo@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Mumbai", + "state": "Maharastra", + "pinCode": "220001" + }, + "status": "pending", + "id": 10 + }, + { + "items": [ + { + "title": "Microsoft Surface Laptop 4", + "description": "Style and speed. Stand out on HD video calls backed by Studio Mics. Capture ideas on the vibrant touchscreen.", + "price": 1499, + "discountPercentage": 10.23, + "rating": 4.43, + "stock": 68, + "brand": "Microsoft Surface", + "category": "laptops", + "thumbnail": "https://i.dummyjson.com/data/products/8/thumbnail.jpg", + "images": [ + "https://i.dummyjson.com/data/products/8/1.jpg", + "https://i.dummyjson.com/data/products/8/2.jpg", + "https://i.dummyjson.com/data/products/8/3.jpg", + "https://i.dummyjson.com/data/products/8/4.jpg", + "https://i.dummyjson.com/data/products/8/thumbnail.jpg" + ], + "quantity": 2, + "user": 2, + "id": 1 + } + ], + "totalAmount": 2692, + "totalItems": 2, + "user": { + "email": "demo@gmail.com", + "password": "Qwerty123", + "role": "user", + "addresses": [ + { + "name": "Demo user", + "email": "demo@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Mumbai", + "state": "Maharastra", + "pinCode": "220001" + } + ], + "id": 2 + }, + "paymentMethod": "cash", + "selectedAddress": { + "name": "Demo user", + "email": "demo@gmail.com", + "phone": "1234567788", + "street": "11th Main", + "city": "Mumbai", + "state": "Maharastra", + "pinCode": "220001" + }, + "status": "pending", + "id": 11 + } + ] +} From 598bfa5c4b18e9d73747417e49694907fdeb95c4 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Wed, 12 Jun 2024 10:14:00 +0530 Subject: [PATCH 02/47] completed till fetching products from dummy api (from folder-static) --- src/features/navbar/Navbar.js | 58 +- .../product-list/components/ProductList.js | 466 ---- .../components/ProductDetail.js | 0 .../product/components/ProductList.js | 2222 +++++++++++++++++ .../{product-list => product}/productAPI.js | 0 .../{product-list => product}/productSlice.js | 0 src/pages/Home.js | 18 +- src/pages/ProductDetailPage.js | 18 +- 8 files changed, 2270 insertions(+), 512 deletions(-) delete mode 100644 src/features/product-list/components/ProductList.js rename src/features/{product-list => product}/components/ProductDetail.js (100%) create mode 100644 src/features/product/components/ProductList.js rename src/features/{product-list => product}/productAPI.js (100%) rename src/features/{product-list => product}/productSlice.js (100%) diff --git a/src/features/navbar/Navbar.js b/src/features/navbar/Navbar.js index e19db81..f265d79 100644 --- a/src/features/navbar/Navbar.js +++ b/src/features/navbar/Navbar.js @@ -1,30 +1,30 @@ -import { Fragment } from 'react'; -import { Disclosure, Menu, Transition } from '@headlessui/react'; +import { Fragment } from "react"; +import { Disclosure, Menu, Transition } from "@headlessui/react"; import { Bars3Icon, ShoppingCartIcon, XMarkIcon, -} from '@heroicons/react/24/outline'; -import { Link } from 'react-router-dom'; +} from "@heroicons/react/24/outline"; +import { Link } from "react-router-dom"; const user = { - name: 'Tom Cook', - email: 'tom@example.com', + name: "Tom Cook", + email: "tom@example.com", imageUrl: - 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80', + "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80", }; const navigation = [ - { name: 'Dashboard', href: '#', current: true }, - { name: 'Team', href: '#', current: false }, + { name: "Dashboard", href: "#", current: true }, + { name: "Team", href: "#", current: false }, ]; const userNavigation = [ - { name: 'Your Profile', href: '#' }, - { name: 'Settings', href: '#' }, - { name: 'Sign out', href: '#' }, + { name: "Your Profile", href: "#" }, + { name: "Settings", href: "#" }, + { name: "Sign out", href: "#" }, ]; function classNames(...classes) { - return classes.filter(Boolean).join(' '); + return classes.filter(Boolean).join(" "); } function NavBar({ children }) { @@ -38,11 +38,13 @@ function NavBar({ children }) { <div className="flex h-16 items-center justify-between"> <div className="flex items-center"> <div className="flex-shrink-0"> - <img - className="h-8 w-8" - src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=500" - alt="Your Company" - /> + <Link to="/"> + <img + className="h-8 w-8" + src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=500" + alt="Your Company" + /> + </Link> </div> <div className="hidden md:block"> <div className="ml-10 flex items-baseline space-x-4"> @@ -52,11 +54,11 @@ function NavBar({ children }) { href={item.href} className={classNames( item.current - ? 'bg-gray-900 text-white' - : 'text-gray-300 hover:bg-gray-700 hover:text-white', - 'rounded-md px-3 py-2 text-sm font-medium' + ? "bg-gray-900 text-white" + : "text-gray-300 hover:bg-gray-700 hover:text-white", + "rounded-md px-3 py-2 text-sm font-medium" )} - aria-current={item.current ? 'page' : undefined} + aria-current={item.current ? "page" : undefined} > {item.name} </a> @@ -110,8 +112,8 @@ function NavBar({ children }) { <a href={item.href} className={classNames( - active ? 'bg-gray-100' : '', - 'block px-4 py-2 text-sm text-gray-700' + active ? "bg-gray-100" : "", + "block px-4 py-2 text-sm text-gray-700" )} > {item.name} @@ -153,11 +155,11 @@ function NavBar({ children }) { href={item.href} className={classNames( item.current - ? 'bg-gray-900 text-white' - : 'text-gray-300 hover:bg-gray-700 hover:text-white', - 'block rounded-md px-3 py-2 text-base font-medium' + ? "bg-gray-900 text-white" + : "text-gray-300 hover:bg-gray-700 hover:text-white", + "block rounded-md px-3 py-2 text-base font-medium" )} - aria-current={item.current ? 'page' : undefined} + aria-current={item.current ? "page" : undefined} > {item.name} </Disclosure.Button> diff --git a/src/features/product-list/components/ProductList.js b/src/features/product-list/components/ProductList.js deleted file mode 100644 index a35ec77..0000000 --- a/src/features/product-list/components/ProductList.js +++ /dev/null @@ -1,466 +0,0 @@ -import React, { useState, Fragment } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; -import { increment, incrementAsync, selectCount } from '../productSlice'; -import { Dialog, Disclosure, Menu, Transition } from '@headlessui/react'; -import { XMarkIcon } from '@heroicons/react/24/outline'; -import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/20/solid' -import { Link } from 'react-router-dom'; -import { - ChevronDownIcon, - FunnelIcon, - MinusIcon, - PlusIcon, - Squares2X2Icon, -} from '@heroicons/react/20/solid'; - -const sortOptions = [ - { name: 'Most Popular', href: '#', current: true }, - { name: 'Best Rating', href: '#', current: false }, - { name: 'Newest', href: '#', current: false }, - { name: 'Price: Low to High', href: '#', current: false }, - { name: 'Price: High to Low', href: '#', current: false }, -]; - -const filters = [ - { - id: 'color', - name: 'Color', - options: [ - { value: 'white', label: 'White', checked: false }, - { value: 'beige', label: 'Beige', checked: false }, - { value: 'blue', label: 'Blue', checked: true }, - { value: 'brown', label: 'Brown', checked: false }, - { value: 'green', label: 'Green', checked: false }, - { value: 'purple', label: 'Purple', checked: false }, - ], - }, - { - id: 'category', - name: 'Category', - options: [ - { value: 'new-arrivals', label: 'New Arrivals', checked: false }, - { value: 'sale', label: 'Sale', checked: false }, - { value: 'travel', label: 'Travel', checked: true }, - { value: 'organization', label: 'Organization', checked: false }, - { value: 'accessories', label: 'Accessories', checked: false }, - ], - }, - { - id: 'size', - name: 'Size', - options: [ - { value: '2l', label: '2L', checked: false }, - { value: '6l', label: '6L', checked: false }, - { value: '12l', label: '12L', checked: false }, - { value: '18l', label: '18L', checked: false }, - { value: '20l', label: '20L', checked: false }, - { value: '40l', label: '40L', checked: true }, - ], - }, -]; - -function classNames(...classes) { - return classes.filter(Boolean).join(' '); -} - -const products = [ - { - id: 1, - name: 'Basic Tee', - href: '#', - imageSrc: - 'https://tailwindui.com/img/ecommerce-images/product-page-01-related-product-01.jpg', - imageAlt: "Front of men's Basic Tee in black.", - price: '$35', - color: 'Black', - }, - { - id: 2, - name: 'Basic Tee', - href: '#', - imageSrc: - 'https://tailwindui.com/img/ecommerce-images/product-page-01-related-product-01.jpg', - imageAlt: "Front of men's Basic Tee in black.", - price: '$35', - color: 'Black', - }, - { - id: 3, - name: 'Basic Tee', - href: '#', - imageSrc: - 'https://tailwindui.com/img/ecommerce-images/product-page-01-related-product-01.jpg', - imageAlt: "Front of men's Basic Tee in black.", - price: '$35', - color: 'Black', - }, -]; - -export default function ProductList() { - const count = useSelector(selectCount); - const dispatch = useDispatch(); - const [mobileFiltersOpen, setMobileFiltersOpen] = useState(false); - - return ( - <div className="bg-white"> - <div> - {/* Mobile filter dialog */} - <Transition.Root show={mobileFiltersOpen} as={Fragment}> - <Dialog - as="div" - className="relative z-40 lg:hidden" - onClose={setMobileFiltersOpen} - > - <Transition.Child - as={Fragment} - enter="transition-opacity ease-linear duration-300" - enterFrom="opacity-0" - enterTo="opacity-100" - leave="transition-opacity ease-linear duration-300" - leaveFrom="opacity-100" - leaveTo="opacity-0" - > - <div className="fixed inset-0 bg-black bg-opacity-25" /> - </Transition.Child> - - <div className="fixed inset-0 z-40 flex"> - <Transition.Child - as={Fragment} - enter="transition ease-in-out duration-300 transform" - enterFrom="translate-x-full" - enterTo="translate-x-0" - leave="transition ease-in-out duration-300 transform" - leaveFrom="translate-x-0" - leaveTo="translate-x-full" - > - <Dialog.Panel className="relative ml-auto flex h-full w-full max-w-xs flex-col overflow-y-auto bg-white py-4 pb-12 shadow-xl"> - <div className="flex items-center justify-between px-4"> - <h2 className="text-lg font-medium text-gray-900"> - Filters - </h2> - <button - type="button" - className="-mr-2 flex h-10 w-10 items-center justify-center rounded-md bg-white p-2 text-gray-400" - onClick={() => setMobileFiltersOpen(false)} - > - <span className="sr-only">Close menu</span> - <XMarkIcon className="h-6 w-6" aria-hidden="true" /> - </button> - </div> - - {/* Filters */} - <form className="mt-4 border-t border-gray-200"> - {filters.map((section) => ( - <Disclosure - as="div" - key={section.id} - className="border-t border-gray-200 px-4 py-6" - > - {({ open }) => ( - <> - <h3 className="-mx-2 -my-3 flow-root"> - <Disclosure.Button className="flex w-full items-center justify-between bg-white px-2 py-3 text-gray-400 hover:text-gray-500"> - <span className="font-medium text-gray-900"> - {section.name} - </span> - <span className="ml-6 flex items-center"> - {open ? ( - <MinusIcon - className="h-5 w-5" - aria-hidden="true" - /> - ) : ( - <PlusIcon - className="h-5 w-5" - aria-hidden="true" - /> - )} - </span> - </Disclosure.Button> - </h3> - <Disclosure.Panel className="pt-6"> - <div className="space-y-6"> - {section.options.map((option, optionIdx) => ( - <div - key={option.value} - className="flex items-center" - > - <input - id={`filter-mobile-${section.id}-${optionIdx}`} - name={`${section.id}[]`} - defaultValue={option.value} - type="checkbox" - defaultChecked={option.checked} - className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500" - /> - <label - htmlFor={`filter-mobile-${section.id}-${optionIdx}`} - className="ml-3 min-w-0 flex-1 text-gray-500" - > - {option.label} - </label> - </div> - ))} - </div> - </Disclosure.Panel> - </> - )} - </Disclosure> - ))} - </form> - </Dialog.Panel> - </Transition.Child> - </div> - </Dialog> - </Transition.Root> - - <main className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"> - <div className="flex items-baseline justify-between border-b border-gray-200 pb-6 pt-24"> - <h1 className="text-4xl font-bold tracking-tight text-gray-900"> - All Products - </h1> - - <div className="flex items-center"> - <Menu as="div" className="relative inline-block text-left"> - <div> - <Menu.Button className="group inline-flex justify-center text-sm font-medium text-gray-700 hover:text-gray-900"> - Sort - <ChevronDownIcon - className="-mr-1 ml-1 h-5 w-5 flex-shrink-0 text-gray-400 group-hover:text-gray-500" - aria-hidden="true" - /> - </Menu.Button> - </div> - - <Transition - as={Fragment} - enter="transition ease-out duration-100" - enterFrom="transform opacity-0 scale-95" - enterTo="transform opacity-100 scale-100" - leave="transition ease-in duration-75" - leaveFrom="transform opacity-100 scale-100" - leaveTo="transform opacity-0 scale-95" - > - <Menu.Items className="absolute right-0 z-10 mt-2 w-40 origin-top-right rounded-md bg-white shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none"> - <div className="py-1"> - {sortOptions.map((option) => ( - <Menu.Item key={option.name}> - {({ active }) => ( - <a - href={option.href} - className={classNames( - option.current - ? 'font-medium text-gray-900' - : 'text-gray-500', - active ? 'bg-gray-100' : '', - 'block px-4 py-2 text-sm' - )} - > - {option.name} - </a> - )} - </Menu.Item> - ))} - </div> - </Menu.Items> - </Transition> - </Menu> - - <button - type="button" - className="-m-2 ml-5 p-2 text-gray-400 hover:text-gray-500 sm:ml-7" - > - <span className="sr-only">View grid</span> - <Squares2X2Icon className="h-5 w-5" aria-hidden="true" /> - </button> - <button - type="button" - className="-m-2 ml-4 p-2 text-gray-400 hover:text-gray-500 sm:ml-6 lg:hidden" - onClick={() => setMobileFiltersOpen(true)} - > - <span className="sr-only">Filters</span> - <FunnelIcon className="h-5 w-5" aria-hidden="true" /> - </button> - </div> - </div> - - <section aria-labelledby="products-heading" className="pb-24 pt-6"> - <h2 id="products-heading" className="sr-only"> - Products - </h2> - - <div className="grid grid-cols-1 gap-x-8 gap-y-10 lg:grid-cols-4"> - {/* Filters */} - <form className="hidden lg:block"> - {filters.map((section) => ( - <Disclosure - as="div" - key={section.id} - className="border-b border-gray-200 py-6" - > - {({ open }) => ( - <> - <h3 className="-my-3 flow-root"> - <Disclosure.Button className="flex w-full items-center justify-between bg-white py-3 text-sm text-gray-400 hover:text-gray-500"> - <span className="font-medium text-gray-900"> - {section.name} - </span> - <span className="ml-6 flex items-center"> - {open ? ( - <MinusIcon - className="h-5 w-5" - aria-hidden="true" - /> - ) : ( - <PlusIcon - className="h-5 w-5" - aria-hidden="true" - /> - )} - </span> - </Disclosure.Button> - </h3> - <Disclosure.Panel className="pt-6"> - <div className="space-y-4"> - {section.options.map((option, optionIdx) => ( - <div - key={option.value} - className="flex items-center" - > - <input - id={`filter-${section.id}-${optionIdx}`} - name={`${section.id}[]`} - defaultValue={option.value} - type="checkbox" - defaultChecked={option.checked} - className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500" - /> - <label - htmlFor={`filter-${section.id}-${optionIdx}`} - className="ml-3 text-sm text-gray-600" - > - {option.label} - </label> - </div> - ))} - </div> - </Disclosure.Panel> - </> - )} - </Disclosure> - ))} - </form> - - {/* Product grid */} - <div className="lg:col-span-3"> - {/* This is our products list */} - <div className="bg-white"> - <div className="mx-auto max-w-2xl px-4 py-0 sm:px-6 sm:py-0 lg:max-w-7xl lg:px-8"> - <div className="mt-6 grid grid-cols-1 gap-x-6 gap-y-10 sm:grid-cols-2 lg:grid-cols-3 xl:gap-x-8"> - {products.map((product) => ( - <Link to="/product-detail"> - <div key={product.id} className="group relative"> - <div className="min-h-80 aspect-h-1 aspect-w-1 w-full overflow-hidden rounded-md bg-gray-200 lg:aspect-none group-hover:opacity-75 lg:h-80"> - <img - src={product.imageSrc} - alt={product.imageAlt} - className="h-full w-full object-cover object-center lg:h-full lg:w-full" - /> - </div> - <div className="mt-4 flex justify-between"> - <div> - <h3 className="text-sm text-gray-700"> - <a href={product.href}> - <span - aria-hidden="true" - className="absolute inset-0" - /> - {product.name} - </a> - </h3> - <p className="mt-1 text-sm text-gray-500"> - {product.color} - </p> - </div> - <p className="text-sm font-medium text-gray-900"> - {product.price} - </p> - </div> - </div> - </Link> - ))} - </div> - </div> - </div> - </div> - {/* Product grid end */} - </div> - </section> - - {/* section of product and filters ends */} - <div className="flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6"> - <div className="flex flex-1 justify-between sm:hidden"> - <a - href="#" - className="relative inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50" - > - Previous - </a> - <a - href="#" - className="relative ml-3 inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50" - > - Next - </a> - </div> - <div className="hidden sm:flex sm:flex-1 sm:items-center sm:justify-between"> - <div> - <p className="text-sm text-gray-700"> - Showing <span className="font-medium">1</span> to{' '} - <span className="font-medium">10</span> of{' '} - <span className="font-medium">97</span> results - </p> - </div> - <div> - <nav - className="isolate inline-flex -space-x-px rounded-md shadow-sm" - aria-label="Pagination" - > - <a - href="#" - className="relative inline-flex items-center rounded-l-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" - > - <span className="sr-only">Previous</span> - <ChevronLeftIcon className="h-5 w-5" aria-hidden="true" /> - </a> - {/* Current: "z-10 bg-indigo-600 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600", Default: "text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:outline-offset-0" */} - <a - href="#" - aria-current="page" - className="relative z-10 inline-flex items-center bg-indigo-600 px-4 py-2 text-sm font-semibold text-white focus:z-20 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" - > - 1 - </a> - <a - href="#" - className="relative inline-flex items-center px-4 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" - > - 2 - </a> - - <a - href="#" - className="relative inline-flex items-center rounded-r-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" - > - <span className="sr-only">Next</span> - <ChevronRightIcon className="h-5 w-5" aria-hidden="true" /> - </a> - </nav> - </div> - </div> - </div> - </main> - </div> - </div> - ); -} diff --git a/src/features/product-list/components/ProductDetail.js b/src/features/product/components/ProductDetail.js similarity index 100% rename from src/features/product-list/components/ProductDetail.js rename to src/features/product/components/ProductDetail.js diff --git a/src/features/product/components/ProductList.js b/src/features/product/components/ProductList.js new file mode 100644 index 0000000..ab7896f --- /dev/null +++ b/src/features/product/components/ProductList.js @@ -0,0 +1,2222 @@ +import React, { useState, Fragment } from "react"; +import { useSelector, useDispatch } from "react-redux"; +import { increment, incrementAsync, selectCount } from "../productSlice"; +import { Dialog, Disclosure, Menu, Transition } from "@headlessui/react"; +import { StarIcon, XMarkIcon } from "@heroicons/react/24/outline"; +import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/20/solid"; +import { Link } from "react-router-dom"; +import { + ChevronDownIcon, + FunnelIcon, + MinusIcon, + PlusIcon, + Squares2X2Icon, +} from "@heroicons/react/20/solid"; + +const sortOptions = [ + { name: "Most Popular", href: "#", current: true }, + { name: "Best Rating", href: "#", current: false }, + { name: "Newest", href: "#", current: false }, + { name: "Price: Low to High", href: "#", current: false }, + { name: "Price: High to Low", href: "#", current: false }, +]; + +const filters = [ + { + id: "color", + name: "Color", + options: [ + { value: "white", label: "White", checked: false }, + { value: "beige", label: "Beige", checked: false }, + { value: "blue", label: "Blue", checked: true }, + { value: "brown", label: "Brown", checked: false }, + { value: "green", label: "Green", checked: false }, + { value: "purple", label: "Purple", checked: false }, + ], + }, + { + id: "category", + name: "Category", + options: [ + { value: "new-arrivals", label: "New Arrivals", checked: false }, + { value: "sale", label: "Sale", checked: false }, + { value: "travel", label: "Travel", checked: true }, + { value: "organization", label: "Organization", checked: false }, + { value: "accessories", label: "Accessories", checked: false }, + ], + }, + { + id: "size", + name: "Size", + options: [ + { value: "2l", label: "2L", checked: false }, + { value: "6l", label: "6L", checked: false }, + { value: "12l", label: "12L", checked: false }, + { value: "18l", label: "18L", checked: false }, + { value: "20l", label: "20L", checked: false }, + { value: "40l", label: "40L", checked: true }, + ], + }, +]; + +function classNames(...classes) { + return classes.filter(Boolean).join(" "); +} + +const products = [ + { + id: 1, + title: "Essence Mascara Lash Princess", + 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, + stock: 5, + tags: ["beauty", "mascara"], + brand: "Essence", + sku: "RCH45Q1A", + weight: 2, + dimensions: { + width: 23.17, + height: 14.43, + depth: 28.01, + }, + warrantyInformation: "1 month warranty", + shippingInformation: "Ships in 1 month", + availabilityStatus: "Low Stock", + reviews: [ + { + rating: 2, + comment: "Very unhappy with my purchase!", + date: "2024-05-23T08:56:21.618Z", + reviewerName: "John Doe", + reviewerEmail: "john.doe@x.dummyjson.com", + }, + { + rating: 2, + comment: "Not as described!", + date: "2024-05-23T08:56:21.618Z", + reviewerName: "Nolan Gonzalez", + reviewerEmail: "nolan.gonzalez@x.dummyjson.com", + }, + { + rating: 5, + comment: "Very satisfied!", + date: "2024-05-23T08:56:21.618Z", + reviewerName: "Scarlett Wright", + reviewerEmail: "scarlett.wright@x.dummyjson.com", + }, + ], + returnPolicy: "30 days return policy", + minimumOrderQuantity: 24, + meta: { + createdAt: "2024-05-23T08:56:21.618Z", + updatedAt: "2024-05-23T08:56:21.618Z", + barcode: "9164035109868", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "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", + }, + { + id: 2, + title: "Eyeshadow Palette with Mirror", + description: + "The Eyeshadow Palette with Mirror offers a versatile range of eyeshadow shades for creating stunning eye looks. With a built-in mirror, it's convenient for on-the-go makeup application.", + category: "beauty", + price: 19.99, + discountPercentage: 5.5, + rating: 3.28, + stock: 44, + tags: ["beauty", "eyeshadow"], + brand: "Glamour Beauty", + sku: "MVCFH27F", + weight: 3, + dimensions: { + width: 12.42, + height: 8.63, + depth: 29.13, + }, + warrantyInformation: "1 year warranty", + shippingInformation: "Ships in 2 weeks", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 4, + comment: "Very satisfied!", + date: "2024-05-23T08:56:21.618Z", + reviewerName: "Liam Garcia", + reviewerEmail: "liam.garcia@x.dummyjson.com", + }, + { + rating: 1, + comment: "Very disappointed!", + date: "2024-05-23T08:56:21.618Z", + reviewerName: "Nora Russell", + reviewerEmail: "nora.russell@x.dummyjson.com", + }, + { + rating: 5, + comment: "Highly impressed!", + date: "2024-05-23T08:56:21.618Z", + reviewerName: "Elena Baker", + reviewerEmail: "elena.baker@x.dummyjson.com", + }, + ], + returnPolicy: "30 days return policy", + minimumOrderQuantity: 32, + meta: { + createdAt: "2024-05-23T08:56:21.618Z", + updatedAt: "2024-05-23T08:56:21.618Z", + barcode: "2817839095220", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/thumbnail.png", + }, + { + id: 3, + 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, + discountPercentage: 18.14, + rating: 3.82, + stock: 59, + tags: ["beauty", "face powder"], + brand: "Velvet Touch", + sku: "9EN8WLT2", + weight: 8, + dimensions: { + width: 24.16, + height: 10.7, + depth: 11.07, + }, + warrantyInformation: "2 year warranty", + shippingInformation: "Ships in 1-2 business days", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Very happy with my purchase!", + date: "2024-05-23T08:56:21.618Z", + reviewerName: "Ethan Thompson", + reviewerEmail: "ethan.thompson@x.dummyjson.com", + }, + { + rating: 4, + comment: "Great value for money!", + date: "2024-05-23T08:56:21.618Z", + reviewerName: "Levi Hicks", + reviewerEmail: "levi.hicks@x.dummyjson.com", + }, + { + rating: 5, + comment: "Highly impressed!", + date: "2024-05-23T08:56:21.618Z", + reviewerName: "Hazel Gardner", + reviewerEmail: "hazel.gardner@x.dummyjson.com", + }, + ], + returnPolicy: "60 days return policy", + minimumOrderQuantity: 25, + meta: { + createdAt: "2024-05-23T08:56:21.618Z", + updatedAt: "2024-05-23T08:56:21.618Z", + barcode: "0516267971277", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png", + }, + { + id: 4, + title: "Red Lipstick", + description: + "The Red Lipstick is a classic and bold choice for adding a pop of color to your lips. With a creamy and pigmented formula, it provides a vibrant and long-lasting finish.", + category: "beauty", + price: 12.99, + discountPercentage: 19.03, + rating: 2.51, + stock: 68, + tags: ["beauty", "lipstick"], + brand: "Chic Cosmetics", + sku: "O5IF1NTA", + weight: 5, + dimensions: { + width: 14.37, + height: 13.94, + depth: 14.6, + }, + warrantyInformation: "Lifetime warranty", + shippingInformation: "Ships in 2 weeks", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Great product!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Leo Rivera", + reviewerEmail: "leo.rivera@x.dummyjson.com", + }, + { + rating: 4, + comment: "Very pleased!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Oscar Powers", + reviewerEmail: "oscar.powers@x.dummyjson.com", + }, + { + rating: 5, + comment: "Very pleased!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Carter Rivera", + reviewerEmail: "carter.rivera@x.dummyjson.com", + }, + ], + returnPolicy: "90 days return policy", + minimumOrderQuantity: 6, + meta: { + createdAt: "2024-05-23T08:56:21.619Z", + updatedAt: "2024-05-23T08:56:21.619Z", + barcode: "9444582199406", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/thumbnail.png", + }, + { + id: 5, + title: "Red Nail Polish", + description: + "The Red Nail Polish offers a rich and glossy red hue for vibrant and polished nails. With a quick-drying formula, it provides a salon-quality finish at home.", + category: "beauty", + price: 8.99, + discountPercentage: 2.46, + rating: 3.91, + stock: 71, + tags: ["beauty", "nail polish"], + brand: "Nail Couture", + sku: "YUIIIP4W", + weight: 9, + dimensions: { + width: 8.11, + height: 10.89, + depth: 29.06, + }, + warrantyInformation: "1 year warranty", + shippingInformation: "Ships in 1 week", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Very pleased!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Leo Rivera", + reviewerEmail: "leo.rivera@x.dummyjson.com", + }, + { + rating: 5, + comment: "Great product!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Evan Reed", + reviewerEmail: "evan.reed@x.dummyjson.com", + }, + { + rating: 4, + comment: "Highly recommended!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Evelyn Sanchez", + reviewerEmail: "evelyn.sanchez@x.dummyjson.com", + }, + ], + returnPolicy: "No return policy", + minimumOrderQuantity: 46, + meta: { + createdAt: "2024-05-23T08:56:21.619Z", + updatedAt: "2024-05-23T08:56:21.619Z", + barcode: "3212847902461", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/beauty/Red%20Nail%20Polish/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/beauty/Red%20Nail%20Polish/thumbnail.png", + }, + { + id: 6, + title: "Calvin Klein CK One", + description: + "CK One by Calvin Klein is a classic unisex fragrance, known for its fresh and clean scent. It's a versatile fragrance suitable for everyday wear.", + category: "fragrances", + price: 49.99, + discountPercentage: 0.32, + rating: 4.85, + stock: 17, + tags: ["fragrances", "perfumes"], + brand: "Calvin Klein", + sku: "DZM2JQZE", + weight: 5, + dimensions: { + width: 11.53, + height: 14.44, + depth: 6.81, + }, + warrantyInformation: "5 year warranty", + shippingInformation: "Ships overnight", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Great value for money!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Sophia Brown", + reviewerEmail: "sophia.brown@x.dummyjson.com", + }, + { + rating: 3, + comment: "Very disappointed!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Madison Collins", + reviewerEmail: "madison.collins@x.dummyjson.com", + }, + { + rating: 1, + comment: "Poor quality!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Maya Reed", + reviewerEmail: "maya.reed@x.dummyjson.com", + }, + ], + returnPolicy: "No return policy", + minimumOrderQuantity: 20, + meta: { + createdAt: "2024-05-23T08:56:21.619Z", + updatedAt: "2024-05-23T08:56:21.619Z", + barcode: "2210136215089", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/fragrances/Calvin%20Klein%20CK%20One/1.png", + "https://cdn.dummyjson.com/products/images/fragrances/Calvin%20Klein%20CK%20One/2.png", + "https://cdn.dummyjson.com/products/images/fragrances/Calvin%20Klein%20CK%20One/3.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/fragrances/Calvin%20Klein%20CK%20One/thumbnail.png", + }, + { + id: 7, + title: "Chanel Coco Noir Eau De", + description: + "Coco Noir by Chanel is an elegant and mysterious fragrance, featuring notes of grapefruit, rose, and sandalwood. Perfect for evening occasions.", + category: "fragrances", + price: 129.99, + discountPercentage: 18.64, + rating: 2.76, + stock: 41, + tags: ["fragrances", "perfumes"], + brand: "Chanel", + sku: "K71HBCGS", + weight: 4, + dimensions: { + width: 21.27, + height: 28, + depth: 11.89, + }, + warrantyInformation: "1 week warranty", + shippingInformation: "Ships in 1 month", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 1, + comment: "Disappointing product!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Lincoln Kelly", + reviewerEmail: "lincoln.kelly@x.dummyjson.com", + }, + { + rating: 4, + comment: "Great product!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Lincoln Kelly", + reviewerEmail: "lincoln.kelly@x.dummyjson.com", + }, + { + rating: 4, + comment: "Excellent quality!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Lucas Allen", + reviewerEmail: "lucas.allen@x.dummyjson.com", + }, + ], + returnPolicy: "60 days return policy", + minimumOrderQuantity: 5, + meta: { + createdAt: "2024-05-23T08:56:21.619Z", + updatedAt: "2024-05-23T08:56:21.619Z", + barcode: "1435582999795", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/fragrances/Chanel%20Coco%20Noir%20Eau%20De/1.png", + "https://cdn.dummyjson.com/products/images/fragrances/Chanel%20Coco%20Noir%20Eau%20De/2.png", + "https://cdn.dummyjson.com/products/images/fragrances/Chanel%20Coco%20Noir%20Eau%20De/3.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/fragrances/Chanel%20Coco%20Noir%20Eau%20De/thumbnail.png", + }, + { + id: 8, + title: "Dior J'adore", + description: + "J'adore by Dior is a luxurious and floral fragrance, known for its blend of ylang-ylang, rose, and jasmine. It embodies femininity and sophistication.", + category: "fragrances", + price: 89.99, + discountPercentage: 17.44, + rating: 3.31, + stock: 91, + tags: ["fragrances", "perfumes"], + brand: "Dior", + sku: "E70NB03B", + weight: 10, + dimensions: { + width: 21.51, + height: 7, + depth: 26.51, + }, + warrantyInformation: "Lifetime warranty", + shippingInformation: "Ships in 2 weeks", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Fast shipping!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Zoe Nicholson", + reviewerEmail: "zoe.nicholson@x.dummyjson.com", + }, + { + rating: 4, + comment: "Excellent quality!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Addison Wright", + reviewerEmail: "addison.wright@x.dummyjson.com", + }, + { + rating: 4, + comment: "Would buy again!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Clara Berry", + reviewerEmail: "clara.berry@x.dummyjson.com", + }, + ], + returnPolicy: "7 days return policy", + minimumOrderQuantity: 8, + meta: { + createdAt: "2024-05-23T08:56:21.619Z", + updatedAt: "2024-05-23T08:56:21.619Z", + barcode: "0887083199279", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/fragrances/Dior%20J'adore/1.png", + "https://cdn.dummyjson.com/products/images/fragrances/Dior%20J'adore/2.png", + "https://cdn.dummyjson.com/products/images/fragrances/Dior%20J'adore/3.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/fragrances/Dior%20J'adore/thumbnail.png", + }, + { + id: 9, + title: "Dolce Shine Eau de", + description: + "Dolce Shine by Dolce & Gabbana is a vibrant and fruity fragrance, featuring notes of mango, jasmine, and blonde woods. It's a joyful and youthful scent.", + category: "fragrances", + price: 69.99, + discountPercentage: 11.47, + rating: 2.68, + stock: 3, + tags: ["fragrances", "perfumes"], + brand: "Dolce & Gabbana", + sku: "1NBFK980", + weight: 5, + dimensions: { + width: 17, + height: 24.57, + depth: 13.3, + }, + warrantyInformation: "5 year warranty", + shippingInformation: "Ships in 1-2 business days", + availabilityStatus: "Low Stock", + reviews: [ + { + rating: 4, + comment: "Very satisfied!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Xavier Wright", + reviewerEmail: "xavier.wright@x.dummyjson.com", + }, + { + rating: 1, + comment: "Poor quality!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Mila Hernandez", + reviewerEmail: "mila.hernandez@x.dummyjson.com", + }, + { + rating: 5, + comment: "Very happy with my purchase!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Sophia Brown", + reviewerEmail: "sophia.brown@x.dummyjson.com", + }, + ], + returnPolicy: "30 days return policy", + minimumOrderQuantity: 9, + meta: { + createdAt: "2024-05-23T08:56:21.619Z", + updatedAt: "2024-05-23T08:56:21.619Z", + barcode: "1939383392674", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/fragrances/Dolce%20Shine%20Eau%20de/1.png", + "https://cdn.dummyjson.com/products/images/fragrances/Dolce%20Shine%20Eau%20de/2.png", + "https://cdn.dummyjson.com/products/images/fragrances/Dolce%20Shine%20Eau%20de/3.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/fragrances/Dolce%20Shine%20Eau%20de/thumbnail.png", + }, + { + id: 10, + title: "Gucci Bloom Eau de", + description: + "Gucci Bloom by Gucci is a floral and captivating fragrance, with notes of tuberose, jasmine, and Rangoon creeper. It's a modern and romantic scent.", + category: "fragrances", + price: 79.99, + discountPercentage: 8.9, + rating: 2.69, + stock: 93, + tags: ["fragrances", "perfumes"], + brand: "Gucci", + sku: "FFKZ6HOF", + weight: 10, + dimensions: { + width: 22.28, + height: 17.81, + depth: 27.18, + }, + warrantyInformation: "No warranty", + shippingInformation: "Ships in 2 weeks", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Great value for money!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Aria Parker", + reviewerEmail: "aria.parker@x.dummyjson.com", + }, + { + rating: 4, + comment: "Excellent quality!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Natalie Harris", + reviewerEmail: "natalie.harris@x.dummyjson.com", + }, + { + rating: 4, + comment: "Fast shipping!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Ava Harris", + reviewerEmail: "ava.harris@x.dummyjson.com", + }, + ], + returnPolicy: "No return policy", + minimumOrderQuantity: 10, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "8232190382069", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/fragrances/Gucci%20Bloom%20Eau%20de/1.png", + "https://cdn.dummyjson.com/products/images/fragrances/Gucci%20Bloom%20Eau%20de/2.png", + "https://cdn.dummyjson.com/products/images/fragrances/Gucci%20Bloom%20Eau%20de/3.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/fragrances/Gucci%20Bloom%20Eau%20de/thumbnail.png", + }, + { + id: 11, + title: "Annibale Colombo Bed", + description: + "The Annibale Colombo Bed is a luxurious and elegant bed frame, crafted with high-quality materials for a comfortable and stylish bedroom.", + category: "furniture", + price: 1899.99, + discountPercentage: 0.29, + rating: 4.14, + stock: 47, + tags: ["furniture", "beds"], + brand: "Annibale Colombo", + sku: "4KMDTZWF", + weight: 3, + dimensions: { + width: 28.75, + height: 26.88, + depth: 24.47, + }, + warrantyInformation: "2 year warranty", + shippingInformation: "Ships overnight", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 4, + comment: "Great value for money!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Julian Newton", + reviewerEmail: "julian.newton@x.dummyjson.com", + }, + { + rating: 5, + comment: "Would buy again!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Madison Collins", + reviewerEmail: "madison.collins@x.dummyjson.com", + }, + { + rating: 4, + comment: "Would buy again!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Clara Berry", + reviewerEmail: "clara.berry@x.dummyjson.com", + }, + ], + returnPolicy: "7 days return policy", + minimumOrderQuantity: 1, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "7113807059215", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Bed/1.png", + "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Bed/2.png", + "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Bed/3.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Bed/thumbnail.png", + }, + { + id: 12, + title: "Annibale Colombo Sofa", + description: + "The Annibale Colombo Sofa is a sophisticated and comfortable seating option, featuring exquisite design and premium upholstery for your living room.", + category: "furniture", + price: 2499.99, + discountPercentage: 18.54, + rating: 3.08, + stock: 16, + tags: ["furniture", "sofas"], + brand: "Annibale Colombo", + sku: "LUU95CQP", + weight: 3, + dimensions: { + width: 20.97, + height: 19.11, + depth: 25.81, + }, + warrantyInformation: "1 month warranty", + shippingInformation: "Ships overnight", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Very satisfied!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Tyler Davis", + reviewerEmail: "tyler.davis@x.dummyjson.com", + }, + { + rating: 5, + comment: "Excellent quality!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Hannah Robinson", + reviewerEmail: "hannah.robinson@x.dummyjson.com", + }, + { + rating: 3, + comment: "Waste of money!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Madison Collins", + reviewerEmail: "madison.collins@x.dummyjson.com", + }, + ], + returnPolicy: "7 days return policy", + minimumOrderQuantity: 1, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "0426785817074", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Sofa/1.png", + "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Sofa/2.png", + "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Sofa/3.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Sofa/thumbnail.png", + }, + { + id: 13, + title: "Bedside Table African Cherry", + description: + "The Bedside Table in African Cherry is a stylish and functional addition to your bedroom, providing convenient storage space and a touch of elegance.", + category: "furniture", + price: 299.99, + discountPercentage: 9.58, + rating: 4.48, + stock: 16, + tags: ["furniture", "bedside tables"], + brand: "Furniture Co.", + sku: "OWPLTZYX", + weight: 10, + dimensions: { + width: 25.43, + height: 20.2, + depth: 24.95, + }, + warrantyInformation: "6 months warranty", + shippingInformation: "Ships in 1-2 business days", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Very happy with my purchase!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "John Doe", + reviewerEmail: "john.doe@x.dummyjson.com", + }, + { + rating: 4, + comment: "Highly recommended!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Avery Carter", + reviewerEmail: "avery.carter@x.dummyjson.com", + }, + { + rating: 4, + comment: "Very pleased!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Evelyn Sanchez", + reviewerEmail: "evelyn.sanchez@x.dummyjson.com", + }, + ], + returnPolicy: "No return policy", + minimumOrderQuantity: 5, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "2913244159666", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/furniture/Bedside%20Table%20African%20Cherry/1.png", + "https://cdn.dummyjson.com/products/images/furniture/Bedside%20Table%20African%20Cherry/2.png", + "https://cdn.dummyjson.com/products/images/furniture/Bedside%20Table%20African%20Cherry/3.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/furniture/Bedside%20Table%20African%20Cherry/thumbnail.png", + }, + { + id: 14, + title: "Knoll Saarinen Executive Conference Chair", + description: + "The Knoll Saarinen Executive Conference Chair is a modern and ergonomic chair, perfect for your office or conference room with its timeless design.", + category: "furniture", + price: 499.99, + discountPercentage: 15.23, + rating: 4.11, + stock: 47, + tags: ["furniture", "office chairs"], + brand: "Knoll", + sku: "RKHVJ4FE", + weight: 3, + dimensions: { + width: 16.59, + height: 21.46, + depth: 29.07, + }, + warrantyInformation: "Lifetime warranty", + shippingInformation: "Ships in 3-5 business days", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 4, + comment: "Very happy with my purchase!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Leah Gutierrez", + reviewerEmail: "leah.gutierrez@x.dummyjson.com", + }, + { + rating: 4, + comment: "Would buy again!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Nolan Gonzalez", + reviewerEmail: "nolan.gonzalez@x.dummyjson.com", + }, + { + rating: 2, + comment: "Waste of money!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Stella Morris", + reviewerEmail: "stella.morris@x.dummyjson.com", + }, + ], + returnPolicy: "30 days return policy", + minimumOrderQuantity: 5, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "0726316339746", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/furniture/Knoll%20Saarinen%20Executive%20Conference%20Chair/1.png", + "https://cdn.dummyjson.com/products/images/furniture/Knoll%20Saarinen%20Executive%20Conference%20Chair/2.png", + "https://cdn.dummyjson.com/products/images/furniture/Knoll%20Saarinen%20Executive%20Conference%20Chair/3.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/furniture/Knoll%20Saarinen%20Executive%20Conference%20Chair/thumbnail.png", + }, + { + id: 15, + title: "Wooden Bathroom Sink With Mirror", + description: + "The Wooden Bathroom Sink with Mirror is a unique and stylish addition to your bathroom, featuring a wooden sink countertop and a matching mirror.", + category: "furniture", + price: 799.99, + discountPercentage: 11.22, + rating: 3.26, + stock: 95, + tags: ["furniture", "bathroom"], + brand: "Bath Trends", + sku: "7OLTIEVO", + weight: 6, + dimensions: { + width: 7.32, + height: 22.64, + depth: 12.37, + }, + warrantyInformation: "6 months warranty", + shippingInformation: "Ships in 3-5 business days", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Highly recommended!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Charlotte Lopez", + reviewerEmail: "charlotte.lopez@x.dummyjson.com", + }, + { + rating: 1, + comment: "Would not recommend!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "William Gonzalez", + reviewerEmail: "william.gonzalez@x.dummyjson.com", + }, + { + rating: 2, + comment: "Not worth the price!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Ava Harrison", + reviewerEmail: "ava.harrison@x.dummyjson.com", + }, + ], + returnPolicy: "7 days return policy", + minimumOrderQuantity: 1, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "7839797529453", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/furniture/Wooden%20Bathroom%20Sink%20With%20Mirror/1.png", + "https://cdn.dummyjson.com/products/images/furniture/Wooden%20Bathroom%20Sink%20With%20Mirror/2.png", + "https://cdn.dummyjson.com/products/images/furniture/Wooden%20Bathroom%20Sink%20With%20Mirror/3.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/furniture/Wooden%20Bathroom%20Sink%20With%20Mirror/thumbnail.png", + }, + { + id: 16, + title: "Apple", + description: + "Fresh and crisp apples, perfect for snacking or incorporating into various recipes.", + category: "groceries", + price: 1.99, + discountPercentage: 1.97, + rating: 2.96, + stock: 9, + tags: ["fruits"], + sku: "QTROUV79", + weight: 8, + dimensions: { + width: 8.29, + height: 5.58, + depth: 12.41, + }, + warrantyInformation: "2 year warranty", + shippingInformation: "Ships in 2 weeks", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 4, + comment: "Great product!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Logan Lee", + reviewerEmail: "logan.lee@x.dummyjson.com", + }, + { + rating: 4, + comment: "Great product!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Elena Long", + reviewerEmail: "elena.long@x.dummyjson.com", + }, + { + rating: 1, + comment: "Not as described!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Grayson Coleman", + reviewerEmail: "grayson.coleman@x.dummyjson.com", + }, + ], + returnPolicy: "60 days return policy", + minimumOrderQuantity: 44, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "2517819903837", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: ["https://cdn.dummyjson.com/products/images/groceries/Apple/1.png"], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Apple/thumbnail.png", + }, + { + id: 17, + title: "Beef Steak", + description: + "High-quality beef steak, great for grilling or cooking to your preferred level of doneness.", + category: "groceries", + price: 12.99, + discountPercentage: 17.99, + rating: 2.83, + stock: 96, + tags: ["meat"], + sku: "BWWA2MSO", + weight: 9, + dimensions: { + width: 23.35, + height: 13.48, + depth: 26.4, + }, + warrantyInformation: "1 month warranty", + shippingInformation: "Ships overnight", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 4, + comment: "Very pleased!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Ethan Martinez", + reviewerEmail: "ethan.martinez@x.dummyjson.com", + }, + { + rating: 3, + comment: "Disappointing product!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Owen Fisher", + reviewerEmail: "owen.fisher@x.dummyjson.com", + }, + { + rating: 4, + comment: "Very happy with my purchase!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Scarlett Wright", + reviewerEmail: "scarlett.wright@x.dummyjson.com", + }, + ], + returnPolicy: "90 days return policy", + minimumOrderQuantity: 21, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "8335515097879", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/groceries/Beef%20Steak/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Beef%20Steak/thumbnail.png", + }, + { + id: 18, + title: "Cat Food", + description: + "Nutritious cat food formulated to meet the dietary needs of your feline friend.", + category: "groceries", + price: 8.99, + discountPercentage: 9.57, + rating: 2.88, + stock: 13, + tags: ["pet supplies", "cat food"], + sku: "C3F8QN6O", + weight: 9, + dimensions: { + width: 15.4, + height: 13.97, + depth: 25.13, + }, + warrantyInformation: "3 months warranty", + shippingInformation: "Ships in 1-2 business days", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Very pleased!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Mateo Bennett", + reviewerEmail: "mateo.bennett@x.dummyjson.com", + }, + { + rating: 5, + comment: "Very pleased!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Aurora Barnes", + reviewerEmail: "aurora.barnes@x.dummyjson.com", + }, + { + rating: 5, + comment: "Great value for money!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Ellie Stewart", + reviewerEmail: "ellie.stewart@x.dummyjson.com", + }, + ], + returnPolicy: "7 days return policy", + minimumOrderQuantity: 48, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "5503491330693", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/groceries/Cat%20Food/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Cat%20Food/thumbnail.png", + }, + { + id: 19, + title: "Chicken Meat", + description: + "Fresh and tender chicken meat, suitable for various culinary preparations.", + category: "groceries", + price: 9.99, + discountPercentage: 10.46, + rating: 4.61, + stock: 69, + tags: ["meat"], + sku: "G5YEHW7B", + weight: 7, + dimensions: { + width: 15.96, + height: 29.24, + depth: 26.25, + }, + warrantyInformation: "Lifetime warranty", + shippingInformation: "Ships in 1 month", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Very satisfied!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Sophia Jones", + reviewerEmail: "sophia.jones@x.dummyjson.com", + }, + { + rating: 5, + comment: "Great value for money!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Maya Reed", + reviewerEmail: "maya.reed@x.dummyjson.com", + }, + { + rating: 4, + comment: "Highly recommended!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Harper Turner", + reviewerEmail: "harper.turner@x.dummyjson.com", + }, + ], + returnPolicy: "7 days return policy", + minimumOrderQuantity: 46, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "0966223559510", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/groceries/Chicken%20Meat/1.png", + "https://cdn.dummyjson.com/products/images/groceries/Chicken%20Meat/2.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Chicken%20Meat/thumbnail.png", + }, + { + id: 20, + title: "Cooking Oil", + description: + "Versatile cooking oil suitable for frying, sautéing, and various culinary applications.", + category: "groceries", + price: 4.99, + discountPercentage: 18.89, + rating: 4.01, + stock: 22, + tags: ["cooking essentials"], + sku: "Q6ZP1UY8", + weight: 8, + dimensions: { + width: 8.18, + height: 27.45, + depth: 27.88, + }, + warrantyInformation: "Lifetime warranty", + shippingInformation: "Ships in 1 month", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Would buy again!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Mason Parker", + reviewerEmail: "mason.parker@x.dummyjson.com", + }, + { + rating: 3, + comment: "Poor quality!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Jonathan Pierce", + reviewerEmail: "jonathan.pierce@x.dummyjson.com", + }, + { + rating: 5, + comment: "Would buy again!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Alexander Hernandez", + reviewerEmail: "alexander.hernandez@x.dummyjson.com", + }, + ], + returnPolicy: "60 days return policy", + minimumOrderQuantity: 2, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "6707669443381", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/groceries/Cooking%20Oil/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Cooking%20Oil/thumbnail.png", + }, + { + id: 21, + title: "Cucumber", + description: + "Crisp and hydrating cucumbers, ideal for salads, snacks, or as a refreshing side.", + category: "groceries", + price: 1.49, + discountPercentage: 11.44, + rating: 4.71, + stock: 22, + tags: ["vegetables"], + sku: "6KGF2K6Z", + weight: 9, + dimensions: { + width: 11.04, + height: 20.5, + depth: 8.18, + }, + warrantyInformation: "5 year warranty", + shippingInformation: "Ships overnight", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 4, + comment: "Very satisfied!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Elijah Hill", + reviewerEmail: "elijah.hill@x.dummyjson.com", + }, + { + rating: 5, + comment: "Fast shipping!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Liam Garcia", + reviewerEmail: "liam.garcia@x.dummyjson.com", + }, + { + rating: 4, + comment: "Excellent quality!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Ella Cook", + reviewerEmail: "ella.cook@x.dummyjson.com", + }, + ], + returnPolicy: "30 days return policy", + minimumOrderQuantity: 7, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "2597004869708", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/groceries/Cucumber/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Cucumber/thumbnail.png", + }, + { + id: 22, + title: "Dog Food", + description: + "Specially formulated dog food designed to provide essential nutrients for your canine companion.", + category: "groceries", + price: 10.99, + discountPercentage: 18.15, + rating: 2.74, + stock: 40, + tags: ["pet supplies", "dog food"], + sku: "A6QRCH37", + weight: 2, + dimensions: { + width: 29.39, + height: 29.77, + depth: 20.54, + }, + warrantyInformation: "1 year warranty", + shippingInformation: "Ships in 1 month", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Highly impressed!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Leo Rivera", + reviewerEmail: "leo.rivera@x.dummyjson.com", + }, + { + rating: 4, + comment: "Highly recommended!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Alexander Jones", + reviewerEmail: "alexander.jones@x.dummyjson.com", + }, + { + rating: 4, + comment: "Would buy again!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Addison Wright", + reviewerEmail: "addison.wright@x.dummyjson.com", + }, + ], + returnPolicy: "90 days return policy", + minimumOrderQuantity: 29, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "7957222289508", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/groceries/Dog%20Food/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Dog%20Food/thumbnail.png", + }, + { + id: 23, + title: "Eggs", + description: + "Fresh eggs, a versatile ingredient for baking, cooking, or breakfast.", + category: "groceries", + price: 2.99, + discountPercentage: 5.8, + rating: 4.46, + stock: 10, + tags: ["dairy"], + sku: "YA617RI7", + weight: 4, + dimensions: { + width: 12.3, + height: 10.99, + depth: 15.53, + }, + warrantyInformation: "3 year warranty", + shippingInformation: "Ships overnight", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 2, + comment: "Very unhappy with my purchase!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Mateo Perez", + reviewerEmail: "mateo.perez@x.dummyjson.com", + }, + { + rating: 4, + comment: "Very happy with my purchase!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Cameron Perez", + reviewerEmail: "cameron.perez@x.dummyjson.com", + }, + { + rating: 5, + comment: "Very happy with my purchase!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Aurora Barnes", + reviewerEmail: "aurora.barnes@x.dummyjson.com", + }, + ], + returnPolicy: "30 days return policy", + minimumOrderQuantity: 43, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "7095702028776", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: ["https://cdn.dummyjson.com/products/images/groceries/Eggs/1.png"], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Eggs/thumbnail.png", + }, + { + id: 24, + title: "Fish Steak", + description: + "Quality fish steak, suitable for grilling, baking, or pan-searing.", + category: "groceries", + price: 14.99, + discountPercentage: 7, + rating: 4.83, + stock: 99, + tags: ["seafood"], + sku: "XNIH1MTA", + weight: 8, + dimensions: { + width: 20.14, + height: 8.4, + depth: 10.01, + }, + warrantyInformation: "1 year warranty", + shippingInformation: "Ships in 1 month", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Great value for money!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Michael Johnson", + reviewerEmail: "michael.johnson@x.dummyjson.com", + }, + { + rating: 4, + comment: "Would buy again!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Julian Newton", + reviewerEmail: "julian.newton@x.dummyjson.com", + }, + { + rating: 5, + comment: "Excellent quality!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Lila Hudson", + reviewerEmail: "lila.hudson@x.dummyjson.com", + }, + ], + returnPolicy: "30 days return policy", + minimumOrderQuantity: 49, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "4250692197342", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/groceries/Fish%20Steak/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Fish%20Steak/thumbnail.png", + }, + { + id: 25, + title: "Green Bell Pepper", + description: + "Fresh and vibrant green bell pepper, perfect for adding color and flavor to your dishes.", + category: "groceries", + price: 1.29, + discountPercentage: 15.5, + rating: 4.28, + stock: 89, + tags: ["vegetables"], + sku: "HU7S7VQ0", + weight: 7, + dimensions: { + width: 7.32, + height: 14.31, + depth: 21.38, + }, + warrantyInformation: "5 year warranty", + shippingInformation: "Ships overnight", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 4, + comment: "Excellent quality!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Maya Reed", + reviewerEmail: "maya.reed@x.dummyjson.com", + }, + { + rating: 4, + comment: "Would buy again!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Madison Collins", + reviewerEmail: "madison.collins@x.dummyjson.com", + }, + { + rating: 5, + comment: "Would buy again!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Ethan Thompson", + reviewerEmail: "ethan.thompson@x.dummyjson.com", + }, + ], + returnPolicy: "30 days return policy", + minimumOrderQuantity: 1, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "7583442707568", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/groceries/Green%20Bell%20Pepper/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Green%20Bell%20Pepper/thumbnail.png", + }, + { + id: 26, + title: "Green Chili Pepper", + description: + "Spicy green chili pepper, ideal for adding heat to your favorite recipes.", + category: "groceries", + price: 0.99, + discountPercentage: 18.51, + rating: 4.43, + stock: 8, + tags: ["vegetables"], + sku: "Y4RM3JCB", + weight: 2, + dimensions: { + width: 18.67, + height: 21.17, + depth: 25.26, + }, + warrantyInformation: "No warranty", + shippingInformation: "Ships in 1-2 business days", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 2, + comment: "Disappointing product!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Mateo Bennett", + reviewerEmail: "mateo.bennett@x.dummyjson.com", + }, + { + rating: 5, + comment: "Very pleased!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Natalie Price", + reviewerEmail: "natalie.price@x.dummyjson.com", + }, + { + rating: 4, + comment: "Very satisfied!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Avery Barnes", + reviewerEmail: "avery.barnes@x.dummyjson.com", + }, + ], + returnPolicy: "30 days return policy", + minimumOrderQuantity: 43, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "8400326844874", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/groceries/Green%20Chili%20Pepper/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Green%20Chili%20Pepper/thumbnail.png", + }, + { + id: 27, + title: "Honey Jar", + description: + "Pure and natural honey in a convenient jar, perfect for sweetening beverages or drizzling over food.", + category: "groceries", + price: 6.99, + discountPercentage: 1.91, + rating: 3.5, + stock: 25, + tags: ["condiments"], + sku: "BTBNIIOU", + weight: 9, + dimensions: { + width: 26.53, + height: 27.11, + depth: 6.63, + }, + warrantyInformation: "2 year warranty", + shippingInformation: "Ships overnight", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 4, + comment: "Fast shipping!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Nicholas Bailey", + reviewerEmail: "nicholas.bailey@x.dummyjson.com", + }, + { + rating: 5, + comment: "Awesome product!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Gabriel Hayes", + reviewerEmail: "gabriel.hayes@x.dummyjson.com", + }, + { + rating: 5, + comment: "Highly impressed!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "James Garcia", + reviewerEmail: "james.garcia@x.dummyjson.com", + }, + ], + returnPolicy: "90 days return policy", + minimumOrderQuantity: 1, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "0668665656044", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/groceries/Honey%20Jar/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Honey%20Jar/thumbnail.png", + }, + { + id: 28, + title: "Ice Cream", + description: + "Creamy and delicious ice cream, available in various flavors for a delightful treat.", + category: "groceries", + price: 5.49, + discountPercentage: 7.58, + rating: 3.77, + stock: 76, + tags: ["desserts"], + sku: "VEZMU1EQ", + weight: 5, + dimensions: { + width: 17.66, + height: 24.49, + depth: 25.98, + }, + warrantyInformation: "2 year warranty", + shippingInformation: "Ships in 2 weeks", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Great product!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Elena Baker", + reviewerEmail: "elena.baker@x.dummyjson.com", + }, + { + rating: 5, + comment: "Highly impressed!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Madeline Simpson", + reviewerEmail: "madeline.simpson@x.dummyjson.com", + }, + { + rating: 5, + comment: "Very happy with my purchase!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Caleb Nelson", + reviewerEmail: "caleb.nelson@x.dummyjson.com", + }, + ], + returnPolicy: "No return policy", + minimumOrderQuantity: 19, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "9603960319256", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/groceries/Ice%20Cream/1.png", + "https://cdn.dummyjson.com/products/images/groceries/Ice%20Cream/2.png", + "https://cdn.dummyjson.com/products/images/groceries/Ice%20Cream/3.png", + "https://cdn.dummyjson.com/products/images/groceries/Ice%20Cream/4.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Ice%20Cream/thumbnail.png", + }, + { + id: 29, + title: "Juice", + description: + "Refreshing fruit juice, packed with vitamins and great for staying hydrated.", + category: "groceries", + price: 3.99, + discountPercentage: 5.45, + rating: 3.41, + stock: 99, + tags: ["beverages"], + sku: "M2K19S06", + weight: 2, + dimensions: { + width: 8.97, + height: 12.26, + depth: 15.05, + }, + warrantyInformation: "1 week warranty", + shippingInformation: "Ships in 1-2 business days", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 4, + comment: "Very satisfied!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Leo Rivera", + reviewerEmail: "leo.rivera@x.dummyjson.com", + }, + { + rating: 2, + comment: "Not worth the price!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Ethan Martinez", + reviewerEmail: "ethan.martinez@x.dummyjson.com", + }, + { + rating: 4, + comment: "Excellent quality!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Max Parker", + reviewerEmail: "max.parker@x.dummyjson.com", + }, + ], + returnPolicy: "90 days return policy", + minimumOrderQuantity: 26, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "8546824122355", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: ["https://cdn.dummyjson.com/products/images/groceries/Juice/1.png"], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Juice/thumbnail.png", + }, + { + id: 30, + title: "Kiwi", + description: + "Nutrient-rich kiwi, perfect for snacking or adding a tropical twist to your dishes.", + category: "groceries", + price: 2.49, + discountPercentage: 10.32, + rating: 4.37, + stock: 1, + tags: ["fruits"], + sku: "0X3NORB9", + weight: 8, + dimensions: { + width: 27.3, + height: 7.48, + depth: 15.08, + }, + warrantyInformation: "6 months warranty", + shippingInformation: "Ships in 3-5 business days", + availabilityStatus: "Low Stock", + reviews: [ + { + rating: 5, + comment: "Very pleased!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Nora Russell", + reviewerEmail: "nora.russell@x.dummyjson.com", + }, + { + rating: 5, + comment: "Very pleased!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Dylan Wells", + reviewerEmail: "dylan.wells@x.dummyjson.com", + }, + { + rating: 5, + comment: "Great product!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Noah Hernandez", + reviewerEmail: "noah.hernandez@x.dummyjson.com", + }, + ], + returnPolicy: "7 days return policy", + minimumOrderQuantity: 8, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "3325493172934", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: ["https://cdn.dummyjson.com/products/images/groceries/Kiwi/1.png"], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Kiwi/thumbnail.png", + }, +]; + +export default function ProductList() { + const count = useSelector(selectCount); + const dispatch = useDispatch(); + const [mobileFiltersOpen, setMobileFiltersOpen] = useState(false); + + return ( + <div className="bg-white"> + <div> + {/* Mobile filter dialog */} + <Transition.Root show={mobileFiltersOpen} as={Fragment}> + <Dialog + as="div" + className="relative z-40 lg:hidden" + onClose={setMobileFiltersOpen} + > + <Transition.Child + as={Fragment} + enter="transition-opacity ease-linear duration-300" + enterFrom="opacity-0" + enterTo="opacity-100" + leave="transition-opacity ease-linear duration-300" + leaveFrom="opacity-100" + leaveTo="opacity-0" + > + <div className="fixed inset-0 bg-black bg-opacity-25" /> + </Transition.Child> + + <div className="fixed inset-0 z-40 flex"> + <Transition.Child + as={Fragment} + enter="transition ease-in-out duration-300 transform" + enterFrom="translate-x-full" + enterTo="translate-x-0" + leave="transition ease-in-out duration-300 transform" + leaveFrom="translate-x-0" + leaveTo="translate-x-full" + > + <Dialog.Panel className="relative ml-auto flex h-full w-full max-w-xs flex-col overflow-y-auto bg-white py-4 pb-12 shadow-xl"> + <div className="flex items-center justify-between px-4"> + <h2 className="text-lg font-medium text-gray-900"> + Filters + </h2> + <button + type="button" + className="-mr-2 flex h-10 w-10 items-center justify-center rounded-md bg-white p-2 text-gray-400" + onClick={() => setMobileFiltersOpen(false)} + > + <span className="sr-only">Close menu</span> + <XMarkIcon className="h-6 w-6" aria-hidden="true" /> + </button> + </div> + + {/* Filters */} + <form className="mt-4 border-t border-gray-200"> + {filters.map((section) => ( + <Disclosure + as="div" + key={section.id} + className="border-t border-gray-200 px-4 py-6" + > + {({ open }) => ( + <> + <h3 className="-mx-2 -my-3 flow-root"> + <Disclosure.Button className="flex w-full items-center justify-between bg-white px-2 py-3 text-gray-400 hover:text-gray-500"> + <span className="font-medium text-gray-900"> + {section.name} + </span> + <span className="ml-6 flex items-center"> + {open ? ( + <MinusIcon + className="h-5 w-5" + aria-hidden="true" + /> + ) : ( + <PlusIcon + className="h-5 w-5" + aria-hidden="true" + /> + )} + </span> + </Disclosure.Button> + </h3> + <Disclosure.Panel className="pt-6"> + <div className="space-y-6"> + {section.options.map((option, optionIdx) => ( + <div + key={option.value} + className="flex items-center" + > + <input + id={`filter-mobile-${section.id}-${optionIdx}`} + name={`${section.id}[]`} + defaultValue={option.value} + type="checkbox" + defaultChecked={option.checked} + className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500" + /> + <label + htmlFor={`filter-mobile-${section.id}-${optionIdx}`} + className="ml-3 min-w-0 flex-1 text-gray-500" + > + {option.label} + </label> + </div> + ))} + </div> + </Disclosure.Panel> + </> + )} + </Disclosure> + ))} + </form> + </Dialog.Panel> + </Transition.Child> + </div> + </Dialog> + </Transition.Root> + + <main className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"> + <div className="flex items-baseline justify-between border-b border-gray-200 pb-6 pt-24"> + <h1 className="text-4xl font-bold tracking-tight text-gray-900"> + All Products + </h1> + + <div className="flex items-center"> + <Menu as="div" className="relative inline-block text-left"> + <div> + <Menu.Button className="group inline-flex justify-center text-sm font-medium text-gray-700 hover:text-gray-900"> + Sort + <ChevronDownIcon + className="-mr-1 ml-1 h-5 w-5 flex-shrink-0 text-gray-400 group-hover:text-gray-500" + aria-hidden="true" + /> + </Menu.Button> + </div> + + <Transition + as={Fragment} + enter="transition ease-out duration-100" + enterFrom="transform opacity-0 scale-95" + enterTo="transform opacity-100 scale-100" + leave="transition ease-in duration-75" + leaveFrom="transform opacity-100 scale-100" + leaveTo="transform opacity-0 scale-95" + > + <Menu.Items className="absolute right-0 z-10 mt-2 w-40 origin-top-right rounded-md bg-white shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none"> + <div className="py-1"> + {sortOptions.map((option) => ( + <Menu.Item key={option.name}> + {({ active }) => ( + <a + href={option.href} + className={classNames( + option.current + ? "font-medium text-gray-900" + : "text-gray-500", + active ? "bg-gray-100" : "", + "block px-4 py-2 text-sm" + )} + > + {option.name} + </a> + )} + </Menu.Item> + ))} + </div> + </Menu.Items> + </Transition> + </Menu> + + <button + type="button" + className="-m-2 ml-5 p-2 text-gray-400 hover:text-gray-500 sm:ml-7" + > + <span className="sr-only">View grid</span> + <Squares2X2Icon className="h-5 w-5" aria-hidden="true" /> + </button> + <button + type="button" + className="-m-2 ml-4 p-2 text-gray-400 hover:text-gray-500 sm:ml-6 lg:hidden" + onClick={() => setMobileFiltersOpen(true)} + > + <span className="sr-only">Filters</span> + <FunnelIcon className="h-5 w-5" aria-hidden="true" /> + </button> + </div> + </div> + + <section aria-labelledby="products-heading" className="pb-24 pt-6"> + <h2 id="products-heading" className="sr-only"> + Products + </h2> + + <div className="grid grid-cols-1 gap-x-8 gap-y-10 lg:grid-cols-4"> + {/* Filters */} + <form className="hidden lg:block"> + {filters.map((section) => ( + <Disclosure + as="div" + key={section.id} + className="border-b border-gray-200 py-6" + > + {({ open }) => ( + <> + <h3 className="-my-3 flow-root"> + <Disclosure.Button className="flex w-full items-center justify-between bg-white py-3 text-sm text-gray-400 hover:text-gray-500"> + <span className="font-medium text-gray-900"> + {section.name} + </span> + <span className="ml-6 flex items-center"> + {open ? ( + <MinusIcon + className="h-5 w-5" + aria-hidden="true" + /> + ) : ( + <PlusIcon + className="h-5 w-5" + aria-hidden="true" + /> + )} + </span> + </Disclosure.Button> + </h3> + <Disclosure.Panel className="pt-6"> + <div className="space-y-4"> + {section.options.map((option, optionIdx) => ( + <div + key={option.value} + className="flex items-center" + > + <input + id={`filter-${section.id}-${optionIdx}`} + name={`${section.id}[]`} + defaultValue={option.value} + type="checkbox" + defaultChecked={option.checked} + className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500" + /> + <label + htmlFor={`filter-${section.id}-${optionIdx}`} + className="ml-3 text-sm text-gray-600" + > + {option.label} + </label> + </div> + ))} + </div> + </Disclosure.Panel> + </> + )} + </Disclosure> + ))} + </form> + + {/* Product grid */} + <div className="lg:col-span-3"> + {/* This is our products list */} + <div className="bg-white"> + <div className="mx-auto max-w-2xl px-4 py-0 sm:px-6 sm:py-0 lg:max-w-7xl lg:px-8"> + <div className="mt-6 grid grid-cols-1 gap-x-6 gap-y-10 sm:grid-cols-2 lg:grid-cols-3 xl:gap-x-8"> + {products.map((product) => ( + <Link to="/product-detail"> + <div + key={product.id} + className="group relative p-2 border-2" + > + <div className="min-h-60 aspect-h-1 aspect-w-1 w-full overflow-hidden rounded-md bg-gray-200 lg:aspect-none group-hover:opacity-75 lg:h-60"> + <img + src={product.thumbnail} + alt={product.title} + className="h-full w-full object-cover object-center lg:h-full lg:w-full" + /> + </div> + <div className="mt-4 flex justify-between"> + <div> + <h3 className="text-sm text-gray-700"> + <a href={product.thumbnail}> + <span + aria-hidden="true" + className="absolute inset-0" + /> + {product.title} + </a> + </h3> + <p className="mt-1 text-sm text-gray-500"> + <StarIcon className="w-6 h-6 inline"></StarIcon> + <span className="align-bottom"> + {product.rating} + </span> + </p> + </div> + <div> + <p className="text-sm font-medium text-gray-900"> + $ + {Math.round( + product.price * + (1 - product.discountPercentage / 100) + )} + <p className="text-sm line-through font-medium text-gray-400"> + ${product.price} + </p> + </p> + </div> + </div> + </div> + </Link> + ))} + </div> + </div> + </div> + </div> + {/* Product grid end */} + </div> + </section> + + {/* section of product and filters ends */} + <div className="flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6"> + <div className="flex flex-1 justify-between sm:hidden"> + <a + href="#" + className="relative inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50" + > + Previous + </a> + <a + href="#" + className="relative ml-3 inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50" + > + Next + </a> + </div> + <div className="hidden sm:flex sm:flex-1 sm:items-center sm:justify-between"> + <div> + <p className="text-sm text-gray-700"> + Showing <span className="font-medium">1</span> to{" "} + <span className="font-medium">10</span> of{" "} + <span className="font-medium">97</span> results + </p> + </div> + <div> + <nav + className="isolate inline-flex -space-x-px rounded-md shadow-sm" + aria-label="Pagination" + > + <a + href="#" + className="relative inline-flex items-center rounded-l-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" + > + <span className="sr-only">Previous</span> + <ChevronLeftIcon className="h-5 w-5" aria-hidden="true" /> + </a> + {/* Current: "z-10 bg-indigo-600 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600", Default: "text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:outline-offset-0" */} + <a + href="#" + aria-current="page" + className="relative z-10 inline-flex items-center bg-indigo-600 px-4 py-2 text-sm font-semibold text-white focus:z-20 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" + > + 1 + </a> + <a + href="#" + className="relative inline-flex items-center px-4 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" + > + 2 + </a> + + <a + href="#" + className="relative inline-flex items-center rounded-r-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" + > + <span className="sr-only">Next</span> + <ChevronRightIcon className="h-5 w-5" aria-hidden="true" /> + </a> + </nav> + </div> + </div> + </div> + </main> + </div> + </div> + ); +} diff --git a/src/features/product-list/productAPI.js b/src/features/product/productAPI.js similarity index 100% rename from src/features/product-list/productAPI.js rename to src/features/product/productAPI.js diff --git a/src/features/product-list/productSlice.js b/src/features/product/productSlice.js similarity index 100% rename from src/features/product-list/productSlice.js rename to src/features/product/productSlice.js diff --git a/src/pages/Home.js b/src/pages/Home.js index 566c4ac..d90c690 100644 --- a/src/pages/Home.js +++ b/src/pages/Home.js @@ -1,14 +1,14 @@ import NavBar from "../features/navbar/Navbar"; -import ProductList from "../features/product-list/components/ProductList"; +import ProductList from "../features/product/components/ProductList"; function Home() { - return ( - <div> - <NavBar> - <ProductList></ProductList> - </NavBar> - </div> - ); + return ( + <div> + <NavBar> + <ProductList></ProductList> + </NavBar> + </div> + ); } -export default Home; \ No newline at end of file +export default Home; diff --git a/src/pages/ProductDetailPage.js b/src/pages/ProductDetailPage.js index 1d93ac5..0f831e7 100644 --- a/src/pages/ProductDetailPage.js +++ b/src/pages/ProductDetailPage.js @@ -1,13 +1,13 @@ import NavBar from "../features/navbar/Navbar"; -import ProductDetail from "../features/product-list/components/ProductDetail"; +import ProductDetail from "../features/product/components/ProductDetail"; function ProductDetailPage() { - return ( - <div> - <NavBar> - <ProductDetail></ProductDetail> - </NavBar> - </div> - ); + return ( + <div> + <NavBar> + <ProductDetail></ProductDetail> + </NavBar> + </div> + ); } -export default ProductDetailPage; \ No newline at end of file +export default ProductDetailPage; From 9e6097766718393aa9a5ce20e89fc7cb9c3bfa91 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Wed, 12 Jun 2024 10:44:40 +0530 Subject: [PATCH 03/47] fetching products through json-server using redux toolkit --- data.json | 4542 ++++++----------- src/app/store.js | 6 +- .../product/components/ProductList.js | 1785 +------ src/features/product/productAPI.js | 14 +- src/features/product/productSlice.js | 36 +- 5 files changed, 1610 insertions(+), 4773 deletions(-) diff --git a/data.json b/data.json index 500b5f3..20bff00 100644 --- a/data.json +++ b/data.json @@ -2,3114 +2,1722 @@ "products": [ { "id": 1, - "title": "iPhone 9", - "description": "An apple mobile which is nothing like apple", - "price": 993, - "discountPercentage": 12.96, - "rating": 0, - "stock": 0, - "brand": "Apple", - "category": "smartphones", - "thumbnail": "https://i.dummyjson.com/data/products/1/thumbnail.jpg", + "title": "Essence Mascara Lash Princess", + "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, + "stock": 5, + "tags": ["beauty", "mascara"], + "brand": "Essence", + "sku": "RCH45Q1A", + "weight": 2, + "dimensions": { + "width": 23.17, + "height": 14.43, + "depth": 28.01 + }, + "warrantyInformation": "1 month warranty", + "shippingInformation": "Ships in 1 month", + "availabilityStatus": "Low Stock", + "reviews": [ + { + "rating": 2, + "comment": "Very unhappy with my purchase!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "John Doe", + "reviewerEmail": "john.doe@x.dummyjson.com" + }, + { + "rating": 2, + "comment": "Not as described!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Nolan Gonzalez", + "reviewerEmail": "nolan.gonzalez@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Very satisfied!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Scarlett Wright", + "reviewerEmail": "scarlett.wright@x.dummyjson.com" + } + ], + "returnPolicy": "30 days return policy", + "minimumOrderQuantity": 24, + "meta": { + "createdAt": "2024-05-23T08:56:21.618Z", + "updatedAt": "2024-05-23T08:56:21.618Z", + "barcode": "9164035109868", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, "images": [ - "https://i.dummyjson.com/data/products/1/1.jpg", - "https://i.dummyjson.com/data/products/1/2.jpg", - "https://i.dummyjson.com/data/products/1/3.jpg", - "https://i.dummyjson.com/data/products/1/thumbnail.jpg" + "https://cdn.dummyjson.com/products/images/beauty/Essence%20Mascara%20Lash%20Princess/1.png" ], - "deleted": true + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Essence%20Mascara%20Lash%20Princess/thumbnail.png" }, { "id": 2, - "title": "iPhone X", - "description": "SIM-Free, Model A19211 6.5-inch Super Retina HD display with OLED technology A12 Bionic chip with ...", - "price": 1111, - "discountPercentage": 17.94, - "rating": 0, - "stock": 34, - "brand": "Apple", - "category": "smartphones", - "thumbnail": "https://i.dummyjson.com/data/products/2/thumbnail.jpg", + "title": "Eyeshadow Palette with Mirror", + "description": "The Eyeshadow Palette with Mirror offers a versatile range of eyeshadow shades for creating stunning eye looks. With a built-in mirror, it's convenient for on-the-go makeup application.", + "category": "beauty", + "price": 19.99, + "discountPercentage": 5.5, + "rating": 3.28, + "stock": 44, + "tags": ["beauty", "eyeshadow"], + "brand": "Glamour Beauty", + "sku": "MVCFH27F", + "weight": 3, + "dimensions": { + "width": 12.42, + "height": 8.63, + "depth": 29.13 + }, + "warrantyInformation": "1 year warranty", + "shippingInformation": "Ships in 2 weeks", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 4, + "comment": "Very satisfied!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Liam Garcia", + "reviewerEmail": "liam.garcia@x.dummyjson.com" + }, + { + "rating": 1, + "comment": "Very disappointed!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Nora Russell", + "reviewerEmail": "nora.russell@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Highly impressed!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Elena Baker", + "reviewerEmail": "elena.baker@x.dummyjson.com" + } + ], + "returnPolicy": "30 days return policy", + "minimumOrderQuantity": 32, + "meta": { + "createdAt": "2024-05-23T08:56:21.618Z", + "updatedAt": "2024-05-23T08:56:21.618Z", + "barcode": "2817839095220", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, "images": [ - "https://i.dummyjson.com/data/products/2/1.jpg", - "https://i.dummyjson.com/data/products/2/2.jpg", - "https://i.dummyjson.com/data/products/2/3.jpg", - "https://i.dummyjson.com/data/products/2/thumbnail.jpg" + "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/1.png" ], - "deleted": true + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/thumbnail.png" }, { "id": 3, - "title": "Samsung Universe 9", - "description": "Samsung's new variant which goes beyond Galaxy to the Universe", - "price": 1249, - "discountPercentage": 15.46, - "rating": 4.09, - "stock": 36, - "brand": "Samsung", - "category": "smartphones", - "thumbnail": "https://i.dummyjson.com/data/products/3/thumbnail.jpg", - "images": ["https://i.dummyjson.com/data/products/3/1.jpg"], - "deleted": true + "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, + "discountPercentage": 18.14, + "rating": 3.82, + "stock": 59, + "tags": ["beauty", "face powder"], + "brand": "Velvet Touch", + "sku": "9EN8WLT2", + "weight": 8, + "dimensions": { + "width": 24.16, + "height": 10.7, + "depth": 11.07 + }, + "warrantyInformation": "2 year warranty", + "shippingInformation": "Ships in 1-2 business days", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Very happy with my purchase!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Ethan Thompson", + "reviewerEmail": "ethan.thompson@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Great value for money!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Levi Hicks", + "reviewerEmail": "levi.hicks@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Highly impressed!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Hazel Gardner", + "reviewerEmail": "hazel.gardner@x.dummyjson.com" + } + ], + "returnPolicy": "60 days return policy", + "minimumOrderQuantity": 25, + "meta": { + "createdAt": "2024-05-23T08:56:21.618Z", + "updatedAt": "2024-05-23T08:56:21.618Z", + "barcode": "0516267971277", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png" }, { "id": 4, - "title": "OPPOF19", - "description": "OPPO F19 is officially announced on April 2021.", - "price": 300, - "discountPercentage": 17.91, - "rating": 0, - "stock": 123, - "brand": "OPPO", - "category": "smartphones", - "thumbnail": "https://i.dummyjson.com/data/products/4/thumbnail.jpg", + "title": "Red Lipstick", + "description": "The Red Lipstick is a classic and bold choice for adding a pop of color to your lips. With a creamy and pigmented formula, it provides a vibrant and long-lasting finish.", + "category": "beauty", + "price": 12.99, + "discountPercentage": 19.03, + "rating": 2.51, + "stock": 68, + "tags": ["beauty", "lipstick"], + "brand": "Chic Cosmetics", + "sku": "O5IF1NTA", + "weight": 5, + "dimensions": { + "width": 14.37, + "height": 13.94, + "depth": 14.6 + }, + "warrantyInformation": "Lifetime warranty", + "shippingInformation": "Ships in 2 weeks", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Great product!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Leo Rivera", + "reviewerEmail": "leo.rivera@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Oscar Powers", + "reviewerEmail": "oscar.powers@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Carter Rivera", + "reviewerEmail": "carter.rivera@x.dummyjson.com" + } + ], + "returnPolicy": "90 days return policy", + "minimumOrderQuantity": 6, + "meta": { + "createdAt": "2024-05-23T08:56:21.619Z", + "updatedAt": "2024-05-23T08:56:21.619Z", + "barcode": "9444582199406", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, "images": [ - "https://i.dummyjson.com/data/products/4/1.jpg", - "https://i.dummyjson.com/data/products/4/2.jpg", - "https://i.dummyjson.com/data/products/4/3.jpg", - "https://i.dummyjson.com/data/products/4/thumbnail.jpg" - ] + "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/thumbnail.png" }, { "id": 5, - "title": "Huawei P30", - "description": "Huawei’s re-badged P30 Pro New Edition was officially unveiled yesterday in Germany and now the device has made its way to the UK.", - "price": 499, - "discountPercentage": 10.58, - "rating": 4.09, - "stock": 32, - "brand": "Huawei", - "category": "smartphones", - "thumbnail": "https://i.dummyjson.com/data/products/5/thumbnail.jpg", + "title": "Red Nail Polish", + "description": "The Red Nail Polish offers a rich and glossy red hue for vibrant and polished nails. With a quick-drying formula, it provides a salon-quality finish at home.", + "category": "beauty", + "price": 8.99, + "discountPercentage": 2.46, + "rating": 3.91, + "stock": 71, + "tags": ["beauty", "nail polish"], + "brand": "Nail Couture", + "sku": "YUIIIP4W", + "weight": 9, + "dimensions": { + "width": 8.11, + "height": 10.89, + "depth": 29.06 + }, + "warrantyInformation": "1 year warranty", + "shippingInformation": "Ships in 1 week", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Leo Rivera", + "reviewerEmail": "leo.rivera@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Great product!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Evan Reed", + "reviewerEmail": "evan.reed@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Highly recommended!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Evelyn Sanchez", + "reviewerEmail": "evelyn.sanchez@x.dummyjson.com" + } + ], + "returnPolicy": "No return policy", + "minimumOrderQuantity": 46, + "meta": { + "createdAt": "2024-05-23T08:56:21.619Z", + "updatedAt": "2024-05-23T08:56:21.619Z", + "barcode": "3212847902461", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, "images": [ - "https://i.dummyjson.com/data/products/5/1.jpg", - "https://i.dummyjson.com/data/products/5/2.jpg", - "https://i.dummyjson.com/data/products/5/3.jpg" - ] + "https://cdn.dummyjson.com/products/images/beauty/Red%20Nail%20Polish/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Red%20Nail%20Polish/thumbnail.png" }, { "id": 6, - "title": "MacBook Pro", - "description": "MacBook Pro 2021 with mini-LED display may launch between September, November", - "price": 1999, - "discountPercentage": 11.02, - "rating": 0, - "stock": 83, - "brand": "Apple", - "category": "laptops", - "thumbnail": "https://i.dummyjson.com/data/products/6/thumbnail.png", + "title": "Calvin Klein CK One", + "description": "CK One by Calvin Klein is a classic unisex fragrance, known for its fresh and clean scent. It's a versatile fragrance suitable for everyday wear.", + "category": "fragrances", + "price": 49.99, + "discountPercentage": 0.32, + "rating": 4.85, + "stock": 17, + "tags": ["fragrances", "perfumes"], + "brand": "Calvin Klein", + "sku": "DZM2JQZE", + "weight": 5, + "dimensions": { + "width": 11.53, + "height": 14.44, + "depth": 6.81 + }, + "warrantyInformation": "5 year warranty", + "shippingInformation": "Ships overnight", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Great value for money!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Sophia Brown", + "reviewerEmail": "sophia.brown@x.dummyjson.com" + }, + { + "rating": 3, + "comment": "Very disappointed!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Madison Collins", + "reviewerEmail": "madison.collins@x.dummyjson.com" + }, + { + "rating": 1, + "comment": "Poor quality!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Maya Reed", + "reviewerEmail": "maya.reed@x.dummyjson.com" + } + ], + "returnPolicy": "No return policy", + "minimumOrderQuantity": 20, + "meta": { + "createdAt": "2024-05-23T08:56:21.619Z", + "updatedAt": "2024-05-23T08:56:21.619Z", + "barcode": "2210136215089", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, "images": [ - "https://i.dummyjson.com/data/products/6/1.png", - "https://i.dummyjson.com/data/products/6/2.jpg", - "https://i.dummyjson.com/data/products/6/3.png", - "https://i.dummyjson.com/data/products/6/thumbnail.png" - ] + "https://cdn.dummyjson.com/products/images/fragrances/Calvin%20Klein%20CK%20One/1.png", + "https://cdn.dummyjson.com/products/images/fragrances/Calvin%20Klein%20CK%20One/2.png", + "https://cdn.dummyjson.com/products/images/fragrances/Calvin%20Klein%20CK%20One/3.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/fragrances/Calvin%20Klein%20CK%20One/thumbnail.png" }, { "id": 7, - "title": "Samsung Galaxy Book", - "description": "Samsung Galaxy Book S (2020) Laptop With Intel Lakefield Chip, 8GB of RAM Launched", - "price": 1499, - "discountPercentage": 4.15, - "rating": 4.25, - "stock": 50, - "brand": "Samsung", - "category": "laptops", - "thumbnail": "https://i.dummyjson.com/data/products/7/thumbnail.jpg", + "title": "Chanel Coco Noir Eau De", + "description": "Coco Noir by Chanel is an elegant and mysterious fragrance, featuring notes of grapefruit, rose, and sandalwood. Perfect for evening occasions.", + "category": "fragrances", + "price": 129.99, + "discountPercentage": 18.64, + "rating": 2.76, + "stock": 41, + "tags": ["fragrances", "perfumes"], + "brand": "Chanel", + "sku": "K71HBCGS", + "weight": 4, + "dimensions": { + "width": 21.27, + "height": 28, + "depth": 11.89 + }, + "warrantyInformation": "1 week warranty", + "shippingInformation": "Ships in 1 month", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 1, + "comment": "Disappointing product!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Lincoln Kelly", + "reviewerEmail": "lincoln.kelly@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Great product!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Lincoln Kelly", + "reviewerEmail": "lincoln.kelly@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Excellent quality!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Lucas Allen", + "reviewerEmail": "lucas.allen@x.dummyjson.com" + } + ], + "returnPolicy": "60 days return policy", + "minimumOrderQuantity": 5, + "meta": { + "createdAt": "2024-05-23T08:56:21.619Z", + "updatedAt": "2024-05-23T08:56:21.619Z", + "barcode": "1435582999795", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, "images": [ - "https://i.dummyjson.com/data/products/7/1.jpg", - "https://i.dummyjson.com/data/products/7/2.jpg", - "https://i.dummyjson.com/data/products/7/3.jpg", - "https://i.dummyjson.com/data/products/7/thumbnail.jpg" - ] + "https://cdn.dummyjson.com/products/images/fragrances/Chanel%20Coco%20Noir%20Eau%20De/1.png", + "https://cdn.dummyjson.com/products/images/fragrances/Chanel%20Coco%20Noir%20Eau%20De/2.png", + "https://cdn.dummyjson.com/products/images/fragrances/Chanel%20Coco%20Noir%20Eau%20De/3.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/fragrances/Chanel%20Coco%20Noir%20Eau%20De/thumbnail.png" }, { "id": 8, - "title": "Microsoft Surface Laptop 4", - "description": "Style and speed. Stand out on HD video calls backed by Studio Mics. Capture ideas on the vibrant touchscreen.", - "price": 1499, - "discountPercentage": 10.23, - "rating": 4.43, - "stock": 68, - "brand": "Microsoft Surface", - "category": "laptops", - "thumbnail": "https://i.dummyjson.com/data/products/8/thumbnail.jpg", + "title": "Dior J'adore", + "description": "J'adore by Dior is a luxurious and floral fragrance, known for its blend of ylang-ylang, rose, and jasmine. It embodies femininity and sophistication.", + "category": "fragrances", + "price": 89.99, + "discountPercentage": 17.44, + "rating": 3.31, + "stock": 91, + "tags": ["fragrances", "perfumes"], + "brand": "Dior", + "sku": "E70NB03B", + "weight": 10, + "dimensions": { + "width": 21.51, + "height": 7, + "depth": 26.51 + }, + "warrantyInformation": "Lifetime warranty", + "shippingInformation": "Ships in 2 weeks", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Fast shipping!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Zoe Nicholson", + "reviewerEmail": "zoe.nicholson@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Excellent quality!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Addison Wright", + "reviewerEmail": "addison.wright@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Would buy again!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Clara Berry", + "reviewerEmail": "clara.berry@x.dummyjson.com" + } + ], + "returnPolicy": "7 days return policy", + "minimumOrderQuantity": 8, + "meta": { + "createdAt": "2024-05-23T08:56:21.619Z", + "updatedAt": "2024-05-23T08:56:21.619Z", + "barcode": "0887083199279", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, "images": [ - "https://i.dummyjson.com/data/products/8/1.jpg", - "https://i.dummyjson.com/data/products/8/2.jpg", - "https://i.dummyjson.com/data/products/8/3.jpg", - "https://i.dummyjson.com/data/products/8/4.jpg", - "https://i.dummyjson.com/data/products/8/thumbnail.jpg" - ] + "https://cdn.dummyjson.com/products/images/fragrances/Dior%20J'adore/1.png", + "https://cdn.dummyjson.com/products/images/fragrances/Dior%20J'adore/2.png", + "https://cdn.dummyjson.com/products/images/fragrances/Dior%20J'adore/3.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/fragrances/Dior%20J'adore/thumbnail.png" }, { "id": 9, - "title": "Infinix INBOOK", - "description": "Infinix Inbook X1 Ci3 10th 8GB 256GB 14 Win10 Grey – 1 Year Warranty", - "price": 1099, - "discountPercentage": 11.83, - "rating": 4.54, - "stock": 96, - "brand": "Infinix", - "category": "laptops", - "thumbnail": "https://i.dummyjson.com/data/products/9/thumbnail.jpg", + "title": "Dolce Shine Eau de", + "description": "Dolce Shine by Dolce & Gabbana is a vibrant and fruity fragrance, featuring notes of mango, jasmine, and blonde woods. It's a joyful and youthful scent.", + "category": "fragrances", + "price": 69.99, + "discountPercentage": 11.47, + "rating": 2.68, + "stock": 3, + "tags": ["fragrances", "perfumes"], + "brand": "Dolce & Gabbana", + "sku": "1NBFK980", + "weight": 5, + "dimensions": { + "width": 17, + "height": 24.57, + "depth": 13.3 + }, + "warrantyInformation": "5 year warranty", + "shippingInformation": "Ships in 1-2 business days", + "availabilityStatus": "Low Stock", + "reviews": [ + { + "rating": 4, + "comment": "Very satisfied!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Xavier Wright", + "reviewerEmail": "xavier.wright@x.dummyjson.com" + }, + { + "rating": 1, + "comment": "Poor quality!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Mila Hernandez", + "reviewerEmail": "mila.hernandez@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Very happy with my purchase!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Sophia Brown", + "reviewerEmail": "sophia.brown@x.dummyjson.com" + } + ], + "returnPolicy": "30 days return policy", + "minimumOrderQuantity": 9, + "meta": { + "createdAt": "2024-05-23T08:56:21.619Z", + "updatedAt": "2024-05-23T08:56:21.619Z", + "barcode": "1939383392674", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, "images": [ - "https://i.dummyjson.com/data/products/9/1.jpg", - "https://i.dummyjson.com/data/products/9/2.png", - "https://i.dummyjson.com/data/products/9/3.png", - "https://i.dummyjson.com/data/products/9/4.jpg", - "https://i.dummyjson.com/data/products/9/thumbnail.jpg" - ] + "https://cdn.dummyjson.com/products/images/fragrances/Dolce%20Shine%20Eau%20de/1.png", + "https://cdn.dummyjson.com/products/images/fragrances/Dolce%20Shine%20Eau%20de/2.png", + "https://cdn.dummyjson.com/products/images/fragrances/Dolce%20Shine%20Eau%20de/3.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/fragrances/Dolce%20Shine%20Eau%20de/thumbnail.png" }, { "id": 10, - "title": "HP Pavilion 15-DK1056WM", - "description": "HP Pavilion 15-DK1056WM Gaming Laptop 10th Gen Core i5, 8GB, 256GB SSD, GTX 1650 4GB, Windows 10", - "price": 1099, - "discountPercentage": 6.18, - "rating": 4.43, - "stock": 89, - "brand": "HP Pavilion", - "category": "laptops", - "thumbnail": "https://i.dummyjson.com/data/products/10/thumbnail.jpeg", + "title": "Gucci Bloom Eau de", + "description": "Gucci Bloom by Gucci is a floral and captivating fragrance, with notes of tuberose, jasmine, and Rangoon creeper. It's a modern and romantic scent.", + "category": "fragrances", + "price": 79.99, + "discountPercentage": 8.9, + "rating": 2.69, + "stock": 93, + "tags": ["fragrances", "perfumes"], + "brand": "Gucci", + "sku": "FFKZ6HOF", + "weight": 10, + "dimensions": { + "width": 22.28, + "height": 17.81, + "depth": 27.18 + }, + "warrantyInformation": "No warranty", + "shippingInformation": "Ships in 2 weeks", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Great value for money!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Aria Parker", + "reviewerEmail": "aria.parker@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Excellent quality!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Natalie Harris", + "reviewerEmail": "natalie.harris@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Fast shipping!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Ava Harris", + "reviewerEmail": "ava.harris@x.dummyjson.com" + } + ], + "returnPolicy": "No return policy", + "minimumOrderQuantity": 10, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "8232190382069", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, "images": [ - "https://i.dummyjson.com/data/products/10/1.jpg", - "https://i.dummyjson.com/data/products/10/2.jpg", - "https://i.dummyjson.com/data/products/10/3.jpg", - "https://i.dummyjson.com/data/products/10/thumbnail.jpeg" - ] + "https://cdn.dummyjson.com/products/images/fragrances/Gucci%20Bloom%20Eau%20de/1.png", + "https://cdn.dummyjson.com/products/images/fragrances/Gucci%20Bloom%20Eau%20de/2.png", + "https://cdn.dummyjson.com/products/images/fragrances/Gucci%20Bloom%20Eau%20de/3.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/fragrances/Gucci%20Bloom%20Eau%20de/thumbnail.png" }, { "id": 11, - "title": "perfume Oil", - "description": "Mega Discount, Impression of Acqua Di Gio by GiorgioArmani concentrated attar perfume Oil", - "price": 13, - "discountPercentage": 8.4, - "rating": 4.26, - "stock": 65, - "brand": "Impression of Acqua Di Gio", - "category": "fragrances", - "thumbnail": "https://i.dummyjson.com/data/products/11/thumbnail.jpg", + "title": "Annibale Colombo Bed", + "description": "The Annibale Colombo Bed is a luxurious and elegant bed frame, crafted with high-quality materials for a comfortable and stylish bedroom.", + "category": "furniture", + "price": 1899.99, + "discountPercentage": 0.29, + "rating": 4.14, + "stock": 47, + "tags": ["furniture", "beds"], + "brand": "Annibale Colombo", + "sku": "4KMDTZWF", + "weight": 3, + "dimensions": { + "width": 28.75, + "height": 26.88, + "depth": 24.47 + }, + "warrantyInformation": "2 year warranty", + "shippingInformation": "Ships overnight", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 4, + "comment": "Great value for money!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Julian Newton", + "reviewerEmail": "julian.newton@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Would buy again!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Madison Collins", + "reviewerEmail": "madison.collins@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Would buy again!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Clara Berry", + "reviewerEmail": "clara.berry@x.dummyjson.com" + } + ], + "returnPolicy": "7 days return policy", + "minimumOrderQuantity": 1, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "7113807059215", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, "images": [ - "https://i.dummyjson.com/data/products/11/1.jpg", - "https://i.dummyjson.com/data/products/11/2.jpg", - "https://i.dummyjson.com/data/products/11/3.jpg", - "https://i.dummyjson.com/data/products/11/thumbnail.jpg" - ] + "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Bed/1.png", + "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Bed/2.png", + "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Bed/3.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Bed/thumbnail.png" }, { "id": 12, - "title": "Brown Perfume", - "description": "Royal_Mirage Sport Brown Perfume for Men & Women - 120ml", - "price": 40, - "discountPercentage": 15.66, - "rating": 4, - "stock": 52, - "brand": "Royal_Mirage", - "category": "fragrances", - "thumbnail": "https://i.dummyjson.com/data/products/12/thumbnail.jpg", + "title": "Annibale Colombo Sofa", + "description": "The Annibale Colombo Sofa is a sophisticated and comfortable seating option, featuring exquisite design and premium upholstery for your living room.", + "category": "furniture", + "price": 2499.99, + "discountPercentage": 18.54, + "rating": 3.08, + "stock": 16, + "tags": ["furniture", "sofas"], + "brand": "Annibale Colombo", + "sku": "LUU95CQP", + "weight": 3, + "dimensions": { + "width": 20.97, + "height": 19.11, + "depth": 25.81 + }, + "warrantyInformation": "1 month warranty", + "shippingInformation": "Ships overnight", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Very satisfied!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Tyler Davis", + "reviewerEmail": "tyler.davis@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Excellent quality!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Hannah Robinson", + "reviewerEmail": "hannah.robinson@x.dummyjson.com" + }, + { + "rating": 3, + "comment": "Waste of money!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Madison Collins", + "reviewerEmail": "madison.collins@x.dummyjson.com" + } + ], + "returnPolicy": "7 days return policy", + "minimumOrderQuantity": 1, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "0426785817074", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, "images": [ - "https://i.dummyjson.com/data/products/12/1.jpg", - "https://i.dummyjson.com/data/products/12/2.jpg", - "https://i.dummyjson.com/data/products/12/3.png", - "https://i.dummyjson.com/data/products/12/4.jpg", - "https://i.dummyjson.com/data/products/12/thumbnail.jpg" - ] + "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Sofa/1.png", + "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Sofa/2.png", + "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Sofa/3.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Sofa/thumbnail.png" }, { "id": 13, - "title": "Fog Scent Xpressio Perfume", - "description": "Product details of Best Fog Scent Xpressio Perfume 100ml For Men cool long lasting perfumes for Men", - "price": 13, - "discountPercentage": 8.14, - "rating": 4.59, - "stock": 61, - "brand": "Fog Scent Xpressio", - "category": "fragrances", - "thumbnail": "https://i.dummyjson.com/data/products/13/thumbnail.webp", + "title": "Bedside Table African Cherry", + "description": "The Bedside Table in African Cherry is a stylish and functional addition to your bedroom, providing convenient storage space and a touch of elegance.", + "category": "furniture", + "price": 299.99, + "discountPercentage": 9.58, + "rating": 4.48, + "stock": 16, + "tags": ["furniture", "bedside tables"], + "brand": "Furniture Co.", + "sku": "OWPLTZYX", + "weight": 10, + "dimensions": { + "width": 25.43, + "height": 20.2, + "depth": 24.95 + }, + "warrantyInformation": "6 months warranty", + "shippingInformation": "Ships in 1-2 business days", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Very happy with my purchase!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "John Doe", + "reviewerEmail": "john.doe@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Highly recommended!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Avery Carter", + "reviewerEmail": "avery.carter@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Evelyn Sanchez", + "reviewerEmail": "evelyn.sanchez@x.dummyjson.com" + } + ], + "returnPolicy": "No return policy", + "minimumOrderQuantity": 5, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "2913244159666", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, "images": [ - "https://i.dummyjson.com/data/products/13/1.jpg", - "https://i.dummyjson.com/data/products/13/2.png", - "https://i.dummyjson.com/data/products/13/3.jpg", - "https://i.dummyjson.com/data/products/13/4.jpg", - "https://i.dummyjson.com/data/products/13/thumbnail.webp" - ] + "https://cdn.dummyjson.com/products/images/furniture/Bedside%20Table%20African%20Cherry/1.png", + "https://cdn.dummyjson.com/products/images/furniture/Bedside%20Table%20African%20Cherry/2.png", + "https://cdn.dummyjson.com/products/images/furniture/Bedside%20Table%20African%20Cherry/3.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/furniture/Bedside%20Table%20African%20Cherry/thumbnail.png" }, { "id": 14, - "title": "Non-Alcoholic Concentrated Perfume Oil", - "description": "Original Al Munakh® by Mahal Al Musk | Our Impression of Climate | 6ml Non-Alcoholic Concentrated Perfume Oil", - "price": 120, - "discountPercentage": 15.6, - "rating": 4.21, - "stock": 114, - "brand": "Al Munakh", - "category": "fragrances", - "thumbnail": "https://i.dummyjson.com/data/products/14/thumbnail.jpg", + "title": "Knoll Saarinen Executive Conference Chair", + "description": "The Knoll Saarinen Executive Conference Chair is a modern and ergonomic chair, perfect for your office or conference room with its timeless design.", + "category": "furniture", + "price": 499.99, + "discountPercentage": 15.23, + "rating": 4.11, + "stock": 47, + "tags": ["furniture", "office chairs"], + "brand": "Knoll", + "sku": "RKHVJ4FE", + "weight": 3, + "dimensions": { + "width": 16.59, + "height": 21.46, + "depth": 29.07 + }, + "warrantyInformation": "Lifetime warranty", + "shippingInformation": "Ships in 3-5 business days", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 4, + "comment": "Very happy with my purchase!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Leah Gutierrez", + "reviewerEmail": "leah.gutierrez@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Would buy again!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Nolan Gonzalez", + "reviewerEmail": "nolan.gonzalez@x.dummyjson.com" + }, + { + "rating": 2, + "comment": "Waste of money!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Stella Morris", + "reviewerEmail": "stella.morris@x.dummyjson.com" + } + ], + "returnPolicy": "30 days return policy", + "minimumOrderQuantity": 5, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "0726316339746", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, "images": [ - "https://i.dummyjson.com/data/products/14/1.jpg", - "https://i.dummyjson.com/data/products/14/2.jpg", - "https://i.dummyjson.com/data/products/14/3.jpg", - "https://i.dummyjson.com/data/products/14/thumbnail.jpg" - ] + "https://cdn.dummyjson.com/products/images/furniture/Knoll%20Saarinen%20Executive%20Conference%20Chair/1.png", + "https://cdn.dummyjson.com/products/images/furniture/Knoll%20Saarinen%20Executive%20Conference%20Chair/2.png", + "https://cdn.dummyjson.com/products/images/furniture/Knoll%20Saarinen%20Executive%20Conference%20Chair/3.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/furniture/Knoll%20Saarinen%20Executive%20Conference%20Chair/thumbnail.png" }, { "id": 15, - "title": "Eau De Perfume Spray", - "description": "Genuine Al-Rehab spray perfume from UAE/Saudi Arabia/Yemen High Quality", - "price": 30, - "discountPercentage": 10.99, - "rating": 4.7, - "stock": 105, - "brand": "Lord - Al-Rehab", - "category": "fragrances", - "thumbnail": "https://i.dummyjson.com/data/products/15/thumbnail.jpg", + "title": "Wooden Bathroom Sink With Mirror", + "description": "The Wooden Bathroom Sink with Mirror is a unique and stylish addition to your bathroom, featuring a wooden sink countertop and a matching mirror.", + "category": "furniture", + "price": 799.99, + "discountPercentage": 11.22, + "rating": 3.26, + "stock": 95, + "tags": ["furniture", "bathroom"], + "brand": "Bath Trends", + "sku": "7OLTIEVO", + "weight": 6, + "dimensions": { + "width": 7.32, + "height": 22.64, + "depth": 12.37 + }, + "warrantyInformation": "6 months warranty", + "shippingInformation": "Ships in 3-5 business days", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Highly recommended!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Charlotte Lopez", + "reviewerEmail": "charlotte.lopez@x.dummyjson.com" + }, + { + "rating": 1, + "comment": "Would not recommend!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "William Gonzalez", + "reviewerEmail": "william.gonzalez@x.dummyjson.com" + }, + { + "rating": 2, + "comment": "Not worth the price!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Ava Harrison", + "reviewerEmail": "ava.harrison@x.dummyjson.com" + } + ], + "returnPolicy": "7 days return policy", + "minimumOrderQuantity": 1, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "7839797529453", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, "images": [ - "https://i.dummyjson.com/data/products/15/1.jpg", - "https://i.dummyjson.com/data/products/15/2.jpg", - "https://i.dummyjson.com/data/products/15/3.jpg", - "https://i.dummyjson.com/data/products/15/4.jpg", - "https://i.dummyjson.com/data/products/15/thumbnail.jpg" - ] + "https://cdn.dummyjson.com/products/images/furniture/Wooden%20Bathroom%20Sink%20With%20Mirror/1.png", + "https://cdn.dummyjson.com/products/images/furniture/Wooden%20Bathroom%20Sink%20With%20Mirror/2.png", + "https://cdn.dummyjson.com/products/images/furniture/Wooden%20Bathroom%20Sink%20With%20Mirror/3.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/furniture/Wooden%20Bathroom%20Sink%20With%20Mirror/thumbnail.png" }, { "id": 16, - "title": "Hyaluronic Acid Serum", - "description": "L'Oréal Paris introduces Hyaluron Expert Replumping Serum formulated with 1.5% Hyaluronic Acid", - "price": 19, - "discountPercentage": 13.31, - "rating": 4.83, - "stock": 110, - "brand": "L'Oreal Paris", - "category": "skincare", - "thumbnail": "https://i.dummyjson.com/data/products/16/thumbnail.jpg", + "title": "Apple", + "description": "Fresh and crisp apples, perfect for snacking or incorporating into various recipes.", + "category": "groceries", + "price": 1.99, + "discountPercentage": 1.97, + "rating": 2.96, + "stock": 9, + "tags": ["fruits"], + "sku": "QTROUV79", + "weight": 8, + "dimensions": { + "width": 8.29, + "height": 5.58, + "depth": 12.41 + }, + "warrantyInformation": "2 year warranty", + "shippingInformation": "Ships in 2 weeks", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 4, + "comment": "Great product!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Logan Lee", + "reviewerEmail": "logan.lee@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Great product!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Elena Long", + "reviewerEmail": "elena.long@x.dummyjson.com" + }, + { + "rating": 1, + "comment": "Not as described!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Grayson Coleman", + "reviewerEmail": "grayson.coleman@x.dummyjson.com" + } + ], + "returnPolicy": "60 days return policy", + "minimumOrderQuantity": 44, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "2517819903837", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, "images": [ - "https://i.dummyjson.com/data/products/16/1.png", - "https://i.dummyjson.com/data/products/16/2.webp", - "https://i.dummyjson.com/data/products/16/3.jpg", - "https://i.dummyjson.com/data/products/16/4.jpg", - "https://i.dummyjson.com/data/products/16/thumbnail.jpg" - ] + "https://cdn.dummyjson.com/products/images/groceries/Apple/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Apple/thumbnail.png" }, { "id": 17, - "title": "Tree Oil 30ml", - "description": "Tea tree oil contains a number of compounds, including terpinen-4-ol, that have been shown to kill certain bacteria,", - "price": 12, - "discountPercentage": 4.09, - "rating": 4.52, - "stock": 78, - "brand": "Hemani Tea", - "category": "skincare", - "thumbnail": "https://i.dummyjson.com/data/products/17/thumbnail.jpg", + "title": "Beef Steak", + "description": "High-quality beef steak, great for grilling or cooking to your preferred level of doneness.", + "category": "groceries", + "price": 12.99, + "discountPercentage": 17.99, + "rating": 2.83, + "stock": 96, + "tags": ["meat"], + "sku": "BWWA2MSO", + "weight": 9, + "dimensions": { + "width": 23.35, + "height": 13.48, + "depth": 26.4 + }, + "warrantyInformation": "1 month warranty", + "shippingInformation": "Ships overnight", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 4, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Ethan Martinez", + "reviewerEmail": "ethan.martinez@x.dummyjson.com" + }, + { + "rating": 3, + "comment": "Disappointing product!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Owen Fisher", + "reviewerEmail": "owen.fisher@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Very happy with my purchase!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Scarlett Wright", + "reviewerEmail": "scarlett.wright@x.dummyjson.com" + } + ], + "returnPolicy": "90 days return policy", + "minimumOrderQuantity": 21, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "8335515097879", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, "images": [ - "https://i.dummyjson.com/data/products/17/1.jpg", - "https://i.dummyjson.com/data/products/17/2.jpg", - "https://i.dummyjson.com/data/products/17/3.jpg", - "https://i.dummyjson.com/data/products/17/thumbnail.jpg" - ] + "https://cdn.dummyjson.com/products/images/groceries/Beef%20Steak/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Beef%20Steak/thumbnail.png" }, { "id": 18, - "title": "Oil Free Moisturizer 100ml", - "description": "Dermive Oil Free Moisturizer with SPF 20 is specifically formulated with ceramides, hyaluronic acid & sunscreen.", - "price": 40, - "discountPercentage": 13.1, - "rating": 4.56, - "stock": 88, - "brand": "Dermive", - "category": "skincare", - "thumbnail": "https://i.dummyjson.com/data/products/18/thumbnail.jpg", + "title": "Cat Food", + "description": "Nutritious cat food formulated to meet the dietary needs of your feline friend.", + "category": "groceries", + "price": 8.99, + "discountPercentage": 9.57, + "rating": 2.88, + "stock": 13, + "tags": ["pet supplies", "cat food"], + "sku": "C3F8QN6O", + "weight": 9, + "dimensions": { + "width": 15.4, + "height": 13.97, + "depth": 25.13 + }, + "warrantyInformation": "3 months warranty", + "shippingInformation": "Ships in 1-2 business days", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Mateo Bennett", + "reviewerEmail": "mateo.bennett@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Aurora Barnes", + "reviewerEmail": "aurora.barnes@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Great value for money!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Ellie Stewart", + "reviewerEmail": "ellie.stewart@x.dummyjson.com" + } + ], + "returnPolicy": "7 days return policy", + "minimumOrderQuantity": 48, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "5503491330693", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, "images": [ - "https://i.dummyjson.com/data/products/18/1.jpg", - "https://i.dummyjson.com/data/products/18/2.jpg", - "https://i.dummyjson.com/data/products/18/3.jpg", - "https://i.dummyjson.com/data/products/18/4.jpg", - "https://i.dummyjson.com/data/products/18/thumbnail.jpg" - ] + "https://cdn.dummyjson.com/products/images/groceries/Cat%20Food/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Cat%20Food/thumbnail.png" }, { "id": 19, - "title": "Skin Beauty Serum.", - "description": "Product name: rorec collagen hyaluronic acid white face serum riceNet weight: 15 m", - "price": 46, - "discountPercentage": 10.68, - "rating": 4.42, - "stock": 54, - "brand": "ROREC White Rice", - "category": "skincare", - "thumbnail": "https://i.dummyjson.com/data/products/19/thumbnail.jpg", + "title": "Chicken Meat", + "description": "Fresh and tender chicken meat, suitable for various culinary preparations.", + "category": "groceries", + "price": 9.99, + "discountPercentage": 10.46, + "rating": 4.61, + "stock": 69, + "tags": ["meat"], + "sku": "G5YEHW7B", + "weight": 7, + "dimensions": { + "width": 15.96, + "height": 29.24, + "depth": 26.25 + }, + "warrantyInformation": "Lifetime warranty", + "shippingInformation": "Ships in 1 month", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Very satisfied!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Sophia Jones", + "reviewerEmail": "sophia.jones@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Great value for money!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Maya Reed", + "reviewerEmail": "maya.reed@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Highly recommended!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Harper Turner", + "reviewerEmail": "harper.turner@x.dummyjson.com" + } + ], + "returnPolicy": "7 days return policy", + "minimumOrderQuantity": 46, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "0966223559510", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, "images": [ - "https://i.dummyjson.com/data/products/19/1.jpg", - "https://i.dummyjson.com/data/products/19/2.jpg", - "https://i.dummyjson.com/data/products/19/3.png", - "https://i.dummyjson.com/data/products/19/thumbnail.jpg" - ] + "https://cdn.dummyjson.com/products/images/groceries/Chicken%20Meat/1.png", + "https://cdn.dummyjson.com/products/images/groceries/Chicken%20Meat/2.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Chicken%20Meat/thumbnail.png" }, { "id": 20, - "title": "Freckle Treatment Cream- 15gm", - "description": "Fair & Clear is Pakistan's only pure Freckle cream which helpsfade Freckles, Darkspots and pigments. Mercury level is 0%, so there are no side effects.", - "price": 70, - "discountPercentage": 16.99, - "rating": 4.06, - "stock": 140, - "brand": "Fair & Clear", - "category": "skincare", - "thumbnail": "https://i.dummyjson.com/data/products/20/thumbnail.jpg", + "title": "Cooking Oil", + "description": "Versatile cooking oil suitable for frying, sautéing, and various culinary applications.", + "category": "groceries", + "price": 4.99, + "discountPercentage": 18.89, + "rating": 4.01, + "stock": 22, + "tags": ["cooking essentials"], + "sku": "Q6ZP1UY8", + "weight": 8, + "dimensions": { + "width": 8.18, + "height": 27.45, + "depth": 27.88 + }, + "warrantyInformation": "Lifetime warranty", + "shippingInformation": "Ships in 1 month", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Would buy again!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Mason Parker", + "reviewerEmail": "mason.parker@x.dummyjson.com" + }, + { + "rating": 3, + "comment": "Poor quality!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Jonathan Pierce", + "reviewerEmail": "jonathan.pierce@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Would buy again!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Alexander Hernandez", + "reviewerEmail": "alexander.hernandez@x.dummyjson.com" + } + ], + "returnPolicy": "60 days return policy", + "minimumOrderQuantity": 2, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "6707669443381", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, "images": [ - "https://i.dummyjson.com/data/products/20/1.jpg", - "https://i.dummyjson.com/data/products/20/2.jpg", - "https://i.dummyjson.com/data/products/20/3.jpg", - "https://i.dummyjson.com/data/products/20/4.jpg", - "https://i.dummyjson.com/data/products/20/thumbnail.jpg" - ] + "https://cdn.dummyjson.com/products/images/groceries/Cooking%20Oil/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Cooking%20Oil/thumbnail.png" }, { "id": 21, - "title": "- Daal Masoor 500 grams", - "description": "Fine quality Branded Product Keep in a cool and dry place", - "price": 20, - "discountPercentage": 4.81, - "rating": 4.44, - "stock": 133, - "brand": "Saaf & Khaas", + "title": "Cucumber", + "description": "Crisp and hydrating cucumbers, ideal for salads, snacks, or as a refreshing side.", "category": "groceries", - "thumbnail": "https://i.dummyjson.com/data/products/21/thumbnail.png", + "price": 1.49, + "discountPercentage": 11.44, + "rating": 4.71, + "stock": 22, + "tags": ["vegetables"], + "sku": "6KGF2K6Z", + "weight": 9, + "dimensions": { + "width": 11.04, + "height": 20.5, + "depth": 8.18 + }, + "warrantyInformation": "5 year warranty", + "shippingInformation": "Ships overnight", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 4, + "comment": "Very satisfied!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Elijah Hill", + "reviewerEmail": "elijah.hill@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Fast shipping!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Liam Garcia", + "reviewerEmail": "liam.garcia@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Excellent quality!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Ella Cook", + "reviewerEmail": "ella.cook@x.dummyjson.com" + } + ], + "returnPolicy": "30 days return policy", + "minimumOrderQuantity": 7, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "2597004869708", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, "images": [ - "https://i.dummyjson.com/data/products/21/1.png", - "https://i.dummyjson.com/data/products/21/2.jpg", - "https://i.dummyjson.com/data/products/21/3.jpg" - ] + "https://cdn.dummyjson.com/products/images/groceries/Cucumber/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Cucumber/thumbnail.png" }, { "id": 22, - "title": "Elbow Macaroni - 400 gm", - "description": "Product details of Bake Parlor Big Elbow Macaroni - 400 gm", - "price": 14, - "discountPercentage": 15.58, - "rating": 4.57, - "stock": 146, - "brand": "Bake Parlor Big", + "title": "Dog Food", + "description": "Specially formulated dog food designed to provide essential nutrients for your canine companion.", "category": "groceries", - "thumbnail": "https://i.dummyjson.com/data/products/22/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/22/1.jpg", - "https://i.dummyjson.com/data/products/22/2.jpg", - "https://i.dummyjson.com/data/products/22/3.jpg" - ] - }, - { - "id": 23, - "title": "Orange Essence Food Flavou", - "description": "Specifications of Orange Essence Food Flavour For Cakes and Baking Food Item", - "price": 14, - "discountPercentage": 8.04, - "rating": 4.85, - "stock": 26, - "brand": "Baking Food Items", - "category": "groceries", - "thumbnail": "https://i.dummyjson.com/data/products/23/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/23/1.jpg", - "https://i.dummyjson.com/data/products/23/2.jpg", - "https://i.dummyjson.com/data/products/23/3.jpg", - "https://i.dummyjson.com/data/products/23/4.jpg", - "https://i.dummyjson.com/data/products/23/thumbnail.jpg" - ] - }, - { - "id": 24, - "title": "cereals muesli fruit nuts", - "description": "original fauji cereal muesli 250gm box pack original fauji cereals muesli fruit nuts flakes breakfast cereal break fast faujicereals cerels cerel foji fouji", - "price": 46, - "discountPercentage": 16.8, - "rating": 4.94, - "stock": 113, - "brand": "fauji", - "category": "groceries", - "thumbnail": "https://i.dummyjson.com/data/products/24/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/24/1.jpg", - "https://i.dummyjson.com/data/products/24/2.jpg", - "https://i.dummyjson.com/data/products/24/3.jpg", - "https://i.dummyjson.com/data/products/24/4.jpg", - "https://i.dummyjson.com/data/products/24/thumbnail.jpg" - ] - }, - { - "id": 25, - "title": "Gulab Powder 50 Gram", - "description": "Dry Rose Flower Powder Gulab Powder 50 Gram • Treats Wounds", - "price": 70, - "discountPercentage": 13.58, - "rating": 4.87, - "stock": 47, - "brand": "Dry Rose", - "category": "groceries", - "thumbnail": "https://i.dummyjson.com/data/products/25/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/25/1.png", - "https://i.dummyjson.com/data/products/25/2.jpg", - "https://i.dummyjson.com/data/products/25/3.png", - "https://i.dummyjson.com/data/products/25/4.jpg", - "https://i.dummyjson.com/data/products/25/thumbnail.jpg" - ] - }, - { - "id": 26, - "title": "Plant Hanger For Home", - "description": "Boho Decor Plant Hanger For Home Wall Decoration Macrame Wall Hanging Shelf", - "price": 41, - "discountPercentage": 17.86, - "rating": 4.08, - "stock": 131, - "brand": "Boho Decor", - "category": "home-decoration", - "thumbnail": "https://i.dummyjson.com/data/products/26/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/26/1.jpg", - "https://i.dummyjson.com/data/products/26/2.jpg", - "https://i.dummyjson.com/data/products/26/3.jpg", - "https://i.dummyjson.com/data/products/26/4.jpg", - "https://i.dummyjson.com/data/products/26/5.jpg", - "https://i.dummyjson.com/data/products/26/thumbnail.jpg" - ] - }, - { - "id": 27, - "title": "Flying Wooden Bird", - "description": "Package Include 6 Birds with Adhesive Tape Shape: 3D Shaped Wooden Birds Material: Wooden MDF, Laminated 3.5mm", - "price": 51, - "discountPercentage": 15.58, - "rating": 4.41, - "stock": 17, - "brand": "Flying Wooden", - "category": "home-decoration", - "thumbnail": "https://i.dummyjson.com/data/products/27/thumbnail.webp", - "images": [ - "https://i.dummyjson.com/data/products/27/1.jpg", - "https://i.dummyjson.com/data/products/27/2.jpg", - "https://i.dummyjson.com/data/products/27/3.jpg", - "https://i.dummyjson.com/data/products/27/4.jpg", - "https://i.dummyjson.com/data/products/27/thumbnail.webp" - ] - }, - { - "id": 28, - "title": "3D Embellishment Art Lamp", - "description": "3D led lamp sticker Wall sticker 3d wall art light on/off button cell operated (included)", - "price": 20, - "discountPercentage": 16.49, - "rating": 4.82, - "stock": 54, - "brand": "LED Lights", - "category": "home-decoration", - "thumbnail": "https://i.dummyjson.com/data/products/28/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/28/1.jpg", - "https://i.dummyjson.com/data/products/28/2.jpg", - "https://i.dummyjson.com/data/products/28/3.png", - "https://i.dummyjson.com/data/products/28/4.jpg", - "https://i.dummyjson.com/data/products/28/thumbnail.jpg" - ] - }, - { - "id": 29, - "title": "Handcraft Chinese style", - "description": "Handcraft Chinese style art luxury palace hotel villa mansion home decor ceramic vase with brass fruit plate", - "price": 60, - "discountPercentage": 15.34, - "rating": 4.44, - "stock": 7, - "brand": "luxury palace", - "category": "home-decoration", - "thumbnail": "https://i.dummyjson.com/data/products/29/thumbnail.webp", - "images": [ - "https://i.dummyjson.com/data/products/29/1.jpg", - "https://i.dummyjson.com/data/products/29/2.jpg", - "https://i.dummyjson.com/data/products/29/3.webp", - "https://i.dummyjson.com/data/products/29/4.webp", - "https://i.dummyjson.com/data/products/29/thumbnail.webp" - ] - }, - { - "id": 30, - "title": "Key Holder", - "description": "Attractive DesignMetallic materialFour key hooksReliable & DurablePremium Quality", - "price": 30, - "discountPercentage": 2.92, - "rating": 4.92, - "stock": 54, - "brand": "Golden", - "category": "home-decoration", - "thumbnail": "https://i.dummyjson.com/data/products/30/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/30/1.jpg", - "https://i.dummyjson.com/data/products/30/2.jpg", - "https://i.dummyjson.com/data/products/30/3.jpg", - "https://i.dummyjson.com/data/products/30/thumbnail.jpg" - ] - }, - { - "id": 31, - "title": "Mornadi Velvet Bed", - "description": "Mornadi Velvet Bed Base with Headboard Slats Support Classic Style Bedroom Furniture Bed Set", - "price": 40, - "discountPercentage": 17, - "rating": 4.16, - "stock": 140, - "brand": "Furniture Bed Set", - "category": "furniture", - "thumbnail": "https://i.dummyjson.com/data/products/31/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/31/1.jpg", - "https://i.dummyjson.com/data/products/31/2.jpg", - "https://i.dummyjson.com/data/products/31/3.jpg", - "https://i.dummyjson.com/data/products/31/4.jpg", - "https://i.dummyjson.com/data/products/31/thumbnail.jpg" - ] - }, - { - "id": 32, - "title": "Sofa for Coffe Cafe", - "description": "Ratttan Outdoor furniture Set Waterproof Rattan Sofa for Coffe Cafe", - "price": 50, - "discountPercentage": 15.59, - "rating": 4.74, - "stock": 30, - "brand": "Ratttan Outdoor", - "category": "furniture", - "thumbnail": "https://i.dummyjson.com/data/products/32/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/32/1.jpg", - "https://i.dummyjson.com/data/products/32/2.jpg", - "https://i.dummyjson.com/data/products/32/3.jpg", - "https://i.dummyjson.com/data/products/32/thumbnail.jpg" - ] - }, - { - "id": 33, - "title": "3 Tier Corner Shelves", - "description": "3 Tier Corner Shelves | 3 PCs Wall Mount Kitchen Shelf | Floating Bedroom Shelf", - "price": 700, - "discountPercentage": 17, - "rating": 4.31, - "stock": 106, - "brand": "Kitchen Shelf", - "category": "furniture", - "thumbnail": "https://i.dummyjson.com/data/products/33/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/33/1.jpg", - "https://i.dummyjson.com/data/products/33/2.jpg", - "https://i.dummyjson.com/data/products/33/3.jpg", - "https://i.dummyjson.com/data/products/33/4.jpg", - "https://i.dummyjson.com/data/products/33/thumbnail.jpg" - ] - }, - { - "id": 34, - "title": "Plastic Table", - "description": "Very good quality plastic table for multi purpose now in reasonable price", - "price": 50, - "discountPercentage": 4, - "rating": 4.01, - "stock": 136, - "brand": "Multi Purpose", - "category": "furniture", - "thumbnail": "https://i.dummyjson.com/data/products/34/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/34/1.jpg", - "https://i.dummyjson.com/data/products/34/2.jpg", - "https://i.dummyjson.com/data/products/34/3.jpg", - "https://i.dummyjson.com/data/products/34/4.jpg", - "https://i.dummyjson.com/data/products/34/thumbnail.jpg" - ] - }, - { - "id": 35, - "title": "3 DOOR PORTABLE", - "description": "Material: Stainless Steel and Fabric Item Size: 110 cm x 45 cm x 175 cm Package Contents: 1 Storage Wardrobe", - "price": 41, - "discountPercentage": 7.98, - "rating": 4.06, - "stock": 68, - "brand": "AmnaMart", - "category": "furniture", - "thumbnail": "https://i.dummyjson.com/data/products/35/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/35/1.jpg", - "https://i.dummyjson.com/data/products/35/2.jpg", - "https://i.dummyjson.com/data/products/35/3.jpg", - "https://i.dummyjson.com/data/products/35/4.jpg", - "https://i.dummyjson.com/data/products/35/thumbnail.jpg" - ] - }, - { - "id": 36, - "title": "Sleeve Shirt Womens", - "description": "Cotton Solid Color Professional Wear Sleeve Shirt Womens Work Blouses Wholesale Clothing Casual Plain Custom Top OEM Customized", - "price": 90, - "discountPercentage": 10.89, - "rating": 4.26, - "stock": 39, - "brand": "Professional Wear", - "category": "tops", - "thumbnail": "https://i.dummyjson.com/data/products/36/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/36/1.jpg", - "https://i.dummyjson.com/data/products/36/2.webp", - "https://i.dummyjson.com/data/products/36/3.webp", - "https://i.dummyjson.com/data/products/36/4.jpg", - "https://i.dummyjson.com/data/products/36/thumbnail.jpg" - ] - }, - { - "id": 37, - "title": "ank Tops for Womens/Girls", - "description": "PACK OF 3 CAMISOLES ,VERY COMFORTABLE SOFT COTTON STUFF, COMFORTABLE IN ALL FOUR SEASONS", - "price": 50, - "discountPercentage": 12.05, - "rating": 4.52, - "stock": 107, - "brand": "Soft Cotton", - "category": "tops", - "thumbnail": "https://i.dummyjson.com/data/products/37/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/37/1.jpg", - "https://i.dummyjson.com/data/products/37/2.jpg", - "https://i.dummyjson.com/data/products/37/3.jpg", - "https://i.dummyjson.com/data/products/37/4.jpg", - "https://i.dummyjson.com/data/products/37/thumbnail.jpg" - ] - }, - { - "id": 38, - "title": "sublimation plain kids tank", - "description": "sublimation plain kids tank tops wholesale", - "price": 100, - "discountPercentage": 11.12, - "rating": 4.8, - "stock": 20, - "brand": "Soft Cotton", - "category": "tops", - "thumbnail": "https://i.dummyjson.com/data/products/38/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/38/1.png", - "https://i.dummyjson.com/data/products/38/2.jpg", - "https://i.dummyjson.com/data/products/38/3.jpg", - "https://i.dummyjson.com/data/products/38/4.jpg" - ] - }, - { - "id": 39, - "title": "Women Sweaters Wool", - "description": "2021 Custom Winter Fall Zebra Knit Crop Top Women Sweaters Wool Mohair Cos Customize Crew Neck Women' S Crop Top Sweater", - "price": 600, - "discountPercentage": 17.2, - "rating": 4.55, - "stock": 55, - "brand": "Top Sweater", - "category": "tops", - "thumbnail": "https://i.dummyjson.com/data/products/39/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/39/1.jpg", - "https://i.dummyjson.com/data/products/39/2.jpg", - "https://i.dummyjson.com/data/products/39/3.jpg", - "https://i.dummyjson.com/data/products/39/4.jpg", - "https://i.dummyjson.com/data/products/39/thumbnail.jpg" - ] - }, - { - "id": 40, - "title": "women winter clothes", - "description": "women winter clothes thick fleece hoodie top with sweat pantjogger women sweatsuit set joggers pants two piece pants set", - "price": 57, - "discountPercentage": 13.39, - "rating": 4.91, - "stock": 84, - "brand": "Top Sweater", - "category": "tops", - "thumbnail": "https://i.dummyjson.com/data/products/40/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/40/1.jpg", - "https://i.dummyjson.com/data/products/40/2.jpg" - ] - }, - { - "id": 41, - "title": "NIGHT SUIT", - "description": "NIGHT SUIT RED MICKY MOUSE.. For Girls. Fantastic Suits.", - "price": 55, - "discountPercentage": 15.05, - "rating": 4.65, - "stock": 21, - "brand": "RED MICKY MOUSE..", - "category": "womens-dresses", - "thumbnail": "https://i.dummyjson.com/data/products/41/thumbnail.webp", - "images": [ - "https://i.dummyjson.com/data/products/41/1.jpg", - "https://i.dummyjson.com/data/products/41/2.webp", - "https://i.dummyjson.com/data/products/41/3.jpg", - "https://i.dummyjson.com/data/products/41/4.jpg", - "https://i.dummyjson.com/data/products/41/thumbnail.webp" - ] - }, - { - "id": 42, - "title": "Stiched Kurta plus trouser", - "description": "FABRIC: LILEIN CHEST: 21 LENGHT: 37 TROUSER: (38) :ARABIC LILEIN", - "price": 80, - "discountPercentage": 15.37, - "rating": 4.05, - "stock": 148, - "brand": "Digital Printed", - "category": "womens-dresses", - "thumbnail": "https://i.dummyjson.com/data/products/42/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/42/1.png", - "https://i.dummyjson.com/data/products/42/2.png", - "https://i.dummyjson.com/data/products/42/3.png", - "https://i.dummyjson.com/data/products/42/4.jpg", - "https://i.dummyjson.com/data/products/42/thumbnail.jpg" - ] - }, - { - "id": 43, - "title": "frock gold printed", - "description": "Ghazi fabric long frock gold printed ready to wear stitched collection (G992)", - "price": 600, - "discountPercentage": 15.55, - "rating": 4.31, - "stock": 150, - "brand": "Ghazi Fabric", - "category": "womens-dresses", - "thumbnail": "https://i.dummyjson.com/data/products/43/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/43/1.jpg", - "https://i.dummyjson.com/data/products/43/2.jpg", - "https://i.dummyjson.com/data/products/43/3.jpg", - "https://i.dummyjson.com/data/products/43/4.jpg", - "https://i.dummyjson.com/data/products/43/thumbnail.jpg" - ] - }, - { - "id": 44, - "title": "Ladies Multicolored Dress", - "description": "This classy shirt for women gives you a gorgeous look on everyday wear and specially for semi-casual wears.", - "price": 79, - "discountPercentage": 16.88, - "rating": 4.03, - "stock": 2, - "brand": "Ghazi Fabric", - "category": "womens-dresses", - "thumbnail": "https://i.dummyjson.com/data/products/44/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/44/1.jpg", - "https://i.dummyjson.com/data/products/44/2.jpg", - "https://i.dummyjson.com/data/products/44/3.jpg", - "https://i.dummyjson.com/data/products/44/4.jpg", - "https://i.dummyjson.com/data/products/44/thumbnail.jpg" - ] - }, - { - "id": 45, - "title": "Malai Maxi Dress", - "description": "Ready to wear, Unique design according to modern standard fashion, Best fitting ,Imported stuff", - "price": 50, - "discountPercentage": 5.07, - "rating": 4.67, - "stock": 96, - "brand": "IELGY", - "category": "womens-dresses", - "thumbnail": "https://i.dummyjson.com/data/products/45/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/45/1.jpg", - "https://i.dummyjson.com/data/products/45/2.webp", - "https://i.dummyjson.com/data/products/45/3.jpg", - "https://i.dummyjson.com/data/products/45/4.jpg", - "https://i.dummyjson.com/data/products/45/thumbnail.jpg" - ] - }, - { - "id": 46, - "title": "women's shoes", - "description": "Close: Lace, Style with bottom: Increased inside, Sole Material: Rubber", - "price": 40, - "discountPercentage": 16.96, - "rating": 4.14, - "stock": 72, - "brand": "IELGY fashion", - "category": "womens-shoes", - "thumbnail": "https://i.dummyjson.com/data/products/46/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/46/1.webp", - "https://i.dummyjson.com/data/products/46/2.jpg", - "https://i.dummyjson.com/data/products/46/3.jpg", - "https://i.dummyjson.com/data/products/46/4.jpg", - "https://i.dummyjson.com/data/products/46/thumbnail.jpg" - ] - }, - { - "id": 47, - "title": "Sneaker shoes", - "description": "Synthetic Leather Casual Sneaker shoes for Women/girls Sneakers For Women", - "price": 120, - "discountPercentage": 10.37, - "rating": 4.19, - "stock": 50, - "brand": "Synthetic Leather", - "category": "womens-shoes", - "thumbnail": "https://i.dummyjson.com/data/products/47/thumbnail.jpeg", - "images": [ - "https://i.dummyjson.com/data/products/47/1.jpg", - "https://i.dummyjson.com/data/products/47/2.jpg", - "https://i.dummyjson.com/data/products/47/3.jpg", - "https://i.dummyjson.com/data/products/47/thumbnail.jpeg" - ] - }, - { - "id": 48, - "title": "Women Strip Heel", - "description": "Features: Flip-flops, Mid Heel, Comfortable, Striped Heel, Antiskid, Striped", - "price": 40, - "discountPercentage": 10.83, - "rating": 4.02, - "stock": 25, - "brand": "Sandals Flip Flops", - "category": "womens-shoes", - "thumbnail": "https://i.dummyjson.com/data/products/48/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/48/1.jpg", - "https://i.dummyjson.com/data/products/48/2.jpg", - "https://i.dummyjson.com/data/products/48/3.jpg", - "https://i.dummyjson.com/data/products/48/4.jpg", - "https://i.dummyjson.com/data/products/48/thumbnail.jpg" - ] - }, - { - "id": 49, - "title": "Chappals & Shoe Ladies Metallic", - "description": "Womens Chappals & Shoe Ladies Metallic Tong Thong Sandal Flat Summer 2020 Maasai Sandals", - "price": 23, - "discountPercentage": 2.62, - "rating": 4.72, - "stock": 107, - "brand": "Maasai Sandals", - "category": "womens-shoes", - "thumbnail": "https://i.dummyjson.com/data/products/49/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/49/1.jpg", - "https://i.dummyjson.com/data/products/49/2.jpg", - "https://i.dummyjson.com/data/products/49/3.webp", - "https://i.dummyjson.com/data/products/49/thumbnail.jpg" - ] - }, - { - "id": 50, - "title": "Women Shoes", - "description": "2020 New Arrivals Genuine Leather Fashion Trend Platform Summer Women Shoes", - "price": 36, - "discountPercentage": 16.87, - "rating": 4.33, - "stock": 46, - "brand": "Arrivals Genuine", - "category": "womens-shoes", - "thumbnail": "https://i.dummyjson.com/data/products/50/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/50/1.jpeg", - "https://i.dummyjson.com/data/products/50/2.jpg", - "https://i.dummyjson.com/data/products/50/3.jpg" - ] - }, - { - "id": 51, - "title": "half sleeves T shirts", - "description": "Many store is creating new designs and trend every month and every year. Daraz.pk have a beautiful range of men fashion brands", - "price": 23, - "discountPercentage": 12.76, - "rating": 4.26, - "stock": 132, - "brand": "Vintage Apparel", - "category": "mens-shirts", - "thumbnail": "https://i.dummyjson.com/data/products/51/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/51/1.png", - "https://i.dummyjson.com/data/products/51/2.jpg", - "https://i.dummyjson.com/data/products/51/3.jpg", - "https://i.dummyjson.com/data/products/51/thumbnail.jpg" - ] - }, - { - "id": 52, - "title": "FREE FIRE T Shirt", - "description": "quality and professional print - It doesn't just look high quality, it is high quality.", - "price": 10, - "discountPercentage": 14.72, - "rating": 4.52, - "stock": 128, - "brand": "FREE FIRE", - "category": "mens-shirts", - "thumbnail": "https://i.dummyjson.com/data/products/52/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/52/1.png", - "https://i.dummyjson.com/data/products/52/2.png", - "https://i.dummyjson.com/data/products/52/3.jpg", - "https://i.dummyjson.com/data/products/52/4.jpg", - "https://i.dummyjson.com/data/products/52/thumbnail.jpg" - ] - }, - { - "id": 53, - "title": "printed high quality T shirts", - "description": "Brand: vintage Apparel ,Export quality", - "price": 35, - "discountPercentage": 7.54, - "rating": 4.89, - "stock": 6, - "brand": "Vintage Apparel", - "category": "mens-shirts", - "thumbnail": "https://i.dummyjson.com/data/products/53/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/53/1.webp", - "https://i.dummyjson.com/data/products/53/2.jpg", - "https://i.dummyjson.com/data/products/53/3.jpg", - "https://i.dummyjson.com/data/products/53/4.jpg", - "https://i.dummyjson.com/data/products/53/thumbnail.jpg" - ] - }, - { - "id": 54, - "title": "Pubg Printed Graphic T-Shirt", - "description": "Product Description Features: 100% Ultra soft Polyester Jersey. Vibrant & colorful printing on front. Feels soft as cotton without ever cracking", - "price": 46, - "discountPercentage": 16.44, - "rating": 4.62, - "stock": 136, - "brand": "The Warehouse", - "category": "mens-shirts", - "thumbnail": "https://i.dummyjson.com/data/products/54/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/54/1.jpg", - "https://i.dummyjson.com/data/products/54/2.jpg", - "https://i.dummyjson.com/data/products/54/3.jpg", - "https://i.dummyjson.com/data/products/54/4.jpg", - "https://i.dummyjson.com/data/products/54/thumbnail.jpg" - ] - }, - { - "id": 55, - "title": "Money Heist Printed Summer T Shirts", - "description": "Fabric Jercy, Size: M & L Wear Stylish Dual Stiched", - "price": 66, - "discountPercentage": 15.97, - "rating": 4.9, - "stock": 122, - "brand": "The Warehouse", - "category": "mens-shirts", - "thumbnail": "https://i.dummyjson.com/data/products/55/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/55/1.jpg", - "https://i.dummyjson.com/data/products/55/2.webp", - "https://i.dummyjson.com/data/products/55/3.jpg", - "https://i.dummyjson.com/data/products/55/4.jpg", - "https://i.dummyjson.com/data/products/55/thumbnail.jpg" - ] - }, - { - "id": 56, - "title": "Sneakers Joggers Shoes", - "description": "Gender: Men , Colors: Same as DisplayedCondition: 100% Brand New", - "price": 40, - "discountPercentage": 12.57, - "rating": 4.38, - "stock": 6, - "brand": "Sneakers", - "category": "mens-shoes", - "thumbnail": "https://i.dummyjson.com/data/products/56/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/56/1.jpg", - "https://i.dummyjson.com/data/products/56/2.jpg", - "https://i.dummyjson.com/data/products/56/3.jpg", - "https://i.dummyjson.com/data/products/56/4.jpg", - "https://i.dummyjson.com/data/products/56/5.jpg", - "https://i.dummyjson.com/data/products/56/thumbnail.jpg" - ] - }, - { - "id": 57, - "title": "Loafers for men", - "description": "Men Shoes - Loafers for men - Rubber Shoes - Nylon Shoes - Shoes for men - Moccassion - Pure Nylon (Rubber) Expot Quality.", - "price": 47, - "discountPercentage": 10.91, - "rating": 4.91, - "stock": 20, - "brand": "Rubber", - "category": "mens-shoes", - "thumbnail": "https://i.dummyjson.com/data/products/57/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/57/1.jpg", - "https://i.dummyjson.com/data/products/57/2.jpg", - "https://i.dummyjson.com/data/products/57/3.jpg", - "https://i.dummyjson.com/data/products/57/4.jpg", - "https://i.dummyjson.com/data/products/57/thumbnail.jpg" - ] - }, - { - "id": 58, - "title": "formal offices shoes", - "description": "Pattern Type: Solid, Material: PU, Toe Shape: Pointed Toe ,Outsole Material: Rubber", - "price": 57, - "discountPercentage": 12, - "rating": 4.41, - "stock": 68, - "brand": "The Warehouse", - "category": "mens-shoes", - "thumbnail": "https://i.dummyjson.com/data/products/58/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/58/1.jpg", - "https://i.dummyjson.com/data/products/58/2.jpg", - "https://i.dummyjson.com/data/products/58/3.jpg", - "https://i.dummyjson.com/data/products/58/4.jpg", - "https://i.dummyjson.com/data/products/58/thumbnail.jpg" - ] - }, - { - "id": 59, - "title": "Spring and summershoes", - "description": "Comfortable stretch cloth, lightweight body; ,rubber sole, anti-skid wear;", - "price": 20, - "discountPercentage": 8.71, - "rating": 4.33, - "stock": 137, - "brand": "Sneakers", - "category": "mens-shoes", - "thumbnail": "https://i.dummyjson.com/data/products/59/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/59/1.jpg", - "https://i.dummyjson.com/data/products/59/2.jpg", - "https://i.dummyjson.com/data/products/59/3.jpg", - "https://i.dummyjson.com/data/products/59/4.jpg", - "https://i.dummyjson.com/data/products/59/thumbnail.jpg" - ] - }, - { - "id": 60, - "title": "Stylish Casual Jeans Shoes", - "description": "High Quality ,Stylish design ,Comfortable wear ,FAshion ,Durable", - "price": 58, - "discountPercentage": 7.55, - "rating": 4.55, - "stock": 129, - "brand": "Sneakers", - "category": "mens-shoes", - "thumbnail": "https://i.dummyjson.com/data/products/60/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/60/1.jpg", - "https://i.dummyjson.com/data/products/60/2.jpg", - "https://i.dummyjson.com/data/products/60/3.jpg", - "https://i.dummyjson.com/data/products/60/thumbnail.jpg" - ] - }, - { - "id": 61, - "title": "Leather Straps Wristwatch", - "description": "Style:Sport ,Clasp:Buckles ,Water Resistance Depth:3Bar", - "price": 120, - "discountPercentage": 7.14, - "rating": 4.63, - "stock": 91, - "brand": "Naviforce", - "category": "mens-watches", - "thumbnail": "https://i.dummyjson.com/data/products/61/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/61/1.jpg", - "https://i.dummyjson.com/data/products/61/2.png", - "https://i.dummyjson.com/data/products/61/3.jpg" - ] - }, - { - "id": 62, - "title": "Waterproof Leather Brand Watch", - "description": "Watch Crown With Environmental IPS Bronze Electroplating; Display system of 12 hours", - "price": 46, - "discountPercentage": 3.15, - "rating": 4.05, - "stock": 95, - "brand": "SKMEI 9117", - "category": "mens-watches", - "thumbnail": "https://i.dummyjson.com/data/products/62/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/62/1.jpg", - "https://i.dummyjson.com/data/products/62/2.jpg" - ] - }, - { - "id": 63, - "title": "Royal Blue Premium Watch", - "description": "Men Silver Chain Royal Blue Premium Watch Latest Analog Watch", - "price": 50, - "discountPercentage": 2.56, - "rating": 4.89, - "stock": 142, - "brand": "SKMEI 9117", - "category": "mens-watches", - "thumbnail": "https://i.dummyjson.com/data/products/63/thumbnail.webp", - "images": [ - "https://i.dummyjson.com/data/products/63/1.jpg", - "https://i.dummyjson.com/data/products/63/2.jpg", - "https://i.dummyjson.com/data/products/63/3.png", - "https://i.dummyjson.com/data/products/63/4.jpeg" - ] - }, - { - "id": 64, - "title": "Leather Strap Skeleton Watch", - "description": "Leather Strap Skeleton Watch for Men - Stylish and Latest Design", - "price": 46, - "discountPercentage": 10.2, - "rating": 4.98, - "stock": 61, - "brand": "Strap Skeleton", - "category": "mens-watches", - "thumbnail": "https://i.dummyjson.com/data/products/64/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/64/1.jpg", - "https://i.dummyjson.com/data/products/64/2.webp", - "https://i.dummyjson.com/data/products/64/3.jpg", - "https://i.dummyjson.com/data/products/64/thumbnail.jpg" - ] - }, - { - "id": 65, - "title": "Stainless Steel Wrist Watch", - "description": "Stylish Watch For Man (Luxury) Classy Men's Stainless Steel Wrist Watch - Box Packed", - "price": 47, - "discountPercentage": 17.79, - "rating": 4.79, - "stock": 94, - "brand": "Stainless", - "category": "mens-watches", - "thumbnail": "https://i.dummyjson.com/data/products/65/thumbnail.webp", - "images": [ - "https://i.dummyjson.com/data/products/65/1.jpg", - "https://i.dummyjson.com/data/products/65/2.webp", - "https://i.dummyjson.com/data/products/65/3.jpg", - "https://i.dummyjson.com/data/products/65/4.webp", - "https://i.dummyjson.com/data/products/65/thumbnail.webp" - ] - }, - { - "id": 66, - "title": "Steel Analog Couple Watches", - "description": "Elegant design, Stylish ,Unique & Trendy,Comfortable wear", - "price": 35, - "discountPercentage": 3.23, - "rating": 4.79, - "stock": 24, - "brand": "Eastern Watches", - "category": "womens-watches", - "thumbnail": "https://i.dummyjson.com/data/products/66/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/66/1.jpg", - "https://i.dummyjson.com/data/products/66/2.jpg", - "https://i.dummyjson.com/data/products/66/3.jpg", - "https://i.dummyjson.com/data/products/66/4.JPG", - "https://i.dummyjson.com/data/products/66/thumbnail.jpg" - ] - }, - { - "id": 67, - "title": "Fashion Magnetic Wrist Watch", - "description": "Buy this awesome The product is originally manufactured by the company and it's a top selling product with a very reasonable", - "price": 60, - "discountPercentage": 16.69, - "rating": 4.03, - "stock": 46, - "brand": "Eastern Watches", - "category": "womens-watches", - "thumbnail": "https://i.dummyjson.com/data/products/67/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/67/1.jpg", - "https://i.dummyjson.com/data/products/67/2.jpg", - "https://i.dummyjson.com/data/products/67/3.jpg", - "https://i.dummyjson.com/data/products/67/4.jpg", - "https://i.dummyjson.com/data/products/67/thumbnail.jpg" - ] - }, - { - "id": 68, - "title": "Stylish Luxury Digital Watch", - "description": "Stylish Luxury Digital Watch For Girls / Women - Led Smart Ladies Watches For Girls", - "price": 57, - "discountPercentage": 9.03, - "rating": 4.55, - "stock": 77, - "brand": "Luxury Digital", - "category": "womens-watches", - "thumbnail": "https://i.dummyjson.com/data/products/68/thumbnail.webp", - "images": [ - "https://i.dummyjson.com/data/products/68/1.jpg", - "https://i.dummyjson.com/data/products/68/2.jpg" - ] - }, - { - "id": 69, - "title": "Golden Watch Pearls Bracelet Watch", - "description": "Product details of Golden Watch Pearls Bracelet Watch For Girls - Golden Chain Ladies Bracelate Watch for Women", - "price": 47, - "discountPercentage": 17.55, - "rating": 4.77, - "stock": 89, - "brand": "Watch Pearls", - "category": "womens-watches", - "thumbnail": "https://i.dummyjson.com/data/products/69/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/69/1.jpg", - "https://i.dummyjson.com/data/products/69/2.jpg", - "https://i.dummyjson.com/data/products/69/3.webp", - "https://i.dummyjson.com/data/products/69/4.jpg", - "https://i.dummyjson.com/data/products/69/thumbnail.jpg" - ] - }, - { - "id": 70, - "title": "Stainless Steel Women", - "description": "Fashion Skmei 1830 Shell Dial Stainless Steel Women Wrist Watch Lady Bracelet Watch Quartz Watches Ladies", - "price": 35, - "discountPercentage": 8.98, - "rating": 4.08, - "stock": 111, - "brand": "Bracelet", - "category": "womens-watches", - "thumbnail": "https://i.dummyjson.com/data/products/70/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/70/1.jpg", - "https://i.dummyjson.com/data/products/70/2.jpg", - "https://i.dummyjson.com/data/products/70/thumbnail.jpg" - ] - }, - { - "id": 71, - "title": "Women Shoulder Bags", - "description": "LouisWill Women Shoulder Bags Long Clutches Cross Body Bags Phone Bags PU Leather Hand Bags Large Capacity Card Holders Zipper Coin Purses Fashion Crossbody Bags for Girls Ladies", - "price": 46, - "discountPercentage": 14.65, - "rating": 4.71, - "stock": 17, - "brand": "LouisWill", - "category": "womens-bags", - "thumbnail": "https://i.dummyjson.com/data/products/71/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/71/1.jpg", - "https://i.dummyjson.com/data/products/71/2.jpg", - "https://i.dummyjson.com/data/products/71/3.webp", - "https://i.dummyjson.com/data/products/71/thumbnail.jpg" - ] - }, - { - "id": 72, - "title": "Handbag For Girls", - "description": "This fashion is designed to add a charming effect to your casual outfit. This Bag is made of synthetic leather.", - "price": 23, - "discountPercentage": 17.5, - "rating": 4.91, - "stock": 27, - "brand": "LouisWill", - "category": "womens-bags", - "thumbnail": "https://i.dummyjson.com/data/products/72/thumbnail.webp", - "images": [ - "https://i.dummyjson.com/data/products/72/1.jpg", - "https://i.dummyjson.com/data/products/72/2.png", - "https://i.dummyjson.com/data/products/72/3.webp", - "https://i.dummyjson.com/data/products/72/4.jpg", - "https://i.dummyjson.com/data/products/72/thumbnail.webp" - ] - }, - { - "id": 73, - "title": "Fancy hand clutch", - "description": "This fashion is designed to add a charming effect to your casual outfit. This Bag is made of synthetic leather.", - "price": 44, - "discountPercentage": 10.39, - "rating": 4.18, - "stock": 101, - "brand": "Bracelet", - "category": "womens-bags", - "thumbnail": "https://i.dummyjson.com/data/products/73/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/73/1.jpg", - "https://i.dummyjson.com/data/products/73/2.webp", - "https://i.dummyjson.com/data/products/73/3.jpg", - "https://i.dummyjson.com/data/products/73/thumbnail.jpg" - ] - }, - { - "id": 74, - "title": "Leather Hand Bag", - "description": "It features an attractive design that makes it a must have accessory in your collection. We sell different kind of bags for boys, kids, women, girls and also for unisex.", - "price": 57, - "discountPercentage": 11.19, - "rating": 4.01, - "stock": 43, - "brand": "Copenhagen Luxe", - "category": "womens-bags", - "thumbnail": "https://i.dummyjson.com/data/products/74/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/74/1.jpg", - "https://i.dummyjson.com/data/products/74/2.jpg", - "https://i.dummyjson.com/data/products/74/3.jpg", - "https://i.dummyjson.com/data/products/74/4.jpg", - "https://i.dummyjson.com/data/products/74/thumbnail.jpg" - ] - }, - { - "id": 75, - "title": "Seven Pocket Women Bag", - "description": "Seven Pocket Women Bag Handbags Lady Shoulder Crossbody Bag Female Purse Seven Pocket Bag", - "price": 68, - "discountPercentage": 14.87, - "rating": 4.93, - "stock": 13, - "brand": "Steal Frame", - "category": "womens-bags", - "thumbnail": "https://i.dummyjson.com/data/products/75/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/75/1.jpg", - "https://i.dummyjson.com/data/products/75/2.jpg", - "https://i.dummyjson.com/data/products/75/3.jpg", - "https://i.dummyjson.com/data/products/75/thumbnail.jpg" - ] - }, - { - "id": 76, - "title": "Silver Ring Set Women", - "description": "Jewelry Type:RingsCertificate Type:NonePlating:Silver PlatedShapeattern:noneStyle:CLASSICReligious", - "price": 70, - "discountPercentage": 13.57, - "rating": 4.61, - "stock": 51, - "brand": "Darojay", - "category": "womens-jewellery", - "thumbnail": "https://i.dummyjson.com/data/products/76/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/76/1.jpg", - "https://i.dummyjson.com/data/products/76/2.jpg", - "https://i.dummyjson.com/data/products/76/thumbnail.jpg" - ] - }, - { - "id": 77, - "title": "Rose Ring", - "description": "Brand: The Greetings Flower Colour: RedRing Colour: GoldenSize: Adjustable", - "price": 100, - "discountPercentage": 3.22, - "rating": 4.21, - "stock": 149, - "brand": "Copenhagen Luxe", - "category": "womens-jewellery", - "thumbnail": "https://i.dummyjson.com/data/products/77/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/77/1.jpg", - "https://i.dummyjson.com/data/products/77/2.jpg", - "https://i.dummyjson.com/data/products/77/3.jpg", - "https://i.dummyjson.com/data/products/77/thumbnail.jpg" - ] - }, - { - "id": 78, - "title": "Rhinestone Korean Style Open Rings", - "description": "Fashion Jewellery 3Pcs Adjustable Pearl Rhinestone Korean Style Open Rings For Women", - "price": 30, - "discountPercentage": 8.02, - "rating": 4.69, - "stock": 9, - "brand": "Fashion Jewellery", - "category": "womens-jewellery", - "thumbnail": "https://i.dummyjson.com/data/products/78/thumbnail.jpg", - "images": ["https://i.dummyjson.com/data/products/78/thumbnail.jpg"] - }, - { - "id": 79, - "title": "Elegant Female Pearl Earrings", - "description": "Elegant Female Pearl Earrings Set Zircon Pearl Earings Women Party Accessories 9 Pairs/Set", - "price": 30, - "discountPercentage": 12.8, - "rating": 4.74, - "stock": 16, - "brand": "Fashion Jewellery", - "category": "womens-jewellery", - "thumbnail": "https://i.dummyjson.com/data/products/79/thumbnail.jpg", - "images": ["https://i.dummyjson.com/data/products/79/1.jpg"] - }, - { - "id": 80, - "title": "Chain Pin Tassel Earrings", - "description": "Pair Of Ear Cuff Butterfly Long Chain Pin Tassel Earrings - Silver ( Long Life Quality Product)", - "price": 45, - "discountPercentage": 17.75, - "rating": 4.59, - "stock": 9, - "brand": "Cuff Butterfly", - "category": "womens-jewellery", - "thumbnail": "https://i.dummyjson.com/data/products/80/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/80/1.jpg", - "https://i.dummyjson.com/data/products/80/2.jpg", - "https://i.dummyjson.com/data/products/80/3.png", - "https://i.dummyjson.com/data/products/80/4.jpg", - "https://i.dummyjson.com/data/products/80/thumbnail.jpg" - ] - }, - { - "id": 81, - "title": "Round Silver Frame Sun Glasses", - "description": "A pair of sunglasses can protect your eyes from being hurt. For car driving, vacation travel, outdoor activities, social gatherings,", - "price": 19, - "discountPercentage": 10.1, - "rating": 4.94, - "stock": 78, - "brand": "Designer Sun Glasses", - "category": "sunglasses", - "thumbnail": "https://i.dummyjson.com/data/products/81/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/81/1.jpg", - "https://i.dummyjson.com/data/products/81/2.jpg", - "https://i.dummyjson.com/data/products/81/3.jpg", - "https://i.dummyjson.com/data/products/81/4.webp", - "https://i.dummyjson.com/data/products/81/thumbnail.jpg" - ] - }, - { - "id": 82, - "title": "Kabir Singh Square Sunglass", - "description": "Orignal Metal Kabir Singh design 2020 Sunglasses Men Brand Designer Sun Glasses Kabir Singh Square Sunglass", - "price": 50, - "discountPercentage": 15.6, - "rating": 4.62, - "stock": 78, - "brand": "Designer Sun Glasses", - "category": "sunglasses", - "thumbnail": "https://i.dummyjson.com/data/products/82/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/82/1.jpg", - "https://i.dummyjson.com/data/products/82/2.webp", - "https://i.dummyjson.com/data/products/82/3.jpg", - "https://i.dummyjson.com/data/products/82/4.jpg", - "https://i.dummyjson.com/data/products/82/thumbnail.jpg" - ] - }, - { - "id": 83, - "title": "Wiley X Night Vision Yellow Glasses", - "description": "Wiley X Night Vision Yellow Glasses for Riders - Night Vision Anti Fog Driving Glasses - Free Night Glass Cover - Shield Eyes From Dust and Virus- For Night Sport Matches", - "price": 30, - "discountPercentage": 6.33, - "rating": 4.97, - "stock": 115, - "brand": "mastar watch", - "category": "sunglasses", - "thumbnail": "https://i.dummyjson.com/data/products/83/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/83/1.jpg", - "https://i.dummyjson.com/data/products/83/2.jpg", - "https://i.dummyjson.com/data/products/83/3.jpg", - "https://i.dummyjson.com/data/products/83/4.jpg", - "https://i.dummyjson.com/data/products/83/thumbnail.jpg" - ] - }, - { - "id": 84, - "title": "Square Sunglasses", - "description": "Fashion Oversized Square Sunglasses Retro Gradient Big Frame Sunglasses For Women One Piece Gafas Shade Mirror Clear Lens 17059", - "price": 28, - "discountPercentage": 13.89, - "rating": 4.64, - "stock": 64, - "brand": "mastar watch", - "category": "sunglasses", - "thumbnail": "https://i.dummyjson.com/data/products/84/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/84/1.jpg", - "https://i.dummyjson.com/data/products/84/2.jpg", - "https://i.dummyjson.com/data/products/84/thumbnail.jpg" - ] - }, - { - "id": 85, - "title": "LouisWill Men Sunglasses", - "description": "LouisWill Men Sunglasses Polarized Sunglasses UV400 Sunglasses Day Night Dual Use Safety Driving Night Vision Eyewear AL-MG Frame Sun Glasses with Free Box for Drivers", - "price": 50, - "discountPercentage": 11.27, - "rating": 4.98, - "stock": 92, - "brand": "LouisWill", - "category": "sunglasses", - "thumbnail": "https://i.dummyjson.com/data/products/85/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/85/1.jpg", - "https://i.dummyjson.com/data/products/85/2.jpg", - "https://i.dummyjson.com/data/products/85/thumbnail.jpg" - ] - }, - { - "id": 86, - "title": "Bluetooth Aux", - "description": "Bluetooth Aux Bluetooth Car Aux Car Bluetooth Transmitter Aux Audio Receiver Handfree Car Bluetooth Music Receiver Universal 3.5mm Streaming A2DP Wireless Auto AUX Audio Adapter With Mic For Phone MP3", - "price": 25, - "discountPercentage": 10.56, - "rating": 4.57, - "stock": 22, - "brand": "Car Aux", - "category": "automotive", - "thumbnail": "https://i.dummyjson.com/data/products/86/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/86/1.jpg", - "https://i.dummyjson.com/data/products/86/2.webp", - "https://i.dummyjson.com/data/products/86/3.jpg", - "https://i.dummyjson.com/data/products/86/4.jpg", - "https://i.dummyjson.com/data/products/86/thumbnail.jpg" - ] - }, - { - "id": 87, - "title": "t Temperature Controller Incubator Controller", - "description": "Both Heat and Cool Purpose, Temperature control range; -50 to +110, Temperature measurement accuracy; 0.1, Control accuracy; 0.1", - "price": 40, - "discountPercentage": 11.3, - "rating": 4.54, - "stock": 37, - "brand": "W1209 DC12V", - "category": "automotive", - "thumbnail": "https://i.dummyjson.com/data/products/87/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/87/1.jpg", - "https://i.dummyjson.com/data/products/87/2.jpg", - "https://i.dummyjson.com/data/products/87/3.jpg", - "https://i.dummyjson.com/data/products/87/4.jpg", - "https://i.dummyjson.com/data/products/87/thumbnail.jpg" - ] - }, - { - "id": 88, - "title": "TC Reusable Silicone Magic Washing Gloves", - "description": "TC Reusable Silicone Magic Washing Gloves with Scrubber, Cleaning Brush Scrubber Gloves Heat Resistant Pair for Cleaning of Kitchen, Dishes, Vegetables and Fruits, Bathroom, Car Wash, Pet Care and Multipurpose", - "price": 29, - "discountPercentage": 3.19, - "rating": 4.98, - "stock": 42, - "brand": "TC Reusable", - "category": "automotive", - "thumbnail": "https://i.dummyjson.com/data/products/88/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/88/1.jpg", - "https://i.dummyjson.com/data/products/88/2.jpg", - "https://i.dummyjson.com/data/products/88/3.jpg", - "https://i.dummyjson.com/data/products/88/4.webp", - "https://i.dummyjson.com/data/products/88/thumbnail.jpg" - ] - }, - { - "id": 89, - "title": "Qualcomm original Car Charger", - "description": "best Quality CHarger , Highly Recommended to all best Quality CHarger , Highly Recommended to all", - "price": 40, - "discountPercentage": 17.53, - "rating": 4.2, - "stock": 79, - "brand": "TC Reusable", - "category": "automotive", - "thumbnail": "https://i.dummyjson.com/data/products/89/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/89/1.jpg", - "https://i.dummyjson.com/data/products/89/2.jpg", - "https://i.dummyjson.com/data/products/89/3.jpg", - "https://i.dummyjson.com/data/products/89/4.jpg", - "https://i.dummyjson.com/data/products/89/thumbnail.jpg" - ] - }, - { - "id": 90, - "title": "Cycle Bike Glow", - "description": "Universal fitment and easy to install no special wires, can be easily installed and removed. Fits most standard tyre air stem valves of road, mountain bicycles, motocycles and cars.Bright led will turn on w", - "price": 35, - "discountPercentage": 11.08, - "rating": 4.1, - "stock": 63, - "brand": "Neon LED Light", - "category": "automotive", - "thumbnail": "https://i.dummyjson.com/data/products/90/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/90/1.jpg", - "https://i.dummyjson.com/data/products/90/2.jpg", - "https://i.dummyjson.com/data/products/90/3.jpg", - "https://i.dummyjson.com/data/products/90/4.jpg", - "https://i.dummyjson.com/data/products/90/thumbnail.jpg" - ] - }, - { - "id": 91, - "title": "Black Motorbike", - "description": "Engine Type:Wet sump, Single Cylinder, Four Stroke, Two Valves, Air Cooled with SOHC (Single Over Head Cam) Chain Drive Bore & Stroke:47.0 x 49.5 MM", - "price": 569, - "discountPercentage": 13.63, - "rating": 4.04, - "stock": 115, - "brand": "METRO 70cc Motorcycle - MR70", - "category": "motorcycle", - "thumbnail": "https://i.dummyjson.com/data/products/91/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/91/1.jpg", - "https://i.dummyjson.com/data/products/91/2.jpg", - "https://i.dummyjson.com/data/products/91/3.jpg", - "https://i.dummyjson.com/data/products/91/4.jpg", - "https://i.dummyjson.com/data/products/91/thumbnail.jpg" - ] - }, - { - "id": 92, - "title": "HOT SALE IN EUROPE electric racing motorcycle", - "description": "HOT SALE IN EUROPE electric racing motorcycle electric motorcycle for sale adult electric motorcycles", - "price": 920, - "discountPercentage": 14.4, - "rating": 4.19, - "stock": 22, - "brand": "BRAVE BULL", - "category": "motorcycle", - "thumbnail": "https://i.dummyjson.com/data/products/92/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/92/1.jpg", - "https://i.dummyjson.com/data/products/92/2.jpg", - "https://i.dummyjson.com/data/products/92/3.jpg", - "https://i.dummyjson.com/data/products/92/4.jpg" - ] - }, - { - "id": 93, - "title": "Automatic Motor Gas Motorcycles", - "description": "150cc 4-Stroke Motorcycle Automatic Motor Gas Motorcycles Scooter motorcycles 150cc scooter", - "price": 1050, - "discountPercentage": 3.34, - "rating": 4.84, - "stock": 127, - "brand": "shock absorber", - "category": "motorcycle", - "thumbnail": "https://i.dummyjson.com/data/products/93/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/93/1.jpg", - "https://i.dummyjson.com/data/products/93/2.jpg", - "https://i.dummyjson.com/data/products/93/3.jpg", - "https://i.dummyjson.com/data/products/93/4.jpg", - "https://i.dummyjson.com/data/products/93/thumbnail.jpg" - ] - }, - { - "id": 94, - "title": "new arrivals Fashion motocross goggles", - "description": "new arrivals Fashion motocross goggles motorcycle motocross racing motorcycle", - "price": 900, - "discountPercentage": 3.85, - "rating": 4.06, - "stock": 109, - "brand": "JIEPOLLY", - "category": "motorcycle", - "thumbnail": "https://i.dummyjson.com/data/products/94/thumbnail.webp", - "images": [ - "https://i.dummyjson.com/data/products/94/1.webp", - "https://i.dummyjson.com/data/products/94/2.jpg", - "https://i.dummyjson.com/data/products/94/3.jpg", - "https://i.dummyjson.com/data/products/94/thumbnail.webp" - ] - }, - { - "id": 95, - "title": "Wholesale cargo lashing Belt", - "description": "Wholesale cargo lashing Belt Tie Down end Ratchet strap customized strap 25mm motorcycle 1500kgs with rubber handle", - "price": 930, - "discountPercentage": 17.67, - "rating": 4.21, - "stock": 144, - "brand": "Xiangle", - "category": "motorcycle", - "thumbnail": "https://i.dummyjson.com/data/products/95/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/95/1.jpg", - "https://i.dummyjson.com/data/products/95/2.jpg", - "https://i.dummyjson.com/data/products/95/3.jpg", - "https://i.dummyjson.com/data/products/95/4.jpg", - "https://i.dummyjson.com/data/products/95/thumbnail.jpg" - ] - }, - { - "id": 96, - "title": "lighting ceiling kitchen", - "description": "Wholesale slim hanging decorative kid room lighting ceiling kitchen chandeliers pendant light modern", - "price": 30, - "discountPercentage": 14.89, - "rating": 4.83, - "stock": 96, - "brand": "lightingbrilliance", - "category": "lighting", - "thumbnail": "https://i.dummyjson.com/data/products/96/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/96/1.jpg", - "https://i.dummyjson.com/data/products/96/2.jpg", - "https://i.dummyjson.com/data/products/96/3.jpg", - "https://i.dummyjson.com/data/products/96/4.jpg", - "https://i.dummyjson.com/data/products/96/thumbnail.jpg" - ] - }, - { - "id": 97, - "title": "Metal Ceramic Flower", - "description": "Metal Ceramic Flower Chandelier Home Lighting American Vintage Hanging Lighting Pendant Lamp", - "price": 35, - "discountPercentage": 10.94, - "rating": 4.93, - "stock": 146, - "brand": "Ifei Home", - "category": "lighting", - "thumbnail": "https://i.dummyjson.com/data/products/97/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/97/1.jpg", - "https://i.dummyjson.com/data/products/97/2.jpg", - "https://i.dummyjson.com/data/products/97/3.jpg", - "https://i.dummyjson.com/data/products/97/4.webp", - "https://i.dummyjson.com/data/products/97/thumbnail.jpg" - ] - }, - { - "id": 98, - "title": "3 lights lndenpant kitchen islang", - "description": "3 lights lndenpant kitchen islang dining room pendant rice paper chandelier contemporary led pendant light modern chandelier", - "price": 34, - "discountPercentage": 5.92, - "rating": 4.99, - "stock": 44, - "brand": "DADAWU", - "category": "lighting", - "thumbnail": "https://i.dummyjson.com/data/products/98/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/98/1.jpg", - "https://i.dummyjson.com/data/products/98/2.jpg", - "https://i.dummyjson.com/data/products/98/3.jpg", - "https://i.dummyjson.com/data/products/98/4.jpg", - "https://i.dummyjson.com/data/products/98/thumbnail.jpg" - ] - }, - { - "id": 99, - "title": "American Vintage Wood Pendant Light", - "description": "American Vintage Wood Pendant Light Farmhouse Antique Hanging Lamp Lampara Colgante", - "price": 46, - "discountPercentage": 8.84, - "rating": 4.32, - "stock": 138, - "brand": "Ifei Home", - "category": "lighting", - "thumbnail": "https://i.dummyjson.com/data/products/99/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/99/1.jpg", - "https://i.dummyjson.com/data/products/99/2.jpg", - "https://i.dummyjson.com/data/products/99/3.jpg", - "https://i.dummyjson.com/data/products/99/4.jpg", - "https://i.dummyjson.com/data/products/99/thumbnail.jpg" - ] - }, - { - "id": 100, - "title": "Crystal chandelier maria theresa for 12 light", - "description": "Crystal chandelier maria theresa for 12 light", - "price": 47, - "discountPercentage": 16, - "rating": 4.74, - "stock": 133, - "brand": "YIOSI", - "category": "lighting", - "thumbnail": "https://i.dummyjson.com/data/products/100/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/100/1.jpg", - "https://i.dummyjson.com/data/products/100/2.jpg", - "https://i.dummyjson.com/data/products/100/3.jpg", - "https://i.dummyjson.com/data/products/100/thumbnail.jpg" - ] - }, - { - "title": "Mac Book Air ", - "description": "Mac Book Air ", - "brand": "Apple", - "category": "laptops", - "price": 1111, - "discountPercentage": 11, - "stock": 22, - "thumbnail": "https://i.dummyjson.com/data/products/6/thumbnail.png", - "images": [ - "https://i.dummyjson.com/data/products/6/thumbnail.png", - "https://i.dummyjson.com/data/products/6/thumbnail.png", - "https://i.dummyjson.com/data/products/6/thumbnail.png", - "https://i.dummyjson.com/data/products/6/thumbnail.png" - ], - "id": 101 - } - ], - "brands": [ - { - "value": "Apple", - "label": "Apple", - "checked": false - }, - { - "value": "Samsung", - "label": "Samsung", - "checked": false - }, - { - "value": "OPPO", - "label": "OPPO", - "checked": false - }, - { - "value": "Huawei", - "label": "Huawei", - "checked": false - }, - { - "value": "Microsoft Surface", - "label": "Microsoft Surface", - "checked": false - }, - { - "value": "Infinix", - "label": "Infinix", - "checked": false - }, - { - "value": "HP Pavilion", - "label": "HP Pavilion", - "checked": false - }, - { - "value": "Impression of Acqua Di Gio", - "label": "Impression of Acqua Di Gio", - "checked": false - }, - { - "value": "Royal_Mirage", - "label": "Royal_Mirage", - "checked": false - }, - { - "value": "Fog Scent Xpressio", - "label": "Fog Scent Xpressio", - "checked": false - }, - { - "value": "Al Munakh", - "label": "Al Munakh", - "checked": false - }, - { - "value": "Lord - Al-Rehab", - "label": "Lord Al Rehab", - "checked": false - }, - { - "value": "L'Oreal Paris", - "label": "L'Oreal Paris", - "checked": false - }, - { - "value": "Hemani Tea", - "label": "Hemani Tea", - "checked": false - }, - { - "value": "Dermive", - "label": "Dermive", - "checked": false - }, - { - "value": "ROREC White Rice", - "label": "ROREC White Rice", - "checked": false - }, - { - "value": "Fair & Clear", - "label": "Fair & Clear", - "checked": false - }, - { - "value": "Saaf & Khaas", - "label": "Saaf & Khaas", - "checked": false - }, - { - "value": "Bake Parlor Big", - "label": "Bake Parlor Big", - "checked": false - }, - { - "value": "Baking Food Items", - "label": "Baking Food Items", - "checked": false - }, - { - "value": "fauji", - "label": "fauji", - "checked": false - }, - { - "value": "Dry Rose", - "label": "Dry Rose", - "checked": false - }, - { - "value": "Boho Decor", - "label": "Boho Decor", - "checked": false - }, - { - "value": "Flying Wooden", - "label": "Flying Wooden", - "checked": false - }, - { - "value": "LED Lights", - "label": "LED Lights", - "checked": false - }, - { - "value": "luxury palace", - "label": "luxury palace", - "checked": false - }, - { - "value": "Golden", - "label": "Golden", - "checked": false - }, - { - "value": "Furniture Bed Set", - "label": "Furniture Bed Set", - "checked": false - }, - { - "value": "Ratttan Outdoor", - "label": "Ratttan Outdoor", - "checked": false - }, - { - "value": "Kitchen Shelf", - "label": "Kitchen Shelf", - "checked": false - }, - { - "value": "Multi Purpose", - "label": "Multi Purpose", - "checked": false - }, - { - "value": "AmnaMart", - "label": "AmnaMart", - "checked": false - }, - { - "value": "Professional Wear", - "label": "Professional Wear", - "checked": false - }, - { - "value": "Soft Cotton", - "label": "Soft Cotton", - "checked": false - }, - { - "value": "Top Sweater", - "label": "Top Sweater", - "checked": false - }, - { - "value": "RED MICKY MOUSE..", - "label": "RED MICKY MOUSE..", - "checked": false - }, - { - "value": "Digital Printed", - "label": "Digital Printed", - "checked": false - }, - { - "value": "Ghazi Fabric", - "label": "Ghazi Fabric", - "checked": false - }, - { - "value": "IELGY", - "label": "IELGY", - "checked": false - }, - { - "value": "IELGY fashion", - "label": "IELGY fashion", - "checked": false - }, - { - "value": "Synthetic Leather", - "label": "Synthetic Leather", - "checked": false - }, - { - "value": "Sandals Flip Flops", - "label": "Sandals Flip Flops", - "checked": false - }, - { - "value": "Maasai Sandals", - "label": "Maasai Sandals", - "checked": false - }, - { - "value": "Arrivals Genuine", - "label": "Arrivals Genuine", - "checked": false - }, - { - "value": "Vintage Apparel", - "label": "Vintage Apparel", - "checked": false - }, - { - "value": "FREE FIRE", - "label": "FREE FIRE", - "checked": false - }, - { - "value": "The Warehouse", - "label": "The Warehouse", - "checked": false - }, - { - "value": "Sneakers", - "label": "Sneakers", - "checked": false - }, - { - "value": "Rubber", - "label": "Rubber", - "checked": false - }, - { - "value": "Naviforce", - "label": "Naviforce", - "checked": false - }, - { - "value": "SKMEI 9117", - "label": "SKMEI 9117", - "checked": false - }, - { - "value": "Strap Skeleton", - "label": "Strap Skeleton", - "checked": false - }, - { - "value": "Stainless", - "label": "Stainless", - "checked": false - }, - { - "value": "Eastern Watches", - "label": "Eastern Watches", - "checked": false - }, - { - "value": "Luxury Digital", - "label": "Luxury Digital", - "checked": false - }, - { - "value": "Watch Pearls", - "label": "Watch Pearls", - "checked": false - }, - { - "value": "Bracelet", - "label": "Bracelet", - "checked": false - }, - { - "value": "LouisWill", - "label": "LouisWill", - "checked": false - }, - { - "value": "Copenhagen Luxe", - "label": "Copenhagen Luxe", - "checked": false - }, - { - "value": "Steal Frame", - "label": "Steal Frame", - "checked": false - }, - { - "value": "Darojay", - "label": "Darojay", - "checked": false - }, - { - "value": "Fashion Jewellery", - "label": "Fashion Jewellery", - "checked": false - }, - { - "value": "Cuff Butterfly", - "label": "Cuff Butterfly", - "checked": false - }, - { - "value": "Designer Sun Glasses", - "label": "Designer Sun Glasses", - "checked": false - }, - { - "value": "mastar watch", - "label": "mastar watch", - "checked": false - }, - { - "value": "Car Aux", - "label": "Car Aux", - "checked": false - }, - { - "value": "W1209 DC12V", - "label": "W1209 DC12V", - "checked": false - }, - { - "value": "TC Reusable", - "label": "TC Reusable", - "checked": false - }, - { - "value": "Neon LED Light", - "label": "Neon LED Light", - "checked": false - }, - { - "value": "METRO 70cc Motorcycle - MR70", - "label": "METRO 70cc Motorcycle MR70", - "checked": false - }, - { - "value": "BRAVE BULL", - "label": "BRAVE BULL", - "checked": false - }, - { - "value": "shock absorber", - "label": "shock absorber", - "checked": false - }, - { - "value": "JIEPOLLY", - "label": "JIEPOLLY", - "checked": false - }, - { - "value": "Xiangle", - "label": "Xiangle", - "checked": false - }, - { - "value": "lightingbrilliance", - "label": "lightingbrilliance", - "checked": false - }, - { - "value": "Ifei Home", - "label": "Ifei Home", - "checked": false - }, - { - "value": "DADAWU", - "label": "DADAWU", - "checked": false - }, - { - "value": "YIOSI", - "label": "YIOSI", - "checked": false - } - ], - "categories": [ - { - "value": "smartphones", - "label": "smartphones", - "checked": false - }, - { - "value": "laptops", - "label": "laptops", - "checked": false - }, - { - "value": "fragrances", - "label": "fragrances", - "checked": false - }, - { - "value": "skincare", - "label": "skincare", - "checked": false - }, - { - "value": "groceries", - "label": "groceries", - "checked": false - }, - { - "value": "home-decoration", - "label": "home decoration", - "checked": false - }, - { - "value": "furniture", - "label": "furniture", - "checked": false - }, - { - "value": "tops", - "label": "tops", - "checked": false - }, - { - "value": "womens-dresses", - "label": "womens dresses", - "checked": false - }, - { - "value": "womens-shoes", - "label": "womens shoes", - "checked": false - }, - { - "value": "mens-shirts", - "label": "mens shirts", - "checked": false - }, - { - "value": "mens-shoes", - "label": "mens shoes", - "checked": false - }, - { - "value": "mens-watches", - "label": "mens watches", - "checked": false - }, - { - "value": "womens-watches", - "label": "womens watches", - "checked": false - }, - { - "value": "womens-bags", - "label": "womens bags", - "checked": false - }, - { - "value": "womens-jewellery", - "label": "womens jewellery", - "checked": false - }, - { - "value": "sunglasses", - "label": "sunglasses", - "checked": false - }, - { - "value": "automotive", - "label": "automotive", - "checked": false - }, - { - "value": "motorcycle", - "label": "motorcycle", - "checked": false - }, - { - "value": "lighting", - "label": "lighting", - "checked": false - } - ], - "users": [ - { - "email": "test@gmail.com", - "password": "Qwerty123", - "role": "user", - "addresses": [ - { - "name": "Abhishek Rathore", - "email": "test@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Banaglore", - "state": "Karnataka", - "pinCode": "560034" + "price": 10.99, + "discountPercentage": 18.15, + "rating": 2.74, + "stock": 40, + "tags": ["pet supplies", "dog food"], + "sku": "A6QRCH37", + "weight": 2, + "dimensions": { + "width": 29.39, + "height": 29.77, + "depth": 20.54 + }, + "warrantyInformation": "1 year warranty", + "shippingInformation": "Ships in 1 month", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Highly impressed!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Leo Rivera", + "reviewerEmail": "leo.rivera@x.dummyjson.com" }, { - "name": "Jack Morris", - "email": "jack@gmail.com", - "city": "Delhi", - "state": "Delhi", - "pinCode": "110006", - "phone": "12312331232", - "street": "12th cross" - } - ], - "id": 1 - }, - { - "email": "demo@gmail.com", - "password": "Qwerty123", - "role": "user", - "addresses": [ - { - "name": "Demo user", - "email": "demo@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Mumbai", - "state": "Maharastra", - "pinCode": "220001" + "rating": 4, + "comment": "Highly recommended!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Alexander Jones", + "reviewerEmail": "alexander.jones@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Would buy again!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Addison Wright", + "reviewerEmail": "addison.wright@x.dummyjson.com" } ], - "id": 2 - }, - { - "email": "admin@gmail.com", - "password": "Qwerty123", - "addresses": [], - "role": "admin", - "id": 3 - } - ], - "cart": [ - { - "title": "Huawei P30", - "description": "Huawei’s re-badged P30 Pro New Edition was officially unveiled yesterday in Germany and now the device has made its way to the UK.", - "price": 499, - "discountPercentage": 10.58, - "rating": 4.09, - "stock": 32, - "brand": "Huawei", - "category": "smartphones", - "thumbnail": "https://i.dummyjson.com/data/products/5/thumbnail.jpg", + "returnPolicy": "90 days return policy", + "minimumOrderQuantity": 29, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "7957222289508", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, "images": [ - "https://i.dummyjson.com/data/products/5/1.jpg", - "https://i.dummyjson.com/data/products/5/2.jpg", - "https://i.dummyjson.com/data/products/5/3.jpg" - ], - "productId": 5, - "quantity": 1, - "user": 2, - "id": 1 - } - ], - "orders": [ - { - "items": [ - { - "title": "iPhone X", - "description": "SIM-Free, Model A19211 6.5-inch Super Retina HD display with OLED technology A12 Bionic chip with ...", - "price": 899, - "discountPercentage": 17.94, - "rating": 4.44, - "stock": 34, - "brand": "Apple", - "category": "smartphones", - "thumbnail": "https://i.dummyjson.com/data/products/2/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/2/1.jpg", - "https://i.dummyjson.com/data/products/2/2.jpg", - "https://i.dummyjson.com/data/products/2/3.jpg", - "https://i.dummyjson.com/data/products/2/thumbnail.jpg" - ], - "quantity": 2, - "user": 1, - "id": 6 - } + "https://cdn.dummyjson.com/products/images/groceries/Dog%20Food/1.png" ], - "totalAmount": 1798, - "totalItems": 2, - "user": { - "email": "test@gmail.com", - "password": "Qwerty123", - "addresses": [ - { - "name": "Abhishek R", - "email": "test@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Banaglore", - "state": "Karnataka", - "pinCode": "560034" - }, - { - "name": "Abhishek R", - "email": "test@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Banaglore", - "state": "Karnataka", - "pinCode": "560034" - } - ], - "id": 1 - }, - "paymentMethod": "cash", - "selectedAddress": { - "name": "Abhishek R", - "email": "test@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Banaglore", - "state": "Karnataka", - "pinCode": "560034" - }, - "status": "dispatched", - "id": 1 + "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Dog%20Food/thumbnail.png" }, { - "items": [ - { - "title": "iPhone X", - "description": "SIM-Free, Model A19211 6.5-inch Super Retina HD display with OLED technology A12 Bionic chip with ...", - "price": 899, - "discountPercentage": 17.94, - "rating": 4.44, - "stock": 34, - "brand": "Apple", - "category": "smartphones", - "thumbnail": "https://i.dummyjson.com/data/products/2/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/2/1.jpg", - "https://i.dummyjson.com/data/products/2/2.jpg", - "https://i.dummyjson.com/data/products/2/3.jpg", - "https://i.dummyjson.com/data/products/2/thumbnail.jpg" - ], - "quantity": 2, - "user": 1, - "id": 6 - } - ], - "totalAmount": 1798, - "totalItems": 2, - "user": { - "email": "test@gmail.com", - "password": "Qwerty123", - "addresses": [ - { - "name": "Abhishek R", - "email": "test@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Banaglore", - "state": "Karnataka", - "pinCode": "560034" - }, - { - "name": "Abhishek R", - "email": "test@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Banaglore", - "state": "Karnataka", - "pinCode": "560034" - } - ], - "id": 1 - }, - "paymentMethod": "cash", - "selectedAddress": { - "name": "Abhishek R", - "email": "test@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Banaglore", - "state": "Karnataka", - "pinCode": "560034" + "id": 23, + "title": "Eggs", + "description": "Fresh eggs, a versatile ingredient for baking, cooking, or breakfast.", + "category": "groceries", + "price": 2.99, + "discountPercentage": 5.8, + "rating": 4.46, + "stock": 10, + "tags": ["dairy"], + "sku": "YA617RI7", + "weight": 4, + "dimensions": { + "width": 12.3, + "height": 10.99, + "depth": 15.53 }, - "status": "delivered", - "id": 2 - }, - { - "items": [ - { - "id": 3, - "title": "Samsung Universe 9", - "description": "Samsung's new variant which goes beyond Galaxy to the Universe", - "price": 1249, - "discountPercentage": 15.46, - "rating": 4.09, - "stock": 36, - "brand": "Samsung", - "category": "smartphones", - "thumbnail": "https://i.dummyjson.com/data/products/3/thumbnail.jpg", - "images": ["https://i.dummyjson.com/data/products/3/1.jpg"], - "quantity": 2, - "user": 2 + "warrantyInformation": "3 year warranty", + "shippingInformation": "Ships overnight", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 2, + "comment": "Very unhappy with my purchase!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Mateo Perez", + "reviewerEmail": "mateo.perez@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Very happy with my purchase!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Cameron Perez", + "reviewerEmail": "cameron.perez@x.dummyjson.com" }, { - "id": 5, - "title": "Huawei P30", - "description": "Huawei’s re-badged P30 Pro New Edition was officially unveiled yesterday in Germany and now the device has made its way to the UK.", - "price": 499, - "discountPercentage": 10.58, - "rating": 4.09, - "stock": 32, - "brand": "Huawei", - "category": "smartphones", - "thumbnail": "https://i.dummyjson.com/data/products/5/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/5/1.jpg", - "https://i.dummyjson.com/data/products/5/2.jpg", - "https://i.dummyjson.com/data/products/5/3.jpg" - ], - "quantity": 1, - "user": 2 + "rating": 5, + "comment": "Very happy with my purchase!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Aurora Barnes", + "reviewerEmail": "aurora.barnes@x.dummyjson.com" } ], - "totalAmount": 2997, - "totalItems": 3, - "user": { - "email": "demo@gmail.com", - "password": "Qwerty123", - "addresses": [ - { - "name": "Demo user", - "email": "demo@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Mumbai", - "state": "Maharastra", - "pinCode": "220001" - } - ], - "id": 2 + "returnPolicy": "30 days return policy", + "minimumOrderQuantity": 43, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "7095702028776", + "qrCode": "https://dummyjson.com/public/qr-code.png" }, - "paymentMethod": "card", - "selectedAddress": { - "name": "Demo user", - "email": "demo@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Mumbai", - "state": "Maharastra", - "pinCode": "220001" - }, - "status": "cancelled", - "id": 3 - }, - { - "items": [ - { - "title": "iPhone 9", - "description": "An apple mobile which is nothing like apple", - "price": 549, - "discountPercentage": 12.96, - "rating": 4.69, - "stock": 94, - "brand": "Apple", - "category": "smartphones", - "thumbnail": "https://i.dummyjson.com/data/products/1/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/1/1.jpg", - "https://i.dummyjson.com/data/products/1/2.jpg", - "https://i.dummyjson.com/data/products/1/3.jpg", - "https://i.dummyjson.com/data/products/1/4.jpg", - "https://i.dummyjson.com/data/products/1/thumbnail.jpg" - ], - "quantity": 2, - "user": 1, - "id": 1 - } + "images": [ + "https://cdn.dummyjson.com/products/images/groceries/Eggs/1.png" ], - "totalAmount": 1098, - "totalItems": 2, - "user": { - "email": "test@gmail.com", - "password": "Qwerty123", - "addresses": [ - { - "name": "Abhishek R", - "email": "test@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Banaglore", - "state": "Karnataka", - "pinCode": "560034" - }, - { - "name": "Abhishek R", - "email": "test@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Banaglore", - "state": "Karnataka", - "pinCode": "560034" - } - ], - "id": 1 - }, - "paymentMethod": "cash", - "selectedAddress": { - "name": "Abhishek R", - "email": "test@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Banaglore", - "state": "Karnataka", - "pinCode": "560034" - }, - "status": "pending", - "id": 4 + "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Eggs/thumbnail.png" }, { - "items": [ - { - "title": "iPhone X", - "description": "SIM-Free, Model A19211 6.5-inch Super Retina HD display with OLED technology A12 Bionic chip with ...", - "price": 899, - "discountPercentage": 17.94, - "rating": 4.44, - "stock": 34, - "brand": "Apple", - "category": "smartphones", - "thumbnail": "https://i.dummyjson.com/data/products/2/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/2/1.jpg", - "https://i.dummyjson.com/data/products/2/2.jpg", - "https://i.dummyjson.com/data/products/2/3.jpg", - "https://i.dummyjson.com/data/products/2/thumbnail.jpg" - ], - "quantity": 1, - "user": 1, - "id": 1 + "id": 24, + "title": "Fish Steak", + "description": "Quality fish steak, suitable for grilling, baking, or pan-searing.", + "category": "groceries", + "price": 14.99, + "discountPercentage": 7, + "rating": 4.83, + "stock": 99, + "tags": ["seafood"], + "sku": "XNIH1MTA", + "weight": 8, + "dimensions": { + "width": 20.14, + "height": 8.4, + "depth": 10.01 + }, + "warrantyInformation": "1 year warranty", + "shippingInformation": "Ships in 1 month", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Great value for money!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Michael Johnson", + "reviewerEmail": "michael.johnson@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Would buy again!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Julian Newton", + "reviewerEmail": "julian.newton@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Excellent quality!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Lila Hudson", + "reviewerEmail": "lila.hudson@x.dummyjson.com" } ], - "totalAmount": 899, - "totalItems": 1, - "user": { - "email": "test@gmail.com", - "password": "Qwerty123", - "addresses": [ - { - "name": "Abhishek Rathore", - "email": "test@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Banaglore", - "state": "Karnataka", - "pinCode": "560034" - }, - { - "name": "Jack Morris", - "email": "jack@gmail.com", - "city": "Delhi", - "state": "Delhi", - "pinCode": "110006", - "phone": "12312331232", - "street": "12th cross" - } - ], - "id": 1 + "returnPolicy": "30 days return policy", + "minimumOrderQuantity": 49, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "4250692197342", + "qrCode": "https://dummyjson.com/public/qr-code.png" }, - "paymentMethod": "cash", - "selectedAddress": { - "name": "Abhishek Rathore", - "email": "test@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Banaglore", - "state": "Karnataka", - "pinCode": "560034" - }, - "status": "pending", - "id": 5 + "images": [ + "https://cdn.dummyjson.com/products/images/groceries/Fish%20Steak/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Fish%20Steak/thumbnail.png" }, { - "items": [ - { - "title": "iPhone X", - "description": "SIM-Free, Model A19211 6.5-inch Super Retina HD display with OLED technology A12 Bionic chip with ...", - "price": 1111, - "discountPercentage": 17.94, - "rating": 0, - "stock": 34, - "brand": "Apple", - "category": "smartphones", - "thumbnail": "https://i.dummyjson.com/data/products/2/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/2/1.jpg", - "https://i.dummyjson.com/data/products/2/2.jpg", - "https://i.dummyjson.com/data/products/2/3.jpg", - "https://i.dummyjson.com/data/products/2/thumbnail.jpg" - ], - "quantity": 2, - "user": 1, - "id": 1 + "id": 25, + "title": "Green Bell Pepper", + "description": "Fresh and vibrant green bell pepper, perfect for adding color and flavor to your dishes.", + "category": "groceries", + "price": 1.29, + "discountPercentage": 15.5, + "rating": 4.28, + "stock": 89, + "tags": ["vegetables"], + "sku": "HU7S7VQ0", + "weight": 7, + "dimensions": { + "width": 7.32, + "height": 14.31, + "depth": 21.38 + }, + "warrantyInformation": "5 year warranty", + "shippingInformation": "Ships overnight", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 4, + "comment": "Excellent quality!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Maya Reed", + "reviewerEmail": "maya.reed@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Would buy again!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Madison Collins", + "reviewerEmail": "madison.collins@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Would buy again!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Ethan Thompson", + "reviewerEmail": "ethan.thompson@x.dummyjson.com" } ], - "totalAmount": 1824, - "totalItems": 2, - "user": { - "email": "test@gmail.com", - "password": "Qwerty123", - "role": "user", - "addresses": [ - { - "name": "Abhishek Rathore", - "email": "test@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Banaglore", - "state": "Karnataka", - "pinCode": "560034" - }, - { - "name": "Jack Morris", - "email": "jack@gmail.com", - "city": "Delhi", - "state": "Delhi", - "pinCode": "110006", - "phone": "12312331232", - "street": "12th cross" - } - ], - "id": 1 + "returnPolicy": "30 days return policy", + "minimumOrderQuantity": 1, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "7583442707568", + "qrCode": "https://dummyjson.com/public/qr-code.png" }, - "paymentMethod": "cash", - "selectedAddress": { - "name": "Jack Morris", - "email": "jack@gmail.com", - "city": "Delhi", - "state": "Delhi", - "pinCode": "110006", - "phone": "12312331232", - "street": "12th cross" - }, - "status": "pending", - "id": 6 + "images": [ + "https://cdn.dummyjson.com/products/images/groceries/Green%20Bell%20Pepper/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Green%20Bell%20Pepper/thumbnail.png" }, { - "items": [ - { - "title": "Samsung Universe 9", - "description": "Samsung's new variant which goes beyond Galaxy to the Universe", - "price": 1249, - "discountPercentage": 15.46, - "rating": 4.09, - "stock": 36, - "brand": "Samsung", - "category": "smartphones", - "thumbnail": "https://i.dummyjson.com/data/products/3/thumbnail.jpg", - "images": ["https://i.dummyjson.com/data/products/3/1.jpg"], - "quantity": 1, - "user": 2, - "id": 1 + "id": 26, + "title": "Green Chili Pepper", + "description": "Spicy green chili pepper, ideal for adding heat to your favorite recipes.", + "category": "groceries", + "price": 0.99, + "discountPercentage": 18.51, + "rating": 4.43, + "stock": 8, + "tags": ["vegetables"], + "sku": "Y4RM3JCB", + "weight": 2, + "dimensions": { + "width": 18.67, + "height": 21.17, + "depth": 25.26 + }, + "warrantyInformation": "No warranty", + "shippingInformation": "Ships in 1-2 business days", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 2, + "comment": "Disappointing product!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Mateo Bennett", + "reviewerEmail": "mateo.bennett@x.dummyjson.com" }, { - "title": "Huawei P30", - "description": "Huawei’s re-badged P30 Pro New Edition was officially unveiled yesterday in Germany and now the device has made its way to the UK.", - "price": 499, - "discountPercentage": 10.58, - "rating": 4.09, - "stock": 32, - "brand": "Huawei", - "category": "smartphones", - "thumbnail": "https://i.dummyjson.com/data/products/5/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/5/1.jpg", - "https://i.dummyjson.com/data/products/5/2.jpg", - "https://i.dummyjson.com/data/products/5/3.jpg" - ], - "quantity": 1, - "user": 2, - "id": 2 + "rating": 5, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Natalie Price", + "reviewerEmail": "natalie.price@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Very satisfied!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Avery Barnes", + "reviewerEmail": "avery.barnes@x.dummyjson.com" } ], - "totalAmount": 1502, - "totalItems": 2, - "user": { - "email": "demo@gmail.com", - "password": "Qwerty123", - "role": "user", - "addresses": [ - { - "name": "Demo user", - "email": "demo@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Mumbai", - "state": "Maharastra", - "pinCode": "220001" - } - ], - "id": 2 + "returnPolicy": "30 days return policy", + "minimumOrderQuantity": 43, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "8400326844874", + "qrCode": "https://dummyjson.com/public/qr-code.png" }, - "paymentMethod": "cash", - "selectedAddress": { - "name": "Demo user", - "email": "demo@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Mumbai", - "state": "Maharastra", - "pinCode": "220001" - }, - "status": "pending", - "id": 7 + "images": [ + "https://cdn.dummyjson.com/products/images/groceries/Green%20Chili%20Pepper/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Green%20Chili%20Pepper/thumbnail.png" }, { - "items": [ - { - "title": "Samsung Universe 9", - "description": "Samsung's new variant which goes beyond Galaxy to the Universe", - "price": 1249, - "discountPercentage": 15.46, - "rating": 4.09, - "stock": 36, - "brand": "Samsung", - "category": "smartphones", - "thumbnail": "https://i.dummyjson.com/data/products/3/thumbnail.jpg", - "images": ["https://i.dummyjson.com/data/products/3/1.jpg"], - "quantity": 1, - "user": 2, - "id": 1 + "id": 27, + "title": "Honey Jar", + "description": "Pure and natural honey in a convenient jar, perfect for sweetening beverages or drizzling over food.", + "category": "groceries", + "price": 6.99, + "discountPercentage": 1.91, + "rating": 3.5, + "stock": 25, + "tags": ["condiments"], + "sku": "BTBNIIOU", + "weight": 9, + "dimensions": { + "width": 26.53, + "height": 27.11, + "depth": 6.63 + }, + "warrantyInformation": "2 year warranty", + "shippingInformation": "Ships overnight", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 4, + "comment": "Fast shipping!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Nicholas Bailey", + "reviewerEmail": "nicholas.bailey@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Awesome product!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Gabriel Hayes", + "reviewerEmail": "gabriel.hayes@x.dummyjson.com" }, { - "title": "Huawei P30", - "description": "Huawei’s re-badged P30 Pro New Edition was officially unveiled yesterday in Germany and now the device has made its way to the UK.", - "price": 499, - "discountPercentage": 10.58, - "rating": 4.09, - "stock": 32, - "brand": "Huawei", - "category": "smartphones", - "thumbnail": "https://i.dummyjson.com/data/products/5/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/5/1.jpg", - "https://i.dummyjson.com/data/products/5/2.jpg", - "https://i.dummyjson.com/data/products/5/3.jpg" - ], - "quantity": 1, - "user": 2, - "id": 2 + "rating": 5, + "comment": "Highly impressed!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "James Garcia", + "reviewerEmail": "james.garcia@x.dummyjson.com" } ], - "totalAmount": 1502, - "totalItems": 2, - "user": { - "email": "demo@gmail.com", - "password": "Qwerty123", - "role": "user", - "addresses": [ - { - "name": "Demo user", - "email": "demo@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Mumbai", - "state": "Maharastra", - "pinCode": "220001" - } - ], - "id": 2 + "returnPolicy": "90 days return policy", + "minimumOrderQuantity": 1, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "0668665656044", + "qrCode": "https://dummyjson.com/public/qr-code.png" }, - "paymentMethod": "cash", - "selectedAddress": { - "name": "Demo user", - "email": "demo@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Mumbai", - "state": "Maharastra", - "pinCode": "220001" - }, - "status": "pending", - "id": 8 + "images": [ + "https://cdn.dummyjson.com/products/images/groceries/Honey%20Jar/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Honey%20Jar/thumbnail.png" }, { - "items": [ - { - "title": "MacBook Pro", - "description": "MacBook Pro 2021 with mini-LED display may launch between September, November", - "price": 1999, - "discountPercentage": 11.02, - "rating": 0, - "stock": 83, - "brand": "Apple", - "category": "laptops", - "thumbnail": "https://i.dummyjson.com/data/products/6/thumbnail.png", - "images": [ - "https://i.dummyjson.com/data/products/6/1.png", - "https://i.dummyjson.com/data/products/6/2.jpg", - "https://i.dummyjson.com/data/products/6/3.png", - "https://i.dummyjson.com/data/products/6/thumbnail.png" - ], - "quantity": 1, - "user": 2, - "id": 1 + "id": 28, + "title": "Ice Cream", + "description": "Creamy and delicious ice cream, available in various flavors for a delightful treat.", + "category": "groceries", + "price": 5.49, + "discountPercentage": 7.58, + "rating": 3.77, + "stock": 76, + "tags": ["desserts"], + "sku": "VEZMU1EQ", + "weight": 5, + "dimensions": { + "width": 17.66, + "height": 24.49, + "depth": 25.98 + }, + "warrantyInformation": "2 year warranty", + "shippingInformation": "Ships in 2 weeks", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Great product!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Elena Baker", + "reviewerEmail": "elena.baker@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Highly impressed!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Madeline Simpson", + "reviewerEmail": "madeline.simpson@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Very happy with my purchase!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Caleb Nelson", + "reviewerEmail": "caleb.nelson@x.dummyjson.com" } ], - "totalAmount": 1779, - "totalItems": 1, - "user": { - "email": "demo@gmail.com", - "password": "Qwerty123", - "role": "user", - "addresses": [ - { - "name": "Demo user", - "email": "demo@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Mumbai", - "state": "Maharastra", - "pinCode": "220001" - } - ], - "id": 2 - }, - "paymentMethod": "card", - "selectedAddress": { - "name": "Demo user", - "email": "demo@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Mumbai", - "state": "Maharastra", - "pinCode": "220001" + "returnPolicy": "No return policy", + "minimumOrderQuantity": 19, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "9603960319256", + "qrCode": "https://dummyjson.com/public/qr-code.png" }, - "status": "pending", - "id": 9 + "images": [ + "https://cdn.dummyjson.com/products/images/groceries/Ice%20Cream/1.png", + "https://cdn.dummyjson.com/products/images/groceries/Ice%20Cream/2.png", + "https://cdn.dummyjson.com/products/images/groceries/Ice%20Cream/3.png", + "https://cdn.dummyjson.com/products/images/groceries/Ice%20Cream/4.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Ice%20Cream/thumbnail.png" }, { - "items": [ - { - "title": "Microsoft Surface Laptop 4", - "description": "Style and speed. Stand out on HD video calls backed by Studio Mics. Capture ideas on the vibrant touchscreen.", - "price": 1499, - "discountPercentage": 10.23, - "rating": 4.43, - "stock": 68, - "brand": "Microsoft Surface", - "category": "laptops", - "thumbnail": "https://i.dummyjson.com/data/products/8/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/8/1.jpg", - "https://i.dummyjson.com/data/products/8/2.jpg", - "https://i.dummyjson.com/data/products/8/3.jpg", - "https://i.dummyjson.com/data/products/8/4.jpg", - "https://i.dummyjson.com/data/products/8/thumbnail.jpg" - ], - "quantity": 1, - "user": 2, - "id": 1 + "id": 29, + "title": "Juice", + "description": "Refreshing fruit juice, packed with vitamins and great for staying hydrated.", + "category": "groceries", + "price": 3.99, + "discountPercentage": 5.45, + "rating": 3.41, + "stock": 99, + "tags": ["beverages"], + "sku": "M2K19S06", + "weight": 2, + "dimensions": { + "width": 8.97, + "height": 12.26, + "depth": 15.05 + }, + "warrantyInformation": "1 week warranty", + "shippingInformation": "Ships in 1-2 business days", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 4, + "comment": "Very satisfied!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Leo Rivera", + "reviewerEmail": "leo.rivera@x.dummyjson.com" + }, + { + "rating": 2, + "comment": "Not worth the price!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Ethan Martinez", + "reviewerEmail": "ethan.martinez@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Excellent quality!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Max Parker", + "reviewerEmail": "max.parker@x.dummyjson.com" } ], - "totalAmount": 1346, - "totalItems": 1, - "user": { - "email": "demo@gmail.com", - "password": "Qwerty123", - "role": "user", - "addresses": [ - { - "name": "Demo user", - "email": "demo@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Mumbai", - "state": "Maharastra", - "pinCode": "220001" - } - ], - "id": 2 + "returnPolicy": "90 days return policy", + "minimumOrderQuantity": 26, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "8546824122355", + "qrCode": "https://dummyjson.com/public/qr-code.png" }, - "paymentMethod": "card", - "selectedAddress": { - "name": "Demo user", - "email": "demo@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Mumbai", - "state": "Maharastra", - "pinCode": "220001" - }, - "status": "pending", - "id": 10 + "images": [ + "https://cdn.dummyjson.com/products/images/groceries/Juice/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Juice/thumbnail.png" }, { - "items": [ - { - "title": "Microsoft Surface Laptop 4", - "description": "Style and speed. Stand out on HD video calls backed by Studio Mics. Capture ideas on the vibrant touchscreen.", - "price": 1499, - "discountPercentage": 10.23, - "rating": 4.43, - "stock": 68, - "brand": "Microsoft Surface", - "category": "laptops", - "thumbnail": "https://i.dummyjson.com/data/products/8/thumbnail.jpg", - "images": [ - "https://i.dummyjson.com/data/products/8/1.jpg", - "https://i.dummyjson.com/data/products/8/2.jpg", - "https://i.dummyjson.com/data/products/8/3.jpg", - "https://i.dummyjson.com/data/products/8/4.jpg", - "https://i.dummyjson.com/data/products/8/thumbnail.jpg" - ], - "quantity": 2, - "user": 2, - "id": 1 + "id": 30, + "title": "Kiwi", + "description": "Nutrient-rich kiwi, perfect for snacking or adding a tropical twist to your dishes.", + "category": "groceries", + "price": 2.49, + "discountPercentage": 10.32, + "rating": 4.37, + "stock": 1, + "tags": ["fruits"], + "sku": "0X3NORB9", + "weight": 8, + "dimensions": { + "width": 27.3, + "height": 7.48, + "depth": 15.08 + }, + "warrantyInformation": "6 months warranty", + "shippingInformation": "Ships in 3-5 business days", + "availabilityStatus": "Low Stock", + "reviews": [ + { + "rating": 5, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Nora Russell", + "reviewerEmail": "nora.russell@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Dylan Wells", + "reviewerEmail": "dylan.wells@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Great product!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Noah Hernandez", + "reviewerEmail": "noah.hernandez@x.dummyjson.com" } ], - "totalAmount": 2692, - "totalItems": 2, - "user": { - "email": "demo@gmail.com", - "password": "Qwerty123", - "role": "user", - "addresses": [ - { - "name": "Demo user", - "email": "demo@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Mumbai", - "state": "Maharastra", - "pinCode": "220001" - } - ], - "id": 2 - }, - "paymentMethod": "cash", - "selectedAddress": { - "name": "Demo user", - "email": "demo@gmail.com", - "phone": "1234567788", - "street": "11th Main", - "city": "Mumbai", - "state": "Maharastra", - "pinCode": "220001" + "returnPolicy": "7 days return policy", + "minimumOrderQuantity": 8, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "3325493172934", + "qrCode": "https://dummyjson.com/public/qr-code.png" }, - "status": "pending", - "id": 11 + "images": [ + "https://cdn.dummyjson.com/products/images/groceries/Kiwi/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Kiwi/thumbnail.png" } ] } diff --git a/src/app/store.js b/src/app/store.js index 9eca6d2..aacfdad 100644 --- a/src/app/store.js +++ b/src/app/store.js @@ -1,8 +1,8 @@ -import { configureStore } from '@reduxjs/toolkit'; -import counterReducer from '../features/counter/counterSlice'; +import { configureStore } from "@reduxjs/toolkit"; +import productReducer from "../features/product/productSlice"; export const store = configureStore({ reducer: { - counter: counterReducer, + product: productReducer, }, }); diff --git a/src/features/product/components/ProductList.js b/src/features/product/components/ProductList.js index ab7896f..225961a 100644 --- a/src/features/product/components/ProductList.js +++ b/src/features/product/components/ProductList.js @@ -1,6 +1,6 @@ -import React, { useState, Fragment } from "react"; +import React, { useState, Fragment, useEffect } from "react"; import { useSelector, useDispatch } from "react-redux"; -import { increment, incrementAsync, selectCount } from "../productSlice"; +import { fetchAllProductsAsync, selectAllProducts } from "../productSlice"; import { Dialog, Disclosure, Menu, Transition } from "@headlessui/react"; import { StarIcon, XMarkIcon } from "@heroicons/react/24/outline"; import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/20/solid"; @@ -63,1785 +63,14 @@ function classNames(...classes) { return classes.filter(Boolean).join(" "); } -const products = [ - { - id: 1, - title: "Essence Mascara Lash Princess", - 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, - stock: 5, - tags: ["beauty", "mascara"], - brand: "Essence", - sku: "RCH45Q1A", - weight: 2, - dimensions: { - width: 23.17, - height: 14.43, - depth: 28.01, - }, - warrantyInformation: "1 month warranty", - shippingInformation: "Ships in 1 month", - availabilityStatus: "Low Stock", - reviews: [ - { - rating: 2, - comment: "Very unhappy with my purchase!", - date: "2024-05-23T08:56:21.618Z", - reviewerName: "John Doe", - reviewerEmail: "john.doe@x.dummyjson.com", - }, - { - rating: 2, - comment: "Not as described!", - date: "2024-05-23T08:56:21.618Z", - reviewerName: "Nolan Gonzalez", - reviewerEmail: "nolan.gonzalez@x.dummyjson.com", - }, - { - rating: 5, - comment: "Very satisfied!", - date: "2024-05-23T08:56:21.618Z", - reviewerName: "Scarlett Wright", - reviewerEmail: "scarlett.wright@x.dummyjson.com", - }, - ], - returnPolicy: "30 days return policy", - minimumOrderQuantity: 24, - meta: { - createdAt: "2024-05-23T08:56:21.618Z", - updatedAt: "2024-05-23T08:56:21.618Z", - barcode: "9164035109868", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "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", - }, - { - id: 2, - title: "Eyeshadow Palette with Mirror", - description: - "The Eyeshadow Palette with Mirror offers a versatile range of eyeshadow shades for creating stunning eye looks. With a built-in mirror, it's convenient for on-the-go makeup application.", - category: "beauty", - price: 19.99, - discountPercentage: 5.5, - rating: 3.28, - stock: 44, - tags: ["beauty", "eyeshadow"], - brand: "Glamour Beauty", - sku: "MVCFH27F", - weight: 3, - dimensions: { - width: 12.42, - height: 8.63, - depth: 29.13, - }, - warrantyInformation: "1 year warranty", - shippingInformation: "Ships in 2 weeks", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 4, - comment: "Very satisfied!", - date: "2024-05-23T08:56:21.618Z", - reviewerName: "Liam Garcia", - reviewerEmail: "liam.garcia@x.dummyjson.com", - }, - { - rating: 1, - comment: "Very disappointed!", - date: "2024-05-23T08:56:21.618Z", - reviewerName: "Nora Russell", - reviewerEmail: "nora.russell@x.dummyjson.com", - }, - { - rating: 5, - comment: "Highly impressed!", - date: "2024-05-23T08:56:21.618Z", - reviewerName: "Elena Baker", - reviewerEmail: "elena.baker@x.dummyjson.com", - }, - ], - returnPolicy: "30 days return policy", - minimumOrderQuantity: 32, - meta: { - createdAt: "2024-05-23T08:56:21.618Z", - updatedAt: "2024-05-23T08:56:21.618Z", - barcode: "2817839095220", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/1.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/thumbnail.png", - }, - { - id: 3, - 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, - discountPercentage: 18.14, - rating: 3.82, - stock: 59, - tags: ["beauty", "face powder"], - brand: "Velvet Touch", - sku: "9EN8WLT2", - weight: 8, - dimensions: { - width: 24.16, - height: 10.7, - depth: 11.07, - }, - warrantyInformation: "2 year warranty", - shippingInformation: "Ships in 1-2 business days", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 5, - comment: "Very happy with my purchase!", - date: "2024-05-23T08:56:21.618Z", - reviewerName: "Ethan Thompson", - reviewerEmail: "ethan.thompson@x.dummyjson.com", - }, - { - rating: 4, - comment: "Great value for money!", - date: "2024-05-23T08:56:21.618Z", - reviewerName: "Levi Hicks", - reviewerEmail: "levi.hicks@x.dummyjson.com", - }, - { - rating: 5, - comment: "Highly impressed!", - date: "2024-05-23T08:56:21.618Z", - reviewerName: "Hazel Gardner", - reviewerEmail: "hazel.gardner@x.dummyjson.com", - }, - ], - returnPolicy: "60 days return policy", - minimumOrderQuantity: 25, - meta: { - createdAt: "2024-05-23T08:56:21.618Z", - updatedAt: "2024-05-23T08:56:21.618Z", - barcode: "0516267971277", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/1.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png", - }, - { - id: 4, - title: "Red Lipstick", - description: - "The Red Lipstick is a classic and bold choice for adding a pop of color to your lips. With a creamy and pigmented formula, it provides a vibrant and long-lasting finish.", - category: "beauty", - price: 12.99, - discountPercentage: 19.03, - rating: 2.51, - stock: 68, - tags: ["beauty", "lipstick"], - brand: "Chic Cosmetics", - sku: "O5IF1NTA", - weight: 5, - dimensions: { - width: 14.37, - height: 13.94, - depth: 14.6, - }, - warrantyInformation: "Lifetime warranty", - shippingInformation: "Ships in 2 weeks", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 5, - comment: "Great product!", - date: "2024-05-23T08:56:21.619Z", - reviewerName: "Leo Rivera", - reviewerEmail: "leo.rivera@x.dummyjson.com", - }, - { - rating: 4, - comment: "Very pleased!", - date: "2024-05-23T08:56:21.619Z", - reviewerName: "Oscar Powers", - reviewerEmail: "oscar.powers@x.dummyjson.com", - }, - { - rating: 5, - comment: "Very pleased!", - date: "2024-05-23T08:56:21.619Z", - reviewerName: "Carter Rivera", - reviewerEmail: "carter.rivera@x.dummyjson.com", - }, - ], - returnPolicy: "90 days return policy", - minimumOrderQuantity: 6, - meta: { - createdAt: "2024-05-23T08:56:21.619Z", - updatedAt: "2024-05-23T08:56:21.619Z", - barcode: "9444582199406", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/1.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/thumbnail.png", - }, - { - id: 5, - title: "Red Nail Polish", - description: - "The Red Nail Polish offers a rich and glossy red hue for vibrant and polished nails. With a quick-drying formula, it provides a salon-quality finish at home.", - category: "beauty", - price: 8.99, - discountPercentage: 2.46, - rating: 3.91, - stock: 71, - tags: ["beauty", "nail polish"], - brand: "Nail Couture", - sku: "YUIIIP4W", - weight: 9, - dimensions: { - width: 8.11, - height: 10.89, - depth: 29.06, - }, - warrantyInformation: "1 year warranty", - shippingInformation: "Ships in 1 week", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 5, - comment: "Very pleased!", - date: "2024-05-23T08:56:21.619Z", - reviewerName: "Leo Rivera", - reviewerEmail: "leo.rivera@x.dummyjson.com", - }, - { - rating: 5, - comment: "Great product!", - date: "2024-05-23T08:56:21.619Z", - reviewerName: "Evan Reed", - reviewerEmail: "evan.reed@x.dummyjson.com", - }, - { - rating: 4, - comment: "Highly recommended!", - date: "2024-05-23T08:56:21.619Z", - reviewerName: "Evelyn Sanchez", - reviewerEmail: "evelyn.sanchez@x.dummyjson.com", - }, - ], - returnPolicy: "No return policy", - minimumOrderQuantity: 46, - meta: { - createdAt: "2024-05-23T08:56:21.619Z", - updatedAt: "2024-05-23T08:56:21.619Z", - barcode: "3212847902461", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/beauty/Red%20Nail%20Polish/1.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/beauty/Red%20Nail%20Polish/thumbnail.png", - }, - { - id: 6, - title: "Calvin Klein CK One", - description: - "CK One by Calvin Klein is a classic unisex fragrance, known for its fresh and clean scent. It's a versatile fragrance suitable for everyday wear.", - category: "fragrances", - price: 49.99, - discountPercentage: 0.32, - rating: 4.85, - stock: 17, - tags: ["fragrances", "perfumes"], - brand: "Calvin Klein", - sku: "DZM2JQZE", - weight: 5, - dimensions: { - width: 11.53, - height: 14.44, - depth: 6.81, - }, - warrantyInformation: "5 year warranty", - shippingInformation: "Ships overnight", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 5, - comment: "Great value for money!", - date: "2024-05-23T08:56:21.619Z", - reviewerName: "Sophia Brown", - reviewerEmail: "sophia.brown@x.dummyjson.com", - }, - { - rating: 3, - comment: "Very disappointed!", - date: "2024-05-23T08:56:21.619Z", - reviewerName: "Madison Collins", - reviewerEmail: "madison.collins@x.dummyjson.com", - }, - { - rating: 1, - comment: "Poor quality!", - date: "2024-05-23T08:56:21.619Z", - reviewerName: "Maya Reed", - reviewerEmail: "maya.reed@x.dummyjson.com", - }, - ], - returnPolicy: "No return policy", - minimumOrderQuantity: 20, - meta: { - createdAt: "2024-05-23T08:56:21.619Z", - updatedAt: "2024-05-23T08:56:21.619Z", - barcode: "2210136215089", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/fragrances/Calvin%20Klein%20CK%20One/1.png", - "https://cdn.dummyjson.com/products/images/fragrances/Calvin%20Klein%20CK%20One/2.png", - "https://cdn.dummyjson.com/products/images/fragrances/Calvin%20Klein%20CK%20One/3.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/fragrances/Calvin%20Klein%20CK%20One/thumbnail.png", - }, - { - id: 7, - title: "Chanel Coco Noir Eau De", - description: - "Coco Noir by Chanel is an elegant and mysterious fragrance, featuring notes of grapefruit, rose, and sandalwood. Perfect for evening occasions.", - category: "fragrances", - price: 129.99, - discountPercentage: 18.64, - rating: 2.76, - stock: 41, - tags: ["fragrances", "perfumes"], - brand: "Chanel", - sku: "K71HBCGS", - weight: 4, - dimensions: { - width: 21.27, - height: 28, - depth: 11.89, - }, - warrantyInformation: "1 week warranty", - shippingInformation: "Ships in 1 month", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 1, - comment: "Disappointing product!", - date: "2024-05-23T08:56:21.619Z", - reviewerName: "Lincoln Kelly", - reviewerEmail: "lincoln.kelly@x.dummyjson.com", - }, - { - rating: 4, - comment: "Great product!", - date: "2024-05-23T08:56:21.619Z", - reviewerName: "Lincoln Kelly", - reviewerEmail: "lincoln.kelly@x.dummyjson.com", - }, - { - rating: 4, - comment: "Excellent quality!", - date: "2024-05-23T08:56:21.619Z", - reviewerName: "Lucas Allen", - reviewerEmail: "lucas.allen@x.dummyjson.com", - }, - ], - returnPolicy: "60 days return policy", - minimumOrderQuantity: 5, - meta: { - createdAt: "2024-05-23T08:56:21.619Z", - updatedAt: "2024-05-23T08:56:21.619Z", - barcode: "1435582999795", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/fragrances/Chanel%20Coco%20Noir%20Eau%20De/1.png", - "https://cdn.dummyjson.com/products/images/fragrances/Chanel%20Coco%20Noir%20Eau%20De/2.png", - "https://cdn.dummyjson.com/products/images/fragrances/Chanel%20Coco%20Noir%20Eau%20De/3.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/fragrances/Chanel%20Coco%20Noir%20Eau%20De/thumbnail.png", - }, - { - id: 8, - title: "Dior J'adore", - description: - "J'adore by Dior is a luxurious and floral fragrance, known for its blend of ylang-ylang, rose, and jasmine. It embodies femininity and sophistication.", - category: "fragrances", - price: 89.99, - discountPercentage: 17.44, - rating: 3.31, - stock: 91, - tags: ["fragrances", "perfumes"], - brand: "Dior", - sku: "E70NB03B", - weight: 10, - dimensions: { - width: 21.51, - height: 7, - depth: 26.51, - }, - warrantyInformation: "Lifetime warranty", - shippingInformation: "Ships in 2 weeks", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 5, - comment: "Fast shipping!", - date: "2024-05-23T08:56:21.619Z", - reviewerName: "Zoe Nicholson", - reviewerEmail: "zoe.nicholson@x.dummyjson.com", - }, - { - rating: 4, - comment: "Excellent quality!", - date: "2024-05-23T08:56:21.619Z", - reviewerName: "Addison Wright", - reviewerEmail: "addison.wright@x.dummyjson.com", - }, - { - rating: 4, - comment: "Would buy again!", - date: "2024-05-23T08:56:21.619Z", - reviewerName: "Clara Berry", - reviewerEmail: "clara.berry@x.dummyjson.com", - }, - ], - returnPolicy: "7 days return policy", - minimumOrderQuantity: 8, - meta: { - createdAt: "2024-05-23T08:56:21.619Z", - updatedAt: "2024-05-23T08:56:21.619Z", - barcode: "0887083199279", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/fragrances/Dior%20J'adore/1.png", - "https://cdn.dummyjson.com/products/images/fragrances/Dior%20J'adore/2.png", - "https://cdn.dummyjson.com/products/images/fragrances/Dior%20J'adore/3.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/fragrances/Dior%20J'adore/thumbnail.png", - }, - { - id: 9, - title: "Dolce Shine Eau de", - description: - "Dolce Shine by Dolce & Gabbana is a vibrant and fruity fragrance, featuring notes of mango, jasmine, and blonde woods. It's a joyful and youthful scent.", - category: "fragrances", - price: 69.99, - discountPercentage: 11.47, - rating: 2.68, - stock: 3, - tags: ["fragrances", "perfumes"], - brand: "Dolce & Gabbana", - sku: "1NBFK980", - weight: 5, - dimensions: { - width: 17, - height: 24.57, - depth: 13.3, - }, - warrantyInformation: "5 year warranty", - shippingInformation: "Ships in 1-2 business days", - availabilityStatus: "Low Stock", - reviews: [ - { - rating: 4, - comment: "Very satisfied!", - date: "2024-05-23T08:56:21.619Z", - reviewerName: "Xavier Wright", - reviewerEmail: "xavier.wright@x.dummyjson.com", - }, - { - rating: 1, - comment: "Poor quality!", - date: "2024-05-23T08:56:21.619Z", - reviewerName: "Mila Hernandez", - reviewerEmail: "mila.hernandez@x.dummyjson.com", - }, - { - rating: 5, - comment: "Very happy with my purchase!", - date: "2024-05-23T08:56:21.619Z", - reviewerName: "Sophia Brown", - reviewerEmail: "sophia.brown@x.dummyjson.com", - }, - ], - returnPolicy: "30 days return policy", - minimumOrderQuantity: 9, - meta: { - createdAt: "2024-05-23T08:56:21.619Z", - updatedAt: "2024-05-23T08:56:21.619Z", - barcode: "1939383392674", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/fragrances/Dolce%20Shine%20Eau%20de/1.png", - "https://cdn.dummyjson.com/products/images/fragrances/Dolce%20Shine%20Eau%20de/2.png", - "https://cdn.dummyjson.com/products/images/fragrances/Dolce%20Shine%20Eau%20de/3.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/fragrances/Dolce%20Shine%20Eau%20de/thumbnail.png", - }, - { - id: 10, - title: "Gucci Bloom Eau de", - description: - "Gucci Bloom by Gucci is a floral and captivating fragrance, with notes of tuberose, jasmine, and Rangoon creeper. It's a modern and romantic scent.", - category: "fragrances", - price: 79.99, - discountPercentage: 8.9, - rating: 2.69, - stock: 93, - tags: ["fragrances", "perfumes"], - brand: "Gucci", - sku: "FFKZ6HOF", - weight: 10, - dimensions: { - width: 22.28, - height: 17.81, - depth: 27.18, - }, - warrantyInformation: "No warranty", - shippingInformation: "Ships in 2 weeks", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 5, - comment: "Great value for money!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Aria Parker", - reviewerEmail: "aria.parker@x.dummyjson.com", - }, - { - rating: 4, - comment: "Excellent quality!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Natalie Harris", - reviewerEmail: "natalie.harris@x.dummyjson.com", - }, - { - rating: 4, - comment: "Fast shipping!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Ava Harris", - reviewerEmail: "ava.harris@x.dummyjson.com", - }, - ], - returnPolicy: "No return policy", - minimumOrderQuantity: 10, - meta: { - createdAt: "2024-05-23T08:56:21.620Z", - updatedAt: "2024-05-23T08:56:21.620Z", - barcode: "8232190382069", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/fragrances/Gucci%20Bloom%20Eau%20de/1.png", - "https://cdn.dummyjson.com/products/images/fragrances/Gucci%20Bloom%20Eau%20de/2.png", - "https://cdn.dummyjson.com/products/images/fragrances/Gucci%20Bloom%20Eau%20de/3.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/fragrances/Gucci%20Bloom%20Eau%20de/thumbnail.png", - }, - { - id: 11, - title: "Annibale Colombo Bed", - description: - "The Annibale Colombo Bed is a luxurious and elegant bed frame, crafted with high-quality materials for a comfortable and stylish bedroom.", - category: "furniture", - price: 1899.99, - discountPercentage: 0.29, - rating: 4.14, - stock: 47, - tags: ["furniture", "beds"], - brand: "Annibale Colombo", - sku: "4KMDTZWF", - weight: 3, - dimensions: { - width: 28.75, - height: 26.88, - depth: 24.47, - }, - warrantyInformation: "2 year warranty", - shippingInformation: "Ships overnight", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 4, - comment: "Great value for money!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Julian Newton", - reviewerEmail: "julian.newton@x.dummyjson.com", - }, - { - rating: 5, - comment: "Would buy again!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Madison Collins", - reviewerEmail: "madison.collins@x.dummyjson.com", - }, - { - rating: 4, - comment: "Would buy again!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Clara Berry", - reviewerEmail: "clara.berry@x.dummyjson.com", - }, - ], - returnPolicy: "7 days return policy", - minimumOrderQuantity: 1, - meta: { - createdAt: "2024-05-23T08:56:21.620Z", - updatedAt: "2024-05-23T08:56:21.620Z", - barcode: "7113807059215", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Bed/1.png", - "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Bed/2.png", - "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Bed/3.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Bed/thumbnail.png", - }, - { - id: 12, - title: "Annibale Colombo Sofa", - description: - "The Annibale Colombo Sofa is a sophisticated and comfortable seating option, featuring exquisite design and premium upholstery for your living room.", - category: "furniture", - price: 2499.99, - discountPercentage: 18.54, - rating: 3.08, - stock: 16, - tags: ["furniture", "sofas"], - brand: "Annibale Colombo", - sku: "LUU95CQP", - weight: 3, - dimensions: { - width: 20.97, - height: 19.11, - depth: 25.81, - }, - warrantyInformation: "1 month warranty", - shippingInformation: "Ships overnight", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 5, - comment: "Very satisfied!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Tyler Davis", - reviewerEmail: "tyler.davis@x.dummyjson.com", - }, - { - rating: 5, - comment: "Excellent quality!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Hannah Robinson", - reviewerEmail: "hannah.robinson@x.dummyjson.com", - }, - { - rating: 3, - comment: "Waste of money!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Madison Collins", - reviewerEmail: "madison.collins@x.dummyjson.com", - }, - ], - returnPolicy: "7 days return policy", - minimumOrderQuantity: 1, - meta: { - createdAt: "2024-05-23T08:56:21.620Z", - updatedAt: "2024-05-23T08:56:21.620Z", - barcode: "0426785817074", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Sofa/1.png", - "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Sofa/2.png", - "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Sofa/3.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Sofa/thumbnail.png", - }, - { - id: 13, - title: "Bedside Table African Cherry", - description: - "The Bedside Table in African Cherry is a stylish and functional addition to your bedroom, providing convenient storage space and a touch of elegance.", - category: "furniture", - price: 299.99, - discountPercentage: 9.58, - rating: 4.48, - stock: 16, - tags: ["furniture", "bedside tables"], - brand: "Furniture Co.", - sku: "OWPLTZYX", - weight: 10, - dimensions: { - width: 25.43, - height: 20.2, - depth: 24.95, - }, - warrantyInformation: "6 months warranty", - shippingInformation: "Ships in 1-2 business days", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 5, - comment: "Very happy with my purchase!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "John Doe", - reviewerEmail: "john.doe@x.dummyjson.com", - }, - { - rating: 4, - comment: "Highly recommended!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Avery Carter", - reviewerEmail: "avery.carter@x.dummyjson.com", - }, - { - rating: 4, - comment: "Very pleased!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Evelyn Sanchez", - reviewerEmail: "evelyn.sanchez@x.dummyjson.com", - }, - ], - returnPolicy: "No return policy", - minimumOrderQuantity: 5, - meta: { - createdAt: "2024-05-23T08:56:21.620Z", - updatedAt: "2024-05-23T08:56:21.620Z", - barcode: "2913244159666", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/furniture/Bedside%20Table%20African%20Cherry/1.png", - "https://cdn.dummyjson.com/products/images/furniture/Bedside%20Table%20African%20Cherry/2.png", - "https://cdn.dummyjson.com/products/images/furniture/Bedside%20Table%20African%20Cherry/3.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/furniture/Bedside%20Table%20African%20Cherry/thumbnail.png", - }, - { - id: 14, - title: "Knoll Saarinen Executive Conference Chair", - description: - "The Knoll Saarinen Executive Conference Chair is a modern and ergonomic chair, perfect for your office or conference room with its timeless design.", - category: "furniture", - price: 499.99, - discountPercentage: 15.23, - rating: 4.11, - stock: 47, - tags: ["furniture", "office chairs"], - brand: "Knoll", - sku: "RKHVJ4FE", - weight: 3, - dimensions: { - width: 16.59, - height: 21.46, - depth: 29.07, - }, - warrantyInformation: "Lifetime warranty", - shippingInformation: "Ships in 3-5 business days", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 4, - comment: "Very happy with my purchase!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Leah Gutierrez", - reviewerEmail: "leah.gutierrez@x.dummyjson.com", - }, - { - rating: 4, - comment: "Would buy again!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Nolan Gonzalez", - reviewerEmail: "nolan.gonzalez@x.dummyjson.com", - }, - { - rating: 2, - comment: "Waste of money!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Stella Morris", - reviewerEmail: "stella.morris@x.dummyjson.com", - }, - ], - returnPolicy: "30 days return policy", - minimumOrderQuantity: 5, - meta: { - createdAt: "2024-05-23T08:56:21.620Z", - updatedAt: "2024-05-23T08:56:21.620Z", - barcode: "0726316339746", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/furniture/Knoll%20Saarinen%20Executive%20Conference%20Chair/1.png", - "https://cdn.dummyjson.com/products/images/furniture/Knoll%20Saarinen%20Executive%20Conference%20Chair/2.png", - "https://cdn.dummyjson.com/products/images/furniture/Knoll%20Saarinen%20Executive%20Conference%20Chair/3.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/furniture/Knoll%20Saarinen%20Executive%20Conference%20Chair/thumbnail.png", - }, - { - id: 15, - title: "Wooden Bathroom Sink With Mirror", - description: - "The Wooden Bathroom Sink with Mirror is a unique and stylish addition to your bathroom, featuring a wooden sink countertop and a matching mirror.", - category: "furniture", - price: 799.99, - discountPercentage: 11.22, - rating: 3.26, - stock: 95, - tags: ["furniture", "bathroom"], - brand: "Bath Trends", - sku: "7OLTIEVO", - weight: 6, - dimensions: { - width: 7.32, - height: 22.64, - depth: 12.37, - }, - warrantyInformation: "6 months warranty", - shippingInformation: "Ships in 3-5 business days", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 5, - comment: "Highly recommended!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Charlotte Lopez", - reviewerEmail: "charlotte.lopez@x.dummyjson.com", - }, - { - rating: 1, - comment: "Would not recommend!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "William Gonzalez", - reviewerEmail: "william.gonzalez@x.dummyjson.com", - }, - { - rating: 2, - comment: "Not worth the price!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Ava Harrison", - reviewerEmail: "ava.harrison@x.dummyjson.com", - }, - ], - returnPolicy: "7 days return policy", - minimumOrderQuantity: 1, - meta: { - createdAt: "2024-05-23T08:56:21.620Z", - updatedAt: "2024-05-23T08:56:21.620Z", - barcode: "7839797529453", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/furniture/Wooden%20Bathroom%20Sink%20With%20Mirror/1.png", - "https://cdn.dummyjson.com/products/images/furniture/Wooden%20Bathroom%20Sink%20With%20Mirror/2.png", - "https://cdn.dummyjson.com/products/images/furniture/Wooden%20Bathroom%20Sink%20With%20Mirror/3.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/furniture/Wooden%20Bathroom%20Sink%20With%20Mirror/thumbnail.png", - }, - { - id: 16, - title: "Apple", - description: - "Fresh and crisp apples, perfect for snacking or incorporating into various recipes.", - category: "groceries", - price: 1.99, - discountPercentage: 1.97, - rating: 2.96, - stock: 9, - tags: ["fruits"], - sku: "QTROUV79", - weight: 8, - dimensions: { - width: 8.29, - height: 5.58, - depth: 12.41, - }, - warrantyInformation: "2 year warranty", - shippingInformation: "Ships in 2 weeks", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 4, - comment: "Great product!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Logan Lee", - reviewerEmail: "logan.lee@x.dummyjson.com", - }, - { - rating: 4, - comment: "Great product!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Elena Long", - reviewerEmail: "elena.long@x.dummyjson.com", - }, - { - rating: 1, - comment: "Not as described!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Grayson Coleman", - reviewerEmail: "grayson.coleman@x.dummyjson.com", - }, - ], - returnPolicy: "60 days return policy", - minimumOrderQuantity: 44, - meta: { - createdAt: "2024-05-23T08:56:21.620Z", - updatedAt: "2024-05-23T08:56:21.620Z", - barcode: "2517819903837", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: ["https://cdn.dummyjson.com/products/images/groceries/Apple/1.png"], - thumbnail: - "https://cdn.dummyjson.com/products/images/groceries/Apple/thumbnail.png", - }, - { - id: 17, - title: "Beef Steak", - description: - "High-quality beef steak, great for grilling or cooking to your preferred level of doneness.", - category: "groceries", - price: 12.99, - discountPercentage: 17.99, - rating: 2.83, - stock: 96, - tags: ["meat"], - sku: "BWWA2MSO", - weight: 9, - dimensions: { - width: 23.35, - height: 13.48, - depth: 26.4, - }, - warrantyInformation: "1 month warranty", - shippingInformation: "Ships overnight", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 4, - comment: "Very pleased!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Ethan Martinez", - reviewerEmail: "ethan.martinez@x.dummyjson.com", - }, - { - rating: 3, - comment: "Disappointing product!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Owen Fisher", - reviewerEmail: "owen.fisher@x.dummyjson.com", - }, - { - rating: 4, - comment: "Very happy with my purchase!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Scarlett Wright", - reviewerEmail: "scarlett.wright@x.dummyjson.com", - }, - ], - returnPolicy: "90 days return policy", - minimumOrderQuantity: 21, - meta: { - createdAt: "2024-05-23T08:56:21.620Z", - updatedAt: "2024-05-23T08:56:21.620Z", - barcode: "8335515097879", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/groceries/Beef%20Steak/1.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/groceries/Beef%20Steak/thumbnail.png", - }, - { - id: 18, - title: "Cat Food", - description: - "Nutritious cat food formulated to meet the dietary needs of your feline friend.", - category: "groceries", - price: 8.99, - discountPercentage: 9.57, - rating: 2.88, - stock: 13, - tags: ["pet supplies", "cat food"], - sku: "C3F8QN6O", - weight: 9, - dimensions: { - width: 15.4, - height: 13.97, - depth: 25.13, - }, - warrantyInformation: "3 months warranty", - shippingInformation: "Ships in 1-2 business days", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 5, - comment: "Very pleased!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Mateo Bennett", - reviewerEmail: "mateo.bennett@x.dummyjson.com", - }, - { - rating: 5, - comment: "Very pleased!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Aurora Barnes", - reviewerEmail: "aurora.barnes@x.dummyjson.com", - }, - { - rating: 5, - comment: "Great value for money!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Ellie Stewart", - reviewerEmail: "ellie.stewart@x.dummyjson.com", - }, - ], - returnPolicy: "7 days return policy", - minimumOrderQuantity: 48, - meta: { - createdAt: "2024-05-23T08:56:21.620Z", - updatedAt: "2024-05-23T08:56:21.620Z", - barcode: "5503491330693", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/groceries/Cat%20Food/1.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/groceries/Cat%20Food/thumbnail.png", - }, - { - id: 19, - title: "Chicken Meat", - description: - "Fresh and tender chicken meat, suitable for various culinary preparations.", - category: "groceries", - price: 9.99, - discountPercentage: 10.46, - rating: 4.61, - stock: 69, - tags: ["meat"], - sku: "G5YEHW7B", - weight: 7, - dimensions: { - width: 15.96, - height: 29.24, - depth: 26.25, - }, - warrantyInformation: "Lifetime warranty", - shippingInformation: "Ships in 1 month", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 5, - comment: "Very satisfied!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Sophia Jones", - reviewerEmail: "sophia.jones@x.dummyjson.com", - }, - { - rating: 5, - comment: "Great value for money!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Maya Reed", - reviewerEmail: "maya.reed@x.dummyjson.com", - }, - { - rating: 4, - comment: "Highly recommended!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Harper Turner", - reviewerEmail: "harper.turner@x.dummyjson.com", - }, - ], - returnPolicy: "7 days return policy", - minimumOrderQuantity: 46, - meta: { - createdAt: "2024-05-23T08:56:21.620Z", - updatedAt: "2024-05-23T08:56:21.620Z", - barcode: "0966223559510", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/groceries/Chicken%20Meat/1.png", - "https://cdn.dummyjson.com/products/images/groceries/Chicken%20Meat/2.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/groceries/Chicken%20Meat/thumbnail.png", - }, - { - id: 20, - title: "Cooking Oil", - description: - "Versatile cooking oil suitable for frying, sautéing, and various culinary applications.", - category: "groceries", - price: 4.99, - discountPercentage: 18.89, - rating: 4.01, - stock: 22, - tags: ["cooking essentials"], - sku: "Q6ZP1UY8", - weight: 8, - dimensions: { - width: 8.18, - height: 27.45, - depth: 27.88, - }, - warrantyInformation: "Lifetime warranty", - shippingInformation: "Ships in 1 month", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 5, - comment: "Would buy again!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Mason Parker", - reviewerEmail: "mason.parker@x.dummyjson.com", - }, - { - rating: 3, - comment: "Poor quality!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Jonathan Pierce", - reviewerEmail: "jonathan.pierce@x.dummyjson.com", - }, - { - rating: 5, - comment: "Would buy again!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Alexander Hernandez", - reviewerEmail: "alexander.hernandez@x.dummyjson.com", - }, - ], - returnPolicy: "60 days return policy", - minimumOrderQuantity: 2, - meta: { - createdAt: "2024-05-23T08:56:21.620Z", - updatedAt: "2024-05-23T08:56:21.620Z", - barcode: "6707669443381", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/groceries/Cooking%20Oil/1.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/groceries/Cooking%20Oil/thumbnail.png", - }, - { - id: 21, - title: "Cucumber", - description: - "Crisp and hydrating cucumbers, ideal for salads, snacks, or as a refreshing side.", - category: "groceries", - price: 1.49, - discountPercentage: 11.44, - rating: 4.71, - stock: 22, - tags: ["vegetables"], - sku: "6KGF2K6Z", - weight: 9, - dimensions: { - width: 11.04, - height: 20.5, - depth: 8.18, - }, - warrantyInformation: "5 year warranty", - shippingInformation: "Ships overnight", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 4, - comment: "Very satisfied!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Elijah Hill", - reviewerEmail: "elijah.hill@x.dummyjson.com", - }, - { - rating: 5, - comment: "Fast shipping!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Liam Garcia", - reviewerEmail: "liam.garcia@x.dummyjson.com", - }, - { - rating: 4, - comment: "Excellent quality!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Ella Cook", - reviewerEmail: "ella.cook@x.dummyjson.com", - }, - ], - returnPolicy: "30 days return policy", - minimumOrderQuantity: 7, - meta: { - createdAt: "2024-05-23T08:56:21.620Z", - updatedAt: "2024-05-23T08:56:21.620Z", - barcode: "2597004869708", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/groceries/Cucumber/1.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/groceries/Cucumber/thumbnail.png", - }, - { - id: 22, - title: "Dog Food", - description: - "Specially formulated dog food designed to provide essential nutrients for your canine companion.", - category: "groceries", - price: 10.99, - discountPercentage: 18.15, - rating: 2.74, - stock: 40, - tags: ["pet supplies", "dog food"], - sku: "A6QRCH37", - weight: 2, - dimensions: { - width: 29.39, - height: 29.77, - depth: 20.54, - }, - warrantyInformation: "1 year warranty", - shippingInformation: "Ships in 1 month", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 5, - comment: "Highly impressed!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Leo Rivera", - reviewerEmail: "leo.rivera@x.dummyjson.com", - }, - { - rating: 4, - comment: "Highly recommended!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Alexander Jones", - reviewerEmail: "alexander.jones@x.dummyjson.com", - }, - { - rating: 4, - comment: "Would buy again!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Addison Wright", - reviewerEmail: "addison.wright@x.dummyjson.com", - }, - ], - returnPolicy: "90 days return policy", - minimumOrderQuantity: 29, - meta: { - createdAt: "2024-05-23T08:56:21.620Z", - updatedAt: "2024-05-23T08:56:21.620Z", - barcode: "7957222289508", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/groceries/Dog%20Food/1.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/groceries/Dog%20Food/thumbnail.png", - }, - { - id: 23, - title: "Eggs", - description: - "Fresh eggs, a versatile ingredient for baking, cooking, or breakfast.", - category: "groceries", - price: 2.99, - discountPercentage: 5.8, - rating: 4.46, - stock: 10, - tags: ["dairy"], - sku: "YA617RI7", - weight: 4, - dimensions: { - width: 12.3, - height: 10.99, - depth: 15.53, - }, - warrantyInformation: "3 year warranty", - shippingInformation: "Ships overnight", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 2, - comment: "Very unhappy with my purchase!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Mateo Perez", - reviewerEmail: "mateo.perez@x.dummyjson.com", - }, - { - rating: 4, - comment: "Very happy with my purchase!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Cameron Perez", - reviewerEmail: "cameron.perez@x.dummyjson.com", - }, - { - rating: 5, - comment: "Very happy with my purchase!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Aurora Barnes", - reviewerEmail: "aurora.barnes@x.dummyjson.com", - }, - ], - returnPolicy: "30 days return policy", - minimumOrderQuantity: 43, - meta: { - createdAt: "2024-05-23T08:56:21.620Z", - updatedAt: "2024-05-23T08:56:21.620Z", - barcode: "7095702028776", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: ["https://cdn.dummyjson.com/products/images/groceries/Eggs/1.png"], - thumbnail: - "https://cdn.dummyjson.com/products/images/groceries/Eggs/thumbnail.png", - }, - { - id: 24, - title: "Fish Steak", - description: - "Quality fish steak, suitable for grilling, baking, or pan-searing.", - category: "groceries", - price: 14.99, - discountPercentage: 7, - rating: 4.83, - stock: 99, - tags: ["seafood"], - sku: "XNIH1MTA", - weight: 8, - dimensions: { - width: 20.14, - height: 8.4, - depth: 10.01, - }, - warrantyInformation: "1 year warranty", - shippingInformation: "Ships in 1 month", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 5, - comment: "Great value for money!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Michael Johnson", - reviewerEmail: "michael.johnson@x.dummyjson.com", - }, - { - rating: 4, - comment: "Would buy again!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Julian Newton", - reviewerEmail: "julian.newton@x.dummyjson.com", - }, - { - rating: 5, - comment: "Excellent quality!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Lila Hudson", - reviewerEmail: "lila.hudson@x.dummyjson.com", - }, - ], - returnPolicy: "30 days return policy", - minimumOrderQuantity: 49, - meta: { - createdAt: "2024-05-23T08:56:21.620Z", - updatedAt: "2024-05-23T08:56:21.620Z", - barcode: "4250692197342", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/groceries/Fish%20Steak/1.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/groceries/Fish%20Steak/thumbnail.png", - }, - { - id: 25, - title: "Green Bell Pepper", - description: - "Fresh and vibrant green bell pepper, perfect for adding color and flavor to your dishes.", - category: "groceries", - price: 1.29, - discountPercentage: 15.5, - rating: 4.28, - stock: 89, - tags: ["vegetables"], - sku: "HU7S7VQ0", - weight: 7, - dimensions: { - width: 7.32, - height: 14.31, - depth: 21.38, - }, - warrantyInformation: "5 year warranty", - shippingInformation: "Ships overnight", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 4, - comment: "Excellent quality!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Maya Reed", - reviewerEmail: "maya.reed@x.dummyjson.com", - }, - { - rating: 4, - comment: "Would buy again!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Madison Collins", - reviewerEmail: "madison.collins@x.dummyjson.com", - }, - { - rating: 5, - comment: "Would buy again!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Ethan Thompson", - reviewerEmail: "ethan.thompson@x.dummyjson.com", - }, - ], - returnPolicy: "30 days return policy", - minimumOrderQuantity: 1, - meta: { - createdAt: "2024-05-23T08:56:21.620Z", - updatedAt: "2024-05-23T08:56:21.620Z", - barcode: "7583442707568", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/groceries/Green%20Bell%20Pepper/1.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/groceries/Green%20Bell%20Pepper/thumbnail.png", - }, - { - id: 26, - title: "Green Chili Pepper", - description: - "Spicy green chili pepper, ideal for adding heat to your favorite recipes.", - category: "groceries", - price: 0.99, - discountPercentage: 18.51, - rating: 4.43, - stock: 8, - tags: ["vegetables"], - sku: "Y4RM3JCB", - weight: 2, - dimensions: { - width: 18.67, - height: 21.17, - depth: 25.26, - }, - warrantyInformation: "No warranty", - shippingInformation: "Ships in 1-2 business days", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 2, - comment: "Disappointing product!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Mateo Bennett", - reviewerEmail: "mateo.bennett@x.dummyjson.com", - }, - { - rating: 5, - comment: "Very pleased!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Natalie Price", - reviewerEmail: "natalie.price@x.dummyjson.com", - }, - { - rating: 4, - comment: "Very satisfied!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Avery Barnes", - reviewerEmail: "avery.barnes@x.dummyjson.com", - }, - ], - returnPolicy: "30 days return policy", - minimumOrderQuantity: 43, - meta: { - createdAt: "2024-05-23T08:56:21.620Z", - updatedAt: "2024-05-23T08:56:21.620Z", - barcode: "8400326844874", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/groceries/Green%20Chili%20Pepper/1.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/groceries/Green%20Chili%20Pepper/thumbnail.png", - }, - { - id: 27, - title: "Honey Jar", - description: - "Pure and natural honey in a convenient jar, perfect for sweetening beverages or drizzling over food.", - category: "groceries", - price: 6.99, - discountPercentage: 1.91, - rating: 3.5, - stock: 25, - tags: ["condiments"], - sku: "BTBNIIOU", - weight: 9, - dimensions: { - width: 26.53, - height: 27.11, - depth: 6.63, - }, - warrantyInformation: "2 year warranty", - shippingInformation: "Ships overnight", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 4, - comment: "Fast shipping!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Nicholas Bailey", - reviewerEmail: "nicholas.bailey@x.dummyjson.com", - }, - { - rating: 5, - comment: "Awesome product!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Gabriel Hayes", - reviewerEmail: "gabriel.hayes@x.dummyjson.com", - }, - { - rating: 5, - comment: "Highly impressed!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "James Garcia", - reviewerEmail: "james.garcia@x.dummyjson.com", - }, - ], - returnPolicy: "90 days return policy", - minimumOrderQuantity: 1, - meta: { - createdAt: "2024-05-23T08:56:21.620Z", - updatedAt: "2024-05-23T08:56:21.620Z", - barcode: "0668665656044", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/groceries/Honey%20Jar/1.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/groceries/Honey%20Jar/thumbnail.png", - }, - { - id: 28, - title: "Ice Cream", - description: - "Creamy and delicious ice cream, available in various flavors for a delightful treat.", - category: "groceries", - price: 5.49, - discountPercentage: 7.58, - rating: 3.77, - stock: 76, - tags: ["desserts"], - sku: "VEZMU1EQ", - weight: 5, - dimensions: { - width: 17.66, - height: 24.49, - depth: 25.98, - }, - warrantyInformation: "2 year warranty", - shippingInformation: "Ships in 2 weeks", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 5, - comment: "Great product!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Elena Baker", - reviewerEmail: "elena.baker@x.dummyjson.com", - }, - { - rating: 5, - comment: "Highly impressed!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Madeline Simpson", - reviewerEmail: "madeline.simpson@x.dummyjson.com", - }, - { - rating: 5, - comment: "Very happy with my purchase!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Caleb Nelson", - reviewerEmail: "caleb.nelson@x.dummyjson.com", - }, - ], - returnPolicy: "No return policy", - minimumOrderQuantity: 19, - meta: { - createdAt: "2024-05-23T08:56:21.620Z", - updatedAt: "2024-05-23T08:56:21.620Z", - barcode: "9603960319256", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: [ - "https://cdn.dummyjson.com/products/images/groceries/Ice%20Cream/1.png", - "https://cdn.dummyjson.com/products/images/groceries/Ice%20Cream/2.png", - "https://cdn.dummyjson.com/products/images/groceries/Ice%20Cream/3.png", - "https://cdn.dummyjson.com/products/images/groceries/Ice%20Cream/4.png", - ], - thumbnail: - "https://cdn.dummyjson.com/products/images/groceries/Ice%20Cream/thumbnail.png", - }, - { - id: 29, - title: "Juice", - description: - "Refreshing fruit juice, packed with vitamins and great for staying hydrated.", - category: "groceries", - price: 3.99, - discountPercentage: 5.45, - rating: 3.41, - stock: 99, - tags: ["beverages"], - sku: "M2K19S06", - weight: 2, - dimensions: { - width: 8.97, - height: 12.26, - depth: 15.05, - }, - warrantyInformation: "1 week warranty", - shippingInformation: "Ships in 1-2 business days", - availabilityStatus: "In Stock", - reviews: [ - { - rating: 4, - comment: "Very satisfied!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Leo Rivera", - reviewerEmail: "leo.rivera@x.dummyjson.com", - }, - { - rating: 2, - comment: "Not worth the price!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Ethan Martinez", - reviewerEmail: "ethan.martinez@x.dummyjson.com", - }, - { - rating: 4, - comment: "Excellent quality!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Max Parker", - reviewerEmail: "max.parker@x.dummyjson.com", - }, - ], - returnPolicy: "90 days return policy", - minimumOrderQuantity: 26, - meta: { - createdAt: "2024-05-23T08:56:21.620Z", - updatedAt: "2024-05-23T08:56:21.620Z", - barcode: "8546824122355", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: ["https://cdn.dummyjson.com/products/images/groceries/Juice/1.png"], - thumbnail: - "https://cdn.dummyjson.com/products/images/groceries/Juice/thumbnail.png", - }, - { - id: 30, - title: "Kiwi", - description: - "Nutrient-rich kiwi, perfect for snacking or adding a tropical twist to your dishes.", - category: "groceries", - price: 2.49, - discountPercentage: 10.32, - rating: 4.37, - stock: 1, - tags: ["fruits"], - sku: "0X3NORB9", - weight: 8, - dimensions: { - width: 27.3, - height: 7.48, - depth: 15.08, - }, - warrantyInformation: "6 months warranty", - shippingInformation: "Ships in 3-5 business days", - availabilityStatus: "Low Stock", - reviews: [ - { - rating: 5, - comment: "Very pleased!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Nora Russell", - reviewerEmail: "nora.russell@x.dummyjson.com", - }, - { - rating: 5, - comment: "Very pleased!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Dylan Wells", - reviewerEmail: "dylan.wells@x.dummyjson.com", - }, - { - rating: 5, - comment: "Great product!", - date: "2024-05-23T08:56:21.620Z", - reviewerName: "Noah Hernandez", - reviewerEmail: "noah.hernandez@x.dummyjson.com", - }, - ], - returnPolicy: "7 days return policy", - minimumOrderQuantity: 8, - meta: { - createdAt: "2024-05-23T08:56:21.620Z", - updatedAt: "2024-05-23T08:56:21.620Z", - barcode: "3325493172934", - qrCode: "https://dummyjson.com/public/qr-code.png", - }, - images: ["https://cdn.dummyjson.com/products/images/groceries/Kiwi/1.png"], - thumbnail: - "https://cdn.dummyjson.com/products/images/groceries/Kiwi/thumbnail.png", - }, -]; - export default function ProductList() { - const count = useSelector(selectCount); - const dispatch = useDispatch(); const [mobileFiltersOpen, setMobileFiltersOpen] = useState(false); + const products = useSelector(selectAllProducts); + const dispatch = useDispatch(); + useEffect(() => { + dispatch(fetchAllProductsAsync()); + }, [dispatch]); return ( <div className="bg-white"> <div> diff --git a/src/features/product/productAPI.js b/src/features/product/productAPI.js index 76c5e9b..0f33107 100644 --- a/src/features/product/productAPI.js +++ b/src/features/product/productAPI.js @@ -1,8 +1,8 @@ -export function fetchCount(amount = 1) { - return new Promise(async (resolve) =>{ - const response = await fetch('http://localhost:8080') - const data = await response.json() - resolve({data}) - } - ); +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 }); + }); } diff --git a/src/features/product/productSlice.js b/src/features/product/productSlice.js index fa5503a..f76ca01 100644 --- a/src/features/product/productSlice.js +++ b/src/features/product/productSlice.js @@ -1,22 +1,22 @@ -import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; -import { fetchCount } from './productAPI'; +import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; +import { fetchAllProducts } from "./productAPI"; const initialState = { - value: 0, - status: 'idle', + products: [], + status: "idle", }; -export const incrementAsync = createAsyncThunk( - 'counter/fetchCount', - async (amount) => { - const response = await fetchCount(amount); +export const fetchAllProductsAsync = createAsyncThunk( + "product/fetchAllProducts", + async () => { + const response = await fetchAllProducts(); // The value we return becomes the `fulfilled` action payload return response.data; } ); -export const counterSlice = createSlice({ - name: 'counter', +export const productSlice = createSlice({ + name: "product", initialState, reducers: { increment: (state) => { @@ -25,18 +25,18 @@ export const counterSlice = createSlice({ }, extraReducers: (builder) => { builder - .addCase(incrementAsync.pending, (state) => { - state.status = 'loading'; + .addCase(fetchAllProductsAsync.pending, (state) => { + state.status = "loading"; }) - .addCase(incrementAsync.fulfilled, (state, action) => { - state.status = 'idle'; - state.value += action.payload; + .addCase(fetchAllProductsAsync.fulfilled, (state, action) => { + state.status = "idle"; + state.products = action.payload; }); }, }); -export const { increment } = counterSlice.actions; +export const { increment } = productSlice.actions; -export const selectCount = (state) => state.counter.value; +export const selectAllProducts = (state) => state.product.products; -export default counterSlice.reducer; +export default productSlice.reducer; From 37a15cb8d9ba7d32ec268b94aa138034b835391e Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Wed, 12 Jun 2024 16:11:05 +0530 Subject: [PATCH 04/47] adding brands and categories section extracting from json (using map) --- .../product/components/ProductList.js | 61 +- test.js | 1810 +++++++++++++++++ 2 files changed, 1846 insertions(+), 25 deletions(-) create mode 100644 test.js diff --git a/src/features/product/components/ProductList.js b/src/features/product/components/ProductList.js index 225961a..823a07e 100644 --- a/src/features/product/components/ProductList.js +++ b/src/features/product/components/ProductList.js @@ -23,38 +23,49 @@ const sortOptions = [ const filters = [ { - id: "color", - name: "Color", + id: "brand", + name: "Brands", options: [ - { value: "white", label: "White", checked: false }, - { value: "beige", label: "Beige", checked: false }, - { value: "blue", label: "Blue", checked: true }, - { value: "brown", label: "Brown", checked: false }, - { value: "green", label: "Green", checked: false }, - { value: "purple", label: "Purple", checked: false }, + { value: "Essence", label: "Essence", checked: false }, + + { value: "Glamour Beauty", label: "Glamour Beauty", checked: false }, + + { value: "Velvet Touch", label: "Velvet Touch", checked: false }, + + { value: "Chic Cosmetics", label: "Chic Cosmetics", checked: false }, + + { value: "Nail Couture", label: "Nail Couture", checked: false }, + + { value: "Calvin Klein", label: "Calvin Klein", checked: false }, + + { value: "Chanel", label: "Chanel", checked: false }, + + { value: "Dior", label: "Dior", checked: false }, + + { value: "Dolce & Gabbana", label: "Dolce & Gabbana", checked: false }, + + { value: "Gucci", label: "Gucci", checked: false }, + + { value: "Annibale Colombo", label: "Annibale Colombo", checked: false }, + + { value: "Furniture Co.", label: "Furniture Co.", checked: false }, + + { value: "Knoll", label: "Knoll", checked: false }, + + { value: "Bath Trends", label: "Bath Trends", checked: false }, ], }, { id: "category", name: "Category", options: [ - { value: "new-arrivals", label: "New Arrivals", checked: false }, - { value: "sale", label: "Sale", checked: false }, - { value: "travel", label: "Travel", checked: true }, - { value: "organization", label: "Organization", checked: false }, - { value: "accessories", label: "Accessories", checked: false }, - ], - }, - { - id: "size", - name: "Size", - options: [ - { value: "2l", label: "2L", checked: false }, - { value: "6l", label: "6L", checked: false }, - { value: "12l", label: "12L", checked: false }, - { value: "18l", label: "18L", checked: false }, - { value: "20l", label: "20L", checked: false }, - { value: "40l", label: "40L", checked: true }, + { value: "beauty", label: "Beauty", checked: false }, + + { value: "fragrances", label: "Fragrances", checked: false }, + + { value: "furniture", label: "Furniture", checked: false }, + + { value: "groceries", label: "Groceries", checked: false }, ], }, ]; diff --git a/test.js b/test.js new file mode 100644 index 0000000..3f1eb36 --- /dev/null +++ b/test.js @@ -0,0 +1,1810 @@ +const products = [ + { + id: 1, + title: "Essence Mascara Lash Princess", + 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, + stock: 5, + tags: ["beauty", "mascara"], + brand: "Essence", + sku: "RCH45Q1A", + weight: 2, + dimensions: { + width: 23.17, + height: 14.43, + depth: 28.01, + }, + warrantyInformation: "1 month warranty", + shippingInformation: "Ships in 1 month", + availabilityStatus: "Low Stock", + reviews: [ + { + rating: 2, + comment: "Very unhappy with my purchase!", + date: "2024-05-23T08:56:21.618Z", + reviewerName: "John Doe", + reviewerEmail: "john.doe@x.dummyjson.com", + }, + { + rating: 2, + comment: "Not as described!", + date: "2024-05-23T08:56:21.618Z", + reviewerName: "Nolan Gonzalez", + reviewerEmail: "nolan.gonzalez@x.dummyjson.com", + }, + { + rating: 5, + comment: "Very satisfied!", + date: "2024-05-23T08:56:21.618Z", + reviewerName: "Scarlett Wright", + reviewerEmail: "scarlett.wright@x.dummyjson.com", + }, + ], + returnPolicy: "30 days return policy", + minimumOrderQuantity: 24, + meta: { + createdAt: "2024-05-23T08:56:21.618Z", + updatedAt: "2024-05-23T08:56:21.618Z", + barcode: "9164035109868", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "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", + }, + { + id: 2, + title: "Eyeshadow Palette with Mirror", + description: + "The Eyeshadow Palette with Mirror offers a versatile range of eyeshadow shades for creating stunning eye looks. With a built-in mirror, it's convenient for on-the-go makeup application.", + category: "beauty", + price: 19.99, + discountPercentage: 5.5, + rating: 3.28, + stock: 44, + tags: ["beauty", "eyeshadow"], + brand: "Glamour Beauty", + sku: "MVCFH27F", + weight: 3, + dimensions: { + width: 12.42, + height: 8.63, + depth: 29.13, + }, + warrantyInformation: "1 year warranty", + shippingInformation: "Ships in 2 weeks", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 4, + comment: "Very satisfied!", + date: "2024-05-23T08:56:21.618Z", + reviewerName: "Liam Garcia", + reviewerEmail: "liam.garcia@x.dummyjson.com", + }, + { + rating: 1, + comment: "Very disappointed!", + date: "2024-05-23T08:56:21.618Z", + reviewerName: "Nora Russell", + reviewerEmail: "nora.russell@x.dummyjson.com", + }, + { + rating: 5, + comment: "Highly impressed!", + date: "2024-05-23T08:56:21.618Z", + reviewerName: "Elena Baker", + reviewerEmail: "elena.baker@x.dummyjson.com", + }, + ], + returnPolicy: "30 days return policy", + minimumOrderQuantity: 32, + meta: { + createdAt: "2024-05-23T08:56:21.618Z", + updatedAt: "2024-05-23T08:56:21.618Z", + barcode: "2817839095220", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/thumbnail.png", + }, + { + id: 3, + 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, + discountPercentage: 18.14, + rating: 3.82, + stock: 59, + tags: ["beauty", "face powder"], + brand: "Velvet Touch", + sku: "9EN8WLT2", + weight: 8, + dimensions: { + width: 24.16, + height: 10.7, + depth: 11.07, + }, + warrantyInformation: "2 year warranty", + shippingInformation: "Ships in 1-2 business days", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Very happy with my purchase!", + date: "2024-05-23T08:56:21.618Z", + reviewerName: "Ethan Thompson", + reviewerEmail: "ethan.thompson@x.dummyjson.com", + }, + { + rating: 4, + comment: "Great value for money!", + date: "2024-05-23T08:56:21.618Z", + reviewerName: "Levi Hicks", + reviewerEmail: "levi.hicks@x.dummyjson.com", + }, + { + rating: 5, + comment: "Highly impressed!", + date: "2024-05-23T08:56:21.618Z", + reviewerName: "Hazel Gardner", + reviewerEmail: "hazel.gardner@x.dummyjson.com", + }, + ], + returnPolicy: "60 days return policy", + minimumOrderQuantity: 25, + meta: { + createdAt: "2024-05-23T08:56:21.618Z", + updatedAt: "2024-05-23T08:56:21.618Z", + barcode: "0516267971277", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png", + }, + { + id: 4, + title: "Red Lipstick", + description: + "The Red Lipstick is a classic and bold choice for adding a pop of color to your lips. With a creamy and pigmented formula, it provides a vibrant and long-lasting finish.", + category: "beauty", + price: 12.99, + discountPercentage: 19.03, + rating: 2.51, + stock: 68, + tags: ["beauty", "lipstick"], + brand: "Chic Cosmetics", + sku: "O5IF1NTA", + weight: 5, + dimensions: { + width: 14.37, + height: 13.94, + depth: 14.6, + }, + warrantyInformation: "Lifetime warranty", + shippingInformation: "Ships in 2 weeks", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Great product!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Leo Rivera", + reviewerEmail: "leo.rivera@x.dummyjson.com", + }, + { + rating: 4, + comment: "Very pleased!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Oscar Powers", + reviewerEmail: "oscar.powers@x.dummyjson.com", + }, + { + rating: 5, + comment: "Very pleased!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Carter Rivera", + reviewerEmail: "carter.rivera@x.dummyjson.com", + }, + ], + returnPolicy: "90 days return policy", + minimumOrderQuantity: 6, + meta: { + createdAt: "2024-05-23T08:56:21.619Z", + updatedAt: "2024-05-23T08:56:21.619Z", + barcode: "9444582199406", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/thumbnail.png", + }, + { + id: 5, + title: "Red Nail Polish", + description: + "The Red Nail Polish offers a rich and glossy red hue for vibrant and polished nails. With a quick-drying formula, it provides a salon-quality finish at home.", + category: "beauty", + price: 8.99, + discountPercentage: 2.46, + rating: 3.91, + stock: 71, + tags: ["beauty", "nail polish"], + brand: "Nail Couture", + sku: "YUIIIP4W", + weight: 9, + dimensions: { + width: 8.11, + height: 10.89, + depth: 29.06, + }, + warrantyInformation: "1 year warranty", + shippingInformation: "Ships in 1 week", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Very pleased!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Leo Rivera", + reviewerEmail: "leo.rivera@x.dummyjson.com", + }, + { + rating: 5, + comment: "Great product!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Evan Reed", + reviewerEmail: "evan.reed@x.dummyjson.com", + }, + { + rating: 4, + comment: "Highly recommended!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Evelyn Sanchez", + reviewerEmail: "evelyn.sanchez@x.dummyjson.com", + }, + ], + returnPolicy: "No return policy", + minimumOrderQuantity: 46, + meta: { + createdAt: "2024-05-23T08:56:21.619Z", + updatedAt: "2024-05-23T08:56:21.619Z", + barcode: "3212847902461", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/beauty/Red%20Nail%20Polish/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/beauty/Red%20Nail%20Polish/thumbnail.png", + }, + { + id: 6, + title: "Calvin Klein CK One", + description: + "CK One by Calvin Klein is a classic unisex fragrance, known for its fresh and clean scent. It's a versatile fragrance suitable for everyday wear.", + category: "fragrances", + price: 49.99, + discountPercentage: 0.32, + rating: 4.85, + stock: 17, + tags: ["fragrances", "perfumes"], + brand: "Calvin Klein", + sku: "DZM2JQZE", + weight: 5, + dimensions: { + width: 11.53, + height: 14.44, + depth: 6.81, + }, + warrantyInformation: "5 year warranty", + shippingInformation: "Ships overnight", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Great value for money!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Sophia Brown", + reviewerEmail: "sophia.brown@x.dummyjson.com", + }, + { + rating: 3, + comment: "Very disappointed!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Madison Collins", + reviewerEmail: "madison.collins@x.dummyjson.com", + }, + { + rating: 1, + comment: "Poor quality!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Maya Reed", + reviewerEmail: "maya.reed@x.dummyjson.com", + }, + ], + returnPolicy: "No return policy", + minimumOrderQuantity: 20, + meta: { + createdAt: "2024-05-23T08:56:21.619Z", + updatedAt: "2024-05-23T08:56:21.619Z", + barcode: "2210136215089", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/fragrances/Calvin%20Klein%20CK%20One/1.png", + "https://cdn.dummyjson.com/products/images/fragrances/Calvin%20Klein%20CK%20One/2.png", + "https://cdn.dummyjson.com/products/images/fragrances/Calvin%20Klein%20CK%20One/3.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/fragrances/Calvin%20Klein%20CK%20One/thumbnail.png", + }, + { + id: 7, + title: "Chanel Coco Noir Eau De", + description: + "Coco Noir by Chanel is an elegant and mysterious fragrance, featuring notes of grapefruit, rose, and sandalwood. Perfect for evening occasions.", + category: "fragrances", + price: 129.99, + discountPercentage: 18.64, + rating: 2.76, + stock: 41, + tags: ["fragrances", "perfumes"], + brand: "Chanel", + sku: "K71HBCGS", + weight: 4, + dimensions: { + width: 21.27, + height: 28, + depth: 11.89, + }, + warrantyInformation: "1 week warranty", + shippingInformation: "Ships in 1 month", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 1, + comment: "Disappointing product!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Lincoln Kelly", + reviewerEmail: "lincoln.kelly@x.dummyjson.com", + }, + { + rating: 4, + comment: "Great product!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Lincoln Kelly", + reviewerEmail: "lincoln.kelly@x.dummyjson.com", + }, + { + rating: 4, + comment: "Excellent quality!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Lucas Allen", + reviewerEmail: "lucas.allen@x.dummyjson.com", + }, + ], + returnPolicy: "60 days return policy", + minimumOrderQuantity: 5, + meta: { + createdAt: "2024-05-23T08:56:21.619Z", + updatedAt: "2024-05-23T08:56:21.619Z", + barcode: "1435582999795", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/fragrances/Chanel%20Coco%20Noir%20Eau%20De/1.png", + "https://cdn.dummyjson.com/products/images/fragrances/Chanel%20Coco%20Noir%20Eau%20De/2.png", + "https://cdn.dummyjson.com/products/images/fragrances/Chanel%20Coco%20Noir%20Eau%20De/3.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/fragrances/Chanel%20Coco%20Noir%20Eau%20De/thumbnail.png", + }, + { + id: 8, + title: "Dior J'adore", + description: + "J'adore by Dior is a luxurious and floral fragrance, known for its blend of ylang-ylang, rose, and jasmine. It embodies femininity and sophistication.", + category: "fragrances", + price: 89.99, + discountPercentage: 17.44, + rating: 3.31, + stock: 91, + tags: ["fragrances", "perfumes"], + brand: "Dior", + sku: "E70NB03B", + weight: 10, + dimensions: { + width: 21.51, + height: 7, + depth: 26.51, + }, + warrantyInformation: "Lifetime warranty", + shippingInformation: "Ships in 2 weeks", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Fast shipping!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Zoe Nicholson", + reviewerEmail: "zoe.nicholson@x.dummyjson.com", + }, + { + rating: 4, + comment: "Excellent quality!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Addison Wright", + reviewerEmail: "addison.wright@x.dummyjson.com", + }, + { + rating: 4, + comment: "Would buy again!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Clara Berry", + reviewerEmail: "clara.berry@x.dummyjson.com", + }, + ], + returnPolicy: "7 days return policy", + minimumOrderQuantity: 8, + meta: { + createdAt: "2024-05-23T08:56:21.619Z", + updatedAt: "2024-05-23T08:56:21.619Z", + barcode: "0887083199279", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/fragrances/Dior%20J'adore/1.png", + "https://cdn.dummyjson.com/products/images/fragrances/Dior%20J'adore/2.png", + "https://cdn.dummyjson.com/products/images/fragrances/Dior%20J'adore/3.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/fragrances/Dior%20J'adore/thumbnail.png", + }, + { + id: 9, + title: "Dolce Shine Eau de", + description: + "Dolce Shine by Dolce & Gabbana is a vibrant and fruity fragrance, featuring notes of mango, jasmine, and blonde woods. It's a joyful and youthful scent.", + category: "fragrances", + price: 69.99, + discountPercentage: 11.47, + rating: 2.68, + stock: 3, + tags: ["fragrances", "perfumes"], + brand: "Dolce & Gabbana", + sku: "1NBFK980", + weight: 5, + dimensions: { + width: 17, + height: 24.57, + depth: 13.3, + }, + warrantyInformation: "5 year warranty", + shippingInformation: "Ships in 1-2 business days", + availabilityStatus: "Low Stock", + reviews: [ + { + rating: 4, + comment: "Very satisfied!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Xavier Wright", + reviewerEmail: "xavier.wright@x.dummyjson.com", + }, + { + rating: 1, + comment: "Poor quality!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Mila Hernandez", + reviewerEmail: "mila.hernandez@x.dummyjson.com", + }, + { + rating: 5, + comment: "Very happy with my purchase!", + date: "2024-05-23T08:56:21.619Z", + reviewerName: "Sophia Brown", + reviewerEmail: "sophia.brown@x.dummyjson.com", + }, + ], + returnPolicy: "30 days return policy", + minimumOrderQuantity: 9, + meta: { + createdAt: "2024-05-23T08:56:21.619Z", + updatedAt: "2024-05-23T08:56:21.619Z", + barcode: "1939383392674", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/fragrances/Dolce%20Shine%20Eau%20de/1.png", + "https://cdn.dummyjson.com/products/images/fragrances/Dolce%20Shine%20Eau%20de/2.png", + "https://cdn.dummyjson.com/products/images/fragrances/Dolce%20Shine%20Eau%20de/3.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/fragrances/Dolce%20Shine%20Eau%20de/thumbnail.png", + }, + { + id: 10, + title: "Gucci Bloom Eau de", + description: + "Gucci Bloom by Gucci is a floral and captivating fragrance, with notes of tuberose, jasmine, and Rangoon creeper. It's a modern and romantic scent.", + category: "fragrances", + price: 79.99, + discountPercentage: 8.9, + rating: 2.69, + stock: 93, + tags: ["fragrances", "perfumes"], + brand: "Gucci", + sku: "FFKZ6HOF", + weight: 10, + dimensions: { + width: 22.28, + height: 17.81, + depth: 27.18, + }, + warrantyInformation: "No warranty", + shippingInformation: "Ships in 2 weeks", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Great value for money!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Aria Parker", + reviewerEmail: "aria.parker@x.dummyjson.com", + }, + { + rating: 4, + comment: "Excellent quality!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Natalie Harris", + reviewerEmail: "natalie.harris@x.dummyjson.com", + }, + { + rating: 4, + comment: "Fast shipping!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Ava Harris", + reviewerEmail: "ava.harris@x.dummyjson.com", + }, + ], + returnPolicy: "No return policy", + minimumOrderQuantity: 10, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "8232190382069", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/fragrances/Gucci%20Bloom%20Eau%20de/1.png", + "https://cdn.dummyjson.com/products/images/fragrances/Gucci%20Bloom%20Eau%20de/2.png", + "https://cdn.dummyjson.com/products/images/fragrances/Gucci%20Bloom%20Eau%20de/3.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/fragrances/Gucci%20Bloom%20Eau%20de/thumbnail.png", + }, + { + id: 11, + title: "Annibale Colombo Bed", + description: + "The Annibale Colombo Bed is a luxurious and elegant bed frame, crafted with high-quality materials for a comfortable and stylish bedroom.", + category: "furniture", + price: 1899.99, + discountPercentage: 0.29, + rating: 4.14, + stock: 47, + tags: ["furniture", "beds"], + brand: "Annibale Colombo", + sku: "4KMDTZWF", + weight: 3, + dimensions: { + width: 28.75, + height: 26.88, + depth: 24.47, + }, + warrantyInformation: "2 year warranty", + shippingInformation: "Ships overnight", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 4, + comment: "Great value for money!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Julian Newton", + reviewerEmail: "julian.newton@x.dummyjson.com", + }, + { + rating: 5, + comment: "Would buy again!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Madison Collins", + reviewerEmail: "madison.collins@x.dummyjson.com", + }, + { + rating: 4, + comment: "Would buy again!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Clara Berry", + reviewerEmail: "clara.berry@x.dummyjson.com", + }, + ], + returnPolicy: "7 days return policy", + minimumOrderQuantity: 1, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "7113807059215", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Bed/1.png", + "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Bed/2.png", + "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Bed/3.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Bed/thumbnail.png", + }, + { + id: 12, + title: "Annibale Colombo Sofa", + description: + "The Annibale Colombo Sofa is a sophisticated and comfortable seating option, featuring exquisite design and premium upholstery for your living room.", + category: "furniture", + price: 2499.99, + discountPercentage: 18.54, + rating: 3.08, + stock: 16, + tags: ["furniture", "sofas"], + brand: "Annibale Colombo", + sku: "LUU95CQP", + weight: 3, + dimensions: { + width: 20.97, + height: 19.11, + depth: 25.81, + }, + warrantyInformation: "1 month warranty", + shippingInformation: "Ships overnight", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Very satisfied!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Tyler Davis", + reviewerEmail: "tyler.davis@x.dummyjson.com", + }, + { + rating: 5, + comment: "Excellent quality!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Hannah Robinson", + reviewerEmail: "hannah.robinson@x.dummyjson.com", + }, + { + rating: 3, + comment: "Waste of money!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Madison Collins", + reviewerEmail: "madison.collins@x.dummyjson.com", + }, + ], + returnPolicy: "7 days return policy", + minimumOrderQuantity: 1, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "0426785817074", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Sofa/1.png", + "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Sofa/2.png", + "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Sofa/3.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Sofa/thumbnail.png", + }, + { + id: 13, + title: "Bedside Table African Cherry", + description: + "The Bedside Table in African Cherry is a stylish and functional addition to your bedroom, providing convenient storage space and a touch of elegance.", + category: "furniture", + price: 299.99, + discountPercentage: 9.58, + rating: 4.48, + stock: 16, + tags: ["furniture", "bedside tables"], + brand: "Furniture Co.", + sku: "OWPLTZYX", + weight: 10, + dimensions: { + width: 25.43, + height: 20.2, + depth: 24.95, + }, + warrantyInformation: "6 months warranty", + shippingInformation: "Ships in 1-2 business days", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Very happy with my purchase!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "John Doe", + reviewerEmail: "john.doe@x.dummyjson.com", + }, + { + rating: 4, + comment: "Highly recommended!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Avery Carter", + reviewerEmail: "avery.carter@x.dummyjson.com", + }, + { + rating: 4, + comment: "Very pleased!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Evelyn Sanchez", + reviewerEmail: "evelyn.sanchez@x.dummyjson.com", + }, + ], + returnPolicy: "No return policy", + minimumOrderQuantity: 5, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "2913244159666", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/furniture/Bedside%20Table%20African%20Cherry/1.png", + "https://cdn.dummyjson.com/products/images/furniture/Bedside%20Table%20African%20Cherry/2.png", + "https://cdn.dummyjson.com/products/images/furniture/Bedside%20Table%20African%20Cherry/3.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/furniture/Bedside%20Table%20African%20Cherry/thumbnail.png", + }, + { + id: 14, + title: "Knoll Saarinen Executive Conference Chair", + description: + "The Knoll Saarinen Executive Conference Chair is a modern and ergonomic chair, perfect for your office or conference room with its timeless design.", + category: "furniture", + price: 499.99, + discountPercentage: 15.23, + rating: 4.11, + stock: 47, + tags: ["furniture", "office chairs"], + brand: "Knoll", + sku: "RKHVJ4FE", + weight: 3, + dimensions: { + width: 16.59, + height: 21.46, + depth: 29.07, + }, + warrantyInformation: "Lifetime warranty", + shippingInformation: "Ships in 3-5 business days", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 4, + comment: "Very happy with my purchase!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Leah Gutierrez", + reviewerEmail: "leah.gutierrez@x.dummyjson.com", + }, + { + rating: 4, + comment: "Would buy again!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Nolan Gonzalez", + reviewerEmail: "nolan.gonzalez@x.dummyjson.com", + }, + { + rating: 2, + comment: "Waste of money!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Stella Morris", + reviewerEmail: "stella.morris@x.dummyjson.com", + }, + ], + returnPolicy: "30 days return policy", + minimumOrderQuantity: 5, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "0726316339746", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/furniture/Knoll%20Saarinen%20Executive%20Conference%20Chair/1.png", + "https://cdn.dummyjson.com/products/images/furniture/Knoll%20Saarinen%20Executive%20Conference%20Chair/2.png", + "https://cdn.dummyjson.com/products/images/furniture/Knoll%20Saarinen%20Executive%20Conference%20Chair/3.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/furniture/Knoll%20Saarinen%20Executive%20Conference%20Chair/thumbnail.png", + }, + { + id: 15, + title: "Wooden Bathroom Sink With Mirror", + description: + "The Wooden Bathroom Sink with Mirror is a unique and stylish addition to your bathroom, featuring a wooden sink countertop and a matching mirror.", + category: "furniture", + price: 799.99, + discountPercentage: 11.22, + rating: 3.26, + stock: 95, + tags: ["furniture", "bathroom"], + brand: "Bath Trends", + sku: "7OLTIEVO", + weight: 6, + dimensions: { + width: 7.32, + height: 22.64, + depth: 12.37, + }, + warrantyInformation: "6 months warranty", + shippingInformation: "Ships in 3-5 business days", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Highly recommended!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Charlotte Lopez", + reviewerEmail: "charlotte.lopez@x.dummyjson.com", + }, + { + rating: 1, + comment: "Would not recommend!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "William Gonzalez", + reviewerEmail: "william.gonzalez@x.dummyjson.com", + }, + { + rating: 2, + comment: "Not worth the price!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Ava Harrison", + reviewerEmail: "ava.harrison@x.dummyjson.com", + }, + ], + returnPolicy: "7 days return policy", + minimumOrderQuantity: 1, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "7839797529453", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/furniture/Wooden%20Bathroom%20Sink%20With%20Mirror/1.png", + "https://cdn.dummyjson.com/products/images/furniture/Wooden%20Bathroom%20Sink%20With%20Mirror/2.png", + "https://cdn.dummyjson.com/products/images/furniture/Wooden%20Bathroom%20Sink%20With%20Mirror/3.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/furniture/Wooden%20Bathroom%20Sink%20With%20Mirror/thumbnail.png", + }, + { + id: 16, + title: "Apple", + description: + "Fresh and crisp apples, perfect for snacking or incorporating into various recipes.", + category: "groceries", + price: 1.99, + discountPercentage: 1.97, + rating: 2.96, + stock: 9, + tags: ["fruits"], + sku: "QTROUV79", + weight: 8, + dimensions: { + width: 8.29, + height: 5.58, + depth: 12.41, + }, + warrantyInformation: "2 year warranty", + shippingInformation: "Ships in 2 weeks", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 4, + comment: "Great product!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Logan Lee", + reviewerEmail: "logan.lee@x.dummyjson.com", + }, + { + rating: 4, + comment: "Great product!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Elena Long", + reviewerEmail: "elena.long@x.dummyjson.com", + }, + { + rating: 1, + comment: "Not as described!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Grayson Coleman", + reviewerEmail: "grayson.coleman@x.dummyjson.com", + }, + ], + returnPolicy: "60 days return policy", + minimumOrderQuantity: 44, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "2517819903837", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: ["https://cdn.dummyjson.com/products/images/groceries/Apple/1.png"], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Apple/thumbnail.png", + }, + { + id: 17, + title: "Beef Steak", + description: + "High-quality beef steak, great for grilling or cooking to your preferred level of doneness.", + category: "groceries", + price: 12.99, + discountPercentage: 17.99, + rating: 2.83, + stock: 96, + tags: ["meat"], + sku: "BWWA2MSO", + weight: 9, + dimensions: { + width: 23.35, + height: 13.48, + depth: 26.4, + }, + warrantyInformation: "1 month warranty", + shippingInformation: "Ships overnight", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 4, + comment: "Very pleased!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Ethan Martinez", + reviewerEmail: "ethan.martinez@x.dummyjson.com", + }, + { + rating: 3, + comment: "Disappointing product!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Owen Fisher", + reviewerEmail: "owen.fisher@x.dummyjson.com", + }, + { + rating: 4, + comment: "Very happy with my purchase!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Scarlett Wright", + reviewerEmail: "scarlett.wright@x.dummyjson.com", + }, + ], + returnPolicy: "90 days return policy", + minimumOrderQuantity: 21, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "8335515097879", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/groceries/Beef%20Steak/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Beef%20Steak/thumbnail.png", + }, + { + id: 18, + title: "Cat Food", + description: + "Nutritious cat food formulated to meet the dietary needs of your feline friend.", + category: "groceries", + price: 8.99, + discountPercentage: 9.57, + rating: 2.88, + stock: 13, + tags: ["pet supplies", "cat food"], + sku: "C3F8QN6O", + weight: 9, + dimensions: { + width: 15.4, + height: 13.97, + depth: 25.13, + }, + warrantyInformation: "3 months warranty", + shippingInformation: "Ships in 1-2 business days", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Very pleased!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Mateo Bennett", + reviewerEmail: "mateo.bennett@x.dummyjson.com", + }, + { + rating: 5, + comment: "Very pleased!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Aurora Barnes", + reviewerEmail: "aurora.barnes@x.dummyjson.com", + }, + { + rating: 5, + comment: "Great value for money!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Ellie Stewart", + reviewerEmail: "ellie.stewart@x.dummyjson.com", + }, + ], + returnPolicy: "7 days return policy", + minimumOrderQuantity: 48, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "5503491330693", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/groceries/Cat%20Food/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Cat%20Food/thumbnail.png", + }, + { + id: 19, + title: "Chicken Meat", + description: + "Fresh and tender chicken meat, suitable for various culinary preparations.", + category: "groceries", + price: 9.99, + discountPercentage: 10.46, + rating: 4.61, + stock: 69, + tags: ["meat"], + sku: "G5YEHW7B", + weight: 7, + dimensions: { + width: 15.96, + height: 29.24, + depth: 26.25, + }, + warrantyInformation: "Lifetime warranty", + shippingInformation: "Ships in 1 month", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Very satisfied!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Sophia Jones", + reviewerEmail: "sophia.jones@x.dummyjson.com", + }, + { + rating: 5, + comment: "Great value for money!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Maya Reed", + reviewerEmail: "maya.reed@x.dummyjson.com", + }, + { + rating: 4, + comment: "Highly recommended!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Harper Turner", + reviewerEmail: "harper.turner@x.dummyjson.com", + }, + ], + returnPolicy: "7 days return policy", + minimumOrderQuantity: 46, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "0966223559510", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/groceries/Chicken%20Meat/1.png", + "https://cdn.dummyjson.com/products/images/groceries/Chicken%20Meat/2.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Chicken%20Meat/thumbnail.png", + }, + { + id: 20, + title: "Cooking Oil", + description: + "Versatile cooking oil suitable for frying, sautéing, and various culinary applications.", + category: "groceries", + price: 4.99, + discountPercentage: 18.89, + rating: 4.01, + stock: 22, + tags: ["cooking essentials"], + sku: "Q6ZP1UY8", + weight: 8, + dimensions: { + width: 8.18, + height: 27.45, + depth: 27.88, + }, + warrantyInformation: "Lifetime warranty", + shippingInformation: "Ships in 1 month", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Would buy again!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Mason Parker", + reviewerEmail: "mason.parker@x.dummyjson.com", + }, + { + rating: 3, + comment: "Poor quality!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Jonathan Pierce", + reviewerEmail: "jonathan.pierce@x.dummyjson.com", + }, + { + rating: 5, + comment: "Would buy again!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Alexander Hernandez", + reviewerEmail: "alexander.hernandez@x.dummyjson.com", + }, + ], + returnPolicy: "60 days return policy", + minimumOrderQuantity: 2, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "6707669443381", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/groceries/Cooking%20Oil/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Cooking%20Oil/thumbnail.png", + }, + { + id: 21, + title: "Cucumber", + description: + "Crisp and hydrating cucumbers, ideal for salads, snacks, or as a refreshing side.", + category: "groceries", + price: 1.49, + discountPercentage: 11.44, + rating: 4.71, + stock: 22, + tags: ["vegetables"], + sku: "6KGF2K6Z", + weight: 9, + dimensions: { + width: 11.04, + height: 20.5, + depth: 8.18, + }, + warrantyInformation: "5 year warranty", + shippingInformation: "Ships overnight", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 4, + comment: "Very satisfied!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Elijah Hill", + reviewerEmail: "elijah.hill@x.dummyjson.com", + }, + { + rating: 5, + comment: "Fast shipping!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Liam Garcia", + reviewerEmail: "liam.garcia@x.dummyjson.com", + }, + { + rating: 4, + comment: "Excellent quality!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Ella Cook", + reviewerEmail: "ella.cook@x.dummyjson.com", + }, + ], + returnPolicy: "30 days return policy", + minimumOrderQuantity: 7, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "2597004869708", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/groceries/Cucumber/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Cucumber/thumbnail.png", + }, + { + id: 22, + title: "Dog Food", + description: + "Specially formulated dog food designed to provide essential nutrients for your canine companion.", + category: "groceries", + price: 10.99, + discountPercentage: 18.15, + rating: 2.74, + stock: 40, + tags: ["pet supplies", "dog food"], + sku: "A6QRCH37", + weight: 2, + dimensions: { + width: 29.39, + height: 29.77, + depth: 20.54, + }, + warrantyInformation: "1 year warranty", + shippingInformation: "Ships in 1 month", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Highly impressed!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Leo Rivera", + reviewerEmail: "leo.rivera@x.dummyjson.com", + }, + { + rating: 4, + comment: "Highly recommended!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Alexander Jones", + reviewerEmail: "alexander.jones@x.dummyjson.com", + }, + { + rating: 4, + comment: "Would buy again!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Addison Wright", + reviewerEmail: "addison.wright@x.dummyjson.com", + }, + ], + returnPolicy: "90 days return policy", + minimumOrderQuantity: 29, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "7957222289508", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/groceries/Dog%20Food/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Dog%20Food/thumbnail.png", + }, + { + id: 23, + title: "Eggs", + description: + "Fresh eggs, a versatile ingredient for baking, cooking, or breakfast.", + category: "groceries", + price: 2.99, + discountPercentage: 5.8, + rating: 4.46, + stock: 10, + tags: ["dairy"], + sku: "YA617RI7", + weight: 4, + dimensions: { + width: 12.3, + height: 10.99, + depth: 15.53, + }, + warrantyInformation: "3 year warranty", + shippingInformation: "Ships overnight", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 2, + comment: "Very unhappy with my purchase!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Mateo Perez", + reviewerEmail: "mateo.perez@x.dummyjson.com", + }, + { + rating: 4, + comment: "Very happy with my purchase!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Cameron Perez", + reviewerEmail: "cameron.perez@x.dummyjson.com", + }, + { + rating: 5, + comment: "Very happy with my purchase!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Aurora Barnes", + reviewerEmail: "aurora.barnes@x.dummyjson.com", + }, + ], + returnPolicy: "30 days return policy", + minimumOrderQuantity: 43, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "7095702028776", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: ["https://cdn.dummyjson.com/products/images/groceries/Eggs/1.png"], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Eggs/thumbnail.png", + }, + { + id: 24, + title: "Fish Steak", + description: + "Quality fish steak, suitable for grilling, baking, or pan-searing.", + category: "groceries", + price: 14.99, + discountPercentage: 7, + rating: 4.83, + stock: 99, + tags: ["seafood"], + sku: "XNIH1MTA", + weight: 8, + dimensions: { + width: 20.14, + height: 8.4, + depth: 10.01, + }, + warrantyInformation: "1 year warranty", + shippingInformation: "Ships in 1 month", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Great value for money!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Michael Johnson", + reviewerEmail: "michael.johnson@x.dummyjson.com", + }, + { + rating: 4, + comment: "Would buy again!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Julian Newton", + reviewerEmail: "julian.newton@x.dummyjson.com", + }, + { + rating: 5, + comment: "Excellent quality!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Lila Hudson", + reviewerEmail: "lila.hudson@x.dummyjson.com", + }, + ], + returnPolicy: "30 days return policy", + minimumOrderQuantity: 49, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "4250692197342", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/groceries/Fish%20Steak/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Fish%20Steak/thumbnail.png", + }, + { + id: 25, + title: "Green Bell Pepper", + description: + "Fresh and vibrant green bell pepper, perfect for adding color and flavor to your dishes.", + category: "groceries", + price: 1.29, + discountPercentage: 15.5, + rating: 4.28, + stock: 89, + tags: ["vegetables"], + sku: "HU7S7VQ0", + weight: 7, + dimensions: { + width: 7.32, + height: 14.31, + depth: 21.38, + }, + warrantyInformation: "5 year warranty", + shippingInformation: "Ships overnight", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 4, + comment: "Excellent quality!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Maya Reed", + reviewerEmail: "maya.reed@x.dummyjson.com", + }, + { + rating: 4, + comment: "Would buy again!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Madison Collins", + reviewerEmail: "madison.collins@x.dummyjson.com", + }, + { + rating: 5, + comment: "Would buy again!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Ethan Thompson", + reviewerEmail: "ethan.thompson@x.dummyjson.com", + }, + ], + returnPolicy: "30 days return policy", + minimumOrderQuantity: 1, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "7583442707568", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/groceries/Green%20Bell%20Pepper/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Green%20Bell%20Pepper/thumbnail.png", + }, + { + id: 26, + title: "Green Chili Pepper", + description: + "Spicy green chili pepper, ideal for adding heat to your favorite recipes.", + category: "groceries", + price: 0.99, + discountPercentage: 18.51, + rating: 4.43, + stock: 8, + tags: ["vegetables"], + sku: "Y4RM3JCB", + weight: 2, + dimensions: { + width: 18.67, + height: 21.17, + depth: 25.26, + }, + warrantyInformation: "No warranty", + shippingInformation: "Ships in 1-2 business days", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 2, + comment: "Disappointing product!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Mateo Bennett", + reviewerEmail: "mateo.bennett@x.dummyjson.com", + }, + { + rating: 5, + comment: "Very pleased!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Natalie Price", + reviewerEmail: "natalie.price@x.dummyjson.com", + }, + { + rating: 4, + comment: "Very satisfied!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Avery Barnes", + reviewerEmail: "avery.barnes@x.dummyjson.com", + }, + ], + returnPolicy: "30 days return policy", + minimumOrderQuantity: 43, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "8400326844874", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/groceries/Green%20Chili%20Pepper/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Green%20Chili%20Pepper/thumbnail.png", + }, + { + id: 27, + title: "Honey Jar", + description: + "Pure and natural honey in a convenient jar, perfect for sweetening beverages or drizzling over food.", + category: "groceries", + price: 6.99, + discountPercentage: 1.91, + rating: 3.5, + stock: 25, + tags: ["condiments"], + sku: "BTBNIIOU", + weight: 9, + dimensions: { + width: 26.53, + height: 27.11, + depth: 6.63, + }, + warrantyInformation: "2 year warranty", + shippingInformation: "Ships overnight", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 4, + comment: "Fast shipping!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Nicholas Bailey", + reviewerEmail: "nicholas.bailey@x.dummyjson.com", + }, + { + rating: 5, + comment: "Awesome product!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Gabriel Hayes", + reviewerEmail: "gabriel.hayes@x.dummyjson.com", + }, + { + rating: 5, + comment: "Highly impressed!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "James Garcia", + reviewerEmail: "james.garcia@x.dummyjson.com", + }, + ], + returnPolicy: "90 days return policy", + minimumOrderQuantity: 1, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "0668665656044", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/groceries/Honey%20Jar/1.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Honey%20Jar/thumbnail.png", + }, + { + id: 28, + title: "Ice Cream", + description: + "Creamy and delicious ice cream, available in various flavors for a delightful treat.", + category: "groceries", + price: 5.49, + discountPercentage: 7.58, + rating: 3.77, + stock: 76, + tags: ["desserts"], + sku: "VEZMU1EQ", + weight: 5, + dimensions: { + width: 17.66, + height: 24.49, + depth: 25.98, + }, + warrantyInformation: "2 year warranty", + shippingInformation: "Ships in 2 weeks", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 5, + comment: "Great product!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Elena Baker", + reviewerEmail: "elena.baker@x.dummyjson.com", + }, + { + rating: 5, + comment: "Highly impressed!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Madeline Simpson", + reviewerEmail: "madeline.simpson@x.dummyjson.com", + }, + { + rating: 5, + comment: "Very happy with my purchase!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Caleb Nelson", + reviewerEmail: "caleb.nelson@x.dummyjson.com", + }, + ], + returnPolicy: "No return policy", + minimumOrderQuantity: 19, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "9603960319256", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: [ + "https://cdn.dummyjson.com/products/images/groceries/Ice%20Cream/1.png", + "https://cdn.dummyjson.com/products/images/groceries/Ice%20Cream/2.png", + "https://cdn.dummyjson.com/products/images/groceries/Ice%20Cream/3.png", + "https://cdn.dummyjson.com/products/images/groceries/Ice%20Cream/4.png", + ], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Ice%20Cream/thumbnail.png", + }, + { + id: 29, + title: "Juice", + description: + "Refreshing fruit juice, packed with vitamins and great for staying hydrated.", + category: "groceries", + price: 3.99, + discountPercentage: 5.45, + rating: 3.41, + stock: 99, + tags: ["beverages"], + sku: "M2K19S06", + weight: 2, + dimensions: { + width: 8.97, + height: 12.26, + depth: 15.05, + }, + warrantyInformation: "1 week warranty", + shippingInformation: "Ships in 1-2 business days", + availabilityStatus: "In Stock", + reviews: [ + { + rating: 4, + comment: "Very satisfied!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Leo Rivera", + reviewerEmail: "leo.rivera@x.dummyjson.com", + }, + { + rating: 2, + comment: "Not worth the price!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Ethan Martinez", + reviewerEmail: "ethan.martinez@x.dummyjson.com", + }, + { + rating: 4, + comment: "Excellent quality!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Max Parker", + reviewerEmail: "max.parker@x.dummyjson.com", + }, + ], + returnPolicy: "90 days return policy", + minimumOrderQuantity: 26, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "8546824122355", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: ["https://cdn.dummyjson.com/products/images/groceries/Juice/1.png"], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Juice/thumbnail.png", + }, + { + id: 30, + title: "Kiwi", + description: + "Nutrient-rich kiwi, perfect for snacking or adding a tropical twist to your dishes.", + category: "groceries", + price: 2.49, + discountPercentage: 10.32, + rating: 4.37, + stock: 1, + tags: ["fruits"], + sku: "0X3NORB9", + weight: 8, + dimensions: { + width: 27.3, + height: 7.48, + depth: 15.08, + }, + warrantyInformation: "6 months warranty", + shippingInformation: "Ships in 3-5 business days", + availabilityStatus: "Low Stock", + reviews: [ + { + rating: 5, + comment: "Very pleased!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Nora Russell", + reviewerEmail: "nora.russell@x.dummyjson.com", + }, + { + rating: 5, + comment: "Very pleased!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Dylan Wells", + reviewerEmail: "dylan.wells@x.dummyjson.com", + }, + { + rating: 5, + comment: "Great product!", + date: "2024-05-23T08:56:21.620Z", + reviewerName: "Noah Hernandez", + reviewerEmail: "noah.hernandez@x.dummyjson.com", + }, + ], + returnPolicy: "7 days return policy", + minimumOrderQuantity: 8, + meta: { + createdAt: "2024-05-23T08:56:21.620Z", + updatedAt: "2024-05-23T08:56:21.620Z", + barcode: "3325493172934", + qrCode: "https://dummyjson.com/public/qr-code.png", + }, + images: ["https://cdn.dummyjson.com/products/images/groceries/Kiwi/1.png"], + thumbnail: + "https://cdn.dummyjson.com/products/images/groceries/Kiwi/thumbnail.png", + }, +]; + +// const categories = [...new Set([...products.map((p) => p.category)])]; + +// categories.map((c) => ({ +// value: c, +// label: c.split("-").join(" "), +// checked: false, +// })); + +//categories results:- +// {value: 'beauty', label: 'beauty', checked: false}, +// {value: 'fragrances', label: 'fragrances', checked: false}, +// {value: 'furniture', label: 'furniture', checked: false}, +// {value: 'groceries', label: 'groceries', checked: false}, + +const brands = [...new Set([...products.map((p) => p.brand)])]; + +brands.map((c) => ({ + value: c, + label: c, + checked: false, +})); + +// Brands results:- +// {value: 'Essence', label: 'Essence', checked: false}, +// {value: 'Glamour Beauty', label: 'Glamour Beauty', checked: false}, +// {value: 'Velvet Touch', label: 'Velvet Touch', checked: false}, +// {value: 'Chic Cosmetics', label: 'Chic Cosmetics', checked: false}, +// {value: 'Nail Couture', label: 'Nail Couture', checked: false}, +// {value: 'Calvin Klein', label: 'Calvin Klein', checked: false}, +// {value: 'Chanel', label: 'Chanel', checked: false}, +// {value: 'Dior', label: 'Dior', checked: false}, +// {value: 'Gucci', label: 'Gucci', checked: false}, +// {value: 'Annibale Colombo', label: 'Annibale Colombo', checked: false}, +// {value: 'Furniture Co.', label: 'Furniture Co.', checked: false}, +// {value: 'Knoll', label: 'Knoll', checked: false}, +// {value: 'Bath Trends', label: 'Bath Trends', checked: false}, From a92b677492f72fc71d736df985b2f12f4c846a34 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Thu, 13 Jun 2024 14:02:55 +0530 Subject: [PATCH 05/47] filter feature added using redux and json server api --- .../product/components/ProductList.js | 53 +++++++++---------- src/features/product/productAPI.js | 16 ++++++ src/features/product/productSlice.js | 17 +++++- test.js | 12 ++--- 4 files changed, 64 insertions(+), 34 deletions(-) diff --git a/src/features/product/components/ProductList.js b/src/features/product/components/ProductList.js index 823a07e..0815dd1 100644 --- a/src/features/product/components/ProductList.js +++ b/src/features/product/components/ProductList.js @@ -1,6 +1,10 @@ import React, { useState, Fragment, useEffect } from "react"; import { useSelector, useDispatch } from "react-redux"; -import { fetchAllProductsAsync, selectAllProducts } from "../productSlice"; +import { + fetchAllProductsAsync, + fetchProductsByFiltersAsync, + selectAllProducts, +} from "../productSlice"; import { Dialog, Disclosure, Menu, Transition } from "@headlessui/react"; import { StarIcon, XMarkIcon } from "@heroicons/react/24/outline"; import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/20/solid"; @@ -22,52 +26,36 @@ const sortOptions = [ ]; const filters = [ + { + id: "category", + name: "Category", + options: [ + { value: "beauty", label: "Beauty", checked: false }, + { value: "fragrances", label: "Fragrances", checked: false }, + { value: "furniture", label: "Furniture", checked: false }, + { value: "groceries", label: "Groceries", checked: false }, + ], + }, { id: "brand", name: "Brands", options: [ { value: "Essence", label: "Essence", checked: false }, - { value: "Glamour Beauty", label: "Glamour Beauty", checked: false }, - { value: "Velvet Touch", label: "Velvet Touch", checked: false }, - { value: "Chic Cosmetics", label: "Chic Cosmetics", checked: false }, - { value: "Nail Couture", label: "Nail Couture", checked: false }, - { value: "Calvin Klein", label: "Calvin Klein", checked: false }, - { value: "Chanel", label: "Chanel", checked: false }, - { value: "Dior", label: "Dior", checked: false }, - { value: "Dolce & Gabbana", label: "Dolce & Gabbana", checked: false }, - { value: "Gucci", label: "Gucci", checked: false }, - { value: "Annibale Colombo", label: "Annibale Colombo", checked: false }, - { value: "Furniture Co.", label: "Furniture Co.", checked: false }, - { value: "Knoll", label: "Knoll", checked: false }, - { value: "Bath Trends", label: "Bath Trends", checked: false }, ], }, - { - id: "category", - name: "Category", - options: [ - { value: "beauty", label: "Beauty", checked: false }, - - { value: "fragrances", label: "Fragrances", checked: false }, - - { value: "furniture", label: "Furniture", checked: false }, - - { value: "groceries", label: "Groceries", checked: false }, - ], - }, ]; function classNames(...classes) { @@ -78,10 +66,18 @@ export default function ProductList() { const [mobileFiltersOpen, setMobileFiltersOpen] = useState(false); const products = useSelector(selectAllProducts); const dispatch = useDispatch(); + const [filter, setFilter] = useState({}); + const handleFilter = (e, section, option) => { + const newFilter = { ...filter, [section.id]: option.value }; + setFilter(newFilter); + dispatch(fetchProductsByFiltersAsync(newFilter)); + console.log(section.id, option.value); + }; useEffect(() => { dispatch(fetchAllProductsAsync()); }, [dispatch]); + return ( <div className="bg-white"> <div> @@ -314,6 +310,9 @@ export default function ProductList() { defaultValue={option.value} type="checkbox" defaultChecked={option.checked} + onChange={(e) => { + handleFilter(e, section, option); + }} className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500" /> <label diff --git a/src/features/product/productAPI.js b/src/features/product/productAPI.js index 0f33107..afbf55f 100644 --- a/src/features/product/productAPI.js +++ b/src/features/product/productAPI.js @@ -6,3 +6,19 @@ export function fetchAllProducts() { resolve({ data }); }); } +export function fetchProductsByFilters(filter) { + // filter ={"category":"frangrances"} + // filter ={"brand":"Essence"} + let queryString = ""; + for (let key in filter) { + queryString += `${key}=${filter[key]}&`; + } + return new Promise(async (resolve) => { + // TODO: we will not hard coded server url here... + const response = await fetch( + "http://localhost:8080/products?" + queryString + ); + const data = await response.json(); + resolve({ data }); + }); +} diff --git a/src/features/product/productSlice.js b/src/features/product/productSlice.js index f76ca01..1eb1a21 100644 --- a/src/features/product/productSlice.js +++ b/src/features/product/productSlice.js @@ -1,5 +1,5 @@ import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; -import { fetchAllProducts } from "./productAPI"; +import { fetchAllProducts, fetchProductsByFilters } from "./productAPI"; const initialState = { products: [], @@ -14,6 +14,14 @@ export const fetchAllProductsAsync = createAsyncThunk( return response.data; } ); +export const fetchProductsByFiltersAsync = createAsyncThunk( + "product/fetchProductsByFilters", + async (filter) => { + const response = await fetchProductsByFilters(filter); + // The value we return becomes the `fulfilled` action payload + return response.data; + } +); export const productSlice = createSlice({ name: "product", @@ -31,6 +39,13 @@ export const productSlice = createSlice({ .addCase(fetchAllProductsAsync.fulfilled, (state, action) => { state.status = "idle"; state.products = action.payload; + }) + .addCase(fetchProductsByFiltersAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(fetchProductsByFiltersAsync.fulfilled, (state, action) => { + state.status = "idle"; + state.products = action.payload; }); }, }); diff --git a/test.js b/test.js index 3f1eb36..d5c5041 100644 --- a/test.js +++ b/test.js @@ -1772,13 +1772,13 @@ const products = [ }, ]; -// const categories = [...new Set([...products.map((p) => p.category)])]; +const categories = [...new Set([...products.map((p) => p.category)])]; -// categories.map((c) => ({ -// value: c, -// label: c.split("-").join(" "), -// checked: false, -// })); +categories.map((c) => ({ + value: c, + label: c.split("-").join(" "), + checked: false, +})); //categories results:- // {value: 'beauty', label: 'beauty', checked: false}, From 138d9f2c9486b819ea5a6dca30752fa503b5d982 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Fri, 14 Jun 2024 10:31:21 +0530 Subject: [PATCH 06/47] adding sorting functionality(desc according to new ruleJSON Server's behavior for sorting changed with different versions.? _sort=-price) --- .../product/components/ProductList.js | 26 ++++++++++++------- src/features/product/productAPI.js | 16 ++++++++++++ 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/features/product/components/ProductList.js b/src/features/product/components/ProductList.js index 0815dd1..3ebf0ce 100644 --- a/src/features/product/components/ProductList.js +++ b/src/features/product/components/ProductList.js @@ -18,11 +18,9 @@ import { } from "@heroicons/react/20/solid"; const sortOptions = [ - { name: "Most Popular", href: "#", current: true }, - { name: "Best Rating", href: "#", current: false }, - { name: "Newest", href: "#", current: false }, - { name: "Price: Low to High", href: "#", current: false }, - { name: "Price: High to Low", href: "#", current: false }, + { name: "Best Rating", sort: "rating", current: false }, + { name: "Price: Low to High", sort: "price", order: "asc", current: false }, + { name: "Price: High to Low", sort: "price", order: "desc", current: false }, ]; const filters = [ @@ -67,11 +65,19 @@ export default function ProductList() { const products = useSelector(selectAllProducts); const dispatch = useDispatch(); const [filter, setFilter] = useState({}); + const handleFilter = (e, section, option) => { const newFilter = { ...filter, [section.id]: option.value }; setFilter(newFilter); dispatch(fetchProductsByFiltersAsync(newFilter)); - console.log(section.id, option.value); + }; + const handleSort = (e, option) => { + const newFilter = { + ...filter, + _sort: option.order === "desc" ? `-${option.sort}` : option.sort, + }; + setFilter(newFilter); + dispatch(fetchProductsByFiltersAsync(newFilter)); }; useEffect(() => { @@ -223,8 +229,10 @@ export default function ProductList() { {sortOptions.map((option) => ( <Menu.Item key={option.name}> {({ active }) => ( - <a - href={option.href} + <p + onClick={(e) => { + handleSort(e, option); + }} className={classNames( option.current ? "font-medium text-gray-900" @@ -234,7 +242,7 @@ export default function ProductList() { )} > {option.name} - </a> + </p> )} </Menu.Item> ))} diff --git a/src/features/product/productAPI.js b/src/features/product/productAPI.js index afbf55f..318a841 100644 --- a/src/features/product/productAPI.js +++ b/src/features/product/productAPI.js @@ -9,6 +9,22 @@ export function fetchAllProducts() { export function fetchProductsByFilters(filter) { // filter ={"category":"frangrances"} // filter ={"brand":"Essence"} + // TODO:we will on server support mutilple value + + // reference for sorting functionality + // JSON Server's behavior for sorting changed with different versions. + + // To resolve the issue, update JSON Server using the command: + + // npm install -g json-server + + // After updating, sorting by default will be ascending. No need to use _order:asc + + // GET /products?_sort=price + + // For descending order, + + // ***** use GET /products?_sort=-price **** let queryString = ""; for (let key in filter) { queryString += `${key}=${filter[key]}&`; From 049dc8d5e42210cab7f41e7c335f129d96babab4 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Fri, 14 Jun 2024 12:24:07 +0530 Subject: [PATCH 07/47] fixing filter in mobile layout (add onClick func in mobile input) --- src/features/product/components/ProductList.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/features/product/components/ProductList.js b/src/features/product/components/ProductList.js index 3ebf0ce..22dd6f8 100644 --- a/src/features/product/components/ProductList.js +++ b/src/features/product/components/ProductList.js @@ -174,6 +174,9 @@ export default function ProductList() { defaultValue={option.value} type="checkbox" defaultChecked={option.checked} + onChange={(e) => { + handleFilter(e, section, option); + }} className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500" /> <label @@ -361,13 +364,13 @@ export default function ProductList() { <div className="mt-4 flex justify-between"> <div> <h3 className="text-sm text-gray-700"> - <a href={product.thumbnail}> + <div href={product.thumbnail}> <span aria-hidden="true" className="absolute inset-0" /> {product.title} - </a> + </div> </h3> <p className="mt-1 text-sm text-gray-500"> <StarIcon className="w-6 h-6 inline"></StarIcon> From be55c818b157a8ccc885771cb5cc69ed4681327e Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Fri, 14 Jun 2024 13:21:23 +0530 Subject: [PATCH 08/47] dividing product list into components (Pagination,Mobile/DesktopFilter,ProductGrid) --- .../product/components/ProductList.js | 485 +++++++++--------- 1 file changed, 247 insertions(+), 238 deletions(-) diff --git a/src/features/product/components/ProductList.js b/src/features/product/components/ProductList.js index 22dd6f8..96bb154 100644 --- a/src/features/product/components/ProductList.js +++ b/src/features/product/components/ProductList.js @@ -88,117 +88,11 @@ export default function ProductList() { <div className="bg-white"> <div> {/* Mobile filter dialog */} - <Transition.Root show={mobileFiltersOpen} as={Fragment}> - <Dialog - as="div" - className="relative z-40 lg:hidden" - onClose={setMobileFiltersOpen} - > - <Transition.Child - as={Fragment} - enter="transition-opacity ease-linear duration-300" - enterFrom="opacity-0" - enterTo="opacity-100" - leave="transition-opacity ease-linear duration-300" - leaveFrom="opacity-100" - leaveTo="opacity-0" - > - <div className="fixed inset-0 bg-black bg-opacity-25" /> - </Transition.Child> - - <div className="fixed inset-0 z-40 flex"> - <Transition.Child - as={Fragment} - enter="transition ease-in-out duration-300 transform" - enterFrom="translate-x-full" - enterTo="translate-x-0" - leave="transition ease-in-out duration-300 transform" - leaveFrom="translate-x-0" - leaveTo="translate-x-full" - > - <Dialog.Panel className="relative ml-auto flex h-full w-full max-w-xs flex-col overflow-y-auto bg-white py-4 pb-12 shadow-xl"> - <div className="flex items-center justify-between px-4"> - <h2 className="text-lg font-medium text-gray-900"> - Filters - </h2> - <button - type="button" - className="-mr-2 flex h-10 w-10 items-center justify-center rounded-md bg-white p-2 text-gray-400" - onClick={() => setMobileFiltersOpen(false)} - > - <span className="sr-only">Close menu</span> - <XMarkIcon className="h-6 w-6" aria-hidden="true" /> - </button> - </div> - - {/* Filters */} - <form className="mt-4 border-t border-gray-200"> - {filters.map((section) => ( - <Disclosure - as="div" - key={section.id} - className="border-t border-gray-200 px-4 py-6" - > - {({ open }) => ( - <> - <h3 className="-mx-2 -my-3 flow-root"> - <Disclosure.Button className="flex w-full items-center justify-between bg-white px-2 py-3 text-gray-400 hover:text-gray-500"> - <span className="font-medium text-gray-900"> - {section.name} - </span> - <span className="ml-6 flex items-center"> - {open ? ( - <MinusIcon - className="h-5 w-5" - aria-hidden="true" - /> - ) : ( - <PlusIcon - className="h-5 w-5" - aria-hidden="true" - /> - )} - </span> - </Disclosure.Button> - </h3> - <Disclosure.Panel className="pt-6"> - <div className="space-y-6"> - {section.options.map((option, optionIdx) => ( - <div - key={option.value} - className="flex items-center" - > - <input - id={`filter-mobile-${section.id}-${optionIdx}`} - name={`${section.id}[]`} - defaultValue={option.value} - type="checkbox" - defaultChecked={option.checked} - onChange={(e) => { - handleFilter(e, section, option); - }} - className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500" - /> - <label - htmlFor={`filter-mobile-${section.id}-${optionIdx}`} - className="ml-3 min-w-0 flex-1 text-gray-500" - > - {option.label} - </label> - </div> - ))} - </div> - </Disclosure.Panel> - </> - )} - </Disclosure> - ))} - </form> - </Dialog.Panel> - </Transition.Child> - </div> - </Dialog> - </Transition.Root> + <MobileFilter + handleFilter={handleFilter} + mobileFiltersOpen={mobileFiltersOpen} + setMobileFiltersOpen={setMobileFiltersOpen} + /> <main className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"> <div className="flex items-baseline justify-between border-b border-gray-200 pb-6 pt-24"> @@ -278,18 +172,84 @@ export default function ProductList() { </h2> <div className="grid grid-cols-1 gap-x-8 gap-y-10 lg:grid-cols-4"> + <DesktopFilter handleFilter={handleFilter} /> + + {/* Product grid */} + <div className="lg:col-span-3"> + {/* This is our products list */} + <ProductGrid products={products} /> + </div> + {/* Product grid end */} + </div> + </section> + + {/* section of product and filters ends */} + + <Pagination /> + </main> + </div> + </div> + ); +} +function MobileFilter({ + mobileFiltersOpen, + setMobileFiltersOpen, + handleFilter, +}) { + return ( + <Transition.Root show={mobileFiltersOpen} as={Fragment}> + <Dialog + as="div" + className="relative z-40 lg:hidden" + onClose={setMobileFiltersOpen} + > + <Transition.Child + as={Fragment} + enter="transition-opacity ease-linear duration-300" + enterFrom="opacity-0" + enterTo="opacity-100" + leave="transition-opacity ease-linear duration-300" + leaveFrom="opacity-100" + leaveTo="opacity-0" + > + <div className="fixed inset-0 bg-black bg-opacity-25" /> + </Transition.Child> + + <div className="fixed inset-0 z-40 flex"> + <Transition.Child + as={Fragment} + enter="transition ease-in-out duration-300 transform" + enterFrom="translate-x-full" + enterTo="translate-x-0" + leave="transition ease-in-out duration-300 transform" + leaveFrom="translate-x-0" + leaveTo="translate-x-full" + > + <Dialog.Panel className="relative ml-auto flex h-full w-full max-w-xs flex-col overflow-y-auto bg-white py-4 pb-12 shadow-xl"> + <div className="flex items-center justify-between px-4"> + <h2 className="text-lg font-medium text-gray-900">Filters</h2> + <button + type="button" + className="-mr-2 flex h-10 w-10 items-center justify-center rounded-md bg-white p-2 text-gray-400" + onClick={() => setMobileFiltersOpen(false)} + > + <span className="sr-only">Close menu</span> + <XMarkIcon className="h-6 w-6" aria-hidden="true" /> + </button> + </div> + {/* Filters */} - <form className="hidden lg:block"> + <form className="mt-4 border-t border-gray-200"> {filters.map((section) => ( <Disclosure as="div" key={section.id} - className="border-b border-gray-200 py-6" + className="border-t border-gray-200 px-4 py-6" > {({ open }) => ( <> - <h3 className="-my-3 flow-root"> - <Disclosure.Button className="flex w-full items-center justify-between bg-white py-3 text-sm text-gray-400 hover:text-gray-500"> + <h3 className="-mx-2 -my-3 flow-root"> + <Disclosure.Button className="flex w-full items-center justify-between bg-white px-2 py-3 text-gray-400 hover:text-gray-500"> <span className="font-medium text-gray-900"> {section.name} </span> @@ -309,14 +269,14 @@ export default function ProductList() { </Disclosure.Button> </h3> <Disclosure.Panel className="pt-6"> - <div className="space-y-4"> + <div className="space-y-6"> {section.options.map((option, optionIdx) => ( <div key={option.value} className="flex items-center" > <input - id={`filter-${section.id}-${optionIdx}`} + id={`filter-mobile-${section.id}-${optionIdx}`} name={`${section.id}[]`} defaultValue={option.value} type="checkbox" @@ -327,8 +287,8 @@ export default function ProductList() { className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500" /> <label - htmlFor={`filter-${section.id}-${optionIdx}`} - className="ml-3 text-sm text-gray-600" + htmlFor={`filter-mobile-${section.id}-${optionIdx}`} + className="ml-3 min-w-0 flex-1 text-gray-500" > {option.label} </label> @@ -341,131 +301,180 @@ export default function ProductList() { </Disclosure> ))} </form> - - {/* Product grid */} - <div className="lg:col-span-3"> - {/* This is our products list */} - <div className="bg-white"> - <div className="mx-auto max-w-2xl px-4 py-0 sm:px-6 sm:py-0 lg:max-w-7xl lg:px-8"> - <div className="mt-6 grid grid-cols-1 gap-x-6 gap-y-10 sm:grid-cols-2 lg:grid-cols-3 xl:gap-x-8"> - {products.map((product) => ( - <Link to="/product-detail"> - <div - key={product.id} - className="group relative p-2 border-2" - > - <div className="min-h-60 aspect-h-1 aspect-w-1 w-full overflow-hidden rounded-md bg-gray-200 lg:aspect-none group-hover:opacity-75 lg:h-60"> - <img - src={product.thumbnail} - alt={product.title} - className="h-full w-full object-cover object-center lg:h-full lg:w-full" - /> - </div> - <div className="mt-4 flex justify-between"> - <div> - <h3 className="text-sm text-gray-700"> - <div href={product.thumbnail}> - <span - aria-hidden="true" - className="absolute inset-0" - /> - {product.title} - </div> - </h3> - <p className="mt-1 text-sm text-gray-500"> - <StarIcon className="w-6 h-6 inline"></StarIcon> - <span className="align-bottom"> - {product.rating} - </span> - </p> - </div> - <div> - <p className="text-sm font-medium text-gray-900"> - $ - {Math.round( - product.price * - (1 - product.discountPercentage / 100) - )} - <p className="text-sm line-through font-medium text-gray-400"> - ${product.price} - </p> - </p> - </div> - </div> - </div> - </Link> - ))} + </Dialog.Panel> + </Transition.Child> + </div> + </Dialog> + </Transition.Root> + ); +} +function DesktopFilter({ handleFilter }) { + return ( + <form className="hidden lg:block"> + {filters.map((section) => ( + <Disclosure + as="div" + key={section.id} + className="border-b border-gray-200 py-6" + > + {({ open }) => ( + <> + <h3 className="-my-3 flow-root"> + <Disclosure.Button className="flex w-full items-center justify-between bg-white py-3 text-sm text-gray-400 hover:text-gray-500"> + <span className="font-medium text-gray-900"> + {section.name} + </span> + <span className="ml-6 flex items-center"> + {open ? ( + <MinusIcon className="h-5 w-5" aria-hidden="true" /> + ) : ( + <PlusIcon className="h-5 w-5" aria-hidden="true" /> + )} + </span> + </Disclosure.Button> + </h3> + <Disclosure.Panel className="pt-6"> + <div className="space-y-4"> + {section.options.map((option, optionIdx) => ( + <div key={option.value} className="flex items-center"> + <input + id={`filter-${section.id}-${optionIdx}`} + name={`${section.id}[]`} + defaultValue={option.value} + type="checkbox" + defaultChecked={option.checked} + onChange={(e) => { + handleFilter(e, section, option); + }} + className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500" + /> + <label + htmlFor={`filter-${section.id}-${optionIdx}`} + className="ml-3 text-sm text-gray-600" + > + {option.label} + </label> </div> + ))} + </div> + </Disclosure.Panel> + </> + )} + </Disclosure> + ))} + </form> + ); +} +function ProductGrid({ products }) { + return ( + <div className="bg-white"> + <div className="mx-auto max-w-2xl px-4 py-0 sm:px-6 sm:py-0 lg:max-w-7xl lg:px-8"> + <div className="mt-6 grid grid-cols-1 gap-x-6 gap-y-10 sm:grid-cols-2 lg:grid-cols-3 xl:gap-x-8"> + {products.map((product) => ( + <Link to="/product-detail"> + <div key={product.id} className="group relative p-2 border-2"> + <div className="min-h-60 aspect-h-1 aspect-w-1 w-full overflow-hidden rounded-md bg-gray-200 lg:aspect-none group-hover:opacity-75 lg:h-60"> + <img + src={product.thumbnail} + alt={product.title} + className="h-full w-full object-cover object-center lg:h-full lg:w-full" + /> + </div> + <div className="mt-4 flex justify-between"> + <div> + <h3 className="text-sm text-gray-700"> + <div href={product.thumbnail}> + <span aria-hidden="true" className="absolute inset-0" /> + {product.title} + </div> + </h3> + <p className="mt-1 text-sm text-gray-500"> + <StarIcon className="w-6 h-6 inline"></StarIcon> + <span className="align-bottom">{product.rating}</span> + </p> + </div> + <div> + <p className="text-sm font-medium text-gray-900"> + $ + {Math.round( + product.price * (1 - product.discountPercentage / 100) + )} + <p className="text-sm line-through font-medium text-gray-400"> + ${product.price} + </p> + </p> </div> </div> </div> - {/* Product grid end */} - </div> - </section> + </Link> + ))} + </div> + </div> + </div> + ); +} - {/* section of product and filters ends */} - <div className="flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6"> - <div className="flex flex-1 justify-between sm:hidden"> - <a - href="#" - className="relative inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50" - > - Previous - </a> - <a - href="#" - className="relative ml-3 inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50" - > - Next - </a> - </div> - <div className="hidden sm:flex sm:flex-1 sm:items-center sm:justify-between"> - <div> - <p className="text-sm text-gray-700"> - Showing <span className="font-medium">1</span> to{" "} - <span className="font-medium">10</span> of{" "} - <span className="font-medium">97</span> results - </p> - </div> - <div> - <nav - className="isolate inline-flex -space-x-px rounded-md shadow-sm" - aria-label="Pagination" - > - <a - href="#" - className="relative inline-flex items-center rounded-l-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" - > - <span className="sr-only">Previous</span> - <ChevronLeftIcon className="h-5 w-5" aria-hidden="true" /> - </a> - {/* Current: "z-10 bg-indigo-600 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600", Default: "text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:outline-offset-0" */} - <a - href="#" - aria-current="page" - className="relative z-10 inline-flex items-center bg-indigo-600 px-4 py-2 text-sm font-semibold text-white focus:z-20 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" - > - 1 - </a> - <a - href="#" - className="relative inline-flex items-center px-4 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" - > - 2 - </a> +function Pagination() { + return ( + <div className="flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6"> + <div className="flex flex-1 justify-between sm:hidden"> + <a + href="#" + className="relative inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50" + > + Previous + </a> + <a + href="#" + className="relative ml-3 inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50" + > + Next + </a> + </div> + <div className="hidden sm:flex sm:flex-1 sm:items-center sm:justify-between"> + <div> + <p className="text-sm text-gray-700"> + Showing <span className="font-medium">1</span> to{" "} + <span className="font-medium">10</span> of{" "} + <span className="font-medium">97</span> results + </p> + </div> + <div> + <nav + className="isolate inline-flex -space-x-px rounded-md shadow-sm" + aria-label="Pagination" + > + <a + href="#" + className="relative inline-flex items-center rounded-l-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" + > + <span className="sr-only">Previous</span> + <ChevronLeftIcon className="h-5 w-5" aria-hidden="true" /> + </a> + {/* Current: "z-10 bg-indigo-600 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600", Default: "text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:outline-offset-0" */} + <a + href="#" + aria-current="page" + className="relative z-10 inline-flex items-center bg-indigo-600 px-4 py-2 text-sm font-semibold text-white focus:z-20 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" + > + 1 + </a> + <a + href="#" + className="relative inline-flex items-center px-4 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" + > + 2 + </a> - <a - href="#" - className="relative inline-flex items-center rounded-r-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" - > - <span className="sr-only">Next</span> - <ChevronRightIcon className="h-5 w-5" aria-hidden="true" /> - </a> - </nav> - </div> - </div> - </div> - </main> + <a + href="#" + className="relative inline-flex items-center rounded-r-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" + > + <span className="sr-only">Next</span> + <ChevronRightIcon className="h-5 w-5" aria-hidden="true" /> + </a> + </nav> + </div> </div> </div> ); From c297593a5c8e485e135ebcee7e6c26921c9c2358 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Sat, 15 Jun 2024 10:02:13 +0530 Subject: [PATCH 09/47] checked functionality/common dispatch(AllProducts,filter) --- src/features/product/components/ProductList.js | 18 +++++++++++++----- src/features/product/productAPI.js | 4 +++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/features/product/components/ProductList.js b/src/features/product/components/ProductList.js index 96bb154..c4d5919 100644 --- a/src/features/product/components/ProductList.js +++ b/src/features/product/components/ProductList.js @@ -67,9 +67,17 @@ export default function ProductList() { const [filter, setFilter] = useState({}); const handleFilter = (e, section, option) => { - const newFilter = { ...filter, [section.id]: option.value }; + console.log(e.target.checked); + // TODO: we will on server support mutilple value + // remove obj when input box is unchecked + const newFilter = { ...filter }; + if (e.target.checked) { + newFilter[section.id] = option.value; + } else { + delete newFilter[section.id]; + } setFilter(newFilter); - dispatch(fetchProductsByFiltersAsync(newFilter)); + console.log(section.id, option.value); }; const handleSort = (e, option) => { const newFilter = { @@ -79,10 +87,10 @@ export default function ProductList() { setFilter(newFilter); dispatch(fetchProductsByFiltersAsync(newFilter)); }; - + // making API call when dispatch or when filter is applied in a one go.... useEffect(() => { - dispatch(fetchAllProductsAsync()); - }, [dispatch]); + dispatch(fetchProductsByFiltersAsync(filter)); + }, [dispatch, filter]); return ( <div className="bg-white"> diff --git a/src/features/product/productAPI.js b/src/features/product/productAPI.js index 318a841..3d8083a 100644 --- a/src/features/product/productAPI.js +++ b/src/features/product/productAPI.js @@ -7,7 +7,9 @@ export function fetchAllProducts() { }); } export function fetchProductsByFilters(filter) { - // filter ={"category":"frangrances"} + // in server side:- + // filter ={"category":["frangrances","furniture"]} + // sort={_sort:price, order="desc"} // filter ={"brand":"Essence"} // TODO:we will on server support mutilple value From 82c96b355dfa03885d5189fd94883733d5c14110 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Sat, 15 Jun 2024 11:23:13 +0530 Subject: [PATCH 10/47] makine array of objects for filter/ making sort as another function and state --- .../product/components/ProductList.js | 28 ++++++++++++------- src/features/product/productAPI.js | 18 ++++++++---- src/features/product/productSlice.js | 4 +-- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/features/product/components/ProductList.js b/src/features/product/components/ProductList.js index c4d5919..5f19cf7 100644 --- a/src/features/product/components/ProductList.js +++ b/src/features/product/components/ProductList.js @@ -65,6 +65,7 @@ export default function ProductList() { const products = useSelector(selectAllProducts); const dispatch = useDispatch(); const [filter, setFilter] = useState({}); + const [sort, setSort] = useState({}); const handleFilter = (e, section, option) => { console.log(e.target.checked); @@ -72,25 +73,32 @@ export default function ProductList() { // remove obj when input box is unchecked const newFilter = { ...filter }; if (e.target.checked) { - newFilter[section.id] = option.value; + if (newFilter[section.id]) { + newFilter[section.id].push(option.value); //{"category":["frangrances","furniture"]} + } else { + newFilter[section.id] = [option.value]; //[] + } } else { - delete newFilter[section.id]; + // delete array item after unchecked... + const index = newFilter[section.id].findIndex( + (el) => el === option.value + ); + newFilter[section.id].splice(index, 1); } + console.log({ newFilter }); setFilter(newFilter); - console.log(section.id, option.value); }; const handleSort = (e, option) => { - const newFilter = { - ...filter, - _sort: option.order === "desc" ? `-${option.sort}` : option.sort, + const sort = { + _sort: option.order === "desc" ? `-${option.sort}` : option.sort, //{_sort:"price", order="desc"} }; - setFilter(newFilter); - dispatch(fetchProductsByFiltersAsync(newFilter)); + console.log(sort); + setSort(sort); }; // making API call when dispatch or when filter is applied in a one go.... useEffect(() => { - dispatch(fetchProductsByFiltersAsync(filter)); - }, [dispatch, filter]); + dispatch(fetchProductsByFiltersAsync({ filter, sort })); + }, [dispatch, filter, sort]); return ( <div className="bg-white"> diff --git a/src/features/product/productAPI.js b/src/features/product/productAPI.js index 3d8083a..0afe90b 100644 --- a/src/features/product/productAPI.js +++ b/src/features/product/productAPI.js @@ -6,10 +6,7 @@ export function fetchAllProducts() { resolve({ data }); }); } -export function fetchProductsByFilters(filter) { - // in server side:- - // filter ={"category":["frangrances","furniture"]} - // sort={_sort:price, order="desc"} +export function fetchProductsByFilters(filter, sort) { // filter ={"brand":"Essence"} // TODO:we will on server support mutilple value @@ -27,9 +24,20 @@ export function fetchProductsByFilters(filter) { // For descending order, // ***** use GET /products?_sort=-price **** + + // in server side:- + // filter ={"category":["frangrances","furniture"]} + // sort={_sort:"price", order="desc"} let queryString = ""; for (let key in filter) { - queryString += `${key}=${filter[key]}&`; + const categoryValues = filter[key]; // "[furniture]" << array + if (categoryValues.length) { + const lastCategoryValue = categoryValues[categoryValues.length - 1]; + queryString += `${key}=${lastCategoryValue}&`; + } + } + for (let key in sort) { + queryString += `${key}=${sort[key]}&`; //_sort:"price" } return new Promise(async (resolve) => { // TODO: we will not hard coded server url here... diff --git a/src/features/product/productSlice.js b/src/features/product/productSlice.js index 1eb1a21..bef076d 100644 --- a/src/features/product/productSlice.js +++ b/src/features/product/productSlice.js @@ -16,8 +16,8 @@ export const fetchAllProductsAsync = createAsyncThunk( ); export const fetchProductsByFiltersAsync = createAsyncThunk( "product/fetchProductsByFilters", - async (filter) => { - const response = await fetchProductsByFilters(filter); + async ({ filter, sort }) => { + const response = await fetchProductsByFilters(filter, sort); // The value we return becomes the `fulfilled` action payload return response.data; } From d5073919d2b38a6daf151a4d9f50e8f07dc9c428 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Mon, 17 Jun 2024 16:29:02 +0530 Subject: [PATCH 11/47] pagination code done but not reflecting products on UI(have to fix it) --- src/app/constants.js | 1 + .../product/components/ProductList.js | 56 ++++++++++++------- src/features/product/productAPI.js | 7 ++- src/features/product/productSlice.js | 5 +- 4 files changed, 46 insertions(+), 23 deletions(-) create mode 100644 src/app/constants.js diff --git a/src/app/constants.js b/src/app/constants.js new file mode 100644 index 0000000..7904744 --- /dev/null +++ b/src/app/constants.js @@ -0,0 +1 @@ +export const ITEMS_PER_PAGE = 6; diff --git a/src/features/product/components/ProductList.js b/src/features/product/components/ProductList.js index 5f19cf7..df5fe48 100644 --- a/src/features/product/components/ProductList.js +++ b/src/features/product/components/ProductList.js @@ -16,6 +16,7 @@ import { PlusIcon, Squares2X2Icon, } from "@heroicons/react/20/solid"; +import { ITEMS_PER_PAGE } from "../../../app/constants"; const sortOptions = [ { name: "Best Rating", sort: "rating", current: false }, @@ -66,6 +67,7 @@ export default function ProductList() { const dispatch = useDispatch(); const [filter, setFilter] = useState({}); const [sort, setSort] = useState({}); + const [page, setPage] = useState(1); const handleFilter = (e, section, option) => { console.log(e.target.checked); @@ -95,10 +97,15 @@ export default function ProductList() { console.log(sort); setSort(sort); }; + const handlePage = (page) => { + console.log({ page }); + setPage(page); + }; // making API call when dispatch or when filter is applied in a one go.... useEffect(() => { - dispatch(fetchProductsByFiltersAsync({ filter, sort })); - }, [dispatch, filter, sort]); + const pagination = { _page: page, _per_page: ITEMS_PER_PAGE }; + dispatch(fetchProductsByFiltersAsync({ filter, sort, pagination })); + }, [dispatch, filter, sort, page]); return ( <div className="bg-white"> @@ -201,7 +208,7 @@ export default function ProductList() { {/* section of product and filters ends */} - <Pagination /> + <Pagination page={page} setPage={setPage} handlePage={handlePage} /> </main> </div> </div> @@ -430,7 +437,7 @@ function ProductGrid({ products }) { ); } -function Pagination() { +function Pagination({ page, setPage, handlePage, totalItems = 30 }) { return ( <div className="flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6"> <div className="flex flex-1 justify-between sm:hidden"> @@ -450,9 +457,15 @@ function Pagination() { <div className="hidden sm:flex sm:flex-1 sm:items-center sm:justify-between"> <div> <p className="text-sm text-gray-700"> - Showing <span className="font-medium">1</span> to{" "} - <span className="font-medium">10</span> of{" "} - <span className="font-medium">97</span> results + Showing + {/* (3-1) * 6 + 1 =13 */} + <span className="font-medium"> + {(page - 1) * ITEMS_PER_PAGE + 1} + </span>{" "} + {/* 3 * 6 = 18 */} + to <span className="font-medium"> + {page * ITEMS_PER_PAGE}{" "} + </span> of <span className="font-medium">{totalItems}</span> results </p> </div> <div> @@ -468,19 +481,22 @@ function Pagination() { <ChevronLeftIcon className="h-5 w-5" aria-hidden="true" /> </a> {/* Current: "z-10 bg-indigo-600 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600", Default: "text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:outline-offset-0" */} - <a - href="#" - aria-current="page" - className="relative z-10 inline-flex items-center bg-indigo-600 px-4 py-2 text-sm font-semibold text-white focus:z-20 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" - > - 1 - </a> - <a - href="#" - className="relative inline-flex items-center px-4 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" - > - 2 - </a> + {Array.from({ length: Math.ceil(totalItems / ITEMS_PER_PAGE) }).map( + //extracting all the indexes of array + (el, index) => ( + <div + onClick={() => handlePage(index + 1)} + aria-current="page" + className={`relative z-10 inline-flex items-center ${ + index + 1 === page + ? "bg-indigo-600 text-white" + : "text-gray-400 " + } px-4 py-2 text-sm font-semibold cursor-pointer focus:z-20 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600`} + > + {index + 1} + </div> + ) + )} <a href="#" diff --git a/src/features/product/productAPI.js b/src/features/product/productAPI.js index 0afe90b..60dd527 100644 --- a/src/features/product/productAPI.js +++ b/src/features/product/productAPI.js @@ -6,7 +6,7 @@ export function fetchAllProducts() { resolve({ data }); }); } -export function fetchProductsByFilters(filter, sort) { +export function fetchProductsByFilters(filter, sort, pagination) { // filter ={"brand":"Essence"} // TODO:we will on server support mutilple value @@ -28,6 +28,7 @@ export function fetchProductsByFilters(filter, sort) { // in server side:- // filter ={"category":["frangrances","furniture"]} // sort={_sort:"price", order="desc"} + // sort={_page:3, _per_page=6} let queryString = ""; for (let key in filter) { const categoryValues = filter[key]; // "[furniture]" << array @@ -39,6 +40,10 @@ export function fetchProductsByFilters(filter, sort) { for (let key in sort) { queryString += `${key}=${sort[key]}&`; //_sort:"price" } + console.log(pagination); + for (let key in pagination) { + queryString += `${pagination}=${pagination[key]}&`; //_page:3 + } return new Promise(async (resolve) => { // TODO: we will not hard coded server url here... const response = await fetch( diff --git a/src/features/product/productSlice.js b/src/features/product/productSlice.js index bef076d..c6c6f6c 100644 --- a/src/features/product/productSlice.js +++ b/src/features/product/productSlice.js @@ -16,8 +16,9 @@ export const fetchAllProductsAsync = createAsyncThunk( ); export const fetchProductsByFiltersAsync = createAsyncThunk( "product/fetchProductsByFilters", - async ({ filter, sort }) => { - const response = await fetchProductsByFilters(filter, sort); + async ({ filter, sort, pagination }) => { + const response = await fetchProductsByFilters(filter, sort, pagination); + // The value we return becomes the `fulfilled` action payload return response.data; } From ac3fa71bcf34295df3256efc26032816d822ca64 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Mon, 17 Jun 2024 17:23:36 +0530 Subject: [PATCH 12/47] fixed pagination issues:key instead of pagination:pagination[key]/extracts data array under the data in pagination api call --- src/features/product/productAPI.js | 2 +- src/features/product/productSlice.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/product/productAPI.js b/src/features/product/productAPI.js index 60dd527..f80ab9b 100644 --- a/src/features/product/productAPI.js +++ b/src/features/product/productAPI.js @@ -42,7 +42,7 @@ export function fetchProductsByFilters(filter, sort, pagination) { } console.log(pagination); for (let key in pagination) { - queryString += `${pagination}=${pagination[key]}&`; //_page:3 + queryString += `${key}=${pagination[key]}&`; //_page:3 } return new Promise(async (resolve) => { // TODO: we will not hard coded server url here... diff --git a/src/features/product/productSlice.js b/src/features/product/productSlice.js index c6c6f6c..e26238d 100644 --- a/src/features/product/productSlice.js +++ b/src/features/product/productSlice.js @@ -20,7 +20,7 @@ export const fetchProductsByFiltersAsync = createAsyncThunk( const response = await fetchProductsByFilters(filter, sort, pagination); // The value we return becomes the `fulfilled` action payload - return response.data; + return response.data.data; //pagination array comes from data array!!!!! } ); From 3316e439e09094a994a60593ddfc9d4d49767b00 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Mon, 17 Jun 2024 20:28:45 +0530 Subject: [PATCH 13/47] unable to render totalItems in UI pagination (have to fix) --- src/features/product/components/ProductList.js | 13 ++++++++++--- src/features/product/productAPI.js | 3 ++- src/features/product/productSlice.js | 5 ++++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/features/product/components/ProductList.js b/src/features/product/components/ProductList.js index df5fe48..bb91776 100644 --- a/src/features/product/components/ProductList.js +++ b/src/features/product/components/ProductList.js @@ -4,6 +4,7 @@ import { fetchAllProductsAsync, fetchProductsByFiltersAsync, selectAllProducts, + selectTotalItems, } from "../productSlice"; import { Dialog, Disclosure, Menu, Transition } from "@headlessui/react"; import { StarIcon, XMarkIcon } from "@heroicons/react/24/outline"; @@ -64,6 +65,7 @@ function classNames(...classes) { export default function ProductList() { const [mobileFiltersOpen, setMobileFiltersOpen] = useState(false); const products = useSelector(selectAllProducts); + const totalItems = useSelector(selectTotalItems); const dispatch = useDispatch(); const [filter, setFilter] = useState({}); const [sort, setSort] = useState({}); @@ -208,7 +210,12 @@ export default function ProductList() { {/* section of product and filters ends */} - <Pagination page={page} setPage={setPage} handlePage={handlePage} /> + <Pagination + page={page} + setPage={setPage} + handlePage={handlePage} + totalItems={totalItems} + /> </main> </div> </div> @@ -437,7 +444,7 @@ function ProductGrid({ products }) { ); } -function Pagination({ page, setPage, handlePage, totalItems = 30 }) { +function Pagination({ page, setPage, handlePage, totalItems }) { return ( <div className="flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6"> <div className="flex flex-1 justify-between sm:hidden"> @@ -457,7 +464,7 @@ function Pagination({ page, setPage, handlePage, totalItems = 30 }) { <div className="hidden sm:flex sm:flex-1 sm:items-center sm:justify-between"> <div> <p className="text-sm text-gray-700"> - Showing + Showing- {/* (3-1) * 6 + 1 =13 */} <span className="font-medium"> {(page - 1) * ITEMS_PER_PAGE + 1} diff --git a/src/features/product/productAPI.js b/src/features/product/productAPI.js index f80ab9b..a3122e9 100644 --- a/src/features/product/productAPI.js +++ b/src/features/product/productAPI.js @@ -50,6 +50,7 @@ export function fetchProductsByFilters(filter, sort, pagination) { "http://localhost:8080/products?" + queryString ); const data = await response.json(); - resolve({ data }); + const totalItems = await response.data.items; + resolve({ data: { products: data, totalItems: totalItems } }); }); } diff --git a/src/features/product/productSlice.js b/src/features/product/productSlice.js index e26238d..b226303 100644 --- a/src/features/product/productSlice.js +++ b/src/features/product/productSlice.js @@ -4,6 +4,7 @@ import { fetchAllProducts, fetchProductsByFilters } from "./productAPI"; const initialState = { products: [], status: "idle", + totalItems: 0, }; export const fetchAllProductsAsync = createAsyncThunk( @@ -46,7 +47,8 @@ export const productSlice = createSlice({ }) .addCase(fetchProductsByFiltersAsync.fulfilled, (state, action) => { state.status = "idle"; - state.products = action.payload; + state.products = action.payload.products; + state.totalItems = action.payload.totalItems; }); }, }); @@ -54,5 +56,6 @@ export const productSlice = createSlice({ export const { increment } = productSlice.actions; export const selectAllProducts = (state) => state.product.products; +export const selectTotalItems = (state) => state.product.totalItems; export default productSlice.reducer; From 40a7fce3c3ead9be863a8e19112ac578fb8a2d0d Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Mon, 17 Jun 2024 22:54:57 +0530 Subject: [PATCH 14/47] fixed totalItems by extracting direct from json obj (data.items)/ improve pagination by putting conditional and use effect --- src/features/product/components/ProductList.js | 18 +++++++++++++----- src/features/product/productAPI.js | 4 ++-- src/features/product/productSlice.js | 2 +- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/features/product/components/ProductList.js b/src/features/product/components/ProductList.js index bb91776..383ef42 100644 --- a/src/features/product/components/ProductList.js +++ b/src/features/product/components/ProductList.js @@ -109,6 +109,11 @@ export default function ProductList() { dispatch(fetchProductsByFiltersAsync({ filter, sort, pagination })); }, [dispatch, filter, sort, page]); + useEffect(() => { + //page always come to first if there is change in totalItems and sorting + setPage(1); + }, [totalItems, sort]); + return ( <div className="bg-white"> <div> @@ -464,15 +469,18 @@ function Pagination({ page, setPage, handlePage, totalItems }) { <div className="hidden sm:flex sm:flex-1 sm:items-center sm:justify-between"> <div> <p className="text-sm text-gray-700"> - Showing- - {/* (3-1) * 6 + 1 =13 */} + Showing {/* (3-1) * 6 + 1 =13 */} <span className="font-medium"> {(page - 1) * ITEMS_PER_PAGE + 1} </span>{" "} {/* 3 * 6 = 18 */} - to <span className="font-medium"> - {page * ITEMS_PER_PAGE}{" "} - </span> of <span className="font-medium">{totalItems}</span> results + to{" "} + <span className="font-medium"> + {page * ITEMS_PER_PAGE > totalItems + ? totalItems + : page * ITEMS_PER_PAGE}{" "} + </span>{" "} + of <span className="font-medium">{totalItems}</span> results </p> </div> <div> diff --git a/src/features/product/productAPI.js b/src/features/product/productAPI.js index a3122e9..59c9a4c 100644 --- a/src/features/product/productAPI.js +++ b/src/features/product/productAPI.js @@ -50,7 +50,7 @@ export function fetchProductsByFilters(filter, sort, pagination) { "http://localhost:8080/products?" + queryString ); const data = await response.json(); - const totalItems = await response.data.items; - resolve({ data: { products: data, totalItems: totalItems } }); + + resolve({ data: { products: data.data, totalItems: data.items } }); }); } diff --git a/src/features/product/productSlice.js b/src/features/product/productSlice.js index b226303..e41ef7e 100644 --- a/src/features/product/productSlice.js +++ b/src/features/product/productSlice.js @@ -21,7 +21,7 @@ export const fetchProductsByFiltersAsync = createAsyncThunk( const response = await fetchProductsByFilters(filter, sort, pagination); // The value we return becomes the `fulfilled` action payload - return response.data.data; //pagination array comes from data array!!!!! + return response.data; //pagination array comes from data array!!!!! data.data:[id:2] } ); From a06e910cc4230f0e92f8f7ec865d38e099115517 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Tue, 18 Jun 2024 19:05:56 +0530 Subject: [PATCH 15/47] calling categories and brand through API --- data.json | 94 +++++++++++++++++++ .../product/components/ProductList.js | 63 ++++++------- src/features/product/productAPI.js | 14 +++ src/features/product/productSlice.js | 43 ++++++++- 4 files changed, 178 insertions(+), 36 deletions(-) diff --git a/data.json b/data.json index 20bff00..ab911f5 100644 --- a/data.json +++ b/data.json @@ -1719,5 +1719,99 @@ ], "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Kiwi/thumbnail.png" } + ], + "brands": [ + { + "value": "Essence", + "label": "Essence", + "checked": false + }, + { + "value": "Glamour Beauty", + "label": "Glamour Beauty", + "checked": false + }, + { + "value": "Velvet Touch", + "label": "Velvet Touch", + "checked": false + }, + { + "value": "Chic Cosmetics", + "label": "Chic Cosmetics", + "checked": false + }, + { + "value": "Nail Couture", + "label": "Nail Couture", + "checked": false + }, + { + "value": "Calvin Klein", + "label": "Calvin Klein", + "checked": false + }, + { + "value": "Chanel", + "label": "Chanel", + "checked": false + }, + { + "value": "Dior", + "label": "Dior", + "checked": false + }, + { + "value": "Dolce & Gabbana", + "label": "Dolce & Gabbana", + "checked": false + }, + { + "value": "Gucci", + "label": "Gucci", + "checked": false + }, + { + "value": "Annibale Colombo", + "label": "Annibale Colombo", + "checked": false + }, + { + "value": "Furniture Co.", + "label": "Furniture Co.", + "checked": false + }, + { + "value": "Knoll", + "label": "Knoll", + "checked": false + }, + { + "value": "Bath Trends", + "label": "Bath Trends", + "checked": false + } + ], + "categories": [ + { + "value": "beauty", + "label": "Beauty", + "checked": false + }, + { + "value": "fragrances", + "label": "Fragrances", + "checked": false + }, + { + "value": "furniture", + "label": "Furniture", + "checked": false + }, + { + "value": "groceries", + "label": "Groceries", + "checked": false + } ] } diff --git a/src/features/product/components/ProductList.js b/src/features/product/components/ProductList.js index 383ef42..ba18384 100644 --- a/src/features/product/components/ProductList.js +++ b/src/features/product/components/ProductList.js @@ -2,8 +2,12 @@ import React, { useState, Fragment, useEffect } from "react"; import { useSelector, useDispatch } from "react-redux"; import { fetchAllProductsAsync, + fetchBrandsAsync, + fetchCategoriesAsync, fetchProductsByFiltersAsync, selectAllProducts, + selectBrands, + selectCategories, selectTotalItems, } from "../productSlice"; import { Dialog, Disclosure, Menu, Transition } from "@headlessui/react"; @@ -25,39 +29,6 @@ const sortOptions = [ { name: "Price: High to Low", sort: "price", order: "desc", current: false }, ]; -const filters = [ - { - id: "category", - name: "Category", - options: [ - { value: "beauty", label: "Beauty", checked: false }, - { value: "fragrances", label: "Fragrances", checked: false }, - { value: "furniture", label: "Furniture", checked: false }, - { value: "groceries", label: "Groceries", checked: false }, - ], - }, - { - id: "brand", - name: "Brands", - options: [ - { value: "Essence", label: "Essence", checked: false }, - { value: "Glamour Beauty", label: "Glamour Beauty", checked: false }, - { value: "Velvet Touch", label: "Velvet Touch", checked: false }, - { value: "Chic Cosmetics", label: "Chic Cosmetics", checked: false }, - { value: "Nail Couture", label: "Nail Couture", checked: false }, - { value: "Calvin Klein", label: "Calvin Klein", checked: false }, - { value: "Chanel", label: "Chanel", checked: false }, - { value: "Dior", label: "Dior", checked: false }, - { value: "Dolce & Gabbana", label: "Dolce & Gabbana", checked: false }, - { value: "Gucci", label: "Gucci", checked: false }, - { value: "Annibale Colombo", label: "Annibale Colombo", checked: false }, - { value: "Furniture Co.", label: "Furniture Co.", checked: false }, - { value: "Knoll", label: "Knoll", checked: false }, - { value: "Bath Trends", label: "Bath Trends", checked: false }, - ], - }, -]; - function classNames(...classes) { return classes.filter(Boolean).join(" "); } @@ -65,7 +36,22 @@ function classNames(...classes) { export default function ProductList() { const [mobileFiltersOpen, setMobileFiltersOpen] = useState(false); const products = useSelector(selectAllProducts); + const categories = useSelector(selectCategories); + const brands = useSelector(selectBrands); const totalItems = useSelector(selectTotalItems); + const filters = [ + { + id: "category", + name: "Category", + options: categories, + }, + { + id: "brand", + name: "Brands", + options: brands, + }, + ]; + const dispatch = useDispatch(); const [filter, setFilter] = useState({}); const [sort, setSort] = useState({}); @@ -114,6 +100,11 @@ export default function ProductList() { setPage(1); }, [totalItems, sort]); + useEffect(() => { + dispatch(fetchBrandsAsync()); + dispatch(fetchCategoriesAsync()); + }, []); + return ( <div className="bg-white"> <div> @@ -122,6 +113,7 @@ export default function ProductList() { handleFilter={handleFilter} mobileFiltersOpen={mobileFiltersOpen} setMobileFiltersOpen={setMobileFiltersOpen} + filters={filters} /> <main className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"> @@ -202,7 +194,7 @@ export default function ProductList() { </h2> <div className="grid grid-cols-1 gap-x-8 gap-y-10 lg:grid-cols-4"> - <DesktopFilter handleFilter={handleFilter} /> + <DesktopFilter handleFilter={handleFilter} filters={filters} /> {/* Product grid */} <div className="lg:col-span-3"> @@ -230,6 +222,7 @@ function MobileFilter({ mobileFiltersOpen, setMobileFiltersOpen, handleFilter, + filters, }) { return ( <Transition.Root show={mobileFiltersOpen} as={Fragment}> @@ -343,7 +336,7 @@ function MobileFilter({ </Transition.Root> ); } -function DesktopFilter({ handleFilter }) { +function DesktopFilter({ handleFilter, filters }) { return ( <form className="hidden lg:block"> {filters.map((section) => ( diff --git a/src/features/product/productAPI.js b/src/features/product/productAPI.js index 59c9a4c..2c0c567 100644 --- a/src/features/product/productAPI.js +++ b/src/features/product/productAPI.js @@ -54,3 +54,17 @@ export function fetchProductsByFilters(filter, sort, pagination) { resolve({ data: { products: data.data, totalItems: data.items } }); }); } +export function fetchCategories() { + return new Promise(async (resolve) => { + const response = await fetch("http://localhost:8080/categories"); + const data = await response.json(); + resolve({ data }); + }); +} +export function fetchBrands() { + return new Promise(async (resolve) => { + const response = await fetch("http://localhost:8080/brands"); + const data = await response.json(); + resolve({ data }); + }); +} diff --git a/src/features/product/productSlice.js b/src/features/product/productSlice.js index e41ef7e..c14a275 100644 --- a/src/features/product/productSlice.js +++ b/src/features/product/productSlice.js @@ -1,8 +1,15 @@ import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; -import { fetchAllProducts, fetchProductsByFilters } from "./productAPI"; +import { + fetchAllProducts, + fetchProductsByFilters, + fetchBrands, + fetchCategories, +} from "./productAPI"; const initialState = { products: [], + brands: [], + categories: [], status: "idle", totalItems: 0, }; @@ -25,6 +32,24 @@ export const fetchProductsByFiltersAsync = createAsyncThunk( } ); +export const fetchBrandsAsync = createAsyncThunk( + "product/fetchBrands", + async () => { + const response = await fetchBrands(); + // The value we return becomes the `fulfilled` action payload + return response.data; + } +); + +export const fetchCategoriesAsync = createAsyncThunk( + "product/fetchCategories", + async () => { + const response = await fetchCategories(); + // The value we return becomes the `fulfilled` action payload + return response.data; + } +); + export const productSlice = createSlice({ name: "product", initialState, @@ -49,6 +74,20 @@ export const productSlice = createSlice({ state.status = "idle"; state.products = action.payload.products; state.totalItems = action.payload.totalItems; + }) + .addCase(fetchBrandsAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(fetchBrandsAsync.fulfilled, (state, action) => { + state.status = "idle"; + state.brands = action.payload; + }) + .addCase(fetchCategoriesAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(fetchCategoriesAsync.fulfilled, (state, action) => { + state.status = "idle"; + state.categories = action.payload; }); }, }); @@ -56,6 +95,8 @@ export const productSlice = createSlice({ export const { increment } = productSlice.actions; export const selectAllProducts = (state) => state.product.products; +export const selectBrands = (state) => state.product.brands; +export const selectCategories = (state) => state.product.categories; export const selectTotalItems = (state) => state.product.totalItems; export default productSlice.reducer; From a591305dfe2aa7c3aceaed7864e4ef588c4a58e8 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Wed, 19 Jun 2024 17:08:02 +0530 Subject: [PATCH 16/47] previous/next button is now working --- .../product/components/ProductList.js | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/features/product/components/ProductList.js b/src/features/product/components/ProductList.js index ba18384..50e9782 100644 --- a/src/features/product/components/ProductList.js +++ b/src/features/product/components/ProductList.js @@ -443,21 +443,22 @@ function ProductGrid({ products }) { } function Pagination({ page, setPage, handlePage, totalItems }) { + const totalPages = Math.ceil(totalItems / ITEMS_PER_PAGE); return ( <div className="flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6"> <div className="flex flex-1 justify-between sm:hidden"> <a - href="#" + onClick={() => handlePage(page > 1 ? page - 1 : page)} className="relative inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50" > Previous </a> - <a - href="#" + <div + onClick={() => handlePage(page < totalPages ? page + 1 : page)} className="relative ml-3 inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50" > Next - </a> + </div> </div> <div className="hidden sm:flex sm:flex-1 sm:items-center sm:justify-between"> <div> @@ -481,15 +482,15 @@ function Pagination({ page, setPage, handlePage, totalItems }) { className="isolate inline-flex -space-x-px rounded-md shadow-sm" aria-label="Pagination" > - <a - href="#" + <div + onClick={() => handlePage(page > 1 ? page - 1 : page)} className="relative inline-flex items-center rounded-l-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" > <span className="sr-only">Previous</span> <ChevronLeftIcon className="h-5 w-5" aria-hidden="true" /> - </a> + </div> {/* Current: "z-10 bg-indigo-600 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600", Default: "text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:outline-offset-0" */} - {Array.from({ length: Math.ceil(totalItems / ITEMS_PER_PAGE) }).map( + {Array.from({ length: totalPages }).map( //extracting all the indexes of array (el, index) => ( <div @@ -506,13 +507,13 @@ function Pagination({ page, setPage, handlePage, totalItems }) { ) )} - <a - href="#" + <div + onClick={() => handlePage(page < totalPages ? page + 1 : page)} className="relative inline-flex items-center rounded-r-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" > <span className="sr-only">Next</span> <ChevronRightIcon className="h-5 w-5" aria-hidden="true" /> - </a> + </div> </nav> </div> </div> From 0f7b7241c7070d82049876d85d06d19b53436e04 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Thu, 20 Jun 2024 11:40:32 +0530 Subject: [PATCH 17/47] product detail page added (dynamically) --- src/App.js | 38 +- .../product/components/ProductDetail.js | 553 +++++++++--------- .../product/components/ProductList.js | 2 +- src/features/product/productAPI.js | 8 + src/features/product/productSlice.js | 18 + 5 files changed, 334 insertions(+), 285 deletions(-) diff --git a/src/App.js b/src/App.js index 23895b6..84b3bd6 100644 --- a/src/App.js +++ b/src/App.js @@ -1,42 +1,42 @@ -import { Counter } from './features/counter/Counter'; -import './App.css'; -import Home from './pages/Home'; -import LoginPage from './pages/LoginPage'; -import SignupPage from './pages/SignupPage'; +import { Counter } from "./features/counter/Counter"; +import "./App.css"; +import Home from "./pages/Home"; +import LoginPage from "./pages/LoginPage"; +import SignupPage from "./pages/SignupPage"; import { createBrowserRouter, RouterProvider, Route, Link, -} from 'react-router-dom'; -import Cart from './features/cart/Cart'; -import CartPage from './pages/CartPage'; -import Checkout from './pages/Checkout'; -import ProductDetailPage from './pages/ProductDetailPage'; +} from "react-router-dom"; +import Cart from "./features/cart/Cart"; +import CartPage from "./pages/CartPage"; +import Checkout from "./pages/Checkout"; +import ProductDetailPage from "./pages/ProductDetailPage"; const router = createBrowserRouter([ { - path: '/', + path: "/", element: <Home></Home>, }, { - path: '/login', + path: "/login", element: <LoginPage></LoginPage>, }, { - path: '/signup', + path: "/signup", element: <SignupPage></SignupPage>, }, - { - path: '/cart', + { + path: "/cart", element: <CartPage></CartPage>, }, - { - path: '/checkout', + { + path: "/checkout", element: <Checkout></Checkout>, }, - { - path: '/product-detail', + { + path: "/product-detail/:id", // :id provided by react-router element: <ProductDetailPage></ProductDetailPage>, }, ]); diff --git a/src/features/product/components/ProductDetail.js b/src/features/product/components/ProductDetail.js index 7196fdb..bc4f4ce 100644 --- a/src/features/product/components/ProductDetail.js +++ b/src/features/product/components/ProductDetail.js @@ -1,312 +1,335 @@ +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 { useParams } from "react-router-dom"; -import { useState } from 'react' -import { StarIcon } from '@heroicons/react/20/solid' -import { RadioGroup } from '@headlessui/react' +// TODO: In server data we will add sizes,colors, highlights to each product -const product = { - name: 'Basic Tee 6-Pack', - price: '$192', - href: '#', - breadcrumbs: [ - { id: 1, name: 'Men', href: '#' }, - { id: 2, name: 'Clothing', href: '#' }, - ], - images: [ - { - src: 'https://tailwindui.com/img/ecommerce-images/product-page-02-secondary-product-shot.jpg', - alt: 'Two each of gray, white, and black shirts laying flat.', - }, - { - src: 'https://tailwindui.com/img/ecommerce-images/product-page-02-tertiary-product-shot-01.jpg', - alt: 'Model wearing plain black basic tee.', - }, - { - src: 'https://tailwindui.com/img/ecommerce-images/product-page-02-tertiary-product-shot-02.jpg', - alt: 'Model wearing plain gray basic tee.', - }, - { - src: 'https://tailwindui.com/img/ecommerce-images/product-page-02-featured-product-shot.jpg', - alt: 'Model wearing plain white basic tee.', - }, - ], - colors: [ - { name: 'White', class: 'bg-white', selectedClass: 'ring-gray-400' }, - { name: 'Gray', class: 'bg-gray-200', selectedClass: 'ring-gray-400' }, - { name: 'Black', class: 'bg-gray-900', selectedClass: 'ring-gray-900' }, - ], - sizes: [ - { name: 'XXS', inStock: false }, - { name: 'XS', inStock: true }, - { name: 'S', inStock: true }, - { name: 'M', inStock: true }, - { name: 'L', inStock: true }, - { name: 'XL', inStock: true }, - { name: '2XL', inStock: true }, - { name: '3XL', inStock: true }, - ], - description: - 'The Basic Tee 6-Pack allows you to fully express your vibrant personality with three grayscale options. Feeling adventurous? Put on a heather gray tee. Want to be a trendsetter? Try our exclusive colorway: "Black". Need to add an extra pop of color to your outfit? Our white tee has you covered.', - highlights: [ - 'Hand cut and sewn locally', - 'Dyed with our proprietary colors', - 'Pre-washed & pre-shrunk', - 'Ultra-soft 100% cotton', - ], - details: - 'The 6-Pack includes two black, two white, and two heather gray Basic Tees. Sign up for our subscription service and be the first to get new, exciting colors, like our upcoming "Charcoal Gray" limited release.', -} -const reviews = { href: '#', average: 4, totalCount: 117 } +const colors = [ + { name: "White", class: "bg-white", selectedClass: "ring-gray-400" }, + { name: "Gray", class: "bg-gray-200", selectedClass: "ring-gray-400" }, + { name: "Black", class: "bg-gray-900", selectedClass: "ring-gray-900" }, +]; +const sizes = [ + { name: "XXS", inStock: false }, + { name: "XS", inStock: true }, + { name: "S", inStock: true }, + { name: "M", inStock: true }, + { name: "L", inStock: true }, + { name: "XL", inStock: true }, + { name: "2XL", inStock: true }, + { name: "3XL", inStock: true }, +]; +const highlights = [ + "Hand cut and sewn locally", + "Dyed with our proprietary colors", + "Pre-washed & pre-shrunk", + "Ultra-soft 100% cotton", +]; function classNames(...classes) { - return classes.filter(Boolean).join(' ') + return classes.filter(Boolean).join(" "); } export default function ProductDetail() { - const [selectedColor, setSelectedColor] = useState(product.colors[0]) - const [selectedSize, setSelectedSize] = useState(product.sizes[2]) + const [selectedColor, setSelectedColor] = useState(colors[0]); + const [selectedSize, setSelectedSize] = useState(sizes[2]); + const product = useSelector(selectProductById); + const dispatch = useDispatch(); + const params = useParams(); // hook provided by react-router to fetch parameters + useEffect(() => { + dispatch(fetchAllProductsIdAsync(params.id)); // :id from path in app.js + }, [dispatch, params.id]); return ( <div className="bg-white"> - <div className="pt-6"> - <nav aria-label="Breadcrumb"> - <ol role="list" className="mx-auto flex max-w-2xl items-center space-x-2 px-4 sm:px-6 lg:max-w-7xl lg:px-8"> - {product.breadcrumbs.map((breadcrumb) => ( - <li key={breadcrumb.id}> - <div className="flex items-center"> - <a href={breadcrumb.href} className="mr-2 text-sm font-medium text-gray-900"> - {breadcrumb.name} - </a> - <svg - width={16} - height={20} - viewBox="0 0 16 20" - fill="currentColor" - aria-hidden="true" - className="h-5 w-4 text-gray-300" - > - <path d="M5.697 4.34L8.98 16.532h1.327L7.025 4.341H5.697z" /> - </svg> - </div> + {product && ( + <div className="pt-6"> + <nav aria-label="Breadcrumb"> + <ol + role="list" + className="mx-auto flex max-w-2xl items-center space-x-2 px-4 sm:px-6 lg:max-w-7xl lg:px-8" + > + {product.breadcrumbs && + product.breadcrumbs.map((breadcrumb) => ( + <li key={breadcrumb.id}> + <div className="flex items-center"> + <a + href={breadcrumb.href} + className="mr-2 text-sm font-medium text-gray-900" + > + {breadcrumb.name} + </a> + <svg + width={16} + height={20} + viewBox="0 0 16 20" + fill="currentColor" + aria-hidden="true" + className="h-5 w-4 text-gray-300" + > + <path d="M5.697 4.34L8.98 16.532h1.327L7.025 4.341H5.697z" /> + </svg> + </div> + </li> + ))} + <li className="text-sm"> + <a + href={product.href} + aria-current="page" + className="font-medium text-gray-500 hover:text-gray-600" + > + {product.title} + </a> </li> - ))} - <li className="text-sm"> - <a href={product.href} aria-current="page" className="font-medium text-gray-500 hover:text-gray-600"> - {product.name} - </a> - </li> - </ol> - </nav> + </ol> + </nav> - {/* Image gallery */} - <div className="mx-auto mt-6 max-w-2xl sm:px-6 lg:grid lg:max-w-7xl lg:grid-cols-3 lg:gap-x-8 lg:px-8"> - <div className="aspect-h-4 aspect-w-3 hidden overflow-hidden rounded-lg lg:block"> - <img - src={product.images[0].src} - alt={product.images[0].alt} - className="h-full w-full object-cover object-center" - /> - </div> - <div className="hidden lg:grid lg:grid-cols-1 lg:gap-y-8"> - <div className="aspect-h-2 aspect-w-3 overflow-hidden rounded-lg"> + {/* Image gallery */} + <div className="mx-auto mt-6 max-w-2xl sm:px-6 lg:grid lg:max-w-7xl lg:grid-cols-3 lg:gap-x-8 lg:px-8"> + <div className="aspect-h-4 aspect-w-3 hidden overflow-hidden rounded-lg lg:block"> <img - src={product.images[1].src} - alt={product.images[1].alt} + src={product.images[0]} + alt={product.title} className="h-full w-full object-cover object-center" /> </div> - <div className="aspect-h-2 aspect-w-3 overflow-hidden rounded-lg"> + <div className="hidden lg:grid lg:grid-cols-1 lg:gap-y-8"> + <div className="aspect-h-2 aspect-w-3 overflow-hidden rounded-lg"> + <img + src={product.images[0]} + alt={product.title} + className="h-full w-full object-cover object-center" + /> + </div> + <div className="aspect-h-2 aspect-w-3 overflow-hidden rounded-lg"> + <img + src={product.images[0]} + alt={product.title} + className="h-full w-full object-cover object-center" + /> + </div> + </div> + <div className="aspect-h-5 aspect-w-4 lg:aspect-h-4 lg:aspect-w-3 sm:overflow-hidden sm:rounded-lg"> <img - src={product.images[2].src} - alt={product.images[2].alt} + src={product.images[0]} + alt={product.title} className="h-full w-full object-cover object-center" /> </div> </div> - <div className="aspect-h-5 aspect-w-4 lg:aspect-h-4 lg:aspect-w-3 sm:overflow-hidden sm:rounded-lg"> - <img - src={product.images[3].src} - alt={product.images[3].alt} - className="h-full w-full object-cover object-center" - /> - </div> - </div> - - {/* Product info */} - <div className="mx-auto max-w-2xl px-4 pb-16 pt-10 sm:px-6 lg:grid lg:max-w-7xl lg:grid-cols-3 lg:grid-rows-[auto,auto,1fr] lg:gap-x-8 lg:px-8 lg:pb-24 lg:pt-16"> - <div className="lg:col-span-2 lg:border-r lg:border-gray-200 lg:pr-8"> - <h1 className="text-2xl font-bold tracking-tight text-gray-900 sm:text-3xl">{product.name}</h1> - </div> - {/* Options */} - <div className="mt-4 lg:row-span-3 lg:mt-0"> - <h2 className="sr-only">Product information</h2> - <p className="text-3xl tracking-tight text-gray-900">{product.price}</p> - - {/* Reviews */} - <div className="mt-6"> - <h3 className="sr-only">Reviews</h3> - <div className="flex items-center"> - <div className="flex items-center"> - {[0, 1, 2, 3, 4].map((rating) => ( - <StarIcon - key={rating} - className={classNames( - reviews.average > rating ? 'text-gray-900' : 'text-gray-200', - 'h-5 w-5 flex-shrink-0' - )} - aria-hidden="true" - /> - ))} - </div> - <p className="sr-only">{reviews.average} out of 5 stars</p> - <a href={reviews.href} className="ml-3 text-sm font-medium text-indigo-600 hover:text-indigo-500"> - {reviews.totalCount} reviews - </a> - </div> + {/* Product info */} + <div className="mx-auto max-w-2xl px-4 pb-16 pt-10 sm:px-6 lg:grid lg:max-w-7xl lg:grid-cols-3 lg:grid-rows-[auto,auto,1fr] lg:gap-x-8 lg:px-8 lg:pb-24 lg:pt-16"> + <div className="lg:col-span-2 lg:border-r lg:border-gray-200 lg:pr-8"> + <h1 className="text-2xl font-bold tracking-tight text-gray-900 sm:text-3xl"> + {product.title} + </h1> </div> - <form className="mt-10"> - {/* Colors */} - <div> - <h3 className="text-sm font-medium text-gray-900">Color</h3> + {/* Options */} + <div className="mt-4 lg:row-span-3 lg:mt-0"> + <h2 className="sr-only">Product information</h2> + <p className="text-3xl tracking-tight text-gray-900"> + ${product.price} + </p> - <RadioGroup value={selectedColor} onChange={setSelectedColor} className="mt-4"> - <RadioGroup.Label className="sr-only">Choose a color</RadioGroup.Label> - <div className="flex items-center space-x-3"> - {product.colors.map((color) => ( - <RadioGroup.Option - key={color.name} - value={color} - className={({ active, checked }) => - classNames( - color.selectedClass, - active && checked ? 'ring ring-offset-1' : '', - !active && checked ? 'ring-2' : '', - 'relative -m-0.5 flex cursor-pointer items-center justify-center rounded-full p-0.5 focus:outline-none' - ) - } - > - <RadioGroup.Label as="span" className="sr-only"> - {color.name} - </RadioGroup.Label> - <span - aria-hidden="true" - className={classNames( - color.class, - 'h-8 w-8 rounded-full border border-black border-opacity-10' - )} - /> - </RadioGroup.Option> + {/* Reviews */} + <div className="mt-6"> + <h3 className="sr-only">Reviews</h3> + <div className="flex items-center"> + <div className="flex items-center"> + {[0, 1, 2, 3, 4].map((rating) => ( + <StarIcon + key={rating} + className={classNames( + product.rating > rating + ? "text-gray-900" + : "text-gray-200", + "h-5 w-5 flex-shrink-0" + )} + aria-hidden="true" + /> ))} </div> - </RadioGroup> + <p className="sr-only">{product.rating} out of 5 stars</p> + </div> </div> - {/* Sizes */} - <div className="mt-10"> - <div className="flex items-center justify-between"> - <h3 className="text-sm font-medium text-gray-900">Size</h3> - <a href="#" className="text-sm font-medium text-indigo-600 hover:text-indigo-500"> - Size guide - </a> - </div> + <form className="mt-10"> + {/* Colors */} + <div> + <h3 className="text-sm font-medium text-gray-900">Color</h3> - <RadioGroup value={selectedSize} onChange={setSelectedSize} className="mt-4"> - <RadioGroup.Label className="sr-only">Choose a size</RadioGroup.Label> - <div className="grid grid-cols-4 gap-4 sm:grid-cols-8 lg:grid-cols-4"> - {product.sizes.map((size) => ( - <RadioGroup.Option - key={size.name} - value={size} - disabled={!size.inStock} - className={({ active }) => - classNames( - size.inStock - ? 'cursor-pointer bg-white text-gray-900 shadow-sm' - : 'cursor-not-allowed bg-gray-50 text-gray-200', - active ? 'ring-2 ring-indigo-500' : '', - 'group relative flex items-center justify-center rounded-md border py-3 px-4 text-sm font-medium uppercase hover:bg-gray-50 focus:outline-none sm:flex-1 sm:py-6' - ) - } - > - {({ active, checked }) => ( - <> - <RadioGroup.Label as="span">{size.name}</RadioGroup.Label> - {size.inStock ? ( - <span - className={classNames( - active ? 'border' : 'border-2', - checked ? 'border-indigo-500' : 'border-transparent', - 'pointer-events-none absolute -inset-px rounded-md' - )} - aria-hidden="true" - /> - ) : ( - <span - aria-hidden="true" - className="pointer-events-none absolute -inset-px rounded-md border-2 border-gray-200" - > - <svg - className="absolute inset-0 h-full w-full stroke-2 text-gray-200" - viewBox="0 0 100 100" - preserveAspectRatio="none" - stroke="currentColor" - > - <line x1={0} y1={100} x2={100} y2={0} vectorEffect="non-scaling-stroke" /> - </svg> - </span> + <RadioGroup + value={selectedColor} + onChange={setSelectedColor} + className="mt-4" + > + <RadioGroup.Label className="sr-only"> + Choose a color + </RadioGroup.Label> + <div className="flex items-center space-x-3"> + {colors.map((color) => ( + <RadioGroup.Option + key={color.name} + value={color} + className={({ active, checked }) => + classNames( + color.selectedClass, + active && checked ? "ring ring-offset-1" : "", + !active && checked ? "ring-2" : "", + "relative -m-0.5 flex cursor-pointer items-center justify-center rounded-full p-0.5 focus:outline-none" + ) + } + > + <RadioGroup.Label as="span" className="sr-only"> + {color.name} + </RadioGroup.Label> + <span + aria-hidden="true" + className={classNames( + color.class, + "h-8 w-8 rounded-full border border-black border-opacity-10" )} - </> - )} - </RadioGroup.Option> - ))} + /> + </RadioGroup.Option> + ))} + </div> + </RadioGroup> + </div> + + {/* Sizes */} + <div className="mt-10"> + <div className="flex items-center justify-between"> + <h3 className="text-sm font-medium text-gray-900">Size</h3> + <a + href="#" + className="text-sm font-medium text-indigo-600 hover:text-indigo-500" + > + Size guide + </a> </div> - </RadioGroup> - </div> - <button - type="submit" - className="mt-10 flex w-full items-center justify-center rounded-md border border-transparent bg-indigo-600 px-8 py-3 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2" - > - Add to Cart - </button> - </form> - </div> + <RadioGroup + value={selectedSize} + onChange={setSelectedSize} + className="mt-4" + > + <RadioGroup.Label className="sr-only"> + Choose a size + </RadioGroup.Label> + <div className="grid grid-cols-4 gap-4 sm:grid-cols-8 lg:grid-cols-4"> + {sizes.map((size) => ( + <RadioGroup.Option + key={size.name} + value={size} + disabled={!size.inStock} + className={({ active }) => + classNames( + size.inStock + ? "cursor-pointer bg-white text-gray-900 shadow-sm" + : "cursor-not-allowed bg-gray-50 text-gray-200", + active ? "ring-2 ring-indigo-500" : "", + "group relative flex items-center justify-center rounded-md border py-3 px-4 text-sm font-medium uppercase hover:bg-gray-50 focus:outline-none sm:flex-1 sm:py-6" + ) + } + > + {({ active, checked }) => ( + <> + <RadioGroup.Label as="span"> + {size.name} + </RadioGroup.Label> + {size.inStock ? ( + <span + className={classNames( + active ? "border" : "border-2", + checked + ? "border-indigo-500" + : "border-transparent", + "pointer-events-none absolute -inset-px rounded-md" + )} + aria-hidden="true" + /> + ) : ( + <span + aria-hidden="true" + className="pointer-events-none absolute -inset-px rounded-md border-2 border-gray-200" + > + <svg + className="absolute inset-0 h-full w-full stroke-2 text-gray-200" + viewBox="0 0 100 100" + preserveAspectRatio="none" + stroke="currentColor" + > + <line + x1={0} + y1={100} + x2={100} + y2={0} + vectorEffect="non-scaling-stroke" + /> + </svg> + </span> + )} + </> + )} + </RadioGroup.Option> + ))} + </div> + </RadioGroup> + </div> - <div className="py-10 lg:col-span-2 lg:col-start-1 lg:border-r lg:border-gray-200 lg:pb-16 lg:pr-8 lg:pt-6"> - {/* Description and details */} - <div> - <h3 className="sr-only">Description</h3> + <button + type="submit" + className="mt-10 flex w-full items-center justify-center rounded-md border border-transparent bg-indigo-600 px-8 py-3 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2" + > + Add to Cart + </button> + </form> + </div> - <div className="space-y-6"> - <p className="text-base text-gray-900">{product.description}</p> + <div className="py-10 lg:col-span-2 lg:col-start-1 lg:border-r lg:border-gray-200 lg:pb-16 lg:pr-8 lg:pt-6"> + {/* Description and details */} + <div> + <h3 className="sr-only">Description</h3> + + <div className="space-y-6"> + <p className="text-base text-gray-900"> + {product.description} + </p> + </div> </div> - </div> - <div className="mt-10"> - <h3 className="text-sm font-medium text-gray-900">Highlights</h3> + <div className="mt-10"> + <h3 className="text-sm font-medium text-gray-900"> + Highlights + </h3> - <div className="mt-4"> - <ul role="list" className="list-disc space-y-2 pl-4 text-sm"> - {product.highlights.map((highlight) => ( - <li key={highlight} className="text-gray-400"> - <span className="text-gray-600">{highlight}</span> - </li> - ))} - </ul> + <div className="mt-4"> + <ul role="list" className="list-disc space-y-2 pl-4 text-sm"> + {highlights.map((highlight) => ( + <li key={highlight} className="text-gray-400"> + <span className="text-gray-600">{highlight}</span> + </li> + ))} + </ul> + </div> </div> - </div> - <div className="mt-10"> - <h2 className="text-sm font-medium text-gray-900">Details</h2> + <div className="mt-10"> + <h2 className="text-sm font-medium text-gray-900">Details</h2> - <div className="mt-4 space-y-6"> - <p className="text-sm text-gray-600">{product.details}</p> + <div className="mt-4 space-y-6"> + <p className="text-sm text-gray-600">{product.description}</p> + </div> </div> </div> </div> </div> - </div> + )} </div> - ) + ); } diff --git a/src/features/product/components/ProductList.js b/src/features/product/components/ProductList.js index 50e9782..94a6842 100644 --- a/src/features/product/components/ProductList.js +++ b/src/features/product/components/ProductList.js @@ -399,7 +399,7 @@ function ProductGrid({ products }) { <div className="mx-auto max-w-2xl px-4 py-0 sm:px-6 sm:py-0 lg:max-w-7xl lg:px-8"> <div className="mt-6 grid grid-cols-1 gap-x-6 gap-y-10 sm:grid-cols-2 lg:grid-cols-3 xl:gap-x-8"> {products.map((product) => ( - <Link to="/product-detail"> + <Link to={`/product-detail/${product.id}`}> <div key={product.id} className="group relative p-2 border-2"> <div className="min-h-60 aspect-h-1 aspect-w-1 w-full overflow-hidden rounded-md bg-gray-200 lg:aspect-none group-hover:opacity-75 lg:h-60"> <img diff --git a/src/features/product/productAPI.js b/src/features/product/productAPI.js index 2c0c567..a745521 100644 --- a/src/features/product/productAPI.js +++ b/src/features/product/productAPI.js @@ -6,6 +6,14 @@ export function fetchAllProducts() { resolve({ data }); }); } +export function fetchProductById(id) { + return new Promise(async (resolve) => { + // TODO: we will not hard coded server url here... + const response = await fetch("http://localhost:8080/products/" + id); + const data = await response.json(); + resolve({ data }); + }); +} export function fetchProductsByFilters(filter, sort, pagination) { // filter ={"brand":"Essence"} // TODO:we will on server support mutilple value diff --git a/src/features/product/productSlice.js b/src/features/product/productSlice.js index c14a275..73243a4 100644 --- a/src/features/product/productSlice.js +++ b/src/features/product/productSlice.js @@ -4,6 +4,7 @@ import { fetchProductsByFilters, fetchBrands, fetchCategories, + fetchProductById, } from "./productAPI"; const initialState = { @@ -12,6 +13,7 @@ const initialState = { categories: [], status: "idle", totalItems: 0, + selectedProduct: null, }; export const fetchAllProductsAsync = createAsyncThunk( @@ -22,6 +24,14 @@ export const fetchAllProductsAsync = createAsyncThunk( return response.data; } ); +export const fetchAllProductsIdAsync = createAsyncThunk( + "product/fetchProductById", + async (id) => { + const response = await fetchProductById(id); + // The value we return becomes the `fulfilled` action payload + return response.data; + } +); export const fetchProductsByFiltersAsync = createAsyncThunk( "product/fetchProductsByFilters", async ({ filter, sort, pagination }) => { @@ -88,6 +98,13 @@ export const productSlice = createSlice({ .addCase(fetchCategoriesAsync.fulfilled, (state, action) => { state.status = "idle"; state.categories = action.payload; + }) + .addCase(fetchAllProductsIdAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(fetchAllProductsIdAsync.fulfilled, (state, action) => { + state.status = "idle"; + state.selectedProduct = action.payload; }); }, }); @@ -98,5 +115,6 @@ export const selectAllProducts = (state) => state.product.products; export const selectBrands = (state) => state.product.brands; export const selectCategories = (state) => state.product.categories; export const selectTotalItems = (state) => state.product.totalItems; +export const selectProductById = (state) => state.product.selectedProduct; export default productSlice.reducer; From 0fb10307f0e7e7b730593175b4ff1dca340658ed Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Fri, 21 Jun 2024 10:58:37 +0530 Subject: [PATCH 18/47] form validation completed --- data.json | 3 +- package-lock.json | 16 ++++ package.json | 1 + src/features/auth/components/Login.js | 37 +++++---- src/features/auth/components/Signup.js | 102 ++++++++++++++++++------- 5 files changed, 114 insertions(+), 45 deletions(-) diff --git a/data.json b/data.json index ab911f5..1484037 100644 --- a/data.json +++ b/data.json @@ -1813,5 +1813,6 @@ "label": "Groceries", "checked": false } - ] + ], + "users": [] } diff --git a/package-lock.json b/package-lock.json index ec3724a..e85912b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@testing-library/user-event": "^14.4.3", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-hook-form": "^7.52.0", "react-redux": "^8.0.5", "react-router-dom": "^6.10.0", "react-scripts": "5.0.1", @@ -14455,6 +14456,21 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, + "node_modules/react-hook-form": { + "version": "7.52.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.52.0.tgz", + "integrity": "sha512-mJX506Xc6mirzLsmXUJyqlAI3Kj9Ph2RhplYhUVffeOQSnubK2uVqBFOBJmvKikvbFV91pxVXmDiR+QMF19x6A==", + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", diff --git a/package.json b/package.json index 377571d..e236cb8 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@testing-library/user-event": "^14.4.3", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-hook-form": "^7.52.0", "react-redux": "^8.0.5", "react-router-dom": "^6.10.0", "react-scripts": "5.0.1", diff --git a/src/features/auth/components/Login.js b/src/features/auth/components/Login.js index 4ed8cee..099d2e9 100644 --- a/src/features/auth/components/Login.js +++ b/src/features/auth/components/Login.js @@ -1,20 +1,13 @@ -import React, { useState } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; -import { - increment, - incrementAsync, - selectCount, -} from '../authSlice'; -import { Link } from 'react-router-dom'; +import React, { useState } from "react"; +import { useSelector, useDispatch } from "react-redux"; +import { increment, incrementAsync } from "../authSlice"; +import { Link } from "react-router-dom"; export default function Login() { - const count = useSelector(selectCount); const dispatch = useDispatch(); - return ( <> - <div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8"> <div className="sm:mx-auto sm:w-full sm:max-w-sm"> <img @@ -30,7 +23,10 @@ export default function Login() { <div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm"> <form className="space-y-6" action="#" method="POST"> <div> - <label htmlFor="email" className="block text-sm font-medium leading-6 text-gray-900"> + <label + htmlFor="email" + className="block text-sm font-medium leading-6 text-gray-900" + > Email address </label> <div className="mt-2"> @@ -47,11 +43,17 @@ export default function Login() { <div> <div className="flex items-center justify-between"> - <label htmlFor="password" className="block text-sm font-medium leading-6 text-gray-900"> + <label + htmlFor="password" + className="block text-sm font-medium leading-6 text-gray-900" + > Password </label> <div className="text-sm"> - <a href="#" className="font-semibold text-indigo-600 hover:text-indigo-500"> + <a + href="#" + className="font-semibold text-indigo-600 hover:text-indigo-500" + > Forgot password? </a> </div> @@ -79,8 +81,11 @@ export default function Login() { </form> <p className="mt-10 text-center text-sm text-gray-500"> - Not a member?{' '} - <Link to="/signup" className="font-semibold leading-6 text-indigo-600 hover:text-indigo-500"> + Not a member?{" "} + <Link + to="/signup" + className="font-semibold leading-6 text-indigo-600 hover:text-indigo-500" + > Create an Account </Link> </p> diff --git a/src/features/auth/components/Signup.js b/src/features/auth/components/Signup.js index 308fa44..62c0355 100644 --- a/src/features/auth/components/Signup.js +++ b/src/features/auth/components/Signup.js @@ -1,20 +1,21 @@ -import React, { useState } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; -import { - increment, - incrementAsync, - selectCount, -} from '../authSlice'; -import { Link } from 'react-router-dom'; - +import React, { useState } from "react"; +import { useSelector, useDispatch } from "react-redux"; +import { increment, incrementAsync } from "../authSlice"; +import { Link } from "react-router-dom"; +import { useForm } from "react-hook-form"; export default function Signup() { - const count = useSelector(selectCount); const dispatch = useDispatch(); + const { + register, + handleSubmit, + watch, + formState: { errors }, + } = useForm(); + console.log(errors); return ( <> - <div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8"> <div className="sm:mx-auto sm:w-full sm:max-w-sm"> <img @@ -28,30 +29,52 @@ export default function Signup() { </div> <div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm"> - <form className="space-y-6" action="#" method="POST"> + <form + noValidate + className="space-y-6" + onSubmit={handleSubmit((data) => { + console.log(data); + })} + > <div> - <label htmlFor="email" className="block text-sm font-medium leading-6 text-gray-900"> + <label + htmlFor="email" + className="block text-sm font-medium leading-6 text-gray-900" + > Email address </label> <div className="mt-2"> <input id="email" - name="email" + {...register("email", { + required: "email is required", + pattern: { + value: /\b[\w\.-]+@[\w\.-]+\.\w{2,4}\b/gi, + message: "email is not valid", + }, + })} type="email" - autoComplete="email" - required className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" /> + {errors.email && ( + <p className="text-red-500">{errors.email.message}</p> + )} </div> </div> <div> <div className="flex items-center justify-between"> - <label htmlFor="password" className="block text-sm font-medium leading-6 text-gray-900"> + <label + htmlFor="password" + className="block text-sm font-medium leading-6 text-gray-900" + > Password </label> <div className="text-sm"> - <a href="#" className="font-semibold text-indigo-600 hover:text-indigo-500"> + <a + href="#" + className="font-semibold text-indigo-600 hover:text-indigo-500" + > Forgot password? </a> </div> @@ -59,30 +82,50 @@ export default function Signup() { <div className="mt-2"> <input id="password" - name="password" + {...register("password", { + required: "password is required", + pattern: { + value: + /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).{8,}$/gm, + message: `- at least 8 characters\n + - must contain at least 1 uppercase letter, 1 lowercase letter, and 1 number\n + - Can contain special characters`, + }, + })} type="password" - autoComplete="current-password" - required className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" /> + {errors.password && ( + <p className="text-red-500">{errors.password.message}</p> + )} </div> </div> <div> <div className="flex items-center justify-between"> - <label htmlFor="password" className="block text-sm font-medium leading-6 text-gray-900"> + <label + htmlFor="password" + className="block text-sm font-medium leading-6 text-gray-900" + > Confirm Password </label> - </div> <div className="mt-2"> <input - id="confirm-password" - name="confirm-password" + id="confirmPassword" + {...register("confirmPassword", { + required: "confirm password is required", + validate: (value, formValues) => + value === formValues.password || "password not matching", + })} type="password" - required className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" /> + {errors.confirmPassword && ( + <p className="text-red-500"> + {errors.confirmPassword.message} + </p> + )} </div> </div> @@ -97,8 +140,11 @@ export default function Signup() { </form> <p className="mt-10 text-center text-sm text-gray-500"> - Already a Member?{' '} - <Link to="/login" className="font-semibold leading-6 text-indigo-600 hover:text-indigo-500"> + Already a Member?{" "} + <Link + to="/login" + className="font-semibold leading-6 text-indigo-600 hover:text-indigo-500" + > Log In </Link> </p> From b50cc6eb6a2800525bcd92967d234687d71d5580 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Fri, 21 Jun 2024 12:57:55 +0530 Subject: [PATCH 19/47] signup/login functionality add --- data.json | 266 +++++++++++++++------- src/App.js | 26 ++- src/app/store.js | 2 + src/features/auth/authAPI.js | 39 +++- src/features/auth/authSlice.js | 57 +++-- src/features/auth/components/Login.js | 48 +++- src/features/auth/components/Protected.js | 12 + src/features/auth/components/Signup.js | 10 +- src/features/navbar/Navbar.js | 12 +- 9 files changed, 345 insertions(+), 127 deletions(-) create mode 100644 src/features/auth/components/Protected.js diff --git a/data.json b/data.json index 1484037..4b3c69b 100644 --- a/data.json +++ b/data.json @@ -1,7 +1,7 @@ { "products": [ { - "id": 1, + "id": "1", "title": "Essence Mascara Lash Princess", "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", @@ -9,7 +9,10 @@ "discountPercentage": 7.17, "rating": 4.94, "stock": 5, - "tags": ["beauty", "mascara"], + "tags": [ + "beauty", + "mascara" + ], "brand": "Essence", "sku": "RCH45Q1A", "weight": 2, @@ -58,7 +61,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Essence%20Mascara%20Lash%20Princess/thumbnail.png" }, { - "id": 2, + "id": "2", "title": "Eyeshadow Palette with Mirror", "description": "The Eyeshadow Palette with Mirror offers a versatile range of eyeshadow shades for creating stunning eye looks. With a built-in mirror, it's convenient for on-the-go makeup application.", "category": "beauty", @@ -66,7 +69,10 @@ "discountPercentage": 5.5, "rating": 3.28, "stock": 44, - "tags": ["beauty", "eyeshadow"], + "tags": [ + "beauty", + "eyeshadow" + ], "brand": "Glamour Beauty", "sku": "MVCFH27F", "weight": 3, @@ -115,7 +121,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/thumbnail.png" }, { - "id": 3, + "id": "3", "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", @@ -123,7 +129,10 @@ "discountPercentage": 18.14, "rating": 3.82, "stock": 59, - "tags": ["beauty", "face powder"], + "tags": [ + "beauty", + "face powder" + ], "brand": "Velvet Touch", "sku": "9EN8WLT2", "weight": 8, @@ -172,7 +181,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png" }, { - "id": 4, + "id": "4", "title": "Red Lipstick", "description": "The Red Lipstick is a classic and bold choice for adding a pop of color to your lips. With a creamy and pigmented formula, it provides a vibrant and long-lasting finish.", "category": "beauty", @@ -180,7 +189,10 @@ "discountPercentage": 19.03, "rating": 2.51, "stock": 68, - "tags": ["beauty", "lipstick"], + "tags": [ + "beauty", + "lipstick" + ], "brand": "Chic Cosmetics", "sku": "O5IF1NTA", "weight": 5, @@ -229,7 +241,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/thumbnail.png" }, { - "id": 5, + "id": "5", "title": "Red Nail Polish", "description": "The Red Nail Polish offers a rich and glossy red hue for vibrant and polished nails. With a quick-drying formula, it provides a salon-quality finish at home.", "category": "beauty", @@ -237,7 +249,10 @@ "discountPercentage": 2.46, "rating": 3.91, "stock": 71, - "tags": ["beauty", "nail polish"], + "tags": [ + "beauty", + "nail polish" + ], "brand": "Nail Couture", "sku": "YUIIIP4W", "weight": 9, @@ -286,7 +301,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Red%20Nail%20Polish/thumbnail.png" }, { - "id": 6, + "id": "6", "title": "Calvin Klein CK One", "description": "CK One by Calvin Klein is a classic unisex fragrance, known for its fresh and clean scent. It's a versatile fragrance suitable for everyday wear.", "category": "fragrances", @@ -294,7 +309,10 @@ "discountPercentage": 0.32, "rating": 4.85, "stock": 17, - "tags": ["fragrances", "perfumes"], + "tags": [ + "fragrances", + "perfumes" + ], "brand": "Calvin Klein", "sku": "DZM2JQZE", "weight": 5, @@ -345,7 +363,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/fragrances/Calvin%20Klein%20CK%20One/thumbnail.png" }, { - "id": 7, + "id": "7", "title": "Chanel Coco Noir Eau De", "description": "Coco Noir by Chanel is an elegant and mysterious fragrance, featuring notes of grapefruit, rose, and sandalwood. Perfect for evening occasions.", "category": "fragrances", @@ -353,7 +371,10 @@ "discountPercentage": 18.64, "rating": 2.76, "stock": 41, - "tags": ["fragrances", "perfumes"], + "tags": [ + "fragrances", + "perfumes" + ], "brand": "Chanel", "sku": "K71HBCGS", "weight": 4, @@ -404,7 +425,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/fragrances/Chanel%20Coco%20Noir%20Eau%20De/thumbnail.png" }, { - "id": 8, + "id": "8", "title": "Dior J'adore", "description": "J'adore by Dior is a luxurious and floral fragrance, known for its blend of ylang-ylang, rose, and jasmine. It embodies femininity and sophistication.", "category": "fragrances", @@ -412,7 +433,10 @@ "discountPercentage": 17.44, "rating": 3.31, "stock": 91, - "tags": ["fragrances", "perfumes"], + "tags": [ + "fragrances", + "perfumes" + ], "brand": "Dior", "sku": "E70NB03B", "weight": 10, @@ -463,7 +487,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/fragrances/Dior%20J'adore/thumbnail.png" }, { - "id": 9, + "id": "9", "title": "Dolce Shine Eau de", "description": "Dolce Shine by Dolce & Gabbana is a vibrant and fruity fragrance, featuring notes of mango, jasmine, and blonde woods. It's a joyful and youthful scent.", "category": "fragrances", @@ -471,7 +495,10 @@ "discountPercentage": 11.47, "rating": 2.68, "stock": 3, - "tags": ["fragrances", "perfumes"], + "tags": [ + "fragrances", + "perfumes" + ], "brand": "Dolce & Gabbana", "sku": "1NBFK980", "weight": 5, @@ -522,7 +549,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/fragrances/Dolce%20Shine%20Eau%20de/thumbnail.png" }, { - "id": 10, + "id": "10", "title": "Gucci Bloom Eau de", "description": "Gucci Bloom by Gucci is a floral and captivating fragrance, with notes of tuberose, jasmine, and Rangoon creeper. It's a modern and romantic scent.", "category": "fragrances", @@ -530,7 +557,10 @@ "discountPercentage": 8.9, "rating": 2.69, "stock": 93, - "tags": ["fragrances", "perfumes"], + "tags": [ + "fragrances", + "perfumes" + ], "brand": "Gucci", "sku": "FFKZ6HOF", "weight": 10, @@ -581,7 +611,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/fragrances/Gucci%20Bloom%20Eau%20de/thumbnail.png" }, { - "id": 11, + "id": "11", "title": "Annibale Colombo Bed", "description": "The Annibale Colombo Bed is a luxurious and elegant bed frame, crafted with high-quality materials for a comfortable and stylish bedroom.", "category": "furniture", @@ -589,7 +619,10 @@ "discountPercentage": 0.29, "rating": 4.14, "stock": 47, - "tags": ["furniture", "beds"], + "tags": [ + "furniture", + "beds" + ], "brand": "Annibale Colombo", "sku": "4KMDTZWF", "weight": 3, @@ -640,7 +673,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Bed/thumbnail.png" }, { - "id": 12, + "id": "12", "title": "Annibale Colombo Sofa", "description": "The Annibale Colombo Sofa is a sophisticated and comfortable seating option, featuring exquisite design and premium upholstery for your living room.", "category": "furniture", @@ -648,7 +681,10 @@ "discountPercentage": 18.54, "rating": 3.08, "stock": 16, - "tags": ["furniture", "sofas"], + "tags": [ + "furniture", + "sofas" + ], "brand": "Annibale Colombo", "sku": "LUU95CQP", "weight": 3, @@ -699,7 +735,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Sofa/thumbnail.png" }, { - "id": 13, + "id": "13", "title": "Bedside Table African Cherry", "description": "The Bedside Table in African Cherry is a stylish and functional addition to your bedroom, providing convenient storage space and a touch of elegance.", "category": "furniture", @@ -707,7 +743,10 @@ "discountPercentage": 9.58, "rating": 4.48, "stock": 16, - "tags": ["furniture", "bedside tables"], + "tags": [ + "furniture", + "bedside tables" + ], "brand": "Furniture Co.", "sku": "OWPLTZYX", "weight": 10, @@ -758,7 +797,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/furniture/Bedside%20Table%20African%20Cherry/thumbnail.png" }, { - "id": 14, + "id": "14", "title": "Knoll Saarinen Executive Conference Chair", "description": "The Knoll Saarinen Executive Conference Chair is a modern and ergonomic chair, perfect for your office or conference room with its timeless design.", "category": "furniture", @@ -766,7 +805,10 @@ "discountPercentage": 15.23, "rating": 4.11, "stock": 47, - "tags": ["furniture", "office chairs"], + "tags": [ + "furniture", + "office chairs" + ], "brand": "Knoll", "sku": "RKHVJ4FE", "weight": 3, @@ -817,7 +859,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/furniture/Knoll%20Saarinen%20Executive%20Conference%20Chair/thumbnail.png" }, { - "id": 15, + "id": "15", "title": "Wooden Bathroom Sink With Mirror", "description": "The Wooden Bathroom Sink with Mirror is a unique and stylish addition to your bathroom, featuring a wooden sink countertop and a matching mirror.", "category": "furniture", @@ -825,7 +867,10 @@ "discountPercentage": 11.22, "rating": 3.26, "stock": 95, - "tags": ["furniture", "bathroom"], + "tags": [ + "furniture", + "bathroom" + ], "brand": "Bath Trends", "sku": "7OLTIEVO", "weight": 6, @@ -876,7 +921,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/furniture/Wooden%20Bathroom%20Sink%20With%20Mirror/thumbnail.png" }, { - "id": 16, + "id": "16", "title": "Apple", "description": "Fresh and crisp apples, perfect for snacking or incorporating into various recipes.", "category": "groceries", @@ -884,7 +929,9 @@ "discountPercentage": 1.97, "rating": 2.96, "stock": 9, - "tags": ["fruits"], + "tags": [ + "fruits" + ], "sku": "QTROUV79", "weight": 8, "dimensions": { @@ -932,7 +979,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Apple/thumbnail.png" }, { - "id": 17, + "id": "17", "title": "Beef Steak", "description": "High-quality beef steak, great for grilling or cooking to your preferred level of doneness.", "category": "groceries", @@ -940,7 +987,9 @@ "discountPercentage": 17.99, "rating": 2.83, "stock": 96, - "tags": ["meat"], + "tags": [ + "meat" + ], "sku": "BWWA2MSO", "weight": 9, "dimensions": { @@ -988,7 +1037,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Beef%20Steak/thumbnail.png" }, { - "id": 18, + "id": "18", "title": "Cat Food", "description": "Nutritious cat food formulated to meet the dietary needs of your feline friend.", "category": "groceries", @@ -996,7 +1045,10 @@ "discountPercentage": 9.57, "rating": 2.88, "stock": 13, - "tags": ["pet supplies", "cat food"], + "tags": [ + "pet supplies", + "cat food" + ], "sku": "C3F8QN6O", "weight": 9, "dimensions": { @@ -1044,7 +1096,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Cat%20Food/thumbnail.png" }, { - "id": 19, + "id": "19", "title": "Chicken Meat", "description": "Fresh and tender chicken meat, suitable for various culinary preparations.", "category": "groceries", @@ -1052,7 +1104,9 @@ "discountPercentage": 10.46, "rating": 4.61, "stock": 69, - "tags": ["meat"], + "tags": [ + "meat" + ], "sku": "G5YEHW7B", "weight": 7, "dimensions": { @@ -1101,7 +1155,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Chicken%20Meat/thumbnail.png" }, { - "id": 20, + "id": "20", "title": "Cooking Oil", "description": "Versatile cooking oil suitable for frying, sautéing, and various culinary applications.", "category": "groceries", @@ -1109,7 +1163,9 @@ "discountPercentage": 18.89, "rating": 4.01, "stock": 22, - "tags": ["cooking essentials"], + "tags": [ + "cooking essentials" + ], "sku": "Q6ZP1UY8", "weight": 8, "dimensions": { @@ -1157,7 +1213,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Cooking%20Oil/thumbnail.png" }, { - "id": 21, + "id": "21", "title": "Cucumber", "description": "Crisp and hydrating cucumbers, ideal for salads, snacks, or as a refreshing side.", "category": "groceries", @@ -1165,7 +1221,9 @@ "discountPercentage": 11.44, "rating": 4.71, "stock": 22, - "tags": ["vegetables"], + "tags": [ + "vegetables" + ], "sku": "6KGF2K6Z", "weight": 9, "dimensions": { @@ -1213,7 +1271,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Cucumber/thumbnail.png" }, { - "id": 22, + "id": "22", "title": "Dog Food", "description": "Specially formulated dog food designed to provide essential nutrients for your canine companion.", "category": "groceries", @@ -1221,7 +1279,10 @@ "discountPercentage": 18.15, "rating": 2.74, "stock": 40, - "tags": ["pet supplies", "dog food"], + "tags": [ + "pet supplies", + "dog food" + ], "sku": "A6QRCH37", "weight": 2, "dimensions": { @@ -1269,7 +1330,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Dog%20Food/thumbnail.png" }, { - "id": 23, + "id": "23", "title": "Eggs", "description": "Fresh eggs, a versatile ingredient for baking, cooking, or breakfast.", "category": "groceries", @@ -1277,7 +1338,9 @@ "discountPercentage": 5.8, "rating": 4.46, "stock": 10, - "tags": ["dairy"], + "tags": [ + "dairy" + ], "sku": "YA617RI7", "weight": 4, "dimensions": { @@ -1325,7 +1388,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Eggs/thumbnail.png" }, { - "id": 24, + "id": "24", "title": "Fish Steak", "description": "Quality fish steak, suitable for grilling, baking, or pan-searing.", "category": "groceries", @@ -1333,7 +1396,9 @@ "discountPercentage": 7, "rating": 4.83, "stock": 99, - "tags": ["seafood"], + "tags": [ + "seafood" + ], "sku": "XNIH1MTA", "weight": 8, "dimensions": { @@ -1381,7 +1446,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Fish%20Steak/thumbnail.png" }, { - "id": 25, + "id": "25", "title": "Green Bell Pepper", "description": "Fresh and vibrant green bell pepper, perfect for adding color and flavor to your dishes.", "category": "groceries", @@ -1389,7 +1454,9 @@ "discountPercentage": 15.5, "rating": 4.28, "stock": 89, - "tags": ["vegetables"], + "tags": [ + "vegetables" + ], "sku": "HU7S7VQ0", "weight": 7, "dimensions": { @@ -1437,7 +1504,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Green%20Bell%20Pepper/thumbnail.png" }, { - "id": 26, + "id": "26", "title": "Green Chili Pepper", "description": "Spicy green chili pepper, ideal for adding heat to your favorite recipes.", "category": "groceries", @@ -1445,7 +1512,9 @@ "discountPercentage": 18.51, "rating": 4.43, "stock": 8, - "tags": ["vegetables"], + "tags": [ + "vegetables" + ], "sku": "Y4RM3JCB", "weight": 2, "dimensions": { @@ -1493,7 +1562,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Green%20Chili%20Pepper/thumbnail.png" }, { - "id": 27, + "id": "27", "title": "Honey Jar", "description": "Pure and natural honey in a convenient jar, perfect for sweetening beverages or drizzling over food.", "category": "groceries", @@ -1501,7 +1570,9 @@ "discountPercentage": 1.91, "rating": 3.5, "stock": 25, - "tags": ["condiments"], + "tags": [ + "condiments" + ], "sku": "BTBNIIOU", "weight": 9, "dimensions": { @@ -1549,7 +1620,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Honey%20Jar/thumbnail.png" }, { - "id": 28, + "id": "28", "title": "Ice Cream", "description": "Creamy and delicious ice cream, available in various flavors for a delightful treat.", "category": "groceries", @@ -1557,7 +1628,9 @@ "discountPercentage": 7.58, "rating": 3.77, "stock": 76, - "tags": ["desserts"], + "tags": [ + "desserts" + ], "sku": "VEZMU1EQ", "weight": 5, "dimensions": { @@ -1608,7 +1681,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Ice%20Cream/thumbnail.png" }, { - "id": 29, + "id": "29", "title": "Juice", "description": "Refreshing fruit juice, packed with vitamins and great for staying hydrated.", "category": "groceries", @@ -1616,7 +1689,9 @@ "discountPercentage": 5.45, "rating": 3.41, "stock": 99, - "tags": ["beverages"], + "tags": [ + "beverages" + ], "sku": "M2K19S06", "weight": 2, "dimensions": { @@ -1664,7 +1739,7 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Juice/thumbnail.png" }, { - "id": 30, + "id": "30", "title": "Kiwi", "description": "Nutrient-rich kiwi, perfect for snacking or adding a tropical twist to your dishes.", "category": "groceries", @@ -1672,7 +1747,9 @@ "discountPercentage": 10.32, "rating": 4.37, "stock": 1, - "tags": ["fruits"], + "tags": [ + "fruits" + ], "sku": "0X3NORB9", "weight": 8, "dimensions": { @@ -1724,95 +1801,124 @@ { "value": "Essence", "label": "Essence", - "checked": false + "checked": false, + "id": "e23f" }, { "value": "Glamour Beauty", "label": "Glamour Beauty", - "checked": false + "checked": false, + "id": "6c60" }, { "value": "Velvet Touch", "label": "Velvet Touch", - "checked": false + "checked": false, + "id": "3062" }, { "value": "Chic Cosmetics", "label": "Chic Cosmetics", - "checked": false + "checked": false, + "id": "77ce" }, { "value": "Nail Couture", "label": "Nail Couture", - "checked": false + "checked": false, + "id": "b6ef" }, { "value": "Calvin Klein", "label": "Calvin Klein", - "checked": false + "checked": false, + "id": "b33c" }, { "value": "Chanel", "label": "Chanel", - "checked": false + "checked": false, + "id": "8d3f" }, { "value": "Dior", "label": "Dior", - "checked": false + "checked": false, + "id": "839a" }, { "value": "Dolce & Gabbana", "label": "Dolce & Gabbana", - "checked": false + "checked": false, + "id": "ded7" }, { "value": "Gucci", "label": "Gucci", - "checked": false + "checked": false, + "id": "8fb0" }, { "value": "Annibale Colombo", "label": "Annibale Colombo", - "checked": false + "checked": false, + "id": "c798" }, { "value": "Furniture Co.", "label": "Furniture Co.", - "checked": false + "checked": false, + "id": "6b10" }, { "value": "Knoll", "label": "Knoll", - "checked": false + "checked": false, + "id": "9c28" }, { "value": "Bath Trends", "label": "Bath Trends", - "checked": false + "checked": false, + "id": "84e2" } ], "categories": [ { "value": "beauty", "label": "Beauty", - "checked": false + "checked": false, + "id": "3040" }, { "value": "fragrances", "label": "Fragrances", - "checked": false + "checked": false, + "id": "3dc4" }, { "value": "furniture", "label": "Furniture", - "checked": false + "checked": false, + "id": "1cc4" }, { "value": "groceries", "label": "Groceries", - "checked": false + "checked": false, + "id": "0d7a" } ], - "users": [] -} + "users": [ + { + "id": "cb1b", + "email": "test@123.com", + "password": "Murtaza123" + }, + { + "id": "0afe", + "email": "test2@gmail.com", + "password": "Tankiwala123" + } + ] +} \ No newline at end of file diff --git a/src/App.js b/src/App.js index 84b3bd6..38d6946 100644 --- a/src/App.js +++ b/src/App.js @@ -14,10 +14,16 @@ import Cart from "./features/cart/Cart"; import CartPage from "./pages/CartPage"; import Checkout from "./pages/Checkout"; import ProductDetailPage from "./pages/ProductDetailPage"; +import Protected from "../features/auth/components/Protected"; const router = createBrowserRouter([ { path: "/", - element: <Home></Home>, + + element: ( + <Protected> + <Home></Home> + </Protected> + ), }, { path: "/login", @@ -29,15 +35,27 @@ const router = createBrowserRouter([ }, { path: "/cart", - element: <CartPage></CartPage>, + element: ( + <Protected> + <CartPage></CartPage> + </Protected> + ), }, { path: "/checkout", - element: <Checkout></Checkout>, + element: ( + <Protected> + <Checkout></Checkout> + </Protected> + ), }, { path: "/product-detail/:id", // :id provided by react-router - element: <ProductDetailPage></ProductDetailPage>, + element: ( + <Protected> + <ProductDetailPage></ProductDetailPage> + </Protected> + ), }, ]); diff --git a/src/app/store.js b/src/app/store.js index aacfdad..2c725b9 100644 --- a/src/app/store.js +++ b/src/app/store.js @@ -1,8 +1,10 @@ import { configureStore } from "@reduxjs/toolkit"; import productReducer from "../features/product/productSlice"; +import authReducer from "../features/auth/authSlice"; export const store = configureStore({ reducer: { product: productReducer, + auth: authReducer, }, }); diff --git a/src/features/auth/authAPI.js b/src/features/auth/authAPI.js index 76c5e9b..4df73f6 100644 --- a/src/features/auth/authAPI.js +++ b/src/features/auth/authAPI.js @@ -1,8 +1,33 @@ -export function fetchCount(amount = 1) { - return new Promise(async (resolve) =>{ - const response = await fetch('http://localhost:8080') - const data = await response.json() - resolve({data}) - } - ); +export function createUser(userData) { + return new Promise(async (resolve) => { + const response = await fetch("http://localhost:8080/users", { + method: "POST", + body: JSON.stringify(userData), + headers: { "content-type": "application/json" }, + }); + const data = await response.json(); + // TODO:on server it will only return some info of user (not password) + resolve({ data }); + }); +} +export function checkUser(loginInfo) { + return new Promise(async (resolve, reject) => { + const email = loginInfo.email; + const password = loginInfo.password; + const response = await fetch("http://localhost:8080/users?email=" + email); + const data = await response.json(); + console.log({ data }); + if (data.length) { + // data[0] to extract first match.. + if (password === data[0].password) { + resolve({ data: data[0] }); + } else { + reject({ message: "wrong credentials" }); + } + } else { + reject({ message: "User not found" }); + } + + // TODO:on server it will only return some info of user (not password) + }); } diff --git a/src/features/auth/authSlice.js b/src/features/auth/authSlice.js index 22ef764..917b6cf 100644 --- a/src/features/auth/authSlice.js +++ b/src/features/auth/authSlice.js @@ -1,22 +1,31 @@ -import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; -import { fetchCount } from './authAPI'; +import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; +import { checkUser, createUser } from "./authAPI"; const initialState = { - value: 0, - status: 'idle', + loggedInUser: null, + status: "idle", + erorr: null, }; -export const incrementAsync = createAsyncThunk( - 'counter/fetchCount', - async (amount) => { - const response = await fetchCount(amount); +export const createUserAsync = createAsyncThunk( + "user/createUser", + async (userData) => { + const response = await createUser(userData); + // The value we return becomes the `fulfilled` action payload + return response.data; + } +); +export const checkUserAsync = createAsyncThunk( + "user/checkUser", + async (loginInfo) => { + const response = await checkUser(loginInfo); // The value we return becomes the `fulfilled` action payload return response.data; } ); -export const counterSlice = createSlice({ - name: 'counter', +export const authSlice = createSlice({ + name: "user", initialState, reducers: { increment: (state) => { @@ -25,18 +34,30 @@ export const counterSlice = createSlice({ }, extraReducers: (builder) => { builder - .addCase(incrementAsync.pending, (state) => { - state.status = 'loading'; + .addCase(createUserAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(createUserAsync.fulfilled, (state, action) => { + state.status = "idle"; + state.loggedInUser = action.payload; + }) + .addCase(checkUserAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(checkUserAsync.fulfilled, (state, action) => { + state.status = "idle"; + state.loggedInUser = action.payload; }) - .addCase(incrementAsync.fulfilled, (state, action) => { - state.status = 'idle'; - state.value += action.payload; + .addCase(checkUserAsync.rejected, (state, action) => { + state.status = "idle"; + state.erorr = action.error; }); }, }); -export const { increment } = counterSlice.actions; +export const { increment } = authSlice.actions; -export const selectCount = (state) => state.counter.value; +export const selectLoggedInUser = (state) => state.auth.loggedInUser; +export const selectError = (state) => state.auth.erorr; -export default counterSlice.reducer; +export default authSlice.reducer; diff --git a/src/features/auth/components/Login.js b/src/features/auth/components/Login.js index 099d2e9..711d9da 100644 --- a/src/features/auth/components/Login.js +++ b/src/features/auth/components/Login.js @@ -1,13 +1,23 @@ import React, { useState } from "react"; import { useSelector, useDispatch } from "react-redux"; -import { increment, incrementAsync } from "../authSlice"; -import { Link } from "react-router-dom"; +import { checkUserAsync, selectError, selectLoggedInUser } from "../authSlice"; +import { Link, Navigate } from "react-router-dom"; +import { useForm } from "react-hook-form"; export default function Login() { const dispatch = useDispatch(); + const error = useSelector(selectError); + const user = useSelector(selectLoggedInUser); + const { + register, + handleSubmit, + formState: { errors }, + } = useForm(); + console.log(errors); return ( <> + {user && <Navigate to="/" replace={true}></Navigate>} <div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8"> <div className="sm:mx-auto sm:w-full sm:max-w-sm"> <img @@ -21,7 +31,16 @@ export default function Login() { </div> <div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm"> - <form className="space-y-6" action="#" method="POST"> + <form + noValidate + className="space-y-6" + onSubmit={handleSubmit((data) => { + dispatch( + checkUserAsync({ email: data.email, password: data.password }) + ); + console.log(data); + })} + > <div> <label htmlFor="email" @@ -32,12 +51,19 @@ export default function Login() { <div className="mt-2"> <input id="email" - name="email" + {...register("email", { + required: "email is required", + pattern: { + value: /\b[\w\.-]+@[\w\.-]+\.\w{2,4}\b/gi, + message: "email is not valid", + }, + })} type="email" - autoComplete="email" - required className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" /> + {errors.email && ( + <p className="text-red-500">{errors.email.message}</p> + )} </div> </div> @@ -61,12 +87,16 @@ export default function Login() { <div className="mt-2"> <input id="password" - name="password" + {...register("password", { + required: "password is required", + })} type="password" - autoComplete="current-password" - required className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" /> + {errors.password && ( + <p className="text-red-500">{errors.password.message}</p> + )} + {error && <p className="text-red-500">{error.message}</p>} </div> </div> diff --git a/src/features/auth/components/Protected.js b/src/features/auth/components/Protected.js new file mode 100644 index 0000000..55d5347 --- /dev/null +++ b/src/features/auth/components/Protected.js @@ -0,0 +1,12 @@ +import { useSelector } from "react-redux"; +import { Navigate } from "react-router-dom"; +import { selectLoggedInUser } from "../authSlice"; +function Protected({ children }) { + const user = useSelector(selectLoggedInUser); + if (!user) { + return <Navigate to="/login" replace={true}></Navigate>; + } + return children; +} + +export default Protected; diff --git a/src/features/auth/components/Signup.js b/src/features/auth/components/Signup.js index 62c0355..81fbf96 100644 --- a/src/features/auth/components/Signup.js +++ b/src/features/auth/components/Signup.js @@ -1,14 +1,14 @@ import React, { useState } from "react"; import { useSelector, useDispatch } from "react-redux"; -import { increment, incrementAsync } from "../authSlice"; -import { Link } from "react-router-dom"; +import { selectLoggedInUser, createUserAsync } from "../authSlice"; +import { Link, Navigate } from "react-router-dom"; import { useForm } from "react-hook-form"; export default function Signup() { const dispatch = useDispatch(); + const user = useSelector(selectLoggedInUser); const { register, handleSubmit, - watch, formState: { errors }, } = useForm(); @@ -16,6 +16,7 @@ export default function Signup() { return ( <> + {user && <Navigate to="/" replace={true}></Navigate>} <div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8"> <div className="sm:mx-auto sm:w-full sm:max-w-sm"> <img @@ -33,6 +34,9 @@ export default function Signup() { noValidate className="space-y-6" onSubmit={handleSubmit((data) => { + dispatch( + createUserAsync({ email: data.email, password: data.password }) + ); console.log(data); })} > diff --git a/src/features/navbar/Navbar.js b/src/features/navbar/Navbar.js index f265d79..376d3ec 100644 --- a/src/features/navbar/Navbar.js +++ b/src/features/navbar/Navbar.js @@ -18,9 +18,9 @@ const navigation = [ { name: "Team", href: "#", current: false }, ]; const userNavigation = [ - { name: "Your Profile", href: "#" }, - { name: "Settings", href: "#" }, - { name: "Sign out", href: "#" }, + { name: "Your Profile", link: "#" }, + { name: "Settings", link: "#" }, + { name: "Sign out", link: "/login" }, ]; function classNames(...classes) { @@ -109,15 +109,15 @@ function NavBar({ children }) { {userNavigation.map((item) => ( <Menu.Item key={item.name}> {({ active }) => ( - <a - href={item.href} + <Link + to={item.link} className={classNames( active ? "bg-gray-100" : "", "block px-4 py-2 text-sm text-gray-700" )} > {item.name} - </a> + </Link> )} </Menu.Item> ))} From 5a68db090c1d1afc435ac13a4742015ba19fde98 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Fri, 21 Jun 2024 13:13:00 +0530 Subject: [PATCH 20/47] fix path issue (Protected) --- src/App.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.js b/src/App.js index 38d6946..537483c 100644 --- a/src/App.js +++ b/src/App.js @@ -14,7 +14,7 @@ import Cart from "./features/cart/Cart"; import CartPage from "./pages/CartPage"; import Checkout from "./pages/Checkout"; import ProductDetailPage from "./pages/ProductDetailPage"; -import Protected from "../features/auth/components/Protected"; +import Protected from "./features/auth/components/Protected"; const router = createBrowserRouter([ { path: "/", From 4e8afb628652d38129e9938eae0d822128281343 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Sat, 22 Jun 2024 12:03:12 +0530 Subject: [PATCH 21/47] add to cart API created --- data.json | 188 ++++++++++++++++++ src/app/store.js | 2 + src/features/cart/Cart.js | 53 +++-- src/features/cart/cartAPI.js | 18 +- src/features/cart/cartSlice.js | 36 ++-- .../product/components/ProductDetail.js | 11 + 6 files changed, 256 insertions(+), 52 deletions(-) diff --git a/data.json b/data.json index 4b3c69b..c6043fc 100644 --- a/data.json +++ b/data.json @@ -1920,5 +1920,193 @@ "email": "test2@gmail.com", "password": "Tankiwala123" } + ], + "cart": [ + { + "id": "1", + "title": "Essence Mascara Lash Princess", + "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, + "stock": 5, + "tags": [ + "beauty", + "mascara" + ], + "brand": "Essence", + "sku": "RCH45Q1A", + "weight": 2, + "dimensions": { + "width": 23.17, + "height": 14.43, + "depth": 28.01 + }, + "warrantyInformation": "1 month warranty", + "shippingInformation": "Ships in 1 month", + "availabilityStatus": "Low Stock", + "reviews": [ + { + "rating": 2, + "comment": "Very unhappy with my purchase!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "John Doe", + "reviewerEmail": "john.doe@x.dummyjson.com" + }, + { + "rating": 2, + "comment": "Not as described!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Nolan Gonzalez", + "reviewerEmail": "nolan.gonzalez@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Very satisfied!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Scarlett Wright", + "reviewerEmail": "scarlett.wright@x.dummyjson.com" + } + ], + "returnPolicy": "30 days return policy", + "minimumOrderQuantity": 24, + "meta": { + "createdAt": "2024-05-23T08:56:21.618Z", + "updatedAt": "2024-05-23T08:56:21.618Z", + "barcode": "9164035109868", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "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", + "quantity": 1, + "user": "cb1b" + }, + { + "id": "2", + "title": "Eyeshadow Palette with Mirror", + "description": "The Eyeshadow Palette with Mirror offers a versatile range of eyeshadow shades for creating stunning eye looks. With a built-in mirror, it's convenient for on-the-go makeup application.", + "category": "beauty", + "price": 19.99, + "discountPercentage": 5.5, + "rating": 3.28, + "stock": 44, + "tags": [ + "beauty", + "eyeshadow" + ], + "brand": "Glamour Beauty", + "sku": "MVCFH27F", + "weight": 3, + "dimensions": { + "width": 12.42, + "height": 8.63, + "depth": 29.13 + }, + "warrantyInformation": "1 year warranty", + "shippingInformation": "Ships in 2 weeks", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 4, + "comment": "Very satisfied!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Liam Garcia", + "reviewerEmail": "liam.garcia@x.dummyjson.com" + }, + { + "rating": 1, + "comment": "Very disappointed!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Nora Russell", + "reviewerEmail": "nora.russell@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Highly impressed!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Elena Baker", + "reviewerEmail": "elena.baker@x.dummyjson.com" + } + ], + "returnPolicy": "30 days return policy", + "minimumOrderQuantity": 32, + "meta": { + "createdAt": "2024-05-23T08:56:21.618Z", + "updatedAt": "2024-05-23T08:56:21.618Z", + "barcode": "2817839095220", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/thumbnail.png", + "quantity": 1, + "user": "cb1b" + }, + { + "id": "3", + "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, + "discountPercentage": 18.14, + "rating": 3.82, + "stock": 59, + "tags": [ + "beauty", + "face powder" + ], + "brand": "Velvet Touch", + "sku": "9EN8WLT2", + "weight": 8, + "dimensions": { + "width": 24.16, + "height": 10.7, + "depth": 11.07 + }, + "warrantyInformation": "2 year warranty", + "shippingInformation": "Ships in 1-2 business days", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Very happy with my purchase!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Ethan Thompson", + "reviewerEmail": "ethan.thompson@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Great value for money!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Levi Hicks", + "reviewerEmail": "levi.hicks@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Highly impressed!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Hazel Gardner", + "reviewerEmail": "hazel.gardner@x.dummyjson.com" + } + ], + "returnPolicy": "60 days return policy", + "minimumOrderQuantity": 25, + "meta": { + "createdAt": "2024-05-23T08:56:21.618Z", + "updatedAt": "2024-05-23T08:56:21.618Z", + "barcode": "0516267971277", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png", + "quantity": 1, + "user": "cb1b" + } ] } \ No newline at end of file diff --git a/src/app/store.js b/src/app/store.js index 2c725b9..e9ec11b 100644 --- a/src/app/store.js +++ b/src/app/store.js @@ -1,10 +1,12 @@ import { configureStore } from "@reduxjs/toolkit"; import productReducer from "../features/product/productSlice"; import authReducer from "../features/auth/authSlice"; +import cartReducer from "../features/cart/cartSlice"; export const store = configureStore({ reducer: { product: productReducer, auth: authReducer, + cart: cartReducer, }, }); diff --git a/src/features/cart/Cart.js b/src/features/cart/Cart.js index 4dfc91f..a069517 100644 --- a/src/features/cart/Cart.js +++ b/src/features/cart/Cart.js @@ -1,40 +1,39 @@ -import React, { useState, Fragment } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; -import { increment, incrementAsync, selectCount } from './cartSlice'; -import { Dialog, Transition } from '@headlessui/react'; -import { XMarkIcon } from '@heroicons/react/24/outline'; -import { Link } from 'react-router-dom'; +import React, { useState, Fragment } from "react"; +import { useSelector, useDispatch } from "react-redux"; +import {} from "./cartSlice"; +import { Dialog, Transition } from "@headlessui/react"; +import { XMarkIcon } from "@heroicons/react/24/outline"; +import { Link } from "react-router-dom"; const products = [ { id: 1, - name: 'Throwback Hip Bag', - href: '#', - color: 'Salmon', - price: '$90.00', + name: "Throwback Hip Bag", + href: "#", + color: "Salmon", + price: "$90.00", quantity: 1, imageSrc: - 'https://tailwindui.com/img/ecommerce-images/shopping-cart-page-04-product-01.jpg', + "https://tailwindui.com/img/ecommerce-images/shopping-cart-page-04-product-01.jpg", imageAlt: - 'Salmon orange fabric pouch with match zipper, gray zipper pull, and adjustable hip belt.', + "Salmon orange fabric pouch with match zipper, gray zipper pull, and adjustable hip belt.", }, { id: 2, - name: 'Medium Stuff Satchel', - href: '#', - color: 'Blue', - price: '$32.00', + name: "Medium Stuff Satchel", + href: "#", + color: "Blue", + price: "$32.00", quantity: 1, imageSrc: - 'https://tailwindui.com/img/ecommerce-images/shopping-cart-page-04-product-02.jpg', + "https://tailwindui.com/img/ecommerce-images/shopping-cart-page-04-product-02.jpg", imageAlt: - 'Front of satchel with blue canvas body, black straps and handle, drawstring top, and front zipper pouch.', + "Front of satchel with blue canvas body, black straps and handle, drawstring top, and front zipper pouch.", }, // More products... ]; export default function Cart() { - const count = useSelector(selectCount); const dispatch = useDispatch(); const [open, setOpen] = useState(true); @@ -109,7 +108,7 @@ export default function Cart() { Shipping and taxes calculated at checkout. </p> <div className="mt-6"> - <Link + <Link to="/checkout" className="flex items-center justify-center rounded-md border border-transparent bg-indigo-600 px-6 py-3 text-base font-medium text-white shadow-sm hover:bg-indigo-700" > @@ -120,13 +119,13 @@ export default function Cart() { <p> or <Link to="/"> - <button - type="button" - className="font-medium text-indigo-600 hover:text-indigo-500" - > - Continue Shopping - <span aria-hidden="true"> →</span> - </button> + <button + type="button" + className="font-medium text-indigo-600 hover:text-indigo-500" + > + Continue Shopping + <span aria-hidden="true"> →</span> + </button> </Link> </p> </div> diff --git a/src/features/cart/cartAPI.js b/src/features/cart/cartAPI.js index 76c5e9b..0e7c5ab 100644 --- a/src/features/cart/cartAPI.js +++ b/src/features/cart/cartAPI.js @@ -1,8 +1,12 @@ -export function fetchCount(amount = 1) { - return new Promise(async (resolve) =>{ - const response = await fetch('http://localhost:8080') - const data = await response.json() - resolve({data}) - } - ); +export function addToCart(item) { + return new Promise(async (resolve) => { + const response = await fetch("http://localhost:8080/cart", { + method: "POST", + body: JSON.stringify(item), + headers: { "content-type": "application/json" }, + }); + const data = await response.json(); + // TODO:on server it will only return some info of user (not password) + resolve({ data }); + }); } diff --git a/src/features/cart/cartSlice.js b/src/features/cart/cartSlice.js index bf3e0a4..8a027fc 100644 --- a/src/features/cart/cartSlice.js +++ b/src/features/cart/cartSlice.js @@ -1,22 +1,22 @@ -import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; -import { fetchCount } from './cartAPI'; +import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; +import { addToCart } from "./cartAPI"; const initialState = { - value: 0, - status: 'idle', + items: [], + status: "idle", }; -export const incrementAsync = createAsyncThunk( - 'counter/fetchCount', - async (amount) => { - const response = await fetchCount(amount); +export const addToCartAsync = createAsyncThunk( + "cart/addToCart", + async (item) => { + const response = await addToCart(item); // The value we return becomes the `fulfilled` action payload return response.data; } ); -export const counterSlice = createSlice({ - name: 'counter', +export const cartSlice = createSlice({ + name: "cart", initialState, reducers: { increment: (state) => { @@ -25,18 +25,18 @@ export const counterSlice = createSlice({ }, extraReducers: (builder) => { builder - .addCase(incrementAsync.pending, (state) => { - state.status = 'loading'; + .addCase(addToCartAsync.pending, (state) => { + state.status = "loading"; }) - .addCase(incrementAsync.fulfilled, (state, action) => { - state.status = 'idle'; - state.value += action.payload; + .addCase(addToCartAsync.fulfilled, (state, action) => { + state.status = "idle"; + state.items.push(action.payload); }); }, }); -export const { increment } = counterSlice.actions; +export const { increment } = cartSlice.actions; -export const selectCount = (state) => state.counter.value; +export const selectItems = (state) => state.cart.items; -export default counterSlice.reducer; +export default cartSlice.reducer; diff --git a/src/features/product/components/ProductDetail.js b/src/features/product/components/ProductDetail.js index bc4f4ce..b6ac35b 100644 --- a/src/features/product/components/ProductDetail.js +++ b/src/features/product/components/ProductDetail.js @@ -4,6 +4,8 @@ import { RadioGroup } from "@headlessui/react"; import { useSelector, useDispatch } from "react-redux"; import { fetchAllProductsIdAsync, selectProductById } from "../productSlice"; import { useParams } from "react-router-dom"; +import { addToCartAsync } from "../../cart/cartSlice"; +import { selectLoggedInUser } from "../../auth/authSlice"; // TODO: In server data we will add sizes,colors, highlights to each product @@ -36,10 +38,16 @@ function classNames(...classes) { export default function ProductDetail() { const [selectedColor, setSelectedColor] = useState(colors[0]); const [selectedSize, setSelectedSize] = useState(sizes[2]); + const user = useSelector(selectLoggedInUser); const product = useSelector(selectProductById); const dispatch = useDispatch(); const params = useParams(); // hook provided by react-router to fetch parameters + const handleCart = (e) => { + e.preventDefault(); + dispatch(addToCartAsync({ ...product, quantity: 1, user: user.id })); + }; + useEffect(() => { dispatch(fetchAllProductsIdAsync(params.id)); // :id from path in app.js }, [dispatch, params.id]); @@ -283,6 +291,9 @@ export default function ProductDetail() { </div> <button + onClick={(e) => { + handleCart(e); + }} type="submit" className="mt-10 flex w-full items-center justify-center rounded-md border border-transparent bg-indigo-600 px-8 py-3 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2" > From 2291936cf5f35eda2e0663511cf2ef81936e20d1 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Sat, 22 Jun 2024 12:49:31 +0530 Subject: [PATCH 22/47] add to cart/fetch items in UI (Cart) --- data.json | 211 ++++++--------------------------- src/App.js | 12 ++ src/features/cart/Cart.js | 57 +++------ src/features/cart/cartAPI.js | 8 ++ src/features/cart/cartSlice.js | 17 ++- src/features/navbar/Navbar.js | 19 ++- 6 files changed, 101 insertions(+), 223 deletions(-) diff --git a/data.json b/data.json index c6043fc..29ac5e2 100644 --- a/data.json +++ b/data.json @@ -9,10 +9,7 @@ "discountPercentage": 7.17, "rating": 4.94, "stock": 5, - "tags": [ - "beauty", - "mascara" - ], + "tags": ["beauty", "mascara"], "brand": "Essence", "sku": "RCH45Q1A", "weight": 2, @@ -69,10 +66,7 @@ "discountPercentage": 5.5, "rating": 3.28, "stock": 44, - "tags": [ - "beauty", - "eyeshadow" - ], + "tags": ["beauty", "eyeshadow"], "brand": "Glamour Beauty", "sku": "MVCFH27F", "weight": 3, @@ -129,10 +123,7 @@ "discountPercentage": 18.14, "rating": 3.82, "stock": 59, - "tags": [ - "beauty", - "face powder" - ], + "tags": ["beauty", "face powder"], "brand": "Velvet Touch", "sku": "9EN8WLT2", "weight": 8, @@ -189,10 +180,7 @@ "discountPercentage": 19.03, "rating": 2.51, "stock": 68, - "tags": [ - "beauty", - "lipstick" - ], + "tags": ["beauty", "lipstick"], "brand": "Chic Cosmetics", "sku": "O5IF1NTA", "weight": 5, @@ -249,10 +237,7 @@ "discountPercentage": 2.46, "rating": 3.91, "stock": 71, - "tags": [ - "beauty", - "nail polish" - ], + "tags": ["beauty", "nail polish"], "brand": "Nail Couture", "sku": "YUIIIP4W", "weight": 9, @@ -309,10 +294,7 @@ "discountPercentage": 0.32, "rating": 4.85, "stock": 17, - "tags": [ - "fragrances", - "perfumes" - ], + "tags": ["fragrances", "perfumes"], "brand": "Calvin Klein", "sku": "DZM2JQZE", "weight": 5, @@ -371,10 +353,7 @@ "discountPercentage": 18.64, "rating": 2.76, "stock": 41, - "tags": [ - "fragrances", - "perfumes" - ], + "tags": ["fragrances", "perfumes"], "brand": "Chanel", "sku": "K71HBCGS", "weight": 4, @@ -433,10 +412,7 @@ "discountPercentage": 17.44, "rating": 3.31, "stock": 91, - "tags": [ - "fragrances", - "perfumes" - ], + "tags": ["fragrances", "perfumes"], "brand": "Dior", "sku": "E70NB03B", "weight": 10, @@ -495,10 +471,7 @@ "discountPercentage": 11.47, "rating": 2.68, "stock": 3, - "tags": [ - "fragrances", - "perfumes" - ], + "tags": ["fragrances", "perfumes"], "brand": "Dolce & Gabbana", "sku": "1NBFK980", "weight": 5, @@ -557,10 +530,7 @@ "discountPercentage": 8.9, "rating": 2.69, "stock": 93, - "tags": [ - "fragrances", - "perfumes" - ], + "tags": ["fragrances", "perfumes"], "brand": "Gucci", "sku": "FFKZ6HOF", "weight": 10, @@ -619,10 +589,7 @@ "discountPercentage": 0.29, "rating": 4.14, "stock": 47, - "tags": [ - "furniture", - "beds" - ], + "tags": ["furniture", "beds"], "brand": "Annibale Colombo", "sku": "4KMDTZWF", "weight": 3, @@ -681,10 +648,7 @@ "discountPercentage": 18.54, "rating": 3.08, "stock": 16, - "tags": [ - "furniture", - "sofas" - ], + "tags": ["furniture", "sofas"], "brand": "Annibale Colombo", "sku": "LUU95CQP", "weight": 3, @@ -743,10 +707,7 @@ "discountPercentage": 9.58, "rating": 4.48, "stock": 16, - "tags": [ - "furniture", - "bedside tables" - ], + "tags": ["furniture", "bedside tables"], "brand": "Furniture Co.", "sku": "OWPLTZYX", "weight": 10, @@ -805,10 +766,7 @@ "discountPercentage": 15.23, "rating": 4.11, "stock": 47, - "tags": [ - "furniture", - "office chairs" - ], + "tags": ["furniture", "office chairs"], "brand": "Knoll", "sku": "RKHVJ4FE", "weight": 3, @@ -867,10 +825,7 @@ "discountPercentage": 11.22, "rating": 3.26, "stock": 95, - "tags": [ - "furniture", - "bathroom" - ], + "tags": ["furniture", "bathroom"], "brand": "Bath Trends", "sku": "7OLTIEVO", "weight": 6, @@ -929,9 +884,7 @@ "discountPercentage": 1.97, "rating": 2.96, "stock": 9, - "tags": [ - "fruits" - ], + "tags": ["fruits"], "sku": "QTROUV79", "weight": 8, "dimensions": { @@ -987,9 +940,7 @@ "discountPercentage": 17.99, "rating": 2.83, "stock": 96, - "tags": [ - "meat" - ], + "tags": ["meat"], "sku": "BWWA2MSO", "weight": 9, "dimensions": { @@ -1045,10 +996,7 @@ "discountPercentage": 9.57, "rating": 2.88, "stock": 13, - "tags": [ - "pet supplies", - "cat food" - ], + "tags": ["pet supplies", "cat food"], "sku": "C3F8QN6O", "weight": 9, "dimensions": { @@ -1104,9 +1052,7 @@ "discountPercentage": 10.46, "rating": 4.61, "stock": 69, - "tags": [ - "meat" - ], + "tags": ["meat"], "sku": "G5YEHW7B", "weight": 7, "dimensions": { @@ -1163,9 +1109,7 @@ "discountPercentage": 18.89, "rating": 4.01, "stock": 22, - "tags": [ - "cooking essentials" - ], + "tags": ["cooking essentials"], "sku": "Q6ZP1UY8", "weight": 8, "dimensions": { @@ -1221,9 +1165,7 @@ "discountPercentage": 11.44, "rating": 4.71, "stock": 22, - "tags": [ - "vegetables" - ], + "tags": ["vegetables"], "sku": "6KGF2K6Z", "weight": 9, "dimensions": { @@ -1279,10 +1221,7 @@ "discountPercentage": 18.15, "rating": 2.74, "stock": 40, - "tags": [ - "pet supplies", - "dog food" - ], + "tags": ["pet supplies", "dog food"], "sku": "A6QRCH37", "weight": 2, "dimensions": { @@ -1338,9 +1277,7 @@ "discountPercentage": 5.8, "rating": 4.46, "stock": 10, - "tags": [ - "dairy" - ], + "tags": ["dairy"], "sku": "YA617RI7", "weight": 4, "dimensions": { @@ -1396,9 +1333,7 @@ "discountPercentage": 7, "rating": 4.83, "stock": 99, - "tags": [ - "seafood" - ], + "tags": ["seafood"], "sku": "XNIH1MTA", "weight": 8, "dimensions": { @@ -1454,9 +1389,7 @@ "discountPercentage": 15.5, "rating": 4.28, "stock": 89, - "tags": [ - "vegetables" - ], + "tags": ["vegetables"], "sku": "HU7S7VQ0", "weight": 7, "dimensions": { @@ -1512,9 +1445,7 @@ "discountPercentage": 18.51, "rating": 4.43, "stock": 8, - "tags": [ - "vegetables" - ], + "tags": ["vegetables"], "sku": "Y4RM3JCB", "weight": 2, "dimensions": { @@ -1570,9 +1501,7 @@ "discountPercentage": 1.91, "rating": 3.5, "stock": 25, - "tags": [ - "condiments" - ], + "tags": ["condiments"], "sku": "BTBNIIOU", "weight": 9, "dimensions": { @@ -1628,9 +1557,7 @@ "discountPercentage": 7.58, "rating": 3.77, "stock": 76, - "tags": [ - "desserts" - ], + "tags": ["desserts"], "sku": "VEZMU1EQ", "weight": 5, "dimensions": { @@ -1689,9 +1616,7 @@ "discountPercentage": 5.45, "rating": 3.41, "stock": 99, - "tags": [ - "beverages" - ], + "tags": ["beverages"], "sku": "M2K19S06", "weight": 2, "dimensions": { @@ -1747,9 +1672,7 @@ "discountPercentage": 10.32, "rating": 4.37, "stock": 1, - "tags": [ - "fruits" - ], + "tags": ["fruits"], "sku": "0X3NORB9", "weight": 8, "dimensions": { @@ -1931,10 +1854,7 @@ "discountPercentage": 7.17, "rating": 4.94, "stock": 5, - "tags": [ - "beauty", - "mascara" - ], + "tags": ["beauty", "mascara"], "brand": "Essence", "sku": "RCH45Q1A", "weight": 2, @@ -1993,10 +1913,7 @@ "discountPercentage": 5.5, "rating": 3.28, "stock": 44, - "tags": [ - "beauty", - "eyeshadow" - ], + "tags": ["beauty", "eyeshadow"], "brand": "Glamour Beauty", "sku": "MVCFH27F", "weight": 3, @@ -2045,68 +1962,6 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/thumbnail.png", "quantity": 1, "user": "cb1b" - }, - { - "id": "3", - "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, - "discountPercentage": 18.14, - "rating": 3.82, - "stock": 59, - "tags": [ - "beauty", - "face powder" - ], - "brand": "Velvet Touch", - "sku": "9EN8WLT2", - "weight": 8, - "dimensions": { - "width": 24.16, - "height": 10.7, - "depth": 11.07 - }, - "warrantyInformation": "2 year warranty", - "shippingInformation": "Ships in 1-2 business days", - "availabilityStatus": "In Stock", - "reviews": [ - { - "rating": 5, - "comment": "Very happy with my purchase!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Ethan Thompson", - "reviewerEmail": "ethan.thompson@x.dummyjson.com" - }, - { - "rating": 4, - "comment": "Great value for money!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Levi Hicks", - "reviewerEmail": "levi.hicks@x.dummyjson.com" - }, - { - "rating": 5, - "comment": "Highly impressed!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Hazel Gardner", - "reviewerEmail": "hazel.gardner@x.dummyjson.com" - } - ], - "returnPolicy": "60 days return policy", - "minimumOrderQuantity": 25, - "meta": { - "createdAt": "2024-05-23T08:56:21.618Z", - "updatedAt": "2024-05-23T08:56:21.618Z", - "barcode": "0516267971277", - "qrCode": "https://dummyjson.com/public/qr-code.png" - }, - "images": [ - "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/1.png" - ], - "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png", - "quantity": 1, - "user": "cb1b" } ] -} \ No newline at end of file +} diff --git a/src/App.js b/src/App.js index 537483c..4af23d5 100644 --- a/src/App.js +++ b/src/App.js @@ -15,6 +15,10 @@ import CartPage from "./pages/CartPage"; import Checkout from "./pages/Checkout"; import ProductDetailPage from "./pages/ProductDetailPage"; import Protected from "./features/auth/components/Protected"; +import { useEffect } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { selectLoggedInUser } from "./features/auth/authSlice"; +import { fetchItemsByUserIdAsync } from "./features/cart/cartSlice"; const router = createBrowserRouter([ { path: "/", @@ -60,6 +64,14 @@ const router = createBrowserRouter([ ]); function App() { + const dispatch = useDispatch(); + const user = useSelector(selectLoggedInUser); + + useEffect(() => { + if (user) { + dispatch(fetchItemsByUserIdAsync(user.id)); + } + }, [dispatch, user]); return ( <div className="App"> <RouterProvider router={router} /> diff --git a/src/features/cart/Cart.js b/src/features/cart/Cart.js index a069517..db3cb5e 100644 --- a/src/features/cart/Cart.js +++ b/src/features/cart/Cart.js @@ -1,42 +1,19 @@ import React, { useState, Fragment } from "react"; import { useSelector, useDispatch } from "react-redux"; -import {} from "./cartSlice"; +import { selectItems } from "./cartSlice"; import { Dialog, Transition } from "@headlessui/react"; import { XMarkIcon } from "@heroicons/react/24/outline"; import { Link } from "react-router-dom"; -const products = [ - { - id: 1, - name: "Throwback Hip Bag", - href: "#", - color: "Salmon", - price: "$90.00", - quantity: 1, - imageSrc: - "https://tailwindui.com/img/ecommerce-images/shopping-cart-page-04-product-01.jpg", - imageAlt: - "Salmon orange fabric pouch with match zipper, gray zipper pull, and adjustable hip belt.", - }, - { - id: 2, - name: "Medium Stuff Satchel", - href: "#", - color: "Blue", - price: "$32.00", - quantity: 1, - imageSrc: - "https://tailwindui.com/img/ecommerce-images/shopping-cart-page-04-product-02.jpg", - imageAlt: - "Front of satchel with blue canvas body, black straps and handle, drawstring top, and front zipper pouch.", - }, - // More products... -]; - export default function Cart() { const dispatch = useDispatch(); const [open, setOpen] = useState(true); - + const items = useSelector(selectItems); + const totalAmount = items.reduce( + (amount, item) => item.price * item.quantity + amount, + 0 + ); + const totalItems = items.reduce((total, item) => item.quantity + total, 0); return ( <> <div> @@ -47,12 +24,12 @@ export default function Cart() { </h1> <div className="flow-root"> <ul role="list" className="-my-6 divide-y divide-gray-200"> - {products.map((product) => ( + {items.map((product) => ( <li key={product.id} className="flex py-6"> <div className="h-24 w-24 flex-shrink-0 overflow-hidden rounded-md border border-gray-200"> <img - src={product.imageSrc} - alt={product.imageAlt} + src={product.thumbnail} + alt={product.title} className="h-full w-full object-cover object-center" /> </div> @@ -61,12 +38,12 @@ export default function Cart() { <div> <div className="flex justify-between text-base font-medium text-gray-900"> <h3> - <a href={product.href}>{product.name}</a> + <a href={product.href}>{product.title}</a> </h3> - <p className="ml-4">{product.price}</p> + <p className="ml-4">${Math.ceil(product.price)}</p> </div> <p className="mt-1 text-sm text-gray-500"> - {product.color} + {product.brand} </p> </div> <div className="flex flex-1 items-end justify-between text-sm"> @@ -100,9 +77,13 @@ export default function Cart() { </div> <div className="border-t border-gray-200 px-4 py-6 sm:px-6"> - <div className="flex justify-between text-base font-medium text-gray-900"> + <div className="flex justify-between my-2 text-base font-medium text-gray-900"> <p>Subtotal</p> - <p>$262.00</p> + <p>${Math.ceil(totalAmount)}</p> + </div> + <div className="flex justify-between my-2 text-base font-medium text-gray-900"> + <p>Total Items in Cart</p> + <p>{totalItems} Items</p> </div> <p className="mt-0.5 text-sm text-gray-500"> Shipping and taxes calculated at checkout. diff --git a/src/features/cart/cartAPI.js b/src/features/cart/cartAPI.js index 0e7c5ab..bdd0b1c 100644 --- a/src/features/cart/cartAPI.js +++ b/src/features/cart/cartAPI.js @@ -10,3 +10,11 @@ export function addToCart(item) { resolve({ data }); }); } +export function fetchItemsByUserId(userId) { + return new Promise(async (resolve) => { + // TODO: we will not hard coded server url here... + const response = await fetch("http://localhost:8080/cart?user=" + userId); + const data = await response.json(); + resolve({ data }); + }); +} diff --git a/src/features/cart/cartSlice.js b/src/features/cart/cartSlice.js index 8a027fc..df3cb0d 100644 --- a/src/features/cart/cartSlice.js +++ b/src/features/cart/cartSlice.js @@ -1,5 +1,5 @@ import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; -import { addToCart } from "./cartAPI"; +import { addToCart, fetchItemsByUserId } from "./cartAPI"; const initialState = { items: [], @@ -14,6 +14,14 @@ export const addToCartAsync = createAsyncThunk( return response.data; } ); +export const fetchItemsByUserIdAsync = createAsyncThunk( + "cart/fetchItemsByUserId", + async (userId) => { + const response = await fetchItemsByUserId(userId); + // The value we return becomes the `fulfilled` action payload + return response.data; + } +); export const cartSlice = createSlice({ name: "cart", @@ -31,6 +39,13 @@ export const cartSlice = createSlice({ .addCase(addToCartAsync.fulfilled, (state, action) => { state.status = "idle"; state.items.push(action.payload); + }) + .addCase(fetchItemsByUserIdAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(fetchItemsByUserIdAsync.fulfilled, (state, action) => { + state.status = "idle"; + state.items = action.payload; }); }, }); diff --git a/src/features/navbar/Navbar.js b/src/features/navbar/Navbar.js index 376d3ec..2f5ef13 100644 --- a/src/features/navbar/Navbar.js +++ b/src/features/navbar/Navbar.js @@ -6,6 +6,8 @@ import { XMarkIcon, } from "@heroicons/react/24/outline"; import { Link } from "react-router-dom"; +import { useSelector } from "react-redux"; +import { selectItems } from "../cart/cartSlice"; const user = { name: "Tom Cook", @@ -28,6 +30,7 @@ function classNames(...classes) { } function NavBar({ children }) { + const items = useSelector(selectItems); return ( <> <div className="min-h-full"> @@ -80,9 +83,11 @@ function NavBar({ children }) { /> </button> </Link> - <span className="inline-flex items-center rounded-md mb-7 -ml-3 bg-red-50 px-2 py-1 text-xs font-medium text-red-700 ring-1 ring-inset ring-red-600/10"> - 3 - </span> + {items.length > 0 && ( + <span className="inline-flex items-center rounded-md mb-7 -ml-3 bg-red-50 px-2 py-1 text-xs font-medium text-red-700 ring-1 ring-inset ring-red-600/10"> + {items.length} + </span> + )} {/* Profile dropdown */} <Menu as="div" className="relative ml-3"> @@ -193,9 +198,11 @@ function NavBar({ children }) { /> </button> </Link> - <span className="inline-flex items-center rounded-md bg-red-50 mb-7 -ml-3 px-2 py-1 text-xs font-medium text-red-700 ring-1 ring-inset ring-red-600/10"> - 3 - </span> + {items.length > 0 && ( + <span className="inline-flex items-center rounded-md bg-red-50 mb-7 -ml-3 px-2 py-1 text-xs font-medium text-red-700 ring-1 ring-inset ring-red-600/10"> + {items.length} + </span> + )} </div> <div className="mt-3 space-y-1 px-2"> {userNavigation.map((item) => ( From 71b78c36b200ed3ff5014be7874f9379f1deefa8 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Sat, 22 Jun 2024 13:09:49 +0530 Subject: [PATCH 23/47] add to cart/update quantity --- data.json | 151 +++++++++++++++++++++++++-------- src/features/cart/Cart.js | 28 ++++-- src/features/cart/cartAPI.js | 12 +++ src/features/cart/cartSlice.js | 20 ++++- 4 files changed, 167 insertions(+), 44 deletions(-) diff --git a/data.json b/data.json index 29ac5e2..751269d 100644 --- a/data.json +++ b/data.json @@ -9,7 +9,10 @@ "discountPercentage": 7.17, "rating": 4.94, "stock": 5, - "tags": ["beauty", "mascara"], + "tags": [ + "beauty", + "mascara" + ], "brand": "Essence", "sku": "RCH45Q1A", "weight": 2, @@ -66,7 +69,10 @@ "discountPercentage": 5.5, "rating": 3.28, "stock": 44, - "tags": ["beauty", "eyeshadow"], + "tags": [ + "beauty", + "eyeshadow" + ], "brand": "Glamour Beauty", "sku": "MVCFH27F", "weight": 3, @@ -123,7 +129,10 @@ "discountPercentage": 18.14, "rating": 3.82, "stock": 59, - "tags": ["beauty", "face powder"], + "tags": [ + "beauty", + "face powder" + ], "brand": "Velvet Touch", "sku": "9EN8WLT2", "weight": 8, @@ -180,7 +189,10 @@ "discountPercentage": 19.03, "rating": 2.51, "stock": 68, - "tags": ["beauty", "lipstick"], + "tags": [ + "beauty", + "lipstick" + ], "brand": "Chic Cosmetics", "sku": "O5IF1NTA", "weight": 5, @@ -237,7 +249,10 @@ "discountPercentage": 2.46, "rating": 3.91, "stock": 71, - "tags": ["beauty", "nail polish"], + "tags": [ + "beauty", + "nail polish" + ], "brand": "Nail Couture", "sku": "YUIIIP4W", "weight": 9, @@ -294,7 +309,10 @@ "discountPercentage": 0.32, "rating": 4.85, "stock": 17, - "tags": ["fragrances", "perfumes"], + "tags": [ + "fragrances", + "perfumes" + ], "brand": "Calvin Klein", "sku": "DZM2JQZE", "weight": 5, @@ -353,7 +371,10 @@ "discountPercentage": 18.64, "rating": 2.76, "stock": 41, - "tags": ["fragrances", "perfumes"], + "tags": [ + "fragrances", + "perfumes" + ], "brand": "Chanel", "sku": "K71HBCGS", "weight": 4, @@ -412,7 +433,10 @@ "discountPercentage": 17.44, "rating": 3.31, "stock": 91, - "tags": ["fragrances", "perfumes"], + "tags": [ + "fragrances", + "perfumes" + ], "brand": "Dior", "sku": "E70NB03B", "weight": 10, @@ -471,7 +495,10 @@ "discountPercentage": 11.47, "rating": 2.68, "stock": 3, - "tags": ["fragrances", "perfumes"], + "tags": [ + "fragrances", + "perfumes" + ], "brand": "Dolce & Gabbana", "sku": "1NBFK980", "weight": 5, @@ -530,7 +557,10 @@ "discountPercentage": 8.9, "rating": 2.69, "stock": 93, - "tags": ["fragrances", "perfumes"], + "tags": [ + "fragrances", + "perfumes" + ], "brand": "Gucci", "sku": "FFKZ6HOF", "weight": 10, @@ -589,7 +619,10 @@ "discountPercentage": 0.29, "rating": 4.14, "stock": 47, - "tags": ["furniture", "beds"], + "tags": [ + "furniture", + "beds" + ], "brand": "Annibale Colombo", "sku": "4KMDTZWF", "weight": 3, @@ -648,7 +681,10 @@ "discountPercentage": 18.54, "rating": 3.08, "stock": 16, - "tags": ["furniture", "sofas"], + "tags": [ + "furniture", + "sofas" + ], "brand": "Annibale Colombo", "sku": "LUU95CQP", "weight": 3, @@ -707,7 +743,10 @@ "discountPercentage": 9.58, "rating": 4.48, "stock": 16, - "tags": ["furniture", "bedside tables"], + "tags": [ + "furniture", + "bedside tables" + ], "brand": "Furniture Co.", "sku": "OWPLTZYX", "weight": 10, @@ -766,7 +805,10 @@ "discountPercentage": 15.23, "rating": 4.11, "stock": 47, - "tags": ["furniture", "office chairs"], + "tags": [ + "furniture", + "office chairs" + ], "brand": "Knoll", "sku": "RKHVJ4FE", "weight": 3, @@ -825,7 +867,10 @@ "discountPercentage": 11.22, "rating": 3.26, "stock": 95, - "tags": ["furniture", "bathroom"], + "tags": [ + "furniture", + "bathroom" + ], "brand": "Bath Trends", "sku": "7OLTIEVO", "weight": 6, @@ -884,7 +929,9 @@ "discountPercentage": 1.97, "rating": 2.96, "stock": 9, - "tags": ["fruits"], + "tags": [ + "fruits" + ], "sku": "QTROUV79", "weight": 8, "dimensions": { @@ -940,7 +987,9 @@ "discountPercentage": 17.99, "rating": 2.83, "stock": 96, - "tags": ["meat"], + "tags": [ + "meat" + ], "sku": "BWWA2MSO", "weight": 9, "dimensions": { @@ -996,7 +1045,10 @@ "discountPercentage": 9.57, "rating": 2.88, "stock": 13, - "tags": ["pet supplies", "cat food"], + "tags": [ + "pet supplies", + "cat food" + ], "sku": "C3F8QN6O", "weight": 9, "dimensions": { @@ -1052,7 +1104,9 @@ "discountPercentage": 10.46, "rating": 4.61, "stock": 69, - "tags": ["meat"], + "tags": [ + "meat" + ], "sku": "G5YEHW7B", "weight": 7, "dimensions": { @@ -1109,7 +1163,9 @@ "discountPercentage": 18.89, "rating": 4.01, "stock": 22, - "tags": ["cooking essentials"], + "tags": [ + "cooking essentials" + ], "sku": "Q6ZP1UY8", "weight": 8, "dimensions": { @@ -1165,7 +1221,9 @@ "discountPercentage": 11.44, "rating": 4.71, "stock": 22, - "tags": ["vegetables"], + "tags": [ + "vegetables" + ], "sku": "6KGF2K6Z", "weight": 9, "dimensions": { @@ -1221,7 +1279,10 @@ "discountPercentage": 18.15, "rating": 2.74, "stock": 40, - "tags": ["pet supplies", "dog food"], + "tags": [ + "pet supplies", + "dog food" + ], "sku": "A6QRCH37", "weight": 2, "dimensions": { @@ -1277,7 +1338,9 @@ "discountPercentage": 5.8, "rating": 4.46, "stock": 10, - "tags": ["dairy"], + "tags": [ + "dairy" + ], "sku": "YA617RI7", "weight": 4, "dimensions": { @@ -1333,7 +1396,9 @@ "discountPercentage": 7, "rating": 4.83, "stock": 99, - "tags": ["seafood"], + "tags": [ + "seafood" + ], "sku": "XNIH1MTA", "weight": 8, "dimensions": { @@ -1389,7 +1454,9 @@ "discountPercentage": 15.5, "rating": 4.28, "stock": 89, - "tags": ["vegetables"], + "tags": [ + "vegetables" + ], "sku": "HU7S7VQ0", "weight": 7, "dimensions": { @@ -1445,7 +1512,9 @@ "discountPercentage": 18.51, "rating": 4.43, "stock": 8, - "tags": ["vegetables"], + "tags": [ + "vegetables" + ], "sku": "Y4RM3JCB", "weight": 2, "dimensions": { @@ -1501,7 +1570,9 @@ "discountPercentage": 1.91, "rating": 3.5, "stock": 25, - "tags": ["condiments"], + "tags": [ + "condiments" + ], "sku": "BTBNIIOU", "weight": 9, "dimensions": { @@ -1557,7 +1628,9 @@ "discountPercentage": 7.58, "rating": 3.77, "stock": 76, - "tags": ["desserts"], + "tags": [ + "desserts" + ], "sku": "VEZMU1EQ", "weight": 5, "dimensions": { @@ -1616,7 +1689,9 @@ "discountPercentage": 5.45, "rating": 3.41, "stock": 99, - "tags": ["beverages"], + "tags": [ + "beverages" + ], "sku": "M2K19S06", "weight": 2, "dimensions": { @@ -1672,7 +1747,9 @@ "discountPercentage": 10.32, "rating": 4.37, "stock": 1, - "tags": ["fruits"], + "tags": [ + "fruits" + ], "sku": "0X3NORB9", "weight": 8, "dimensions": { @@ -1854,7 +1931,10 @@ "discountPercentage": 7.17, "rating": 4.94, "stock": 5, - "tags": ["beauty", "mascara"], + "tags": [ + "beauty", + "mascara" + ], "brand": "Essence", "sku": "RCH45Q1A", "weight": 2, @@ -1901,7 +1981,7 @@ "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", - "quantity": 1, + "quantity": 4, "user": "cb1b" }, { @@ -1913,7 +1993,10 @@ "discountPercentage": 5.5, "rating": 3.28, "stock": 44, - "tags": ["beauty", "eyeshadow"], + "tags": [ + "beauty", + "eyeshadow" + ], "brand": "Glamour Beauty", "sku": "MVCFH27F", "weight": 3, @@ -1964,4 +2047,4 @@ "user": "cb1b" } ] -} +} \ No newline at end of file diff --git a/src/features/cart/Cart.js b/src/features/cart/Cart.js index db3cb5e..88fbb1f 100644 --- a/src/features/cart/Cart.js +++ b/src/features/cart/Cart.js @@ -1,6 +1,6 @@ import React, { useState, Fragment } from "react"; import { useSelector, useDispatch } from "react-redux"; -import { selectItems } from "./cartSlice"; +import { selectItems, updateCartAsync } from "./cartSlice"; import { Dialog, Transition } from "@headlessui/react"; import { XMarkIcon } from "@heroicons/react/24/outline"; import { Link } from "react-router-dom"; @@ -14,6 +14,9 @@ export default function Cart() { 0 ); const totalItems = items.reduce((total, item) => item.quantity + total, 0); + const handleQuantity = (e, item) => { + dispatch(updateCartAsync({ ...item, quantity: +e.target.value })); + }; return ( <> <div> @@ -24,12 +27,12 @@ export default function Cart() { </h1> <div className="flow-root"> <ul role="list" className="-my-6 divide-y divide-gray-200"> - {items.map((product) => ( - <li key={product.id} className="flex py-6"> + {items.map((item) => ( + <li key={item.id} className="flex py-6"> <div className="h-24 w-24 flex-shrink-0 overflow-hidden rounded-md border border-gray-200"> <img - src={product.thumbnail} - alt={product.title} + src={item.thumbnail} + alt={item.title} className="h-full w-full object-cover object-center" /> </div> @@ -38,12 +41,12 @@ export default function Cart() { <div> <div className="flex justify-between text-base font-medium text-gray-900"> <h3> - <a href={product.href}>{product.title}</a> + <a href={item.href}>{item.title}</a> </h3> - <p className="ml-4">${Math.ceil(product.price)}</p> + <p className="ml-4">${Math.ceil(item.price)}</p> </div> <p className="mt-1 text-sm text-gray-500"> - {product.brand} + {item.brand} </p> </div> <div className="flex flex-1 items-end justify-between text-sm"> @@ -54,9 +57,16 @@ export default function Cart() { > Qty </label> - <select> + <select + onChange={(e) => { + handleQuantity(e, item); + }} + > <option value="1">1</option> <option value="2">2</option> + <option value="3">3</option> + <option value="4">4</option> + <option value="5">5</option> </select> </div> diff --git a/src/features/cart/cartAPI.js b/src/features/cart/cartAPI.js index bdd0b1c..3874864 100644 --- a/src/features/cart/cartAPI.js +++ b/src/features/cart/cartAPI.js @@ -18,3 +18,15 @@ export function fetchItemsByUserId(userId) { resolve({ data }); }); } +export function updateCart(update) { + return new Promise(async (resolve) => { + const response = await fetch("http://localhost:8080/cart/" + update.id, { + method: "PATCH", + body: JSON.stringify(update), + headers: { "content-type": "application/json" }, + }); + const data = await response.json(); + // TODO:on server it will only return some info of user (not password) + resolve({ data }); + }); +} diff --git a/src/features/cart/cartSlice.js b/src/features/cart/cartSlice.js index df3cb0d..f6c4aec 100644 --- a/src/features/cart/cartSlice.js +++ b/src/features/cart/cartSlice.js @@ -1,5 +1,5 @@ import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; -import { addToCart, fetchItemsByUserId } from "./cartAPI"; +import { addToCart, fetchItemsByUserId, updateCart } from "./cartAPI"; const initialState = { items: [], @@ -22,6 +22,14 @@ export const fetchItemsByUserIdAsync = createAsyncThunk( return response.data; } ); +export const updateCartAsync = createAsyncThunk( + "cart/updateCart", + async (item) => { + const response = await updateCart(item); + // The value we return becomes the `fulfilled` action payload + return response.data; + } +); export const cartSlice = createSlice({ name: "cart", @@ -46,6 +54,16 @@ export const cartSlice = createSlice({ .addCase(fetchItemsByUserIdAsync.fulfilled, (state, action) => { state.status = "idle"; state.items = action.payload; + }) + .addCase(updateCartAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(updateCartAsync.fulfilled, (state, action) => { + state.status = "idle"; + const index = state.items.findIndex( + (item) => item.id === action.payload.id + ); + state.items[index] = action.payload; }); }, }); From 6300124664abaa0dca9baef3e56bec2830d7a437 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Sat, 22 Jun 2024 13:36:18 +0530 Subject: [PATCH 24/47] add to cart/delete item --- data.json | 4 ++-- src/features/cart/Cart.js | 14 +++++++++++++- src/features/cart/cartAPI.js | 15 +++++++++++++++ src/features/cart/cartSlice.js | 29 ++++++++++++++++++++++++++--- 4 files changed, 56 insertions(+), 6 deletions(-) diff --git a/data.json b/data.json index 751269d..f37b0e7 100644 --- a/data.json +++ b/data.json @@ -1981,7 +1981,7 @@ "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", - "quantity": 4, + "quantity": 1, "user": "cb1b" }, { @@ -2043,7 +2043,7 @@ "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/1.png" ], "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/thumbnail.png", - "quantity": 1, + "quantity": 3, "user": "cb1b" } ] diff --git a/src/features/cart/Cart.js b/src/features/cart/Cart.js index 88fbb1f..7dab5cb 100644 --- a/src/features/cart/Cart.js +++ b/src/features/cart/Cart.js @@ -1,6 +1,10 @@ import React, { useState, Fragment } from "react"; import { useSelector, useDispatch } from "react-redux"; -import { selectItems, updateCartAsync } from "./cartSlice"; +import { + deleteItemFromCartAsync, + selectItems, + updateCartAsync, +} from "./cartSlice"; import { Dialog, Transition } from "@headlessui/react"; import { XMarkIcon } from "@heroicons/react/24/outline"; import { Link } from "react-router-dom"; @@ -17,6 +21,10 @@ export default function Cart() { const handleQuantity = (e, item) => { dispatch(updateCartAsync({ ...item, quantity: +e.target.value })); }; + + const handleRemove = (e, id) => { + dispatch(deleteItemFromCartAsync(id)); + }; return ( <> <div> @@ -61,6 +69,7 @@ export default function Cart() { onChange={(e) => { handleQuantity(e, item); }} + value={item.quantity} > <option value="1">1</option> <option value="2">2</option> @@ -72,6 +81,9 @@ export default function Cart() { <div className="flex"> <button + onClick={(e) => { + handleRemove(e, item.id); + }} type="button" className="font-medium text-indigo-600 hover:text-indigo-500" > diff --git a/src/features/cart/cartAPI.js b/src/features/cart/cartAPI.js index 3874864..c8b8ddd 100644 --- a/src/features/cart/cartAPI.js +++ b/src/features/cart/cartAPI.js @@ -1,3 +1,4 @@ +// Create export function addToCart(item) { return new Promise(async (resolve) => { const response = await fetch("http://localhost:8080/cart", { @@ -10,6 +11,7 @@ export function addToCart(item) { resolve({ data }); }); } +// Read export function fetchItemsByUserId(userId) { return new Promise(async (resolve) => { // TODO: we will not hard coded server url here... @@ -18,6 +20,7 @@ export function fetchItemsByUserId(userId) { resolve({ data }); }); } +// Update export function updateCart(update) { return new Promise(async (resolve) => { const response = await fetch("http://localhost:8080/cart/" + update.id, { @@ -30,3 +33,15 @@ export function updateCart(update) { resolve({ data }); }); } +// Delete +export function deleteItemFromCart(itemId) { + return new Promise(async (resolve) => { + const response = await fetch("http://localhost:8080/cart/" + itemId, { + method: "DELETE", + headers: { "content-type": "application/json" }, + }); + const data = await response.json(); + // TODO:on server it will only return some info of user (not password) + resolve({ data: { id: itemId } }); + }); +} diff --git a/src/features/cart/cartSlice.js b/src/features/cart/cartSlice.js index f6c4aec..a3ec12f 100644 --- a/src/features/cart/cartSlice.js +++ b/src/features/cart/cartSlice.js @@ -1,5 +1,10 @@ import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; -import { addToCart, fetchItemsByUserId, updateCart } from "./cartAPI"; +import { + addToCart, + fetchItemsByUserId, + updateCart, + deleteItemFromCart, +} from "./cartAPI"; const initialState = { items: [], @@ -24,8 +29,16 @@ export const fetchItemsByUserIdAsync = createAsyncThunk( ); export const updateCartAsync = createAsyncThunk( "cart/updateCart", - async (item) => { - const response = await updateCart(item); + async (update) => { + const response = await updateCart(update); + // The value we return becomes the `fulfilled` action payload + return response.data; + } +); +export const deleteItemFromCartAsync = createAsyncThunk( + "cart/deleteItemFromCart", + async (itemId) => { + const response = await deleteItemFromCart(itemId); // The value we return becomes the `fulfilled` action payload return response.data; } @@ -64,6 +77,16 @@ export const cartSlice = createSlice({ (item) => item.id === action.payload.id ); state.items[index] = action.payload; + }) + .addCase(deleteItemFromCartAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(deleteItemFromCartAsync.fulfilled, (state, action) => { + state.status = "idle"; + const index = state.items.findIndex( + (item) => item.id === action.payload.id + ); + state.items.splice(index, 1); }); }, }); From 6f0e5a127d7bb6fdeb809dd979f8e631cd01bfb7 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Mon, 24 Jun 2024 12:45:43 +0530 Subject: [PATCH 25/47] checkout page functionality (grabing form values) done --- data.json | 161 ++-- src/features/auth/authAPI.js | 14 + src/features/auth/authSlice.js | 17 +- src/features/auth/components/Signup.js | 6 +- src/features/cart/Cart.js | 2 + .../product/components/ProductDetail.js | 4 +- src/pages/Checkout.js | 762 +++++++++--------- 7 files changed, 494 insertions(+), 472 deletions(-) diff --git a/data.json b/data.json index f37b0e7..20a180b 100644 --- a/data.json +++ b/data.json @@ -1913,137 +1913,96 @@ { "id": "cb1b", "email": "test@123.com", - "password": "Murtaza123" + "password": "Murtaza123", + "addresses": [ + { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + }, + { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + } + ] }, { "id": "0afe", "email": "test2@gmail.com", - "password": "Tankiwala123" + "password": "Tankiwala123", + "addresses": [] } ], "cart": [ { - "id": "1", - "title": "Essence Mascara Lash Princess", - "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, - "stock": 5, - "tags": [ - "beauty", - "mascara" - ], - "brand": "Essence", - "sku": "RCH45Q1A", - "weight": 2, - "dimensions": { - "width": 23.17, - "height": 14.43, - "depth": 28.01 - }, - "warrantyInformation": "1 month warranty", - "shippingInformation": "Ships in 1 month", - "availabilityStatus": "Low Stock", - "reviews": [ - { - "rating": 2, - "comment": "Very unhappy with my purchase!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "John Doe", - "reviewerEmail": "john.doe@x.dummyjson.com" - }, - { - "rating": 2, - "comment": "Not as described!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Nolan Gonzalez", - "reviewerEmail": "nolan.gonzalez@x.dummyjson.com" - }, - { - "rating": 5, - "comment": "Very satisfied!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Scarlett Wright", - "reviewerEmail": "scarlett.wright@x.dummyjson.com" - } - ], - "returnPolicy": "30 days return policy", - "minimumOrderQuantity": 24, - "meta": { - "createdAt": "2024-05-23T08:56:21.618Z", - "updatedAt": "2024-05-23T08:56:21.618Z", - "barcode": "9164035109868", - "qrCode": "https://dummyjson.com/public/qr-code.png" - }, - "images": [ - "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", - "quantity": 1, - "user": "cb1b" - }, - { - "id": "2", - "title": "Eyeshadow Palette with Mirror", - "description": "The Eyeshadow Palette with Mirror offers a versatile range of eyeshadow shades for creating stunning eye looks. With a built-in mirror, it's convenient for on-the-go makeup application.", + "id": "6a4c", + "title": "Red Lipstick", + "description": "The Red Lipstick is a classic and bold choice for adding a pop of color to your lips. With a creamy and pigmented formula, it provides a vibrant and long-lasting finish.", "category": "beauty", - "price": 19.99, - "discountPercentage": 5.5, - "rating": 3.28, - "stock": 44, + "price": 12.99, + "discountPercentage": 19.03, + "rating": 2.51, + "stock": 68, "tags": [ "beauty", - "eyeshadow" + "lipstick" ], - "brand": "Glamour Beauty", - "sku": "MVCFH27F", - "weight": 3, + "brand": "Chic Cosmetics", + "sku": "O5IF1NTA", + "weight": 5, "dimensions": { - "width": 12.42, - "height": 8.63, - "depth": 29.13 + "width": 14.37, + "height": 13.94, + "depth": 14.6 }, - "warrantyInformation": "1 year warranty", + "warrantyInformation": "Lifetime warranty", "shippingInformation": "Ships in 2 weeks", "availabilityStatus": "In Stock", "reviews": [ { - "rating": 4, - "comment": "Very satisfied!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Liam Garcia", - "reviewerEmail": "liam.garcia@x.dummyjson.com" + "rating": 5, + "comment": "Great product!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Leo Rivera", + "reviewerEmail": "leo.rivera@x.dummyjson.com" }, { - "rating": 1, - "comment": "Very disappointed!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Nora Russell", - "reviewerEmail": "nora.russell@x.dummyjson.com" + "rating": 4, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Oscar Powers", + "reviewerEmail": "oscar.powers@x.dummyjson.com" }, { "rating": 5, - "comment": "Highly impressed!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Elena Baker", - "reviewerEmail": "elena.baker@x.dummyjson.com" + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Carter Rivera", + "reviewerEmail": "carter.rivera@x.dummyjson.com" } ], - "returnPolicy": "30 days return policy", - "minimumOrderQuantity": 32, + "returnPolicy": "90 days return policy", + "minimumOrderQuantity": 6, "meta": { - "createdAt": "2024-05-23T08:56:21.618Z", - "updatedAt": "2024-05-23T08:56:21.618Z", - "barcode": "2817839095220", + "createdAt": "2024-05-23T08:56:21.619Z", + "updatedAt": "2024-05-23T08:56:21.619Z", + "barcode": "9444582199406", "qrCode": "https://dummyjson.com/public/qr-code.png" }, "images": [ - "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/1.png" + "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/1.png" ], - "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/thumbnail.png", - "quantity": 3, + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/thumbnail.png", + "quantity": 1, "user": "cb1b" } ] diff --git a/src/features/auth/authAPI.js b/src/features/auth/authAPI.js index 4df73f6..a42ac82 100644 --- a/src/features/auth/authAPI.js +++ b/src/features/auth/authAPI.js @@ -31,3 +31,17 @@ export function checkUser(loginInfo) { // TODO:on server it will only return some info of user (not password) }); } +// update user address from checkout page.... +export function updateUser(update) { + return new Promise(async (resolve) => { + const response = await fetch("http://localhost:8080/users/" + update.id, { + // to get the particular user and address in it + method: "PATCH", + body: JSON.stringify(update), + headers: { "content-type": "application/json" }, + }); + const data = await response.json(); + // TODO:on server it will only return some info of user (not password) + resolve({ data }); + }); +} diff --git a/src/features/auth/authSlice.js b/src/features/auth/authSlice.js index 917b6cf..5162ea1 100644 --- a/src/features/auth/authSlice.js +++ b/src/features/auth/authSlice.js @@ -1,5 +1,5 @@ import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; -import { checkUser, createUser } from "./authAPI"; +import { checkUser, createUser, updateUser } from "./authAPI"; const initialState = { loggedInUser: null, @@ -15,6 +15,14 @@ export const createUserAsync = createAsyncThunk( return response.data; } ); +export const updateUserAsync = createAsyncThunk( + "user/updateUser", + async (update) => { + const response = await updateUser(update); + // The value we return becomes the `fulfilled` action payload + return response.data; + } +); export const checkUserAsync = createAsyncThunk( "user/checkUser", async (loginInfo) => { @@ -51,6 +59,13 @@ export const authSlice = createSlice({ .addCase(checkUserAsync.rejected, (state, action) => { state.status = "idle"; state.erorr = action.error; + }) + .addCase(updateUserAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(updateUserAsync.fulfilled, (state, action) => { + state.status = "idle"; + state.loggedInUser = action.payload; }); }, }); diff --git a/src/features/auth/components/Signup.js b/src/features/auth/components/Signup.js index 81fbf96..ac057c6 100644 --- a/src/features/auth/components/Signup.js +++ b/src/features/auth/components/Signup.js @@ -35,7 +35,11 @@ export default function Signup() { className="space-y-6" onSubmit={handleSubmit((data) => { dispatch( - createUserAsync({ email: data.email, password: data.password }) + createUserAsync({ + email: data.email, + password: data.password, + addresses: [], + }) ); console.log(data); })} diff --git a/src/features/cart/Cart.js b/src/features/cart/Cart.js index 7dab5cb..81d4e3c 100644 --- a/src/features/cart/Cart.js +++ b/src/features/cart/Cart.js @@ -8,6 +8,7 @@ import { import { Dialog, Transition } from "@headlessui/react"; import { XMarkIcon } from "@heroicons/react/24/outline"; import { Link } from "react-router-dom"; +import { Navigate } from "react-router-dom"; export default function Cart() { const dispatch = useDispatch(); @@ -27,6 +28,7 @@ export default function Cart() { }; return ( <> + {!items.length && <Navigate to="/" replace={true}></Navigate>} <div> <div className="mx-auto mt-12 bg-white max-w-7xl px-4 sm:px-6 lg:px-8"> <div className="border-t border-gray-200 px-4 py-6 sm:px-6"> diff --git a/src/features/product/components/ProductDetail.js b/src/features/product/components/ProductDetail.js index b6ac35b..c203fe8 100644 --- a/src/features/product/components/ProductDetail.js +++ b/src/features/product/components/ProductDetail.js @@ -45,7 +45,9 @@ export default function ProductDetail() { const handleCart = (e) => { e.preventDefault(); - dispatch(addToCartAsync({ ...product, quantity: 1, user: user.id })); + const newItem = { ...product, quantity: 1, user: user.id }; + delete newItem["id"]; // to avoid creating same id on adding same product to cart.... + dispatch(addToCartAsync(newItem)); }; useEffect(() => { diff --git a/src/pages/Checkout.js b/src/pages/Checkout.js index 4a6a7f8..bac2e82 100644 --- a/src/pages/Checkout.js +++ b/src/pages/Checkout.js @@ -1,415 +1,441 @@ -import { Link } from 'react-router-dom'; +import { Link } from "react-router-dom"; +import { useSelector, useDispatch } from "react-redux"; +import { + deleteItemFromCartAsync, + selectItems, + updateCartAsync, +} from "../features/cart/cartSlice"; +import { Navigate } from "react-router-dom"; +import { useForm } from "react-hook-form"; +import { + selectLoggedInUser, + updateUserAsync, +} from "../features/auth/authSlice"; +import { useState } from "react"; -const products = [ - { - id: 1, - name: 'Throwback Hip Bag', - href: '#', - color: 'Salmon', - price: '$90.00', - quantity: 1, - imageSrc: - 'https://tailwindui.com/img/ecommerce-images/shopping-cart-page-04-product-01.jpg', - imageAlt: - 'Salmon orange fabric pouch with match zipper, gray zipper pull, and adjustable hip belt.', - }, - { - id: 2, - name: 'Medium Stuff Satchel', - href: '#', - color: 'Blue', - price: '$32.00', - quantity: 1, - imageSrc: - 'https://tailwindui.com/img/ecommerce-images/shopping-cart-page-04-product-02.jpg', - imageAlt: - 'Front of satchel with blue canvas body, black straps and handle, drawstring top, and front zipper pouch.', - }, - // More products... -]; - -const addresses = [ - { - name: 'John wick', - street: '11th Main', - city: 'Delhi', - pinCode: 110001, - state: 'Delhi', - phone: 12312321331, - }, - { - name: 'John Doe', - street: '15th Main', - city: 'Bangalore', - pinCode: 560034, - state: 'Karnataka', - phone: 123123123, - }, -]; function Checkout() { - return ( - <div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"> - <div className="grid grid-cols-1 gap-x-8 gap-y-10 lg:grid-cols-5"> - <div className="lg:col-span-3"> - <form className="bg-white px-5 py-12 mt-12"> - <div className="space-y-12"> - <div className="border-b border-gray-900/10 pb-12"> - <h2 className="text-2xl font-semibold leading-7 text-gray-900"> - Personal Information - </h2> - <p className="mt-1 text-sm leading-6 text-gray-600"> - Use a permanent address where you can receive mail. - </p> + const dispatch = useDispatch(); + const { + register, + handleSubmit, + reset, + formState: { errors }, + } = useForm(); - <div className="mt-10 grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6"> - <div className="sm:col-span-3"> - <label - htmlFor="first-name" - className="block text-sm font-medium leading-6 text-gray-900" - > - First name - </label> - <div className="mt-2"> - <input - type="text" - name="first-name" - id="first-name" - autoComplete="given-name" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" - /> - </div> - </div> + const user = useSelector(selectLoggedInUser); + const items = useSelector(selectItems); + const totalAmount = items.reduce( + (amount, item) => item.price * item.quantity + amount, + 0 + ); + const totalItems = items.reduce((total, item) => item.quantity + total, 0); - <div className="sm:col-span-3"> - <label - htmlFor="last-name" - className="block text-sm font-medium leading-6 text-gray-900" - > - Last name - </label> - <div className="mt-2"> - <input - type="text" - name="last-name" - id="last-name" - autoComplete="family-name" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" - /> - </div> - </div> + //to pass selected address and payment method to order... + const [selectedAddress, setSelectedAddress] = useState(null); + const [paymentMethod, setpaymentMethod] = useState("cash"); - <div className="sm:col-span-4"> - <label - htmlFor="email" - className="block text-sm font-medium leading-6 text-gray-900" - > - Email address - </label> - <div className="mt-2"> - <input - id="email" - name="email" - type="email" - autoComplete="email" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" - /> - </div> - </div> + const handleQuantity = (e, item) => { + dispatch(updateCartAsync({ ...item, quantity: +e.target.value })); + }; - <div className="sm:col-span-3"> - <label - htmlFor="country" - className="block text-sm font-medium leading-6 text-gray-900" - > - Country - </label> - <div className="mt-2"> - <select - id="country" - name="country" - autoComplete="country-name" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:max-w-xs sm:text-sm sm:leading-6" - > - <option>United States</option> - <option>Canada</option> - <option>Mexico</option> - </select> - </div> - </div> + const handleRemove = (e, id) => { + dispatch(deleteItemFromCartAsync(id)); + }; + const handleAddress = (e) => { + console.log(e.target.value); + setSelectedAddress(user.addresses[e.target.value]); // user.addresses[1] + }; + const handlePayment = (e) => { + console.log(e.target.value); + setpaymentMethod(e.target.value); // cash/card + }; - <div className="col-span-full"> - <label - htmlFor="street-address" - className="block text-sm font-medium leading-6 text-gray-900" - > - Street address - </label> - <div className="mt-2"> - <input - type="text" - name="street-address" - id="street-address" - autoComplete="street-address" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" - /> - </div> - </div> + return ( + <> + {!items.length && <Navigate to="/" replace={true}></Navigate>} + <div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"> + <div className="grid grid-cols-1 gap-x-8 gap-y-10 lg:grid-cols-5"> + <div className="lg:col-span-3"> + <form + className="bg-white px-5 py-12 mt-12" + noValidate + onSubmit={handleSubmit((data) => { + console.log(data); + dispatch( + updateUserAsync({ + ...user, + addresses: [...user.addresses, data], + }) + ); + reset(); + })} + > + <div className="space-y-12"> + <div className="border-b border-gray-900/10 pb-12"> + <h2 className="text-2xl font-semibold leading-7 text-gray-900"> + Personal Information + </h2> + <p className="mt-1 text-sm leading-6 text-gray-600"> + Use a permanent address where you can receive mail. + </p> - <div className="sm:col-span-2 sm:col-start-1"> - <label - htmlFor="city" - className="block text-sm font-medium leading-6 text-gray-900" - > - City - </label> - <div className="mt-2"> - <input - type="text" - name="city" - id="city" - autoComplete="address-level2" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" - /> + <div className="mt-10 grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6"> + <div className="sm:col-span-4"> + <label + htmlFor="name" + className="block text-sm font-medium leading-6 text-gray-900" + > + Full Name + </label> + <div className="mt-2"> + <input + type="text" + {...register("name", { + required: "name is required", + })} + id="name" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + /> + </div> </div> - </div> - <div className="sm:col-span-2"> - <label - htmlFor="region" - className="block text-sm font-medium leading-6 text-gray-900" - > - State / Province - </label> - <div className="mt-2"> - <input - type="text" - name="region" - id="region" - autoComplete="address-level1" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" - /> + <div className="sm:col-span-4"> + <label + htmlFor="email" + className="block text-sm font-medium leading-6 text-gray-900" + > + Email address + </label> + <div className="mt-2"> + <input + id="email" + {...register("email", { + required: "email is required", + })} + type="email" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + /> + </div> </div> - </div> - <div className="sm:col-span-2"> - <label - htmlFor="postal-code" - className="block text-sm font-medium leading-6 text-gray-900" - > - ZIP / Postal code - </label> - <div className="mt-2"> - <input - type="text" - name="postal-code" - id="postal-code" - autoComplete="postal-code" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" - /> + <div className="sm:col-span-3"> + <label + htmlFor="Phone" + className="block text-sm font-medium leading-6 text-gray-900" + > + Phone + </label> + <div className="mt-2"> + <input + id="email" + {...register("phone", { + required: "phone is required", + })} + type="tel" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + /> + </div> </div> - </div> - </div> - </div> - - <div className="mt-6 flex items-center justify-end gap-x-6"> - <button - type="button" - className="text-sm font-semibold leading-6 text-gray-900" - > - Reset - </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" - > - Add Address - </button> - </div> - <div className="border-b border-gray-900/10 pb-12"> - <h2 className="text-base font-semibold leading-7 text-gray-900"> - Addresses - </h2> - <p className="mt-1 text-sm leading-6 text-gray-600"> - Choose from Existing addresses - </p> - <ul role="list"> - {addresses.map((address) => ( - <li - key={address.email} - className="flex justify-between gap-x-6 px-5 py-5 border-solid border-2 border-gray-200" - > - <div className="flex gap-x-4"> + <div className="col-span-full"> + <label + htmlFor="street-address" + className="block text-sm font-medium leading-6 text-gray-900" + > + Street address + </label> + <div className="mt-2"> <input - name="address" - type="radio" - className="h-4 w-4 border-gray-300 text-indigo-600 focus:ring-indigo-600" + type="text" + {...register("street", { + required: "street is required", + })} + id="street-address" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" /> - <div className="min-w-0 flex-auto"> - <p className="text-sm font-semibold leading-6 text-gray-900"> - {address.name} - </p> - <p className="mt-1 truncate text-xs leading-5 text-gray-500"> - {address.street} - </p> - <p className="mt-1 truncate text-xs leading-5 text-gray-500"> - {address.pinCode} - </p> - </div> - </div> - <div className="hidden sm:flex sm:flex-col sm:items-end"> - <p className="text-sm leading-6 text-gray-900"> - Phone: {address.phone} - </p> - <p className="text-sm leading-6 text-gray-500"> - {address.city} - </p> </div> - </li> - ))} - </ul> + </div> - <div className="mt-10 space-y-10"> - <fieldset> - <legend className="text-sm font-semibold leading-6 text-gray-900"> - Payment Methods - </legend> - <p className="mt-1 text-sm leading-6 text-gray-600"> - Choose One - </p> - <div className="mt-6 space-y-6"> - <div className="flex items-center gap-x-3"> + <div className="sm:col-span-2 sm:col-start-1"> + <label + htmlFor="city" + className="block text-sm font-medium leading-6 text-gray-900" + > + City + </label> + <div className="mt-2"> <input - id="cash" - name="payments" - type="radio" - className="h-4 w-4 border-gray-300 text-indigo-600 focus:ring-indigo-600" + type="text" + {...register("city", { + required: "city is required", + })} + id="city" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" /> - <label - htmlFor="cash" - className="block text-sm font-medium leading-6 text-gray-900" - > - Cash - </label> </div> - <div className="flex items-center gap-x-3"> + </div> + + <div className="sm:col-span-2"> + <label + htmlFor="state" + className="block text-sm font-medium leading-6 text-gray-900" + > + State / Province + </label> + <div className="mt-2"> <input - id="card" - name="payments" - type="radio" - className="h-4 w-4 border-gray-300 text-indigo-600 focus:ring-indigo-600" + type="text" + {...register("state", { + required: "state is required", + })} + id="state" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" /> - <label - htmlFor="card" - className="block text-sm font-medium leading-6 text-gray-900" - > - Card Payment - </label> </div> </div> - </fieldset> - </div> - </div> - </div> - - </form> - </div> - <div className="lg:col-span-2"> - <div className="mx-auto mt-12 bg-white max-w-7xl px-0 sm:px-0 lg:px-0"> - <div className="border-t border-gray-200 px-4 py-6 sm:px-6"> - <h1 className="text-4xl my-5 font-bold tracking-tight text-gray-900"> - Cart - </h1> - <div className="flow-root"> - <ul role="list" className="-my-6 divide-y divide-gray-200"> - {products.map((product) => ( - <li key={product.id} className="flex py-6"> - <div className="h-24 w-24 flex-shrink-0 overflow-hidden rounded-md border border-gray-200"> - <img - src={product.imageSrc} - alt={product.imageAlt} - className="h-full w-full object-cover object-center" + <div className="sm:col-span-2"> + <label + htmlFor="pinCode" + className="block text-sm font-medium leading-6 text-gray-900" + > + ZIP / Postal code + </label> + <div className="mt-2"> + <input + type="text" + {...register("pinCode", { + required: "pinCode is required", + })} + id="pinCode" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" /> </div> + </div> + </div> + </div> + + <div className="mt-6 flex items-center justify-end gap-x-6"> + <button + type="button" + className="text-sm font-semibold leading-6 text-gray-900" + > + Reset + </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" + > + Add Address + </button> + </div> - <div className="ml-4 flex flex-1 flex-col"> - <div> - <div className="flex justify-between text-base font-medium text-gray-900"> - <h3> - <a href={product.href}>{product.name}</a> - </h3> - <p className="ml-4">{product.price}</p> + <div className="border-b border-gray-900/10 pb-12"> + <h2 className="text-base font-semibold leading-7 text-gray-900"> + Addresses + </h2> + <p className="mt-1 text-sm leading-6 text-gray-600"> + Choose from Existing addresses + </p> + <ul role="list"> + {user.addresses.map((address, index) => ( + <li + key={index} + className="flex justify-between gap-x-6 px-5 py-5 border-solid border-2 border-gray-200" + > + <div className="flex gap-x-4"> + <input + name="address" + onChange={handleAddress} + value={index} + type="radio" + className="h-4 w-4 border-gray-300 text-indigo-600 focus:ring-indigo-600" + /> + <div className="min-w-0 flex-auto"> + <p className="text-sm font-semibold leading-6 text-gray-900"> + {address.name} + </p> + <p className="mt-1 truncate text-xs leading-5 text-gray-500"> + {address.street} + </p> + <p className="mt-1 truncate text-xs leading-5 text-gray-500"> + {address.pinCode} + </p> </div> - <p className="mt-1 text-sm text-gray-500"> - {product.color} + </div> + <div className="hidden sm:flex sm:flex-col sm:items-end"> + <p className="text-sm leading-6 text-gray-900"> + Phone: {address.phone} + </p> + <p className="text-sm leading-6 text-gray-500"> + {address.city} </p> </div> - <div className="flex flex-1 items-end justify-between text-sm"> - <div className="text-gray-500"> - <label - htmlFor="quantity" - className="inline mr-5 text-sm font-medium leading-6 text-gray-900" - > - Qty - </label> - <select> - <option value="1">1</option> - <option value="2">2</option> - </select> - </div> + </li> + ))} + </ul> - <div className="flex"> - <button - type="button" - className="font-medium text-indigo-600 hover:text-indigo-500" - > - Remove - </button> - </div> + <div className="mt-10 space-y-10"> + <fieldset> + <legend className="text-sm font-semibold leading-6 text-gray-900"> + Payment Methods + </legend> + <p className="mt-1 text-sm leading-6 text-gray-600"> + Choose One + </p> + <div className="mt-6 space-y-6"> + <div className="flex items-center gap-x-3"> + <input + id="cash" + name="payments" + onChange={handlePayment} + value="cash" + checked={paymentMethod === "cash"} + type="radio" + className="h-4 w-4 border-gray-300 text-indigo-600 focus:ring-indigo-600" + /> + <label + htmlFor="cash" + className="block text-sm font-medium leading-6 text-gray-900" + > + Cash + </label> + </div> + <div className="flex items-center gap-x-3"> + <input + id="card" + name="payments" + onChange={handlePayment} + value="card" + checked={paymentMethod === "card"} + type="radio" + className="h-4 w-4 border-gray-300 text-indigo-600 focus:ring-indigo-600" + /> + <label + htmlFor="card" + className="block text-sm font-medium leading-6 text-gray-900" + > + Card Payment + </label> </div> </div> - </li> - ))} - </ul> + </fieldset> + </div> + </div> </div> - </div> + </form> + </div> + <div className="lg:col-span-2"> + <div className="mx-auto mt-12 bg-white max-w-7xl px-2 sm:px-2 lg:px-2"> + <div className="border-t border-gray-200 px-0 py-6 sm:px-0"> + <h1 className="text-4xl my-5 font-bold tracking-tight text-gray-900"> + Cart + </h1> + <div className="flow-root"> + <ul role="list" className="-my-6 divide-y divide-gray-200"> + {items.map((item) => ( + <li key={item.id} className="flex py-6"> + <div className="h-24 w-24 flex-shrink-0 overflow-hidden rounded-md border border-gray-200"> + <img + src={item.thumbnail} + alt={item.title} + className="h-full w-full object-cover object-center" + /> + </div> - <div className="border-t border-gray-200 px-4 py-6 sm:px-6"> - <div className="flex justify-between text-base font-medium text-gray-900"> - <p>Subtotal</p> - <p>$262.00</p> - </div> - <p className="mt-0.5 text-sm text-gray-500"> - Shipping and taxes calculated at checkout. - </p> - <div className="mt-6"> - <Link - to="/pay" - className="flex items-center justify-center rounded-md border border-transparent bg-indigo-600 px-6 py-3 text-base font-medium text-white shadow-sm hover:bg-indigo-700" - > - Pay and Order - </Link> + <div className="ml-4 flex flex-1 flex-col"> + <div> + <div className="flex justify-between text-base font-medium text-gray-900"> + <h3> + <a href={item.href}>{item.title}</a> + </h3> + <p className="ml-4">${Math.ceil(item.price)}</p> + </div> + <p className="mt-1 text-sm text-gray-500"> + {item.brand} + </p> + </div> + <div className="flex flex-1 items-end justify-between text-sm"> + <div className="text-gray-500"> + <label + htmlFor="quantity" + className="inline mr-5 text-sm font-medium leading-6 text-gray-900" + > + Qty + </label> + <select + onChange={(e) => { + handleQuantity(e, item); + }} + value={item.quantity} + > + <option value="1">1</option> + <option value="2">2</option> + <option value="3">3</option> + <option value="4">4</option> + <option value="5">5</option> + </select> + </div> + + <div className="flex"> + <button + onClick={(e) => { + handleRemove(e, item.id); + }} + type="button" + className="font-medium text-indigo-600 hover:text-indigo-500" + > + Remove + </button> + </div> + </div> + </div> + </li> + ))} + </ul> + </div> </div> - <div className="mt-6 flex justify-center text-center text-sm text-gray-500"> - <p> - or - <Link to="/"> - <button - type="button" - className="font-medium text-indigo-600 hover:text-indigo-500" - > - Continue Shopping - <span aria-hidden="true"> →</span> - </button> - </Link> + + <div className="border-t border-gray-200 px-2 py-6 sm:px-2"> + <div className="flex justify-between my-2 text-base font-medium text-gray-900"> + <p>Subtotal</p> + <p>${Math.ceil(totalAmount)}</p> + </div> + <div className="flex justify-between my-2 text-base font-medium text-gray-900"> + <p>Total Items in Cart</p> + <p>{totalItems} Items</p> + </div> + <p className="mt-0.5 text-sm text-gray-500"> + Shipping and taxes calculated at checkout. </p> + <div className="mt-6"> + <Link + to="/checkout" + className="flex items-center justify-center rounded-md border border-transparent bg-indigo-600 px-6 py-3 text-base font-medium text-white shadow-sm hover:bg-indigo-700" + > + Checkout + </Link> + </div> + <div className="mt-6 flex justify-center text-center text-sm text-gray-500"> + <p> + or + <Link to="/"> + <button + type="button" + className="font-medium text-indigo-600 hover:text-indigo-500" + > + Continue Shopping + <span aria-hidden="true"> →</span> + </button> + </Link> + </p> + </div> </div> </div> </div> </div> </div> - </div> + </> ); } From 9071af602e434beb34468a66e160e24b6d161be3 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Mon, 24 Jun 2024 14:39:39 +0530 Subject: [PATCH 26/47] order API added --- data.json | 501 ++++++++++++++++++++++++++++++- src/app/store.js | 2 + src/features/order/Order.js | 13 + src/features/order/orderAPI.js | 12 + src/features/order/orderSlice.js | 42 +++ src/pages/Checkout.js | 25 +- 6 files changed, 589 insertions(+), 6 deletions(-) create mode 100644 src/features/order/Order.js create mode 100644 src/features/order/orderAPI.js create mode 100644 src/features/order/orderSlice.js diff --git a/data.json b/data.json index 20a180b..f8073c6 100644 --- a/data.json +++ b/data.json @@ -1939,7 +1939,17 @@ "id": "0afe", "email": "test2@gmail.com", "password": "Tankiwala123", - "addresses": [] + "addresses": [ + { + "name": "Ammar ", + "email": "ammar@example.com", + "phone": "14520566", + "street": "1102,Dhar Road", + "city": "Kolkata", + "state": "West Bengal", + "pinCode": "4520012" + } + ] } ], "cart": [ @@ -2004,6 +2014,495 @@ "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/thumbnail.png", "quantity": 1, "user": "cb1b" + }, + { + "id": "f1fc", + "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, + "discountPercentage": 18.14, + "rating": 3.82, + "stock": 59, + "tags": [ + "beauty", + "face powder" + ], + "brand": "Velvet Touch", + "sku": "9EN8WLT2", + "weight": 8, + "dimensions": { + "width": 24.16, + "height": 10.7, + "depth": 11.07 + }, + "warrantyInformation": "2 year warranty", + "shippingInformation": "Ships in 1-2 business days", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Very happy with my purchase!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Ethan Thompson", + "reviewerEmail": "ethan.thompson@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Great value for money!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Levi Hicks", + "reviewerEmail": "levi.hicks@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Highly impressed!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Hazel Gardner", + "reviewerEmail": "hazel.gardner@x.dummyjson.com" + } + ], + "returnPolicy": "60 days return policy", + "minimumOrderQuantity": 25, + "meta": { + "createdAt": "2024-05-23T08:56:21.618Z", + "updatedAt": "2024-05-23T08:56:21.618Z", + "barcode": "0516267971277", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png", + "quantity": 1, + "user": "cb1b" + } + ], + "orders": [ + { + "id": "2775", + "items": [ + { + "id": "6a4c", + "title": "Red Lipstick", + "description": "The Red Lipstick is a classic and bold choice for adding a pop of color to your lips. With a creamy and pigmented formula, it provides a vibrant and long-lasting finish.", + "category": "beauty", + "price": 12.99, + "discountPercentage": 19.03, + "rating": 2.51, + "stock": 68, + "tags": [ + "beauty", + "lipstick" + ], + "brand": "Chic Cosmetics", + "sku": "O5IF1NTA", + "weight": 5, + "dimensions": { + "width": 14.37, + "height": 13.94, + "depth": 14.6 + }, + "warrantyInformation": "Lifetime warranty", + "shippingInformation": "Ships in 2 weeks", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Great product!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Leo Rivera", + "reviewerEmail": "leo.rivera@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Oscar Powers", + "reviewerEmail": "oscar.powers@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Carter Rivera", + "reviewerEmail": "carter.rivera@x.dummyjson.com" + } + ], + "returnPolicy": "90 days return policy", + "minimumOrderQuantity": 6, + "meta": { + "createdAt": "2024-05-23T08:56:21.619Z", + "updatedAt": "2024-05-23T08:56:21.619Z", + "barcode": "9444582199406", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/thumbnail.png", + "quantity": 1, + "user": "cb1b" + }, + { + "id": "f1fc", + "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, + "discountPercentage": 18.14, + "rating": 3.82, + "stock": 59, + "tags": [ + "beauty", + "face powder" + ], + "brand": "Velvet Touch", + "sku": "9EN8WLT2", + "weight": 8, + "dimensions": { + "width": 24.16, + "height": 10.7, + "depth": 11.07 + }, + "warrantyInformation": "2 year warranty", + "shippingInformation": "Ships in 1-2 business days", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Very happy with my purchase!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Ethan Thompson", + "reviewerEmail": "ethan.thompson@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Great value for money!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Levi Hicks", + "reviewerEmail": "levi.hicks@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Highly impressed!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Hazel Gardner", + "reviewerEmail": "hazel.gardner@x.dummyjson.com" + } + ], + "returnPolicy": "60 days return policy", + "minimumOrderQuantity": 25, + "meta": { + "createdAt": "2024-05-23T08:56:21.618Z", + "updatedAt": "2024-05-23T08:56:21.618Z", + "barcode": "0516267971277", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png", + "quantity": 1, + "user": "cb1b" + } + ], + "totalAmount": 27.98, + "totalItems": 2, + "user": { + "id": "cb1b", + "email": "test@123.com", + "password": "Murtaza123", + "addresses": [ + { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + }, + { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + } + ] + }, + "selectedAddress": { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + }, + "paymentMethod": "cash" + }, + { + "id": "0b2e", + "items": [ + { + "id": "6a4c", + "title": "Red Lipstick", + "description": "The Red Lipstick is a classic and bold choice for adding a pop of color to your lips. With a creamy and pigmented formula, it provides a vibrant and long-lasting finish.", + "category": "beauty", + "price": 12.99, + "discountPercentage": 19.03, + "rating": 2.51, + "stock": 68, + "tags": [ + "beauty", + "lipstick" + ], + "brand": "Chic Cosmetics", + "sku": "O5IF1NTA", + "weight": 5, + "dimensions": { + "width": 14.37, + "height": 13.94, + "depth": 14.6 + }, + "warrantyInformation": "Lifetime warranty", + "shippingInformation": "Ships in 2 weeks", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Great product!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Leo Rivera", + "reviewerEmail": "leo.rivera@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Oscar Powers", + "reviewerEmail": "oscar.powers@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Carter Rivera", + "reviewerEmail": "carter.rivera@x.dummyjson.com" + } + ], + "returnPolicy": "90 days return policy", + "minimumOrderQuantity": 6, + "meta": { + "createdAt": "2024-05-23T08:56:21.619Z", + "updatedAt": "2024-05-23T08:56:21.619Z", + "barcode": "9444582199406", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/thumbnail.png", + "quantity": 1, + "user": "cb1b" + }, + { + "id": "f1fc", + "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, + "discountPercentage": 18.14, + "rating": 3.82, + "stock": 59, + "tags": [ + "beauty", + "face powder" + ], + "brand": "Velvet Touch", + "sku": "9EN8WLT2", + "weight": 8, + "dimensions": { + "width": 24.16, + "height": 10.7, + "depth": 11.07 + }, + "warrantyInformation": "2 year warranty", + "shippingInformation": "Ships in 1-2 business days", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Very happy with my purchase!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Ethan Thompson", + "reviewerEmail": "ethan.thompson@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Great value for money!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Levi Hicks", + "reviewerEmail": "levi.hicks@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Highly impressed!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Hazel Gardner", + "reviewerEmail": "hazel.gardner@x.dummyjson.com" + } + ], + "returnPolicy": "60 days return policy", + "minimumOrderQuantity": 25, + "meta": { + "createdAt": "2024-05-23T08:56:21.618Z", + "updatedAt": "2024-05-23T08:56:21.618Z", + "barcode": "0516267971277", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png", + "quantity": 1, + "user": "cb1b" + } + ], + "totalAmount": 27.98, + "totalItems": 2, + "user": { + "id": "cb1b", + "email": "test@123.com", + "password": "Murtaza123", + "addresses": [ + { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + }, + { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + } + ] + }, + "selectedAddress": { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + }, + "paymentMethod": "cash" + }, + { + "id": "e11a", + "items": [ + { + "id": "4da1", + "title": "Cucumber", + "description": "Crisp and hydrating cucumbers, ideal for salads, snacks, or as a refreshing side.", + "category": "groceries", + "price": 1.49, + "discountPercentage": 11.44, + "rating": 4.71, + "stock": 22, + "tags": [ + "vegetables" + ], + "sku": "6KGF2K6Z", + "weight": 9, + "dimensions": { + "width": 11.04, + "height": 20.5, + "depth": 8.18 + }, + "warrantyInformation": "5 year warranty", + "shippingInformation": "Ships overnight", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 4, + "comment": "Very satisfied!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Elijah Hill", + "reviewerEmail": "elijah.hill@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Fast shipping!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Liam Garcia", + "reviewerEmail": "liam.garcia@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Excellent quality!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Ella Cook", + "reviewerEmail": "ella.cook@x.dummyjson.com" + } + ], + "returnPolicy": "30 days return policy", + "minimumOrderQuantity": 7, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "2597004869708", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/groceries/Cucumber/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Cucumber/thumbnail.png", + "quantity": 2, + "user": "0afe" + } + ], + "totalAmount": 2.98, + "totalItems": 2, + "user": { + "id": "0afe", + "email": "test2@gmail.com", + "password": "Tankiwala123", + "addresses": [ + { + "name": "Ammar ", + "email": "ammar@example.com", + "phone": "14520566", + "street": "1102,Dhar Road", + "city": "Kolkata", + "state": "West Bengal", + "pinCode": "4520012" + } + ] + }, + "selectedAddress": { + "name": "Ammar ", + "email": "ammar@example.com", + "phone": "14520566", + "street": "1102,Dhar Road", + "city": "Kolkata", + "state": "West Bengal", + "pinCode": "4520012" + }, + "paymentMethod": "card" } ] } \ No newline at end of file diff --git a/src/app/store.js b/src/app/store.js index e9ec11b..5619441 100644 --- a/src/app/store.js +++ b/src/app/store.js @@ -2,11 +2,13 @@ import { configureStore } from "@reduxjs/toolkit"; import productReducer from "../features/product/productSlice"; import authReducer from "../features/auth/authSlice"; import cartReducer from "../features/cart/cartSlice"; +import orderReducer from "../features/order/orderSlice"; export const store = configureStore({ reducer: { product: productReducer, auth: authReducer, cart: cartReducer, + order: orderReducer, }, }); diff --git a/src/features/order/Order.js b/src/features/order/Order.js new file mode 100644 index 0000000..6559b60 --- /dev/null +++ b/src/features/order/Order.js @@ -0,0 +1,13 @@ +import React, { useState } from "react"; +import { useSelector, useDispatch } from "react-redux"; +import { increment, incrementAsync } from "./orderSlice"; + +export default function Order() { + const dispatch = useDispatch(); + + return ( + <div> + <div></div> + </div> + ); +} diff --git a/src/features/order/orderAPI.js b/src/features/order/orderAPI.js new file mode 100644 index 0000000..a1d4d99 --- /dev/null +++ b/src/features/order/orderAPI.js @@ -0,0 +1,12 @@ +export function createOrder(order) { + return new Promise(async (resolve) => { + const response = await fetch("http://localhost:8080/orders", { + method: "POST", + body: JSON.stringify(order), + headers: { "content-type": "application/json" }, + }); + const data = await response.json(); + // TODO:on server it will only return some info of user (not password) + resolve({ data }); + }); +} diff --git a/src/features/order/orderSlice.js b/src/features/order/orderSlice.js new file mode 100644 index 0000000..f5f7f0f --- /dev/null +++ b/src/features/order/orderSlice.js @@ -0,0 +1,42 @@ +import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; +import { createOrder } from "./orderAPI"; + +const initialState = { + orders: [], + status: "idle", +}; + +export const createOrderAsync = createAsyncThunk( + "order/createOrder", + async (order) => { + const response = await createOrder(order); + // The value we return becomes the `fulfilled` action payload + return response.data; + } +); + +export const orderSlice = createSlice({ + name: "order", + initialState, + reducers: { + increment: (state) => { + state.value += 1; + }, + }, + extraReducers: (builder) => { + builder + .addCase(createOrderAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(createOrderAsync.fulfilled, (state, action) => { + state.status = "idle"; + state.orders.push(action.payload); + }); + }, +}); + +export const { increment } = orderSlice.actions; + +// export const selectCount = (state) => state.order.orders; + +export default orderSlice.reducer; diff --git a/src/pages/Checkout.js b/src/pages/Checkout.js index bac2e82..0790fba 100644 --- a/src/pages/Checkout.js +++ b/src/pages/Checkout.js @@ -11,6 +11,7 @@ import { selectLoggedInUser, updateUserAsync, } from "../features/auth/authSlice"; +import { createOrderAsync } from "../features/order/orderSlice"; import { useState } from "react"; function Checkout() { @@ -49,6 +50,20 @@ function Checkout() { console.log(e.target.value); setpaymentMethod(e.target.value); // cash/card }; + const handleOrder = (e) => { + const order = { + items, + totalAmount, + totalItems, + user, + selectedAddress, + paymentMethod, + }; + dispatch(createOrderAsync(order)); + // TODO: redirect to order success page + // TODO: clear cart after order + // TODO: on server change the stock number of items + }; return ( <> @@ -409,12 +424,12 @@ function Checkout() { Shipping and taxes calculated at checkout. </p> <div className="mt-6"> - <Link - to="/checkout" - className="flex items-center justify-center rounded-md border border-transparent bg-indigo-600 px-6 py-3 text-base font-medium text-white shadow-sm hover:bg-indigo-700" + <div + onClick={handleOrder} + className="flex items-center cursor-pointer justify-center rounded-md border border-transparent bg-indigo-600 px-6 py-3 text-base font-medium text-white shadow-sm hover:bg-indigo-700" > - Checkout - </Link> + Order Now + </div> </div> <div className="mt-6 flex justify-center text-center text-sm text-gray-500"> <p> From 9b18c857d4a844cebe069002ef15bc36a86dd52b Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Tue, 25 Jun 2024 10:40:51 +0530 Subject: [PATCH 27/47] checkout validation/404page/OrderSuccessPage --- src/App.js | 12 +++++++++++- src/pages/404.js | 27 +++++++++++++++++++++++++++ src/pages/Checkout.js | 24 ++++++++++++++---------- src/pages/OrderSuccessPage.js | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 11 deletions(-) create mode 100644 src/pages/404.js create mode 100644 src/pages/OrderSuccessPage.js diff --git a/src/App.js b/src/App.js index 4af23d5..d81dfdc 100644 --- a/src/App.js +++ b/src/App.js @@ -10,7 +10,7 @@ import { Route, Link, } from "react-router-dom"; -import Cart from "./features/cart/Cart"; + import CartPage from "./pages/CartPage"; import Checkout from "./pages/Checkout"; import ProductDetailPage from "./pages/ProductDetailPage"; @@ -19,6 +19,8 @@ import { useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; import { selectLoggedInUser } from "./features/auth/authSlice"; import { fetchItemsByUserIdAsync } from "./features/cart/cartSlice"; +import PageNotFound from "./pages/404"; +import OrderSuccessPage from "./pages/OrderSuccessPage"; const router = createBrowserRouter([ { path: "/", @@ -61,6 +63,14 @@ const router = createBrowserRouter([ </Protected> ), }, + { + path: "/order-success", + element: <OrderSuccessPage></OrderSuccessPage>, + }, + { + path: "*", + element: <PageNotFound></PageNotFound>, + }, ]); function App() { diff --git a/src/pages/404.js b/src/pages/404.js new file mode 100644 index 0000000..1f67683 --- /dev/null +++ b/src/pages/404.js @@ -0,0 +1,27 @@ +import { Link } from "react-router-dom"; +function PageNotFound() { + return ( + <main className="grid min-h-full place-items-center bg-white px-6 py-24 sm:py-32 lg:px-8"> + <div className="text-center"> + <p className="text-base font-semibold text-indigo-600">404</p> + <h1 className="mt-4 text-3xl font-bold tracking-tight text-gray-900 sm:text-5xl"> + Page not found + </h1> + <p className="mt-6 text-base leading-7 text-gray-600"> + Sorry, we couldn’t find the page you’re looking for. + </p> + <div className="mt-10 flex items-center justify-center gap-x-6"> + <Link + to="/" + href="#" + className="rounded-md bg-indigo-600 px-3.5 py-2.5 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" + > + Go back home + </Link> + </div> + </div> + </main> + ); +} + +export default PageNotFound; diff --git a/src/pages/Checkout.js b/src/pages/Checkout.js index 0790fba..442caed 100644 --- a/src/pages/Checkout.js +++ b/src/pages/Checkout.js @@ -51,16 +51,20 @@ function Checkout() { setpaymentMethod(e.target.value); // cash/card }; const handleOrder = (e) => { - const order = { - items, - totalAmount, - totalItems, - user, - selectedAddress, - paymentMethod, - }; - dispatch(createOrderAsync(order)); - // TODO: redirect to order success page + if (selectedAddress && paymentMethod) { + const order = { + items, + totalAmount, + totalItems, + user, + selectedAddress, + paymentMethod, + }; + dispatch(createOrderAsync(order)); + // TODO: redirect to order success page + } else { + alert("Enter Address and Payment Method"); + } // TODO: clear cart after order // TODO: on server change the stock number of items }; diff --git a/src/pages/OrderSuccessPage.js b/src/pages/OrderSuccessPage.js new file mode 100644 index 0000000..ce04057 --- /dev/null +++ b/src/pages/OrderSuccessPage.js @@ -0,0 +1,33 @@ +import { Link } from "react-router-dom"; +import { Navigate } from "react-router-dom"; +function OrderSuccessPage({ order }) { + return ( + <> + {!order && <Navigate to="/" replace={true}></Navigate>} + <main className="grid min-h-full place-items-center bg-white px-6 py-24 sm:py-32 lg:px-8"> + <div className="text-center"> + <p className="text-base font-semibold text-indigo-600"> + Order Successfully Placed + </p> + <h1 className="mt-4 text-3xl font-bold tracking-tight text-gray-900 sm:text-5xl"> + Order Number #{order?.id} + </h1> + <p className="mt-6 text-base leading-7 text-gray-600"> + You can check your order in My Account > My Orders + </p> + <div className="mt-10 flex items-center justify-center gap-x-6"> + <Link + to="/" + href="#" + className="rounded-md bg-indigo-600 px-3.5 py-2.5 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" + > + Go back home + </Link> + </div> + </div> + </main> + </> + ); +} + +export default OrderSuccessPage; From 266b7d71385cf989f482bac3e9ab6c4b68892e1b Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Tue, 25 Jun 2024 11:05:50 +0530 Subject: [PATCH 28/47] successfully navigate to orderSuccessPage after clicking on order now --- data.json | 665 +++++++++++++++++++++++++++++++ src/App.js | 2 +- src/features/order/orderSlice.js | 4 +- src/pages/Checkout.js | 13 +- src/pages/OrderSuccessPage.js | 13 +- 5 files changed, 688 insertions(+), 9 deletions(-) diff --git a/data.json b/data.json index f8073c6..8343330 100644 --- a/data.json +++ b/data.json @@ -2503,6 +2503,671 @@ "pinCode": "4520012" }, "paymentMethod": "card" + }, + { + "id": "d637", + "items": [ + { + "id": "6a4c", + "title": "Red Lipstick", + "description": "The Red Lipstick is a classic and bold choice for adding a pop of color to your lips. With a creamy and pigmented formula, it provides a vibrant and long-lasting finish.", + "category": "beauty", + "price": 12.99, + "discountPercentage": 19.03, + "rating": 2.51, + "stock": 68, + "tags": [ + "beauty", + "lipstick" + ], + "brand": "Chic Cosmetics", + "sku": "O5IF1NTA", + "weight": 5, + "dimensions": { + "width": 14.37, + "height": 13.94, + "depth": 14.6 + }, + "warrantyInformation": "Lifetime warranty", + "shippingInformation": "Ships in 2 weeks", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Great product!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Leo Rivera", + "reviewerEmail": "leo.rivera@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Oscar Powers", + "reviewerEmail": "oscar.powers@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Carter Rivera", + "reviewerEmail": "carter.rivera@x.dummyjson.com" + } + ], + "returnPolicy": "90 days return policy", + "minimumOrderQuantity": 6, + "meta": { + "createdAt": "2024-05-23T08:56:21.619Z", + "updatedAt": "2024-05-23T08:56:21.619Z", + "barcode": "9444582199406", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/thumbnail.png", + "quantity": 1, + "user": "cb1b" + }, + { + "id": "f1fc", + "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, + "discountPercentage": 18.14, + "rating": 3.82, + "stock": 59, + "tags": [ + "beauty", + "face powder" + ], + "brand": "Velvet Touch", + "sku": "9EN8WLT2", + "weight": 8, + "dimensions": { + "width": 24.16, + "height": 10.7, + "depth": 11.07 + }, + "warrantyInformation": "2 year warranty", + "shippingInformation": "Ships in 1-2 business days", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Very happy with my purchase!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Ethan Thompson", + "reviewerEmail": "ethan.thompson@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Great value for money!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Levi Hicks", + "reviewerEmail": "levi.hicks@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Highly impressed!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Hazel Gardner", + "reviewerEmail": "hazel.gardner@x.dummyjson.com" + } + ], + "returnPolicy": "60 days return policy", + "minimumOrderQuantity": 25, + "meta": { + "createdAt": "2024-05-23T08:56:21.618Z", + "updatedAt": "2024-05-23T08:56:21.618Z", + "barcode": "0516267971277", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png", + "quantity": 1, + "user": "cb1b" + } + ], + "totalAmount": 27.98, + "totalItems": 2, + "user": { + "id": "cb1b", + "email": "test@123.com", + "password": "Murtaza123", + "addresses": [ + { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + }, + { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + } + ] + }, + "selectedAddress": { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + }, + "paymentMethod": "card" + }, + { + "id": "c7de", + "items": [ + { + "id": "6a4c", + "title": "Red Lipstick", + "description": "The Red Lipstick is a classic and bold choice for adding a pop of color to your lips. With a creamy and pigmented formula, it provides a vibrant and long-lasting finish.", + "category": "beauty", + "price": 12.99, + "discountPercentage": 19.03, + "rating": 2.51, + "stock": 68, + "tags": [ + "beauty", + "lipstick" + ], + "brand": "Chic Cosmetics", + "sku": "O5IF1NTA", + "weight": 5, + "dimensions": { + "width": 14.37, + "height": 13.94, + "depth": 14.6 + }, + "warrantyInformation": "Lifetime warranty", + "shippingInformation": "Ships in 2 weeks", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Great product!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Leo Rivera", + "reviewerEmail": "leo.rivera@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Oscar Powers", + "reviewerEmail": "oscar.powers@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Carter Rivera", + "reviewerEmail": "carter.rivera@x.dummyjson.com" + } + ], + "returnPolicy": "90 days return policy", + "minimumOrderQuantity": 6, + "meta": { + "createdAt": "2024-05-23T08:56:21.619Z", + "updatedAt": "2024-05-23T08:56:21.619Z", + "barcode": "9444582199406", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/thumbnail.png", + "quantity": 1, + "user": "cb1b" + }, + { + "id": "f1fc", + "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, + "discountPercentage": 18.14, + "rating": 3.82, + "stock": 59, + "tags": [ + "beauty", + "face powder" + ], + "brand": "Velvet Touch", + "sku": "9EN8WLT2", + "weight": 8, + "dimensions": { + "width": 24.16, + "height": 10.7, + "depth": 11.07 + }, + "warrantyInformation": "2 year warranty", + "shippingInformation": "Ships in 1-2 business days", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Very happy with my purchase!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Ethan Thompson", + "reviewerEmail": "ethan.thompson@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Great value for money!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Levi Hicks", + "reviewerEmail": "levi.hicks@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Highly impressed!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Hazel Gardner", + "reviewerEmail": "hazel.gardner@x.dummyjson.com" + } + ], + "returnPolicy": "60 days return policy", + "minimumOrderQuantity": 25, + "meta": { + "createdAt": "2024-05-23T08:56:21.618Z", + "updatedAt": "2024-05-23T08:56:21.618Z", + "barcode": "0516267971277", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png", + "quantity": 1, + "user": "cb1b" + } + ], + "totalAmount": 27.98, + "totalItems": 2, + "user": { + "id": "cb1b", + "email": "test@123.com", + "password": "Murtaza123", + "addresses": [ + { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + }, + { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + } + ] + }, + "selectedAddress": { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + }, + "paymentMethod": "card" + }, + { + "id": "dac7", + "items": [ + { + "id": "6a4c", + "title": "Red Lipstick", + "description": "The Red Lipstick is a classic and bold choice for adding a pop of color to your lips. With a creamy and pigmented formula, it provides a vibrant and long-lasting finish.", + "category": "beauty", + "price": 12.99, + "discountPercentage": 19.03, + "rating": 2.51, + "stock": 68, + "tags": [ + "beauty", + "lipstick" + ], + "brand": "Chic Cosmetics", + "sku": "O5IF1NTA", + "weight": 5, + "dimensions": { + "width": 14.37, + "height": 13.94, + "depth": 14.6 + }, + "warrantyInformation": "Lifetime warranty", + "shippingInformation": "Ships in 2 weeks", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Great product!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Leo Rivera", + "reviewerEmail": "leo.rivera@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Oscar Powers", + "reviewerEmail": "oscar.powers@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Carter Rivera", + "reviewerEmail": "carter.rivera@x.dummyjson.com" + } + ], + "returnPolicy": "90 days return policy", + "minimumOrderQuantity": 6, + "meta": { + "createdAt": "2024-05-23T08:56:21.619Z", + "updatedAt": "2024-05-23T08:56:21.619Z", + "barcode": "9444582199406", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/thumbnail.png", + "quantity": 1, + "user": "cb1b" + }, + { + "id": "f1fc", + "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, + "discountPercentage": 18.14, + "rating": 3.82, + "stock": 59, + "tags": [ + "beauty", + "face powder" + ], + "brand": "Velvet Touch", + "sku": "9EN8WLT2", + "weight": 8, + "dimensions": { + "width": 24.16, + "height": 10.7, + "depth": 11.07 + }, + "warrantyInformation": "2 year warranty", + "shippingInformation": "Ships in 1-2 business days", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Very happy with my purchase!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Ethan Thompson", + "reviewerEmail": "ethan.thompson@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Great value for money!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Levi Hicks", + "reviewerEmail": "levi.hicks@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Highly impressed!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Hazel Gardner", + "reviewerEmail": "hazel.gardner@x.dummyjson.com" + } + ], + "returnPolicy": "60 days return policy", + "minimumOrderQuantity": 25, + "meta": { + "createdAt": "2024-05-23T08:56:21.618Z", + "updatedAt": "2024-05-23T08:56:21.618Z", + "barcode": "0516267971277", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png", + "quantity": 1, + "user": "cb1b" + } + ], + "totalAmount": 27.98, + "totalItems": 2, + "user": { + "id": "cb1b", + "email": "test@123.com", + "password": "Murtaza123", + "addresses": [ + { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + }, + { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + } + ] + }, + "selectedAddress": { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + }, + "paymentMethod": "card" + }, + { + "id": "41dd", + "items": [ + { + "id": "6a4c", + "title": "Red Lipstick", + "description": "The Red Lipstick is a classic and bold choice for adding a pop of color to your lips. With a creamy and pigmented formula, it provides a vibrant and long-lasting finish.", + "category": "beauty", + "price": 12.99, + "discountPercentage": 19.03, + "rating": 2.51, + "stock": 68, + "tags": [ + "beauty", + "lipstick" + ], + "brand": "Chic Cosmetics", + "sku": "O5IF1NTA", + "weight": 5, + "dimensions": { + "width": 14.37, + "height": 13.94, + "depth": 14.6 + }, + "warrantyInformation": "Lifetime warranty", + "shippingInformation": "Ships in 2 weeks", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Great product!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Leo Rivera", + "reviewerEmail": "leo.rivera@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Oscar Powers", + "reviewerEmail": "oscar.powers@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Carter Rivera", + "reviewerEmail": "carter.rivera@x.dummyjson.com" + } + ], + "returnPolicy": "90 days return policy", + "minimumOrderQuantity": 6, + "meta": { + "createdAt": "2024-05-23T08:56:21.619Z", + "updatedAt": "2024-05-23T08:56:21.619Z", + "barcode": "9444582199406", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/thumbnail.png", + "quantity": 1, + "user": "cb1b" + }, + { + "id": "f1fc", + "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, + "discountPercentage": 18.14, + "rating": 3.82, + "stock": 59, + "tags": [ + "beauty", + "face powder" + ], + "brand": "Velvet Touch", + "sku": "9EN8WLT2", + "weight": 8, + "dimensions": { + "width": 24.16, + "height": 10.7, + "depth": 11.07 + }, + "warrantyInformation": "2 year warranty", + "shippingInformation": "Ships in 1-2 business days", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Very happy with my purchase!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Ethan Thompson", + "reviewerEmail": "ethan.thompson@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Great value for money!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Levi Hicks", + "reviewerEmail": "levi.hicks@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Highly impressed!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Hazel Gardner", + "reviewerEmail": "hazel.gardner@x.dummyjson.com" + } + ], + "returnPolicy": "60 days return policy", + "minimumOrderQuantity": 25, + "meta": { + "createdAt": "2024-05-23T08:56:21.618Z", + "updatedAt": "2024-05-23T08:56:21.618Z", + "barcode": "0516267971277", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png", + "quantity": 1, + "user": "cb1b" + } + ], + "totalAmount": 27.98, + "totalItems": 2, + "user": { + "id": "cb1b", + "email": "test@123.com", + "password": "Murtaza123", + "addresses": [ + { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + }, + { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + } + ] + }, + "selectedAddress": { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + }, + "paymentMethod": "card", + "status": "pending" } ] } \ No newline at end of file diff --git a/src/App.js b/src/App.js index d81dfdc..d5c6ac4 100644 --- a/src/App.js +++ b/src/App.js @@ -64,7 +64,7 @@ const router = createBrowserRouter([ ), }, { - path: "/order-success", + path: "/order-success/:id", element: <OrderSuccessPage></OrderSuccessPage>, }, { diff --git a/src/features/order/orderSlice.js b/src/features/order/orderSlice.js index f5f7f0f..19fda16 100644 --- a/src/features/order/orderSlice.js +++ b/src/features/order/orderSlice.js @@ -4,6 +4,7 @@ import { createOrder } from "./orderAPI"; const initialState = { orders: [], status: "idle", + currentOrder: null, }; export const createOrderAsync = createAsyncThunk( @@ -31,12 +32,13 @@ export const orderSlice = createSlice({ .addCase(createOrderAsync.fulfilled, (state, action) => { state.status = "idle"; state.orders.push(action.payload); + state.currentOrder = action.payload; }); }, }); export const { increment } = orderSlice.actions; -// export const selectCount = (state) => state.order.orders; +export const selectCurrentOrder = (state) => state.order.currentOrder; export default orderSlice.reducer; diff --git a/src/pages/Checkout.js b/src/pages/Checkout.js index 442caed..5a99969 100644 --- a/src/pages/Checkout.js +++ b/src/pages/Checkout.js @@ -11,7 +11,10 @@ import { selectLoggedInUser, updateUserAsync, } from "../features/auth/authSlice"; -import { createOrderAsync } from "../features/order/orderSlice"; +import { + createOrderAsync, + selectCurrentOrder, +} from "../features/order/orderSlice"; import { useState } from "react"; function Checkout() { @@ -25,6 +28,7 @@ function Checkout() { const user = useSelector(selectLoggedInUser); const items = useSelector(selectItems); + const currentOrder = useSelector(selectCurrentOrder); const totalAmount = items.reduce( (amount, item) => item.price * item.quantity + amount, 0 @@ -59,6 +63,7 @@ function Checkout() { user, selectedAddress, paymentMethod, + status: "pending", //other status can be delivered,recieved. }; dispatch(createOrderAsync(order)); // TODO: redirect to order success page @@ -72,6 +77,12 @@ function Checkout() { return ( <> {!items.length && <Navigate to="/" replace={true}></Navigate>} + {currentOrder && ( + <Navigate + to={`/order-success/${currentOrder.id}`} + replace={true} + ></Navigate> + )} <div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"> <div className="grid grid-cols-1 gap-x-8 gap-y-10 lg:grid-cols-5"> <div className="lg:col-span-3"> diff --git a/src/pages/OrderSuccessPage.js b/src/pages/OrderSuccessPage.js index ce04057..f22f98f 100644 --- a/src/pages/OrderSuccessPage.js +++ b/src/pages/OrderSuccessPage.js @@ -1,19 +1,20 @@ -import { Link } from "react-router-dom"; -import { Navigate } from "react-router-dom"; -function OrderSuccessPage({ order }) { +import { Link, useParams, Navigate } from "react-router-dom"; + +function OrderSuccessPage() { + const params = useParams(); return ( <> - {!order && <Navigate to="/" replace={true}></Navigate>} + {!params.id && <Navigate to="/" replace={true}></Navigate>} <main className="grid min-h-full place-items-center bg-white px-6 py-24 sm:py-32 lg:px-8"> <div className="text-center"> <p className="text-base font-semibold text-indigo-600"> Order Successfully Placed </p> <h1 className="mt-4 text-3xl font-bold tracking-tight text-gray-900 sm:text-5xl"> - Order Number #{order?.id} + Order Number #{params?.id} </h1> <p className="mt-6 text-base leading-7 text-gray-600"> - You can check your order in My Account > My Orders + You can check your order in My Account / My Orders </p> <div className="mt-10 flex items-center justify-center gap-x-6"> <Link From d89caed7f0c8be08bd7fe90982a1637002db6170 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Tue, 25 Jun 2024 11:40:46 +0530 Subject: [PATCH 29/47] empty card and currentOrder from the UI and API --- data.json | 1004 ++---------------------------- src/features/cart/cartAPI.js | 12 + src/features/cart/cartSlice.js | 16 + src/features/order/orderSlice.js | 6 +- src/pages/OrderSuccessPage.js | 15 +- 5 files changed, 104 insertions(+), 949 deletions(-) diff --git a/data.json b/data.json index 8343330..4dfb76d 100644 --- a/data.json +++ b/data.json @@ -1952,726 +1952,10 @@ ] } ], - "cart": [ - { - "id": "6a4c", - "title": "Red Lipstick", - "description": "The Red Lipstick is a classic and bold choice for adding a pop of color to your lips. With a creamy and pigmented formula, it provides a vibrant and long-lasting finish.", - "category": "beauty", - "price": 12.99, - "discountPercentage": 19.03, - "rating": 2.51, - "stock": 68, - "tags": [ - "beauty", - "lipstick" - ], - "brand": "Chic Cosmetics", - "sku": "O5IF1NTA", - "weight": 5, - "dimensions": { - "width": 14.37, - "height": 13.94, - "depth": 14.6 - }, - "warrantyInformation": "Lifetime warranty", - "shippingInformation": "Ships in 2 weeks", - "availabilityStatus": "In Stock", - "reviews": [ - { - "rating": 5, - "comment": "Great product!", - "date": "2024-05-23T08:56:21.619Z", - "reviewerName": "Leo Rivera", - "reviewerEmail": "leo.rivera@x.dummyjson.com" - }, - { - "rating": 4, - "comment": "Very pleased!", - "date": "2024-05-23T08:56:21.619Z", - "reviewerName": "Oscar Powers", - "reviewerEmail": "oscar.powers@x.dummyjson.com" - }, - { - "rating": 5, - "comment": "Very pleased!", - "date": "2024-05-23T08:56:21.619Z", - "reviewerName": "Carter Rivera", - "reviewerEmail": "carter.rivera@x.dummyjson.com" - } - ], - "returnPolicy": "90 days return policy", - "minimumOrderQuantity": 6, - "meta": { - "createdAt": "2024-05-23T08:56:21.619Z", - "updatedAt": "2024-05-23T08:56:21.619Z", - "barcode": "9444582199406", - "qrCode": "https://dummyjson.com/public/qr-code.png" - }, - "images": [ - "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/1.png" - ], - "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/thumbnail.png", - "quantity": 1, - "user": "cb1b" - }, - { - "id": "f1fc", - "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, - "discountPercentage": 18.14, - "rating": 3.82, - "stock": 59, - "tags": [ - "beauty", - "face powder" - ], - "brand": "Velvet Touch", - "sku": "9EN8WLT2", - "weight": 8, - "dimensions": { - "width": 24.16, - "height": 10.7, - "depth": 11.07 - }, - "warrantyInformation": "2 year warranty", - "shippingInformation": "Ships in 1-2 business days", - "availabilityStatus": "In Stock", - "reviews": [ - { - "rating": 5, - "comment": "Very happy with my purchase!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Ethan Thompson", - "reviewerEmail": "ethan.thompson@x.dummyjson.com" - }, - { - "rating": 4, - "comment": "Great value for money!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Levi Hicks", - "reviewerEmail": "levi.hicks@x.dummyjson.com" - }, - { - "rating": 5, - "comment": "Highly impressed!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Hazel Gardner", - "reviewerEmail": "hazel.gardner@x.dummyjson.com" - } - ], - "returnPolicy": "60 days return policy", - "minimumOrderQuantity": 25, - "meta": { - "createdAt": "2024-05-23T08:56:21.618Z", - "updatedAt": "2024-05-23T08:56:21.618Z", - "barcode": "0516267971277", - "qrCode": "https://dummyjson.com/public/qr-code.png" - }, - "images": [ - "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/1.png" - ], - "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png", - "quantity": 1, - "user": "cb1b" - } - ], - "orders": [ - { - "id": "2775", - "items": [ - { - "id": "6a4c", - "title": "Red Lipstick", - "description": "The Red Lipstick is a classic and bold choice for adding a pop of color to your lips. With a creamy and pigmented formula, it provides a vibrant and long-lasting finish.", - "category": "beauty", - "price": 12.99, - "discountPercentage": 19.03, - "rating": 2.51, - "stock": 68, - "tags": [ - "beauty", - "lipstick" - ], - "brand": "Chic Cosmetics", - "sku": "O5IF1NTA", - "weight": 5, - "dimensions": { - "width": 14.37, - "height": 13.94, - "depth": 14.6 - }, - "warrantyInformation": "Lifetime warranty", - "shippingInformation": "Ships in 2 weeks", - "availabilityStatus": "In Stock", - "reviews": [ - { - "rating": 5, - "comment": "Great product!", - "date": "2024-05-23T08:56:21.619Z", - "reviewerName": "Leo Rivera", - "reviewerEmail": "leo.rivera@x.dummyjson.com" - }, - { - "rating": 4, - "comment": "Very pleased!", - "date": "2024-05-23T08:56:21.619Z", - "reviewerName": "Oscar Powers", - "reviewerEmail": "oscar.powers@x.dummyjson.com" - }, - { - "rating": 5, - "comment": "Very pleased!", - "date": "2024-05-23T08:56:21.619Z", - "reviewerName": "Carter Rivera", - "reviewerEmail": "carter.rivera@x.dummyjson.com" - } - ], - "returnPolicy": "90 days return policy", - "minimumOrderQuantity": 6, - "meta": { - "createdAt": "2024-05-23T08:56:21.619Z", - "updatedAt": "2024-05-23T08:56:21.619Z", - "barcode": "9444582199406", - "qrCode": "https://dummyjson.com/public/qr-code.png" - }, - "images": [ - "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/1.png" - ], - "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/thumbnail.png", - "quantity": 1, - "user": "cb1b" - }, - { - "id": "f1fc", - "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, - "discountPercentage": 18.14, - "rating": 3.82, - "stock": 59, - "tags": [ - "beauty", - "face powder" - ], - "brand": "Velvet Touch", - "sku": "9EN8WLT2", - "weight": 8, - "dimensions": { - "width": 24.16, - "height": 10.7, - "depth": 11.07 - }, - "warrantyInformation": "2 year warranty", - "shippingInformation": "Ships in 1-2 business days", - "availabilityStatus": "In Stock", - "reviews": [ - { - "rating": 5, - "comment": "Very happy with my purchase!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Ethan Thompson", - "reviewerEmail": "ethan.thompson@x.dummyjson.com" - }, - { - "rating": 4, - "comment": "Great value for money!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Levi Hicks", - "reviewerEmail": "levi.hicks@x.dummyjson.com" - }, - { - "rating": 5, - "comment": "Highly impressed!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Hazel Gardner", - "reviewerEmail": "hazel.gardner@x.dummyjson.com" - } - ], - "returnPolicy": "60 days return policy", - "minimumOrderQuantity": 25, - "meta": { - "createdAt": "2024-05-23T08:56:21.618Z", - "updatedAt": "2024-05-23T08:56:21.618Z", - "barcode": "0516267971277", - "qrCode": "https://dummyjson.com/public/qr-code.png" - }, - "images": [ - "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/1.png" - ], - "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png", - "quantity": 1, - "user": "cb1b" - } - ], - "totalAmount": 27.98, - "totalItems": 2, - "user": { - "id": "cb1b", - "email": "test@123.com", - "password": "Murtaza123", - "addresses": [ - { - "name": "Murtaza Tankiwala", - "email": "murtazashabbir14@gmail.com", - "phone": "07869558609", - "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", - "city": "Indore", - "state": "Madhya Pradesh", - "pinCode": "452002" - }, - { - "name": "Murtaza Tankiwala", - "email": "murtazashabbir14@gmail.com", - "phone": "07869558609", - "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", - "city": "Indore", - "state": "Madhya Pradesh", - "pinCode": "452002" - } - ] - }, - "selectedAddress": { - "name": "Murtaza Tankiwala", - "email": "murtazashabbir14@gmail.com", - "phone": "07869558609", - "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", - "city": "Indore", - "state": "Madhya Pradesh", - "pinCode": "452002" - }, - "paymentMethod": "cash" - }, - { - "id": "0b2e", - "items": [ - { - "id": "6a4c", - "title": "Red Lipstick", - "description": "The Red Lipstick is a classic and bold choice for adding a pop of color to your lips. With a creamy and pigmented formula, it provides a vibrant and long-lasting finish.", - "category": "beauty", - "price": 12.99, - "discountPercentage": 19.03, - "rating": 2.51, - "stock": 68, - "tags": [ - "beauty", - "lipstick" - ], - "brand": "Chic Cosmetics", - "sku": "O5IF1NTA", - "weight": 5, - "dimensions": { - "width": 14.37, - "height": 13.94, - "depth": 14.6 - }, - "warrantyInformation": "Lifetime warranty", - "shippingInformation": "Ships in 2 weeks", - "availabilityStatus": "In Stock", - "reviews": [ - { - "rating": 5, - "comment": "Great product!", - "date": "2024-05-23T08:56:21.619Z", - "reviewerName": "Leo Rivera", - "reviewerEmail": "leo.rivera@x.dummyjson.com" - }, - { - "rating": 4, - "comment": "Very pleased!", - "date": "2024-05-23T08:56:21.619Z", - "reviewerName": "Oscar Powers", - "reviewerEmail": "oscar.powers@x.dummyjson.com" - }, - { - "rating": 5, - "comment": "Very pleased!", - "date": "2024-05-23T08:56:21.619Z", - "reviewerName": "Carter Rivera", - "reviewerEmail": "carter.rivera@x.dummyjson.com" - } - ], - "returnPolicy": "90 days return policy", - "minimumOrderQuantity": 6, - "meta": { - "createdAt": "2024-05-23T08:56:21.619Z", - "updatedAt": "2024-05-23T08:56:21.619Z", - "barcode": "9444582199406", - "qrCode": "https://dummyjson.com/public/qr-code.png" - }, - "images": [ - "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/1.png" - ], - "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/thumbnail.png", - "quantity": 1, - "user": "cb1b" - }, - { - "id": "f1fc", - "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, - "discountPercentage": 18.14, - "rating": 3.82, - "stock": 59, - "tags": [ - "beauty", - "face powder" - ], - "brand": "Velvet Touch", - "sku": "9EN8WLT2", - "weight": 8, - "dimensions": { - "width": 24.16, - "height": 10.7, - "depth": 11.07 - }, - "warrantyInformation": "2 year warranty", - "shippingInformation": "Ships in 1-2 business days", - "availabilityStatus": "In Stock", - "reviews": [ - { - "rating": 5, - "comment": "Very happy with my purchase!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Ethan Thompson", - "reviewerEmail": "ethan.thompson@x.dummyjson.com" - }, - { - "rating": 4, - "comment": "Great value for money!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Levi Hicks", - "reviewerEmail": "levi.hicks@x.dummyjson.com" - }, - { - "rating": 5, - "comment": "Highly impressed!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Hazel Gardner", - "reviewerEmail": "hazel.gardner@x.dummyjson.com" - } - ], - "returnPolicy": "60 days return policy", - "minimumOrderQuantity": 25, - "meta": { - "createdAt": "2024-05-23T08:56:21.618Z", - "updatedAt": "2024-05-23T08:56:21.618Z", - "barcode": "0516267971277", - "qrCode": "https://dummyjson.com/public/qr-code.png" - }, - "images": [ - "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/1.png" - ], - "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png", - "quantity": 1, - "user": "cb1b" - } - ], - "totalAmount": 27.98, - "totalItems": 2, - "user": { - "id": "cb1b", - "email": "test@123.com", - "password": "Murtaza123", - "addresses": [ - { - "name": "Murtaza Tankiwala", - "email": "murtazashabbir14@gmail.com", - "phone": "07869558609", - "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", - "city": "Indore", - "state": "Madhya Pradesh", - "pinCode": "452002" - }, - { - "name": "Murtaza Tankiwala", - "email": "murtazashabbir14@gmail.com", - "phone": "07869558609", - "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", - "city": "Indore", - "state": "Madhya Pradesh", - "pinCode": "452002" - } - ] - }, - "selectedAddress": { - "name": "Murtaza Tankiwala", - "email": "murtazashabbir14@gmail.com", - "phone": "07869558609", - "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", - "city": "Indore", - "state": "Madhya Pradesh", - "pinCode": "452002" - }, - "paymentMethod": "cash" - }, - { - "id": "e11a", - "items": [ - { - "id": "4da1", - "title": "Cucumber", - "description": "Crisp and hydrating cucumbers, ideal for salads, snacks, or as a refreshing side.", - "category": "groceries", - "price": 1.49, - "discountPercentage": 11.44, - "rating": 4.71, - "stock": 22, - "tags": [ - "vegetables" - ], - "sku": "6KGF2K6Z", - "weight": 9, - "dimensions": { - "width": 11.04, - "height": 20.5, - "depth": 8.18 - }, - "warrantyInformation": "5 year warranty", - "shippingInformation": "Ships overnight", - "availabilityStatus": "In Stock", - "reviews": [ - { - "rating": 4, - "comment": "Very satisfied!", - "date": "2024-05-23T08:56:21.620Z", - "reviewerName": "Elijah Hill", - "reviewerEmail": "elijah.hill@x.dummyjson.com" - }, - { - "rating": 5, - "comment": "Fast shipping!", - "date": "2024-05-23T08:56:21.620Z", - "reviewerName": "Liam Garcia", - "reviewerEmail": "liam.garcia@x.dummyjson.com" - }, - { - "rating": 4, - "comment": "Excellent quality!", - "date": "2024-05-23T08:56:21.620Z", - "reviewerName": "Ella Cook", - "reviewerEmail": "ella.cook@x.dummyjson.com" - } - ], - "returnPolicy": "30 days return policy", - "minimumOrderQuantity": 7, - "meta": { - "createdAt": "2024-05-23T08:56:21.620Z", - "updatedAt": "2024-05-23T08:56:21.620Z", - "barcode": "2597004869708", - "qrCode": "https://dummyjson.com/public/qr-code.png" - }, - "images": [ - "https://cdn.dummyjson.com/products/images/groceries/Cucumber/1.png" - ], - "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Cucumber/thumbnail.png", - "quantity": 2, - "user": "0afe" - } - ], - "totalAmount": 2.98, - "totalItems": 2, - "user": { - "id": "0afe", - "email": "test2@gmail.com", - "password": "Tankiwala123", - "addresses": [ - { - "name": "Ammar ", - "email": "ammar@example.com", - "phone": "14520566", - "street": "1102,Dhar Road", - "city": "Kolkata", - "state": "West Bengal", - "pinCode": "4520012" - } - ] - }, - "selectedAddress": { - "name": "Ammar ", - "email": "ammar@example.com", - "phone": "14520566", - "street": "1102,Dhar Road", - "city": "Kolkata", - "state": "West Bengal", - "pinCode": "4520012" - }, - "paymentMethod": "card" - }, - { - "id": "d637", - "items": [ - { - "id": "6a4c", - "title": "Red Lipstick", - "description": "The Red Lipstick is a classic and bold choice for adding a pop of color to your lips. With a creamy and pigmented formula, it provides a vibrant and long-lasting finish.", - "category": "beauty", - "price": 12.99, - "discountPercentage": 19.03, - "rating": 2.51, - "stock": 68, - "tags": [ - "beauty", - "lipstick" - ], - "brand": "Chic Cosmetics", - "sku": "O5IF1NTA", - "weight": 5, - "dimensions": { - "width": 14.37, - "height": 13.94, - "depth": 14.6 - }, - "warrantyInformation": "Lifetime warranty", - "shippingInformation": "Ships in 2 weeks", - "availabilityStatus": "In Stock", - "reviews": [ - { - "rating": 5, - "comment": "Great product!", - "date": "2024-05-23T08:56:21.619Z", - "reviewerName": "Leo Rivera", - "reviewerEmail": "leo.rivera@x.dummyjson.com" - }, - { - "rating": 4, - "comment": "Very pleased!", - "date": "2024-05-23T08:56:21.619Z", - "reviewerName": "Oscar Powers", - "reviewerEmail": "oscar.powers@x.dummyjson.com" - }, - { - "rating": 5, - "comment": "Very pleased!", - "date": "2024-05-23T08:56:21.619Z", - "reviewerName": "Carter Rivera", - "reviewerEmail": "carter.rivera@x.dummyjson.com" - } - ], - "returnPolicy": "90 days return policy", - "minimumOrderQuantity": 6, - "meta": { - "createdAt": "2024-05-23T08:56:21.619Z", - "updatedAt": "2024-05-23T08:56:21.619Z", - "barcode": "9444582199406", - "qrCode": "https://dummyjson.com/public/qr-code.png" - }, - "images": [ - "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/1.png" - ], - "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/thumbnail.png", - "quantity": 1, - "user": "cb1b" - }, - { - "id": "f1fc", - "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, - "discountPercentage": 18.14, - "rating": 3.82, - "stock": 59, - "tags": [ - "beauty", - "face powder" - ], - "brand": "Velvet Touch", - "sku": "9EN8WLT2", - "weight": 8, - "dimensions": { - "width": 24.16, - "height": 10.7, - "depth": 11.07 - }, - "warrantyInformation": "2 year warranty", - "shippingInformation": "Ships in 1-2 business days", - "availabilityStatus": "In Stock", - "reviews": [ - { - "rating": 5, - "comment": "Very happy with my purchase!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Ethan Thompson", - "reviewerEmail": "ethan.thompson@x.dummyjson.com" - }, - { - "rating": 4, - "comment": "Great value for money!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Levi Hicks", - "reviewerEmail": "levi.hicks@x.dummyjson.com" - }, - { - "rating": 5, - "comment": "Highly impressed!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Hazel Gardner", - "reviewerEmail": "hazel.gardner@x.dummyjson.com" - } - ], - "returnPolicy": "60 days return policy", - "minimumOrderQuantity": 25, - "meta": { - "createdAt": "2024-05-23T08:56:21.618Z", - "updatedAt": "2024-05-23T08:56:21.618Z", - "barcode": "0516267971277", - "qrCode": "https://dummyjson.com/public/qr-code.png" - }, - "images": [ - "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/1.png" - ], - "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png", - "quantity": 1, - "user": "cb1b" - } - ], - "totalAmount": 27.98, - "totalItems": 2, - "user": { - "id": "cb1b", - "email": "test@123.com", - "password": "Murtaza123", - "addresses": [ - { - "name": "Murtaza Tankiwala", - "email": "murtazashabbir14@gmail.com", - "phone": "07869558609", - "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", - "city": "Indore", - "state": "Madhya Pradesh", - "pinCode": "452002" - }, - { - "name": "Murtaza Tankiwala", - "email": "murtazashabbir14@gmail.com", - "phone": "07869558609", - "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", - "city": "Indore", - "state": "Madhya Pradesh", - "pinCode": "452002" - } - ] - }, - "selectedAddress": { - "name": "Murtaza Tankiwala", - "email": "murtazashabbir14@gmail.com", - "phone": "07869558609", - "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", - "city": "Indore", - "state": "Madhya Pradesh", - "pinCode": "452002" - }, - "paymentMethod": "card" - }, + "cart": [], + "orders": [ { - "id": "c7de", + "id": "d496", "items": [ { "id": "6a4c", @@ -2834,179 +2118,76 @@ "state": "Madhya Pradesh", "pinCode": "452002" }, - "paymentMethod": "card" + "paymentMethod": "cash", + "status": "pending" }, { - "id": "dac7", + "id": "f1e8", "items": [ { - "id": "6a4c", - "title": "Red Lipstick", - "description": "The Red Lipstick is a classic and bold choice for adding a pop of color to your lips. With a creamy and pigmented formula, it provides a vibrant and long-lasting finish.", + "id": "05fe", + "title": "Eyeshadow Palette with Mirror", + "description": "The Eyeshadow Palette with Mirror offers a versatile range of eyeshadow shades for creating stunning eye looks. With a built-in mirror, it's convenient for on-the-go makeup application.", "category": "beauty", - "price": 12.99, - "discountPercentage": 19.03, - "rating": 2.51, - "stock": 68, + "price": 19.99, + "discountPercentage": 5.5, + "rating": 3.28, + "stock": 44, "tags": [ "beauty", - "lipstick" + "eyeshadow" ], - "brand": "Chic Cosmetics", - "sku": "O5IF1NTA", - "weight": 5, + "brand": "Glamour Beauty", + "sku": "MVCFH27F", + "weight": 3, "dimensions": { - "width": 14.37, - "height": 13.94, - "depth": 14.6 + "width": 12.42, + "height": 8.63, + "depth": 29.13 }, - "warrantyInformation": "Lifetime warranty", + "warrantyInformation": "1 year warranty", "shippingInformation": "Ships in 2 weeks", "availabilityStatus": "In Stock", "reviews": [ - { - "rating": 5, - "comment": "Great product!", - "date": "2024-05-23T08:56:21.619Z", - "reviewerName": "Leo Rivera", - "reviewerEmail": "leo.rivera@x.dummyjson.com" - }, { "rating": 4, - "comment": "Very pleased!", - "date": "2024-05-23T08:56:21.619Z", - "reviewerName": "Oscar Powers", - "reviewerEmail": "oscar.powers@x.dummyjson.com" - }, - { - "rating": 5, - "comment": "Very pleased!", - "date": "2024-05-23T08:56:21.619Z", - "reviewerName": "Carter Rivera", - "reviewerEmail": "carter.rivera@x.dummyjson.com" - } - ], - "returnPolicy": "90 days return policy", - "minimumOrderQuantity": 6, - "meta": { - "createdAt": "2024-05-23T08:56:21.619Z", - "updatedAt": "2024-05-23T08:56:21.619Z", - "barcode": "9444582199406", - "qrCode": "https://dummyjson.com/public/qr-code.png" - }, - "images": [ - "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/1.png" - ], - "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/thumbnail.png", - "quantity": 1, - "user": "cb1b" - }, - { - "id": "f1fc", - "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, - "discountPercentage": 18.14, - "rating": 3.82, - "stock": 59, - "tags": [ - "beauty", - "face powder" - ], - "brand": "Velvet Touch", - "sku": "9EN8WLT2", - "weight": 8, - "dimensions": { - "width": 24.16, - "height": 10.7, - "depth": 11.07 - }, - "warrantyInformation": "2 year warranty", - "shippingInformation": "Ships in 1-2 business days", - "availabilityStatus": "In Stock", - "reviews": [ - { - "rating": 5, - "comment": "Very happy with my purchase!", + "comment": "Very satisfied!", "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Ethan Thompson", - "reviewerEmail": "ethan.thompson@x.dummyjson.com" + "reviewerName": "Liam Garcia", + "reviewerEmail": "liam.garcia@x.dummyjson.com" }, { - "rating": 4, - "comment": "Great value for money!", + "rating": 1, + "comment": "Very disappointed!", "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Levi Hicks", - "reviewerEmail": "levi.hicks@x.dummyjson.com" + "reviewerName": "Nora Russell", + "reviewerEmail": "nora.russell@x.dummyjson.com" }, { "rating": 5, "comment": "Highly impressed!", "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Hazel Gardner", - "reviewerEmail": "hazel.gardner@x.dummyjson.com" + "reviewerName": "Elena Baker", + "reviewerEmail": "elena.baker@x.dummyjson.com" } ], - "returnPolicy": "60 days return policy", - "minimumOrderQuantity": 25, + "returnPolicy": "30 days return policy", + "minimumOrderQuantity": 32, "meta": { "createdAt": "2024-05-23T08:56:21.618Z", "updatedAt": "2024-05-23T08:56:21.618Z", - "barcode": "0516267971277", + "barcode": "2817839095220", "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/Eyeshadow%20Palette%20with%20Mirror/1.png" ], - "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png", + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/thumbnail.png", "quantity": 1, - "user": "cb1b" - } - ], - "totalAmount": 27.98, - "totalItems": 2, - "user": { - "id": "cb1b", - "email": "test@123.com", - "password": "Murtaza123", - "addresses": [ - { - "name": "Murtaza Tankiwala", - "email": "murtazashabbir14@gmail.com", - "phone": "07869558609", - "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", - "city": "Indore", - "state": "Madhya Pradesh", - "pinCode": "452002" - }, - { - "name": "Murtaza Tankiwala", - "email": "murtazashabbir14@gmail.com", - "phone": "07869558609", - "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", - "city": "Indore", - "state": "Madhya Pradesh", - "pinCode": "452002" - } - ] - }, - "selectedAddress": { - "name": "Murtaza Tankiwala", - "email": "murtazashabbir14@gmail.com", - "phone": "07869558609", - "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", - "city": "Indore", - "state": "Madhya Pradesh", - "pinCode": "452002" - }, - "paymentMethod": "card" - }, - { - "id": "41dd", - "items": [ + "user": "0afe" + }, { - "id": "6a4c", + "id": "6ddf", "title": "Red Lipstick", "description": "The Red Lipstick is a classic and bold choice for adding a pop of color to your lips. With a creamy and pigmented formula, it provides a vibrant and long-lasting finish.", "category": "beauty", @@ -3065,106 +2246,35 @@ ], "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Red%20Lipstick/thumbnail.png", "quantity": 1, - "user": "cb1b" - }, - { - "id": "f1fc", - "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, - "discountPercentage": 18.14, - "rating": 3.82, - "stock": 59, - "tags": [ - "beauty", - "face powder" - ], - "brand": "Velvet Touch", - "sku": "9EN8WLT2", - "weight": 8, - "dimensions": { - "width": 24.16, - "height": 10.7, - "depth": 11.07 - }, - "warrantyInformation": "2 year warranty", - "shippingInformation": "Ships in 1-2 business days", - "availabilityStatus": "In Stock", - "reviews": [ - { - "rating": 5, - "comment": "Very happy with my purchase!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Ethan Thompson", - "reviewerEmail": "ethan.thompson@x.dummyjson.com" - }, - { - "rating": 4, - "comment": "Great value for money!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Levi Hicks", - "reviewerEmail": "levi.hicks@x.dummyjson.com" - }, - { - "rating": 5, - "comment": "Highly impressed!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Hazel Gardner", - "reviewerEmail": "hazel.gardner@x.dummyjson.com" - } - ], - "returnPolicy": "60 days return policy", - "minimumOrderQuantity": 25, - "meta": { - "createdAt": "2024-05-23T08:56:21.618Z", - "updatedAt": "2024-05-23T08:56:21.618Z", - "barcode": "0516267971277", - "qrCode": "https://dummyjson.com/public/qr-code.png" - }, - "images": [ - "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/1.png" - ], - "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png", - "quantity": 1, - "user": "cb1b" + "user": "0afe" } ], - "totalAmount": 27.98, + "totalAmount": 32.98, "totalItems": 2, "user": { - "id": "cb1b", - "email": "test@123.com", - "password": "Murtaza123", + "id": "0afe", + "email": "test2@gmail.com", + "password": "Tankiwala123", "addresses": [ { - "name": "Murtaza Tankiwala", - "email": "murtazashabbir14@gmail.com", - "phone": "07869558609", - "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", - "city": "Indore", - "state": "Madhya Pradesh", - "pinCode": "452002" - }, - { - "name": "Murtaza Tankiwala", - "email": "murtazashabbir14@gmail.com", - "phone": "07869558609", - "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", - "city": "Indore", - "state": "Madhya Pradesh", - "pinCode": "452002" + "name": "Ammar ", + "email": "ammar@example.com", + "phone": "14520566", + "street": "1102,Dhar Road", + "city": "Kolkata", + "state": "West Bengal", + "pinCode": "4520012" } ] }, "selectedAddress": { - "name": "Murtaza Tankiwala", - "email": "murtazashabbir14@gmail.com", - "phone": "07869558609", - "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", - "city": "Indore", - "state": "Madhya Pradesh", - "pinCode": "452002" + "name": "Ammar ", + "email": "ammar@example.com", + "phone": "14520566", + "street": "1102,Dhar Road", + "city": "Kolkata", + "state": "West Bengal", + "pinCode": "4520012" }, "paymentMethod": "card", "status": "pending" diff --git a/src/features/cart/cartAPI.js b/src/features/cart/cartAPI.js index c8b8ddd..720c4e0 100644 --- a/src/features/cart/cartAPI.js +++ b/src/features/cart/cartAPI.js @@ -45,3 +45,15 @@ export function deleteItemFromCart(itemId) { resolve({ data: { id: itemId } }); }); } +// Reset Cart +export function resetCart(userId) { + // get all the item of user's cart and delete rach + return new Promise(async (resolve) => { + const response = await fetchItemsByUserId(userId); + const items = response.data; + for (let item of items) { + await deleteItemFromCart(item.id); + } + resolve({ status: "success" }); + }); +} diff --git a/src/features/cart/cartSlice.js b/src/features/cart/cartSlice.js index a3ec12f..0150466 100644 --- a/src/features/cart/cartSlice.js +++ b/src/features/cart/cartSlice.js @@ -4,6 +4,7 @@ import { fetchItemsByUserId, updateCart, deleteItemFromCart, + resetCart, } from "./cartAPI"; const initialState = { @@ -43,6 +44,14 @@ export const deleteItemFromCartAsync = createAsyncThunk( return response.data; } ); +export const resetCartAsync = createAsyncThunk( + "cart/resetCart", + async (userId) => { + const response = await resetCart(userId); + // The value we return becomes the `fulfilled` action payload + return response.data; + } +); export const cartSlice = createSlice({ name: "cart", @@ -87,6 +96,13 @@ export const cartSlice = createSlice({ (item) => item.id === action.payload.id ); state.items.splice(index, 1); + }) + .addCase(resetCartAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(resetCartAsync.fulfilled, (state, action) => { + state.status = "idle"; + state.items = []; }); }, }); diff --git a/src/features/order/orderSlice.js b/src/features/order/orderSlice.js index 19fda16..6309381 100644 --- a/src/features/order/orderSlice.js +++ b/src/features/order/orderSlice.js @@ -1,5 +1,6 @@ import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; import { createOrder } from "./orderAPI"; +import { resetCart } from "../cart/cartAPI"; const initialState = { orders: [], @@ -23,6 +24,9 @@ export const orderSlice = createSlice({ increment: (state) => { state.value += 1; }, + resetOrder: (state) => { + state.currentOrder = null; + }, }, extraReducers: (builder) => { builder @@ -37,7 +41,7 @@ export const orderSlice = createSlice({ }, }); -export const { increment } = orderSlice.actions; +export const { resetOrder } = orderSlice.actions; export const selectCurrentOrder = (state) => state.order.currentOrder; diff --git a/src/pages/OrderSuccessPage.js b/src/pages/OrderSuccessPage.js index f22f98f..8b36257 100644 --- a/src/pages/OrderSuccessPage.js +++ b/src/pages/OrderSuccessPage.js @@ -1,7 +1,20 @@ +import { useEffect } from "react"; import { Link, useParams, Navigate } from "react-router-dom"; +import { useDispatch, useSelector } from "react-redux"; +import { resetCartAsync } from "../features/cart/cartSlice"; +import { selectLoggedInUser } from "../features/auth/authSlice"; +import { resetOrder } from "../features/order/orderSlice"; function OrderSuccessPage() { const params = useParams(); + const dispatch = useDispatch(); + const user = useSelector(selectLoggedInUser); + useEffect(() => { + // reset cart + dispatch(resetCartAsync(user.id)); + // reset currentOrder + dispatch(resetOrder); + }, [dispatch, user]); return ( <> {!params.id && <Navigate to="/" replace={true}></Navigate>} @@ -11,7 +24,7 @@ function OrderSuccessPage() { Order Successfully Placed </p> <h1 className="mt-4 text-3xl font-bold tracking-tight text-gray-900 sm:text-5xl"> - Order Number #{params?.id} + Order ID #{params?.id} </h1> <p className="mt-6 text-base leading-7 text-gray-600"> You can check your order in My Account / My Orders From 51072c9f220889f2124d72b16efddab0a51b6331 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Tue, 25 Jun 2024 14:18:28 +0530 Subject: [PATCH 30/47] UserOrder created successfully/ (Math.ceil removed for exact calc) --- data.json | 65 +++++++++++- src/App.js | 6 ++ src/app/store.js | 2 + src/features/cart/Cart.js | 4 +- src/features/navbar/Navbar.js | 4 +- src/features/user/UserAPI.js | 10 ++ src/features/user/components/UserOrders.js | 111 ++++++++++++++++++++ src/features/user/components/UserProfile.js | 14 +++ src/features/user/userSlice.js | 43 ++++++++ src/pages/Checkout.js | 4 +- src/pages/UserOrderPage.js | 14 +++ 11 files changed, 270 insertions(+), 7 deletions(-) create mode 100644 src/features/user/UserAPI.js create mode 100644 src/features/user/components/UserOrders.js create mode 100644 src/features/user/components/UserProfile.js create mode 100644 src/features/user/userSlice.js create mode 100644 src/pages/UserOrderPage.js diff --git a/data.json b/data.json index 4dfb76d..5038df9 100644 --- a/data.json +++ b/data.json @@ -1952,7 +1952,70 @@ ] } ], - "cart": [], + "cart": [ + { + "id": "efca", + "title": "Eyeshadow Palette with Mirror", + "description": "The Eyeshadow Palette with Mirror offers a versatile range of eyeshadow shades for creating stunning eye looks. With a built-in mirror, it's convenient for on-the-go makeup application.", + "category": "beauty", + "price": 19.99, + "discountPercentage": 5.5, + "rating": 3.28, + "stock": 44, + "tags": [ + "beauty", + "eyeshadow" + ], + "brand": "Glamour Beauty", + "sku": "MVCFH27F", + "weight": 3, + "dimensions": { + "width": 12.42, + "height": 8.63, + "depth": 29.13 + }, + "warrantyInformation": "1 year warranty", + "shippingInformation": "Ships in 2 weeks", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 4, + "comment": "Very satisfied!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Liam Garcia", + "reviewerEmail": "liam.garcia@x.dummyjson.com" + }, + { + "rating": 1, + "comment": "Very disappointed!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Nora Russell", + "reviewerEmail": "nora.russell@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Highly impressed!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Elena Baker", + "reviewerEmail": "elena.baker@x.dummyjson.com" + } + ], + "returnPolicy": "30 days return policy", + "minimumOrderQuantity": 32, + "meta": { + "createdAt": "2024-05-23T08:56:21.618Z", + "updatedAt": "2024-05-23T08:56:21.618Z", + "barcode": "2817839095220", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/thumbnail.png", + "quantity": 1, + "user": "cb1b" + } + ], "orders": [ { "id": "d496", diff --git a/src/App.js b/src/App.js index d5c6ac4..b2a2ccd 100644 --- a/src/App.js +++ b/src/App.js @@ -21,6 +21,8 @@ import { selectLoggedInUser } from "./features/auth/authSlice"; import { fetchItemsByUserIdAsync } from "./features/cart/cartSlice"; import PageNotFound from "./pages/404"; import OrderSuccessPage from "./pages/OrderSuccessPage"; +import UserOrderPage from "./pages/UserOrderPage"; +import UserOrders from "./features/user/components/UserOrders"; const router = createBrowserRouter([ { path: "/", @@ -67,6 +69,10 @@ const router = createBrowserRouter([ path: "/order-success/:id", element: <OrderSuccessPage></OrderSuccessPage>, }, + { + path: "/orders", + element: <UserOrderPage></UserOrderPage>, + }, { path: "*", element: <PageNotFound></PageNotFound>, diff --git a/src/app/store.js b/src/app/store.js index 5619441..47af029 100644 --- a/src/app/store.js +++ b/src/app/store.js @@ -3,6 +3,7 @@ import productReducer from "../features/product/productSlice"; import authReducer from "../features/auth/authSlice"; import cartReducer from "../features/cart/cartSlice"; import orderReducer from "../features/order/orderSlice"; +import userReducer from "../features/user/userSlice"; export const store = configureStore({ reducer: { @@ -10,5 +11,6 @@ export const store = configureStore({ auth: authReducer, cart: cartReducer, order: orderReducer, + user: userReducer, }, }); diff --git a/src/features/cart/Cart.js b/src/features/cart/Cart.js index 81d4e3c..fb756df 100644 --- a/src/features/cart/Cart.js +++ b/src/features/cart/Cart.js @@ -53,7 +53,7 @@ export default function Cart() { <h3> <a href={item.href}>{item.title}</a> </h3> - <p className="ml-4">${Math.ceil(item.price)}</p> + <p className="ml-4">${item.price}</p> </div> <p className="mt-1 text-sm text-gray-500"> {item.brand} @@ -103,7 +103,7 @@ export default function Cart() { <div className="border-t border-gray-200 px-4 py-6 sm:px-6"> <div className="flex justify-between my-2 text-base font-medium text-gray-900"> <p>Subtotal</p> - <p>${Math.ceil(totalAmount)}</p> + <p>${totalAmount}</p> </div> <div className="flex justify-between my-2 text-base font-medium text-gray-900"> <p>Total Items in Cart</p> diff --git a/src/features/navbar/Navbar.js b/src/features/navbar/Navbar.js index 2f5ef13..43beba7 100644 --- a/src/features/navbar/Navbar.js +++ b/src/features/navbar/Navbar.js @@ -20,8 +20,8 @@ const navigation = [ { name: "Team", href: "#", current: false }, ]; const userNavigation = [ - { name: "Your Profile", link: "#" }, - { name: "Settings", link: "#" }, + { name: "Your Profile", link: "/profile" }, + { name: "My Orders", link: "/orders" }, { name: "Sign out", link: "/login" }, ]; diff --git a/src/features/user/UserAPI.js b/src/features/user/UserAPI.js new file mode 100644 index 0000000..3c17a5d --- /dev/null +++ b/src/features/user/UserAPI.js @@ -0,0 +1,10 @@ +export function fetchLoggedInUserOrders(userId) { + return new Promise(async (resolve) => { + const response = await fetch( + "http://localhost:8080/orders/?user.id=" + userId + ); + const data = await response.json(); + + resolve({ data }); + }); +} diff --git a/src/features/user/components/UserOrders.js b/src/features/user/components/UserOrders.js new file mode 100644 index 0000000..e998245 --- /dev/null +++ b/src/features/user/components/UserOrders.js @@ -0,0 +1,111 @@ +import React, { useEffect } from "react"; +import { useSelector, useDispatch } from "react-redux"; +import { fetchLoggedInUserOrderAsync, selectUserOrders } from "../userSlice"; +import { selectLoggedInUser } from "../../auth/authSlice"; + +export default function UserOrders() { + const user = useSelector(selectLoggedInUser); + const dispatch = useDispatch(); + const orders = useSelector(selectUserOrders); + + useEffect(() => { + dispatch(fetchLoggedInUserOrderAsync(user.id)); + }, []); + + return ( + <div> + {orders.map((order) => ( + <div> + <div> + <div className="mx-auto mt-12 bg-white max-w-7xl px-4 sm:px-6 lg:px-8"> + <div className="border-t border-gray-200 px-4 py-6 sm:px-6"> + <h1 className="text-4xl my-5 font-bold tracking-tight text-gray-900"> + Order # {order.id} + </h1> + <h2 className="text-xl my-5 font-bold tracking-tight text-red-900"> + Order Status : {order.status} + </h2> + <div className="flow-root"> + <ul role="list" className="-my-6 divide-y divide-gray-200"> + {order.items.map((item) => ( + <li key={item.id} className="flex py-6"> + <div className="h-24 w-24 flex-shrink-0 overflow-hidden rounded-md border border-gray-200"> + <img + src={item.thumbnail} + alt={item.title} + className="h-full w-full object-cover object-center" + /> + </div> + + <div className="ml-4 flex flex-1 flex-col"> + <div> + <div className="flex justify-between text-base font-medium text-gray-900"> + <h3> + <a href={item.href}>{item.title}</a> + </h3> + <p className="ml-4">${item.price}</p> + </div> + <p className="mt-1 text-sm text-gray-500"> + {item.brand} + </p> + </div> + <div className="flex flex-1 items-end justify-between text-sm"> + <div className="text-gray-500"> + <label + htmlFor="quantity" + className="inline mr-5 text-sm font-medium leading-6 text-gray-900" + > + Qty:{item.quantity} + </label> + </div> + + <div className="flex"></div> + </div> + </div> + </li> + ))} + </ul> + </div> + </div> + + <div className="border-t border-gray-200 px-4 py-6 sm:px-6"> + <div className="flex justify-between my-2 text-base font-medium text-gray-900"> + <p>Subtotal</p> + <p>${order.totalAmount}</p> + </div> + <div className="flex justify-between my-2 text-base font-medium text-gray-900"> + <p>Total Items in Cart</p> + <p>{order.totalItems} Items</p> + </div> + <p className="mt-0.5 text-sm text-gray-500">Shipping Address</p> + <div className="flex justify-between gap-x-6 px-5 py-5 border-solid border-2 border-gray-200"> + <div className="flex gap-x-4"> + <div className="min-w-0 flex-auto"> + <p className="text-sm font-semibold leading-6 text-gray-900"> + {order.selectedAddress.name} + </p> + <p className="mt-1 truncate text-xs leading-5 text-gray-500"> + {order.selectedAddress.street} + </p> + <p className="mt-1 truncate text-xs leading-5 text-gray-500"> + {order.selectedAddress.pinCode} + </p> + </div> + </div> + <div className="hidden sm:flex sm:flex-col sm:items-end"> + <p className="text-sm leading-6 text-gray-900"> + Phone: {order.selectedAddress.phone} + </p> + <p className="text-sm leading-6 text-gray-500"> + {order.selectedAddress.city} + </p> + </div> + </div> + </div> + </div> + </div> + </div> + ))} + </div> + ); +} diff --git a/src/features/user/components/UserProfile.js b/src/features/user/components/UserProfile.js new file mode 100644 index 0000000..b431120 --- /dev/null +++ b/src/features/user/components/UserProfile.js @@ -0,0 +1,14 @@ +import React, { useState } from "react"; +import { useSelector, useDispatch } from "react-redux"; +import {} from "../userSlice"; + +export default function Counter() { + const count = useSelector(selectCount); + const dispatch = useDispatch(); + + return ( + <div> + <div></div> + </div> + ); +} diff --git a/src/features/user/userSlice.js b/src/features/user/userSlice.js new file mode 100644 index 0000000..4ab821e --- /dev/null +++ b/src/features/user/userSlice.js @@ -0,0 +1,43 @@ +import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; +import { fetchLoggedInUserOrders } from "./UserAPI"; + +const initialState = { + userOrders: [], + status: "idle", +}; + +export const fetchLoggedInUserOrderAsync = createAsyncThunk( + "user/fetchLoggedInUserOrders", + async (userId) => { + const response = await fetchLoggedInUserOrders(userId); + // The value we return becomes the `fulfilled` action payload + console.log(response.data); + return response.data; + } +); + +export const userSlice = createSlice({ + name: "user", + initialState, + reducers: { + increment: (state) => { + state.value += 1; + }, + }, + extraReducers: (builder) => { + builder + .addCase(fetchLoggedInUserOrderAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(fetchLoggedInUserOrderAsync.fulfilled, (state, action) => { + state.status = "idle"; + //this info can be diffrent or more than logged-in user info + state.userOrders = action.payload; + }); + }, +}); + +export const { increment } = userSlice.actions; +export const selectUserOrders = (state) => state.user.userOrders; + +export default userSlice.reducer; diff --git a/src/pages/Checkout.js b/src/pages/Checkout.js index 5a99969..48cfe15 100644 --- a/src/pages/Checkout.js +++ b/src/pages/Checkout.js @@ -379,7 +379,7 @@ function Checkout() { <h3> <a href={item.href}>{item.title}</a> </h3> - <p className="ml-4">${Math.ceil(item.price)}</p> + <p className="ml-4">${item.price}</p> </div> <p className="mt-1 text-sm text-gray-500"> {item.brand} @@ -429,7 +429,7 @@ function Checkout() { <div className="border-t border-gray-200 px-2 py-6 sm:px-2"> <div className="flex justify-between my-2 text-base font-medium text-gray-900"> <p>Subtotal</p> - <p>${Math.ceil(totalAmount)}</p> + <p>${totalAmount}</p> </div> <div className="flex justify-between my-2 text-base font-medium text-gray-900"> <p>Total Items in Cart</p> diff --git a/src/pages/UserOrderPage.js b/src/pages/UserOrderPage.js new file mode 100644 index 0000000..6627725 --- /dev/null +++ b/src/pages/UserOrderPage.js @@ -0,0 +1,14 @@ +import NavBar from "../features/navbar/Navbar"; +import UserOrders from "../features/user/components/UserOrders"; +function UserOrderPage() { + return ( + <div> + <NavBar> + <h1 className="mx-auto text-2xl">My Orders</h1> + <UserOrders></UserOrders> + </NavBar> + </div> + ); +} + +export default UserOrderPage; From f6bcaecb4f82a2472cf241e3c6ce47154d1b2710 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Wed, 26 Jun 2024 11:00:05 +0530 Subject: [PATCH 31/47] fix navbar menu issue in mobile layout --- src/features/navbar/Navbar.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/navbar/Navbar.js b/src/features/navbar/Navbar.js index 43beba7..49ad246 100644 --- a/src/features/navbar/Navbar.js +++ b/src/features/navbar/Navbar.js @@ -208,8 +208,8 @@ function NavBar({ children }) { {userNavigation.map((item) => ( <Disclosure.Button key={item.name} - as="a" - href={item.href} + as={Link} // change as=a to as=Link to use Disclosure.Button as a Link (react-router-dom) + to={item.link} // {link: "/orders"} className="block rounded-md px-3 py-2 text-base font-medium text-gray-400 hover:bg-gray-700 hover:text-white" > {item.name} From 9a7f78d7f6a1feb447359d6d09d3843dfe0a5e32 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Wed, 26 Jun 2024 17:58:47 +0530 Subject: [PATCH 32/47] My profile page added/fettchLoogedInUser API created for full details of user --- data.json | 239 ++++++++++++++------ src/App.js | 8 +- src/features/auth/authAPI.js | 14 -- src/features/auth/authSlice.js | 3 +- src/features/navbar/Navbar.js | 2 +- src/features/user/UserAPI.js | 22 ++ src/features/user/components/UserOrders.js | 9 +- src/features/user/components/UserProfile.js | 72 +++++- src/features/user/userSlice.js | 41 +++- src/pages/Checkout.js | 8 +- src/pages/UserProfilePage.js | 15 ++ 11 files changed, 329 insertions(+), 104 deletions(-) create mode 100644 src/pages/UserProfilePage.js diff --git a/data.json b/data.json index 5038df9..6bf7958 100644 --- a/data.json +++ b/data.json @@ -1915,15 +1915,6 @@ "email": "test@123.com", "password": "Murtaza123", "addresses": [ - { - "name": "Murtaza Tankiwala", - "email": "murtazashabbir14@gmail.com", - "phone": "07869558609", - "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", - "city": "Indore", - "state": "Madhya Pradesh", - "pinCode": "452002" - }, { "name": "Murtaza Tankiwala", "email": "murtazashabbir14@gmail.com", @@ -1952,70 +1943,7 @@ ] } ], - "cart": [ - { - "id": "efca", - "title": "Eyeshadow Palette with Mirror", - "description": "The Eyeshadow Palette with Mirror offers a versatile range of eyeshadow shades for creating stunning eye looks. With a built-in mirror, it's convenient for on-the-go makeup application.", - "category": "beauty", - "price": 19.99, - "discountPercentage": 5.5, - "rating": 3.28, - "stock": 44, - "tags": [ - "beauty", - "eyeshadow" - ], - "brand": "Glamour Beauty", - "sku": "MVCFH27F", - "weight": 3, - "dimensions": { - "width": 12.42, - "height": 8.63, - "depth": 29.13 - }, - "warrantyInformation": "1 year warranty", - "shippingInformation": "Ships in 2 weeks", - "availabilityStatus": "In Stock", - "reviews": [ - { - "rating": 4, - "comment": "Very satisfied!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Liam Garcia", - "reviewerEmail": "liam.garcia@x.dummyjson.com" - }, - { - "rating": 1, - "comment": "Very disappointed!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Nora Russell", - "reviewerEmail": "nora.russell@x.dummyjson.com" - }, - { - "rating": 5, - "comment": "Highly impressed!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Elena Baker", - "reviewerEmail": "elena.baker@x.dummyjson.com" - } - ], - "returnPolicy": "30 days return policy", - "minimumOrderQuantity": 32, - "meta": { - "createdAt": "2024-05-23T08:56:21.618Z", - "updatedAt": "2024-05-23T08:56:21.618Z", - "barcode": "2817839095220", - "qrCode": "https://dummyjson.com/public/qr-code.png" - }, - "images": [ - "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/1.png" - ], - "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/thumbnail.png", - "quantity": 1, - "user": "cb1b" - } - ], + "cart": [], "orders": [ { "id": "d496", @@ -2341,6 +2269,171 @@ }, "paymentMethod": "card", "status": "pending" + }, + { + "id": "6cf3", + "items": [ + { + "id": "efca", + "title": "Eyeshadow Palette with Mirror", + "description": "The Eyeshadow Palette with Mirror offers a versatile range of eyeshadow shades for creating stunning eye looks. With a built-in mirror, it's convenient for on-the-go makeup application.", + "category": "beauty", + "price": 19.99, + "discountPercentage": 5.5, + "rating": 3.28, + "stock": 44, + "tags": [ + "beauty", + "eyeshadow" + ], + "brand": "Glamour Beauty", + "sku": "MVCFH27F", + "weight": 3, + "dimensions": { + "width": 12.42, + "height": 8.63, + "depth": 29.13 + }, + "warrantyInformation": "1 year warranty", + "shippingInformation": "Ships in 2 weeks", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 4, + "comment": "Very satisfied!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Liam Garcia", + "reviewerEmail": "liam.garcia@x.dummyjson.com" + }, + { + "rating": 1, + "comment": "Very disappointed!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Nora Russell", + "reviewerEmail": "nora.russell@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Highly impressed!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Elena Baker", + "reviewerEmail": "elena.baker@x.dummyjson.com" + } + ], + "returnPolicy": "30 days return policy", + "minimumOrderQuantity": 32, + "meta": { + "createdAt": "2024-05-23T08:56:21.618Z", + "updatedAt": "2024-05-23T08:56:21.618Z", + "barcode": "2817839095220", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/thumbnail.png", + "quantity": 2, + "user": "cb1b" + }, + { + "id": "f678", + "title": "Eggs", + "description": "Fresh eggs, a versatile ingredient for baking, cooking, or breakfast.", + "category": "groceries", + "price": 2.99, + "discountPercentage": 5.8, + "rating": 4.46, + "stock": 10, + "tags": [ + "dairy" + ], + "sku": "YA617RI7", + "weight": 4, + "dimensions": { + "width": 12.3, + "height": 10.99, + "depth": 15.53 + }, + "warrantyInformation": "3 year warranty", + "shippingInformation": "Ships overnight", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 2, + "comment": "Very unhappy with my purchase!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Mateo Perez", + "reviewerEmail": "mateo.perez@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Very happy with my purchase!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Cameron Perez", + "reviewerEmail": "cameron.perez@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Very happy with my purchase!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Aurora Barnes", + "reviewerEmail": "aurora.barnes@x.dummyjson.com" + } + ], + "returnPolicy": "30 days return policy", + "minimumOrderQuantity": 43, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "7095702028776", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/groceries/Eggs/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Eggs/thumbnail.png", + "quantity": 4, + "user": "cb1b" + } + ], + "totalAmount": 51.94, + "totalItems": 6, + "user": { + "id": "cb1b", + "email": "test@123.com", + "password": "Murtaza123", + "addresses": [ + { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + }, + { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + } + ] + }, + "selectedAddress": { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + }, + "paymentMethod": "card", + "status": "pending" } ] } \ No newline at end of file diff --git a/src/App.js b/src/App.js index b2a2ccd..ad52bcc 100644 --- a/src/App.js +++ b/src/App.js @@ -22,7 +22,8 @@ import { fetchItemsByUserIdAsync } from "./features/cart/cartSlice"; import PageNotFound from "./pages/404"; import OrderSuccessPage from "./pages/OrderSuccessPage"; import UserOrderPage from "./pages/UserOrderPage"; -import UserOrders from "./features/user/components/UserOrders"; +import UserProfilePage from "./pages/UserProfilePage"; +import { fetchLoggedInUserAsync } from "./features/user/userSlice"; const router = createBrowserRouter([ { path: "/", @@ -73,6 +74,10 @@ const router = createBrowserRouter([ path: "/orders", element: <UserOrderPage></UserOrderPage>, }, + { + path: "/profile", + element: <UserProfilePage></UserProfilePage>, + }, { path: "*", element: <PageNotFound></PageNotFound>, @@ -86,6 +91,7 @@ function App() { useEffect(() => { if (user) { dispatch(fetchItemsByUserIdAsync(user.id)); + dispatch(fetchLoggedInUserAsync(user.id)); } }, [dispatch, user]); return ( diff --git a/src/features/auth/authAPI.js b/src/features/auth/authAPI.js index a42ac82..4df73f6 100644 --- a/src/features/auth/authAPI.js +++ b/src/features/auth/authAPI.js @@ -31,17 +31,3 @@ export function checkUser(loginInfo) { // TODO:on server it will only return some info of user (not password) }); } -// update user address from checkout page.... -export function updateUser(update) { - return new Promise(async (resolve) => { - const response = await fetch("http://localhost:8080/users/" + update.id, { - // to get the particular user and address in it - method: "PATCH", - body: JSON.stringify(update), - headers: { "content-type": "application/json" }, - }); - const data = await response.json(); - // TODO:on server it will only return some info of user (not password) - resolve({ data }); - }); -} diff --git a/src/features/auth/authSlice.js b/src/features/auth/authSlice.js index 5162ea1..d3d5ffa 100644 --- a/src/features/auth/authSlice.js +++ b/src/features/auth/authSlice.js @@ -1,5 +1,6 @@ import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; -import { checkUser, createUser, updateUser } from "./authAPI"; +import { checkUser, createUser } from "./authAPI"; +import { updateUser } from "../user/userAPI"; const initialState = { loggedInUser: null, diff --git a/src/features/navbar/Navbar.js b/src/features/navbar/Navbar.js index 49ad246..b930271 100644 --- a/src/features/navbar/Navbar.js +++ b/src/features/navbar/Navbar.js @@ -20,7 +20,7 @@ const navigation = [ { name: "Team", href: "#", current: false }, ]; const userNavigation = [ - { name: "Your Profile", link: "/profile" }, + { name: "My Profile", link: "/profile" }, { name: "My Orders", link: "/orders" }, { name: "Sign out", link: "/login" }, ]; diff --git a/src/features/user/UserAPI.js b/src/features/user/UserAPI.js index 3c17a5d..8e79cd5 100644 --- a/src/features/user/UserAPI.js +++ b/src/features/user/UserAPI.js @@ -8,3 +8,25 @@ export function fetchLoggedInUserOrders(userId) { resolve({ data }); }); } +export function fetchLoggedInUser(userId) { + return new Promise(async (resolve) => { + const response = await fetch("http://localhost:8080/users/" + userId); + const data = await response.json(); + + resolve({ data }); + }); +} +// update user address from checkout page.... +export function updateUser(update) { + return new Promise(async (resolve) => { + const response = await fetch("http://localhost:8080/users/" + update.id, { + // to get the particular user and address in it + method: "PATCH", + body: JSON.stringify(update), + headers: { "content-type": "application/json" }, + }); + const data = await response.json(); + // TODO:on server it will only return some info of user (not password) + resolve({ data }); + }); +} diff --git a/src/features/user/components/UserOrders.js b/src/features/user/components/UserOrders.js index e998245..e94bbee 100644 --- a/src/features/user/components/UserOrders.js +++ b/src/features/user/components/UserOrders.js @@ -1,10 +1,13 @@ import React, { useEffect } from "react"; import { useSelector, useDispatch } from "react-redux"; -import { fetchLoggedInUserOrderAsync, selectUserOrders } from "../userSlice"; -import { selectLoggedInUser } from "../../auth/authSlice"; +import { + fetchLoggedInUserOrderAsync, + selectUserInfo, + selectUserOrders, +} from "../userSlice"; export default function UserOrders() { - const user = useSelector(selectLoggedInUser); + const user = useSelector(selectUserInfo); const dispatch = useDispatch(); const orders = useSelector(selectUserOrders); diff --git a/src/features/user/components/UserProfile.js b/src/features/user/components/UserProfile.js index b431120..0c937a0 100644 --- a/src/features/user/components/UserProfile.js +++ b/src/features/user/components/UserProfile.js @@ -1,14 +1,78 @@ import React, { useState } from "react"; import { useSelector, useDispatch } from "react-redux"; -import {} from "../userSlice"; +import { selectUserInfo, updateUserAsync } from "../userSlice"; -export default function Counter() { - const count = useSelector(selectCount); +export default function UserProfile() { const dispatch = useDispatch(); + const user = useSelector(selectUserInfo); + const handleEdit = () => {}; + const handleRemove = (e, index) => { + const newUser = { ...user, addresses: [...user.addresses] }; //for shallow copy issue + newUser.addresses.splice(index, 1); + dispatch(updateUserAsync(newUser)); + }; return ( <div> - <div></div> + <div className="mx-auto mt-12 bg-white max-w-7xl px-4 sm:px-6 lg:px-8"> + <div className="border-t border-gray-200 px-4 py-6 sm:px-6"> + <h1 className="text-4xl my-5 font-bold tracking-tight text-gray-900"> + Name: {user.name ? user.name : "New User"} + </h1> + <h2 className="text-xl my-5 font-bold tracking-tight text-red-900"> + Email Address: {user.email} + </h2> + </div> + + <div className="border-t border-gray-200 px-4 py-6 sm:px-6"> + <p className="mt-0.5 text-sm text-gray-500">Your Addresses:</p> + {user.addresses.map((address, index) => ( + <div className="flex justify-between gap-x-6 px-5 py-5 border-solid border-2 border-gray-200"> + <div className="flex gap-x-4"> + <div className="min-w-0 flex-auto"> + <p className="text-sm font-semibold leading-6 text-gray-900"> + {address.name} + </p> + <p className="mt-1 truncate text-xs leading-5 text-gray-500"> + {address.street} + </p> + <p className="mt-1 truncate text-xs leading-5 text-gray-500"> + {address.pinCode} + </p> + </div> + </div> + <div className="hidden sm:flex sm:flex-col sm:items-end"> + <p className="text-sm leading-6 text-gray-900"> + Phone: {address.phone} + </p> + <p className="text-sm leading-6 text-gray-500"> + {address.city} + </p> + </div> + <div className="hidden sm:flex sm:flex-col sm:items-end"> + <button + onClick={(e) => { + handleEdit(e, address.id); + }} + type="button" + className="font-medium text-indigo-600 hover:text-indigo-500" + > + Edit + </button> + <button + onClick={(e) => { + handleRemove(e, index); + }} + type="button" + className="font-medium text-indigo-600 hover:text-indigo-500" + > + Remove + </button> + </div> + </div> + ))} + </div> + </div> </div> ); } diff --git a/src/features/user/userSlice.js b/src/features/user/userSlice.js index 4ab821e..7ee076e 100644 --- a/src/features/user/userSlice.js +++ b/src/features/user/userSlice.js @@ -1,9 +1,14 @@ import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; -import { fetchLoggedInUserOrders } from "./UserAPI"; +import { + fetchLoggedInUserOrders, + updateUser, + fetchLoggedInUser, +} from "./userAPI"; const initialState = { userOrders: [], status: "idle", + userInfo: null, // this info is use in case of detailed user info, while auth will only used used for loggedInUser id etc checks. }; export const fetchLoggedInUserOrderAsync = createAsyncThunk( @@ -15,6 +20,24 @@ export const fetchLoggedInUserOrderAsync = createAsyncThunk( return response.data; } ); +export const fetchLoggedInUserAsync = createAsyncThunk( + "user/fetchLoggedInUser", + async (userId) => { + const response = await fetchLoggedInUser(userId); + // The value we return becomes the `fulfilled` action payload + console.log(response.data); + return response.data; + } +); +export const updateUserAsync = createAsyncThunk( + "user/updateUser", + async (userId) => { + const response = await updateUser(userId); + // The value we return becomes the `fulfilled` action payload + console.log(response.data); + return response.data; + } +); export const userSlice = createSlice({ name: "user", @@ -31,13 +54,27 @@ export const userSlice = createSlice({ }) .addCase(fetchLoggedInUserOrderAsync.fulfilled, (state, action) => { state.status = "idle"; - //this info can be diffrent or more than logged-in user info state.userOrders = action.payload; + }) + .addCase(updateUserAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(updateUserAsync.fulfilled, (state, action) => { + state.status = "idle"; + state.userOrders = action.payload; + }) + .addCase(fetchLoggedInUserAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(fetchLoggedInUserAsync.fulfilled, (state, action) => { + state.status = "idle"; + state.userInfo = action.payload; }); }, }); export const { increment } = userSlice.actions; export const selectUserOrders = (state) => state.user.userOrders; +export const selectUserInfo = (state) => state.user.userInfo; export default userSlice.reducer; diff --git a/src/pages/Checkout.js b/src/pages/Checkout.js index 48cfe15..9558b56 100644 --- a/src/pages/Checkout.js +++ b/src/pages/Checkout.js @@ -7,15 +7,13 @@ import { } from "../features/cart/cartSlice"; import { Navigate } from "react-router-dom"; import { useForm } from "react-hook-form"; -import { - selectLoggedInUser, - updateUserAsync, -} from "../features/auth/authSlice"; +import { updateUserAsync } from "../features/auth/authSlice"; import { createOrderAsync, selectCurrentOrder, } from "../features/order/orderSlice"; import { useState } from "react"; +import { selectUserInfo } from "../features/user/userSlice"; function Checkout() { const dispatch = useDispatch(); @@ -26,7 +24,7 @@ function Checkout() { formState: { errors }, } = useForm(); - const user = useSelector(selectLoggedInUser); + const user = useSelector(selectUserInfo); const items = useSelector(selectItems); const currentOrder = useSelector(selectCurrentOrder); const totalAmount = items.reduce( diff --git a/src/pages/UserProfilePage.js b/src/pages/UserProfilePage.js new file mode 100644 index 0000000..4b308f7 --- /dev/null +++ b/src/pages/UserProfilePage.js @@ -0,0 +1,15 @@ +import NavBar from "../features/navbar/Navbar"; +import UserProfile from "../features/user/components/UserProfile"; + +function UserProfilePage() { + return ( + <div> + <NavBar> + <h1 className="mx-auto text-2xl">My Profile</h1> + <UserProfile></UserProfile> + </NavBar> + </div> + ); +} + +export default UserProfilePage; From beab14095f64e06f0d40daba2c2105a114d763dc Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Wed, 26 Jun 2024 19:09:17 +0530 Subject: [PATCH 33/47] edit/remove address functionality in my profile page --- data.json | 7 +- src/features/user/components/UserProfile.js | 281 +++++++++++++++++--- 2 files changed, 246 insertions(+), 42 deletions(-) diff --git a/data.json b/data.json index 6bf7958..1c822a6 100644 --- a/data.json +++ b/data.json @@ -1916,13 +1916,14 @@ "password": "Murtaza123", "addresses": [ { - "name": "Murtaza Tankiwala", + "name": "Murtaza ", "email": "murtazashabbir14@gmail.com", - "phone": "07869558609", + "phone": "7869558609", "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", "city": "Indore", "state": "Madhya Pradesh", - "pinCode": "452002" + "pinCode": "452002", + "address": "Indore" } ] }, diff --git a/src/features/user/components/UserProfile.js b/src/features/user/components/UserProfile.js index 0c937a0..06497aa 100644 --- a/src/features/user/components/UserProfile.js +++ b/src/features/user/components/UserProfile.js @@ -1,16 +1,44 @@ import React, { useState } from "react"; import { useSelector, useDispatch } from "react-redux"; import { selectUserInfo, updateUserAsync } from "../userSlice"; +import { useForm } from "react-hook-form"; export default function UserProfile() { const dispatch = useDispatch(); const user = useSelector(selectUserInfo); - const handleEdit = () => {}; + const [selectedEditIndex, setSelectedEditIndex] = useState(-1); + const { + register, + handleSubmit, + reset, + setValue, + formState: { errors }, + } = useForm(); + // to edit existing form data + const handleEdit = (addressUpdate, index) => { + const newUser = { ...user, addresses: [...user.addresses] }; //for shallow copy issue + newUser.addresses.splice(index, 1, addressUpdate); + dispatch(updateUserAsync(newUser)); + setSelectedEditIndex(-1); + }; + // to remove existing address const handleRemove = (e, index) => { const newUser = { ...user, addresses: [...user.addresses] }; //for shallow copy issue newUser.addresses.splice(index, 1); dispatch(updateUserAsync(newUser)); }; + // to show and edit form values.... + const handleEditForm = (index) => { + setSelectedEditIndex(index); + const address = user.addresses[index]; // to grab address according to index... + setValue("name", address.name); + setValue("email", address.email); + setValue("phone", address.phone); + setValue("street", address.street); + setValue("address", address.city); + setValue("state", address.state); + setValue("pinCode", address.pinCode); + }; return ( <div> @@ -27,47 +55,222 @@ export default function UserProfile() { <div className="border-t border-gray-200 px-4 py-6 sm:px-6"> <p className="mt-0.5 text-sm text-gray-500">Your Addresses:</p> {user.addresses.map((address, index) => ( - <div className="flex justify-between gap-x-6 px-5 py-5 border-solid border-2 border-gray-200"> - <div className="flex gap-x-4"> - <div className="min-w-0 flex-auto"> - <p className="text-sm font-semibold leading-6 text-gray-900"> - {address.name} - </p> - <p className="mt-1 truncate text-xs leading-5 text-gray-500"> - {address.street} + <div> + {selectedEditIndex === index ? ( + <form + className="bg-white px-5 py-12 mt-12" + noValidate + onSubmit={handleSubmit((data) => { + console.log(data); + handleEdit(data, index); + reset(); + })} + > + <div className="space-y-12"> + <div className="border-b border-gray-900/10 pb-12"> + <h2 className="text-2xl font-semibold leading-7 text-gray-900"> + Personal Information + </h2> + <p className="mt-1 text-sm leading-6 text-gray-600"> + Use a permanent address where you can receive mail. + </p> + + <div className="mt-10 grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6"> + <div className="sm:col-span-4"> + <label + htmlFor="name" + className="block text-sm font-medium leading-6 text-gray-900" + > + Full Name + </label> + <div className="mt-2"> + <input + type="text" + {...register("name", { + required: "name is required", + })} + id="name" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + /> + </div> + </div> + + <div className="sm:col-span-4"> + <label + htmlFor="email" + className="block text-sm font-medium leading-6 text-gray-900" + > + Email address + </label> + <div className="mt-2"> + <input + id="email" + {...register("email", { + required: "email is required", + })} + type="email" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + /> + </div> + </div> + + <div className="sm:col-span-3"> + <label + htmlFor="Phone" + className="block text-sm font-medium leading-6 text-gray-900" + > + Phone + </label> + <div className="mt-2"> + <input + id="email" + {...register("phone", { + required: "phone is required", + })} + type="tel" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + /> + </div> + </div> + + <div className="col-span-full"> + <label + htmlFor="street-address" + className="block text-sm font-medium leading-6 text-gray-900" + > + Street address + </label> + <div className="mt-2"> + <input + type="text" + {...register("street", { + required: "street is required", + })} + id="street-address" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + /> + </div> + </div> + + <div className="sm:col-span-2 sm:col-start-1"> + <label + htmlFor="city" + className="block text-sm font-medium leading-6 text-gray-900" + > + City + </label> + <div className="mt-2"> + <input + type="text" + {...register("city", { + required: "city is required", + })} + id="city" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + /> + </div> + </div> + + <div className="sm:col-span-2"> + <label + htmlFor="state" + className="block text-sm font-medium leading-6 text-gray-900" + > + State / Province + </label> + <div className="mt-2"> + <input + type="text" + {...register("state", { + required: "state is required", + })} + id="state" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + /> + </div> + </div> + + <div className="sm:col-span-2"> + <label + htmlFor="pinCode" + className="block text-sm font-medium leading-6 text-gray-900" + > + ZIP / Postal code + </label> + <div className="mt-2"> + <input + type="text" + {...register("pinCode", { + required: "pinCode is required", + })} + id="pinCode" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + /> + </div> + </div> + </div> + </div> + + <div className="mt-6 flex items-center justify-end gap-x-6"> + <button + onClick={(e) => setSelectedEditIndex(-1)} + type="submit" + className="rounded-md px-3 py-2 text-sm font-semibold text-grey shadow-sm hover:bg-grey-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" + > + Cancle + </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" + > + Edit Address + </button> + </div> + </div> + </form> + ) : null} + <div className="flex justify-between gap-x-6 px-5 py-5 border-solid border-2 border-gray-200"> + <div className="flex gap-x-4"> + <div className="min-w-0 flex-auto"> + <p className="text-sm font-semibold leading-6 text-gray-900"> + {address.name} + </p> + <p className="mt-1 truncate text-xs leading-5 text-gray-500"> + {address.street} + </p> + <p className="mt-1 truncate text-xs leading-5 text-gray-500"> + {address.pinCode} + </p> + </div> + </div> + <div className="hidden sm:flex sm:flex-col sm:items-end"> + <p className="text-sm leading-6 text-gray-900"> + Phone: {address.phone} </p> - <p className="mt-1 truncate text-xs leading-5 text-gray-500"> - {address.pinCode} + <p className="text-sm leading-6 text-gray-500"> + {address.city} </p> </div> - </div> - <div className="hidden sm:flex sm:flex-col sm:items-end"> - <p className="text-sm leading-6 text-gray-900"> - Phone: {address.phone} - </p> - <p className="text-sm leading-6 text-gray-500"> - {address.city} - </p> - </div> - <div className="hidden sm:flex sm:flex-col sm:items-end"> - <button - onClick={(e) => { - handleEdit(e, address.id); - }} - type="button" - className="font-medium text-indigo-600 hover:text-indigo-500" - > - Edit - </button> - <button - onClick={(e) => { - handleRemove(e, index); - }} - type="button" - className="font-medium text-indigo-600 hover:text-indigo-500" - > - Remove - </button> + <div className="hidden sm:flex sm:flex-col sm:items-end"> + <button + onClick={(e) => { + handleEditForm(index); + }} + type="button" + className="font-medium text-indigo-600 hover:text-indigo-500" + > + Edit + </button> + <button + onClick={(e) => { + handleRemove(e, index); + }} + type="button" + className="font-medium text-indigo-600 hover:text-indigo-500" + > + Remove + </button> + </div> </div> </div> ))} From 834bc67addbea1b3379faa56e7809be79ee041cf Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Wed, 26 Jun 2024 19:46:21 +0530 Subject: [PATCH 34/47] Add new Address functionality added --- data.json | 222 ++++++++++++++++++++ src/features/user/components/UserProfile.js | 184 +++++++++++++++- 2 files changed, 405 insertions(+), 1 deletion(-) diff --git a/data.json b/data.json index 1c822a6..9b27d6c 100644 --- a/data.json +++ b/data.json @@ -1924,6 +1924,16 @@ "state": "Madhya Pradesh", "pinCode": "452002", "address": "Indore" + }, + { + "name": "Jack Hill", + "email": "jack@gmail.com", + "phone": "12345", + "street": "11th road", + "address": "Mumbai", + "state": "Maharastra", + "pinCode": "452003", + "city": "Mumbai" } ] }, @@ -2435,6 +2445,218 @@ }, "paymentMethod": "card", "status": "pending" + }, + { + "id": "3802", + "items": [ + { + "id": "92dd", + "title": "Fish Steak", + "description": "Quality fish steak, suitable for grilling, baking, or pan-searing.", + "category": "groceries", + "price": 14.99, + "discountPercentage": 7, + "rating": 4.83, + "stock": 99, + "tags": [ + "seafood" + ], + "sku": "XNIH1MTA", + "weight": 8, + "dimensions": { + "width": 20.14, + "height": 8.4, + "depth": 10.01 + }, + "warrantyInformation": "1 year warranty", + "shippingInformation": "Ships in 1 month", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Great value for money!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Michael Johnson", + "reviewerEmail": "michael.johnson@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Would buy again!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Julian Newton", + "reviewerEmail": "julian.newton@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Excellent quality!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Lila Hudson", + "reviewerEmail": "lila.hudson@x.dummyjson.com" + } + ], + "returnPolicy": "30 days return policy", + "minimumOrderQuantity": 49, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "4250692197342", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/groceries/Fish%20Steak/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Fish%20Steak/thumbnail.png", + "quantity": 1, + "user": "cb1b" + } + ], + "totalAmount": 14.99, + "totalItems": 1, + "user": { + "id": "cb1b", + "email": "test@123.com", + "password": "Murtaza123", + "addresses": [ + { + "name": "Murtaza ", + "email": "murtazashabbir14@gmail.com", + "phone": "7869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002", + "address": "Indore" + }, + { + "name": "Jack Hill", + "email": "jack@gmail.com", + "phone": "12345", + "street": "11th road", + "address": "Mumbai", + "state": "Maharastra", + "pinCode": "452003", + "city": "Mumbai" + } + ] + }, + "selectedAddress": { + "name": "Jack Hill", + "email": "jack@gmail.com", + "phone": "12345", + "street": "11th road", + "address": "Mumbai", + "state": "Maharastra", + "pinCode": "452003", + "city": "Mumbai" + }, + "paymentMethod": "card", + "status": "pending" + }, + { + "id": "5c73", + "items": [ + { + "id": "7d4d", + "title": "Cooking Oil", + "description": "Versatile cooking oil suitable for frying, sautéing, and various culinary applications.", + "category": "groceries", + "price": 4.99, + "discountPercentage": 18.89, + "rating": 4.01, + "stock": 22, + "tags": [ + "cooking essentials" + ], + "sku": "Q6ZP1UY8", + "weight": 8, + "dimensions": { + "width": 8.18, + "height": 27.45, + "depth": 27.88 + }, + "warrantyInformation": "Lifetime warranty", + "shippingInformation": "Ships in 1 month", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Would buy again!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Mason Parker", + "reviewerEmail": "mason.parker@x.dummyjson.com" + }, + { + "rating": 3, + "comment": "Poor quality!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Jonathan Pierce", + "reviewerEmail": "jonathan.pierce@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Would buy again!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Alexander Hernandez", + "reviewerEmail": "alexander.hernandez@x.dummyjson.com" + } + ], + "returnPolicy": "60 days return policy", + "minimumOrderQuantity": 2, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "6707669443381", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/groceries/Cooking%20Oil/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Cooking%20Oil/thumbnail.png", + "quantity": 1, + "user": "cb1b" + } + ], + "totalAmount": 4.99, + "totalItems": 1, + "user": { + "id": "cb1b", + "email": "test@123.com", + "password": "Murtaza123", + "addresses": [ + { + "name": "Murtaza ", + "email": "murtazashabbir14@gmail.com", + "phone": "7869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002", + "address": "Indore" + }, + { + "name": "Jack Hill", + "email": "jack@gmail.com", + "phone": "12345", + "street": "11th road", + "address": "Mumbai", + "state": "Maharastra", + "pinCode": "452003", + "city": "Mumbai" + } + ] + }, + "selectedAddress": { + "name": "Murtaza ", + "email": "murtazashabbir14@gmail.com", + "phone": "7869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002", + "address": "Indore" + }, + "paymentMethod": "card", + "status": "pending" } ] } \ No newline at end of file diff --git a/src/features/user/components/UserProfile.js b/src/features/user/components/UserProfile.js index 06497aa..96ce7f9 100644 --- a/src/features/user/components/UserProfile.js +++ b/src/features/user/components/UserProfile.js @@ -7,6 +7,7 @@ export default function UserProfile() { const dispatch = useDispatch(); const user = useSelector(selectUserInfo); const [selectedEditIndex, setSelectedEditIndex] = useState(-1); + const [showAddressForm, setshowAddressForm] = useState(false); const { register, handleSubmit, @@ -39,6 +40,12 @@ export default function UserProfile() { setValue("state", address.state); setValue("pinCode", address.pinCode); }; + // to add new address + const handleAdd = (address) => { + const newUser = { ...user, addresses: [...user.addresses, address] }; + dispatch(updateUserAsync(newUser)); + setshowAddressForm(false); + }; return ( <div> @@ -51,8 +58,183 @@ export default function UserProfile() { Email Address: {user.email} </h2> </div> - <div className="border-t border-gray-200 px-4 py-6 sm:px-6"> + <button + onClick={(e) => { + setshowAddressForm(true); + setSelectedEditIndex(-1); + }} + type="submit" + className="rounded-md my-5 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 New Address + </button> + {showAddressForm ? ( + <form + className="bg-white px-5 py-12 mt-12" + noValidate + onSubmit={handleSubmit((data) => { + console.log(data); + handleAdd(data); + reset(); + })} + > + <div className="space-y-12"> + <div className="border-b border-gray-900/10 pb-12"> + <h2 className="text-2xl font-semibold leading-7 text-gray-900"> + Personal Information + </h2> + <p className="mt-1 text-sm leading-6 text-gray-600"> + Use a permanent address where you can receive mail. + </p> + + <div className="mt-10 grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6"> + <div className="sm:col-span-4"> + <label + htmlFor="name" + className="block text-sm font-medium leading-6 text-gray-900" + > + Full Name + </label> + <div className="mt-2"> + <input + type="text" + {...register("name", { + required: "name is required", + })} + id="name" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + /> + </div> + </div> + + <div className="sm:col-span-4"> + <label + htmlFor="email" + className="block text-sm font-medium leading-6 text-gray-900" + > + Email address + </label> + <div className="mt-2"> + <input + id="email" + {...register("email", { + required: "email is required", + })} + type="email" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + /> + </div> + </div> + + <div className="sm:col-span-3"> + <label + htmlFor="Phone" + className="block text-sm font-medium leading-6 text-gray-900" + > + Phone + </label> + <div className="mt-2"> + <input + id="email" + {...register("phone", { + required: "phone is required", + })} + type="tel" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + /> + </div> + </div> + + <div className="col-span-full"> + <label + htmlFor="street-address" + className="block text-sm font-medium leading-6 text-gray-900" + > + Street address + </label> + <div className="mt-2"> + <input + type="text" + {...register("street", { + required: "street is required", + })} + id="street-address" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + /> + </div> + </div> + + <div className="sm:col-span-2 sm:col-start-1"> + <label + htmlFor="city" + className="block text-sm font-medium leading-6 text-gray-900" + > + City + </label> + <div className="mt-2"> + <input + type="text" + {...register("city", { + required: "city is required", + })} + id="city" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + /> + </div> + </div> + + <div className="sm:col-span-2"> + <label + htmlFor="state" + className="block text-sm font-medium leading-6 text-gray-900" + > + State / Province + </label> + <div className="mt-2"> + <input + type="text" + {...register("state", { + required: "state is required", + })} + id="state" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + /> + </div> + </div> + + <div className="sm:col-span-2"> + <label + htmlFor="pinCode" + className="block text-sm font-medium leading-6 text-gray-900" + > + ZIP / Postal code + </label> + <div className="mt-2"> + <input + type="text" + {...register("pinCode", { + required: "pinCode is required", + })} + id="pinCode" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + /> + </div> + </div> + </div> + </div> + + <div className="mt-6 flex items-center justify-end gap-x-6"> + <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" + > + Add Address + </button> + </div> + </div> + </form> + ) : null} <p className="mt-0.5 text-sm text-gray-500">Your Addresses:</p> {user.addresses.map((address, index) => ( <div> From 67348b78b25128838954f703167cb8bd5a4041f6 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Thu, 27 Jun 2024 09:35:47 +0530 Subject: [PATCH 35/47] signout functionality/forgot password page --- src/App.js | 10 +++ src/features/auth/authAPI.js | 9 ++ src/features/auth/authSlice.js | 14 +++- .../auth/components/ForgotPassword.js | 84 +++++++++++++++++++ src/features/auth/components/LogOut.js | 16 ++++ src/features/auth/components/Login.js | 6 +- src/features/auth/components/Signup.js | 6 +- src/features/navbar/Navbar.js | 2 +- src/features/user/components/UserProfile.js | 2 + src/pages/ForgotPasswordPage.js | 10 +++ 10 files changed, 151 insertions(+), 8 deletions(-) create mode 100644 src/features/auth/components/ForgotPassword.js create mode 100644 src/features/auth/components/LogOut.js create mode 100644 src/pages/ForgotPasswordPage.js diff --git a/src/App.js b/src/App.js index ad52bcc..1f14688 100644 --- a/src/App.js +++ b/src/App.js @@ -24,6 +24,8 @@ import OrderSuccessPage from "./pages/OrderSuccessPage"; import UserOrderPage from "./pages/UserOrderPage"; import UserProfilePage from "./pages/UserProfilePage"; import { fetchLoggedInUserAsync } from "./features/user/userSlice"; +import LogOut from "./features/auth/components/LogOut"; +import ForgotPasswordPage from "./pages/ForgotPasswordPage"; const router = createBrowserRouter([ { path: "/", @@ -78,6 +80,14 @@ const router = createBrowserRouter([ path: "/profile", element: <UserProfilePage></UserProfilePage>, }, + { + path: "/logout", + element: <LogOut></LogOut>, + }, + { + path: "/forgot-paasword", + element: <ForgotPasswordPage></ForgotPasswordPage>, + }, { path: "*", element: <PageNotFound></PageNotFound>, diff --git a/src/features/auth/authAPI.js b/src/features/auth/authAPI.js index 4df73f6..4de2a75 100644 --- a/src/features/auth/authAPI.js +++ b/src/features/auth/authAPI.js @@ -1,3 +1,4 @@ +// create user export function createUser(userData) { return new Promise(async (resolve) => { const response = await fetch("http://localhost:8080/users", { @@ -10,6 +11,7 @@ export function createUser(userData) { resolve({ data }); }); } +// check user export function checkUser(loginInfo) { return new Promise(async (resolve, reject) => { const email = loginInfo.email; @@ -31,3 +33,10 @@ export function checkUser(loginInfo) { // TODO:on server it will only return some info of user (not password) }); } +// remove user +export function signOut(userId) { + return new Promise(async (resolve) => { + // TODO: on server we will remove user session info + resolve({ data: "success" }); + }); +} diff --git a/src/features/auth/authSlice.js b/src/features/auth/authSlice.js index d3d5ffa..b62bebf 100644 --- a/src/features/auth/authSlice.js +++ b/src/features/auth/authSlice.js @@ -1,5 +1,5 @@ import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; -import { checkUser, createUser } from "./authAPI"; +import { checkUser, createUser, signOut } from "./authAPI"; import { updateUser } from "../user/userAPI"; const initialState = { @@ -32,6 +32,11 @@ export const checkUserAsync = createAsyncThunk( return response.data; } ); +export const signOutAsync = createAsyncThunk("user/signOut", async (userId) => { + const response = await signOut(userId); + // The value we return becomes the `fulfilled` action payload + return response.data; +}); export const authSlice = createSlice({ name: "user", @@ -67,6 +72,13 @@ export const authSlice = createSlice({ .addCase(updateUserAsync.fulfilled, (state, action) => { state.status = "idle"; state.loggedInUser = action.payload; + }) + .addCase(signOutAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(signOutAsync.fulfilled, (state, action) => { + state.status = "idle"; + state.loggedInUser = null; }); }, }); diff --git a/src/features/auth/components/ForgotPassword.js b/src/features/auth/components/ForgotPassword.js new file mode 100644 index 0000000..ac244a1 --- /dev/null +++ b/src/features/auth/components/ForgotPassword.js @@ -0,0 +1,84 @@ +import { useForm } from "react-hook-form"; +import { Link } from "react-router-dom"; + +export default function ForgotPassword() { + const { + register, + handleSubmit, + formState: { errors }, + } = useForm(); + + console.log(errors); + return ( + <> + <div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8"> + <div className="sm:mx-auto sm:w-full sm:max-w-sm"> + <img + className="mx-auto h-10 w-auto" + src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=600" + alt="Your Company" + /> + <h2 className="mt-10 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900"> + Enter Email to Reset Password + </h2> + </div> + + <div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm"> + <form + noValidate + className="space-y-6" + onSubmit={handleSubmit((data) => { + console.log(data); + // TODO: implementation on backend with email + })} + > + <div> + <label + htmlFor="email" + className="block text-sm font-medium leading-6 text-gray-900" + > + Email address + </label> + <div className="mt-2"> + <input + id="email" + {...register("email", { + required: "email is required", + pattern: { + value: /\b[\w\.-]+@[\w\.-]+\.\w{2,4}\b/gi, + message: "email is not valid", + }, + })} + type="email" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + /> + {errors.email && ( + <p className="text-red-500">{errors.email.message}</p> + )} + </div> + </div> + + <div> + <button + type="submit" + className="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 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" + > + Log in + </button> + </div> + </form> + + <p className="mt-10 text-center text-sm text-gray-500"> + Send me back to{" "} + <Link + to="/login" + className="font-semibold leading-6 text-indigo-600 hover:text-indigo-500" + > + Login + </Link> + </p> + </div> + </div> + </> + ); +} diff --git a/src/features/auth/components/LogOut.js b/src/features/auth/components/LogOut.js new file mode 100644 index 0000000..d024915 --- /dev/null +++ b/src/features/auth/components/LogOut.js @@ -0,0 +1,16 @@ +import { useEffect } from "react"; +import { selectLoggedInUser, signOutAsync } from "../authSlice"; +import { useDispatch, useSelector } from "react-redux"; +import { Navigate } from "react-router-dom"; + +function LogOut() { + const dispatch = useDispatch(); + const user = useSelector(selectLoggedInUser); + useEffect(() => { + dispatch(signOutAsync()); + }); + //but useEffect runs after render, so we have to delay + return <>{!user && <Navigate to="/login" replace={true}></Navigate>}</>; +} + +export default LogOut; diff --git a/src/features/auth/components/Login.js b/src/features/auth/components/Login.js index 711d9da..5205b94 100644 --- a/src/features/auth/components/Login.js +++ b/src/features/auth/components/Login.js @@ -76,12 +76,12 @@ export default function Login() { Password </label> <div className="text-sm"> - <a - href="#" + <Link + to="/forgot-paasword" className="font-semibold text-indigo-600 hover:text-indigo-500" > Forgot password? - </a> + </Link> </div> </div> <div className="mt-2"> diff --git a/src/features/auth/components/Signup.js b/src/features/auth/components/Signup.js index ac057c6..cea7eca 100644 --- a/src/features/auth/components/Signup.js +++ b/src/features/auth/components/Signup.js @@ -79,12 +79,12 @@ export default function Signup() { Password </label> <div className="text-sm"> - <a - href="#" + <Link + to="/forgot-paasword" className="font-semibold text-indigo-600 hover:text-indigo-500" > Forgot password? - </a> + </Link> </div> </div> <div className="mt-2"> diff --git a/src/features/navbar/Navbar.js b/src/features/navbar/Navbar.js index b930271..507f44d 100644 --- a/src/features/navbar/Navbar.js +++ b/src/features/navbar/Navbar.js @@ -22,7 +22,7 @@ const navigation = [ const userNavigation = [ { name: "My Profile", link: "/profile" }, { name: "My Orders", link: "/orders" }, - { name: "Sign out", link: "/login" }, + { name: "Sign out", link: "/logout" }, ]; function classNames(...classes) { diff --git a/src/features/user/components/UserProfile.js b/src/features/user/components/UserProfile.js index 96ce7f9..9ff6692 100644 --- a/src/features/user/components/UserProfile.js +++ b/src/features/user/components/UserProfile.js @@ -8,6 +8,8 @@ export default function UserProfile() { const user = useSelector(selectUserInfo); const [selectedEditIndex, setSelectedEditIndex] = useState(-1); const [showAddressForm, setshowAddressForm] = useState(false); + + // TODO: we will add payment section when we work on backend const { register, handleSubmit, diff --git a/src/pages/ForgotPasswordPage.js b/src/pages/ForgotPasswordPage.js new file mode 100644 index 0000000..f05dca0 --- /dev/null +++ b/src/pages/ForgotPasswordPage.js @@ -0,0 +1,10 @@ +import ForgotPassword from "../features/auth/components/ForgotPassword"; +function ForgotPasswordPage() { + return ( + <div> + <ForgotPassword></ForgotPassword> + </div> + ); +} + +export default ForgotPasswordPage; From 22b1dabb8fd8e64c075e0a7fc6b078685279cf47 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Fri, 28 Jun 2024 15:32:32 +0530 Subject: [PATCH 36/47] admin route created --- data.json | 185 ++----- src/App.js | 20 + .../admin/components/AdminProductDetail.js | 351 ++++++++++++ .../admin/components/AdminProductList.js | 522 ++++++++++++++++++ src/features/admin/productSlice.js | 120 ++++ .../auth/components/ProtectedAdmin.js | 15 + src/features/auth/components/Signup.js | 2 + src/features/navbar/Navbar.js | 80 +-- src/features/user/components/UserProfile.js | 5 + src/pages/AdimHome.js | 14 + src/pages/AdminProductDetailPage.js | 13 + 11 files changed, 1151 insertions(+), 176 deletions(-) create mode 100644 src/features/admin/components/AdminProductDetail.js create mode 100644 src/features/admin/components/AdminProductList.js create mode 100644 src/features/admin/productSlice.js create mode 100644 src/features/auth/components/ProtectedAdmin.js create mode 100644 src/pages/AdimHome.js create mode 100644 src/pages/AdminProductDetailPage.js diff --git a/data.json b/data.json index 9b27d6c..76be797 100644 --- a/data.json +++ b/data.json @@ -9,10 +9,7 @@ "discountPercentage": 7.17, "rating": 4.94, "stock": 5, - "tags": [ - "beauty", - "mascara" - ], + "tags": ["beauty", "mascara"], "brand": "Essence", "sku": "RCH45Q1A", "weight": 2, @@ -69,10 +66,7 @@ "discountPercentage": 5.5, "rating": 3.28, "stock": 44, - "tags": [ - "beauty", - "eyeshadow" - ], + "tags": ["beauty", "eyeshadow"], "brand": "Glamour Beauty", "sku": "MVCFH27F", "weight": 3, @@ -129,10 +123,7 @@ "discountPercentage": 18.14, "rating": 3.82, "stock": 59, - "tags": [ - "beauty", - "face powder" - ], + "tags": ["beauty", "face powder"], "brand": "Velvet Touch", "sku": "9EN8WLT2", "weight": 8, @@ -189,10 +180,7 @@ "discountPercentage": 19.03, "rating": 2.51, "stock": 68, - "tags": [ - "beauty", - "lipstick" - ], + "tags": ["beauty", "lipstick"], "brand": "Chic Cosmetics", "sku": "O5IF1NTA", "weight": 5, @@ -249,10 +237,7 @@ "discountPercentage": 2.46, "rating": 3.91, "stock": 71, - "tags": [ - "beauty", - "nail polish" - ], + "tags": ["beauty", "nail polish"], "brand": "Nail Couture", "sku": "YUIIIP4W", "weight": 9, @@ -309,10 +294,7 @@ "discountPercentage": 0.32, "rating": 4.85, "stock": 17, - "tags": [ - "fragrances", - "perfumes" - ], + "tags": ["fragrances", "perfumes"], "brand": "Calvin Klein", "sku": "DZM2JQZE", "weight": 5, @@ -371,10 +353,7 @@ "discountPercentage": 18.64, "rating": 2.76, "stock": 41, - "tags": [ - "fragrances", - "perfumes" - ], + "tags": ["fragrances", "perfumes"], "brand": "Chanel", "sku": "K71HBCGS", "weight": 4, @@ -433,10 +412,7 @@ "discountPercentage": 17.44, "rating": 3.31, "stock": 91, - "tags": [ - "fragrances", - "perfumes" - ], + "tags": ["fragrances", "perfumes"], "brand": "Dior", "sku": "E70NB03B", "weight": 10, @@ -495,10 +471,7 @@ "discountPercentage": 11.47, "rating": 2.68, "stock": 3, - "tags": [ - "fragrances", - "perfumes" - ], + "tags": ["fragrances", "perfumes"], "brand": "Dolce & Gabbana", "sku": "1NBFK980", "weight": 5, @@ -557,10 +530,7 @@ "discountPercentage": 8.9, "rating": 2.69, "stock": 93, - "tags": [ - "fragrances", - "perfumes" - ], + "tags": ["fragrances", "perfumes"], "brand": "Gucci", "sku": "FFKZ6HOF", "weight": 10, @@ -619,10 +589,7 @@ "discountPercentage": 0.29, "rating": 4.14, "stock": 47, - "tags": [ - "furniture", - "beds" - ], + "tags": ["furniture", "beds"], "brand": "Annibale Colombo", "sku": "4KMDTZWF", "weight": 3, @@ -681,10 +648,7 @@ "discountPercentage": 18.54, "rating": 3.08, "stock": 16, - "tags": [ - "furniture", - "sofas" - ], + "tags": ["furniture", "sofas"], "brand": "Annibale Colombo", "sku": "LUU95CQP", "weight": 3, @@ -743,10 +707,7 @@ "discountPercentage": 9.58, "rating": 4.48, "stock": 16, - "tags": [ - "furniture", - "bedside tables" - ], + "tags": ["furniture", "bedside tables"], "brand": "Furniture Co.", "sku": "OWPLTZYX", "weight": 10, @@ -805,10 +766,7 @@ "discountPercentage": 15.23, "rating": 4.11, "stock": 47, - "tags": [ - "furniture", - "office chairs" - ], + "tags": ["furniture", "office chairs"], "brand": "Knoll", "sku": "RKHVJ4FE", "weight": 3, @@ -867,10 +825,7 @@ "discountPercentage": 11.22, "rating": 3.26, "stock": 95, - "tags": [ - "furniture", - "bathroom" - ], + "tags": ["furniture", "bathroom"], "brand": "Bath Trends", "sku": "7OLTIEVO", "weight": 6, @@ -929,9 +884,7 @@ "discountPercentage": 1.97, "rating": 2.96, "stock": 9, - "tags": [ - "fruits" - ], + "tags": ["fruits"], "sku": "QTROUV79", "weight": 8, "dimensions": { @@ -987,9 +940,7 @@ "discountPercentage": 17.99, "rating": 2.83, "stock": 96, - "tags": [ - "meat" - ], + "tags": ["meat"], "sku": "BWWA2MSO", "weight": 9, "dimensions": { @@ -1045,10 +996,7 @@ "discountPercentage": 9.57, "rating": 2.88, "stock": 13, - "tags": [ - "pet supplies", - "cat food" - ], + "tags": ["pet supplies", "cat food"], "sku": "C3F8QN6O", "weight": 9, "dimensions": { @@ -1104,9 +1052,7 @@ "discountPercentage": 10.46, "rating": 4.61, "stock": 69, - "tags": [ - "meat" - ], + "tags": ["meat"], "sku": "G5YEHW7B", "weight": 7, "dimensions": { @@ -1163,9 +1109,7 @@ "discountPercentage": 18.89, "rating": 4.01, "stock": 22, - "tags": [ - "cooking essentials" - ], + "tags": ["cooking essentials"], "sku": "Q6ZP1UY8", "weight": 8, "dimensions": { @@ -1221,9 +1165,7 @@ "discountPercentage": 11.44, "rating": 4.71, "stock": 22, - "tags": [ - "vegetables" - ], + "tags": ["vegetables"], "sku": "6KGF2K6Z", "weight": 9, "dimensions": { @@ -1279,10 +1221,7 @@ "discountPercentage": 18.15, "rating": 2.74, "stock": 40, - "tags": [ - "pet supplies", - "dog food" - ], + "tags": ["pet supplies", "dog food"], "sku": "A6QRCH37", "weight": 2, "dimensions": { @@ -1338,9 +1277,7 @@ "discountPercentage": 5.8, "rating": 4.46, "stock": 10, - "tags": [ - "dairy" - ], + "tags": ["dairy"], "sku": "YA617RI7", "weight": 4, "dimensions": { @@ -1396,9 +1333,7 @@ "discountPercentage": 7, "rating": 4.83, "stock": 99, - "tags": [ - "seafood" - ], + "tags": ["seafood"], "sku": "XNIH1MTA", "weight": 8, "dimensions": { @@ -1454,9 +1389,7 @@ "discountPercentage": 15.5, "rating": 4.28, "stock": 89, - "tags": [ - "vegetables" - ], + "tags": ["vegetables"], "sku": "HU7S7VQ0", "weight": 7, "dimensions": { @@ -1512,9 +1445,7 @@ "discountPercentage": 18.51, "rating": 4.43, "stock": 8, - "tags": [ - "vegetables" - ], + "tags": ["vegetables"], "sku": "Y4RM3JCB", "weight": 2, "dimensions": { @@ -1570,9 +1501,7 @@ "discountPercentage": 1.91, "rating": 3.5, "stock": 25, - "tags": [ - "condiments" - ], + "tags": ["condiments"], "sku": "BTBNIIOU", "weight": 9, "dimensions": { @@ -1628,9 +1557,7 @@ "discountPercentage": 7.58, "rating": 3.77, "stock": 76, - "tags": [ - "desserts" - ], + "tags": ["desserts"], "sku": "VEZMU1EQ", "weight": 5, "dimensions": { @@ -1689,9 +1616,7 @@ "discountPercentage": 5.45, "rating": 3.41, "stock": 99, - "tags": [ - "beverages" - ], + "tags": ["beverages"], "sku": "M2K19S06", "weight": 2, "dimensions": { @@ -1747,9 +1672,7 @@ "discountPercentage": 10.32, "rating": 4.37, "stock": 1, - "tags": [ - "fruits" - ], + "tags": ["fruits"], "sku": "0X3NORB9", "weight": 8, "dimensions": { @@ -1914,6 +1837,7 @@ "id": "cb1b", "email": "test@123.com", "password": "Murtaza123", + "role": "user", "addresses": [ { "name": "Murtaza ", @@ -1941,6 +1865,7 @@ "id": "0afe", "email": "test2@gmail.com", "password": "Tankiwala123", + "role": "user", "addresses": [ { "name": "Ammar ", @@ -1952,6 +1877,13 @@ "pinCode": "4520012" } ] + }, + { + "id": "86e2", + "email": "admin@gmail.com", + "password": "Admin123", + "addresses": [], + "role": "admin" } ], "cart": [], @@ -1968,10 +1900,7 @@ "discountPercentage": 19.03, "rating": 2.51, "stock": 68, - "tags": [ - "beauty", - "lipstick" - ], + "tags": ["beauty", "lipstick"], "brand": "Chic Cosmetics", "sku": "O5IF1NTA", "weight": 5, @@ -2030,10 +1959,7 @@ "discountPercentage": 18.14, "rating": 3.82, "stock": 59, - "tags": [ - "beauty", - "face powder" - ], + "tags": ["beauty", "face powder"], "brand": "Velvet Touch", "sku": "9EN8WLT2", "weight": 8, @@ -2135,10 +2061,7 @@ "discountPercentage": 5.5, "rating": 3.28, "stock": 44, - "tags": [ - "beauty", - "eyeshadow" - ], + "tags": ["beauty", "eyeshadow"], "brand": "Glamour Beauty", "sku": "MVCFH27F", "weight": 3, @@ -2197,10 +2120,7 @@ "discountPercentage": 19.03, "rating": 2.51, "stock": 68, - "tags": [ - "beauty", - "lipstick" - ], + "tags": ["beauty", "lipstick"], "brand": "Chic Cosmetics", "sku": "O5IF1NTA", "weight": 5, @@ -2293,10 +2213,7 @@ "discountPercentage": 5.5, "rating": 3.28, "stock": 44, - "tags": [ - "beauty", - "eyeshadow" - ], + "tags": ["beauty", "eyeshadow"], "brand": "Glamour Beauty", "sku": "MVCFH27F", "weight": 3, @@ -2355,9 +2272,7 @@ "discountPercentage": 5.8, "rating": 4.46, "stock": 10, - "tags": [ - "dairy" - ], + "tags": ["dairy"], "sku": "YA617RI7", "weight": 4, "dimensions": { @@ -2458,9 +2373,7 @@ "discountPercentage": 7, "rating": 4.83, "stock": 99, - "tags": [ - "seafood" - ], + "tags": ["seafood"], "sku": "XNIH1MTA", "weight": 8, "dimensions": { @@ -2564,9 +2477,7 @@ "discountPercentage": 18.89, "rating": 4.01, "stock": 22, - "tags": [ - "cooking essentials" - ], + "tags": ["cooking essentials"], "sku": "Q6ZP1UY8", "weight": 8, "dimensions": { @@ -2659,4 +2570,4 @@ "status": "pending" } ] -} \ No newline at end of file +} diff --git a/src/App.js b/src/App.js index 1f14688..9ffd0a8 100644 --- a/src/App.js +++ b/src/App.js @@ -26,6 +26,9 @@ import UserProfilePage from "./pages/UserProfilePage"; import { fetchLoggedInUserAsync } from "./features/user/userSlice"; import LogOut from "./features/auth/components/LogOut"; import ForgotPasswordPage from "./pages/ForgotPasswordPage"; +import ProtectedAdmin from "./features/auth/components/ProtectedAdmin"; +import AdminHome from "./pages/AdimHome"; +import AdminProductDetail from "./features/admin/components/AdminProductDetail"; const router = createBrowserRouter([ { path: "/", @@ -36,6 +39,15 @@ const router = createBrowserRouter([ </Protected> ), }, + { + path: "/admin", + + element: ( + <ProtectedAdmin> + <AdminHome></AdminHome> + </ProtectedAdmin> + ), + }, { path: "/login", element: <LoginPage></LoginPage>, @@ -68,6 +80,14 @@ const router = createBrowserRouter([ </Protected> ), }, + { + path: "/admin/product-detail/:id", // :id provided by react-router + element: ( + <ProtectedAdmin> + <AdminProductDetail></AdminProductDetail> + </ProtectedAdmin> + ), + }, { path: "/order-success/:id", element: <OrderSuccessPage></OrderSuccessPage>, diff --git a/src/features/admin/components/AdminProductDetail.js b/src/features/admin/components/AdminProductDetail.js new file mode 100644 index 0000000..32ba573 --- /dev/null +++ b/src/features/admin/components/AdminProductDetail.js @@ -0,0 +1,351 @@ +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 "../../product/productSlice"; +import { useParams } from "react-router-dom"; +import { addToCartAsync } from "../../cart/cartSlice"; +import { selectLoggedInUser } from "../../auth/authSlice"; + +// TODO: In server data we will add sizes,colors, highlights to each product + +const colors = [ + { name: "White", class: "bg-white", selectedClass: "ring-gray-400" }, + { name: "Gray", class: "bg-gray-200", selectedClass: "ring-gray-400" }, + { name: "Black", class: "bg-gray-900", selectedClass: "ring-gray-900" }, +]; +const sizes = [ + { name: "XXS", inStock: false }, + { name: "XS", inStock: true }, + { name: "S", inStock: true }, + { name: "M", inStock: true }, + { name: "L", inStock: true }, + { name: "XL", inStock: true }, + { name: "2XL", inStock: true }, + { name: "3XL", inStock: true }, +]; +const highlights = [ + "Hand cut and sewn locally", + "Dyed with our proprietary colors", + "Pre-washed & pre-shrunk", + "Ultra-soft 100% cotton", +]; + +function classNames(...classes) { + return classes.filter(Boolean).join(" "); +} + +export default function AdminProductDetail() { + const [selectedColor, setSelectedColor] = useState(colors[0]); + const [selectedSize, setSelectedSize] = useState(sizes[2]); + const user = useSelector(selectLoggedInUser); + const product = useSelector(selectProductById); + const dispatch = useDispatch(); + const params = useParams(); // hook provided by react-router to fetch parameters + + const handleCart = (e) => { + e.preventDefault(); + const newItem = { ...product, quantity: 1, user: user.id }; + delete newItem["id"]; // to avoid creating same id on adding same product to cart.... + dispatch(addToCartAsync(newItem)); + }; + + useEffect(() => { + dispatch(fetchAllProductsIdAsync(params.id)); // :id from path in app.js + }, [dispatch, params.id]); + return ( + <div className="bg-white"> + {product && ( + <div className="pt-6"> + <nav aria-label="Breadcrumb"> + <ol + role="list" + className="mx-auto flex max-w-2xl items-center space-x-2 px-4 sm:px-6 lg:max-w-7xl lg:px-8" + > + {product.breadcrumbs && + product.breadcrumbs.map((breadcrumb) => ( + <li key={breadcrumb.id}> + <div className="flex items-center"> + <a + href={breadcrumb.href} + className="mr-2 text-sm font-medium text-gray-900" + > + {breadcrumb.name} + </a> + <svg + width={16} + height={20} + viewBox="0 0 16 20" + fill="currentColor" + aria-hidden="true" + className="h-5 w-4 text-gray-300" + > + <path d="M5.697 4.34L8.98 16.532h1.327L7.025 4.341H5.697z" /> + </svg> + </div> + </li> + ))} + <li className="text-sm"> + <a + href={product.href} + aria-current="page" + className="font-medium text-gray-500 hover:text-gray-600" + > + {product.title} + </a> + </li> + </ol> + </nav> + + {/* Image gallery */} + <div className="mx-auto mt-6 max-w-2xl sm:px-6 lg:grid lg:max-w-7xl lg:grid-cols-3 lg:gap-x-8 lg:px-8"> + <div className="aspect-h-4 aspect-w-3 hidden overflow-hidden rounded-lg lg:block"> + <img + src={product.images[0]} + alt={product.title} + className="h-full w-full object-cover object-center" + /> + </div> + <div className="hidden lg:grid lg:grid-cols-1 lg:gap-y-8"> + <div className="aspect-h-2 aspect-w-3 overflow-hidden rounded-lg"> + <img + src={product.images[0]} + alt={product.title} + className="h-full w-full object-cover object-center" + /> + </div> + <div className="aspect-h-2 aspect-w-3 overflow-hidden rounded-lg"> + <img + src={product.images[0]} + alt={product.title} + className="h-full w-full object-cover object-center" + /> + </div> + </div> + <div className="aspect-h-5 aspect-w-4 lg:aspect-h-4 lg:aspect-w-3 sm:overflow-hidden sm:rounded-lg"> + <img + src={product.images[0]} + alt={product.title} + className="h-full w-full object-cover object-center" + /> + </div> + </div> + + {/* Product info */} + <div className="mx-auto max-w-2xl px-4 pb-16 pt-10 sm:px-6 lg:grid lg:max-w-7xl lg:grid-cols-3 lg:grid-rows-[auto,auto,1fr] lg:gap-x-8 lg:px-8 lg:pb-24 lg:pt-16"> + <div className="lg:col-span-2 lg:border-r lg:border-gray-200 lg:pr-8"> + <h1 className="text-2xl font-bold tracking-tight text-gray-900 sm:text-3xl"> + {product.title} + </h1> + </div> + + {/* Options */} + <div className="mt-4 lg:row-span-3 lg:mt-0"> + <h2 className="sr-only">Product information</h2> + <p className="text-3xl tracking-tight text-gray-900"> + ${product.price} + </p> + + {/* Reviews */} + <div className="mt-6"> + <h3 className="sr-only">Reviews</h3> + <div className="flex items-center"> + <div className="flex items-center"> + {[0, 1, 2, 3, 4].map((rating) => ( + <StarIcon + key={rating} + className={classNames( + product.rating > rating + ? "text-gray-900" + : "text-gray-200", + "h-5 w-5 flex-shrink-0" + )} + aria-hidden="true" + /> + ))} + </div> + <p className="sr-only">{product.rating} out of 5 stars</p> + </div> + </div> + + <form className="mt-10"> + {/* Colors */} + <div> + <h3 className="text-sm font-medium text-gray-900">Color</h3> + + <RadioGroup + value={selectedColor} + onChange={setSelectedColor} + className="mt-4" + > + <RadioGroup.Label className="sr-only"> + Choose a color + </RadioGroup.Label> + <div className="flex items-center space-x-3"> + {colors.map((color) => ( + <RadioGroup.Option + key={color.name} + value={color} + className={({ active, checked }) => + classNames( + color.selectedClass, + active && checked ? "ring ring-offset-1" : "", + !active && checked ? "ring-2" : "", + "relative -m-0.5 flex cursor-pointer items-center justify-center rounded-full p-0.5 focus:outline-none" + ) + } + > + <RadioGroup.Label as="span" className="sr-only"> + {color.name} + </RadioGroup.Label> + <span + aria-hidden="true" + className={classNames( + color.class, + "h-8 w-8 rounded-full border border-black border-opacity-10" + )} + /> + </RadioGroup.Option> + ))} + </div> + </RadioGroup> + </div> + + {/* Sizes */} + <div className="mt-10"> + <div className="flex items-center justify-between"> + <h3 className="text-sm font-medium text-gray-900">Size</h3> + <a + href="#" + className="text-sm font-medium text-indigo-600 hover:text-indigo-500" + > + Size guide + </a> + </div> + + <RadioGroup + value={selectedSize} + onChange={setSelectedSize} + className="mt-4" + > + <RadioGroup.Label className="sr-only"> + Choose a size + </RadioGroup.Label> + <div className="grid grid-cols-4 gap-4 sm:grid-cols-8 lg:grid-cols-4"> + {sizes.map((size) => ( + <RadioGroup.Option + key={size.name} + value={size} + disabled={!size.inStock} + className={({ active }) => + classNames( + size.inStock + ? "cursor-pointer bg-white text-gray-900 shadow-sm" + : "cursor-not-allowed bg-gray-50 text-gray-200", + active ? "ring-2 ring-indigo-500" : "", + "group relative flex items-center justify-center rounded-md border py-3 px-4 text-sm font-medium uppercase hover:bg-gray-50 focus:outline-none sm:flex-1 sm:py-6" + ) + } + > + {({ active, checked }) => ( + <> + <RadioGroup.Label as="span"> + {size.name} + </RadioGroup.Label> + {size.inStock ? ( + <span + className={classNames( + active ? "border" : "border-2", + checked + ? "border-indigo-500" + : "border-transparent", + "pointer-events-none absolute -inset-px rounded-md" + )} + aria-hidden="true" + /> + ) : ( + <span + aria-hidden="true" + className="pointer-events-none absolute -inset-px rounded-md border-2 border-gray-200" + > + <svg + className="absolute inset-0 h-full w-full stroke-2 text-gray-200" + viewBox="0 0 100 100" + preserveAspectRatio="none" + stroke="currentColor" + > + <line + x1={0} + y1={100} + x2={100} + y2={0} + vectorEffect="non-scaling-stroke" + /> + </svg> + </span> + )} + </> + )} + </RadioGroup.Option> + ))} + </div> + </RadioGroup> + </div> + + <button + onClick={(e) => { + handleCart(e); + }} + type="submit" + className="mt-10 flex w-full items-center justify-center rounded-md border border-transparent bg-indigo-600 px-8 py-3 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2" + > + Add to Cart + </button> + </form> + </div> + + <div className="py-10 lg:col-span-2 lg:col-start-1 lg:border-r lg:border-gray-200 lg:pb-16 lg:pr-8 lg:pt-6"> + {/* Description and details */} + <div> + <h3 className="sr-only">Description</h3> + + <div className="space-y-6"> + <p className="text-base text-gray-900"> + {product.description} + </p> + </div> + </div> + + <div className="mt-10"> + <h3 className="text-sm font-medium text-gray-900"> + Highlights + </h3> + + <div className="mt-4"> + <ul role="list" className="list-disc space-y-2 pl-4 text-sm"> + {highlights.map((highlight) => ( + <li key={highlight} className="text-gray-400"> + <span className="text-gray-600">{highlight}</span> + </li> + ))} + </ul> + </div> + </div> + + <div className="mt-10"> + <h2 className="text-sm font-medium text-gray-900">Details</h2> + + <div className="mt-4 space-y-6"> + <p className="text-sm text-gray-600">{product.description}</p> + </div> + </div> + </div> + </div> + </div> + )} + </div> + ); +} diff --git a/src/features/admin/components/AdminProductList.js b/src/features/admin/components/AdminProductList.js new file mode 100644 index 0000000..cb88063 --- /dev/null +++ b/src/features/admin/components/AdminProductList.js @@ -0,0 +1,522 @@ +import React, { useState, Fragment, useEffect } from "react"; +import { useSelector, useDispatch } from "react-redux"; +import { + fetchAllProductsAsync, + fetchBrandsAsync, + fetchCategoriesAsync, + fetchProductsByFiltersAsync, + selectAllProducts, + selectBrands, + selectCategories, + selectTotalItems, +} from "../../product/productSlice"; +import { Dialog, Disclosure, Menu, Transition } from "@headlessui/react"; +import { StarIcon, XMarkIcon } from "@heroicons/react/24/outline"; +import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/20/solid"; +import { Link } from "react-router-dom"; +import { + ChevronDownIcon, + FunnelIcon, + MinusIcon, + PlusIcon, + Squares2X2Icon, +} from "@heroicons/react/20/solid"; +import { ITEMS_PER_PAGE } from "../../../app/constants"; + +const sortOptions = [ + { name: "Best Rating", sort: "rating", current: false }, + { name: "Price: Low to High", sort: "price", order: "asc", current: false }, + { name: "Price: High to Low", sort: "price", order: "desc", current: false }, +]; + +function classNames(...classes) { + return classes.filter(Boolean).join(" "); +} + +export default function AdminProductList() { + const [mobileFiltersOpen, setMobileFiltersOpen] = useState(false); + const products = useSelector(selectAllProducts); + const categories = useSelector(selectCategories); + const brands = useSelector(selectBrands); + const totalItems = useSelector(selectTotalItems); + const filters = [ + { + id: "category", + name: "Category", + options: categories, + }, + { + id: "brand", + name: "Brands", + options: brands, + }, + ]; + + const dispatch = useDispatch(); + const [filter, setFilter] = useState({}); + const [sort, setSort] = useState({}); + const [page, setPage] = useState(1); + + const handleFilter = (e, section, option) => { + console.log(e.target.checked); + // TODO: we will on server support mutilple value + // remove obj when input box is unchecked + const newFilter = { ...filter }; + if (e.target.checked) { + if (newFilter[section.id]) { + newFilter[section.id].push(option.value); //{"category":["frangrances","furniture"]} + } else { + newFilter[section.id] = [option.value]; //[] + } + } else { + // delete array item after unchecked... + const index = newFilter[section.id].findIndex( + (el) => el === option.value + ); + newFilter[section.id].splice(index, 1); + } + console.log({ newFilter }); + setFilter(newFilter); + }; + const handleSort = (e, option) => { + const sort = { + _sort: option.order === "desc" ? `-${option.sort}` : option.sort, //{_sort:"price", order="desc"} + }; + console.log(sort); + setSort(sort); + }; + const handlePage = (page) => { + console.log({ page }); + setPage(page); + }; + // making API call when dispatch or when filter is applied in a one go.... + useEffect(() => { + const pagination = { _page: page, _per_page: ITEMS_PER_PAGE }; + dispatch(fetchProductsByFiltersAsync({ filter, sort, pagination })); + }, [dispatch, filter, sort, page]); + + useEffect(() => { + //page always come to first if there is change in totalItems and sorting + setPage(1); + }, [totalItems, sort]); + + useEffect(() => { + dispatch(fetchBrandsAsync()); + dispatch(fetchCategoriesAsync()); + }, []); + + return ( + <div className="bg-white"> + <div> + {/* Mobile filter dialog */} + <MobileFilter + handleFilter={handleFilter} + mobileFiltersOpen={mobileFiltersOpen} + setMobileFiltersOpen={setMobileFiltersOpen} + filters={filters} + /> + + <main className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"> + <div className="flex items-baseline justify-between border-b border-gray-200 pb-6 pt-24"> + <h1 className="text-4xl font-bold tracking-tight text-gray-900"> + All Products + </h1> + + <div className="flex items-center"> + <Menu as="div" className="relative inline-block text-left"> + <div> + <Menu.Button className="group inline-flex justify-center text-sm font-medium text-gray-700 hover:text-gray-900"> + Sort + <ChevronDownIcon + className="-mr-1 ml-1 h-5 w-5 flex-shrink-0 text-gray-400 group-hover:text-gray-500" + aria-hidden="true" + /> + </Menu.Button> + </div> + + <Transition + as={Fragment} + enter="transition ease-out duration-100" + enterFrom="transform opacity-0 scale-95" + enterTo="transform opacity-100 scale-100" + leave="transition ease-in duration-75" + leaveFrom="transform opacity-100 scale-100" + leaveTo="transform opacity-0 scale-95" + > + <Menu.Items className="absolute right-0 z-10 mt-2 w-40 origin-top-right rounded-md bg-white shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none"> + <div className="py-1"> + {sortOptions.map((option) => ( + <Menu.Item key={option.name}> + {({ active }) => ( + <p + onClick={(e) => { + handleSort(e, option); + }} + className={classNames( + option.current + ? "font-medium text-gray-900" + : "text-gray-500", + active ? "bg-gray-100" : "", + "block px-4 py-2 text-sm" + )} + > + {option.name} + </p> + )} + </Menu.Item> + ))} + </div> + </Menu.Items> + </Transition> + </Menu> + + <button + type="button" + className="-m-2 ml-5 p-2 text-gray-400 hover:text-gray-500 sm:ml-7" + > + <span className="sr-only">View grid</span> + <Squares2X2Icon className="h-5 w-5" aria-hidden="true" /> + </button> + <button + type="button" + className="-m-2 ml-4 p-2 text-gray-400 hover:text-gray-500 sm:ml-6 lg:hidden" + onClick={() => setMobileFiltersOpen(true)} + > + <span className="sr-only">Filters</span> + <FunnelIcon className="h-5 w-5" aria-hidden="true" /> + </button> + </div> + </div> + + <section aria-labelledby="products-heading" className="pb-24 pt-6"> + <h2 id="products-heading" className="sr-only"> + Products + </h2> + + <div className="grid grid-cols-1 gap-x-8 gap-y-10 lg:grid-cols-4"> + <DesktopFilter handleFilter={handleFilter} filters={filters} /> + + {/* Product grid */} + <div className="lg:col-span-3"> + {/* This is our products list */} + <ProductGrid products={products} /> + </div> + {/* Product grid end */} + </div> + </section> + + {/* section of product and filters ends */} + + <Pagination + page={page} + setPage={setPage} + handlePage={handlePage} + totalItems={totalItems} + /> + </main> + </div> + </div> + ); +} +function MobileFilter({ + mobileFiltersOpen, + setMobileFiltersOpen, + handleFilter, + filters, +}) { + return ( + <Transition.Root show={mobileFiltersOpen} as={Fragment}> + <Dialog + as="div" + className="relative z-40 lg:hidden" + onClose={setMobileFiltersOpen} + > + <Transition.Child + as={Fragment} + enter="transition-opacity ease-linear duration-300" + enterFrom="opacity-0" + enterTo="opacity-100" + leave="transition-opacity ease-linear duration-300" + leaveFrom="opacity-100" + leaveTo="opacity-0" + > + <div className="fixed inset-0 bg-black bg-opacity-25" /> + </Transition.Child> + + <div className="fixed inset-0 z-40 flex"> + <Transition.Child + as={Fragment} + enter="transition ease-in-out duration-300 transform" + enterFrom="translate-x-full" + enterTo="translate-x-0" + leave="transition ease-in-out duration-300 transform" + leaveFrom="translate-x-0" + leaveTo="translate-x-full" + > + <Dialog.Panel className="relative ml-auto flex h-full w-full max-w-xs flex-col overflow-y-auto bg-white py-4 pb-12 shadow-xl"> + <div className="flex items-center justify-between px-4"> + <h2 className="text-lg font-medium text-gray-900">Filters</h2> + <button + type="button" + className="-mr-2 flex h-10 w-10 items-center justify-center rounded-md bg-white p-2 text-gray-400" + onClick={() => setMobileFiltersOpen(false)} + > + <span className="sr-only">Close menu</span> + <XMarkIcon className="h-6 w-6" aria-hidden="true" /> + </button> + </div> + + {/* Filters */} + <form className="mt-4 border-t border-gray-200"> + {filters.map((section) => ( + <Disclosure + as="div" + key={section.id} + className="border-t border-gray-200 px-4 py-6" + > + {({ open }) => ( + <> + <h3 className="-mx-2 -my-3 flow-root"> + <Disclosure.Button className="flex w-full items-center justify-between bg-white px-2 py-3 text-gray-400 hover:text-gray-500"> + <span className="font-medium text-gray-900"> + {section.name} + </span> + <span className="ml-6 flex items-center"> + {open ? ( + <MinusIcon + className="h-5 w-5" + aria-hidden="true" + /> + ) : ( + <PlusIcon + className="h-5 w-5" + aria-hidden="true" + /> + )} + </span> + </Disclosure.Button> + </h3> + <Disclosure.Panel className="pt-6"> + <div className="space-y-6"> + {section.options.map((option, optionIdx) => ( + <div + key={option.value} + className="flex items-center" + > + <input + id={`filter-mobile-${section.id}-${optionIdx}`} + name={`${section.id}[]`} + defaultValue={option.value} + type="checkbox" + defaultChecked={option.checked} + onChange={(e) => { + handleFilter(e, section, option); + }} + className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500" + /> + <label + htmlFor={`filter-mobile-${section.id}-${optionIdx}`} + className="ml-3 min-w-0 flex-1 text-gray-500" + > + {option.label} + </label> + </div> + ))} + </div> + </Disclosure.Panel> + </> + )} + </Disclosure> + ))} + </form> + </Dialog.Panel> + </Transition.Child> + </div> + </Dialog> + </Transition.Root> + ); +} +function DesktopFilter({ handleFilter, filters }) { + return ( + <form className="hidden lg:block"> + {filters.map((section) => ( + <Disclosure + as="div" + key={section.id} + className="border-b border-gray-200 py-6" + > + {({ open }) => ( + <> + <h3 className="-my-3 flow-root"> + <Disclosure.Button className="flex w-full items-center justify-between bg-white py-3 text-sm text-gray-400 hover:text-gray-500"> + <span className="font-medium text-gray-900"> + {section.name} + </span> + <span className="ml-6 flex items-center"> + {open ? ( + <MinusIcon className="h-5 w-5" aria-hidden="true" /> + ) : ( + <PlusIcon className="h-5 w-5" aria-hidden="true" /> + )} + </span> + </Disclosure.Button> + </h3> + <Disclosure.Panel className="pt-6"> + <div className="space-y-4"> + {section.options.map((option, optionIdx) => ( + <div key={option.value} className="flex items-center"> + <input + id={`filter-${section.id}-${optionIdx}`} + name={`${section.id}[]`} + defaultValue={option.value} + type="checkbox" + defaultChecked={option.checked} + onChange={(e) => { + handleFilter(e, section, option); + }} + className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500" + /> + <label + htmlFor={`filter-${section.id}-${optionIdx}`} + className="ml-3 text-sm text-gray-600" + > + {option.label} + </label> + </div> + ))} + </div> + </Disclosure.Panel> + </> + )} + </Disclosure> + ))} + </form> + ); +} +function ProductGrid({ products }) { + return ( + <div className="bg-white"> + <div className="mx-auto max-w-2xl px-4 py-0 sm:px-6 sm:py-0 lg:max-w-7xl lg:px-8"> + <div className="mt-6 grid grid-cols-1 gap-x-6 gap-y-10 sm:grid-cols-2 lg:grid-cols-3 xl:gap-x-8"> + {products.map((product) => ( + <Link to={`/product-detail/${product.id}`}> + <div key={product.id} className="group relative p-2 border-2"> + <div className="min-h-60 aspect-h-1 aspect-w-1 w-full overflow-hidden rounded-md bg-gray-200 lg:aspect-none group-hover:opacity-75 lg:h-60"> + <img + src={product.thumbnail} + alt={product.title} + className="h-full w-full object-cover object-center lg:h-full lg:w-full" + /> + </div> + <div className="mt-4 flex justify-between"> + <div> + <h3 className="text-sm text-gray-700"> + <div href={product.thumbnail}> + <span aria-hidden="true" className="absolute inset-0" /> + {product.title} + </div> + </h3> + <p className="mt-1 text-sm text-gray-500"> + <StarIcon className="w-6 h-6 inline"></StarIcon> + <span className="align-bottom">{product.rating}</span> + </p> + </div> + <div> + <p className="text-sm font-medium text-gray-900"> + $ + {Math.round( + product.price * (1 - product.discountPercentage / 100) + )} + <p className="text-sm line-through font-medium text-gray-400"> + ${product.price} + </p> + </p> + </div> + </div> + </div> + </Link> + ))} + </div> + </div> + </div> + ); +} + +function Pagination({ page, setPage, handlePage, totalItems }) { + const totalPages = Math.ceil(totalItems / ITEMS_PER_PAGE); + return ( + <div className="flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6"> + <div className="flex flex-1 justify-between sm:hidden"> + <a + onClick={() => handlePage(page > 1 ? page - 1 : page)} + className="relative inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50" + > + Previous + </a> + <div + onClick={() => handlePage(page < totalPages ? page + 1 : page)} + className="relative ml-3 inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50" + > + Next + </div> + </div> + <div className="hidden sm:flex sm:flex-1 sm:items-center sm:justify-between"> + <div> + <p className="text-sm text-gray-700"> + Showing {/* (3-1) * 6 + 1 =13 */} + <span className="font-medium"> + {(page - 1) * ITEMS_PER_PAGE + 1} + </span>{" "} + {/* 3 * 6 = 18 */} + to{" "} + <span className="font-medium"> + {page * ITEMS_PER_PAGE > totalItems + ? totalItems + : page * ITEMS_PER_PAGE}{" "} + </span>{" "} + of <span className="font-medium">{totalItems}</span> results + </p> + </div> + <div> + <nav + className="isolate inline-flex -space-x-px rounded-md shadow-sm" + aria-label="Pagination" + > + <div + onClick={() => handlePage(page > 1 ? page - 1 : page)} + className="relative inline-flex items-center rounded-l-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" + > + <span className="sr-only">Previous</span> + <ChevronLeftIcon className="h-5 w-5" aria-hidden="true" /> + </div> + {/* Current: "z-10 bg-indigo-600 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600", Default: "text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:outline-offset-0" */} + {Array.from({ length: totalPages }).map( + //extracting all the indexes of array + (el, index) => ( + <div + onClick={() => handlePage(index + 1)} + aria-current="page" + className={`relative z-10 inline-flex items-center ${ + index + 1 === page + ? "bg-indigo-600 text-white" + : "text-gray-400 " + } px-4 py-2 text-sm font-semibold cursor-pointer focus:z-20 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600`} + > + {index + 1} + </div> + ) + )} + + <div + onClick={() => handlePage(page < totalPages ? page + 1 : page)} + className="relative inline-flex items-center rounded-r-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" + > + <span className="sr-only">Next</span> + <ChevronRightIcon className="h-5 w-5" aria-hidden="true" /> + </div> + </nav> + </div> + </div> + </div> + ); +} diff --git a/src/features/admin/productSlice.js b/src/features/admin/productSlice.js new file mode 100644 index 0000000..73243a4 --- /dev/null +++ b/src/features/admin/productSlice.js @@ -0,0 +1,120 @@ +import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; +import { + fetchAllProducts, + fetchProductsByFilters, + fetchBrands, + fetchCategories, + fetchProductById, +} from "./productAPI"; + +const initialState = { + products: [], + brands: [], + categories: [], + status: "idle", + totalItems: 0, + selectedProduct: null, +}; + +export const fetchAllProductsAsync = createAsyncThunk( + "product/fetchAllProducts", + async () => { + const response = await fetchAllProducts(); + // The value we return becomes the `fulfilled` action payload + return response.data; + } +); +export const fetchAllProductsIdAsync = createAsyncThunk( + "product/fetchProductById", + async (id) => { + const response = await fetchProductById(id); + // The value we return becomes the `fulfilled` action payload + return response.data; + } +); +export const fetchProductsByFiltersAsync = createAsyncThunk( + "product/fetchProductsByFilters", + async ({ filter, sort, pagination }) => { + const response = await fetchProductsByFilters(filter, sort, pagination); + + // The value we return becomes the `fulfilled` action payload + return response.data; //pagination array comes from data array!!!!! data.data:[id:2] + } +); + +export const fetchBrandsAsync = createAsyncThunk( + "product/fetchBrands", + async () => { + const response = await fetchBrands(); + // The value we return becomes the `fulfilled` action payload + return response.data; + } +); + +export const fetchCategoriesAsync = createAsyncThunk( + "product/fetchCategories", + async () => { + const response = await fetchCategories(); + // The value we return becomes the `fulfilled` action payload + return response.data; + } +); + +export const productSlice = createSlice({ + name: "product", + initialState, + reducers: { + increment: (state) => { + state.value += 1; + }, + }, + extraReducers: (builder) => { + builder + .addCase(fetchAllProductsAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(fetchAllProductsAsync.fulfilled, (state, action) => { + state.status = "idle"; + state.products = action.payload; + }) + .addCase(fetchProductsByFiltersAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(fetchProductsByFiltersAsync.fulfilled, (state, action) => { + state.status = "idle"; + state.products = action.payload.products; + state.totalItems = action.payload.totalItems; + }) + .addCase(fetchBrandsAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(fetchBrandsAsync.fulfilled, (state, action) => { + state.status = "idle"; + state.brands = action.payload; + }) + .addCase(fetchCategoriesAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(fetchCategoriesAsync.fulfilled, (state, action) => { + state.status = "idle"; + state.categories = action.payload; + }) + .addCase(fetchAllProductsIdAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(fetchAllProductsIdAsync.fulfilled, (state, action) => { + state.status = "idle"; + state.selectedProduct = action.payload; + }); + }, +}); + +export const { increment } = productSlice.actions; + +export const selectAllProducts = (state) => state.product.products; +export const selectBrands = (state) => state.product.brands; +export const selectCategories = (state) => state.product.categories; +export const selectTotalItems = (state) => state.product.totalItems; +export const selectProductById = (state) => state.product.selectedProduct; + +export default productSlice.reducer; diff --git a/src/features/auth/components/ProtectedAdmin.js b/src/features/auth/components/ProtectedAdmin.js new file mode 100644 index 0000000..d8ad8c0 --- /dev/null +++ b/src/features/auth/components/ProtectedAdmin.js @@ -0,0 +1,15 @@ +import { useSelector } from "react-redux"; +import { Navigate } from "react-router-dom"; +import { selectLoggedInUser } from "../authSlice"; +function ProtectedAdmin({ children }) { + const user = useSelector(selectLoggedInUser); + if (!user) { + return <Navigate to="/login" replace={true}></Navigate>; + } + if (user && user.role !== "admin") { + return <Navigate to="/" replace={true}></Navigate>; + } + return children; +} + +export default ProtectedAdmin; diff --git a/src/features/auth/components/Signup.js b/src/features/auth/components/Signup.js index cea7eca..4473c9d 100644 --- a/src/features/auth/components/Signup.js +++ b/src/features/auth/components/Signup.js @@ -39,6 +39,8 @@ export default function Signup() { email: data.email, password: data.password, addresses: [], + role: "user", + // TODO:this role can be directly given to the backend }) ); console.log(data); diff --git a/src/features/navbar/Navbar.js b/src/features/navbar/Navbar.js index 507f44d..90a3836 100644 --- a/src/features/navbar/Navbar.js +++ b/src/features/navbar/Navbar.js @@ -8,16 +8,13 @@ import { import { Link } from "react-router-dom"; import { useSelector } from "react-redux"; import { selectItems } from "../cart/cartSlice"; +import { fetchLoggedInUser } from "../user/userAPI"; +import { selectLoggedInUser } from "../auth/authSlice"; -const user = { - name: "Tom Cook", - email: "tom@example.com", - imageUrl: - "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80", -}; const navigation = [ - { name: "Dashboard", href: "#", current: true }, - { name: "Team", href: "#", current: false }, + { name: "Dashboard", link: "#", user: true }, + { name: "Team", link: "#", user: true }, + { name: "Admin", link: "/admin", admin: true }, ]; const userNavigation = [ { name: "My Profile", link: "/profile" }, @@ -31,6 +28,7 @@ function classNames(...classes) { function NavBar({ children }) { const items = useSelector(selectItems); + const user = useSelector(selectLoggedInUser); return ( <> <div className="min-h-full"> @@ -51,21 +49,23 @@ function NavBar({ children }) { </div> <div className="hidden md:block"> <div className="ml-10 flex items-baseline space-x-4"> - {navigation.map((item) => ( - <a - key={item.name} - href={item.href} - className={classNames( - item.current - ? "bg-gray-900 text-white" - : "text-gray-300 hover:bg-gray-700 hover:text-white", - "rounded-md px-3 py-2 text-sm font-medium" - )} - aria-current={item.current ? "page" : undefined} - > - {item.name} - </a> - ))} + {navigation.map((item) => + item[user.role] ? ( + <Link + key={item.name} + to={item.link} + className={classNames( + item.current + ? "bg-gray-900 text-white" + : "text-gray-300 hover:bg-gray-700 hover:text-white", + "rounded-md px-3 py-2 text-sm font-medium" + )} + aria-current={item.current ? "page" : undefined} + > + {item.name} + </Link> + ) : null + )} </div> </div> </div> @@ -153,22 +153,24 @@ function NavBar({ children }) { <Disclosure.Panel className="md:hidden"> <div className="space-y-1 px-2 pb-3 pt-2 sm:px-3"> - {navigation.map((item) => ( - <Disclosure.Button - key={item.name} - as="a" - href={item.href} - className={classNames( - item.current - ? "bg-gray-900 text-white" - : "text-gray-300 hover:bg-gray-700 hover:text-white", - "block rounded-md px-3 py-2 text-base font-medium" - )} - aria-current={item.current ? "page" : undefined} - > - {item.name} - </Disclosure.Button> - ))} + {navigation.map((item) => + item[user.role] ? ( + <Disclosure.Button + key={item.name} + as={Link} + to={item.link} + className={classNames( + item.current + ? "bg-gray-900 text-white" + : "text-gray-300 hover:bg-gray-700 hover:text-white", + "block rounded-md px-3 py-2 text-base font-medium" + )} + aria-current={item.current ? "page" : undefined} + > + {item.name} + </Disclosure.Button> + ) : null + )} </div> <div className="border-t border-gray-700 pb-3 pt-4"> <div className="flex items-center px-5"> diff --git a/src/features/user/components/UserProfile.js b/src/features/user/components/UserProfile.js index 9ff6692..15f253b 100644 --- a/src/features/user/components/UserProfile.js +++ b/src/features/user/components/UserProfile.js @@ -59,6 +59,11 @@ export default function UserProfile() { <h2 className="text-xl my-5 font-bold tracking-tight text-red-900"> Email Address: {user.email} </h2> + {user.role === "admin" && ( + <h2 className="text-xl my-5 font-bold tracking-tight text-red-900"> + Role: {user.role} + </h2> + )} </div> <div className="border-t border-gray-200 px-4 py-6 sm:px-6"> <button diff --git a/src/pages/AdimHome.js b/src/pages/AdimHome.js new file mode 100644 index 0000000..a9ce6cb --- /dev/null +++ b/src/pages/AdimHome.js @@ -0,0 +1,14 @@ +import NavBar from "../features/navbar/Navbar"; +import AdminProductList from "../features/admin/components/AdminProductList"; + +function AdminHome() { + return ( + <div> + <NavBar> + <AdminProductList></AdminProductList> + </NavBar> + </div> + ); +} + +export default AdminHome; diff --git a/src/pages/AdminProductDetailPage.js b/src/pages/AdminProductDetailPage.js new file mode 100644 index 0000000..8414b92 --- /dev/null +++ b/src/pages/AdminProductDetailPage.js @@ -0,0 +1,13 @@ +import AdminProductDetail from "../features/admin/components/AdminProductDetail"; +import NavBar from "../features/navbar/Navbar"; +function AdminProductDetailPage() { + return ( + <div> + <NavBar> + <AdminProductDetail></AdminProductDetail> + </NavBar> + </div> + ); +} + +export default AdminProductDetailPage; From 1a5a4b2342016a491975fa501d8bc4a6ea03ce4b Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Sat, 29 Jun 2024 01:53:19 +0530 Subject: [PATCH 37/47] new product functionality added in admin route --- data.json | 194 +++++++-- src/App.js | 11 +- .../admin/components/AdminProductList.js | 86 ++-- src/features/admin/components/ProductForm.js | 375 ++++++++++++++++++ src/features/admin/productSlice.js | 120 ------ src/features/product/productAPI.js | 12 + src/features/product/productSlice.js | 17 + src/pages/{AdimHome.js => AdminHome.js} | 0 src/pages/ProductFormPage.js | 13 + 9 files changed, 636 insertions(+), 192 deletions(-) create mode 100644 src/features/admin/components/ProductForm.js delete mode 100644 src/features/admin/productSlice.js rename src/pages/{AdimHome.js => AdminHome.js} (100%) create mode 100644 src/pages/ProductFormPage.js diff --git a/data.json b/data.json index 76be797..6ead48a 100644 --- a/data.json +++ b/data.json @@ -9,7 +9,10 @@ "discountPercentage": 7.17, "rating": 4.94, "stock": 5, - "tags": ["beauty", "mascara"], + "tags": [ + "beauty", + "mascara" + ], "brand": "Essence", "sku": "RCH45Q1A", "weight": 2, @@ -66,7 +69,10 @@ "discountPercentage": 5.5, "rating": 3.28, "stock": 44, - "tags": ["beauty", "eyeshadow"], + "tags": [ + "beauty", + "eyeshadow" + ], "brand": "Glamour Beauty", "sku": "MVCFH27F", "weight": 3, @@ -123,7 +129,10 @@ "discountPercentage": 18.14, "rating": 3.82, "stock": 59, - "tags": ["beauty", "face powder"], + "tags": [ + "beauty", + "face powder" + ], "brand": "Velvet Touch", "sku": "9EN8WLT2", "weight": 8, @@ -180,7 +189,10 @@ "discountPercentage": 19.03, "rating": 2.51, "stock": 68, - "tags": ["beauty", "lipstick"], + "tags": [ + "beauty", + "lipstick" + ], "brand": "Chic Cosmetics", "sku": "O5IF1NTA", "weight": 5, @@ -237,7 +249,10 @@ "discountPercentage": 2.46, "rating": 3.91, "stock": 71, - "tags": ["beauty", "nail polish"], + "tags": [ + "beauty", + "nail polish" + ], "brand": "Nail Couture", "sku": "YUIIIP4W", "weight": 9, @@ -294,7 +309,10 @@ "discountPercentage": 0.32, "rating": 4.85, "stock": 17, - "tags": ["fragrances", "perfumes"], + "tags": [ + "fragrances", + "perfumes" + ], "brand": "Calvin Klein", "sku": "DZM2JQZE", "weight": 5, @@ -353,7 +371,10 @@ "discountPercentage": 18.64, "rating": 2.76, "stock": 41, - "tags": ["fragrances", "perfumes"], + "tags": [ + "fragrances", + "perfumes" + ], "brand": "Chanel", "sku": "K71HBCGS", "weight": 4, @@ -412,7 +433,10 @@ "discountPercentage": 17.44, "rating": 3.31, "stock": 91, - "tags": ["fragrances", "perfumes"], + "tags": [ + "fragrances", + "perfumes" + ], "brand": "Dior", "sku": "E70NB03B", "weight": 10, @@ -471,7 +495,10 @@ "discountPercentage": 11.47, "rating": 2.68, "stock": 3, - "tags": ["fragrances", "perfumes"], + "tags": [ + "fragrances", + "perfumes" + ], "brand": "Dolce & Gabbana", "sku": "1NBFK980", "weight": 5, @@ -530,7 +557,10 @@ "discountPercentage": 8.9, "rating": 2.69, "stock": 93, - "tags": ["fragrances", "perfumes"], + "tags": [ + "fragrances", + "perfumes" + ], "brand": "Gucci", "sku": "FFKZ6HOF", "weight": 10, @@ -589,7 +619,10 @@ "discountPercentage": 0.29, "rating": 4.14, "stock": 47, - "tags": ["furniture", "beds"], + "tags": [ + "furniture", + "beds" + ], "brand": "Annibale Colombo", "sku": "4KMDTZWF", "weight": 3, @@ -648,7 +681,10 @@ "discountPercentage": 18.54, "rating": 3.08, "stock": 16, - "tags": ["furniture", "sofas"], + "tags": [ + "furniture", + "sofas" + ], "brand": "Annibale Colombo", "sku": "LUU95CQP", "weight": 3, @@ -707,7 +743,10 @@ "discountPercentage": 9.58, "rating": 4.48, "stock": 16, - "tags": ["furniture", "bedside tables"], + "tags": [ + "furniture", + "bedside tables" + ], "brand": "Furniture Co.", "sku": "OWPLTZYX", "weight": 10, @@ -766,7 +805,10 @@ "discountPercentage": 15.23, "rating": 4.11, "stock": 47, - "tags": ["furniture", "office chairs"], + "tags": [ + "furniture", + "office chairs" + ], "brand": "Knoll", "sku": "RKHVJ4FE", "weight": 3, @@ -825,7 +867,10 @@ "discountPercentage": 11.22, "rating": 3.26, "stock": 95, - "tags": ["furniture", "bathroom"], + "tags": [ + "furniture", + "bathroom" + ], "brand": "Bath Trends", "sku": "7OLTIEVO", "weight": 6, @@ -884,7 +929,9 @@ "discountPercentage": 1.97, "rating": 2.96, "stock": 9, - "tags": ["fruits"], + "tags": [ + "fruits" + ], "sku": "QTROUV79", "weight": 8, "dimensions": { @@ -940,7 +987,9 @@ "discountPercentage": 17.99, "rating": 2.83, "stock": 96, - "tags": ["meat"], + "tags": [ + "meat" + ], "sku": "BWWA2MSO", "weight": 9, "dimensions": { @@ -996,7 +1045,10 @@ "discountPercentage": 9.57, "rating": 2.88, "stock": 13, - "tags": ["pet supplies", "cat food"], + "tags": [ + "pet supplies", + "cat food" + ], "sku": "C3F8QN6O", "weight": 9, "dimensions": { @@ -1052,7 +1104,9 @@ "discountPercentage": 10.46, "rating": 4.61, "stock": 69, - "tags": ["meat"], + "tags": [ + "meat" + ], "sku": "G5YEHW7B", "weight": 7, "dimensions": { @@ -1109,7 +1163,9 @@ "discountPercentage": 18.89, "rating": 4.01, "stock": 22, - "tags": ["cooking essentials"], + "tags": [ + "cooking essentials" + ], "sku": "Q6ZP1UY8", "weight": 8, "dimensions": { @@ -1165,7 +1221,9 @@ "discountPercentage": 11.44, "rating": 4.71, "stock": 22, - "tags": ["vegetables"], + "tags": [ + "vegetables" + ], "sku": "6KGF2K6Z", "weight": 9, "dimensions": { @@ -1221,7 +1279,10 @@ "discountPercentage": 18.15, "rating": 2.74, "stock": 40, - "tags": ["pet supplies", "dog food"], + "tags": [ + "pet supplies", + "dog food" + ], "sku": "A6QRCH37", "weight": 2, "dimensions": { @@ -1277,7 +1338,9 @@ "discountPercentage": 5.8, "rating": 4.46, "stock": 10, - "tags": ["dairy"], + "tags": [ + "dairy" + ], "sku": "YA617RI7", "weight": 4, "dimensions": { @@ -1333,7 +1396,9 @@ "discountPercentage": 7, "rating": 4.83, "stock": 99, - "tags": ["seafood"], + "tags": [ + "seafood" + ], "sku": "XNIH1MTA", "weight": 8, "dimensions": { @@ -1389,7 +1454,9 @@ "discountPercentage": 15.5, "rating": 4.28, "stock": 89, - "tags": ["vegetables"], + "tags": [ + "vegetables" + ], "sku": "HU7S7VQ0", "weight": 7, "dimensions": { @@ -1445,7 +1512,9 @@ "discountPercentage": 18.51, "rating": 4.43, "stock": 8, - "tags": ["vegetables"], + "tags": [ + "vegetables" + ], "sku": "Y4RM3JCB", "weight": 2, "dimensions": { @@ -1501,7 +1570,9 @@ "discountPercentage": 1.91, "rating": 3.5, "stock": 25, - "tags": ["condiments"], + "tags": [ + "condiments" + ], "sku": "BTBNIIOU", "weight": 9, "dimensions": { @@ -1557,7 +1628,9 @@ "discountPercentage": 7.58, "rating": 3.77, "stock": 76, - "tags": ["desserts"], + "tags": [ + "desserts" + ], "sku": "VEZMU1EQ", "weight": 5, "dimensions": { @@ -1616,7 +1689,9 @@ "discountPercentage": 5.45, "rating": 3.41, "stock": 99, - "tags": ["beverages"], + "tags": [ + "beverages" + ], "sku": "M2K19S06", "weight": 2, "dimensions": { @@ -1672,7 +1747,9 @@ "discountPercentage": 10.32, "rating": 4.37, "stock": 1, - "tags": ["fruits"], + "tags": [ + "fruits" + ], "sku": "0X3NORB9", "weight": 8, "dimensions": { @@ -1718,6 +1795,24 @@ "https://cdn.dummyjson.com/products/images/groceries/Kiwi/1.png" ], "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", + "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" + ], + "rating": 0 } ], "brands": [ @@ -1900,7 +1995,10 @@ "discountPercentage": 19.03, "rating": 2.51, "stock": 68, - "tags": ["beauty", "lipstick"], + "tags": [ + "beauty", + "lipstick" + ], "brand": "Chic Cosmetics", "sku": "O5IF1NTA", "weight": 5, @@ -1959,7 +2057,10 @@ "discountPercentage": 18.14, "rating": 3.82, "stock": 59, - "tags": ["beauty", "face powder"], + "tags": [ + "beauty", + "face powder" + ], "brand": "Velvet Touch", "sku": "9EN8WLT2", "weight": 8, @@ -2061,7 +2162,10 @@ "discountPercentage": 5.5, "rating": 3.28, "stock": 44, - "tags": ["beauty", "eyeshadow"], + "tags": [ + "beauty", + "eyeshadow" + ], "brand": "Glamour Beauty", "sku": "MVCFH27F", "weight": 3, @@ -2120,7 +2224,10 @@ "discountPercentage": 19.03, "rating": 2.51, "stock": 68, - "tags": ["beauty", "lipstick"], + "tags": [ + "beauty", + "lipstick" + ], "brand": "Chic Cosmetics", "sku": "O5IF1NTA", "weight": 5, @@ -2213,7 +2320,10 @@ "discountPercentage": 5.5, "rating": 3.28, "stock": 44, - "tags": ["beauty", "eyeshadow"], + "tags": [ + "beauty", + "eyeshadow" + ], "brand": "Glamour Beauty", "sku": "MVCFH27F", "weight": 3, @@ -2272,7 +2382,9 @@ "discountPercentage": 5.8, "rating": 4.46, "stock": 10, - "tags": ["dairy"], + "tags": [ + "dairy" + ], "sku": "YA617RI7", "weight": 4, "dimensions": { @@ -2373,7 +2485,9 @@ "discountPercentage": 7, "rating": 4.83, "stock": 99, - "tags": ["seafood"], + "tags": [ + "seafood" + ], "sku": "XNIH1MTA", "weight": 8, "dimensions": { @@ -2477,7 +2591,9 @@ "discountPercentage": 18.89, "rating": 4.01, "stock": 22, - "tags": ["cooking essentials"], + "tags": [ + "cooking essentials" + ], "sku": "Q6ZP1UY8", "weight": 8, "dimensions": { @@ -2570,4 +2686,4 @@ "status": "pending" } ] -} +} \ No newline at end of file diff --git a/src/App.js b/src/App.js index 9ffd0a8..d050bf3 100644 --- a/src/App.js +++ b/src/App.js @@ -27,8 +27,9 @@ import { fetchLoggedInUserAsync } from "./features/user/userSlice"; import LogOut from "./features/auth/components/LogOut"; import ForgotPasswordPage from "./pages/ForgotPasswordPage"; import ProtectedAdmin from "./features/auth/components/ProtectedAdmin"; -import AdminHome from "./pages/AdimHome"; +import AdminHome from "./pages/AdminHome"; import AdminProductDetail from "./features/admin/components/AdminProductDetail"; +import ProductFormPage from "./pages/ProductFormPage"; const router = createBrowserRouter([ { path: "/", @@ -88,6 +89,14 @@ const router = createBrowserRouter([ </ProtectedAdmin> ), }, + { + path: "/admin/product-form", + element: ( + <ProtectedAdmin> + <ProductFormPage></ProductFormPage> + </ProtectedAdmin> + ), + }, { path: "/order-success/:id", element: <OrderSuccessPage></OrderSuccessPage>, diff --git a/src/features/admin/components/AdminProductList.js b/src/features/admin/components/AdminProductList.js index cb88063..97501f5 100644 --- a/src/features/admin/components/AdminProductList.js +++ b/src/features/admin/components/AdminProductList.js @@ -199,6 +199,12 @@ export default function AdminProductList() { {/* Product grid */} <div className="lg:col-span-3"> {/* 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" + > + Add Product + </Link> <ProductGrid products={products} /> </div> {/* Product grid end */} @@ -399,42 +405,58 @@ function ProductGrid({ products }) { <div className="mx-auto max-w-2xl px-4 py-0 sm:px-6 sm:py-0 lg:max-w-7xl lg:px-8"> <div className="mt-6 grid grid-cols-1 gap-x-6 gap-y-10 sm:grid-cols-2 lg:grid-cols-3 xl:gap-x-8"> {products.map((product) => ( - <Link to={`/product-detail/${product.id}`}> - <div key={product.id} className="group relative p-2 border-2"> - <div className="min-h-60 aspect-h-1 aspect-w-1 w-full overflow-hidden rounded-md bg-gray-200 lg:aspect-none group-hover:opacity-75 lg:h-60"> - <img - src={product.thumbnail} - alt={product.title} - className="h-full w-full object-cover object-center lg:h-full lg:w-full" - /> - </div> - <div className="mt-4 flex justify-between"> - <div> - <h3 className="text-sm text-gray-700"> - <div href={product.thumbnail}> - <span aria-hidden="true" className="absolute inset-0" /> - {product.title} + <div> + <Link to={`/product-detail/${product.id}`}> + <div> + <div + key={product.id} + className="group relative p-2 border-2" + > + <div className="min-h-60 aspect-h-1 aspect-w-1 w-full overflow-hidden rounded-md bg-gray-200 lg:aspect-none group-hover:opacity-75 lg:h-60"> + <img + src={product.thumbnail} + alt={product.title} + className="h-full w-full object-cover object-center lg:h-full lg:w-full" + /> + </div> + <div className="mt-4 flex justify-between"> + <div> + <h3 className="text-sm text-gray-700"> + <div href={product.thumbnail}> + <span + aria-hidden="true" + className="absolute inset-0" + /> + {product.title} + </div> + </h3> + <p className="mt-1 text-sm text-gray-500"> + <StarIcon className="w-6 h-6 inline"></StarIcon> + <span className="align-bottom">{product.rating}</span> + </p> </div> - </h3> - <p className="mt-1 text-sm text-gray-500"> - <StarIcon className="w-6 h-6 inline"></StarIcon> - <span className="align-bottom">{product.rating}</span> - </p> - </div> - <div> - <p className="text-sm font-medium text-gray-900"> - $ - {Math.round( - product.price * (1 - product.discountPercentage / 100) - )} - <p className="text-sm line-through font-medium text-gray-400"> - ${product.price} - </p> - </p> + <div> + <p className="text-sm font-medium text-gray-900"> + $ + {Math.round( + product.price * + (1 - product.discountPercentage / 100) + )} + <p className="text-sm line-through font-medium text-gray-400"> + ${product.price} + </p> + </p> + </div> + </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"> + Edit Product + </button> </div> - </Link> + </div> ))} </div> </div> diff --git a/src/features/admin/components/ProductForm.js b/src/features/admin/components/ProductForm.js new file mode 100644 index 0000000..4bb24db --- /dev/null +++ b/src/features/admin/components/ProductForm.js @@ -0,0 +1,375 @@ +import { useDispatch, useSelector } from "react-redux"; +import { + createProductAsync, + selectBrands, + selectCategories, +} from "../../product/productSlice"; +import { PhotoIcon, UserCircleIcon } from "@heroicons/react/24/solid"; +import { useForm } from "react-hook-form"; +function ProductForm() { + const categories = useSelector(selectCategories); + const brands = useSelector(selectBrands); + const dispatch = useDispatch(); + const { + register, + handleSubmit, + formState: { errors }, + } = useForm(); + + return ( + <form + noValidate + onSubmit={handleSubmit((data) => { + console.log(data); + const product = { ...data }; + product.images = [ + product.image1, + product.image2, + product.image3, + product.thumbnail, + ]; + product.rating = 0; + delete product["image1"]; + delete product["image2"]; + delete product["image3"]; + console.log(product); + dispatch(createProductAsync(product)); + })} + > + <div className="space-y-12 bg-white p-12"> + <div className="border-b border-gray-900/10 pb-12"> + <h2 className="text-base font-semibold leading-7 text-gray-900"> + Add Product + </h2> + + <div className="mt-10 grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6"> + <div className="sm:col-span-6"> + <label + htmlFor="title" + className="block text-sm font-medium leading-6 text-gray-900" + > + Product Name + </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("title", { + required: "title is required", + })} + id="title" + 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="col-span-full"> + <label + htmlFor="description" + className="block text-sm font-medium leading-6 text-gray-900" + > + Description + </label> + <div className="mt-2"> + <textarea + id="description" + {...register("description", { + required: "description is required", + })} + rows={3} + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + defaultValue={""} + /> + </div> + <p className="mt-3 text-sm leading-6 text-gray-600"> + Write a few sentences about product. + </p> + </div> + <div className="col-span-full"> + <label + htmlFor="brand" + className="block text-sm font-medium leading-6 text-gray-900" + > + Brand + </label> + <div className="mt-2"> + <select + {...register("brand", { + required: "brand is required", + })} + > + <option>--choose brand--</option> + {brands.map((brand) => ( + <option value={brand.value}>{brand.label}</option> + ))} + </select> + </div> + </div> + <div className="col-span-full"> + <label + htmlFor="Category" + className="block text-sm font-medium leading-6 text-gray-900" + > + Category + </label> + <div className="mt-2"> + <select + {...register("category", { + required: "catergory is required", + })} + > + <option>--choose category--</option> + {categories.map((category) => ( + <option value={category.value}>{category.label}</option> + ))} + </select> + </div> + </div> + <div className="sm:col-span-2"> + <label + htmlFor="price" + className="block text-sm font-medium leading-6 text-gray-900" + > + Price + </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="number" + {...register("price", { + required: "price is required", + min: 1, + max: 10000, + })} + id="price" + 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-2"> + <label + htmlFor="discountPercentage" + className="block text-sm font-medium leading-6 text-gray-900" + > + Discount + </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="number" + {...register("discountPercentage", { + required: "discountPercentage is required", + min: 0, + max: 100, + })} + id="discountPercentage" + 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-2"> + <label + htmlFor="stock" + className="block text-sm font-medium leading-6 text-gray-900" + > + Stock + </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="number" + {...register("stock", { + required: "stock is required", + min: 0, + })} + id="stock" + 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="thumbnail" + className="block text-sm font-medium leading-6 text-gray-900" + > + Thumbnail + </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("thumbnail", { + required: "thumbnail is required", + })} + id="thumbnail" + 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="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" + className="block text-sm font-medium leading-6 text-gray-900" + > + Image3 + </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", + })} + id="image3" + 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> + </div> + + <div className="border-b border-gray-900/10 pb-12"> + <h2 className="text-base font-semibold leading-7 text-gray-900"> + Extra + </h2> + + <div className="mt-10 space-y-10"> + <fieldset> + <legend className="text-sm font-semibold leading-6 text-gray-900"> + By Email + </legend> + <div className="mt-6 space-y-6"> + <div className="relative flex gap-x-3"> + <div className="flex h-6 items-center"> + <input + id="comments" + name="comments" + type="checkbox" + className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600" + /> + </div> + <div className="text-sm leading-6"> + <label + htmlFor="comments" + className="font-medium text-gray-900" + > + Comments + </label> + <p className="text-gray-500"> + Get notified when someones posts a comment on a posting. + </p> + </div> + </div> + <div className="relative flex gap-x-3"> + <div className="flex h-6 items-center"> + <input + id="candidates" + name="candidates" + type="checkbox" + className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600" + /> + </div> + <div className="text-sm leading-6"> + <label + htmlFor="candidates" + className="font-medium text-gray-900" + > + Candidates + </label> + <p className="text-gray-500"> + Get notified when a candidate applies for a job. + </p> + </div> + </div> + <div className="relative flex gap-x-3"> + <div className="flex h-6 items-center"> + <input + id="offers" + name="offers" + type="checkbox" + className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600" + /> + </div> + <div className="text-sm leading-6"> + <label + htmlFor="offers" + className="font-medium text-gray-900" + > + Offers + </label> + <p className="text-gray-500"> + Get notified when a candidate accepts or rejects an offer. + </p> + </div> + </div> + </div> + </fieldset> + </div> + </div> + <div className="mt-6 flex items-center justify-end gap-x-6"> + <button + type="button" + className="text-sm font-semibold leading-6 text-gray-900" + > + Cancel + </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" + > + Save + </button> + </div> + </div> + </form> + ); +} + +export default ProductForm; diff --git a/src/features/admin/productSlice.js b/src/features/admin/productSlice.js deleted file mode 100644 index 73243a4..0000000 --- a/src/features/admin/productSlice.js +++ /dev/null @@ -1,120 +0,0 @@ -import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; -import { - fetchAllProducts, - fetchProductsByFilters, - fetchBrands, - fetchCategories, - fetchProductById, -} from "./productAPI"; - -const initialState = { - products: [], - brands: [], - categories: [], - status: "idle", - totalItems: 0, - selectedProduct: null, -}; - -export const fetchAllProductsAsync = createAsyncThunk( - "product/fetchAllProducts", - async () => { - const response = await fetchAllProducts(); - // The value we return becomes the `fulfilled` action payload - return response.data; - } -); -export const fetchAllProductsIdAsync = createAsyncThunk( - "product/fetchProductById", - async (id) => { - const response = await fetchProductById(id); - // The value we return becomes the `fulfilled` action payload - return response.data; - } -); -export const fetchProductsByFiltersAsync = createAsyncThunk( - "product/fetchProductsByFilters", - async ({ filter, sort, pagination }) => { - const response = await fetchProductsByFilters(filter, sort, pagination); - - // The value we return becomes the `fulfilled` action payload - return response.data; //pagination array comes from data array!!!!! data.data:[id:2] - } -); - -export const fetchBrandsAsync = createAsyncThunk( - "product/fetchBrands", - async () => { - const response = await fetchBrands(); - // The value we return becomes the `fulfilled` action payload - return response.data; - } -); - -export const fetchCategoriesAsync = createAsyncThunk( - "product/fetchCategories", - async () => { - const response = await fetchCategories(); - // The value we return becomes the `fulfilled` action payload - return response.data; - } -); - -export const productSlice = createSlice({ - name: "product", - initialState, - reducers: { - increment: (state) => { - state.value += 1; - }, - }, - extraReducers: (builder) => { - builder - .addCase(fetchAllProductsAsync.pending, (state) => { - state.status = "loading"; - }) - .addCase(fetchAllProductsAsync.fulfilled, (state, action) => { - state.status = "idle"; - state.products = action.payload; - }) - .addCase(fetchProductsByFiltersAsync.pending, (state) => { - state.status = "loading"; - }) - .addCase(fetchProductsByFiltersAsync.fulfilled, (state, action) => { - state.status = "idle"; - state.products = action.payload.products; - state.totalItems = action.payload.totalItems; - }) - .addCase(fetchBrandsAsync.pending, (state) => { - state.status = "loading"; - }) - .addCase(fetchBrandsAsync.fulfilled, (state, action) => { - state.status = "idle"; - state.brands = action.payload; - }) - .addCase(fetchCategoriesAsync.pending, (state) => { - state.status = "loading"; - }) - .addCase(fetchCategoriesAsync.fulfilled, (state, action) => { - state.status = "idle"; - state.categories = action.payload; - }) - .addCase(fetchAllProductsIdAsync.pending, (state) => { - state.status = "loading"; - }) - .addCase(fetchAllProductsIdAsync.fulfilled, (state, action) => { - state.status = "idle"; - state.selectedProduct = action.payload; - }); - }, -}); - -export const { increment } = productSlice.actions; - -export const selectAllProducts = (state) => state.product.products; -export const selectBrands = (state) => state.product.brands; -export const selectCategories = (state) => state.product.categories; -export const selectTotalItems = (state) => state.product.totalItems; -export const selectProductById = (state) => state.product.selectedProduct; - -export default productSlice.reducer; diff --git a/src/features/product/productAPI.js b/src/features/product/productAPI.js index a745521..652deb2 100644 --- a/src/features/product/productAPI.js +++ b/src/features/product/productAPI.js @@ -14,6 +14,18 @@ export function fetchProductById(id) { resolve({ data }); }); } +export function createProduct(product) { + return new Promise(async (resolve) => { + // TODO: we will not hard coded server url here... + const response = await fetch("http://localhost:8080/products/", { + method: "POST", + body: JSON.stringify(product), + 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 diff --git a/src/features/product/productSlice.js b/src/features/product/productSlice.js index 73243a4..02aa55f 100644 --- a/src/features/product/productSlice.js +++ b/src/features/product/productSlice.js @@ -5,6 +5,7 @@ import { fetchBrands, fetchCategories, fetchProductById, + createProduct, } from "./productAPI"; const initialState = { @@ -32,6 +33,7 @@ export const fetchAllProductsIdAsync = createAsyncThunk( return response.data; } ); + export const fetchProductsByFiltersAsync = createAsyncThunk( "product/fetchProductsByFilters", async ({ filter, sort, pagination }) => { @@ -59,6 +61,14 @@ export const fetchCategoriesAsync = createAsyncThunk( return response.data; } ); +export const createProductAsync = createAsyncThunk( + "product/createProduct", + async (product) => { + const response = await createProduct(product); + // The value we return becomes the `fulfilled` action payload + return response.data; + } +); export const productSlice = createSlice({ name: "product", @@ -105,6 +115,13 @@ export const productSlice = createSlice({ .addCase(fetchAllProductsIdAsync.fulfilled, (state, action) => { state.status = "idle"; state.selectedProduct = action.payload; + }) + .addCase(createProductAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(createProductAsync.fulfilled, (state, action) => { + state.status = "idle"; + state.products.push(action.payload); }); }, }); diff --git a/src/pages/AdimHome.js b/src/pages/AdminHome.js similarity index 100% rename from src/pages/AdimHome.js rename to src/pages/AdminHome.js diff --git a/src/pages/ProductFormPage.js b/src/pages/ProductFormPage.js new file mode 100644 index 0000000..1c40e13 --- /dev/null +++ b/src/pages/ProductFormPage.js @@ -0,0 +1,13 @@ +import ProductForm from "../features/admin/components/ProductForm"; +import NavBar from "../features/navbar/Navbar"; +function ProductFormPage() { + return ( + <div> + <NavBar> + <ProductForm></ProductForm> + </NavBar> + </div> + ); +} + +export default ProductFormPage; From 418d60e12c90968a7c27f6ea361eb0ab98c8393a Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Sat, 29 Jun 2024 05:01:17 +0530 Subject: [PATCH 38/47] edit product functionality added in admin route --- data.json | 45 ++++--- src/App.js | 8 ++ .../admin/components/AdminProductDetail.js | 4 +- .../admin/components/AdminProductList.js | 16 ++- src/features/admin/components/ProductForm.js | 123 ++++++++++-------- .../product/components/ProductDetail.js | 4 +- .../product/components/ProductList.js | 1 + src/features/product/productAPI.js | 17 +++ src/features/product/productSlice.js | 33 ++++- 9 files changed, 160 insertions(+), 91 deletions(-) diff --git a/data.json b/data.json index 6ead48a..4a8fd7f 100644 --- a/data.json +++ b/data.json @@ -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", @@ -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", @@ -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", @@ -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", @@ -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": [ diff --git a/src/App.js b/src/App.js index d050bf3..0fda088 100644 --- a/src/App.js +++ b/src/App.js @@ -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>, diff --git a/src/features/admin/components/AdminProductDetail.js b/src/features/admin/components/AdminProductDetail.js index 32ba573..f80af7c 100644 --- a/src/features/admin/components/AdminProductDetail.js +++ b/src/features/admin/components/AdminProductDetail.js @@ -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"; @@ -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"> diff --git a/src/features/admin/components/AdminProductList.js b/src/features/admin/components/AdminProductList.js index 97501f5..cdec39d 100644 --- a/src/features/admin/components/AdminProductList.js +++ b/src/features/admin/components/AdminProductList.js @@ -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> @@ -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> ))} diff --git a/src/features/admin/components/ProductForm.js b/src/features/admin/components/ProductForm.js index 4bb24db..470d3c3 100644 --- a/src/features/admin/components/ProductForm.js +++ b/src/features/admin/components/ProductForm.js @@ -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"> @@ -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> @@ -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" diff --git a/src/features/product/components/ProductDetail.js b/src/features/product/components/ProductDetail.js index c203fe8..177cf0a 100644 --- a/src/features/product/components/ProductDetail.js +++ b/src/features/product/components/ProductDetail.js @@ -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"; @@ -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"> diff --git a/src/features/product/components/ProductList.js b/src/features/product/components/ProductList.js index 94a6842..74643d8 100644 --- a/src/features/product/components/ProductList.js +++ b/src/features/product/components/ProductList.js @@ -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(() => { diff --git a/src/features/product/productAPI.js b/src/features/product/productAPI.js index 652deb2..aa8523c 100644 --- a/src/features/product/productAPI.js +++ b/src/features/product/productAPI.js @@ -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 }); }); @@ -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. diff --git a/src/features/product/productSlice.js b/src/features/product/productSlice.js index 02aa55f..9ed010c 100644 --- a/src/features/product/productSlice.js +++ b/src/features/product/productSlice.js @@ -6,6 +6,7 @@ import { fetchCategories, fetchProductById, createProduct, + updateProduct, } from "./productAPI"; const initialState = { @@ -25,7 +26,7 @@ export const fetchAllProductsAsync = createAsyncThunk( return response.data; } ); -export const fetchAllProductsIdAsync = createAsyncThunk( +export const fetchProductByIdAsync = createAsyncThunk( "product/fetchProductById", async (id) => { const response = await fetchProductById(id); @@ -63,8 +64,16 @@ export const fetchCategoriesAsync = createAsyncThunk( ); export const createProductAsync = createAsyncThunk( "product/createProduct", + async (update) => { + const response = await createProduct(update); + // The value we return becomes the `fulfilled` action payload + return response.data; + } +); +export const updateProductAsync = createAsyncThunk( + "product/updateProduct", async (product) => { - const response = await createProduct(product); + const response = await updateProduct(product); // The value we return becomes the `fulfilled` action payload return response.data; } @@ -74,8 +83,8 @@ export const productSlice = createSlice({ name: "product", initialState, reducers: { - increment: (state) => { - state.value += 1; + clearSelectedProduct: (state) => { + state.selectedProduct = null; }, }, extraReducers: (builder) => { @@ -109,10 +118,10 @@ export const productSlice = createSlice({ state.status = "idle"; state.categories = action.payload; }) - .addCase(fetchAllProductsIdAsync.pending, (state) => { + .addCase(fetchProductByIdAsync.pending, (state) => { state.status = "loading"; }) - .addCase(fetchAllProductsIdAsync.fulfilled, (state, action) => { + .addCase(fetchProductByIdAsync.fulfilled, (state, action) => { state.status = "idle"; state.selectedProduct = action.payload; }) @@ -122,11 +131,21 @@ export const productSlice = createSlice({ .addCase(createProductAsync.fulfilled, (state, action) => { state.status = "idle"; state.products.push(action.payload); + }) + .addCase(updateProductAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(updateProductAsync.fulfilled, (state, action) => { + state.status = "idle"; + const index = state.products.findIndex( + (product) => product.id === action.payload.id + ); + state.products[index] = action.payload; }); }, }); -export const { increment } = productSlice.actions; +export const { clearSelectedProduct } = productSlice.actions; export const selectAllProducts = (state) => state.product.products; export const selectBrands = (state) => state.product.brands; From e1960cdd738b2117737bb2204b09a23b1e909be9 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Sat, 29 Jun 2024 10:25:08 +0530 Subject: [PATCH 39/47] clean up the code (discard counter app actions) --- data.json | 245 ++++++++++++++++++++++++++++++- src/features/auth/authSlice.js | 8 +- src/features/cart/cartSlice.js | 8 +- src/features/order/orderSlice.js | 3 - src/features/user/userSlice.js | 7 +- 5 files changed, 247 insertions(+), 24 deletions(-) diff --git a/data.json b/data.json index 4a8fd7f..bcf4618 100644 --- a/data.json +++ b/data.json @@ -2,7 +2,7 @@ "products": [ { "id": "1", - "title": "Essence Mascara Lash", + "title": "Essence Mascara Lash 2", "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, @@ -1956,6 +1956,15 @@ "state": "Maharastra", "pinCode": "452003", "city": "Mumbai" + }, + { + "name": "Brat", + "email": "brat@gmail.com", + "phone": "4568745623", + "street": "12th street", + "city": "Pune", + "state": "Maharashtra", + "pinCode": "4520014" } ] }, @@ -2687,6 +2696,240 @@ }, "paymentMethod": "card", "status": "pending" + }, + { + "id": "0a5b", + "items": [ + { + "id": "6614", + "title": "Red Nail Polish", + "description": "The Red Nail Polish offers a rich and glossy red hue for vibrant and polished nails. With a quick-drying formula, it provides a salon-quality finish at home.", + "category": "beauty", + "price": 8.99, + "discountPercentage": 2.46, + "rating": 3.91, + "stock": 71, + "tags": [ + "beauty", + "nail polish" + ], + "brand": "Nail Couture", + "sku": "YUIIIP4W", + "weight": 9, + "dimensions": { + "width": 8.11, + "height": 10.89, + "depth": 29.06 + }, + "warrantyInformation": "1 year warranty", + "shippingInformation": "Ships in 1 week", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Leo Rivera", + "reviewerEmail": "leo.rivera@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Great product!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Evan Reed", + "reviewerEmail": "evan.reed@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Highly recommended!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Evelyn Sanchez", + "reviewerEmail": "evelyn.sanchez@x.dummyjson.com" + } + ], + "returnPolicy": "No return policy", + "minimumOrderQuantity": 46, + "meta": { + "createdAt": "2024-05-23T08:56:21.619Z", + "updatedAt": "2024-05-23T08:56:21.619Z", + "barcode": "3212847902461", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/beauty/Red%20Nail%20Polish/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Red%20Nail%20Polish/thumbnail.png", + "quantity": 1, + "user": "cb1b" + } + ], + "totalAmount": 8.99, + "totalItems": 1, + "user": { + "id": "cb1b", + "email": "test@123.com", + "password": "Murtaza123", + "role": "user", + "addresses": [ + { + "name": "Murtaza ", + "email": "murtazashabbir14@gmail.com", + "phone": "7869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002", + "address": "Indore" + }, + { + "name": "Jack Hill", + "email": "jack@gmail.com", + "phone": "12345", + "street": "11th road", + "address": "Mumbai", + "state": "Maharastra", + "pinCode": "452003", + "city": "Mumbai" + }, + { + "name": "Brat", + "email": "brat@gmail.com", + "phone": "4568745623", + "street": "12th street", + "city": "Pune", + "state": "Maharashtra", + "pinCode": "4520014" + } + ] + }, + "selectedAddress": { + "name": "Brat", + "email": "brat@gmail.com", + "phone": "4568745623", + "street": "12th street", + "city": "Pune", + "state": "Maharashtra", + "pinCode": "4520014" + }, + "paymentMethod": "cash", + "status": "pending" + }, + { + "id": "a37d", + "items": [ + { + "id": "230f", + "title": "Red Nail Polish", + "description": "The Red Nail Polish offers a rich and glossy red hue for vibrant and polished nails. With a quick-drying formula, it provides a salon-quality finish at home.", + "category": "beauty", + "price": 8.99, + "discountPercentage": 2.46, + "rating": 3.91, + "stock": 71, + "tags": [ + "beauty", + "nail polish" + ], + "brand": "Nail Couture", + "sku": "YUIIIP4W", + "weight": 9, + "dimensions": { + "width": 8.11, + "height": 10.89, + "depth": 29.06 + }, + "warrantyInformation": "1 year warranty", + "shippingInformation": "Ships in 1 week", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Leo Rivera", + "reviewerEmail": "leo.rivera@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Great product!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Evan Reed", + "reviewerEmail": "evan.reed@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Highly recommended!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Evelyn Sanchez", + "reviewerEmail": "evelyn.sanchez@x.dummyjson.com" + } + ], + "returnPolicy": "No return policy", + "minimumOrderQuantity": 46, + "meta": { + "createdAt": "2024-05-23T08:56:21.619Z", + "updatedAt": "2024-05-23T08:56:21.619Z", + "barcode": "3212847902461", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/beauty/Red%20Nail%20Polish/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Red%20Nail%20Polish/thumbnail.png", + "quantity": 2, + "user": "cb1b" + } + ], + "totalAmount": 17.98, + "totalItems": 2, + "user": { + "id": "cb1b", + "email": "test@123.com", + "password": "Murtaza123", + "role": "user", + "addresses": [ + { + "name": "Murtaza ", + "email": "murtazashabbir14@gmail.com", + "phone": "7869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002", + "address": "Indore" + }, + { + "name": "Jack Hill", + "email": "jack@gmail.com", + "phone": "12345", + "street": "11th road", + "address": "Mumbai", + "state": "Maharastra", + "pinCode": "452003", + "city": "Mumbai" + }, + { + "name": "Brat", + "email": "brat@gmail.com", + "phone": "4568745623", + "street": "12th street", + "city": "Pune", + "state": "Maharashtra", + "pinCode": "4520014" + } + ] + }, + "selectedAddress": { + "name": "Brat", + "email": "brat@gmail.com", + "phone": "4568745623", + "street": "12th street", + "city": "Pune", + "state": "Maharashtra", + "pinCode": "4520014" + }, + "paymentMethod": "cash", + "status": "pending" } ] } \ No newline at end of file diff --git a/src/features/auth/authSlice.js b/src/features/auth/authSlice.js index b62bebf..441bbfa 100644 --- a/src/features/auth/authSlice.js +++ b/src/features/auth/authSlice.js @@ -41,11 +41,7 @@ export const signOutAsync = createAsyncThunk("user/signOut", async (userId) => { export const authSlice = createSlice({ name: "user", initialState, - reducers: { - increment: (state) => { - state.value += 1; - }, - }, + reducers: {}, extraReducers: (builder) => { builder .addCase(createUserAsync.pending, (state) => { @@ -83,8 +79,6 @@ export const authSlice = createSlice({ }, }); -export const { increment } = authSlice.actions; - export const selectLoggedInUser = (state) => state.auth.loggedInUser; export const selectError = (state) => state.auth.erorr; diff --git a/src/features/cart/cartSlice.js b/src/features/cart/cartSlice.js index 0150466..cd7e4f3 100644 --- a/src/features/cart/cartSlice.js +++ b/src/features/cart/cartSlice.js @@ -56,11 +56,7 @@ export const resetCartAsync = createAsyncThunk( export const cartSlice = createSlice({ name: "cart", initialState, - reducers: { - increment: (state) => { - state.value += 1; - }, - }, + reducers: {}, extraReducers: (builder) => { builder .addCase(addToCartAsync.pending, (state) => { @@ -107,8 +103,6 @@ export const cartSlice = createSlice({ }, }); -export const { increment } = cartSlice.actions; - export const selectItems = (state) => state.cart.items; export default cartSlice.reducer; diff --git a/src/features/order/orderSlice.js b/src/features/order/orderSlice.js index 6309381..dee7022 100644 --- a/src/features/order/orderSlice.js +++ b/src/features/order/orderSlice.js @@ -21,9 +21,6 @@ export const orderSlice = createSlice({ name: "order", initialState, reducers: { - increment: (state) => { - state.value += 1; - }, resetOrder: (state) => { state.currentOrder = null; }, diff --git a/src/features/user/userSlice.js b/src/features/user/userSlice.js index 7ee076e..6505319 100644 --- a/src/features/user/userSlice.js +++ b/src/features/user/userSlice.js @@ -42,11 +42,7 @@ export const updateUserAsync = createAsyncThunk( export const userSlice = createSlice({ name: "user", initialState, - reducers: { - increment: (state) => { - state.value += 1; - }, - }, + reducers: {}, extraReducers: (builder) => { builder .addCase(fetchLoggedInUserOrderAsync.pending, (state) => { @@ -73,7 +69,6 @@ export const userSlice = createSlice({ }, }); -export const { increment } = userSlice.actions; export const selectUserOrders = (state) => state.user.userOrders; export const selectUserInfo = (state) => state.user.userInfo; From 7be4998975d75f4d9f11664285318d56bc0626c4 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Sat, 29 Jun 2024 10:30:38 +0530 Subject: [PATCH 40/47] invoke reset button in checkout page --- data.json | 65 ++++++++++++++++++++++++++++++++++++++++++- src/pages/Checkout.js | 4 ++- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/data.json b/data.json index bcf4618..08834c5 100644 --- a/data.json +++ b/data.json @@ -1993,7 +1993,70 @@ "role": "admin" } ], - "cart": [], + "cart": [ + { + "id": "2889", + "title": "Eyeshadow Palette with Mirror", + "description": "The Eyeshadow Palette with Mirror offers a versatile range of eyeshadow shades for creating stunning eye looks. With a built-in mirror, it's convenient for on-the-go makeup application.", + "category": "beauty", + "price": 19.99, + "discountPercentage": 5.5, + "rating": 3.28, + "stock": 44, + "tags": [ + "beauty", + "eyeshadow" + ], + "brand": "Glamour Beauty", + "sku": "MVCFH27F", + "weight": 3, + "dimensions": { + "width": 12.42, + "height": 8.63, + "depth": 29.13 + }, + "warrantyInformation": "1 year warranty", + "shippingInformation": "Ships in 2 weeks", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 4, + "comment": "Very satisfied!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Liam Garcia", + "reviewerEmail": "liam.garcia@x.dummyjson.com" + }, + { + "rating": 1, + "comment": "Very disappointed!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Nora Russell", + "reviewerEmail": "nora.russell@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Highly impressed!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Elena Baker", + "reviewerEmail": "elena.baker@x.dummyjson.com" + } + ], + "returnPolicy": "30 days return policy", + "minimumOrderQuantity": 32, + "meta": { + "createdAt": "2024-05-23T08:56:21.618Z", + "updatedAt": "2024-05-23T08:56:21.618Z", + "barcode": "2817839095220", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/thumbnail.png", + "quantity": 1, + "user": "cb1b" + } + ], "orders": [ { "id": "d496", diff --git a/src/pages/Checkout.js b/src/pages/Checkout.js index 9558b56..c5b6945 100644 --- a/src/pages/Checkout.js +++ b/src/pages/Checkout.js @@ -245,7 +245,9 @@ function Checkout() { <div className="mt-6 flex items-center justify-end gap-x-6"> <button - type="button" + onClick={() => { + reset(); + }} className="text-sm font-semibold leading-6 text-gray-900" > Reset From a589aa778f0406d12d299768f3608bcea8b99b1b Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Sat, 29 Jun 2024 12:56:42 +0530 Subject: [PATCH 41/47] setValue fix in ProductForm.js --- data.json | 19 ------------------- src/features/user/components/UserProfile.js | 2 +- 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/data.json b/data.json index 08834c5..6bbe642 100644 --- a/data.json +++ b/data.json @@ -1946,25 +1946,6 @@ "state": "Madhya Pradesh", "pinCode": "452002", "address": "Indore" - }, - { - "name": "Jack Hill", - "email": "jack@gmail.com", - "phone": "12345", - "street": "11th road", - "address": "Mumbai", - "state": "Maharastra", - "pinCode": "452003", - "city": "Mumbai" - }, - { - "name": "Brat", - "email": "brat@gmail.com", - "phone": "4568745623", - "street": "12th street", - "city": "Pune", - "state": "Maharashtra", - "pinCode": "4520014" } ] }, diff --git a/src/features/user/components/UserProfile.js b/src/features/user/components/UserProfile.js index 15f253b..d6198e2 100644 --- a/src/features/user/components/UserProfile.js +++ b/src/features/user/components/UserProfile.js @@ -38,7 +38,7 @@ export default function UserProfile() { setValue("email", address.email); setValue("phone", address.phone); setValue("street", address.street); - setValue("address", address.city); + setValue("city", address.city); setValue("state", address.state); setValue("pinCode", address.pinCode); }; From 86bacfa461e9d9e45277828735eafd2dabb750da Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Mon, 1 Jul 2024 15:39:09 +0530 Subject: [PATCH 42/47] AdminOrderPage created (static) --- src/App.js | 9 ++ src/features/admin/components/AdminOrder.js | 102 ++++++++++++++++++++ src/features/navbar/Navbar.js | 1 + src/features/order/orderAPI.js | 13 +++ src/features/order/orderSlice.js | 21 +++- src/pages/AdminOrderPage.js | 13 +++ 6 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 src/features/admin/components/AdminOrder.js create mode 100644 src/pages/AdminOrderPage.js diff --git a/src/App.js b/src/App.js index 0fda088..9c2ad7b 100644 --- a/src/App.js +++ b/src/App.js @@ -30,6 +30,7 @@ import ProtectedAdmin from "./features/auth/components/ProtectedAdmin"; import AdminHome from "./pages/AdminHome"; import AdminProductDetail from "./features/admin/components/AdminProductDetail"; import ProductFormPage from "./pages/ProductFormPage"; +import AdminOrderPage from "./pages/AdminOrderPage"; const router = createBrowserRouter([ { path: "/", @@ -97,6 +98,14 @@ const router = createBrowserRouter([ </ProtectedAdmin> ), }, + { + path: "/admin/orders", + element: ( + <ProtectedAdmin> + <AdminOrderPage></AdminOrderPage> + </ProtectedAdmin> + ), + }, { path: "/admin/product-form/edit/:id", element: ( diff --git a/src/features/admin/components/AdminOrder.js b/src/features/admin/components/AdminOrder.js new file mode 100644 index 0000000..6e6de3f --- /dev/null +++ b/src/features/admin/components/AdminOrder.js @@ -0,0 +1,102 @@ +import { useDispatch, useSelector } from "react-redux"; +import { ITEMS_PER_PAGE } from "../../../app/constants"; +import { useEffect, useState } from "react"; +import { EyeIcon, PencilIcon } from "@heroicons/react/24/outline"; +import { + fetchAllOrdersAsync, + selectOrders, + selectTotalOrders, +} from "../../order/orderSlice"; + +function AdminOrder() { + const [page, setPage] = useState(1); + const dispatch = useDispatch(); + const orders = useSelector(selectOrders); + const totalOrders = useSelector(selectTotalOrders); + useEffect(() => { + const pagination = { _page: page, _per_page: ITEMS_PER_PAGE }; + dispatch(fetchAllOrdersAsync(pagination)); + // TODO:server will filter the deleted products + }, [dispatch, page]); + return ( + <div className="overflow-x-auto"> + <div className=" bg-gray-100 flex items-center justify-center bg-gray-100 font-sans overflow-hidden"> + <div className="w-full"> + <div className="bg-white shadow-md rounded my-6"> + <table className="min-w-max w-full table-auto"> + <thead> + <tr className="bg-gray-200 text-gray-600 uppercase text-sm leading-normal"> + <th className="py-3 px-6 text-left">Order#</th> + <th className="py-3 px-6 text-left">Items</th> + <th className="py-3 px-6 text-center">Total Amount</th> + <th className="py-3 px-6 text-center">Shipping Address</th> + <th className="py-3 px-6 text-center">Status</th> + <th className="py-3 px-6 text-center">Actions</th> + </tr> + </thead> + <tbody className="text-gray-600 text-sm font-light"> + {orders.map((order) => ( + <tr className="border-b border-gray-200 hover:bg-gray-100"> + <td className="py-3 px-6 text-left whitespace-nowrap"> + <div className="flex items-center"> + <span className="font-medium">{order.id}</span> + </div> + </td> + <td className="py-3 px-6 text-left"> + {order.items.map((item) => ( + <div className="flex items-center"> + <div className="mr-2"> + <img + className="w-6 h-6 rounded-full" + src={item.thumbnail} + /> + </div> + <span> + {item.title} -#{item.quantity} -${item.price} + </span> + </div> + ))} + </td> + <td className="py-3 px-6 text-center"> + <div className="flex items-center justify-center"> + ${order.totalAmount} + </div> + </td> + <td className="py-3 px-6 text-center"> + <div> + <div> + <strong>{order.selectedAddress.name}</strong> + </div> + <div> {order.selectedAddress.street}</div> + <div> {order.selectedAddress.city}</div> + <div> {order.selectedAddress.pinCode}</div> + <div>{order.selectedAddress.phone}</div> + </div> + </td> + <td className="py-3 px-6 text-center"> + <span className="bg-red-200 text-red-600 py-1 px-3 rounded-full text-xs"> + {order.status} + </span> + </td> + <td className="py-3 px-6 text-center"> + <div className="flex item-center justify-center"> + <div className="w-4 mr-4 transform hover:text-purple-500 hover:scale-110"> + <EyeIcon className="w-6 h-8"></EyeIcon> + </div> + <div className="w-4 mr-2 transform hover:text-purple-500 hover:scale-110"> + <PencilIcon className="w-6 h-8"></PencilIcon> + </div> + </div> + </td> + </tr> + ))} + </tbody> + </table> + </div> + </div> + </div> + </div> + ); +} + +export default AdminOrder; diff --git a/src/features/navbar/Navbar.js b/src/features/navbar/Navbar.js index 90a3836..80bb946 100644 --- a/src/features/navbar/Navbar.js +++ b/src/features/navbar/Navbar.js @@ -15,6 +15,7 @@ const navigation = [ { name: "Dashboard", link: "#", user: true }, { name: "Team", link: "#", user: true }, { name: "Admin", link: "/admin", admin: true }, + { name: "Orders", link: "/admin/orders", admin: true }, ]; const userNavigation = [ { name: "My Profile", link: "/profile" }, diff --git a/src/features/order/orderAPI.js b/src/features/order/orderAPI.js index a1d4d99..118a1e5 100644 --- a/src/features/order/orderAPI.js +++ b/src/features/order/orderAPI.js @@ -10,3 +10,16 @@ export function createOrder(order) { resolve({ data }); }); } +export function fetchAllOrders(pagination) { + let queryString = ""; + for (let key in pagination) { + queryString += `${key}=${pagination[key]}&`; //_page:3 + } + return new Promise(async (resolve) => { + // TODO: we will not hard coded server url here... + const response = await fetch("http://localhost:8080/orders?" + queryString); + const data = await response.json(); + + resolve({ data: { orders: data.data, totalOrders: data.items } }); + }); +} diff --git a/src/features/order/orderSlice.js b/src/features/order/orderSlice.js index dee7022..6fd25e8 100644 --- a/src/features/order/orderSlice.js +++ b/src/features/order/orderSlice.js @@ -1,11 +1,12 @@ import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; -import { createOrder } from "./orderAPI"; +import { createOrder, fetchAllOrders } from "./orderAPI"; import { resetCart } from "../cart/cartAPI"; const initialState = { orders: [], status: "idle", currentOrder: null, + totalOrders: 0, }; export const createOrderAsync = createAsyncThunk( @@ -16,6 +17,14 @@ export const createOrderAsync = createAsyncThunk( return response.data; } ); +export const fetchAllOrdersAsync = createAsyncThunk( + "order/fetchAllOrders", + async (pagination) => { + const response = await fetchAllOrders(pagination); + // The value we return becomes the `fulfilled` action payload + return response.data; + } +); export const orderSlice = createSlice({ name: "order", @@ -34,6 +43,14 @@ export const orderSlice = createSlice({ state.status = "idle"; state.orders.push(action.payload); state.currentOrder = action.payload; + }) + .addCase(fetchAllOrdersAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(fetchAllOrdersAsync.fulfilled, (state, action) => { + state.status = "idle"; + state.orders = action.payload.orders; + state.totalOrders = action.payload.totalOrders; }); }, }); @@ -41,5 +58,7 @@ export const orderSlice = createSlice({ export const { resetOrder } = orderSlice.actions; export const selectCurrentOrder = (state) => state.order.currentOrder; +export const selectOrders = (state) => state.order.orders; +export const selectTotalOrders = (state) => state.order.totalOrders; export default orderSlice.reducer; diff --git a/src/pages/AdminOrderPage.js b/src/pages/AdminOrderPage.js new file mode 100644 index 0000000..b70bb7b --- /dev/null +++ b/src/pages/AdminOrderPage.js @@ -0,0 +1,13 @@ +import AdminOrder from "../features/admin/components/AdminOrder"; +import NavBar from "../features/navbar/Navbar"; +function AdminOrderPage() { + return ( + <div> + <NavBar> + <AdminOrder></AdminOrder> + </NavBar> + </div> + ); +} + +export default AdminOrderPage; From 290a6f95ab9106793d53e7f7f3d3555c7f503658 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Mon, 1 Jul 2024 20:22:05 +0530 Subject: [PATCH 43/47] make constant of discountedPrice()/ cancle buttons works in product form --- data.json | 8 +++++--- src/app/constants.js | 4 ++++ src/features/admin/components/AdminOrder.js | 5 +++-- src/features/admin/components/AdminProductDetail.js | 6 +++++- src/features/admin/components/AdminProductList.js | 10 +++------- src/features/admin/components/ProductForm.js | 7 ++++--- src/features/cart/Cart.js | 5 +++-- src/features/product/components/ProductDetail.js | 4 ++++ src/features/product/components/ProductList.js | 7 ++----- src/features/user/components/UserOrders.js | 3 ++- src/pages/Checkout.js | 5 +++-- 11 files changed, 38 insertions(+), 26 deletions(-) diff --git a/data.json b/data.json index 6bbe642..33ec553 100644 --- a/data.json +++ b/data.json @@ -247,7 +247,7 @@ }, { "id": "5", - "title": "Red Nail Polish", + "title": "Red Nail Polish Premium", "description": "The Red Nail Polish offers a rich and glossy red hue for vibrant and polished nails. With a quick-drying formula, it provides a salon-quality finish at home.", "category": "beauty", "price": 8.99, @@ -301,9 +301,11 @@ "qrCode": "https://dummyjson.com/public/qr-code.png" }, "images": [ - "https://cdn.dummyjson.com/products/images/beauty/Red%20Nail%20Polish/1.png" + "https://cdn.dummyjson.com/products/images/beauty/Red%20Nail%20Polish/1.png", + "https://cdn.dummyjson.com/products/images/beauty/Red%20Nail%20Polish/thumbnail.png" ], - "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Red%20Nail%20Polish/thumbnail.png" + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Red%20Nail%20Polish/thumbnail.png", + "discount": null }, { "id": "6", diff --git a/src/app/constants.js b/src/app/constants.js index 7904744..5c2c0b7 100644 --- a/src/app/constants.js +++ b/src/app/constants.js @@ -1 +1,5 @@ export const ITEMS_PER_PAGE = 6; +export function discountedPrice(item) { + const discountPercentage = item.discountPercentage || 0; // Ensure discountPercentage is defined or default to 0 + return Math.round(item.price * (1 - item.discountPercentage / 100)); +} diff --git a/src/features/admin/components/AdminOrder.js b/src/features/admin/components/AdminOrder.js index 6e6de3f..8239672 100644 --- a/src/features/admin/components/AdminOrder.js +++ b/src/features/admin/components/AdminOrder.js @@ -1,5 +1,5 @@ import { useDispatch, useSelector } from "react-redux"; -import { ITEMS_PER_PAGE } from "../../../app/constants"; +import { ITEMS_PER_PAGE, discountedPrice } from "../../../app/constants"; import { useEffect, useState } from "react"; import { EyeIcon, PencilIcon } from "@heroicons/react/24/outline"; import { @@ -52,7 +52,8 @@ function AdminOrder() { /> </div> <span> - {item.title} -#{item.quantity} -${item.price} + {item.title} -#{item.quantity} -$ + {discountedPrice(item)} </span> </div> ))} diff --git a/src/features/admin/components/AdminProductDetail.js b/src/features/admin/components/AdminProductDetail.js index f80af7c..a45fc0a 100644 --- a/src/features/admin/components/AdminProductDetail.js +++ b/src/features/admin/components/AdminProductDetail.js @@ -9,6 +9,7 @@ import { import { useParams } from "react-router-dom"; import { addToCartAsync } from "../../cart/cartSlice"; import { selectLoggedInUser } from "../../auth/authSlice"; +import { discountedPrice } from "../../../app/constants"; // TODO: In server data we will add sizes,colors, highlights to each product @@ -145,9 +146,12 @@ export default function AdminProductDetail() { {/* Options */} <div className="mt-4 lg:row-span-3 lg:mt-0"> <h2 className="sr-only">Product information</h2> - <p className="text-3xl tracking-tight text-gray-900"> + <p className="text-3xl line-through tracking-tight text-gray-900"> ${product.price} </p> + <p className="text-3xl tracking-tight text-gray-900"> + ${discountedPrice(product)} + </p> {/* Reviews */} <div className="mt-6"> diff --git a/src/features/admin/components/AdminProductList.js b/src/features/admin/components/AdminProductList.js index cdec39d..bb91cbc 100644 --- a/src/features/admin/components/AdminProductList.js +++ b/src/features/admin/components/AdminProductList.js @@ -21,7 +21,7 @@ import { PlusIcon, Squares2X2Icon, } from "@heroicons/react/20/solid"; -import { ITEMS_PER_PAGE } from "../../../app/constants"; +import { ITEMS_PER_PAGE, discountedPrice } from "../../../app/constants"; const sortOptions = [ { name: "Best Rating", sort: "rating", current: false }, @@ -406,7 +406,7 @@ function ProductGrid({ products }) { <div className="mt-6 grid grid-cols-1 gap-x-6 gap-y-10 sm:grid-cols-2 lg:grid-cols-3 xl:gap-x-8"> {products.map((product) => ( <div> - <Link to={`/product-detail/${product.id}`}> + <Link to={`/admin/product-detail/${product.id}`}> <div> <div key={product.id} @@ -437,11 +437,7 @@ function ProductGrid({ products }) { </div> <div> <p className="text-sm font-medium text-gray-900"> - $ - {Math.round( - product.price * - (1 - product.discountPercentage / 100) - )} + ${discountedPrice(product)} <p className="text-sm line-through font-medium text-gray-400"> ${product.price} </p> diff --git a/src/features/admin/components/ProductForm.js b/src/features/admin/components/ProductForm.js index 470d3c3..10fa745 100644 --- a/src/features/admin/components/ProductForm.js +++ b/src/features/admin/components/ProductForm.js @@ -11,7 +11,7 @@ import { import { PhotoIcon, UserCircleIcon } from "@heroicons/react/24/solid"; import { useForm } from "react-hook-form"; import { useEffect } from "react"; -import { useParams } from "react-router-dom"; +import { Link, useParams } from "react-router-dom"; function ProductForm() { const categories = useSelector(selectCategories); const brands = useSelector(selectBrands); @@ -359,12 +359,13 @@ function ProductForm() { </div> </div> <div className="mt-6 flex items-center justify-end gap-x-6"> - <button + <Link + to="/admin" type="button" className="text-sm font-semibold leading-6 text-gray-900" > Cancel - </button> + </Link> {selectedProduct && ( <button onClick={handleDelete} diff --git a/src/features/cart/Cart.js b/src/features/cart/Cart.js index fb756df..e7e9c14 100644 --- a/src/features/cart/Cart.js +++ b/src/features/cart/Cart.js @@ -9,13 +9,14 @@ import { Dialog, Transition } from "@headlessui/react"; import { XMarkIcon } from "@heroicons/react/24/outline"; import { Link } from "react-router-dom"; import { Navigate } from "react-router-dom"; +import { discountedPrice } from "../../app/constants"; export default function Cart() { const dispatch = useDispatch(); const [open, setOpen] = useState(true); const items = useSelector(selectItems); const totalAmount = items.reduce( - (amount, item) => item.price * item.quantity + amount, + (amount, item) => discountedPrice(item) * item.quantity + amount, 0 ); const totalItems = items.reduce((total, item) => item.quantity + total, 0); @@ -53,7 +54,7 @@ export default function Cart() { <h3> <a href={item.href}>{item.title}</a> </h3> - <p className="ml-4">${item.price}</p> + <p className="ml-4">${discountedPrice(item)}</p> </div> <p className="mt-1 text-sm text-gray-500"> {item.brand} diff --git a/src/features/product/components/ProductDetail.js b/src/features/product/components/ProductDetail.js index 177cf0a..988a8c9 100644 --- a/src/features/product/components/ProductDetail.js +++ b/src/features/product/components/ProductDetail.js @@ -6,6 +6,7 @@ import { fetchProductByIdAsync, selectProductById } from "../productSlice"; import { useParams } from "react-router-dom"; import { addToCartAsync } from "../../cart/cartSlice"; import { selectLoggedInUser } from "../../auth/authSlice"; +import { discountedPrice } from "../../../app/constants"; // TODO: In server data we will add sizes,colors, highlights to each product @@ -143,6 +144,9 @@ export default function ProductDetail() { <div className="mt-4 lg:row-span-3 lg:mt-0"> <h2 className="sr-only">Product information</h2> <p className="text-3xl tracking-tight text-gray-900"> + ${discountedPrice(product)} + </p> + <p className="text-3xl line-through tracking-tight text-gray-900"> ${product.price} </p> diff --git a/src/features/product/components/ProductList.js b/src/features/product/components/ProductList.js index 74643d8..0a00f8f 100644 --- a/src/features/product/components/ProductList.js +++ b/src/features/product/components/ProductList.js @@ -21,7 +21,7 @@ import { PlusIcon, Squares2X2Icon, } from "@heroicons/react/20/solid"; -import { ITEMS_PER_PAGE } from "../../../app/constants"; +import { ITEMS_PER_PAGE, discountedPrice } from "../../../app/constants"; const sortOptions = [ { name: "Best Rating", sort: "rating", current: false }, @@ -424,10 +424,7 @@ function ProductGrid({ products }) { </div> <div> <p className="text-sm font-medium text-gray-900"> - $ - {Math.round( - product.price * (1 - product.discountPercentage / 100) - )} + ${discountedPrice(product)} <p className="text-sm line-through font-medium text-gray-400"> ${product.price} </p> diff --git a/src/features/user/components/UserOrders.js b/src/features/user/components/UserOrders.js index e94bbee..9f4ea67 100644 --- a/src/features/user/components/UserOrders.js +++ b/src/features/user/components/UserOrders.js @@ -5,6 +5,7 @@ import { selectUserInfo, selectUserOrders, } from "../userSlice"; +import { discountedPrice } from "../../../app/constants"; export default function UserOrders() { const user = useSelector(selectUserInfo); @@ -46,7 +47,7 @@ export default function UserOrders() { <h3> <a href={item.href}>{item.title}</a> </h3> - <p className="ml-4">${item.price}</p> + <p className="ml-4">${discountedPrice(item)}</p> </div> <p className="mt-1 text-sm text-gray-500"> {item.brand} diff --git a/src/pages/Checkout.js b/src/pages/Checkout.js index c5b6945..5ac2377 100644 --- a/src/pages/Checkout.js +++ b/src/pages/Checkout.js @@ -14,6 +14,7 @@ import { } from "../features/order/orderSlice"; import { useState } from "react"; import { selectUserInfo } from "../features/user/userSlice"; +import { discountedPrice } from "../app/constants"; function Checkout() { const dispatch = useDispatch(); @@ -28,7 +29,7 @@ function Checkout() { const items = useSelector(selectItems); const currentOrder = useSelector(selectCurrentOrder); const totalAmount = items.reduce( - (amount, item) => item.price * item.quantity + amount, + (amount, item) => discountedPrice(item) * item.quantity + amount, 0 ); const totalItems = items.reduce((total, item) => item.quantity + total, 0); @@ -379,7 +380,7 @@ function Checkout() { <h3> <a href={item.href}>{item.title}</a> </h3> - <p className="ml-4">${item.price}</p> + <p className="ml-4">${discountedPrice(item)}</p> </div> <p className="mt-1 text-sm text-gray-500"> {item.brand} From 88cd7bcb5af0ecf74aa43f76cd00b38a9bacbd16 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Tue, 2 Jul 2024 12:40:47 +0530 Subject: [PATCH 44/47] edit functionality in adminOrderPage/fix route of AdminDetailPage --- data.json | 8 +- src/features/admin/components/AdminOrder.js | 99 +++++++++++++++---- .../admin/components/AdminProductList.js | 2 +- src/features/order/orderAPI.js | 14 ++- src/features/order/orderSlice.js | 20 +++- 5 files changed, 118 insertions(+), 25 deletions(-) diff --git a/data.json b/data.json index 33ec553..75f0c65 100644 --- a/data.json +++ b/data.json @@ -2206,7 +2206,7 @@ "pinCode": "452002" }, "paymentMethod": "cash", - "status": "pending" + "status": "dispatched" }, { "id": "f1e8", @@ -2364,7 +2364,7 @@ "pinCode": "4520012" }, "paymentMethod": "card", - "status": "pending" + "status": "dispatched" }, { "id": "6cf3", @@ -2529,7 +2529,7 @@ "pinCode": "452002" }, "paymentMethod": "card", - "status": "pending" + "status": "dispatched" }, { "id": "3802", @@ -2635,7 +2635,7 @@ "city": "Mumbai" }, "paymentMethod": "card", - "status": "pending" + "status": "delivered" }, { "id": "5c73", diff --git a/src/features/admin/components/AdminOrder.js b/src/features/admin/components/AdminOrder.js index 8239672..6e58712 100644 --- a/src/features/admin/components/AdminOrder.js +++ b/src/features/admin/components/AdminOrder.js @@ -6,6 +6,7 @@ import { fetchAllOrdersAsync, selectOrders, selectTotalOrders, + updateOrderAsync, } from "../../order/orderSlice"; function AdminOrder() { @@ -13,16 +14,50 @@ function AdminOrder() { const dispatch = useDispatch(); const orders = useSelector(selectOrders); const totalOrders = useSelector(selectTotalOrders); + const [editableOrderId, setEditableOrderId] = useState(-1); + + const handleEdit = (order) => { + console.log("Editing order ID:", order.id); + setEditableOrderId(order.id); + }; + + const handleShow = () => { + console.log("show"); + }; + + const handleUpdate = (e, order) => { + const updatedOrder = { ...order, status: e.target.value }; + dispatch(updateOrderAsync(updatedOrder)); + setEditableOrderId(-1); // Optionally reset the editable order ID after update + }; + const chooseColor = (status) => { + switch (status) { + case "pending": + return "bg-purple-200 ms-4 text-purple-600 "; + case "dispatched": + return "bg-yellow-200 ms-4 text-yellow-600 "; + case "delivered": + return "bg-green-200 ms-4 text-green-600 "; + case "pending": + return "bg-red-200 ms-4 text-red-600 "; + case "cancelled": + return "bg-red-200 ms-4 text-red-600 "; + default: + return "bg-purple-200 ms-4 text-purple-600 "; + } + }; + useEffect(() => { const pagination = { _page: page, _per_page: ITEMS_PER_PAGE }; dispatch(fetchAllOrdersAsync(pagination)); // TODO:server will filter the deleted products }, [dispatch, page]); + return ( - <div className="overflow-x-auto"> - <div className=" bg-gray-100 flex items-center justify-center bg-gray-100 font-sans overflow-hidden"> + <div className="overflow-x-auto w-full"> + <div className="bg-gray-100 flex items-center justify-center bg-gray-100 font-sans overflow-hidden w-full"> <div className="w-full"> - <div className="bg-white shadow-md rounded my-6"> + <div className="bg-white shadow-md rounded my-6 w-full"> <table className="min-w-max w-full table-auto"> <thead> <tr className="bg-gray-200 text-gray-600 uppercase text-sm leading-normal"> @@ -36,7 +71,10 @@ function AdminOrder() { </thead> <tbody className="text-gray-600 text-sm font-light"> {orders.map((order) => ( - <tr className="border-b border-gray-200 hover:bg-gray-100"> + <tr + className="border-b border-gray-200 hover:bg-gray-100" + key={order.id} + > <td className="py-3 px-6 text-left whitespace-nowrap"> <div className="flex items-center"> <span className="font-medium">{order.id}</span> @@ -44,15 +82,16 @@ function AdminOrder() { </td> <td className="py-3 px-6 text-left"> {order.items.map((item) => ( - <div className="flex items-center"> + <div className="flex items-center" key={item.id}> <div className="mr-2"> <img className="w-6 h-6 rounded-full" src={item.thumbnail} + alt={item.title} /> </div> <span> - {item.title} -#{item.quantity} -$ + {item.title} - #{item.quantity} - $ {discountedPrice(item)} </span> </div> @@ -68,24 +107,48 @@ function AdminOrder() { <div> <strong>{order.selectedAddress.name}</strong> </div> - <div> {order.selectedAddress.street}</div> - <div> {order.selectedAddress.city}</div> - <div> {order.selectedAddress.pinCode}</div> + <div>{order.selectedAddress.street}</div> + <div>{order.selectedAddress.city}</div> + <div>{order.selectedAddress.pinCode}</div> <div>{order.selectedAddress.phone}</div> </div> </td> - <td className="py-3 px-6 text-center"> - <span className="bg-red-200 text-red-600 py-1 px-3 rounded-full text-xs"> - {order.status} - </span> + <td className=" text-center"> + {order.id === editableOrderId ? ( + <div className="flex items-center justify-center"> + <select + onChange={(e) => handleUpdate(e, order)} + value={order.status} + > + <option value="pending">Pending</option> + <option value="dispatched">Dispatched</option> + <option value="delivered">Delivered</option> + <option value="cancelled">Cancelled</option> + </select> + </div> + ) : ( + <span + className={`${chooseColor( + order.status + )}py-1 px-3 rounded-full text-xs`} + > + {order.status} + </span> + )} </td> <td className="py-3 px-6 text-center"> - <div className="flex item-center justify-center"> - <div className="w-4 mr-4 transform hover:text-purple-500 hover:scale-110"> - <EyeIcon className="w-6 h-8"></EyeIcon> + <div className="flex items-center justify-center"> + <div className="w-2 mr-6 transform hover:text-purple-500 hover:scale-110"> + <EyeIcon + className="w-6 h-6" + onClick={() => handleShow(order)} + /> </div> - <div className="w-4 mr-2 transform hover:text-purple-500 hover:scale-110"> - <PencilIcon className="w-6 h-8"></PencilIcon> + <div className="w-2 transform hover:text-purple-500 hover:scale-110"> + <PencilIcon + className="w-6 h-6" + onClick={() => handleEdit(order)} + /> </div> </div> </td> diff --git a/src/features/admin/components/AdminProductList.js b/src/features/admin/components/AdminProductList.js index bb91cbc..872e0f0 100644 --- a/src/features/admin/components/AdminProductList.js +++ b/src/features/admin/components/AdminProductList.js @@ -406,7 +406,7 @@ function ProductGrid({ products }) { <div className="mt-6 grid grid-cols-1 gap-x-6 gap-y-10 sm:grid-cols-2 lg:grid-cols-3 xl:gap-x-8"> {products.map((product) => ( <div> - <Link to={`/admin/product-detail/${product.id}`}> + <Link to={`/product-detail/${product.id}`}> <div> <div key={product.id} diff --git a/src/features/order/orderAPI.js b/src/features/order/orderAPI.js index 118a1e5..12c6b72 100644 --- a/src/features/order/orderAPI.js +++ b/src/features/order/orderAPI.js @@ -6,7 +6,19 @@ export function createOrder(order) { headers: { "content-type": "application/json" }, }); const data = await response.json(); - // TODO:on server it will only return some info of user (not password) + + resolve({ data }); + }); +} +export function updateOrder(order) { + return new Promise(async (resolve) => { + const response = await fetch("http://localhost:8080/orders/" + order.id, { + method: "PATCH", + body: JSON.stringify(order), + headers: { "content-type": "application/json" }, + }); + const data = await response.json(); + resolve({ data }); }); } diff --git a/src/features/order/orderSlice.js b/src/features/order/orderSlice.js index 6fd25e8..59c4889 100644 --- a/src/features/order/orderSlice.js +++ b/src/features/order/orderSlice.js @@ -1,5 +1,5 @@ import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; -import { createOrder, fetchAllOrders } from "./orderAPI"; +import { createOrder, fetchAllOrders, updateOrder } from "./orderAPI"; import { resetCart } from "../cart/cartAPI"; const initialState = { @@ -17,6 +17,14 @@ export const createOrderAsync = createAsyncThunk( return response.data; } ); +export const updateOrderAsync = createAsyncThunk( + "order/updateOrder", + async (order) => { + const response = await updateOrder(order); + // The value we return becomes the `fulfilled` action payload + return response.data; + } +); export const fetchAllOrdersAsync = createAsyncThunk( "order/fetchAllOrders", async (pagination) => { @@ -44,6 +52,16 @@ export const orderSlice = createSlice({ state.orders.push(action.payload); state.currentOrder = action.payload; }) + .addCase(updateOrderAsync.pending, (state) => { + state.status = "loading"; + }) + .addCase(updateOrderAsync.fulfilled, (state, action) => { + state.status = "idle"; + const index = state.orders.findIndex( + (order) => order.id === action.payload.id + ); + state.orders[index] = action.payload; + }) .addCase(fetchAllOrdersAsync.pending, (state) => { state.status = "loading"; }) From 303bc939030e511df58f16848688d5553b639916 Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Tue, 2 Jul 2024 17:05:13 +0530 Subject: [PATCH 45/47] pagination added it AdminOrder Page (by making pagination a common component) --- data.json | 376 +++++++++++++++--- src/features/admin/components/AdminOrder.js | 16 +- src/features/common/Pagination.js | 81 ++++ .../product/components/ProductList.js | 80 +--- 4 files changed, 407 insertions(+), 146 deletions(-) create mode 100644 src/features/common/Pagination.js diff --git a/data.json b/data.json index 75f0c65..8dd6b23 100644 --- a/data.json +++ b/data.json @@ -1972,74 +1972,21 @@ "id": "86e2", "email": "admin@gmail.com", "password": "Admin123", - "addresses": [], - "role": "admin" - } - ], - "cart": [ - { - "id": "2889", - "title": "Eyeshadow Palette with Mirror", - "description": "The Eyeshadow Palette with Mirror offers a versatile range of eyeshadow shades for creating stunning eye looks. With a built-in mirror, it's convenient for on-the-go makeup application.", - "category": "beauty", - "price": 19.99, - "discountPercentage": 5.5, - "rating": 3.28, - "stock": 44, - "tags": [ - "beauty", - "eyeshadow" - ], - "brand": "Glamour Beauty", - "sku": "MVCFH27F", - "weight": 3, - "dimensions": { - "width": 12.42, - "height": 8.63, - "depth": 29.13 - }, - "warrantyInformation": "1 year warranty", - "shippingInformation": "Ships in 2 weeks", - "availabilityStatus": "In Stock", - "reviews": [ - { - "rating": 4, - "comment": "Very satisfied!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Liam Garcia", - "reviewerEmail": "liam.garcia@x.dummyjson.com" - }, - { - "rating": 1, - "comment": "Very disappointed!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Nora Russell", - "reviewerEmail": "nora.russell@x.dummyjson.com" - }, + "addresses": [ { - "rating": 5, - "comment": "Highly impressed!", - "date": "2024-05-23T08:56:21.618Z", - "reviewerName": "Elena Baker", - "reviewerEmail": "elena.baker@x.dummyjson.com" + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" } ], - "returnPolicy": "30 days return policy", - "minimumOrderQuantity": 32, - "meta": { - "createdAt": "2024-05-23T08:56:21.618Z", - "updatedAt": "2024-05-23T08:56:21.618Z", - "barcode": "2817839095220", - "qrCode": "https://dummyjson.com/public/qr-code.png" - }, - "images": [ - "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/1.png" - ], - "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/thumbnail.png", - "quantity": 1, - "user": "cb1b" + "role": "admin" } ], + "cart": [], "orders": [ { "id": "d496", @@ -2364,7 +2311,7 @@ "pinCode": "4520012" }, "paymentMethod": "card", - "status": "dispatched" + "status": "delivered" }, { "id": "6cf3", @@ -2976,6 +2923,307 @@ }, "paymentMethod": "cash", "status": "pending" + }, + { + "id": "9337", + "items": [ + { + "id": "bccc", + "title": "Eyeshadow Palette with Mirror", + "description": "The Eyeshadow Palette with Mirror offers a versatile range of eyeshadow shades for creating stunning eye looks. With a built-in mirror, it's convenient for on-the-go makeup application.", + "category": "beauty", + "price": 19.99, + "discountPercentage": 5.5, + "rating": 3.28, + "stock": 44, + "tags": [ + "beauty", + "eyeshadow" + ], + "brand": "Glamour Beauty", + "sku": "MVCFH27F", + "weight": 3, + "dimensions": { + "width": 12.42, + "height": 8.63, + "depth": 29.13 + }, + "warrantyInformation": "1 year warranty", + "shippingInformation": "Ships in 2 weeks", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 4, + "comment": "Very satisfied!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Liam Garcia", + "reviewerEmail": "liam.garcia@x.dummyjson.com" + }, + { + "rating": 1, + "comment": "Very disappointed!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Nora Russell", + "reviewerEmail": "nora.russell@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Highly impressed!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Elena Baker", + "reviewerEmail": "elena.baker@x.dummyjson.com" + } + ], + "returnPolicy": "30 days return policy", + "minimumOrderQuantity": 32, + "meta": { + "createdAt": "2024-05-23T08:56:21.618Z", + "updatedAt": "2024-05-23T08:56:21.618Z", + "barcode": "2817839095220", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/thumbnail.png", + "quantity": 1, + "user": "cb1b" + } + ], + "totalAmount": 19, + "totalItems": 1, + "user": { + "id": "cb1b", + "email": "test@123.com", + "password": "Murtaza123", + "role": "user", + "addresses": [ + { + "name": "Murtaza ", + "email": "murtazashabbir14@gmail.com", + "phone": "7869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002", + "address": "Indore" + } + ] + }, + "selectedAddress": { + "name": "Murtaza ", + "email": "murtazashabbir14@gmail.com", + "phone": "7869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002", + "address": "Indore" + }, + "paymentMethod": "card", + "status": "pending" + }, + { + "id": "c7ba", + "items": [ + { + "id": "3e3b", + "title": "Essence Mascara Lash 2", + "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": 0, + "stock": 5, + "tags": [ + "beauty", + "mascara" + ], + "brand": "Essence", + "sku": "RCH45Q1A", + "weight": 2, + "dimensions": { + "width": 23.17, + "height": 14.43, + "depth": 28.01 + }, + "warrantyInformation": "1 month warranty", + "shippingInformation": "Ships in 1 month", + "availabilityStatus": "Low Stock", + "reviews": [ + { + "rating": 2, + "comment": "Very unhappy with my purchase!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "John Doe", + "reviewerEmail": "john.doe@x.dummyjson.com" + }, + { + "rating": 2, + "comment": "Not as described!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Nolan Gonzalez", + "reviewerEmail": "nolan.gonzalez@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Very satisfied!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Scarlett Wright", + "reviewerEmail": "scarlett.wright@x.dummyjson.com" + } + ], + "returnPolicy": "30 days return policy", + "minimumOrderQuantity": 24, + "meta": { + "createdAt": "2024-05-23T08:56:21.618Z", + "updatedAt": "2024-05-23T08:56:21.618Z", + "barcode": "9164035109868", + "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/1.png", + "discount": null, + "deleted": true, + "quantity": 1, + "user": "86e2" + } + ], + "totalAmount": 9, + "totalItems": 1, + "user": { + "id": "86e2", + "email": "admin@gmail.com", + "password": "Admin123", + "addresses": [ + { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + } + ], + "role": "admin" + }, + "selectedAddress": { + "name": "Murtaza Tankiwala", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + }, + "paymentMethod": "card", + "status": "pending" + }, + { + "id": "e222", + "items": [ + { + "id": "5891", + "title": "Essence Mascara Lash 2", + "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": 0, + "stock": 5, + "tags": [ + "beauty", + "mascara" + ], + "brand": "Essence", + "sku": "RCH45Q1A", + "weight": 2, + "dimensions": { + "width": 23.17, + "height": 14.43, + "depth": 28.01 + }, + "warrantyInformation": "1 month warranty", + "shippingInformation": "Ships in 1 month", + "availabilityStatus": "Low Stock", + "reviews": [ + { + "rating": 2, + "comment": "Very unhappy with my purchase!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "John Doe", + "reviewerEmail": "john.doe@x.dummyjson.com" + }, + { + "rating": 2, + "comment": "Not as described!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Nolan Gonzalez", + "reviewerEmail": "nolan.gonzalez@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Very satisfied!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Scarlett Wright", + "reviewerEmail": "scarlett.wright@x.dummyjson.com" + } + ], + "returnPolicy": "30 days return policy", + "minimumOrderQuantity": 24, + "meta": { + "createdAt": "2024-05-23T08:56:21.618Z", + "updatedAt": "2024-05-23T08:56:21.618Z", + "barcode": "9164035109868", + "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/1.png", + "discount": null, + "deleted": true, + "quantity": 1, + "user": "cb1b" + } + ], + "totalAmount": 9, + "totalItems": 1, + "user": { + "id": "cb1b", + "email": "test@123.com", + "password": "Murtaza123", + "role": "user", + "addresses": [ + { + "name": "Murtaza ", + "email": "murtazashabbir14@gmail.com", + "phone": "7869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002", + "address": "Indore" + } + ] + }, + "selectedAddress": { + "name": "Murtaza ", + "email": "murtazashabbir14@gmail.com", + "phone": "7869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002", + "address": "Indore" + }, + "paymentMethod": "cash", + "status": "pending" } ] } \ No newline at end of file diff --git a/src/features/admin/components/AdminOrder.js b/src/features/admin/components/AdminOrder.js index 6e58712..1c97d1e 100644 --- a/src/features/admin/components/AdminOrder.js +++ b/src/features/admin/components/AdminOrder.js @@ -8,6 +8,7 @@ import { selectTotalOrders, updateOrderAsync, } from "../../order/orderSlice"; +import Pagination from "../../common/Pagination"; function AdminOrder() { const [page, setPage] = useState(1); @@ -46,11 +47,14 @@ function AdminOrder() { return "bg-purple-200 ms-4 text-purple-600 "; } }; - - useEffect(() => { + const handlePage = (page) => { + setPage(page); const pagination = { _page: page, _per_page: ITEMS_PER_PAGE }; dispatch(fetchAllOrdersAsync(pagination)); - // TODO:server will filter the deleted products + }; + + useEffect(() => { + handlePage(page); }, [dispatch, page]); return ( @@ -159,6 +163,12 @@ function AdminOrder() { </div> </div> </div> + <Pagination + page={page} + setPage={setPage} + handlePage={handlePage} + totalItems={totalOrders} + /> </div> ); } diff --git a/src/features/common/Pagination.js b/src/features/common/Pagination.js new file mode 100644 index 0000000..6c01988 --- /dev/null +++ b/src/features/common/Pagination.js @@ -0,0 +1,81 @@ +import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/24/outline"; +import { ITEMS_PER_PAGE } from "../../app/constants"; + +export default function Pagination({ page, setPage, handlePage, totalItems }) { + const totalPages = Math.ceil(totalItems / ITEMS_PER_PAGE); + return ( + <div className="flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6"> + <div className="flex flex-1 justify-between sm:hidden"> + <a + onClick={() => handlePage(page > 1 ? page - 1 : page)} + className="relative inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50" + > + Previous + </a> + <div + onClick={() => handlePage(page < totalPages ? page + 1 : page)} + className="relative ml-3 inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50" + > + Next + </div> + </div> + <div className="hidden sm:flex sm:flex-1 sm:items-center sm:justify-between"> + <div> + <p className="text-sm text-gray-700"> + Showing {/* (3-1) * 6 + 1 =13 */} + <span className="font-medium"> + {(page - 1) * ITEMS_PER_PAGE + 1} + </span>{" "} + {/* 3 * 6 = 18 */} + to{" "} + <span className="font-medium"> + {page * ITEMS_PER_PAGE > totalItems + ? totalItems + : page * ITEMS_PER_PAGE}{" "} + </span>{" "} + of <span className="font-medium">{totalItems}</span> results + </p> + </div> + <div> + <nav + className="isolate inline-flex -space-x-px rounded-md shadow-sm" + aria-label="Pagination" + > + <div + onClick={() => handlePage(page > 1 ? page - 1 : page)} + className="relative inline-flex items-center rounded-l-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" + > + <span className="sr-only">Previous</span> + <ChevronLeftIcon className="h-5 w-5" aria-hidden="true" /> + </div> + {/* Current: "z-10 bg-indigo-600 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600", Default: "text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:outline-offset-0" */} + {Array.from({ length: totalPages }).map( + //extracting all the indexes of array + (el, index) => ( + <div + onClick={() => handlePage(index + 1)} + aria-current="page" + className={`relative z-10 inline-flex items-center ${ + index + 1 === page + ? "bg-indigo-600 text-white" + : "text-gray-400 " + } px-4 py-2 text-sm font-semibold cursor-pointer focus:z-20 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600`} + > + {index + 1} + </div> + ) + )} + + <div + onClick={() => handlePage(page < totalPages ? page + 1 : page)} + className="relative inline-flex items-center rounded-r-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" + > + <span className="sr-only">Next</span> + <ChevronRightIcon className="h-5 w-5" aria-hidden="true" /> + </div> + </nav> + </div> + </div> + </div> + ); +} diff --git a/src/features/product/components/ProductList.js b/src/features/product/components/ProductList.js index 0a00f8f..1f48240 100644 --- a/src/features/product/components/ProductList.js +++ b/src/features/product/components/ProductList.js @@ -22,6 +22,7 @@ import { Squares2X2Icon, } from "@heroicons/react/20/solid"; import { ITEMS_PER_PAGE, discountedPrice } from "../../../app/constants"; +import Pagination from "../../common/Pagination"; const sortOptions = [ { name: "Best Rating", sort: "rating", current: false }, @@ -439,82 +440,3 @@ function ProductGrid({ products }) { </div> ); } - -function Pagination({ page, setPage, handlePage, totalItems }) { - const totalPages = Math.ceil(totalItems / ITEMS_PER_PAGE); - return ( - <div className="flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6"> - <div className="flex flex-1 justify-between sm:hidden"> - <a - onClick={() => handlePage(page > 1 ? page - 1 : page)} - className="relative inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50" - > - Previous - </a> - <div - onClick={() => handlePage(page < totalPages ? page + 1 : page)} - className="relative ml-3 inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50" - > - Next - </div> - </div> - <div className="hidden sm:flex sm:flex-1 sm:items-center sm:justify-between"> - <div> - <p className="text-sm text-gray-700"> - Showing {/* (3-1) * 6 + 1 =13 */} - <span className="font-medium"> - {(page - 1) * ITEMS_PER_PAGE + 1} - </span>{" "} - {/* 3 * 6 = 18 */} - to{" "} - <span className="font-medium"> - {page * ITEMS_PER_PAGE > totalItems - ? totalItems - : page * ITEMS_PER_PAGE}{" "} - </span>{" "} - of <span className="font-medium">{totalItems}</span> results - </p> - </div> - <div> - <nav - className="isolate inline-flex -space-x-px rounded-md shadow-sm" - aria-label="Pagination" - > - <div - onClick={() => handlePage(page > 1 ? page - 1 : page)} - className="relative inline-flex items-center rounded-l-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" - > - <span className="sr-only">Previous</span> - <ChevronLeftIcon className="h-5 w-5" aria-hidden="true" /> - </div> - {/* Current: "z-10 bg-indigo-600 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600", Default: "text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:outline-offset-0" */} - {Array.from({ length: totalPages }).map( - //extracting all the indexes of array - (el, index) => ( - <div - onClick={() => handlePage(index + 1)} - aria-current="page" - className={`relative z-10 inline-flex items-center ${ - index + 1 === page - ? "bg-indigo-600 text-white" - : "text-gray-400 " - } px-4 py-2 text-sm font-semibold cursor-pointer focus:z-20 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600`} - > - {index + 1} - </div> - ) - )} - - <div - onClick={() => handlePage(page < totalPages ? page + 1 : page)} - className="relative inline-flex items-center rounded-r-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" - > - <span className="sr-only">Next</span> - <ChevronRightIcon className="h-5 w-5" aria-hidden="true" /> - </div> - </nav> - </div> - </div> - </div> - ); -} From 92a66e402cf0ae03a6731f73cf80de3bc7f34f4f Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Tue, 2 Jul 2024 18:37:21 +0530 Subject: [PATCH 46/47] fix pagination logic in AdminOrder --- data.json | 298 +++++++++++++++++++- src/features/admin/components/AdminOrder.js | 3 +- 2 files changed, 299 insertions(+), 2 deletions(-) diff --git a/data.json b/data.json index 8dd6b23..0a478d9 100644 --- a/data.json +++ b/data.json @@ -3021,7 +3021,7 @@ "address": "Indore" }, "paymentMethod": "card", - "status": "pending" + "status": "cancelled" }, { "id": "c7ba", @@ -3224,6 +3224,302 @@ }, "paymentMethod": "cash", "status": "pending" + }, + { + "id": "dcc5", + "items": [ + { + "id": "f1e2", + "title": "Essence Mascara Lash 2", + "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": 0, + "stock": 5, + "tags": [ + "beauty", + "mascara" + ], + "brand": "Essence", + "sku": "RCH45Q1A", + "weight": 2, + "dimensions": { + "width": 23.17, + "height": 14.43, + "depth": 28.01 + }, + "warrantyInformation": "1 month warranty", + "shippingInformation": "Ships in 1 month", + "availabilityStatus": "Low Stock", + "reviews": [ + { + "rating": 2, + "comment": "Very unhappy with my purchase!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "John Doe", + "reviewerEmail": "john.doe@x.dummyjson.com" + }, + { + "rating": 2, + "comment": "Not as described!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Nolan Gonzalez", + "reviewerEmail": "nolan.gonzalez@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Very satisfied!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Scarlett Wright", + "reviewerEmail": "scarlett.wright@x.dummyjson.com" + } + ], + "returnPolicy": "30 days return policy", + "minimumOrderQuantity": 24, + "meta": { + "createdAt": "2024-05-23T08:56:21.618Z", + "updatedAt": "2024-05-23T08:56:21.618Z", + "barcode": "9164035109868", + "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/1.png", + "discount": null, + "deleted": true, + "quantity": 2, + "user": "cb1b" + } + ], + "totalAmount": 18, + "totalItems": 2, + "user": { + "id": "cb1b", + "email": "test@123.com", + "password": "Murtaza123", + "role": "user", + "addresses": [ + { + "name": "Murtaza ", + "email": "murtazashabbir14@gmail.com", + "phone": "7869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002", + "address": "Indore" + } + ] + }, + "selectedAddress": { + "name": "Murtaza ", + "email": "murtazashabbir14@gmail.com", + "phone": "7869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002", + "address": "Indore" + }, + "paymentMethod": "cash", + "status": "pending" + }, + { + "id": "7a16", + "items": [ + { + "id": "eaa1", + "title": "Eggs", + "description": "Fresh eggs, a versatile ingredient for baking, cooking, or breakfast.", + "category": "groceries", + "price": 2.99, + "discountPercentage": 5.8, + "rating": 4.46, + "stock": 10, + "tags": [ + "dairy" + ], + "sku": "YA617RI7", + "weight": 4, + "dimensions": { + "width": 12.3, + "height": 10.99, + "depth": 15.53 + }, + "warrantyInformation": "3 year warranty", + "shippingInformation": "Ships overnight", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 2, + "comment": "Very unhappy with my purchase!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Mateo Perez", + "reviewerEmail": "mateo.perez@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Very happy with my purchase!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Cameron Perez", + "reviewerEmail": "cameron.perez@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Very happy with my purchase!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Aurora Barnes", + "reviewerEmail": "aurora.barnes@x.dummyjson.com" + } + ], + "returnPolicy": "30 days return policy", + "minimumOrderQuantity": 43, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "7095702028776", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/groceries/Eggs/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Eggs/thumbnail.png", + "quantity": 1, + "user": "cb1b" + } + ], + "totalAmount": 3, + "totalItems": 1, + "user": { + "id": "cb1b", + "email": "test@123.com", + "password": "Murtaza123", + "role": "user", + "addresses": [ + { + "name": "Murtaza ", + "email": "murtazashabbir14@gmail.com", + "phone": "7869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002", + "address": "Indore" + } + ] + }, + "selectedAddress": { + "name": "Murtaza ", + "email": "murtazashabbir14@gmail.com", + "phone": "7869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002", + "address": "Indore" + }, + "paymentMethod": "card", + "status": "pending" + }, + { + "id": "17a9", + "items": [ + { + "id": "baa3", + "title": "Honey Jar", + "description": "Pure and natural honey in a convenient jar, perfect for sweetening beverages or drizzling over food.", + "category": "groceries", + "price": 6.99, + "discountPercentage": 1.91, + "rating": 3.5, + "stock": 25, + "tags": [ + "condiments" + ], + "sku": "BTBNIIOU", + "weight": 9, + "dimensions": { + "width": 26.53, + "height": 27.11, + "depth": 6.63 + }, + "warrantyInformation": "2 year warranty", + "shippingInformation": "Ships overnight", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 4, + "comment": "Fast shipping!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Nicholas Bailey", + "reviewerEmail": "nicholas.bailey@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Awesome product!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Gabriel Hayes", + "reviewerEmail": "gabriel.hayes@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Highly impressed!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "James Garcia", + "reviewerEmail": "james.garcia@x.dummyjson.com" + } + ], + "returnPolicy": "90 days return policy", + "minimumOrderQuantity": 1, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "0668665656044", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/groceries/Honey%20Jar/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Honey%20Jar/thumbnail.png", + "quantity": 1, + "user": "cb1b" + } + ], + "totalAmount": 7, + "totalItems": 1, + "user": { + "id": "cb1b", + "email": "test@123.com", + "password": "Murtaza123", + "role": "user", + "addresses": [ + { + "name": "Murtaza ", + "email": "murtazashabbir14@gmail.com", + "phone": "7869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002", + "address": "Indore" + } + ] + }, + "selectedAddress": { + "name": "Murtaza ", + "email": "murtazashabbir14@gmail.com", + "phone": "7869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002", + "address": "Indore" + }, + "paymentMethod": "cash", + "status": "cancelled" } ] } \ No newline at end of file diff --git a/src/features/admin/components/AdminOrder.js b/src/features/admin/components/AdminOrder.js index 1c97d1e..0989a24 100644 --- a/src/features/admin/components/AdminOrder.js +++ b/src/features/admin/components/AdminOrder.js @@ -54,7 +54,8 @@ function AdminOrder() { }; useEffect(() => { - handlePage(page); + const pagination = { _page: page, _per_page: ITEMS_PER_PAGE }; + dispatch(fetchAllOrdersAsync(pagination)); }, [dispatch, page]); return ( From acf0ed0d952525d4274225e6c0c619d2addbabff Mon Sep 17 00:00:00 2001 From: Murtaza Tankiwala <115138620+murtazatankiwala456@users.noreply.github.com> Date: Tue, 2 Jul 2024 20:11:32 +0530 Subject: [PATCH 47/47] UI Changes + toggle show hide password button in login page --- data.json | 303 +++++++++++++++++- .../admin/components/AdminProductDetail.js | 8 +- .../admin/components/AdminProductList.js | 14 +- src/features/admin/components/ProductForm.js | 24 +- .../auth/components/ForgotPassword.js | 8 +- src/features/auth/components/Login.js | 35 +- src/features/auth/components/Signup.js | 14 +- src/features/cart/Cart.js | 6 +- src/features/common/Pagination.js | 6 +- src/features/navbar/Navbar.js | 4 +- .../product/components/ProductDetail.js | 8 +- .../product/components/ProductList.js | 4 +- src/features/user/components/UserProfile.js | 40 +-- src/pages/404.js | 4 +- src/pages/Checkout.js | 28 +- src/pages/OrderSuccessPage.js | 4 +- 16 files changed, 410 insertions(+), 100 deletions(-) diff --git a/data.json b/data.json index 0a478d9..5f4e9bf 100644 --- a/data.json +++ b/data.json @@ -1940,14 +1940,13 @@ "role": "user", "addresses": [ { - "name": "Murtaza ", + "name": "Murtaza Tankiwala 3", "email": "murtazashabbir14@gmail.com", - "phone": "7869558609", - "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir ", "city": "Indore", "state": "Madhya Pradesh", - "pinCode": "452002", - "address": "Indore" + "pinCode": "452002" } ] }, @@ -3520,6 +3519,300 @@ }, "paymentMethod": "cash", "status": "cancelled" + }, + { + "id": "a963", + "items": [ + { + "id": "fffe", + "title": "Chicken Meat", + "description": "Fresh and tender chicken meat, suitable for various culinary preparations.", + "category": "groceries", + "price": 9.99, + "discountPercentage": 10.46, + "rating": 4.61, + "stock": 69, + "tags": [ + "meat" + ], + "sku": "G5YEHW7B", + "weight": 7, + "dimensions": { + "width": 15.96, + "height": 29.24, + "depth": 26.25 + }, + "warrantyInformation": "Lifetime warranty", + "shippingInformation": "Ships in 1 month", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Very satisfied!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Sophia Jones", + "reviewerEmail": "sophia.jones@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Great value for money!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Maya Reed", + "reviewerEmail": "maya.reed@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Highly recommended!", + "date": "2024-05-23T08:56:21.620Z", + "reviewerName": "Harper Turner", + "reviewerEmail": "harper.turner@x.dummyjson.com" + } + ], + "returnPolicy": "7 days return policy", + "minimumOrderQuantity": 46, + "meta": { + "createdAt": "2024-05-23T08:56:21.620Z", + "updatedAt": "2024-05-23T08:56:21.620Z", + "barcode": "0966223559510", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/groceries/Chicken%20Meat/1.png", + "https://cdn.dummyjson.com/products/images/groceries/Chicken%20Meat/2.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Chicken%20Meat/thumbnail.png", + "quantity": 1, + "user": "cb1b" + } + ], + "totalAmount": 9, + "totalItems": 1, + "user": { + "id": "cb1b", + "email": "test@123.com", + "password": "Murtaza123", + "role": "user", + "addresses": [ + { + "name": "Murtaza ", + "email": "murtazashabbir14@gmail.com", + "phone": "7869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002", + "address": "Indore" + } + ] + }, + "selectedAddress": { + "name": "Murtaza ", + "email": "murtazashabbir14@gmail.com", + "phone": "7869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir bhai Khergonewala", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002", + "address": "Indore" + }, + "paymentMethod": "cash", + "status": "pending" + }, + { + "id": "f9fb", + "items": [ + { + "id": "d68e", + "title": "Eyeshadow Palette with Mirror", + "description": "The Eyeshadow Palette with Mirror offers a versatile range of eyeshadow shades for creating stunning eye looks. With a built-in mirror, it's convenient for on-the-go makeup application.", + "category": "beauty", + "price": 19.99, + "discountPercentage": 5.5, + "rating": 3.28, + "stock": 44, + "tags": [ + "beauty", + "eyeshadow" + ], + "brand": "Glamour Beauty", + "sku": "MVCFH27F", + "weight": 3, + "dimensions": { + "width": 12.42, + "height": 8.63, + "depth": 29.13 + }, + "warrantyInformation": "1 year warranty", + "shippingInformation": "Ships in 2 weeks", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 4, + "comment": "Very satisfied!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Liam Garcia", + "reviewerEmail": "liam.garcia@x.dummyjson.com" + }, + { + "rating": 1, + "comment": "Very disappointed!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Nora Russell", + "reviewerEmail": "nora.russell@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Highly impressed!", + "date": "2024-05-23T08:56:21.618Z", + "reviewerName": "Elena Baker", + "reviewerEmail": "elena.baker@x.dummyjson.com" + } + ], + "returnPolicy": "30 days return policy", + "minimumOrderQuantity": 32, + "meta": { + "createdAt": "2024-05-23T08:56:21.618Z", + "updatedAt": "2024-05-23T08:56:21.618Z", + "barcode": "2817839095220", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/1.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/thumbnail.png", + "quantity": 1, + "user": "cb1b" + } + ], + "totalAmount": 19, + "totalItems": 1, + "user": { + "id": "cb1b", + "email": "test@123.com", + "password": "Murtaza123", + "role": "user", + "addresses": [ + { + "name": "Murtaza Tankiwala 2", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir ", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + } + ] + }, + "selectedAddress": { + "name": "Murtaza Tankiwala 2", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir ", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + }, + "paymentMethod": "cash", + "status": "pending" + }, + { + "id": "627f", + "items": [ + { + "id": "970f", + "title": "Red Nail Polish Premium", + "description": "The Red Nail Polish offers a rich and glossy red hue for vibrant and polished nails. With a quick-drying formula, it provides a salon-quality finish at home.", + "category": "beauty", + "price": 8.99, + "discountPercentage": 2.46, + "rating": 3.91, + "stock": 71, + "tags": [ + "beauty", + "nail polish" + ], + "brand": "Nail Couture", + "sku": "YUIIIP4W", + "weight": 9, + "dimensions": { + "width": 8.11, + "height": 10.89, + "depth": 29.06 + }, + "warrantyInformation": "1 year warranty", + "shippingInformation": "Ships in 1 week", + "availabilityStatus": "In Stock", + "reviews": [ + { + "rating": 5, + "comment": "Very pleased!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Leo Rivera", + "reviewerEmail": "leo.rivera@x.dummyjson.com" + }, + { + "rating": 5, + "comment": "Great product!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Evan Reed", + "reviewerEmail": "evan.reed@x.dummyjson.com" + }, + { + "rating": 4, + "comment": "Highly recommended!", + "date": "2024-05-23T08:56:21.619Z", + "reviewerName": "Evelyn Sanchez", + "reviewerEmail": "evelyn.sanchez@x.dummyjson.com" + } + ], + "returnPolicy": "No return policy", + "minimumOrderQuantity": 46, + "meta": { + "createdAt": "2024-05-23T08:56:21.619Z", + "updatedAt": "2024-05-23T08:56:21.619Z", + "barcode": "3212847902461", + "qrCode": "https://dummyjson.com/public/qr-code.png" + }, + "images": [ + "https://cdn.dummyjson.com/products/images/beauty/Red%20Nail%20Polish/1.png", + "https://cdn.dummyjson.com/products/images/beauty/Red%20Nail%20Polish/thumbnail.png" + ], + "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Red%20Nail%20Polish/thumbnail.png", + "discount": null, + "quantity": 1, + "user": "cb1b" + } + ], + "totalAmount": 9, + "totalItems": 1, + "user": { + "id": "cb1b", + "email": "test@123.com", + "password": "Murtaza123", + "role": "user", + "addresses": [ + { + "name": "Murtaza Tankiwala 2", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir ", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + } + ] + }, + "selectedAddress": { + "name": "Murtaza Tankiwala 2", + "email": "murtazashabbir14@gmail.com", + "phone": "07869558609", + "street": "1109 - Noorani Nagar - Near M.Shabbir ", + "city": "Indore", + "state": "Madhya Pradesh", + "pinCode": "452002" + }, + "paymentMethod": "cash", + "status": "pending" } ] } \ No newline at end of file diff --git a/src/features/admin/components/AdminProductDetail.js b/src/features/admin/components/AdminProductDetail.js index a45fc0a..44cad89 100644 --- a/src/features/admin/components/AdminProductDetail.js +++ b/src/features/admin/components/AdminProductDetail.js @@ -224,7 +224,7 @@ export default function AdminProductDetail() { <h3 className="text-sm font-medium text-gray-900">Size</h3> <a href="#" - className="text-sm font-medium text-indigo-600 hover:text-indigo-500" + className="text-sm font-medium text-gray-600 hover:text-gray-500" > Size guide </a> @@ -249,7 +249,7 @@ export default function AdminProductDetail() { size.inStock ? "cursor-pointer bg-white text-gray-900 shadow-sm" : "cursor-not-allowed bg-gray-50 text-gray-200", - active ? "ring-2 ring-indigo-500" : "", + active ? "ring-2 ring-gray-500" : "", "group relative flex items-center justify-center rounded-md border py-3 px-4 text-sm font-medium uppercase hover:bg-gray-50 focus:outline-none sm:flex-1 sm:py-6" ) } @@ -264,7 +264,7 @@ export default function AdminProductDetail() { className={classNames( active ? "border" : "border-2", checked - ? "border-indigo-500" + ? "border-gray-500" : "border-transparent", "pointer-events-none absolute -inset-px rounded-md" )} @@ -304,7 +304,7 @@ export default function AdminProductDetail() { handleCart(e); }} type="submit" - className="mt-10 flex w-full items-center justify-center rounded-md border border-transparent bg-indigo-600 px-8 py-3 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2" + className="mt-10 flex w-full items-center justify-center rounded-md border border-transparent bg-gray-600 px-8 py-3 text-base font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2" > Add to Cart </button> diff --git a/src/features/admin/components/AdminProductList.js b/src/features/admin/components/AdminProductList.js index 872e0f0..4064d0a 100644 --- a/src/features/admin/components/AdminProductList.js +++ b/src/features/admin/components/AdminProductList.js @@ -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-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" + className="rounded-md mx-10 bg-yellow-600 px-3 py-2 text-sm font-semibold text-dark shadow-sm hover:bg-yellow-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600" > Add Product </Link> @@ -318,7 +318,7 @@ function MobileFilter({ onChange={(e) => { handleFilter(e, section, option); }} - className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500" + className="h-4 w-4 rounded border-gray-300 text-gray-600 focus:ring-gray-500" /> <label htmlFor={`filter-mobile-${section.id}-${optionIdx}`} @@ -380,7 +380,7 @@ function DesktopFilter({ handleFilter, filters }) { onChange={(e) => { handleFilter(e, section, option); }} - className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500" + className="h-4 w-4 rounded border-gray-300 text-gray-600 focus:ring-gray-500" /> <label htmlFor={`filter-${section.id}-${optionIdx}`} @@ -455,7 +455,7 @@ function ProductGrid({ products }) { <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" + className="rounded-md my-2 bg-gray-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600" > Edit Product </Link> @@ -515,7 +515,7 @@ function Pagination({ page, setPage, handlePage, totalItems }) { <span className="sr-only">Previous</span> <ChevronLeftIcon className="h-5 w-5" aria-hidden="true" /> </div> - {/* Current: "z-10 bg-indigo-600 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600", Default: "text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:outline-offset-0" */} + {/* Current: "z-10 bg-gray-600 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600", Default: "text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:outline-offset-0" */} {Array.from({ length: totalPages }).map( //extracting all the indexes of array (el, index) => ( @@ -524,9 +524,9 @@ function Pagination({ page, setPage, handlePage, totalItems }) { aria-current="page" className={`relative z-10 inline-flex items-center ${ index + 1 === page - ? "bg-indigo-600 text-white" + ? "bg-gray-600 text-white" : "text-gray-400 " - } px-4 py-2 text-sm font-semibold cursor-pointer focus:z-20 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600`} + } px-4 py-2 text-sm font-semibold cursor-pointer focus:z-20 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600`} > {index + 1} </div> diff --git a/src/features/admin/components/ProductForm.js b/src/features/admin/components/ProductForm.js index 10fa745..824f660 100644 --- a/src/features/admin/components/ProductForm.js +++ b/src/features/admin/components/ProductForm.js @@ -96,7 +96,7 @@ function ProductForm() { Product Name </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"> + <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-gray-600"> <input type="text" {...register("title", { @@ -123,7 +123,7 @@ function ProductForm() { required: "description is required", })} rows={3} - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" defaultValue={""} /> </div> @@ -179,7 +179,7 @@ function ProductForm() { Price </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"> + <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-gray-600"> <input type="number" {...register("price", { @@ -201,7 +201,7 @@ function ProductForm() { Discount </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"> + <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-gray-600"> <input type="number" {...register("discountPercentage", { @@ -223,7 +223,7 @@ function ProductForm() { Stock </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"> + <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-gray-600"> <input type="number" {...register("stock", { @@ -244,7 +244,7 @@ function ProductForm() { Thumbnail </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"> + <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-gray-600"> <input type="text" {...register("thumbnail", { @@ -265,7 +265,7 @@ function ProductForm() { 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"> + <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-gray-600"> <input type="text" {...register("image", { @@ -297,7 +297,7 @@ function ProductForm() { id="comments" name="comments" type="checkbox" - className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600" + className="h-4 w-4 rounded border-gray-300 text-gray-600 focus:ring-gray-600" /> </div> <div className="text-sm leading-6"> @@ -318,7 +318,7 @@ function ProductForm() { id="candidates" name="candidates" type="checkbox" - className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600" + className="h-4 w-4 rounded border-gray-300 text-gray-600 focus:ring-gray-600" /> </div> <div className="text-sm leading-6"> @@ -339,7 +339,7 @@ function ProductForm() { id="offers" name="offers" type="checkbox" - className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600" + className="h-4 w-4 rounded border-gray-300 text-gray-600 focus:ring-gray-600" /> </div> <div className="text-sm leading-6"> @@ -369,14 +369,14 @@ function ProductForm() { {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" + 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-gray-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" + className="rounded-md bg-gray-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600" > Save </button> diff --git a/src/features/auth/components/ForgotPassword.js b/src/features/auth/components/ForgotPassword.js index ac244a1..f6e83d7 100644 --- a/src/features/auth/components/ForgotPassword.js +++ b/src/features/auth/components/ForgotPassword.js @@ -15,7 +15,7 @@ export default function ForgotPassword() { <div className="sm:mx-auto sm:w-full sm:max-w-sm"> <img className="mx-auto h-10 w-auto" - src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=600" + src="https://e7.pngegg.com/pngimages/282/123/png-clipart-retail-business-computer-icons-e-commerce-online-shopping-business-computer-network-angle.png" alt="Your Company" /> <h2 className="mt-10 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900"> @@ -50,7 +50,7 @@ export default function ForgotPassword() { }, })} type="email" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> {errors.email && ( <p className="text-red-500">{errors.email.message}</p> @@ -61,7 +61,7 @@ export default function ForgotPassword() { <div> <button type="submit" - className="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 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="flex w-full justify-center rounded-md bg-gray-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600" > Log in </button> @@ -72,7 +72,7 @@ export default function ForgotPassword() { Send me back to{" "} <Link to="/login" - className="font-semibold leading-6 text-indigo-600 hover:text-indigo-500" + className="font-semibold leading-6 text-gray-600 hover:text-gray-500" > Login </Link> diff --git a/src/features/auth/components/Login.js b/src/features/auth/components/Login.js index 5205b94..8b72bf9 100644 --- a/src/features/auth/components/Login.js +++ b/src/features/auth/components/Login.js @@ -3,17 +3,23 @@ import { useSelector, useDispatch } from "react-redux"; import { checkUserAsync, selectError, selectLoggedInUser } from "../authSlice"; import { Link, Navigate } from "react-router-dom"; import { useForm } from "react-hook-form"; +import { EyeIcon, EyeSlashIcon } from "@heroicons/react/24/outline"; // Import EyeSlashIcon for the toggle export default function Login() { const dispatch = useDispatch(); const error = useSelector(selectError); const user = useSelector(selectLoggedInUser); + const [showPassword, setShowPassword] = useState(true); const { register, handleSubmit, formState: { errors }, } = useForm(); + const togglePasswordVisibility = () => { + setShowPassword((showPassword) => !showPassword); + }; + console.log(errors); return ( <> @@ -22,7 +28,7 @@ export default function Login() { <div className="sm:mx-auto sm:w-full sm:max-w-sm"> <img className="mx-auto h-10 w-auto" - src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=600" + src="https://e7.pngegg.com/pngimages/282/123/png-clipart-retail-business-computer-icons-e-commerce-online-shopping-business-computer-network-angle.png" alt="Your Company" /> <h2 className="mt-10 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900"> @@ -59,7 +65,7 @@ export default function Login() { }, })} type="email" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> {errors.email && ( <p className="text-red-500">{errors.email.message}</p> @@ -77,22 +83,33 @@ export default function Login() { </label> <div className="text-sm"> <Link - to="/forgot-paasword" - className="font-semibold text-indigo-600 hover:text-indigo-500" + to="/forgot-password" + className="font-semibold text-gray-600 hover:text-gray-500" > Forgot password? </Link> </div> </div> - <div className="mt-2"> + <div className="mt-2 relative"> <input id="password" {...register("password", { required: "password is required", })} - type="password" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + type={showPassword ? "password" : "text"} + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6 pr-10" /> + <button + type="button" + className=" inset-y-0 right-0 pr-3 absolute flex items-center text-black-400" + onClick={togglePasswordVisibility} + > + {showPassword ? ( + <EyeSlashIcon className="h-6 w-6" aria-hidden="true" /> + ) : ( + <EyeIcon className="h-6 w-6" aria-hidden="true" /> + )} + </button> {errors.password && ( <p className="text-red-500">{errors.password.message}</p> )} @@ -103,7 +120,7 @@ export default function Login() { <div> <button type="submit" - className="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 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="flex w-full justify-center rounded-md bg-gray-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600" > Log in </button> @@ -114,7 +131,7 @@ export default function Login() { Not a member?{" "} <Link to="/signup" - className="font-semibold leading-6 text-indigo-600 hover:text-indigo-500" + className="font-semibold leading-6 text-gray-600 hover:text-gray-500" > Create an Account </Link> diff --git a/src/features/auth/components/Signup.js b/src/features/auth/components/Signup.js index 4473c9d..c22d77c 100644 --- a/src/features/auth/components/Signup.js +++ b/src/features/auth/components/Signup.js @@ -21,7 +21,7 @@ export default function Signup() { <div className="sm:mx-auto sm:w-full sm:max-w-sm"> <img className="mx-auto h-10 w-auto" - src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=600" + src="https://e7.pngegg.com/pngimages/282/123/png-clipart-retail-business-computer-icons-e-commerce-online-shopping-business-computer-network-angle.png" alt="Your Company" /> <h2 className="mt-10 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900"> @@ -64,7 +64,7 @@ export default function Signup() { }, })} type="email" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> {errors.email && ( <p className="text-red-500">{errors.email.message}</p> @@ -83,7 +83,7 @@ export default function Signup() { <div className="text-sm"> <Link to="/forgot-paasword" - className="font-semibold text-indigo-600 hover:text-indigo-500" + className="font-semibold text-gray-600 hover:text-gray-500" > Forgot password? </Link> @@ -103,7 +103,7 @@ export default function Signup() { }, })} type="password" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> {errors.password && ( <p className="text-red-500">{errors.password.message}</p> @@ -129,7 +129,7 @@ export default function Signup() { value === formValues.password || "password not matching", })} type="password" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> {errors.confirmPassword && ( <p className="text-red-500"> @@ -142,7 +142,7 @@ export default function Signup() { <div> <button type="submit" - className="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 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="flex w-full justify-center rounded-md bg-gray-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600" > Sign Up </button> @@ -153,7 +153,7 @@ export default function Signup() { Already a Member?{" "} <Link to="/login" - className="font-semibold leading-6 text-indigo-600 hover:text-indigo-500" + className="font-semibold leading-6 text-gray-600 hover:text-gray-500" > Log In </Link> diff --git a/src/features/cart/Cart.js b/src/features/cart/Cart.js index e7e9c14..cecdb6e 100644 --- a/src/features/cart/Cart.js +++ b/src/features/cart/Cart.js @@ -88,7 +88,7 @@ export default function Cart() { handleRemove(e, item.id); }} type="button" - className="font-medium text-indigo-600 hover:text-indigo-500" + className="font-medium text-gray-600 hover:text-gray-500" > Remove </button> @@ -116,7 +116,7 @@ export default function Cart() { <div className="mt-6"> <Link to="/checkout" - className="flex items-center justify-center rounded-md border border-transparent bg-indigo-600 px-6 py-3 text-base font-medium text-white shadow-sm hover:bg-indigo-700" + className="flex items-center justify-center rounded-md border border-transparent bg-gray-600 px-6 py-3 text-base font-medium text-white shadow-sm hover:bg-gray-700" > Checkout </Link> @@ -127,7 +127,7 @@ export default function Cart() { <Link to="/"> <button type="button" - className="font-medium text-indigo-600 hover:text-indigo-500" + className="font-medium text-gray-600 hover:text-gray-500" > Continue Shopping <span aria-hidden="true"> →</span> diff --git a/src/features/common/Pagination.js b/src/features/common/Pagination.js index 6c01988..943cc83 100644 --- a/src/features/common/Pagination.js +++ b/src/features/common/Pagination.js @@ -48,7 +48,7 @@ export default function Pagination({ page, setPage, handlePage, totalItems }) { <span className="sr-only">Previous</span> <ChevronLeftIcon className="h-5 w-5" aria-hidden="true" /> </div> - {/* Current: "z-10 bg-indigo-600 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600", Default: "text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:outline-offset-0" */} + {/* Current: "z-10 bg-gray-600 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600", Default: "text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:outline-offset-0" */} {Array.from({ length: totalPages }).map( //extracting all the indexes of array (el, index) => ( @@ -57,9 +57,9 @@ export default function Pagination({ page, setPage, handlePage, totalItems }) { aria-current="page" className={`relative z-10 inline-flex items-center ${ index + 1 === page - ? "bg-indigo-600 text-white" + ? "bg-gray-600 text-white" : "text-gray-400 " - } px-4 py-2 text-sm font-semibold cursor-pointer focus:z-20 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600`} + } px-4 py-2 text-sm font-semibold cursor-pointer focus:z-20 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600`} > {index + 1} </div> diff --git a/src/features/navbar/Navbar.js b/src/features/navbar/Navbar.js index 80bb946..449af42 100644 --- a/src/features/navbar/Navbar.js +++ b/src/features/navbar/Navbar.js @@ -43,7 +43,7 @@ function NavBar({ children }) { <Link to="/"> <img className="h-8 w-8" - src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=500" + src="https://e7.pngegg.com/pngimages/282/123/png-clipart-retail-business-computer-icons-e-commerce-online-shopping-business-computer-network-angle.png" alt="Your Company" /> </Link> @@ -85,7 +85,7 @@ function NavBar({ children }) { </button> </Link> {items.length > 0 && ( - <span className="inline-flex items-center rounded-md mb-7 -ml-3 bg-red-50 px-2 py-1 text-xs font-medium text-red-700 ring-1 ring-inset ring-red-600/10"> + <span className="inline-flex items-center rounded-md mb-7 -ml-3 bg-yellow-50 px-2 py-1 text-xs font-medium text-dark-700 ring-1 ring-inset ring-yellow-600/10"> {items.length} </span> )} diff --git a/src/features/product/components/ProductDetail.js b/src/features/product/components/ProductDetail.js index 988a8c9..009fa8c 100644 --- a/src/features/product/components/ProductDetail.js +++ b/src/features/product/components/ProductDetail.js @@ -221,7 +221,7 @@ export default function ProductDetail() { <h3 className="text-sm font-medium text-gray-900">Size</h3> <a href="#" - className="text-sm font-medium text-indigo-600 hover:text-indigo-500" + className="text-sm font-medium text-gray-600 hover:text-gray-500" > Size guide </a> @@ -246,7 +246,7 @@ export default function ProductDetail() { size.inStock ? "cursor-pointer bg-white text-gray-900 shadow-sm" : "cursor-not-allowed bg-gray-50 text-gray-200", - active ? "ring-2 ring-indigo-500" : "", + active ? "ring-2 ring-gray-500" : "", "group relative flex items-center justify-center rounded-md border py-3 px-4 text-sm font-medium uppercase hover:bg-gray-50 focus:outline-none sm:flex-1 sm:py-6" ) } @@ -261,7 +261,7 @@ export default function ProductDetail() { className={classNames( active ? "border" : "border-2", checked - ? "border-indigo-500" + ? "border-gray-500" : "border-transparent", "pointer-events-none absolute -inset-px rounded-md" )} @@ -301,7 +301,7 @@ export default function ProductDetail() { handleCart(e); }} type="submit" - className="mt-10 flex w-full items-center justify-center rounded-md border border-transparent bg-indigo-600 px-8 py-3 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2" + className="mt-10 flex w-full items-center justify-center rounded-md border border-transparent bg-gray-600 px-8 py-3 text-base font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2" > Add to Cart </button> diff --git a/src/features/product/components/ProductList.js b/src/features/product/components/ProductList.js index 1f48240..2120b08 100644 --- a/src/features/product/components/ProductList.js +++ b/src/features/product/components/ProductList.js @@ -314,7 +314,7 @@ function MobileFilter({ onChange={(e) => { handleFilter(e, section, option); }} - className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500" + className="h-4 w-4 rounded border-gray-300 text-gray-600 focus:ring-gray-500" /> <label htmlFor={`filter-mobile-${section.id}-${optionIdx}`} @@ -376,7 +376,7 @@ function DesktopFilter({ handleFilter, filters }) { onChange={(e) => { handleFilter(e, section, option); }} - className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500" + className="h-4 w-4 rounded border-gray-300 text-gray-600 focus:ring-gray-500" /> <label htmlFor={`filter-${section.id}-${optionIdx}`} diff --git a/src/features/user/components/UserProfile.js b/src/features/user/components/UserProfile.js index d6198e2..2f978d8 100644 --- a/src/features/user/components/UserProfile.js +++ b/src/features/user/components/UserProfile.js @@ -72,7 +72,7 @@ export default function UserProfile() { setSelectedEditIndex(-1); }} type="submit" - className="rounded-md my-5 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" + className="rounded-md my-5 bg-yellow-600 px-3 py-2 text-sm font-semibold text-dark shadow-sm hover:bg-yellow-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600" > Add New Address </button> @@ -110,7 +110,7 @@ export default function UserProfile() { required: "name is required", })} id="name" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> </div> </div> @@ -129,7 +129,7 @@ export default function UserProfile() { required: "email is required", })} type="email" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> </div> </div> @@ -148,7 +148,7 @@ export default function UserProfile() { required: "phone is required", })} type="tel" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> </div> </div> @@ -167,7 +167,7 @@ export default function UserProfile() { required: "street is required", })} id="street-address" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> </div> </div> @@ -186,7 +186,7 @@ export default function UserProfile() { required: "city is required", })} id="city" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> </div> </div> @@ -205,7 +205,7 @@ export default function UserProfile() { required: "state is required", })} id="state" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> </div> </div> @@ -224,7 +224,7 @@ export default function UserProfile() { required: "pinCode is required", })} id="pinCode" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> </div> </div> @@ -234,7 +234,7 @@ export default function UserProfile() { <div className="mt-6 flex items-center justify-end gap-x-6"> <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" + className="rounded-md bg-gray-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600" > Add Address </button> @@ -279,7 +279,7 @@ export default function UserProfile() { required: "name is required", })} id="name" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> </div> </div> @@ -298,7 +298,7 @@ export default function UserProfile() { required: "email is required", })} type="email" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> </div> </div> @@ -317,7 +317,7 @@ export default function UserProfile() { required: "phone is required", })} type="tel" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> </div> </div> @@ -336,7 +336,7 @@ export default function UserProfile() { required: "street is required", })} id="street-address" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> </div> </div> @@ -355,7 +355,7 @@ export default function UserProfile() { required: "city is required", })} id="city" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> </div> </div> @@ -374,7 +374,7 @@ export default function UserProfile() { required: "state is required", })} id="state" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> </div> </div> @@ -393,7 +393,7 @@ export default function UserProfile() { required: "pinCode is required", })} id="pinCode" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> </div> </div> @@ -404,13 +404,13 @@ export default function UserProfile() { <button onClick={(e) => setSelectedEditIndex(-1)} type="submit" - className="rounded-md px-3 py-2 text-sm font-semibold text-grey shadow-sm hover:bg-grey-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" + className="rounded-md px-3 py-2 text-sm font-semibold text-grey shadow-sm hover:bg-grey-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600" > Cancle </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" + className="rounded-md bg-gray-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600" > Edit Address </button> @@ -446,7 +446,7 @@ export default function UserProfile() { handleEditForm(index); }} type="button" - className="font-medium text-indigo-600 hover:text-indigo-500" + className="font-medium text-gray-600 hover:text-gray-500" > Edit </button> @@ -455,7 +455,7 @@ export default function UserProfile() { handleRemove(e, index); }} type="button" - className="font-medium text-indigo-600 hover:text-indigo-500" + className="font-medium text-gray-600 hover:text-gray-500" > Remove </button> diff --git a/src/pages/404.js b/src/pages/404.js index 1f67683..5256c81 100644 --- a/src/pages/404.js +++ b/src/pages/404.js @@ -3,7 +3,7 @@ function PageNotFound() { return ( <main className="grid min-h-full place-items-center bg-white px-6 py-24 sm:py-32 lg:px-8"> <div className="text-center"> - <p className="text-base font-semibold text-indigo-600">404</p> + <p className="text-base font-semibold text-gray-600">404</p> <h1 className="mt-4 text-3xl font-bold tracking-tight text-gray-900 sm:text-5xl"> Page not found </h1> @@ -14,7 +14,7 @@ function PageNotFound() { <Link to="/" href="#" - className="rounded-md bg-indigo-600 px-3.5 py-2.5 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 bg-gray-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600" > Go back home </Link> diff --git a/src/pages/Checkout.js b/src/pages/Checkout.js index 5ac2377..ecc6363 100644 --- a/src/pages/Checkout.js +++ b/src/pages/Checkout.js @@ -123,7 +123,7 @@ function Checkout() { required: "name is required", })} id="name" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> </div> </div> @@ -142,7 +142,7 @@ function Checkout() { required: "email is required", })} type="email" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> </div> </div> @@ -161,7 +161,7 @@ function Checkout() { required: "phone is required", })} type="tel" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> </div> </div> @@ -180,7 +180,7 @@ function Checkout() { required: "street is required", })} id="street-address" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> </div> </div> @@ -199,7 +199,7 @@ function Checkout() { required: "city is required", })} id="city" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> </div> </div> @@ -218,7 +218,7 @@ function Checkout() { required: "state is required", })} id="state" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> </div> </div> @@ -237,7 +237,7 @@ function Checkout() { required: "pinCode is required", })} id="pinCode" - className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 sm:text-sm sm:leading-6" /> </div> </div> @@ -255,7 +255,7 @@ function Checkout() { </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" + className="rounded-md bg-gray-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600" > Add Address </button> @@ -280,7 +280,7 @@ function Checkout() { onChange={handleAddress} value={index} type="radio" - className="h-4 w-4 border-gray-300 text-indigo-600 focus:ring-indigo-600" + className="h-4 w-4 border-gray-300 text-gray-600 focus:ring-gray-600" /> <div className="min-w-0 flex-auto"> <p className="text-sm font-semibold leading-6 text-gray-900"> @@ -323,7 +323,7 @@ function Checkout() { value="cash" checked={paymentMethod === "cash"} type="radio" - className="h-4 w-4 border-gray-300 text-indigo-600 focus:ring-indigo-600" + className="h-4 w-4 border-gray-300 text-gray-600 focus:ring-gray-600" /> <label htmlFor="cash" @@ -340,7 +340,7 @@ function Checkout() { value="card" checked={paymentMethod === "card"} type="radio" - className="h-4 w-4 border-gray-300 text-indigo-600 focus:ring-indigo-600" + className="h-4 w-4 border-gray-300 text-gray-600 focus:ring-gray-600" /> <label htmlFor="card" @@ -414,7 +414,7 @@ function Checkout() { handleRemove(e, item.id); }} type="button" - className="font-medium text-indigo-600 hover:text-indigo-500" + className="font-medium text-gray-600 hover:text-gray-500" > Remove </button> @@ -442,7 +442,7 @@ function Checkout() { <div className="mt-6"> <div onClick={handleOrder} - className="flex items-center cursor-pointer justify-center rounded-md border border-transparent bg-indigo-600 px-6 py-3 text-base font-medium text-white shadow-sm hover:bg-indigo-700" + className="flex items-center cursor-pointer justify-center rounded-md border border-transparent bg-gray-600 px-6 py-3 text-base font-medium text-white shadow-sm hover:bg-gray-700" > Order Now </div> @@ -453,7 +453,7 @@ function Checkout() { <Link to="/"> <button type="button" - className="font-medium text-indigo-600 hover:text-indigo-500" + className="font-medium text-gray-600 hover:text-gray-500" > Continue Shopping <span aria-hidden="true"> →</span> diff --git a/src/pages/OrderSuccessPage.js b/src/pages/OrderSuccessPage.js index 8b36257..5af0c12 100644 --- a/src/pages/OrderSuccessPage.js +++ b/src/pages/OrderSuccessPage.js @@ -20,7 +20,7 @@ function OrderSuccessPage() { {!params.id && <Navigate to="/" replace={true}></Navigate>} <main className="grid min-h-full place-items-center bg-white px-6 py-24 sm:py-32 lg:px-8"> <div className="text-center"> - <p className="text-base font-semibold text-indigo-600"> + <p className="text-base font-semibold text-gray-600"> Order Successfully Placed </p> <h1 className="mt-4 text-3xl font-bold tracking-tight text-gray-900 sm:text-5xl"> @@ -33,7 +33,7 @@ function OrderSuccessPage() { <Link to="/" href="#" - className="rounded-md bg-indigo-600 px-3.5 py-2.5 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 bg-gray-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600" > Go back home </Link>