From 7302ce4030f96684ef43783395ae4373cbea81e2 Mon Sep 17 00:00:00 2001 From: Amir Amiri Date: Sat, 25 Feb 2023 16:10:40 +0330 Subject: [PATCH] category added --- src/App.js | 2 +- .../category-preview.component.jsx | 22 ++ .../category-preview.styles.scss | 18 ++ .../directory-item.component.jsx} | 10 +- .../directory-item.styles.scss} | 6 +- .../directory/directory.component.jsx | 4 +- src/contexts/categories.context.jsx | 26 ++ src/contexts/products.context.jsx | 17 -- src/index.js | 6 +- .../categories-preview.component.jsx | 22 ++ src/routes/category/category-component.jsx | 31 +++ src/routes/category/category.styles.scss | 13 + src/routes/shop/shop.component.jsx | 17 +- src/shop-data.js | 239 ++++++++++++++++++ src/shop-data.json | 57 ----- src/utils/firebase/firebase.utils.js | 29 ++- 16 files changed, 419 insertions(+), 100 deletions(-) create mode 100644 src/components/category-preview/category-preview.component.jsx create mode 100644 src/components/category-preview/category-preview.styles.scss rename src/components/{category-item/category-item.component.jsx => directory-item/directory-item.component.jsx} (57%) rename src/components/{category-item/category-item.styles.scss => directory-item/directory-item.styles.scss} (92%) create mode 100644 src/contexts/categories.context.jsx delete mode 100644 src/contexts/products.context.jsx create mode 100644 src/routes/categories-preview/categories-preview.component.jsx create mode 100644 src/routes/category/category-component.jsx create mode 100644 src/routes/category/category.styles.scss create mode 100644 src/shop-data.js delete mode 100644 src/shop-data.json diff --git a/src/App.js b/src/App.js index 5204acf..140a6ed 100644 --- a/src/App.js +++ b/src/App.js @@ -10,7 +10,7 @@ const App = () => { < Routes > }> } /> - } /> + } /> } /> } /> diff --git a/src/components/category-preview/category-preview.component.jsx b/src/components/category-preview/category-preview.component.jsx new file mode 100644 index 0000000..4bbde8d --- /dev/null +++ b/src/components/category-preview/category-preview.component.jsx @@ -0,0 +1,22 @@ +import { Link } from 'react-router-dom'; +import ProductCard from '../product-card/product-card.component'; +import './category-preview.styles.scss'; + +const CategoryPreview = ({ title, products }) => { + return ( +
+

+ {title.toUpperCase()} +

+
+ { + products + .filter((_, idx) => idx < 4) + .map((product) => ) + } +
+
+ ) +} + +export default CategoryPreview; \ No newline at end of file diff --git a/src/components/category-preview/category-preview.styles.scss b/src/components/category-preview/category-preview.styles.scss new file mode 100644 index 0000000..3b0661d --- /dev/null +++ b/src/components/category-preview/category-preview.styles.scss @@ -0,0 +1,18 @@ +.category-preview-container { + display: flex; + flex-direction: column; + margin-bottom: 30px; + + .title { + font-size: 28px; + margin-bottom: 25px; + cursor: pointer; + } + + .preview { + display: grid; + grid-template-columns: repeat(4, 1fr); + column-gap: 20px; + } + } + \ No newline at end of file diff --git a/src/components/category-item/category-item.component.jsx b/src/components/directory-item/directory-item.component.jsx similarity index 57% rename from src/components/category-item/category-item.component.jsx rename to src/components/directory-item/directory-item.component.jsx index c27e3ac..ea7169b 100644 --- a/src/components/category-item/category-item.component.jsx +++ b/src/components/directory-item/directory-item.component.jsx @@ -1,14 +1,14 @@ -import './category-item.styles.scss' +import './directory-item.styles.scss' -const CategoryItem = ({category}) => { +const DirectoryItem = ({category}) => { const {imageUrl, title} = category; return ( -
+
-
+

{title}

Shop Now

@@ -17,4 +17,4 @@ const CategoryItem = ({category}) => { ) } -export default CategoryItem \ No newline at end of file +export default DirectoryItem \ No newline at end of file diff --git a/src/components/category-item/category-item.styles.scss b/src/components/directory-item/directory-item.styles.scss similarity index 92% rename from src/components/category-item/category-item.styles.scss rename to src/components/directory-item/directory-item.styles.scss index b90e1c9..909ae0b 100644 --- a/src/components/category-item/category-item.styles.scss +++ b/src/components/directory-item/directory-item.styles.scss @@ -1,4 +1,4 @@ -.category-container { +.directory-item-container { min-width: 30%; height: 240px; flex: 1 1 auto; @@ -17,7 +17,7 @@ transition: transform 6s cubic-bezier(0.25, 0.45, 0.45, 0.95); } - & .category-body-container { + & .directory-item-body { opacity: 0.9; } } @@ -41,7 +41,7 @@ background-position: center; } - .category-body-container { + .directory-item-body { height: 90px; padding: 0 25px; display: flex; diff --git a/src/components/directory/directory.component.jsx b/src/components/directory/directory.component.jsx index aede75c..b729509 100644 --- a/src/components/directory/directory.component.jsx +++ b/src/components/directory/directory.component.jsx @@ -1,10 +1,10 @@ -import CategoryItem from '../category-item/category-item.component' +import DirectoryItem from '../directory-item/directory-item.component'; import './directory.styles.scss' const Directory = ({categories}) => { return (
{categories.map((category) => ( - + ))}
) diff --git a/src/contexts/categories.context.jsx b/src/contexts/categories.context.jsx new file mode 100644 index 0000000..a48310c --- /dev/null +++ b/src/contexts/categories.context.jsx @@ -0,0 +1,26 @@ +import { useEffect } from "react"; +import { createContext, useState } from "react"; +import { GetCategoriesAndDocuments } from "../utils/firebase/firebase.utils"; + + +export const CategoriesContext = createContext({ + categoriesMap: [], +}); + +export const CategoriesProvider = ({ children }) => { + const [categoriesMap, setCategoriesMap] = useState({}); + useEffect(() => { + const getCategoriesMap = async () => { + const categoriesMap = await GetCategoriesAndDocuments(); + setCategoriesMap(categoriesMap) + } + + getCategoriesMap(); + }, []) + const value = { categoriesMap }; + return ( + + {children} + + ) +} \ No newline at end of file diff --git a/src/contexts/products.context.jsx b/src/contexts/products.context.jsx deleted file mode 100644 index c78e60b..0000000 --- a/src/contexts/products.context.jsx +++ /dev/null @@ -1,17 +0,0 @@ -import { createContext, useState } from "react"; -import PRODUCTS from '../shop-data.json'; - - -export const ProductContext = createContext({ - products: [], -}); - -export const ProductsProvider = ({children}) => { - const [products, setProducts] = useState(PRODUCTS); - const value = {products}; - return ( - - {children} - - ) -} \ No newline at end of file diff --git a/src/index.js b/src/index.js index 029d370..081dd68 100644 --- a/src/index.js +++ b/src/index.js @@ -6,7 +6,7 @@ import { BrowserRouter } from 'react-router-dom'; import './index.css'; import App from './App'; import { UserProvider } from './contexts/user.context'; -import { ProductsProvider } from './contexts/products.context'; +import { CategoriesProvider } from './contexts/categories.context'; import { CartProvider } from './contexts/cart.context'; const root = ReactDOM.createRoot(document.getElementById('root')); @@ -14,11 +14,11 @@ root.render( - + - + diff --git a/src/routes/categories-preview/categories-preview.component.jsx b/src/routes/categories-preview/categories-preview.component.jsx new file mode 100644 index 0000000..29c68c7 --- /dev/null +++ b/src/routes/categories-preview/categories-preview.component.jsx @@ -0,0 +1,22 @@ +import { useContext, Fragment } from "react"; +import { CategoriesContext } from "../../contexts/categories.context"; +import CategoryPreview from "../../components/category-preview/category-preview.component"; + +const CategoriesPreview = () => { + const { categoriesMap } = useContext(CategoriesContext); + + return ( + + { + Object.keys(categoriesMap).map(title => { + const products = categoriesMap[title]; + return + }) + } + + + ) + +} + +export default CategoriesPreview; \ No newline at end of file diff --git a/src/routes/category/category-component.jsx b/src/routes/category/category-component.jsx new file mode 100644 index 0000000..fd8925b --- /dev/null +++ b/src/routes/category/category-component.jsx @@ -0,0 +1,31 @@ +import { Fragment, useContext, useEffect, useState } from 'react'; +import { useParams } from 'react-router-dom'; +import { CategoriesContext } from '../../contexts/categories.context'; +import ProductCard from '../../components/product-card/product-card.component'; +import './category.styles.scss'; + +const Category = () => { + const { category } = useParams() + const { categoriesMap } = useContext(CategoriesContext) + const [products, setProducts] = useState(categoriesMap[category]) + + useEffect(() => { + setProducts(categoriesMap[category]) + }, [categoriesMap, category]) + + return ( + +

+ {category} +

+
+ {products && + products.map((product) => ) + } +
+
+ + ) +} + +export default Category; \ No newline at end of file diff --git a/src/routes/category/category.styles.scss b/src/routes/category/category.styles.scss new file mode 100644 index 0000000..6bbda62 --- /dev/null +++ b/src/routes/category/category.styles.scss @@ -0,0 +1,13 @@ +.category-container { + display: grid; + grid-template-columns: repeat(4, 1fr); + column-gap: 20px; + row-gap: 50px; + +} + +.category-title { + font-size: 38px; + margin-bottom: 25px; + text-align: center; +} \ No newline at end of file diff --git a/src/routes/shop/shop.component.jsx b/src/routes/shop/shop.component.jsx index 5ae7ec7..bfbbdf4 100644 --- a/src/routes/shop/shop.component.jsx +++ b/src/routes/shop/shop.component.jsx @@ -1,17 +1,14 @@ -import { useContext } from "react"; -import { ProductContext } from "../../contexts/products.context"; -import ProductCard from "../../components/product-card/product-card.component"; +import { Routes, Route } from 'react-router-dom'; +import CategoriesPreview from '../categories-preview/categories-preview.component'; +import Category from '../category/category-component'; import './shop.styles.scss'; const Shop = () => { - const { products } = useContext(ProductContext); - return ( -
- {products.map((product) => ( - - ))} -
+ + } /> + } /> + ) } diff --git a/src/shop-data.js b/src/shop-data.js new file mode 100644 index 0000000..cd74e7c --- /dev/null +++ b/src/shop-data.js @@ -0,0 +1,239 @@ +const SHOP_DATA = [ + { + title: 'Hats', + items: [ + { + id: 1, + name: 'Brown Brim', + imageUrl: 'https://i.ibb.co/ZYW3VTp/brown-brim.png', + price: 25, + }, + { + id: 2, + name: 'Blue Beanie', + imageUrl: 'https://i.ibb.co/ypkgK0X/blue-beanie.png', + price: 18, + }, + { + id: 3, + name: 'Brown Cowboy', + imageUrl: 'https://i.ibb.co/QdJwgmp/brown-cowboy.png', + price: 35, + }, + { + id: 4, + name: 'Grey Brim', + imageUrl: 'https://i.ibb.co/RjBLWxB/grey-brim.png', + price: 25, + }, + { + id: 5, + name: 'Green Beanie', + imageUrl: 'https://i.ibb.co/YTjW3vF/green-beanie.png', + price: 18, + }, + { + id: 6, + name: 'Palm Tree Cap', + imageUrl: 'https://i.ibb.co/rKBDvJX/palm-tree-cap.png', + price: 14, + }, + { + id: 7, + name: 'Red Beanie', + imageUrl: 'https://i.ibb.co/bLB646Z/red-beanie.png', + price: 18, + }, + { + id: 8, + name: 'Wolf Cap', + imageUrl: 'https://i.ibb.co/1f2nWMM/wolf-cap.png', + price: 14, + }, + { + id: 9, + name: 'Blue Snapback', + imageUrl: 'https://i.ibb.co/X2VJP2W/blue-snapback.png', + price: 16, + }, + ], + }, + { + title: 'Sneakers', + items: [ + { + id: 10, + name: 'Adidas NMD', + imageUrl: 'https://i.ibb.co/0s3pdnc/adidas-nmd.png', + price: 220, + }, + { + id: 11, + name: 'Adidas Yeezy', + imageUrl: 'https://i.ibb.co/dJbG1cT/yeezy.png', + price: 280, + }, + { + id: 12, + name: 'Black Converse', + imageUrl: 'https://i.ibb.co/bPmVXyP/black-converse.png', + price: 110, + }, + { + id: 13, + name: 'Nike White AirForce', + imageUrl: 'https://i.ibb.co/1RcFPk0/white-nike-high-tops.png', + price: 160, + }, + { + id: 14, + name: 'Nike Red High Tops', + imageUrl: 'https://i.ibb.co/QcvzydB/nikes-red.png', + price: 160, + }, + { + id: 15, + name: 'Nike Brown High Tops', + imageUrl: 'https://i.ibb.co/fMTV342/nike-brown.png', + price: 160, + }, + { + id: 16, + name: 'Air Jordan Limited', + imageUrl: 'https://i.ibb.co/w4k6Ws9/nike-funky.png', + price: 190, + }, + { + id: 17, + name: 'Timberlands', + imageUrl: 'https://i.ibb.co/Mhh6wBg/timberlands.png', + price: 200, + }, + ], + }, + { + title: 'Jackets', + items: [ + { + id: 18, + name: 'Black Jean Shearling', + imageUrl: 'https://i.ibb.co/XzcwL5s/black-shearling.png', + price: 125, + }, + { + id: 19, + name: 'Blue Jean Jacket', + imageUrl: 'https://i.ibb.co/mJS6vz0/blue-jean-jacket.png', + price: 90, + }, + { + id: 20, + name: 'Grey Jean Jacket', + imageUrl: 'https://i.ibb.co/N71k1ML/grey-jean-jacket.png', + price: 90, + }, + { + id: 21, + name: 'Brown Shearling', + imageUrl: 'https://i.ibb.co/s96FpdP/brown-shearling.png', + price: 165, + }, + { + id: 22, + name: 'Tan Trench', + imageUrl: 'https://i.ibb.co/M6hHc3F/brown-trench.png', + price: 185, + }, + ], + }, + { + title: 'Womens', + items: [ + { + id: 23, + name: 'Blue Tanktop', + imageUrl: 'https://i.ibb.co/7CQVJNm/blue-tank.png', + price: 25, + }, + { + id: 24, + name: 'Floral Blouse', + imageUrl: 'https://i.ibb.co/4W2DGKm/floral-blouse.png', + price: 20, + }, + { + id: 25, + name: 'Floral Dress', + imageUrl: 'https://i.ibb.co/KV18Ysr/floral-skirt.png', + price: 80, + }, + { + id: 26, + name: 'Red Dots Dress', + imageUrl: 'https://i.ibb.co/N3BN1bh/red-polka-dot-dress.png', + price: 80, + }, + { + id: 27, + name: 'Striped Sweater', + imageUrl: 'https://i.ibb.co/KmSkMbH/striped-sweater.png', + price: 45, + }, + { + id: 28, + name: 'Yellow Track Suit', + imageUrl: 'https://i.ibb.co/v1cvwNf/yellow-track-suit.png', + price: 135, + }, + { + id: 29, + name: 'White Blouse', + imageUrl: 'https://i.ibb.co/qBcrsJg/white-vest.png', + price: 20, + }, + ], + }, + { + title: 'Mens', + items: [ + { + id: 30, + name: 'Camo Down Vest', + imageUrl: 'https://i.ibb.co/xJS0T3Y/camo-vest.png', + price: 325, + }, + { + id: 31, + name: 'Floral T-shirt', + imageUrl: 'https://i.ibb.co/qMQ75QZ/floral-shirt.png', + price: 20, + }, + { + id: 32, + name: 'Black & White Longsleeve', + imageUrl: 'https://i.ibb.co/55z32tw/long-sleeve.png', + price: 25, + }, + { + id: 33, + name: 'Pink T-shirt', + imageUrl: 'https://i.ibb.co/RvwnBL8/pink-shirt.png', + price: 25, + }, + { + id: 34, + name: 'Jean Long Sleeve', + imageUrl: 'https://i.ibb.co/VpW4x5t/roll-up-jean-shirt.png', + price: 40, + }, + { + id: 35, + name: 'Burgundy T-shirt', + imageUrl: 'https://i.ibb.co/mh3VM1f/polka-dot-shirt.png', + price: 25, + }, + ], + }, +]; + +export default SHOP_DATA \ No newline at end of file diff --git a/src/shop-data.json b/src/shop-data.json deleted file mode 100644 index 806bff5..0000000 --- a/src/shop-data.json +++ /dev/null @@ -1,57 +0,0 @@ -[ - { - "id": 1, - "name": "Brown Brim", - "imageUrl": "https://i.ibb.co/ZYW3VTp/brown-brim.png", - "price": 25 - }, - { - "id": 2, - "name": "Blue Beanie", - "imageUrl": "https://i.ibb.co/ypkgK0X/blue-beanie.png", - "price": 18 - }, - { - "id": 3, - "name": "Brown Cowboy", - "imageUrl": "https://i.ibb.co/QdJwgmp/brown-cowboy.png", - "price": 35 - }, - { - "id": 4, - "name": "Grey Brim", - "imageUrl": "https://i.ibb.co/RjBLWxB/grey-brim.png", - "price": 25 - }, - { - "id": 5, - "name": "Green Beanie", - "imageUrl": "https://i.ibb.co/YTjW3vF/green-beanie.png", - "price": 18 - }, - { - "id": 6, - "name": "Palm Tree Cap", - "imageUrl": "https://i.ibb.co/rKBDvJX/palm-tree-cap.png", - "price": 14 - }, - { - "id": 7, - "name": "Red Beanie", - "imageUrl": "https://i.ibb.co/bLB646Z/red-beanie.png", - "price": 18 - }, - { - "id": 8, - "name": "Wolf Cap", - "imageUrl": "https://i.ibb.co/1f2nWMM/wolf-cap.png", - "price": 14 - }, - { - "id": 9, - "name": "Blue Snapback", - "imageUrl": "https://i.ibb.co/X2VJP2W/blue-snapback.png", - "price": 16 - } - ] - \ No newline at end of file diff --git a/src/utils/firebase/firebase.utils.js b/src/utils/firebase/firebase.utils.js index c0071ad..9f45588 100644 --- a/src/utils/firebase/firebase.utils.js +++ b/src/utils/firebase/firebase.utils.js @@ -6,9 +6,9 @@ import { createUserWithEmailAndPassword, signInWithEmailAndPassword, signOut, - onAuthStateChanged + onAuthStateChanged, } from 'firebase/auth'; -import { getFirestore, doc, getDoc, setDoc } from 'firebase/firestore'; +import { getFirestore, doc, getDoc, setDoc, collection, writeBatch, query, getDocs } from 'firebase/firestore'; const firebaseConfig = { apiKey: "AIzaSyBklymcP7qNanysoqUWsf7L2NJBaZ8H2y0", @@ -31,6 +31,31 @@ export const auth = getAuth() export const signInWithGooglePopup = () => signInWithPopup(auth, provider) export const db = getFirestore(); +export const addCollectionAndDocuments = async (collectionKey, objectsToAdd) => { + const CollectionRef = collection(db, collectionKey); + const batch = writeBatch(db); + objectsToAdd.forEach((object) => { + const docRef = doc(CollectionRef, object.title.toLowerCase()); + batch.set(docRef, object); + }); + + await batch.commit() +} + +export const GetCategoriesAndDocuments = async () => { + const collectionRef = collection(db, 'categories'); + const q = query(collectionRef); + const querySnapshot = await getDocs(q); + const categoryMap = querySnapshot.docs.reduce((acc, docSnapshot) => { + const { title, items } = docSnapshot.data(); + acc[title.toLowerCase()] = items; + return acc; + }, {}) + + return categoryMap; + +} + export const createUserDocumentFromAuth = async (userAuth, additionalInformation = {}) => { const userDocRef = doc(db, 'users', userAuth.uid); const userSnapShot = await getDoc(userDocRef);