├── .vscode └── settings.json ├── public ├── logo.png ├── robots.txt ├── favicon.ico ├── logo192.png ├── logo512.png ├── manifest.json └── index.html ├── src ├── Asssets │ ├── LogoI.png │ ├── logo2.png │ ├── SliderImage1.jpg │ ├── SliderImage2.gif │ ├── SliderImage3.jpg │ ├── SliderImage4.gif │ ├── SliderImage5.jpg │ └── SliderImage6.jpg ├── setupTests.js ├── utils │ └── idMapper.ts ├── redux │ ├── authReducer │ │ ├── actionTypes.ts │ │ ├── action.ts │ │ └── reducer.ts │ ├── MenReducer │ │ ├── actionType.ts │ │ ├── reducer.ts │ │ └── action.ts │ ├── hooks.ts │ ├── wishlistReducer │ │ ├── actionTypes.ts │ │ ├── reducer.ts │ │ └── action.ts │ ├── cartReducer │ │ ├── actionTypes.ts │ │ └── reducer.ts │ └── store.ts ├── reportWebVitals.js ├── types │ ├── images.d.ts │ └── index.ts ├── App.tsx ├── index.css ├── index.js ├── Components │ ├── Home │ │ ├── SearchBar.tsx │ │ ├── HomeSlider.tsx │ │ ├── NavbarTop.jsx │ │ ├── TrendingSlider.jsx │ │ ├── KidSlider.jsx │ │ ├── UnMissSlider.jsx │ │ ├── MensSlider.jsx │ │ ├── WomenSlider.jsx │ │ ├── Sidebar.tsx │ │ └── Navbar.tsx │ ├── Admin │ │ ├── AdminNavbar.tsx │ │ ├── AdminSidebar.tsx │ │ ├── AdminProduct.tsx │ │ ├── AdminLogin.tsx │ │ └── ManageUsers.tsx │ ├── Filter │ │ ├── Pagination1.tsx │ │ └── Menfilter.tsx │ ├── PrivateRoutes.tsx │ ├── Card.tsx │ ├── MainRoutes.tsx │ ├── NewCard.tsx │ └── Singlecard.tsx ├── lib │ └── supabase.ts ├── services │ ├── authService.ts │ ├── orderService.ts │ ├── productService.ts │ ├── newProductService.ts │ └── wishlistService.ts ├── logo.svg └── pages │ ├── Admin.tsx │ ├── Men.tsx │ ├── Women.tsx │ ├── Login.tsx │ ├── Wishlist.tsx │ └── SignUp.tsx ├── postcss.config.js ├── .gitignore ├── tsconfig.json ├── supabase ├── migrations │ ├── 20250629175709_mute_night.sql │ ├── 20250704030456_silver_field.sql │ ├── 20250704030625_odd_dawn.sql │ ├── 20250629034518_yellow_oasis.sql │ ├── 20250703023429_bronze_spire.sql │ └── 20250703024936_warm_sunset.sql └── functions │ └── admin-users │ └── index.ts ├── tailwind.config.js ├── package.json └── README.md /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.linkedEditing": true 3 | } -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepakpatil26/spiffy-farm-6274/HEAD/public/logo.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepakpatil26/spiffy-farm-6274/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepakpatil26/spiffy-farm-6274/HEAD/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepakpatil26/spiffy-farm-6274/HEAD/public/logo512.png -------------------------------------------------------------------------------- /src/Asssets/LogoI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepakpatil26/spiffy-farm-6274/HEAD/src/Asssets/LogoI.png -------------------------------------------------------------------------------- /src/Asssets/logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepakpatil26/spiffy-farm-6274/HEAD/src/Asssets/logo2.png -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } -------------------------------------------------------------------------------- /src/Asssets/SliderImage1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepakpatil26/spiffy-farm-6274/HEAD/src/Asssets/SliderImage1.jpg -------------------------------------------------------------------------------- /src/Asssets/SliderImage2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepakpatil26/spiffy-farm-6274/HEAD/src/Asssets/SliderImage2.gif -------------------------------------------------------------------------------- /src/Asssets/SliderImage3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepakpatil26/spiffy-farm-6274/HEAD/src/Asssets/SliderImage3.jpg -------------------------------------------------------------------------------- /src/Asssets/SliderImage4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepakpatil26/spiffy-farm-6274/HEAD/src/Asssets/SliderImage4.gif -------------------------------------------------------------------------------- /src/Asssets/SliderImage5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepakpatil26/spiffy-farm-6274/HEAD/src/Asssets/SliderImage5.jpg -------------------------------------------------------------------------------- /src/Asssets/SliderImage6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepakpatil26/spiffy-farm-6274/HEAD/src/Asssets/SliderImage6.jpg -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import "@testing-library/jest-dom"; 6 | -------------------------------------------------------------------------------- /src/utils/idMapper.ts: -------------------------------------------------------------------------------- 1 | const ID_OFFSET = 151; // Since your database IDs start from 152 2 | 3 | export const toBackendId = (frontendId: number | string): number => { 4 | return Number(frontendId) + ID_OFFSET; 5 | }; 6 | 7 | export const toFrontendId = (backendId: number | string): number => { 8 | return Number(backendId) - ID_OFFSET; 9 | }; 10 | -------------------------------------------------------------------------------- /src/redux/authReducer/actionTypes.ts: -------------------------------------------------------------------------------- 1 | export const SIGNIN_REQUEST = "SIGNIN_REQUEST"; 2 | export const SIGNIN_SUCCESS = "SIGNIN_SUCCESS"; 3 | export const SIGNIN_FAILURE = "SIGNIN_FAILURE"; 4 | export const SIGNOUT = "SIGNOUT"; 5 | export const GET_USER = "GET_USER"; 6 | 7 | export const SIGNUP_REQUEST = "SIGNUP_REQUEST"; 8 | export const SIGNUP_SUCCESS = "SIGNUP_SUCCESS"; 9 | export const SIGNUP_FAILURE = "SIGNUP_FAILURE"; -------------------------------------------------------------------------------- /src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = (onPerfEntry) => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /src/redux/MenReducer/actionType.ts: -------------------------------------------------------------------------------- 1 | // Men's actions 2 | export const MEN_REQUEST_PENDING = "MEN_REQUEST_PENDING"; 3 | export const MEN_REQUEST_FAILURE = "MEN_REQUEST_FAILURE"; 4 | export const MEN_REQUEST_SUCCESS = "MEN_REQUEST_SUCCESS"; 5 | 6 | // Women's actions 7 | export const WOMEN_REQUEST_PENDING = "WOMEN_REQUEST_PENDING"; 8 | export const WOMEN_REQUEST_FAILURE = "WOMEN_REQUEST_FAILURE"; 9 | export const WOMEN_REQUEST_SUCCESS = "WOMEN_REQUEST_SUCCESS"; -------------------------------------------------------------------------------- /src/redux/hooks.ts: -------------------------------------------------------------------------------- 1 | import { useDispatch, useSelector, TypedUseSelectorHook } from 'react-redux'; 2 | import type { RootState } from '../types'; 3 | import { ThunkDispatch } from 'redux-thunk'; 4 | import { AnyAction } from 'redux'; 5 | 6 | export type AppDispatch = ThunkDispatch; 7 | 8 | export const useAppDispatch = () => useDispatch(); 9 | export const useAppSelector: TypedUseSelectorHook = useSelector; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /package-lock.json 6 | /.pnp 7 | .pnp.js 8 | 9 | # testing 10 | /coverage 11 | 12 | # production 13 | /build 14 | 15 | # misc 16 | .DS_Store 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | .vercel 26 | .env 27 | -------------------------------------------------------------------------------- /src/types/images.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.png' { 2 | const value: string; 3 | export default value; 4 | } 5 | 6 | declare module '*.jpg' { 7 | const value: string; 8 | export default value; 9 | } 10 | 11 | declare module '*.jpeg' { 12 | const value: string; 13 | export default value; 14 | } 15 | 16 | declare module '*.gif' { 17 | const value: string; 18 | export default value; 19 | } 20 | 21 | declare module '*.svg' { 22 | const value: string; 23 | export default value; 24 | } -------------------------------------------------------------------------------- /src/redux/wishlistReducer/actionTypes.ts: -------------------------------------------------------------------------------- 1 | // Wishlist item actions 2 | export const ADD_TO_WISHLIST = "ADD_TO_WISHLIST"; 3 | export const REMOVE_FROM_WISHLIST = "REMOVE_FROM_WISHLIST"; 4 | export const LOAD_WISHLIST = "LOAD_WISHLIST"; 5 | export const CLEAR_WISHLIST = "CLEAR_WISHLIST"; 6 | 7 | // Wishlist request states 8 | export const WISHLIST_REQUEST_PENDING = "WISHLIST_REQUEST_PENDING"; 9 | export const WISHLIST_REQUEST_SUCCESS = "WISHLIST_REQUEST_SUCCESS"; 10 | export const WISHLIST_REQUEST_FAILURE = "WISHLIST_REQUEST_FAILURE"; -------------------------------------------------------------------------------- /src/redux/cartReducer/actionTypes.ts: -------------------------------------------------------------------------------- 1 | // Cart item actions 2 | export const ADD_TO_CART = "ADD_TO_CART"; 3 | export const REMOVE_FROM_CART = "REMOVE_FROM_CART"; 4 | export const INCREMENT_QUANTITY = "INCREMENT_QUANTITY"; 5 | export const DECREMENT_QUANTITY = "DECREMENT_QUANTITY"; 6 | export const CLEAR_CART = "CLEAR_CART"; 7 | export const LOAD_CART = "LOAD_CART"; 8 | 9 | // Cart request states 10 | export const CART_REQUEST_PENDING = "CART_REQUEST_PENDING"; 11 | export const CART_REQUEST_SUCCESS = "CART_REQUEST_SUCCESS"; 12 | export const CART_REQUEST_FAILURE = "CART_REQUEST_FAILURE"; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "es6" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Out-Fit", 3 | "name": "Out-Fit - Fashion Store", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | }, 20 | { 21 | "src": "logo.png", 22 | "type": "image/png", 23 | "sizes": "256x256" 24 | } 25 | ], 26 | "start_url": ".", 27 | "display": "standalone", 28 | "theme_color": "#000000", 29 | "background_color": "#ffffff" 30 | } 31 | -------------------------------------------------------------------------------- /supabase/migrations/20250629175709_mute_night.sql: -------------------------------------------------------------------------------- 1 | /* 2 | # Add type column to products table 3 | 4 | 1. Changes 5 | - Add `type` column to `products` table as text type 6 | - Set default value to empty string to handle existing records 7 | - Make column nullable to allow for flexibility 8 | 9 | 2. Notes 10 | - This resolves the "column products_1.type does not exist" error 11 | - Existing products will have null/empty type values that can be updated later 12 | */ 13 | 14 | -- Add the missing type column to products table 15 | DO $$ 16 | BEGIN 17 | IF NOT EXISTS ( 18 | SELECT 1 FROM information_schema.columns 19 | WHERE table_name = 'products' AND column_name = 'type' 20 | ) THEN 21 | ALTER TABLE products ADD COLUMN type text; 22 | END IF; 23 | END $$; -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import MainRoutes from './Components/MainRoutes'; 3 | import { ToastContainer } from 'react-toastify'; 4 | import 'react-toastify/dist/ReactToastify.css'; 5 | import 'slick-carousel/slick/slick.css'; 6 | import 'slick-carousel/slick/slick-theme.css'; 7 | 8 | const App: React.FC = () => { 9 | return ( 10 |
11 | 12 | 24 |
25 | ); 26 | }; 27 | 28 | export default App; -------------------------------------------------------------------------------- /src/redux/store.ts: -------------------------------------------------------------------------------- 1 | import { 2 | applyMiddleware, 3 | combineReducers, 4 | legacy_createStore, 5 | compose, 6 | Store, 7 | } from "redux"; 8 | import { thunk } from "redux-thunk"; 9 | import { reducer as MenReducer } from "./MenReducer/reducer"; 10 | import { reducer as cartReducer } from "./cartReducer/reducer"; 11 | import { reducer as AuthReducer } from "../redux/authReducer/reducer"; 12 | import { reducer as wishlistReducer } from "./wishlistReducer/reducer"; 13 | import { RootState } from "../types"; 14 | 15 | const rootReducer = combineReducers({ 16 | MenReducer, 17 | AuthReducer, 18 | cartReducer, 19 | wishlistReducer, 20 | }); 21 | 22 | const composeEnhancers = 23 | (typeof window !== 'undefined' && 24 | (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || 25 | compose; 26 | 27 | export const store: Store = legacy_createStore( 28 | rootReducer, 29 | composeEnhancers(applyMiddleware(thunk)) 30 | ); -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | body { 7 | @apply font-sans antialiased; 8 | } 9 | } 10 | 11 | @layer components { 12 | .btn-primary { 13 | @apply bg-primary-500 hover:bg-primary-600 text-white font-medium py-2 px-4 rounded-lg transition-colors duration-200; 14 | } 15 | 16 | .btn-secondary { 17 | @apply bg-secondary-500 hover:bg-secondary-600 text-white font-medium py-2 px-4 rounded-lg transition-colors duration-200; 18 | } 19 | 20 | .card { 21 | @apply bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow duration-300; 22 | } 23 | 24 | .input-field { 25 | @apply w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent; 26 | } 27 | } 28 | 29 | @layer utilities { 30 | .text-gradient { 31 | @apply bg-gradient-to-r from-primary-500 to-secondary-500 bg-clip-text text-transparent; 32 | } 33 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import "./index.css"; 4 | import App from "./App"; 5 | import reportWebVitals from "./reportWebVitals"; 6 | import { BrowserRouter } from "react-router-dom"; 7 | import { ChakraProvider } from "@chakra-ui/react"; 8 | import { store } from "./redux/store"; 9 | import { Provider } from "react-redux"; 10 | import "slick-carousel/slick/slick.css"; 11 | import "slick-carousel/slick/slick-theme.css"; 12 | const root = ReactDOM.createRoot(document.getElementById("root")); 13 | root.render( 14 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ); 25 | 26 | // If you want to start measuring performance in your app, pass a function 27 | // to log results (for example: reportWebVitals(console.log)) 28 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 29 | reportWebVitals(); 30 | -------------------------------------------------------------------------------- /src/Components/Home/SearchBar.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { BsSearch } from "react-icons/bs"; 3 | 4 | const SearchBar: React.FC = () => { 5 | const [searchTerm, setSearchTerm] = useState(""); 6 | 7 | const handleSearch = (e: React.FormEvent) => { 8 | e.preventDefault(); 9 | // Implement search functionality 10 | console.log("Searching for:", searchTerm); 11 | }; 12 | 13 | return ( 14 |
15 |
16 |
17 | 18 |
19 | setSearchTerm(e.target.value)} 23 | placeholder="What are you looking for?" 24 | className="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md leading-5 bg-white placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-primary-500 focus:border-primary-500" 25 | /> 26 |
27 |
28 | ); 29 | }; 30 | 31 | export default SearchBar; -------------------------------------------------------------------------------- /src/Components/Admin/AdminNavbar.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | import { BsPerson } from "react-icons/bs"; 4 | import Logo from "../../Asssets/logo2.png"; 5 | 6 | const AdminNavbar: React.FC = () => { 7 | return ( 8 |
9 |
10 | 11 | Logo 16 | 17 |
18 | 19 |
20 |

Admin Panel

21 |
22 | 23 |
24 |
25 | 26 | Admin User 27 |
28 |
29 |
30 | ); 31 | }; 32 | 33 | export default AdminNavbar; -------------------------------------------------------------------------------- /src/lib/supabase.ts: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | 3 | const supabaseUrl = 4 | process.env.REACT_APP_SUPABASE_URL || 5 | "https://fultjhdhqmmuzrtkopyp.supabase.co"; 6 | const supabaseAnonKey = 7 | process.env.REACT_APP_SUPABASE_ANON_KEY || 8 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImZ1bHRqaGRocW1tdXpydGtvcHlwIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTAxMzMwNzAsImV4cCI6MjA2NTcwOTA3MH0.fphhyybfNdO2hLupdAMHnmkQ2jqk4kHd-vRxZPwToww"; 9 | 10 | // Configure the auth options to persist the session in local storage 11 | export const supabase = createClient(supabaseUrl, supabaseAnonKey, { 12 | auth: { 13 | persistSession: true, 14 | autoRefreshToken: true, 15 | detectSessionInUrl: true, 16 | storage: window.localStorage, 17 | }, 18 | }); 19 | 20 | // Database types 21 | export interface Product { 22 | id: string; 23 | title: string; 24 | price: number; 25 | actualPrice: number; 26 | image: string; 27 | img1?: string; 28 | img2?: string; 29 | img3?: string; 30 | img4?: string; 31 | category: string; 32 | gender: "men" | "women"; 33 | type: string; 34 | discount?: number; 35 | created_at?: string; 36 | } 37 | 38 | export interface CartItem extends Product { 39 | quantity: number; 40 | } 41 | 42 | export interface User { 43 | id: string; 44 | email: string; 45 | firstName: string; 46 | lastName: string; 47 | created_at?: string; 48 | } 49 | -------------------------------------------------------------------------------- /src/Components/Home/HomeSlider.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Slider from "react-slick"; 3 | import sliderImage1 from "../../Asssets/SliderImage1.jpg"; 4 | import sliderImage2 from "../../Asssets/SliderImage2.gif"; 5 | import sliderImage3 from "../../Asssets/SliderImage3.jpg"; 6 | import sliderImage4 from "../../Asssets/SliderImage4.gif"; 7 | import sliderImage5 from "../../Asssets/SliderImage5.jpg"; 8 | 9 | const HomeSlider: React.FC = () => { 10 | const settings = { 11 | dots: true, 12 | infinite: true, 13 | speed: 500, 14 | slidesToShow: 1, 15 | slidesToScroll: 1, 16 | autoplay: true, 17 | autoplaySpeed: 4000, 18 | pauseOnHover: true, 19 | responsive: [ 20 | { 21 | breakpoint: 768, 22 | settings: { 23 | dots: false, 24 | } 25 | } 26 | ] 27 | }; 28 | 29 | const slides = [ 30 | sliderImage2, 31 | sliderImage1, 32 | sliderImage3, 33 | sliderImage4, 34 | sliderImage5, 35 | ]; 36 | 37 | return ( 38 |
39 | 40 | {slides.map((slide, index) => ( 41 |
42 | {`Slide 47 |
48 | ))} 49 |
50 |
51 | ); 52 | }; 53 | 54 | export default HomeSlider; -------------------------------------------------------------------------------- /src/services/authService.ts: -------------------------------------------------------------------------------- 1 | import { supabase } from '../lib/supabase' 2 | 3 | export interface SignUpData { 4 | email: string 5 | password: string 6 | firstName: string 7 | lastName: string 8 | } 9 | 10 | export interface SignInData { 11 | email: string 12 | password: string 13 | } 14 | 15 | export const authService = { 16 | // Sign up 17 | async signUp({ email, password, firstName, lastName }: SignUpData) { 18 | const { data, error } = await supabase.auth.signUp({ 19 | email, 20 | password, 21 | options: { 22 | data: { 23 | first_name: firstName, 24 | last_name: lastName, 25 | } 26 | } 27 | }) 28 | 29 | if (error) throw error 30 | return data 31 | }, 32 | 33 | // Sign in 34 | async signIn({ email, password }: SignInData) { 35 | const { data, error } = await supabase.auth.signInWithPassword({ 36 | email, 37 | password 38 | }) 39 | 40 | if (error) throw error 41 | return data 42 | }, 43 | 44 | // Sign out 45 | async signOut() { 46 | const { error } = await supabase.auth.signOut() 47 | if (error) throw error 48 | }, 49 | 50 | // Get current user 51 | async getCurrentUser() { 52 | const { data: { user }, error } = await supabase.auth.getUser() 53 | if (error) throw error 54 | return user 55 | }, 56 | 57 | // Get session 58 | async getSession() { 59 | const { data: { session }, error } = await supabase.auth.getSession() 60 | if (error) throw error 61 | return session 62 | } 63 | } -------------------------------------------------------------------------------- /supabase/migrations/20250704030456_silver_field.sql: -------------------------------------------------------------------------------- 1 | /* 2 | # Update cart_items table for products_data integration 3 | 4 | 1. Changes 5 | - Update cart_items.product_id to reference products_data.id 6 | - Add foreign key constraint to products_data table 7 | - Update existing data if needed 8 | 9 | 2. Security 10 | - Maintain existing RLS policies 11 | */ 12 | 13 | -- First, let's check if we need to update the product_id column type 14 | -- The products_data table uses integer IDs, but cart_items might be using text 15 | 16 | -- Update the product_id column to be integer to match products_data.id 17 | ALTER TABLE cart_items 18 | ALTER COLUMN product_id TYPE integer USING product_id::integer; 19 | 20 | -- Add foreign key constraint to products_data table 21 | -- First drop the old foreign key if it exists 22 | DO $$ 23 | BEGIN 24 | IF EXISTS ( 25 | SELECT 1 FROM information_schema.table_constraints 26 | WHERE constraint_name = 'cart_items_product_id_fkey' 27 | AND table_name = 'cart_items' 28 | ) THEN 29 | ALTER TABLE cart_items DROP CONSTRAINT cart_items_product_id_fkey; 30 | END IF; 31 | END $$; 32 | 33 | -- Add new foreign key constraint to products_data 34 | ALTER TABLE cart_items 35 | ADD CONSTRAINT cart_items_product_id_fkey 36 | FOREIGN KEY (product_id) REFERENCES products_data(id) ON DELETE CASCADE; 37 | 38 | -- Update the unique constraint to use the new structure 39 | DROP INDEX IF EXISTS cart_items_user_product_unique; 40 | CREATE UNIQUE INDEX cart_items_user_product_unique 41 | ON cart_items (user_id, product_id); -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./src/**/*.{js,jsx,ts,tsx}", 5 | ], 6 | theme: { 7 | extend: { 8 | colors: { 9 | primary: { 10 | 50: '#fef7ed', 11 | 100: '#fdedd3', 12 | 200: '#fbd7a5', 13 | 300: '#f8bb6d', 14 | 400: '#f59e42', 15 | 500: '#f89f17', 16 | 600: '#df9018', 17 | 700: '#b8721a', 18 | 800: '#94591c', 19 | 900: '#794a1a', 20 | }, 21 | secondary: { 22 | 50: '#fdf2f8', 23 | 100: '#fce7f3', 24 | 200: '#fbcfe8', 25 | 300: '#f9a8d4', 26 | 400: '#f472b6', 27 | 500: '#ec4899', 28 | 600: '#db2777', 29 | 700: '#be185d', 30 | 800: '#9d174d', 31 | 900: '#831843', 32 | } 33 | }, 34 | fontFamily: { 35 | sans: ['Inter', 'system-ui', 'sans-serif'], 36 | }, 37 | animation: { 38 | 'fade-in': 'fadeIn 0.5s ease-in-out', 39 | 'slide-up': 'slideUp 0.3s ease-out', 40 | 'bounce-gentle': 'bounceGentle 2s infinite', 41 | }, 42 | keyframes: { 43 | fadeIn: { 44 | '0%': { opacity: '0' }, 45 | '100%': { opacity: '1' }, 46 | }, 47 | slideUp: { 48 | '0%': { transform: 'translateY(10px)', opacity: '0' }, 49 | '100%': { transform: 'translateY(0)', opacity: '1' }, 50 | }, 51 | bounceGentle: { 52 | '0%, 100%': { transform: 'translateY(0)' }, 53 | '50%': { transform: 'translateY(-5px)' }, 54 | } 55 | } 56 | }, 57 | }, 58 | plugins: [], 59 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@chakra-ui/react": "^2.8.2", 7 | "@emotion/react": "^11.11.1", 8 | "@emotion/styled": "^11.11.0", 9 | "@supabase/supabase-js": "^2.39.0", 10 | "@types/node": "^20.10.0", 11 | "@types/react": "^18.2.45", 12 | "@types/react-dom": "^18.2.18", 13 | "@types/react-redux": "^7.1.34", 14 | "@types/react-router-dom": "^5.3.3", 15 | "@types/react-slick": "^0.23.13", 16 | "@types/redux-logger": "^3.0.13", 17 | "axios": "^1.6.2", 18 | "framer-motion": "^10.16.16", 19 | "react": "^18.2.0", 20 | "react-dom": "^18.2.0", 21 | "react-icons": "^4.12.0", 22 | "react-redux": "^9.0.4", 23 | "react-router-dom": "^6.20.1", 24 | "react-scripts": "5.0.1", 25 | "react-slick": "^0.29.0", 26 | "react-toastify": "^9.1.3", 27 | "redux": "^5.0.0", 28 | "redux-logger": "^3.0.6", 29 | "redux-persist": "^6.0.0", 30 | "redux-thunk": "^3.1.0", 31 | "slick-carousel": "^1.8.1", 32 | "typescript": "^4.9.5", 33 | "web-vitals": "^3.5.0" 34 | }, 35 | "scripts": { 36 | "start": "react-scripts start", 37 | "build": "react-scripts build", 38 | "test": "react-scripts test", 39 | "eject": "react-scripts eject" 40 | }, 41 | "eslintConfig": { 42 | "extends": [ 43 | "react-app", 44 | "react-app/jest" 45 | ] 46 | }, 47 | "browserslist": { 48 | "production": [ 49 | ">0.2%", 50 | "not dead", 51 | "not op_mini all" 52 | ], 53 | "development": [ 54 | "last 1 chrome version", 55 | "last 1 firefox version", 56 | "last 1 safari version" 57 | ] 58 | }, 59 | "devDependencies": { 60 | "@babel/plugin-proposal-private-property-in-object": "^7.21.11", 61 | "autoprefixer": "^10.4.16", 62 | "postcss": "^8.4.32", 63 | "tailwindcss": "^3.3.6" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | 14 | 18 | 19 | 28 | Out-Fit 29 | 30 | 31 | 32 | 33 |
34 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/services/orderService.ts: -------------------------------------------------------------------------------- 1 | import { supabase } from '../lib/supabase' 2 | import { CartItem } from '../types' 3 | 4 | export interface OrderItem { 5 | id: string 6 | title: string 7 | price: number 8 | quantity: number 9 | image: string 10 | } 11 | 12 | export interface CreateOrderData { 13 | user_id: string 14 | total: number 15 | status: string 16 | items: OrderItem[] 17 | } 18 | 19 | export const orderService = { 20 | // Create a new order 21 | async createOrder(orderData: CreateOrderData) { 22 | const { data, error } = await supabase 23 | .from('orders') 24 | .insert([orderData]) 25 | .select() 26 | .single() 27 | 28 | if (error) throw error 29 | return data 30 | }, 31 | 32 | // Get orders for a user 33 | async getUserOrders(userId: string) { 34 | const { data, error } = await supabase 35 | .from('orders') 36 | .select('*') 37 | .eq('user_id', userId) 38 | .order('created_at', { ascending: false }) 39 | 40 | if (error) throw error 41 | return data || [] 42 | }, 43 | 44 | // Get a specific order 45 | async getOrder(orderId: string) { 46 | const { data, error } = await supabase 47 | .from('orders') 48 | .select('*') 49 | .eq('id', orderId) 50 | .single() 51 | 52 | if (error) throw error 53 | return data 54 | }, 55 | 56 | // Update order status 57 | async updateOrderStatus(orderId: string, status: string) { 58 | const { data, error } = await supabase 59 | .from('orders') 60 | .update({ status }) 61 | .eq('id', orderId) 62 | .select() 63 | .single() 64 | 65 | if (error) throw error 66 | return data 67 | }, 68 | 69 | // Convert cart items to order items 70 | convertCartToOrderItems(cartItems: CartItem[]): OrderItem[] { 71 | return cartItems.map(item => ({ 72 | id: item.id, 73 | title: item.title, 74 | price: item.price, 75 | quantity: item.quantity, 76 | image: item.image 77 | })) 78 | } 79 | } -------------------------------------------------------------------------------- /src/Components/Admin/AdminSidebar.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link, useLocation } from "react-router-dom"; 3 | import { MdOutlineProductionQuantityLimits, MdOutlineAddCircleOutline } from "react-icons/md"; 4 | import { BsGraphUpArrow } from "react-icons/bs"; 5 | 6 | interface SidebarItem { 7 | title: string; 8 | icon: React.ReactNode; 9 | link: string; 10 | } 11 | 12 | const sidebarData: SidebarItem[] = [ 13 | { 14 | title: "Products", 15 | icon: , 16 | link: "/products", 17 | }, 18 | { 19 | title: "Add Product", 20 | icon: , 21 | link: "/manageProduct", 22 | }, 23 | { 24 | title: "Manage Users", 25 | icon: , 26 | link: "/users", 27 | }, 28 | ]; 29 | 30 | const AdminSidebar: React.FC = () => { 31 | const location = useLocation(); 32 | 33 | return ( 34 |
35 |
36 |
    37 | {sidebarData.map((item, index) => { 38 | const isActive = location.pathname === item.link; 39 | return ( 40 |
  • 41 | 49 | 50 | {item.icon} 51 | 52 | {item.title} 53 | 54 |
  • 55 | ); 56 | })} 57 |
58 |
59 |
60 | ); 61 | }; 62 | 63 | export default AdminSidebar; -------------------------------------------------------------------------------- /src/Components/Filter/Pagination1.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import { useSelector } from "react-redux"; 3 | import { useSearchParams } from "react-router-dom"; 4 | import { RootState } from "../../types"; 5 | 6 | const Pagination1: React.FC = () => { 7 | const { total } = useSelector((store: RootState) => store.MenReducer); 8 | 9 | const getCurrentPage = (page: string | null): number => { 10 | const pageNum = Number(page); 11 | return typeof pageNum !== "number" || pageNum <= 0 || !pageNum ? 1 : pageNum; 12 | }; 13 | 14 | const [searchParams, setSearchParams] = useSearchParams(); 15 | const [page, setPage] = useState(getCurrentPage(searchParams.get("page"))); 16 | 17 | const handlePage = (val: number) => { 18 | setPage((prev) => prev + val); 19 | }; 20 | 21 | useEffect(() => { 22 | const params: Record = { 23 | page: page.toString(), 24 | }; 25 | setSearchParams(params); 26 | }, [page, setSearchParams]); 27 | 28 | const totalPages = Math.ceil(total / 12); 29 | 30 | return ( 31 |
32 |
33 | 40 | 41 | 42 | {page} 43 | 44 | 45 | 52 |
53 | 54 |
55 | Page {page} of {totalPages} 56 |
57 |
58 | ); 59 | }; 60 | 61 | export default Pagination1; -------------------------------------------------------------------------------- /src/Components/PrivateRoutes.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useSelector } from "react-redux"; 3 | import { Navigate, useLocation } from "react-router-dom"; 4 | import { RootState } from "../types"; 5 | 6 | interface PrivateRoutesProps { 7 | children: React.ReactNode; 8 | } 9 | 10 | const PrivateRoutes: React.FC = ({ children }) => { 11 | const { isAuth, isLoading, isAdmin } = useSelector((state: RootState) => state.AuthReducer); 12 | const location = useLocation(); 13 | 14 | // Check if it's an admin route 15 | const isAdminRoute = location.pathname.startsWith("/admin") || 16 | location.pathname === "/products" || 17 | location.pathname === "/manageProduct" || 18 | location.pathname === "/users" || 19 | location.pathname.startsWith("/editProduct"); 20 | 21 | // Show loading state while checking auth 22 | if (isLoading) { 23 | return ( 24 |
25 |
26 |
27 | ); 28 | } 29 | 30 | // Check for authentication 31 | if (!isAuth) { 32 | return ; 33 | } 34 | 35 | // Additional check for admin routes 36 | if (isAdminRoute && !isAdmin) { 37 | return ( 38 |
39 |
40 |
🚫
41 |

Access Denied

42 |

43 | You don't have permission to access this admin area. Please contact an administrator if you believe this is an error. 44 |

45 | 51 |
52 |
53 | ); 54 | } 55 | 56 | return <>{children}; 57 | }; 58 | 59 | export default PrivateRoutes; -------------------------------------------------------------------------------- /src/redux/MenReducer/reducer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | MEN_REQUEST_FAILURE, 3 | MEN_REQUEST_PENDING, 4 | MEN_REQUEST_SUCCESS, 5 | WOMEN_REQUEST_FAILURE, 6 | WOMEN_REQUEST_PENDING, 7 | WOMEN_REQUEST_SUCCESS, 8 | } from "./actionType"; 9 | import { ProductState, Product, ApiResponse } from "../../types"; 10 | 11 | const initialState: ProductState = { 12 | menLoading: false, 13 | menError: false, 14 | womenLoading: false, 15 | womenError: false, 16 | total: 0, 17 | men: [], 18 | women: [], 19 | products: [], 20 | categories: [], 21 | isLoading: false, 22 | isError: false, 23 | }; 24 | 25 | interface ProductAction { 26 | type: string; 27 | payload?: ApiResponse; 28 | } 29 | 30 | export const reducer = (state = initialState, { type, payload }: ProductAction): ProductState => { 31 | switch (type) { 32 | case MEN_REQUEST_PENDING: 33 | return { 34 | ...state, 35 | menLoading: true, 36 | menError: false, 37 | isLoading: true, 38 | isError: false 39 | }; 40 | case MEN_REQUEST_FAILURE: 41 | return { 42 | ...state, 43 | menLoading: false, 44 | menError: true, 45 | isLoading: false, 46 | isError: true 47 | }; 48 | case MEN_REQUEST_SUCCESS: 49 | return { 50 | ...state, 51 | menLoading: false, 52 | menError: false, 53 | isLoading: false, 54 | isError: false, 55 | total: payload?.total || 0, 56 | men: payload?.data || [], 57 | }; 58 | case WOMEN_REQUEST_PENDING: 59 | return { 60 | ...state, 61 | womenLoading: true, 62 | womenError: false, 63 | isLoading: true, 64 | isError: false 65 | }; 66 | case WOMEN_REQUEST_FAILURE: 67 | return { 68 | ...state, 69 | womenLoading: false, 70 | womenError: true, 71 | isLoading: false, 72 | isError: true 73 | }; 74 | case WOMEN_REQUEST_SUCCESS: 75 | return { 76 | ...state, 77 | womenLoading: false, 78 | womenError: false, 79 | isLoading: false, 80 | isError: false, 81 | total: payload?.total || 0, 82 | women: payload?.data || [], 83 | }; 84 | default: 85 | return state; 86 | } 87 | }; -------------------------------------------------------------------------------- /supabase/migrations/20250704030625_odd_dawn.sql: -------------------------------------------------------------------------------- 1 | /* 2 | # Update cart_items table for products_data compatibility 3 | 4 | 1. Changes 5 | - Update product_id column type from text to integer 6 | - Update foreign key constraint to reference products_data table 7 | - Ensure compatibility with new product structure 8 | 9 | 2. Security 10 | - Maintain existing RLS policies 11 | - Preserve user data integrity 12 | */ 13 | 14 | -- First, let's check the current structure and clean up existing data if needed 15 | -- Remove any cart items that don't have valid integer product IDs 16 | DELETE FROM cart_items 17 | WHERE product_id !~ '^[0-9]+$' OR product_id IS NULL; 18 | 19 | -- Drop existing foreign key constraint if it exists 20 | DO $$ 21 | BEGIN 22 | IF EXISTS ( 23 | SELECT 1 FROM information_schema.table_constraints 24 | WHERE constraint_name = 'cart_items_product_id_fkey' 25 | AND table_name = 'cart_items' 26 | ) THEN 27 | ALTER TABLE cart_items DROP CONSTRAINT cart_items_product_id_fkey; 28 | END IF; 29 | END $$; 30 | 31 | -- Drop existing unique index 32 | DROP INDEX IF EXISTS cart_items_user_product_unique; 33 | 34 | -- Update the product_id column type from text to integer 35 | -- This will only work if all existing values are valid integers 36 | ALTER TABLE cart_items 37 | ALTER COLUMN product_id TYPE integer USING product_id::integer; 38 | 39 | -- Add new foreign key constraint to products_data table 40 | ALTER TABLE cart_items 41 | ADD CONSTRAINT cart_items_product_id_fkey 42 | FOREIGN KEY (product_id) REFERENCES products_data(id) ON DELETE CASCADE; 43 | 44 | -- Recreate the unique constraint with the new integer type 45 | CREATE UNIQUE INDEX cart_items_user_product_unique 46 | ON cart_items (user_id, product_id); 47 | 48 | -- Add index for better performance 49 | CREATE INDEX IF NOT EXISTS cart_items_product_id_idx 50 | ON cart_items (product_id); 51 | 52 | -- Ensure the updated_at trigger function exists and is applied 53 | CREATE OR REPLACE FUNCTION handle_updated_at() 54 | RETURNS TRIGGER AS $$ 55 | BEGIN 56 | NEW.updated_at = now(); 57 | RETURN NEW; 58 | END; 59 | $$ language 'plpgsql'; 60 | 61 | -- Ensure the trigger exists 62 | DROP TRIGGER IF EXISTS handle_cart_items_updated_at ON cart_items; 63 | CREATE TRIGGER handle_cart_items_updated_at 64 | BEFORE UPDATE ON cart_items 65 | FOR EACH ROW 66 | EXECUTE PROCEDURE handle_updated_at(); -------------------------------------------------------------------------------- /src/redux/wishlistReducer/reducer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ADD_TO_WISHLIST, 3 | REMOVE_FROM_WISHLIST, 4 | LOAD_WISHLIST, 5 | CLEAR_WISHLIST, 6 | WISHLIST_REQUEST_PENDING, 7 | WISHLIST_REQUEST_SUCCESS, 8 | WISHLIST_REQUEST_FAILURE, 9 | } from "./actionTypes"; 10 | import { WishlistItem } from "../../types"; 11 | 12 | interface WishlistState { 13 | items: WishlistItem[]; 14 | isLoading: boolean; 15 | error: string | null; 16 | } 17 | 18 | const initialState: WishlistState = { 19 | items: [], 20 | isLoading: false, 21 | error: null, 22 | }; 23 | 24 | type WishlistAction = 25 | | { type: typeof WISHLIST_REQUEST_PENDING } 26 | | { type: typeof WISHLIST_REQUEST_SUCCESS } 27 | | { type: typeof WISHLIST_REQUEST_FAILURE; payload: string } 28 | | { type: typeof LOAD_WISHLIST; payload: WishlistItem[] } 29 | | { type: typeof ADD_TO_WISHLIST; payload: WishlistItem } 30 | | { type: typeof REMOVE_FROM_WISHLIST; payload: string } 31 | | { type: typeof CLEAR_WISHLIST }; 32 | 33 | export const reducer = (state = initialState, action: WishlistAction): WishlistState => { 34 | switch (action.type) { 35 | case WISHLIST_REQUEST_PENDING: 36 | return { 37 | ...state, 38 | isLoading: true, 39 | error: null, 40 | }; 41 | 42 | case WISHLIST_REQUEST_SUCCESS: 43 | return { 44 | ...state, 45 | isLoading: false, 46 | error: null, 47 | }; 48 | 49 | case WISHLIST_REQUEST_FAILURE: 50 | return { 51 | ...state, 52 | isLoading: false, 53 | error: action.payload, 54 | }; 55 | 56 | case LOAD_WISHLIST: 57 | return { 58 | ...state, 59 | items: action.payload || [], 60 | isLoading: false, 61 | error: null, 62 | }; 63 | 64 | case ADD_TO_WISHLIST: { 65 | // Check if item already exists in wishlist 66 | const existingItem = state.items.some( 67 | (item) => item.product_id === action.payload.product_id 68 | ); 69 | 70 | if (existingItem) { 71 | return state; 72 | } 73 | 74 | return { 75 | ...state, 76 | items: [...state.items, action.payload], 77 | }; 78 | } 79 | 80 | case REMOVE_FROM_WISHLIST: 81 | return { 82 | ...state, 83 | items: state.items.filter((item) => item.product_id !== action.payload), 84 | }; 85 | 86 | case CLEAR_WISHLIST: 87 | return { 88 | ...state, 89 | items: [], 90 | }; 91 | 92 | default: 93 | return state; 94 | } 95 | }; -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /supabase/migrations/20250629034518_yellow_oasis.sql: -------------------------------------------------------------------------------- 1 | /* 2 | # Create profiles table for user account management 3 | 4 | 1. New Tables 5 | - `profiles` 6 | - `id` (uuid, primary key, references auth.users) 7 | - `email` (text) 8 | - `first_name` (text) 9 | - `last_name` (text) 10 | - `phone` (text, optional) 11 | - `created_at` (timestamp) 12 | - `updated_at` (timestamp) 13 | 14 | 2. Security 15 | - Enable RLS on `profiles` table 16 | - Add policies for users to manage their own profiles 17 | */ 18 | 19 | CREATE TABLE IF NOT EXISTS profiles ( 20 | id uuid PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE, 21 | email text, 22 | first_name text, 23 | last_name text, 24 | phone text, 25 | created_at timestamptz DEFAULT now(), 26 | updated_at timestamptz DEFAULT now() 27 | ); 28 | 29 | -- Enable RLS 30 | ALTER TABLE profiles ENABLE ROW LEVEL SECURITY; 31 | 32 | -- Create policies 33 | CREATE POLICY "Users can view own profile" 34 | ON profiles 35 | FOR SELECT 36 | TO authenticated 37 | USING (auth.uid() = id); 38 | 39 | CREATE POLICY "Users can update own profile" 40 | ON profiles 41 | FOR UPDATE 42 | TO authenticated 43 | USING (auth.uid() = id); 44 | 45 | CREATE POLICY "Users can insert own profile" 46 | ON profiles 47 | FOR INSERT 48 | TO authenticated 49 | WITH CHECK (auth.uid() = id); 50 | 51 | -- Create function to automatically create profile on user signup 52 | CREATE OR REPLACE FUNCTION public.handle_new_user() 53 | RETURNS trigger AS $$ 54 | BEGIN 55 | INSERT INTO public.profiles (id, email, first_name, last_name) 56 | VALUES ( 57 | new.id, 58 | new.email, 59 | COALESCE(new.raw_user_meta_data->>'first_name', ''), 60 | COALESCE(new.raw_user_meta_data->>'last_name', '') 61 | ); 62 | RETURN new; 63 | END; 64 | $$ LANGUAGE plpgsql SECURITY DEFINER; 65 | 66 | -- Create trigger to automatically create profile on user signup 67 | DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users; 68 | CREATE TRIGGER on_auth_user_created 69 | AFTER INSERT ON auth.users 70 | FOR EACH ROW EXECUTE PROCEDURE public.handle_new_user(); 71 | 72 | -- Create function to update updated_at timestamp 73 | CREATE OR REPLACE FUNCTION public.handle_updated_at() 74 | RETURNS trigger AS $$ 75 | BEGIN 76 | NEW.updated_at = now(); 77 | RETURN NEW; 78 | END; 79 | $$ LANGUAGE plpgsql; 80 | 81 | -- Create trigger to update updated_at on profile changes 82 | CREATE TRIGGER handle_profiles_updated_at 83 | BEFORE UPDATE ON profiles 84 | FOR EACH ROW EXECUTE PROCEDURE public.handle_updated_at(); -------------------------------------------------------------------------------- /src/Components/Card.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import axios from 'axios'; 4 | import { toast } from 'react-toastify'; 5 | import { CardProps } from '../types'; 6 | 7 | const Card: React.FC = ({ 8 | actualPrice, 9 | type, 10 | id, 11 | image, 12 | price, 13 | title, 14 | discount 15 | }) => { 16 | const [isLoading, setIsLoading] = useState(false); 17 | 18 | const cartItem = { 19 | actualPrice, 20 | type, 21 | image, 22 | price, 23 | title, 24 | discount, 25 | quantity: 1, 26 | id, 27 | category: '', 28 | gender: type as 'men' | 'women' 29 | }; 30 | 31 | const handleClick = async (): Promise => { 32 | setIsLoading(true); 33 | try { 34 | await axios.post(`https://lifestyle-mock-server-api.onrender.com/cart`, cartItem); 35 | toast.success('Added to cart successfully!'); 36 | } catch (error) { 37 | console.error('Error adding to cart:', error); 38 | toast.error('Failed to add to cart'); 39 | } finally { 40 | setIsLoading(false); 41 | } 42 | }; 43 | 44 | return ( 45 |
46 | 47 |
48 | {title} 53 |
54 |
55 |
56 | ₹{price} 57 | {actualPrice && ( 58 | ₹{actualPrice} 59 | )} 60 |
61 |

{title}

62 |
63 | 64 | 65 | 72 |
73 | ); 74 | }; 75 | 76 | export default Card; -------------------------------------------------------------------------------- /src/redux/authReducer/action.ts: -------------------------------------------------------------------------------- 1 | import { Dispatch } from 'redux'; 2 | import { 3 | SIGNUP_FAILURE, 4 | SIGNUP_REQUEST, 5 | SIGNUP_SUCCESS, 6 | SIGNIN_FAILURE, 7 | SIGNIN_REQUEST, 8 | SIGNIN_SUCCESS, 9 | SIGNOUT, 10 | GET_USER, 11 | } from './actionTypes'; 12 | import { authService, SignUpData, SignInData } from '../../services/authService'; 13 | 14 | export const signUp = (userData: SignUpData) => async (dispatch: Dispatch) => { 15 | dispatch({ type: SIGNUP_REQUEST }); 16 | try { 17 | const data = await authService.signUp(userData); 18 | dispatch({ type: SIGNUP_SUCCESS, payload: data }); 19 | return data; 20 | } catch (error: any) { 21 | dispatch({ type: SIGNUP_FAILURE, payload: error.message }); 22 | throw error; 23 | } 24 | }; 25 | 26 | export const signIn = (credentials: SignInData) => async (dispatch: Dispatch) => { 27 | dispatch({ type: SIGNIN_REQUEST }); 28 | try { 29 | const data = await authService.signIn(credentials); 30 | dispatch({ type: SIGNIN_SUCCESS, payload: data }); 31 | return data; 32 | } catch (error: any) { 33 | dispatch({ type: SIGNIN_FAILURE, payload: error.message }); 34 | throw error; 35 | } 36 | }; 37 | 38 | export const signOut = () => async (dispatch: Dispatch) => { 39 | try { 40 | await authService.signOut(); 41 | dispatch({ type: SIGNOUT }); 42 | } catch (error: any) { 43 | console.error('Sign out error:', error); 44 | } 45 | }; 46 | 47 | export const getCurrentUser = () => async (dispatch: Dispatch) => { 48 | try { 49 | const user = await authService.getCurrentUser(); 50 | dispatch({ type: GET_USER, payload: user }); 51 | return user; 52 | } catch (error: any) { 53 | console.error('Get user error:', error); 54 | return null; 55 | } 56 | }; 57 | 58 | export const getSession = () => async (dispatch: Dispatch) => { 59 | try { 60 | const session = await authService.getSession(); 61 | if (session?.user) { 62 | dispatch({ type: SIGNIN_SUCCESS, payload: { user: session.user, session } }); 63 | } 64 | return session; 65 | } catch (error: any) { 66 | console.error('Get session error:', error); 67 | return null; 68 | } 69 | }; 70 | 71 | // Legacy actions for backward compatibility 72 | export const SignUpFunc = signUp; 73 | export const loginFunction = (userData: any) => (dispatch: Dispatch) => { 74 | dispatch({ type: SIGNIN_SUCCESS, payload: { user: userData } }); 75 | }; 76 | export const logout = signOut; 77 | export const getdata = () => async (dispatch: Dispatch) => { 78 | // This was used to get registered users from JSON server 79 | // Now we'll use Supabase auth instead 80 | return []; 81 | }; -------------------------------------------------------------------------------- /supabase/migrations/20250703023429_bronze_spire.sql: -------------------------------------------------------------------------------- 1 | /* 2 | # Create products_data table for new API integration 3 | 4 | 1. New Tables 5 | - `products_data` 6 | - `id` (integer, primary key) 7 | - `title` (text, product title) 8 | - `slug` (text, URL-friendly version) 9 | - `price` (integer, price in cents) 10 | - `description` (text, product description) 11 | - `category_id` (integer, category ID) 12 | - `category_name` (text, category name) 13 | - `category_slug` (text, category slug) 14 | - `category_image` (text, category image URL) 15 | - `images` (text[], array of image URLs) 16 | - `creation_at` (timestamptz, creation timestamp) 17 | - `updated_at` (timestamptz, update timestamp) 18 | 19 | 2. Security 20 | - Enable RLS on `products_data` table 21 | - Add policy for public read access 22 | - Add policy for authenticated users to insert/update (for admin) 23 | */ 24 | 25 | CREATE TABLE IF NOT EXISTS products_data ( 26 | id integer PRIMARY KEY, 27 | title text NOT NULL, 28 | slug text UNIQUE NOT NULL, 29 | price integer NOT NULL, 30 | description text, 31 | category_id integer, 32 | category_name text, 33 | category_slug text, 34 | category_image text, 35 | images text[], 36 | creation_at timestamptz DEFAULT now(), 37 | updated_at timestamptz DEFAULT now() 38 | ); 39 | 40 | -- Enable RLS 41 | ALTER TABLE products_data ENABLE ROW LEVEL SECURITY; 42 | 43 | -- Create policies 44 | CREATE POLICY "Enable read access for all users" 45 | ON products_data 46 | FOR SELECT 47 | TO public 48 | USING (true); 49 | 50 | CREATE POLICY "Enable insert for authenticated users" 51 | ON products_data 52 | FOR INSERT 53 | TO authenticated 54 | WITH CHECK (true); 55 | 56 | CREATE POLICY "Enable update for authenticated users" 57 | ON products_data 58 | FOR UPDATE 59 | TO authenticated 60 | USING (true); 61 | 62 | CREATE POLICY "Enable delete for authenticated users" 63 | ON products_data 64 | FOR DELETE 65 | TO authenticated 66 | USING (true); 67 | 68 | -- Create indexes for better performance 69 | CREATE INDEX IF NOT EXISTS products_data_category_id_idx ON products_data(category_id); 70 | CREATE INDEX IF NOT EXISTS products_data_category_name_idx ON products_data(category_name); 71 | CREATE INDEX IF NOT EXISTS products_data_price_idx ON products_data(price); 72 | CREATE INDEX IF NOT EXISTS products_data_slug_idx ON products_data(slug); 73 | 74 | -- Create function to update updated_at timestamp 75 | CREATE OR REPLACE FUNCTION update_products_data_updated_at() 76 | RETURNS trigger AS $$ 77 | BEGIN 78 | NEW.updated_at = now(); 79 | RETURN NEW; 80 | END; 81 | $$ LANGUAGE plpgsql; 82 | 83 | -- Create trigger to update updated_at on changes 84 | CREATE TRIGGER update_products_data_updated_at 85 | BEFORE UPDATE ON products_data 86 | FOR EACH ROW EXECUTE PROCEDURE update_products_data_updated_at(); -------------------------------------------------------------------------------- /supabase/functions/admin-users/index.ts: -------------------------------------------------------------------------------- 1 | import { createClient } from "npm:@supabase/supabase-js@2"; 2 | 3 | const corsHeaders = { 4 | "Access-Control-Allow-Origin": "*", 5 | "Access-Control-Allow-Headers": 6 | "authorization, x-client-info, apikey, content-type", 7 | "Access-Control-Allow-Methods": "GET, POST, OPTIONS", 8 | }; 9 | 10 | interface AuthUser { 11 | id: string; 12 | email: string; 13 | created_at: string; 14 | user_metadata: { 15 | first_name?: string; 16 | last_name?: string; 17 | }; 18 | } 19 | 20 | Deno.serve(async (req) => { 21 | // Handle CORS preflight requests 22 | if (req.method === "OPTIONS") { 23 | return new Response(null, { headers: corsHeaders }); 24 | } 25 | 26 | try { 27 | // Get the authorization header 28 | const authHeader = req.headers.get("Authorization"); 29 | if (!authHeader) { 30 | return new Response( 31 | JSON.stringify({ error: "Missing authorization header" }), 32 | { 33 | status: 401, 34 | headers: { ...corsHeaders, "Content-Type": "application/json" }, 35 | } 36 | ); 37 | } 38 | 39 | // Create Supabase client with service role key for admin operations 40 | const supabaseAdmin = createClient( 41 | Deno.env.get("SUPABASE_URL") ?? "", 42 | Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") ?? "" 43 | ); 44 | 45 | // Verify the user making the request is authenticated 46 | const supabaseClient = createClient( 47 | Deno.env.get("SUPABASE_URL") ?? "", 48 | Deno.env.get("SUPABASE_ANON_KEY") ?? "" 49 | ); 50 | 51 | const { 52 | data: { user }, 53 | error: authError, 54 | } = await supabaseClient.auth.getUser(authHeader.replace("Bearer ", "")); 55 | 56 | if (authError || !user) { 57 | return new Response(JSON.stringify({ error: "Unauthorized" }), { 58 | status: 401, 59 | headers: { ...corsHeaders, "Content-Type": "application/json" }, 60 | }); 61 | } 62 | 63 | // TODO: Add additional admin role verification here if needed 64 | // For now, any authenticated user can access this endpoint 65 | // In production, you should verify the user has admin privileges 66 | 67 | // Fetch all users using admin client 68 | const { data: authUsers, error: usersError } = 69 | await supabaseAdmin.auth.admin.listUsers(); 70 | 71 | if (usersError) { 72 | throw usersError; 73 | } 74 | 75 | // Transform the data to match the expected format 76 | const users: AuthUser[] = authUsers.users.map((user) => ({ 77 | id: user.id, 78 | email: user.email || "", 79 | created_at: user.created_at, 80 | user_metadata: user.user_metadata || {}, 81 | })); 82 | 83 | return new Response(JSON.stringify({ users }), { 84 | headers: { ...corsHeaders, "Content-Type": "application/json" }, 85 | }); 86 | } catch (error) { 87 | console.error("Error in admin-users function:", error); 88 | return new Response(JSON.stringify({ error: "Internal server error" }), { 89 | status: 500, 90 | headers: { ...corsHeaders, "Content-Type": "application/json" }, 91 | }); 92 | } 93 | }); 94 | -------------------------------------------------------------------------------- /src/redux/wishlistReducer/action.ts: -------------------------------------------------------------------------------- 1 | import { Dispatch } from 'redux'; 2 | import { 3 | ADD_TO_WISHLIST, 4 | REMOVE_FROM_WISHLIST, 5 | LOAD_WISHLIST, 6 | CLEAR_WISHLIST, 7 | WISHLIST_REQUEST_PENDING, 8 | WISHLIST_REQUEST_SUCCESS, 9 | WISHLIST_REQUEST_FAILURE, 10 | } from "./actionTypes"; 11 | import { wishlistService } from '../../services/wishlistService'; 12 | 13 | // Action to start wishlist operation 14 | const wishlistRequestPending = () => ({ 15 | type: WISHLIST_REQUEST_PENDING, 16 | }); 17 | 18 | // Action for successful wishlist operation 19 | const wishlistRequestSuccess = () => ({ 20 | type: WISHLIST_REQUEST_SUCCESS, 21 | }); 22 | 23 | // Action for failed wishlist operation 24 | const wishlistRequestFailure = (error: string) => ({ 25 | type: WISHLIST_REQUEST_FAILURE, 26 | payload: error, 27 | }); 28 | 29 | // Load wishlist from Supabase 30 | export const loadWishlist = (userId: string) => async (dispatch: Dispatch) => { 31 | dispatch(wishlistRequestPending()); 32 | try { 33 | const wishlistItems = await wishlistService.getWishlistItems(userId); 34 | dispatch({ 35 | type: LOAD_WISHLIST, 36 | payload: wishlistItems, 37 | }); 38 | dispatch(wishlistRequestSuccess()); 39 | } catch (error: any) { 40 | dispatch(wishlistRequestFailure(error.message)); 41 | } 42 | }; 43 | 44 | // Add item to wishlist 45 | export const addToWishlist = (userId: string, productId: string) => async (dispatch: Dispatch) => { 46 | dispatch(wishlistRequestPending()); 47 | try { 48 | const wishlistItem = await wishlistService.addToWishlist(userId, productId); 49 | 50 | dispatch({ 51 | type: ADD_TO_WISHLIST, 52 | payload: { 53 | ...wishlistItem, 54 | product_id: productId, 55 | user_id: userId, 56 | created_at: wishlistItem.created_at || new Date().toISOString(), 57 | }, 58 | }); 59 | 60 | dispatch(wishlistRequestSuccess()); 61 | return wishlistItem; 62 | } catch (error: any) { 63 | dispatch(wishlistRequestFailure(error.message)); 64 | throw error; 65 | } 66 | }; 67 | 68 | // Remove item from wishlist 69 | export const removeFromWishlist = (userId: string, productId: string) => async (dispatch: Dispatch) => { 70 | dispatch(wishlistRequestPending()); 71 | try { 72 | await wishlistService.removeFromWishlist(userId, productId); 73 | 74 | dispatch({ 75 | type: REMOVE_FROM_WISHLIST, 76 | payload: productId, 77 | }); 78 | 79 | dispatch(wishlistRequestSuccess()); 80 | } catch (error: any) { 81 | dispatch(wishlistRequestFailure(error.message)); 82 | throw error; 83 | } 84 | }; 85 | 86 | // Clear wishlist 87 | export const clearWishlist = (userId: string) => async (dispatch: Dispatch) => { 88 | dispatch(wishlistRequestPending()); 89 | try { 90 | await wishlistService.clearWishlist(userId); 91 | 92 | dispatch({ 93 | type: CLEAR_WISHLIST, 94 | }); 95 | 96 | dispatch(wishlistRequestSuccess()); 97 | } catch (error: any) { 98 | dispatch(wishlistRequestFailure(error.message)); 99 | throw error; 100 | } 101 | }; -------------------------------------------------------------------------------- /src/redux/authReducer/reducer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SIGNUP_FAILURE, 3 | SIGNUP_REQUEST, 4 | SIGNUP_SUCCESS, 5 | SIGNIN_FAILURE, 6 | SIGNIN_REQUEST, 7 | SIGNIN_SUCCESS, 8 | SIGNOUT, 9 | GET_USER, 10 | } from "./actionTypes"; 11 | import { AuthState, User, AuthUser } from "../../types"; 12 | 13 | const initialState: AuthState = { 14 | createAccountLoading: false, 15 | successCreate: false, 16 | createError: false, 17 | userData: [], 18 | isAuth: false, 19 | isLoading: false, 20 | isError: false, 21 | afterLoginUser: {} as AuthUser, 22 | isAdmin: false, 23 | user: null, 24 | session: null, 25 | }; 26 | 27 | interface AuthAction { 28 | type: string; 29 | payload?: any; 30 | } 31 | 32 | // Admin email list - you can expand this or move to environment variables 33 | const ADMIN_EMAILS = ['deepakpatil2612@gmail.com']; 34 | 35 | const checkIsAdmin = (email: string): boolean => { 36 | return ADMIN_EMAILS.includes(email.toLowerCase()); 37 | }; 38 | 39 | export const reducer = (state = initialState, action: AuthAction): AuthState => { 40 | const { type, payload } = action; 41 | 42 | switch (type) { 43 | case SIGNUP_REQUEST: 44 | return { 45 | ...state, 46 | createAccountLoading: true, 47 | createError: false, 48 | }; 49 | 50 | case SIGNUP_SUCCESS: 51 | return { 52 | ...state, 53 | createAccountLoading: false, 54 | successCreate: true, 55 | createError: false, 56 | }; 57 | 58 | case SIGNUP_FAILURE: 59 | return { 60 | ...state, 61 | createAccountLoading: false, 62 | successCreate: false, 63 | createError: true, 64 | }; 65 | 66 | case SIGNIN_REQUEST: 67 | return { 68 | ...state, 69 | isLoading: true, 70 | isError: false, 71 | }; 72 | 73 | case SIGNIN_SUCCESS: 74 | const userEmail = payload.user?.email || ''; 75 | const isAdmin = checkIsAdmin(userEmail); 76 | 77 | return { 78 | ...state, 79 | isLoading: false, 80 | isAuth: true, 81 | user: payload.user, 82 | session: payload.session, 83 | isAdmin: isAdmin, 84 | afterLoginUser: { 85 | email: userEmail, 86 | name: payload.user?.user_metadata?.first_name || payload.user?.email?.split('@')[0] || '', 87 | password: '', 88 | }, 89 | isError: false, 90 | }; 91 | 92 | case SIGNIN_FAILURE: 93 | return { 94 | ...state, 95 | isLoading: false, 96 | isError: true, 97 | isAuth: false, 98 | user: null, 99 | session: null, 100 | isAdmin: false, 101 | }; 102 | 103 | case SIGNOUT: 104 | return { 105 | ...initialState, // Reset to initial state to clear cart and other data 106 | }; 107 | 108 | case GET_USER: 109 | const getUserEmail = payload?.email || ''; 110 | const getUserIsAdmin = checkIsAdmin(getUserEmail); 111 | 112 | return { 113 | ...state, 114 | user: payload, 115 | isAuth: !!payload, 116 | isAdmin: getUserIsAdmin, 117 | }; 118 | 119 | default: 120 | return state; 121 | } 122 | }; -------------------------------------------------------------------------------- /src/Components/MainRoutes.tsx: -------------------------------------------------------------------------------- 1 | import React, { Suspense } from "react"; 2 | import { Route, Routes } from "react-router-dom"; 3 | import NewHomePage from "../pages/NewHomePage"; 4 | import CategoryPage from "../pages/CategoryPage"; 5 | import ProductDetailPage from "../pages/ProductDetailPage"; 6 | import { Men } from "../pages/Men"; 7 | import { Women } from "../pages/Women"; 8 | import Singlecardwomen from "./Singlecard"; 9 | import Singlecardmen from "./Singlecardmen"; 10 | import Login from "../pages/Login"; 11 | import Signup from "../pages/SignUp"; 12 | import Checkout from "../pages/Checkout"; 13 | import Payment from "../pages/Payment"; 14 | import { Cart } from "../pages/Cart"; 15 | import Account from "../pages/Account"; 16 | import OrderHistory from "../pages/OrderHistory"; 17 | import Wishlist from "../pages/Wishlist"; 18 | import AdminProduct from "./Admin/AdminProduct"; 19 | import AdminManageProduct from "./Admin/AdminManageProduct"; 20 | import AdminEdit from "./Admin/AdminEdit"; 21 | import ManageUsers from "./Admin/ManageUsers"; 22 | import AdminLogin from "./Admin/AdminLogin"; 23 | import Admin from "../pages/Admin"; 24 | import PrivateRoutes from "./PrivateRoutes"; 25 | 26 | const LoadingSpinner: React.FC = () => ( 27 |
28 |
29 |
30 | ); 31 | 32 | const MainRoutes: React.FC = () => { 33 | return ( 34 | }> 35 | 36 | {/* New Routes with API Integration */} 37 | } /> 38 | } /> 39 | } /> 40 | 41 | {/* Legacy Routes (keeping for backward compatibility) */} 42 | } /> 43 | } /> 44 | } /> 45 | } /> 46 | 47 | {/* Auth Routes */} 48 | } /> 49 | } /> 50 | 51 | {/* Protected Routes */} 52 | } /> 53 | } /> 54 | } /> 55 | } /> 56 | } /> 57 | } /> 58 | 59 | {/* Admin Routes */} 60 | } /> 61 | } /> 62 | } /> 63 | } /> 64 | } /> 65 | } /> 66 | 67 | 68 | ); 69 | }; 70 | 71 | export default MainRoutes; -------------------------------------------------------------------------------- /src/Components/Home/NavbarTop.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Box, Divider, Flex } from "@chakra-ui/react"; 3 | import { 4 | MdOutlineLocalShipping, 5 | MdOutlineAddToHomeScreen, 6 | } from "react-icons/md"; 7 | import { FaHome } from "react-icons/fa"; 8 | 9 | const NavbarTop = () => { 10 | return ( 11 | 12 | 18 | 19 | 20 | 21 | 29 | Free shipping 30 | 31 | 32 | 33 | 34 | 35 | 43 | Click & Collect 44 | 45 | 46 | 47 | 48 | 49 | 57 | Return To Store 58 | 59 | 60 | 61 | 62 | 63 | 64 | 72 | Download Our App 73 | 74 | 75 | 76 | 77 | 78 | 79 | 87 | Store Locator 88 | 89 | 90 | 91 | 92 | 93 | 94 | 102 | Help 103 | 104 | 105 | 106 | 107 | 108 | ); 109 | }; 110 | export default NavbarTop; 111 | -------------------------------------------------------------------------------- /src/redux/MenReducer/action.ts: -------------------------------------------------------------------------------- 1 | import { Dispatch } from 'redux'; 2 | import { 3 | MEN_REQUEST_FAILURE, 4 | MEN_REQUEST_PENDING, 5 | MEN_REQUEST_SUCCESS, 6 | WOMEN_REQUEST_FAILURE, 7 | WOMEN_REQUEST_PENDING, 8 | WOMEN_REQUEST_SUCCESS, 9 | } from "./actionType"; 10 | import { productService, ProductFilters } from "../../services/productService"; 11 | import { newProductService } from "../../services/newProductService"; 12 | import { QueryParams, Product, ApiResponse } from "../../types"; 13 | 14 | export const getmens = (queryParams: QueryParams) => async (dispatch: Dispatch) => { 15 | dispatch({ type: MEN_REQUEST_PENDING }); 16 | try { 17 | const filters: ProductFilters = { 18 | category: queryParams.params.category, 19 | page: queryParams.params._page ? parseInt(queryParams.params._page) : 1, 20 | limit: 12, 21 | sortBy: queryParams.params._sort ? 'price' : undefined, 22 | sortOrder: queryParams.params._order as 'asc' | 'desc' | undefined, 23 | }; 24 | 25 | const result = await productService.getMenProducts(filters); 26 | 27 | const obj: ApiResponse = { 28 | data: result.data, 29 | total: result.total, 30 | }; 31 | 32 | dispatch({ type: MEN_REQUEST_SUCCESS, payload: obj }); 33 | } catch (error) { 34 | console.error('Error fetching men products:', error); 35 | dispatch({ type: MEN_REQUEST_FAILURE }); 36 | } 37 | }; 38 | 39 | export const getwomens = (queryParams: QueryParams) => async (dispatch: Dispatch) => { 40 | dispatch({ type: WOMEN_REQUEST_PENDING }); 41 | try { 42 | const filters: ProductFilters = { 43 | category: queryParams.params.category, 44 | page: queryParams.params._page ? parseInt(queryParams.params._page) : 1, 45 | limit: 12, 46 | sortBy: queryParams.params._sort ? 'price' : undefined, 47 | sortOrder: queryParams.params._order as 'asc' | 'desc' | undefined, 48 | }; 49 | 50 | const result = await productService.getWomenProducts(filters); 51 | 52 | const obj: ApiResponse = { 53 | data: result.data, 54 | total: result.total, 55 | }; 56 | 57 | dispatch({ type: WOMEN_REQUEST_SUCCESS, payload: obj }); 58 | } catch (error) { 59 | console.error('Error fetching women products:', error); 60 | dispatch({ type: WOMEN_REQUEST_FAILURE }); 61 | } 62 | }; 63 | 64 | // New actions for API integration 65 | export const getProductsByCategory = (categorySlug: string, limit = 12, offset = 0) => async (dispatch: Dispatch) => { 66 | dispatch({ type: MEN_REQUEST_PENDING }); 67 | try { 68 | const result = await newProductService.getProductsByCategorySlug(categorySlug, limit, offset); 69 | 70 | const obj: ApiResponse = { 71 | data: result.data, 72 | total: result.total, 73 | }; 74 | 75 | dispatch({ type: MEN_REQUEST_SUCCESS, payload: obj }); 76 | } catch (error) { 77 | console.error('Error fetching products by category:', error); 78 | dispatch({ type: MEN_REQUEST_FAILURE }); 79 | } 80 | }; 81 | 82 | export const searchProducts = (query: string, limit = 12) => async (dispatch: Dispatch) => { 83 | dispatch({ type: MEN_REQUEST_PENDING }); 84 | try { 85 | const result = await newProductService.searchProducts(query, limit); 86 | 87 | const obj: ApiResponse = { 88 | data: result.data, 89 | total: result.total, 90 | }; 91 | 92 | dispatch({ type: MEN_REQUEST_SUCCESS, payload: obj }); 93 | } catch (error) { 94 | console.error('Error searching products:', error); 95 | dispatch({ type: MEN_REQUEST_FAILURE }); 96 | } 97 | }; -------------------------------------------------------------------------------- /src/services/productService.ts: -------------------------------------------------------------------------------- 1 | import { supabase } from '../lib/supabase' 2 | import { Product } from '../lib/supabase' 3 | import { toBackendId, toFrontendId } from '../utils/idMapper' 4 | 5 | export interface ProductFilters { 6 | category?: string[] 7 | page?: number 8 | limit?: number 9 | sortBy?: 'price' 10 | sortOrder?: 'asc' | 'desc' 11 | } 12 | 13 | export const productService = { 14 | // Get men's products 15 | async getMenProducts(filters: ProductFilters = {}) { 16 | const { category, page = 1, limit = 12, sortBy, sortOrder } = filters 17 | 18 | let query = supabase 19 | .from('products_data') 20 | .select('*', { count: 'exact' }) 21 | .eq('gender', 'men') 22 | 23 | // Apply pagination 24 | const from = (page - 1) * limit 25 | const to = from + limit - 1 26 | query = query.range(from, to) 27 | 28 | if (category && category.length > 0) { 29 | query = query.in('category', category) 30 | } 31 | 32 | if (sortBy && sortOrder) { 33 | query = query.order(sortBy, { ascending: sortOrder === 'asc' }) 34 | } 35 | 36 | const { data, error, count } = await query 37 | 38 | if (error) throw error 39 | 40 | return { data: data || [], total: count || 0 } 41 | }, 42 | 43 | // Get women's products 44 | async getWomenProducts(filters: ProductFilters = {}) { 45 | const { category, page = 1, limit = 12, sortBy, sortOrder } = filters 46 | 47 | let query = supabase 48 | .from('products_data') 49 | .select('*', { count: 'exact' }) 50 | .eq('gender', 'women') 51 | 52 | // Apply pagination 53 | const from = (page - 1) * limit 54 | const to = from + limit - 1 55 | query = query.range(from, to) 56 | 57 | if (category && category.length > 0) { 58 | query = query.in('category', category) 59 | } 60 | 61 | if (sortBy && sortOrder) { 62 | query = query.order(sortBy, { ascending: sortOrder === 'asc' }) 63 | } 64 | 65 | const { data, error, count } = await query 66 | 67 | if (error) throw error 68 | 69 | return { data: data || [], total: count || 0 } 70 | }, 71 | 72 | // Get single product 73 | async getProduct(id: string) { 74 | const backendId = toBackendId(id) 75 | const { data, error } = await supabase 76 | .from('products_data') 77 | .select('*') 78 | .eq('id', backendId) 79 | .single() 80 | 81 | if (error) throw error 82 | 83 | // Convert the ID back to frontend format 84 | if (data) { 85 | return { 86 | ...data, 87 | id: toFrontendId(data.id).toString() 88 | } 89 | } 90 | return null 91 | }, 92 | 93 | // Add product (admin) 94 | async addProduct(product: Omit) { 95 | const { data, error } = await supabase 96 | .from('products_data') 97 | .insert([product]) 98 | .select() 99 | .single() 100 | 101 | if (error) throw error 102 | return data 103 | }, 104 | 105 | // Update product (admin) 106 | async updateProduct(id: string, updates: Partial) { 107 | const { data, error } = await supabase 108 | .from('products_data') 109 | .update(updates) 110 | .eq('id', id) 111 | .select() 112 | .single() 113 | 114 | if (error) throw error 115 | return data 116 | }, 117 | 118 | // Delete product (admin) 119 | async deleteProduct(id: string) { 120 | const { error } = await supabase 121 | .from('products_data') 122 | .delete() 123 | .eq('id', id) 124 | 125 | if (error) throw error 126 | } 127 | } -------------------------------------------------------------------------------- /src/Components/Home/TrendingSlider.jsx: -------------------------------------------------------------------------------- 1 | import { Box, Image, Text } from "@chakra-ui/react"; 2 | import React, { Component } from "react"; 3 | import Slider from "react-slick"; 4 | import { IoIosArrowForward } from "react-icons/io"; 5 | const data = [ 6 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-Promowidget20-Common-Banner1-10Mar23.jpg", 7 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-Promowidget20-Common-Banner2-10Mar23.jpg", 8 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-Promowidget20-Common-Banner3-10Mar23.jpg", 9 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-Promowidget20-Common-Banner4-10Mar23.jpg", 10 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-Promowidget20-Common-Banner5-10Mar23.jpg", 11 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-Promowidget20-Common-Banner6-10Mar23.jpg", 12 | ]; 13 | function SampleNextArrow({ onClick }) { 14 | return ( 15 | 35 | 36 | 37 | ); 38 | } 39 | export default class TrendingSlider extends Component { 40 | render() { 41 | var settings = { 42 | dots: true, 43 | infinite: false, 44 | speed: 500, 45 | slidesToShow: 4, 46 | slidesToScroll: 2, 47 | initialSlide: 0, 48 | nextArrow: , 49 | responsive: [ 50 | { 51 | breakpoint: 1024, 52 | settings: { 53 | slidesToShow: 3, 54 | slidesToScroll: 2, 55 | infinite: true, 56 | dots: true, 57 | }, 58 | }, 59 | { 60 | breakpoint: 600, 61 | settings: { 62 | slidesToShow: 2, 63 | slidesToScroll: 2, 64 | initialSlide: 2, 65 | }, 66 | }, 67 | { 68 | breakpoint: 480, 69 | settings: { 70 | slidesToShow: 2, 71 | slidesToScroll: 1, 72 | }, 73 | }, 74 | ], 75 | }; 76 | return ( 77 | 78 | 79 | {data.map((item, i) => { 80 | return ( 81 | 82 | 88 | 89 | Up To 50% Off 90 | 91 | 92 | ); 93 | })} 94 | 95 | 96 | ); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /supabase/migrations/20250703024936_warm_sunset.sql: -------------------------------------------------------------------------------- 1 | /* 2 | # Add individual image columns to products_data table 3 | 4 | 1. Changes 5 | - Add `images__001` column for first image 6 | - Add `images__002` column for second image 7 | - Add `images__003` column for third image 8 | - Add `images__004` column for fourth image 9 | - Add `images__005` column for fifth image 10 | - All columns are text type and nullable 11 | 12 | 2. Notes 13 | - These columns will work alongside the existing `images` array column 14 | - Allows for easier querying of individual images 15 | - Maintains backward compatibility with existing `images` array 16 | */ 17 | 18 | -- Add individual image columns to products_data table 19 | DO $$ 20 | BEGIN 21 | -- Add images__001 column 22 | IF NOT EXISTS ( 23 | SELECT 1 FROM information_schema.columns 24 | WHERE table_name = 'products_data' AND column_name = 'images__001' 25 | ) THEN 26 | ALTER TABLE products_data ADD COLUMN images__001 text; 27 | END IF; 28 | 29 | -- Add images__002 column 30 | IF NOT EXISTS ( 31 | SELECT 1 FROM information_schema.columns 32 | WHERE table_name = 'products_data' AND column_name = 'images__002' 33 | ) THEN 34 | ALTER TABLE products_data ADD COLUMN images__002 text; 35 | END IF; 36 | 37 | -- Add images__003 column 38 | IF NOT EXISTS ( 39 | SELECT 1 FROM information_schema.columns 40 | WHERE table_name = 'products_data' AND column_name = 'images__003' 41 | ) THEN 42 | ALTER TABLE products_data ADD COLUMN images__003 text; 43 | END IF; 44 | 45 | -- Add images__004 column 46 | IF NOT EXISTS ( 47 | SELECT 1 FROM information_schema.columns 48 | WHERE table_name = 'products_data' AND column_name = 'images__004' 49 | ) THEN 50 | ALTER TABLE products_data ADD COLUMN images__004 text; 51 | END IF; 52 | 53 | -- Add images__005 column 54 | IF NOT EXISTS ( 55 | SELECT 1 FROM information_schema.columns 56 | WHERE table_name = 'products_data' AND column_name = 'images__005' 57 | ) THEN 58 | ALTER TABLE products_data ADD COLUMN images__005 text; 59 | END IF; 60 | END $$; 61 | 62 | -- Create a function to automatically populate individual image columns from the images array 63 | CREATE OR REPLACE FUNCTION populate_individual_images() 64 | RETURNS trigger AS $$ 65 | BEGIN 66 | -- Extract individual images from the array and populate separate columns 67 | NEW.images__001 := CASE WHEN array_length(NEW.images, 1) >= 1 THEN NEW.images[1] ELSE NULL END; 68 | NEW.images__002 := CASE WHEN array_length(NEW.images, 1) >= 2 THEN NEW.images[2] ELSE NULL END; 69 | NEW.images__003 := CASE WHEN array_length(NEW.images, 1) >= 3 THEN NEW.images[3] ELSE NULL END; 70 | NEW.images__004 := CASE WHEN array_length(NEW.images, 1) >= 4 THEN NEW.images[4] ELSE NULL END; 71 | NEW.images__005 := CASE WHEN array_length(NEW.images, 1) >= 5 THEN NEW.images[5] ELSE NULL END; 72 | 73 | RETURN NEW; 74 | END; 75 | $$ LANGUAGE plpgsql; 76 | 77 | -- Create trigger to automatically populate individual image columns 78 | DROP TRIGGER IF EXISTS populate_individual_images_trigger ON products_data; 79 | CREATE TRIGGER populate_individual_images_trigger 80 | BEFORE INSERT OR UPDATE ON products_data 81 | FOR EACH ROW EXECUTE PROCEDURE populate_individual_images(); 82 | 83 | -- Update existing records to populate the individual image columns 84 | UPDATE products_data SET 85 | images__001 = CASE WHEN array_length(images, 1) >= 1 THEN images[1] ELSE NULL END, 86 | images__002 = CASE WHEN array_length(images, 1) >= 2 THEN images[2] ELSE NULL END, 87 | images__003 = CASE WHEN array_length(images, 1) >= 3 THEN images[3] ELSE NULL END, 88 | images__004 = CASE WHEN array_length(images, 1) >= 4 THEN images[4] ELSE NULL END, 89 | images__005 = CASE WHEN array_length(images, 1) >= 5 THEN images[5] ELSE NULL END 90 | WHERE images IS NOT NULL; -------------------------------------------------------------------------------- /src/Components/Home/KidSlider.jsx: -------------------------------------------------------------------------------- 1 | import { Box, Image, Text } from "@chakra-ui/react"; 2 | import React, { Component } from "react"; 3 | import Slider from "react-slick"; 4 | import { IoIosArrowForward } from "react-icons/io"; 5 | const data = [ 6 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-Promowidget27-Desktop-Banner1-10March23.jpg", 7 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-Promowidget27-Desktop-Banner2-10March23.jpg", 8 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-Promowidget27-Desktop-Banner3-10March23.jpg", 9 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-Promowidget27-Desktop-Banner4-10March23.jpg", 10 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-Promowidget27-Desktop-Banner5-10March23.jpg", 11 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-Promowidget27-Desktop-Banner6-10March23.jpg", 12 | ]; 13 | 14 | function SampleNextArrow({ onClick }) { 15 | return ( 16 | 36 | 37 | 38 | ); 39 | } 40 | export default class KidSlider extends Component { 41 | render() { 42 | var settings = { 43 | dots: true, 44 | infinite: false, 45 | speed: 500, 46 | slidesToShow: 4, 47 | slidesToScroll: 2, 48 | initialSlide: 0, 49 | nextArrow: , 50 | responsive: [ 51 | { 52 | breakpoint: 1024, 53 | settings: { 54 | slidesToShow: 3, 55 | slidesToScroll: 2, 56 | infinite: true, 57 | dots: true, 58 | }, 59 | }, 60 | { 61 | breakpoint: 600, 62 | settings: { 63 | slidesToShow: 2, 64 | slidesToScroll: 2, 65 | initialSlide: 2, 66 | }, 67 | }, 68 | { 69 | breakpoint: 480, 70 | settings: { 71 | slidesToShow: 2, 72 | slidesToScroll: 1, 73 | }, 74 | }, 75 | ], 76 | }; 77 | return ( 78 | 79 | 80 | {data.map((item, i) => { 81 | return ( 82 | 83 | 89 | 90 | Up To 50% Off 91 | 92 | 93 | ); 94 | })} 95 | 96 | 97 | ); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/Components/Home/UnMissSlider.jsx: -------------------------------------------------------------------------------- 1 | import { Box, Image } from "@chakra-ui/react"; 2 | import React, { Component } from "react"; 3 | import Slider from "react-slick"; 4 | import { IoIosArrowForward } from "react-icons/io"; 5 | 6 | const data = [ 7 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-LimitedSale-Desk-Banner1-24March23.jpg", 8 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-LimitedSale-Desk-Banner2-24March23.jpg", 9 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-LimitedSale-Desk-Banner3-01March23.jpg", 10 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-LimitedSale-Desk-Banner4-24March23.jpg", 11 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-LimitedSale-Desk-Banner4-24March23.jpg", 12 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-LimitedSale-Desk-Banner5-01March23.jpg", 13 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-LimitedSale-Desk-Banner6-01March23.jpg", 14 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-LimitedSale-Desk-Banner7-01March23.jpg", 15 | ]; 16 | 17 | function SampleNextArrow({ onClick }) { 18 | return ( 19 | 39 | 40 | 41 | ); 42 | } 43 | 44 | export default class UnMissSlider extends Component { 45 | render() { 46 | var settings = { 47 | dots: true, 48 | infinite: false, 49 | speed: 500, 50 | slidesToShow: 4, 51 | slidesToScroll: 2, 52 | initialSlide: 0, 53 | nextArrow: , 54 | responsive: [ 55 | { 56 | breakpoint: 1024, 57 | settings: { 58 | slidesToShow: 3, 59 | slidesToScroll: 2, 60 | infinite: true, 61 | dots: true, 62 | }, 63 | }, 64 | { 65 | breakpoint: 600, 66 | settings: { 67 | slidesToShow: 2, 68 | slidesToScroll: 1, 69 | initialSlide: 2, 70 | }, 71 | }, 72 | { 73 | breakpoint: 480, 74 | settings: { 75 | slidesToShow: 2, 76 | slidesToScroll: 1, 77 | }, 78 | }, 79 | ], 80 | }; 81 | return ( 82 | 83 | 84 | {data.map((item, i) => { 85 | return ( 86 | 87 | 93 | 94 | ); 95 | })} 96 | 97 | 98 | ); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/pages/Admin.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useSelector } from "react-redux"; 3 | import AdminNavbar from "../Components/Admin/AdminNavbar"; 4 | import AdminSidebar from "../Components/Admin/AdminSidebar"; 5 | import { RootState } from "../types"; 6 | 7 | const Admin: React.FC = () => { 8 | const { afterLoginUser, isAdmin } = useSelector((state: RootState) => state.AuthReducer); 9 | 10 | return ( 11 |
12 | 13 | 14 |
15 |
16 |
17 |

Admin Dashboard

18 |

19 | Welcome back, {afterLoginUser.name}! You have admin access to manage the store. 20 |

21 |
22 | 23 |
24 |
25 |

Products

26 |

Manage your product catalog

27 |
28 | 29 | Items 30 |
31 |
32 | 33 |
34 |

Users

35 |

View and manage users

36 |
37 | 38 | Users 39 |
40 |
41 | 42 |
43 |

Orders

44 |

Track customer orders

45 |
46 | 47 | Orders 48 |
49 |
50 |
51 | 52 |
53 |
54 |
55 |
56 |

Admin Access Confirmed

57 |

58 | You are logged in as an administrator with full access to all admin features. 59 |

60 |
61 |
62 |
63 | 64 | 83 |
84 |
85 |
86 | ); 87 | }; 88 | 89 | export default Admin; -------------------------------------------------------------------------------- /src/Components/Home/MensSlider.jsx: -------------------------------------------------------------------------------- 1 | import { Box, Image, Text } from "@chakra-ui/react"; 2 | import React, { Component } from "react"; 3 | import Slider from "react-slick"; 4 | import { IoIosArrowForward } from "react-icons/io"; 5 | const data = [ 6 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-Promowidge2-Common-Banner4-24March23.jpg", 7 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-Promowidge2-Common-Banner1-07March23.jpg", 8 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-Promowidge2-Common-Banner2-07March23.jpg", 9 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-Promowidge2-Common-Banner3-07March23.jpg", 10 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-Promowidge2-Common-Banner5-24March23.jpg", 11 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-Promowidge2-Common-Banner6-07March23.jpg", 12 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-Promowidge2-Common-Banner9-16March23.jpg", 13 | "https://lmsin.net/cdn-cgi/image/w=300,q=70,fit=cover/https://70415bb9924dca896de0-34a37044c62e41b40b39fcedad8af927.lmsin.net/LS-Fest/LS-new/LS-UberHP-Promowidge2-Common-Banner8-07March23.jpg", 14 | ]; 15 | function SampleNextArrow({ onClick }) { 16 | return ( 17 | 37 | 38 | 39 | ); 40 | } 41 | export default class MenSlider extends Component { 42 | render() { 43 | var settings = { 44 | dots: true, 45 | infinite: false, 46 | speed: 500, 47 | slidesToShow: 4, 48 | slidesToScroll: 2, 49 | initialSlide: 0, 50 | nextArrow: , 51 | responsive: [ 52 | { 53 | breakpoint: 1024, 54 | settings: { 55 | slidesToShow: 3, 56 | slidesToScroll: 2, 57 | infinite: true, 58 | dots: true, 59 | }, 60 | }, 61 | { 62 | breakpoint: 600, 63 | settings: { 64 | slidesToShow: 2, 65 | slidesToScroll: 2, 66 | initialSlide: 2, 67 | }, 68 | }, 69 | { 70 | breakpoint: 480, 71 | settings: { 72 | slidesToShow: 2, 73 | slidesToScroll: 1, 74 | }, 75 | }, 76 | ], 77 | }; 78 | return ( 79 | 80 | 81 | {data.map((item, i) => { 82 | return ( 83 | 84 | 90 | 91 | Up To 50% Off 92 | 93 | 94 | ); 95 | })} 96 | 97 | 98 | ); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/pages/Men.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import { useDispatch, useSelector } from "react-redux"; 3 | import { useLocation, useSearchParams, useNavigate } from "react-router-dom"; 4 | import { getmens } from "../redux/MenReducer/action"; 5 | import Card from "../Components/Card"; 6 | import Pagination1 from "../Components/Filter/Pagination1"; 7 | import Navbar from "../Components/Home/Navbar"; 8 | import Footer from "../Components/Home/Footer"; 9 | import Menfilter from "../Components/Filter/Menfilter"; 10 | import { RootState } from "../types"; 11 | import { useAppDispatch } from "../redux/hooks"; 12 | 13 | export const Men: React.FC = () => { 14 | const [searchParams] = useSearchParams(); 15 | const location = useLocation(); 16 | const dispatch = useAppDispatch(); 17 | const navigate = useNavigate(); 18 | 19 | const { men, isLoading, isError, total } = useSelector((store: RootState) => store.MenReducer); 20 | 21 | const queryParams = { 22 | params: { 23 | category: searchParams.getAll("category"), 24 | _page: searchParams.get("page"), 25 | _sort: searchParams.get("order") ? "price" : null, 26 | _order: searchParams.get("order"), 27 | }, 28 | }; 29 | 30 | useEffect(() => { 31 | dispatch(getmens(queryParams)); 32 | }, [location.search, dispatch]); 33 | 34 | if (isLoading) { 35 | return ( 36 | <> 37 | 38 |
39 |
40 |
41 |