{"id":26218080,"url":"https://github.com/arnobt78/ecommerce-comfy--reactvite-reduxtoolkit","last_synced_at":"2025-03-12T13:15:27.391Z","repository":{"id":279445130,"uuid":"938830906","full_name":"arnobt78/eCommerce-Comfy--ReactVite-ReduxToolkit","owner":"arnobt78","description":"This project is an eCommerce application built using React-Vite, TailwindCSS, redux-toolkit, redux-thunk, redux-store, redux-form, tanstack/react-query, and DaisyUI. It includes features such as product listings, filtering, pagination, user authentication, dark-light theme, external api call, and more.","archived":false,"fork":false,"pushed_at":"2025-02-25T15:32:27.000Z","size":855,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-25T16:31:46.462Z","etag":null,"topics":["daisyui","dark-theme","ecommerce","ecommerce-website","filtering","netlify-deployment","pagination","product-listing","product-orders","react","react-vite","reactjs","redux-form","redux-store","redux-thunk","redux-toolkit","tailwindcss","tanstack-react-query","tanstack-react-query-devtools","user-authentication"],"latest_commit_sha":null,"homepage":"https://ecommerce-comfy-arnob.netlify.app/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/arnobt78.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-02-25T15:14:15.000Z","updated_at":"2025-02-25T15:34:16.000Z","dependencies_parsed_at":"2025-02-25T16:32:14.682Z","dependency_job_id":"b322005f-bf2d-4a44-98f3-498e3f52939a","html_url":"https://github.com/arnobt78/eCommerce-Comfy--ReactVite-ReduxToolkit","commit_stats":null,"previous_names":["arnobt78/ecommerce-comfy--reactvite-reduxtoolkit"],"tags_count":0,"template":true,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arnobt78%2FeCommerce-Comfy--ReactVite-ReduxToolkit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arnobt78%2FeCommerce-Comfy--ReactVite-ReduxToolkit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arnobt78%2FeCommerce-Comfy--ReactVite-ReduxToolkit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arnobt78%2FeCommerce-Comfy--ReactVite-ReduxToolkit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arnobt78","download_url":"https://codeload.github.com/arnobt78/eCommerce-Comfy--ReactVite-ReduxToolkit/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243222183,"owners_count":20256229,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["daisyui","dark-theme","ecommerce","ecommerce-website","filtering","netlify-deployment","pagination","product-listing","product-orders","react","react-vite","reactjs","redux-form","redux-store","redux-thunk","redux-toolkit","tailwindcss","tanstack-react-query","tanstack-react-query-devtools","user-authentication"],"created_at":"2025-03-12T13:15:26.313Z","updated_at":"2025-03-12T13:15:27.348Z","avatar_url":"https://github.com/arnobt78.png","language":"JavaScript","readme":"## eCommerce-Comfy - ReactVite-ReduxToolkit\n\n\u003cimg width=\"1125\" alt=\"Screenshot 2025-02-25 at 16 26 43\" src=\"https://github.com/user-attachments/assets/9cecb6cc-6dd5-4944-ae68-d7b1ec53d67a\" /\u003e\u003cimg width=\"1101\" alt=\"Screenshot 2025-02-25 at 16 26 56\" src=\"https://github.com/user-attachments/assets/c7ed2baa-9b44-445e-b5d0-a574b5adf7d1\" /\u003e\u003cimg width=\"1101\" alt=\"Screenshot 2025-02-25 at 16 27 16\" src=\"https://github.com/user-attachments/assets/0f6b9586-f959-46fc-b775-4264513a8676\" /\u003e\u003cimg width=\"1105\" alt=\"Screenshot 2025-02-25 at 16 28 17\" src=\"https://github.com/user-attachments/assets/78dc6803-3afa-46d4-9602-80d3d4f4430d\" /\u003e\u003cimg width=\"1101\" alt=\"Screenshot 2025-02-25 at 16 28 37\" src=\"https://github.com/user-attachments/assets/0414051f-2d85-4c38-97be-1b6ba231a04d\" /\u003e\u003cimg width=\"1114\" alt=\"Screenshot 2025-02-25 at 16 29 21\" src=\"https://github.com/user-attachments/assets/96f92595-a8aa-4b53-83ad-26f535bc7fd1\" /\u003e\u003cimg width=\"403\" alt=\"Screenshot 2025-02-25 at 16 29 30\" src=\"https://github.com/user-attachments/assets/5b346957-d97d-49aa-92dd-4f2c9afd46b0\" /\u003e\u003cimg width=\"1106\" alt=\"Screenshot 2025-02-25 at 16 29 54\" src=\"https://github.com/user-attachments/assets/1cca2809-225f-4226-8ce2-df1d2f20ece7\" /\u003e\u003cimg width=\"1105\" alt=\"Screenshot 2025-02-25 at 16 30 23\" src=\"https://github.com/user-attachments/assets/ca100a98-b9a0-4b30-b4b2-45ed8eadaec7\" /\u003e\n\nThis project is an eCommerce application built using React-Vite, TailwindCSS, redux-toolkit, redux-thunk, redux-store, redux-form, tanstack/react-query, and DaisyUI. It includes features such as product listings, filtering, pagination, user authentication, dark-light theme, external api call, and more.\n\n**Online Live:** https://ecommerce-comfy-arnob.netlify.app/\n\n## Useful Project Resources\n\n- [API DOCS](https://documenter.getpostman.com/view/18152321/2s9Xy5KpTi)\n\n## Project Structure\n\n```\neCommerce-Comfy\n├── public\n│   ├── favicon.ico\n│   ├── index.html\n│   └── robots.txt\n├── src\n│   ├── assets\n│   │   ├── hero1.webp\n│   │   ├── hero2.webp\n│   │   ├── hero3.webp\n│   │   └── hero4.webp\n│   ├── components\n│   │   ├── CartItem.jsx\n│   │   ├── CartItemsList.jsx\n│   │   ├── CartTotals.jsx\n│   │   ├── CheckoutForm.jsx\n│   │   ├── ComplexPaginationContainer.jsx\n│   │   ├── ErrorElement.jsx\n│   │   ├── FeaturedProducts.jsx\n│   │   ├── Filters.jsx\n│   │   ├── FormCheckbox.jsx\n│   │   ├── FormInput.jsx\n│   │   ├── FormRange.jsx\n│   │   ├── FormSelect.jsx\n│   │   ├── Header.jsx\n│   │   ├── Hero.jsx\n│   │   ├── Loading.jsx\n│   │   ├── NavLinks.jsx\n│   │   ├── Navbar.jsx\n│   │   ├── OrdersList.jsx\n│   │   ├── PaginationContainer.jsx\n│   │   ├── ProductsContainer.jsx\n│   │   ├── ProductsGrid.jsx\n│   │   ├── ProductsList.jsx\n│   │   ├── SectionTitle.jsx\n│   │   ├── SubmitBtn.jsx\n│   │   └── index.js\n│   ├── features\n│   │   ├── cart\n│   │   │   └── cartSlice.js\n│   │   └── user\n│   │       └── userSlice.js\n│   ├── pages\n│   │   ├── About.jsx\n│   │   ├── Cart.jsx\n│   │   ├── Checkout.jsx\n│   │   ├── Error.jsx\n│   │   ├── HomeLayout.jsx\n│   │   ├── Landing.jsx\n│   │   ├── Login.jsx\n│   │   ├── Orders.jsx\n│   │   ├── Products.jsx\n│   │   ├── Register.jsx\n│   │   ├── SingleProduct.jsx\n│   │   └── index.js\n│   ├── store.js\n│   ├── utils\n│   │   ├── index.js\n│   │   └── customFetch.js\n│   ├── App.jsx\n│   ├── index.css\n│   ├── main.jsx\n│   └── tailwind.config.cjs\n├── .gitignore\n├── package.json\n├── README.md\n└── vite.config.js\n```\n\n## Install and Setup\n\n```sh\nnpm install\n```\n\n```sh\nnpm run dev\n```\n\n## Project Details and Steps by Steps\n\n## IMPORTANT !!!\n\nTry to work on one challenge at a time and observe the solution. This approach will make it easier later to identify any potential bugs.\n\n## Challenge (1) - Setup\n\n- create vite project with tailwind\n\n## Solution (1) - Setup Vite and Tailwind\n\n[Tailwind Docs](https://tailwindcss.com/docs/guides/vite)\n\n- setup vite project\n\n```sh\nnpm create vite@latest comfy-store -- --template react\ncd comfy-store\n```\n\n- add tailwind\n\n```sh\nnpm install -D tailwindcss postcss autoprefixer\nnpx tailwindcss init -p\n```\n\n- rename to tailwind.config.cjs\n- add following content\n\ntailwind.config.cjs\n\n```js\n/** @type {import('tailwindcss').Config} */\nexport default {\n  content: [\"./index.html\", \"./src/**/*.{js,ts,jsx,tsx}\"],\n  theme: {\n    extend: {},\n  },\n  plugins: [],\n};\n```\n\n- remove App.css\n- delete contents of index.css\n- delete contents of App.jsx\n- change title\n\n```js\nconst App = () =\u003e {\n  return \u003cdiv\u003eApp\u003c/div\u003e;\n};\nexport default App;\n```\n\n- Add the Tailwind directives to your CSS\n\nindex.css\n\n```css\n@tailwind base;\n@tailwind components;\n@tailwind utilities;\n```\n\nTailwind directives are instructions that decide how Tailwind CSS creates the styles for your website. They control the global styles, component styles, and utility classes.\n\n- start the project \"npm run dev\"\n- setup first tailwind classes in App.jsx\n- remove StrictMode\n\n  App.jsx\n\n```js\nconst App = () =\u003e {\n  return \u003ch1 className=\"text-7xl font-bold underline\"\u003eTailwind project\u003c/h1\u003e;\n};\nexport default App;\n```\n\n## Assets\n\n- get project assets\n\n## Challenge (2) - Setup DaisyUI\n\n- [DaisyUI](https://daisyui.com/)\n\n- add and configure daisyui to our project\n- add TailwindCSS Typography plugin\n\n## Solution (2) - Setup DaisyUI\n\n[DaisyUI](https://daisyui.com/)\n\n```sh\nnpm i  -D daisyui@latest @tailwindcss/typography\n```\n\ntailwind.config.js\n\n```js\n{\n plugins: [require('@tailwindcss/typography'), require('daisyui')],\n}\n```\n\n## Install All Libraries\n\n```sh\nnpm i axios@1.4.0 dayjs@1.11.9 @reduxjs/toolkit@1.9.5 @tanstack/react-query@4.32.6 @tanstack/react-query-devtools@4.32.6 react-icons@4.10.1 react-redux@8.1.2 react-router-dom@6.14.2 react-toastify@9.1.3\n\n```\n\n## Challenge (3) - Create All Pages\n\n- create pages directory\n- create all pages and export from index.js\n- About, Cart, Checkout, Error,\n  HomeLayout, Landing, Login, Orders,\n  Products, Register, SingleProduct\n- import in app.jsx\n\n## Solution (3) - Create All Pages\n\npages/About.jsx\n\n```js\nconst About = () =\u003e {\n  return \u003ch1 className=\"text-4xl\"\u003eAbout\u003c/h1\u003e;\n};\nexport default About;\n```\n\npages/index.js\n\n```js\nexport { default as HomeLayout } from \"./HomeLayout\";\nexport { default as Landing } from \"./Landing\";\nexport { default as SingleProduct } from \"./SingleProduct\";\nexport { default as Products } from \"./Products\";\nexport { default as Cart } from \"./Cart\";\nexport { default as Error } from \"./Error\";\nexport { default as About } from \"./About\";\nexport { default as Login } from \"./Login\";\nexport { default as Register } from \"./Register\";\nexport { default as Checkout } from \"./Checkout\";\nexport { default as Orders } from \"./Orders\";\n```\n\nApp.jsx\n\n```js\nimport {\n  HomeLayout,\n  Landing,\n  Error,\n  Products,\n  SingleProduct,\n  Cart,\n  About,\n  Register,\n  Login,\n  Checkout,\n  Orders,\n} from \"./pages\";\n```\n\n## Challenge (4) - React Router\n\n- configure react router\n- setup initial route structure\n  hint : look for nested UI (basically navbar)\n\n### App.jsx\n\n1. Import Dependencies:\n\n   - Import necessary modules from the 'react-router-dom' library.\n\n2. Create Router Configuration:\n\n   - Use the `createBrowserRouter` function to set up a router configuration.\n   - Define an array of route objects, each representing a different route in your application.\n   - Configure routes for different paths, including components like `HomeLayout`, `Landing`, `Products`, etc.\n\n3. Create Router Instance:\n\n   - Create a router instance using the `createBrowserRouter` function and pass in the defined route configuration.\n\n4. Define App Component:\n\n   - Create a functional component named `App`.\n   - Return a `RouterProvider` component and pass in the created router instance as a prop.\n\n5. Export App Component:\n   - Export the `App` component as the default export of the module.\n\n## Solution (4) - React Router\n\nApp.jsx\n\n```js\nimport { RouterProvider, createBrowserRouter } from \"react-router-dom\";\n\nconst router = createBrowserRouter([\n  {\n    path: \"/\",\n    element: \u003cHomeLayout /\u003e,\n    errorElement: \u003cError /\u003e,\n    children: [\n      {\n        index: true,\n        element: \u003cLanding /\u003e,\n      },\n      {\n        path: \"products\",\n        element: \u003cProducts /\u003e,\n      },\n      {\n        path: \"products/:id\",\n        element: \u003cSingleProduct /\u003e,\n      },\n      {\n        path: \"cart\",\n        element: \u003cCart /\u003e,\n      },\n      { path: \"about\", element: \u003cAbout /\u003e },\n      {\n        path: \"checkout\",\n        element: \u003cCheckout /\u003e,\n      },\n      {\n        path: \"orders\",\n        element: \u003cOrders /\u003e,\n      },\n    ],\n  },\n  {\n    path: \"/login\",\n    element: \u003cLogin /\u003e,\n    errorElement: \u003cError /\u003e,\n  },\n  {\n    path: \"/register\",\n    element: \u003cRegister /\u003e,\n    errorElement: \u003cError /\u003e,\n  },\n]);\n\nconst App = () =\u003e {\n  return \u003cRouterProvider router={router} /\u003e;\n};\nexport default App;\n```\n\nHomeLayout.jsx\n\n```js\nimport { Outlet } from \"react-router-dom\";\n\nconst HomeLayout = () =\u003e {\n  return (\n    \u003c\u003e\n      \u003cnav\u003e\n        \u003cspan className=\"text-4xl text-primary\"\u003eComfy\u003c/span\u003e\n      \u003c/nav\u003e\n      \u003cOutlet /\u003e\n    \u003c/\u003e\n  );\n};\nexport default HomeLayout;\n```\n\n## Challenge (5) - Error Page\n\n- complete error page\n- create two returns\n- first for 404 errors\n- second for all other errors\n- log the error\n- add option to navigate home\n\n### Error.jsx\n\n1. Import Dependencies:\n\n   - Import the necessary modules `useRouteError` and `Link` from the 'react-router-dom' library.\n\n2. Create Error Component:\n\n   - Define a functional component named `Error`.\n   - Inside the component, use the `useRouteError` hook to get information about the route error.\n   - Check the status of the error using `error.status`.\n\n3. Conditional Rendering for 404 Error:\n\n   - If the error status is 404, render a `main` element with a grid layout and centered content.\n   - Display a large \"404\" text, a title \"Page not found,\" and a description.\n   - Include a link back to the home page using the `Link` component.\n\n4. Conditional Rendering for Other Errors:\n\n   - If the error status is not 404, render a `main` element with a grid layout and centered content.\n   - Display a text message indicating that there was an error.\n\n5. Export Error Component:\n   - Export the `Error` component as the default export of the module.\n\n## Solution (5) - Error Page\n\nError.jsx\n\n```js\nimport { useRouteError, Link } from \"react-router-dom\";\nconst Error = () =\u003e {\n  const error = useRouteError();\n  console.log(error);\n  if (error.status === 404)\n    return (\n      \u003cmain className=\"grid min-h-[100vh] place-items-center px-8 \"\u003e\n        \u003cdiv className=\"text-center\"\u003e\n          \u003cp className=\"text-9xl font-semibold text-primary\"\u003e404\u003c/p\u003e\n          \u003ch1 className=\"mt-4 text-3xl font-bold tracking-tight sm:text-5xl\"\u003e\n            Page not found\n          \u003c/h1\u003e\n          \u003cp className=\"mt-6 text-lg leading-7 \"\u003e\n            Sorry, we couldn’t find the page you’re looking for.\n          \u003c/p\u003e\n          \u003cdiv className=\"mt-10 \"\u003e\n            \u003cLink to=\"/\" className=\"btn btn-secondary\"\u003e\n              Go back home\n            \u003c/Link\u003e\n          \u003c/div\u003e\n        \u003c/div\u003e\n      \u003c/main\u003e\n    );\n\n  return (\n    \u003cmain className=\"grid min-h-[100vh] place-items-center px-8 \"\u003e\n      \u003ch4 className=\"text-center font-bold text-4xl\"\u003ethere was an error... \u003c/h4\u003e\n    \u003c/main\u003e\n  );\n};\nexport default Error;\n```\n\n## Challenge (6) - Input Field Component\n\n- create components folder with index.js\n- in daisyui\n- find Text input component\n- more specifically \"With form-control and labels\"\n- set it as component (in my case FormInput.jsx)\n- decide on props\n- export from index.js\n- test in login\n\n### FormInput.jsx\n\n1. Create FormInput Component:\n\n   - Define a functional component named `FormInput`.\n   - The component accepts the following props: `label`, `name`, `type`, and `defaultValue`.\n\n2. FormInput Structure:\n\n   - Inside the component, return a `div` element with the class `form-control`, which provides styling for form inputs.\n   - Within this `div`, create a `label` element with the class `label`.\n   - Inside the `label`, use the `label-text` class to display the capitalized label text provided through the props.\n\n3. Input Element:\n\n   - After the `label`, create an `input` element.\n   - Set the `type` attribute of the `input` element to the value provided through the `type` prop.\n   - Set the `name` attribute of the `input` element to the value provided through the `name` prop.\n   - Use the `defaultValue` prop to set the initial value of the input element.\n   - Apply the `input` and `input-bordered` classes to the `input` element for styling.\n\n4. Export FormInput Component:\n   - Export the `FormInput` component as the default export of the module.\n\n## Solution (6) - Input Field Component\n\ncomponents/index.js\n\n```js\nexport { default as FormInput } from \"./FormInput\";\n```\n\nFormInput.jsx\n\n```js\nconst FormInput = ({ label, name, type, defaultValue }) =\u003e {\n  return (\n    \u003cdiv className=\"form-control \"\u003e\n      \u003clabel className=\"label\"\u003e\n        \u003cspan className=\"label-text capitalize\"\u003e{label}\u003c/span\u003e\n      \u003c/label\u003e\n      \u003cinput\n        type={type}\n        name={name}\n        defaultValue={defaultValue}\n        className=\"input input-bordered \"\n      /\u003e\n    \u003c/div\u003e\n  );\n};\nexport default FormInput;\n```\n\n## Challenge (7) - Login Page Structure\n\n- setup structure for login page (use complete project as reference)\n- check for loading state and disable submit button\n- setup submit button in a separate component\n- add loading spinner\n\n### SubmitBtn.jsx\n\n- Import Dependencies:\n\n  - Import `useNavigation` from `'react-router-dom'`.\n\n- Create the `SubmitBtn` Component:\n\n  - Define a functional component named `SubmitBtn`.\n  - Accept a prop `text`.\n\n  - Inside the component, use the `useNavigation()` hook to access navigation state.\n  - Determine whether the form is submitting by checking if `navigation.state` is equal to `'submitting'`.\n\n  - Return a `button` element with the following attributes:\n\n    - Type set to `'submit'`.\n    - Class set to `'btn btn-primary btn-block'`.\n    - Disabled attribute set to the value of `isSubmitting`.\n\n    - Inside the `button` element, use a conditional rendering:\n      - If `isSubmitting` is true:\n        - Render a `span` element with class `'loading loading-spinner'`.\n        - Render the text `'sending...'`.\n      - If `isSubmitting` is false:\n        - Render the `text` prop if provided, otherwise render `'submit'`.\n\n### Login.jsx\n\n- Import Dependencies:\n\n  - Import `FormInput` and `SubmitBtn` components from the `'../components'` directory.\n  - Import `Form` and `Link` from `'react-router-dom'`.\n\n- Create the `Login` Component:\n\n  - Define a functional component named `Login`.\n\n  - Return a `section` element with class `'h-screen grid place-items-center'`.\n\n    - Inside the `section` element, create a `Form` element with the following attributes:\n\n      - `method` set to `'post'`.\n      - Class set to `'card w-96 p-8 bg-base-100 shadow-lg flex flex-col gap-y-4'`.\n\n      - Inside the `Form` element, create an `h4` element with class `'text-center text-3xl font-bold'` containing the text `'Login'`.\n\n      - Use the `FormInput` component twice:\n\n        - First, for an email input with type `'email'`, label `'email'`, name `'identifier'`, and defaultValue `'test@test.com'`.\n        - Second, for a password input with type `'password'`, label `'password'`, name `'password'`, and defaultValue `'secret'`.\n\n      - Create a `div` element with class `'mt-4'`.\n\n        - Inside the `div` element, use the `SubmitBtn` component with a prop `text` set to `'login'`.\n\n      - Create a `button` element with the following attributes:\n\n        - Type set to `'button'`.\n        - Class set to `'btn btn-secondary btn-block'`.\n        - Text content set to `'guest user'`.\n\n      - Create a `p` element with class `'text-center'`.\n\n        - Inside the `p` element, display the text `'Not a member yet?'`.\n\n        - Create a `Link` element with the following attributes:\n          - `to` set to `'/register'`.\n          - Class set to `'ml-2 link link-hover link-primary capitalize'`.\n          - Text content set to `'register'`.\n\n## Solution (7) - Login Page Structure\n\nLogin.jsx\n\n```js\nimport { FormInput, SubmitBtn } from \"../components\";\nimport { Form, Link } from \"react-router-dom\";\n\nconst Login = () =\u003e {\n  return (\n    \u003csection className=\"h-screen grid place-items-center\"\u003e\n      \u003cForm\n        method=\"post\"\n        className=\"card w-96 p-8 bg-base-100 shadow-lg flex flex-col gap-y-4\"\n      \u003e\n        \u003ch4 className=\"text-center text-3xl font-bold\"\u003eLogin\u003c/h4\u003e\n        \u003cFormInput\n          type=\"email\"\n          label=\"email\"\n          name=\"identifier\"\n          defaultValue=\"test@test.com\"\n        /\u003e\n        \u003cFormInput\n          type=\"password\"\n          label=\"password\"\n          name=\"password\"\n          defaultValue=\"secret\"\n        /\u003e\n        \u003cdiv className=\"mt-4\"\u003e\n          \u003cSubmitBtn text=\"login\" /\u003e\n        \u003c/div\u003e\n        \u003cbutton type=\"button\" className=\"btn btn-secondary btn-block\"\u003e\n          guest user\n        \u003c/button\u003e\n        \u003cp className=\"text-center\"\u003e\n          Not a member yet?\n          \u003cLink\n            to=\"/register\"\n            className=\"ml-2 link link-hover link-primary capitalize\"\n          \u003e\n            register\n          \u003c/Link\u003e\n        \u003c/p\u003e\n      \u003c/Form\u003e\n    \u003c/section\u003e\n  );\n};\nexport default Login;\n```\n\nSubmitBtn.jsx\n\n```js\nimport { useNavigation } from \"react-router-dom\";\nconst SubmitBtn = ({ text }) =\u003e {\n  const navigation = useNavigation();\n  const isSubmitting = navigation.state === \"submitting\";\n  return (\n    \u003cbutton\n      type=\"submit\"\n      className=\"btn btn-primary btn-block\"\n      disabled={isSubmitting}\n    \u003e\n      {isSubmitting ? (\n        \u003c\u003e\n          \u003cspan className=\"loading loading-spinner\"\u003e\u003c/span\u003e\n          sending...\n        \u003c/\u003e\n      ) : (\n        text || \"submit\"\n      )}\n    \u003c/button\u003e\n  );\n};\nexport default SubmitBtn;\n```\n\n## Challenge (8) - Register Page Structure\n\n- setup structure for register page (use complete project as reference)\n\n### Register.jsx\n\n- Import Dependencies:\n\n  - Import `FormInput` and `SubmitBtn` components from the `'../components'` directory.\n  - Import `Form` and `Link` from `'react-router-dom'`.\n\n- Create the `Register` Component:\n\n  - Define a functional component named `Register`.\n\n  - Return a `section` element with class `'h-screen grid place-items-center'`.\n\n    - Inside the `section` element, create a `Form` element with the following attributes:\n\n      - `method` set to `'POST'`.\n      - Class set to `'card w-96 p-8 bg-base-100 shadow-lg flex flex-col gap-y-4'`.\n\n      - Inside the `Form` element, create an `h4` element with class `'text-center text-3xl font-bold'` containing the text `'Register'`.\n\n      - Use the `FormInput` component three times:\n\n        - First, for a text input with type `'text'`, label `'username'`, and name `'username'`.\n        - Second, for an email input with type `'email'`, label `'email'`, and name `'email'`.\n        - Third, for a password input with type `'password'`, label `'password'`, and name `'password'`.\n\n      - Create a `div` element with class `'mt-4'`.\n\n        - Inside the `div` element, use the `SubmitBtn` component with a prop `text` set to `'register'`.\n\n      - Create a `p` element with class `'text-center'`.\n\n        - Inside the `p` element, display the text `'Already a member?'`.\n\n        - Create a `Link` element with the following attributes:\n          - `to` set to `'/login'`.\n          - Class set to `'ml-2 link link-hover link-primary capitalize'`.\n          - Text content set to `'login'`.\n\n## Solution (8) - Register Page Structure\n\n```js\nimport { FormInput, SubmitBtn } from \"../components\";\nimport { Form, Link } from \"react-router-dom\";\n\nconst Register = () =\u003e {\n  return (\n    \u003csection className=\"h-screen grid place-items-center\"\u003e\n      \u003cForm\n        method=\"POST\"\n        className=\"card w-96 p-8 bg-base-100 shadow-lg flex flex-col gap-y-4\"\n      \u003e\n        \u003ch4 className=\"text-center text-3xl font-bold\"\u003eRegister\u003c/h4\u003e\n        \u003cFormInput type=\"text\" label=\"username\" name=\"username\" /\u003e\n        \u003cFormInput type=\"email\" label=\"email\" name=\"email\" /\u003e\n        \u003cFormInput type=\"password\" label=\"password\" name=\"password\" /\u003e\n        \u003cdiv className=\"mt-4\"\u003e\n          \u003cSubmitBtn text=\"register\" /\u003e\n        \u003c/div\u003e\n\n        \u003cp className=\"text-center\"\u003e\n          Already a member?\n          \u003cLink\n            to=\"/login\"\n            className=\"ml-2 link link-hover link-primary capitalize\"\n          \u003e\n            login\n          \u003c/Link\u003e\n        \u003c/p\u003e\n      \u003c/Form\u003e\n    \u003c/section\u003e\n  );\n};\nexport default Register;\n```\n\n## Challenge (9) - Custom Class\n\n- create custom class\n- align content\n- add to HomeLayout Outlet component\n\n## Solution (9) - Custom Class\n\nindex.css\n\n```css\n@layer components {\n  .align-element {\n    @apply mx-auto max-w-6xl px-8;\n  }\n}\n```\n\n```js\n\u003csection className=\"align-element py-20\"\u003e\n  \u003cOutlet /\u003e\n\u003c/section\u003e\n```\n\n## Challenge (10) - Header Component\n\n- setup and render header component in HomeLayout\n- add two links - Login and Register\n\n### Header.jsx\n\n- Import Dependencies:\n\n  - Import `Link` from `'react-router-dom'`.\n\n- Create the `Header` Component:\n\n  - Define a functional component named `Header`.\n\n  - Return a `header` element with classes `'bg-neutral py-2 text-neutral-content'`.\n\n    - Inside the `header` element, create a `div` element with classes `'align-element flex justify-center sm:justify-end'`.\n\n      - Inside the `div` element, create another `div` element with classes `'flex gap-x-6 justify-center items-center'`.\n\n        - Use the `Link` component twice:\n\n          - First, create a `Link` to `'/login'` with the following attributes:\n\n            - Class set to `'link link-hover text-xs sm:text-sm'`.\n            - Text content set to `'Sign in / Guest'`.\n\n          - Second, create a `Link` to `'/register'` with the following attributes:\n            - Class set to `'link link-hover text-xs sm:text-sm'`.\n            - Text content set to `'Create an Account'`.\n\n## Solution (10) - Header Component\n\n```js\nimport { Link } from \"react-router-dom\";\n\nconst Header = () =\u003e {\n  return (\n    \u003cheader className=\" bg-neutral py-2 text-neutral-content \"\u003e\n      \u003cdiv className=\"align-element flex justify-center sm:justify-end \"\u003e\n        {/* USER */}\n        {/* LINKS */}\n        \u003cdiv className=\"flex gap-x-6 justify-center items-center\"\u003e\n          \u003cLink to=\"/login\" className=\"link link-hover text-xs sm:text-sm\"\u003e\n            Sign in / Guest\n          \u003c/Link\u003e\n          \u003cLink to=\"/register\" className=\"link link-hover text-xs sm:text-sm\"\u003e\n            Create an Account\n          \u003c/Link\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/header\u003e\n  );\n};\nexport default Header;\n```\n\n## Challenge (11) - Navbar Structure\n\n- create components/Navbar.jsx\n- setup initial structure\n- use Daisy navbar component\n- \"# responsive (dropdown menu on small screen, center menu on large screen)\"\n- import icons from react-icons\n- render in HomeLayout.jsx\n\n### Navbar.jsx\n\n- Import Dependencies:\n\n  - Import icons `BsCart3`, `BsMoonFill`, `BsSunFill`, and `FaBarsStaggered` from their respective packages.\n  - Import `NavLink` from `'react-router-dom'`.\n\n- Create the `Navbar` Component:\n\n  - Define a functional component named `Navbar`.\n\n  - Return a `nav` element with class `'bg-base-200'`.\n\n    - Inside the `nav` element, create a `div` element with class `'navbar align-element '`.\n\n      - Inside the first `div` element, create another `div` element with class `'navbar-start'`.\n\n        - Create a `NavLink` to `'/'` with the following attributes:\n\n          - Class set to `'hidden lg:flex btn btn-primary text-3xl items-center '`.\n\n        - Create a `div` element with class `'dropdown'`.\n\n          - Create a `label` element with `tabIndex={0}` and class `'btn btn-ghost lg:hidden'`.\n\n          - Create a `ul` element with `tabIndex={0}` and class `'menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-200 rounded-box w-52'`.\n\n      - Inside the second `div` element, create a `div` element with class `'navbar-center hidden lg:flex'`.\n\n        - Create a `ul` element with class `'menu menu-horizontal '`.\n\n      - Inside the third `div` element, create another `div` element with class `'navbar-end'`.\n\n        - Create a `NavLink` to `'cart'` with the following attributes:\n\n          - Class set to `'btn btn-ghost btn-circle btn-md ml-4'`.\n\n          - Inside the `NavLink`, create a `div` element with class `'indicator'`.\n\n            - Add the `BsCart3` icon component with class `'h-6 w-6'`.\n\n            - Create a `span` element with classes `'badge badge-sm badge-primary indicator-item'` and text content `'8'`.\n\n## Solution (11) - Navbar Structure\n\n```js\nimport { BsCart3, BsMoonFill, BsSunFill } from \"react-icons/bs\";\nimport { FaBarsStaggered } from \"react-icons/fa6\";\nimport { NavLink } from \"react-router-dom\";\n\nconst Navbar = () =\u003e {\n  return (\n    \u003cnav className=\"bg-base-200\"\u003e\n      \u003cdiv className=\"navbar align-element \"\u003e\n        \u003cdiv className=\"navbar-start\"\u003e\n          {/* Title */}\n          \u003cNavLink\n            to=\"/\"\n            className=\"hidden lg:flex btn btn-primary text-3xl items-center \"\n          \u003e\n            C\n          \u003c/NavLink\u003e\n          {/* DROPDOWN */}\n          \u003cdiv className=\"dropdown\"\u003e\n            \u003clabel tabIndex={0} className=\"btn btn-ghost lg:hidden\"\u003e\n              \u003cFaBarsStaggered className=\"h-6 w-6\" /\u003e\n            \u003c/label\u003e\n            \u003cul\n              tabIndex={0}\n              className=\"menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-200 rounded-box w-52\"\n            \u003e\n              nav links\n            \u003c/ul\u003e\n          \u003c/div\u003e\n        \u003c/div\u003e\n        \u003cdiv className=\"navbar-center hidden lg:flex\"\u003e\n          \u003cul className=\"menu menu-horizontal \"\u003enav links\u003c/ul\u003e\n        \u003c/div\u003e\n        \u003cdiv className=\"navbar-end\"\u003e\n          {/* THEME ICONS */}\n          {/* CART LINK*/}\n          \u003cNavLink to=\"cart\" className=\"btn btn-ghost btn-circle btn-md ml-4\"\u003e\n            \u003cdiv className=\"indicator\"\u003e\n              \u003cBsCart3 className=\"h-6 w-6\" /\u003e\n              \u003cspan className=\"badge badge-sm badge-primary indicator-item\"\u003e\n                8\n              \u003c/span\u003e\n            \u003c/div\u003e\n          \u003c/NavLink\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/nav\u003e\n  );\n};\nexport default Navbar;\n```\n\n## Challenge (12) - NavLinks\n\n- create NavLinks component\n- setup an array of links\n- iterate over and setup return\n\n### Navbar.jsx\n\n- Import Dependencies:\n\n  - Import `NavLink` from `'react-router-dom'`.\n\n- Create the NavLinks Component:\n\n  - Define a functional component named `NavLinks`.\n\n  - Return a fragment (`\u003c\u003e...\u003c/\u003e`) to contain the list of navigation links.\n\n    - Use the `.map()` function to iterate over the `links` array.\n\n      - For each `link` object, extract the `id`, `url`, and `text`.\n\n      - Create an `li` element with a `key` attribute set to `id`.\n\n        - Inside the `li` element, create a `NavLink` with the following attributes:\n\n          - Class set to `'capitalize'`.\n\n          - `to` attribute set to the `url`.\n\n          - Text content set to the `text`.\n\n## Solution (12) - NavLinks\n\nNavLinks.jsx\n\n```js\nconst links = [\n  { id: 1, url: \"/\", text: \"home\" },\n  { id: 2, url: \"about\", text: \"about\" },\n  { id: 3, url: \"products\", text: \"products\" },\n  { id: 4, url: \"cart\", text: \"cart\" },\n  { id: 5, url: \"checkout\", text: \"checkout\" },\n  { id: 6, url: \"orders\", text: \"orders\" },\n];\nimport { NavLink } from \"react-router-dom\";\n\nconst NavLinks = () =\u003e {\n  return (\n    \u003c\u003e\n      {links.map((link) =\u003e {\n        const { id, url, text } = link;\n        return (\n          \u003cli key={id}\u003e\n            \u003cNavLink className=\"capitalize\" to={url}\u003e\n              {text}\n            \u003c/NavLink\u003e\n          \u003c/li\u003e\n        );\n      })}\n    \u003c/\u003e\n  );\n};\nexport default NavLinks;\n```\n\n## Challenge (13) - Toggle Component\n\n- add daisyui swap component\n\n## Solution (13) - Toggle Component\n\n```js\nimport { useState } from \"react\";\n\nconst [theme, setTheme] = useState(false);\n\nconst handleTheme = () =\u003e {\n  setTheme(!theme);\n};\n\u003cdiv className=\"navbar-end\"\u003e\n  \u003clabel className=\"swap swap-rotate \"\u003e\n    {/* this hidden checkbox controls the state */}\n    \u003cinput type=\"checkbox\" onChange={handleTheme} /\u003e\n\n    {/* sun icon */}\n    \u003cBsSunFill className=\"swap-on h-4 w-4\" /\u003e\n\n    {/* moon icon */}\n    \u003cBsMoonFill className=\"swap-off h-4 w-4\" /\u003e\n  \u003c/label\u003e\n\u003c/div\u003e;\n```\n\n## Challenge (14) - Set Themes\n\n- add few themes from daisyui\n- test in index.html\n\n## Solution (14) - Set Themes\n\ntailwind.config.cjs\n\n```js\n{\n...\n  daisyui: {\n    themes: ['winter', 'dracula'],\n  },\n}\n```\n\n```html\n\u003chtml lang=\"en\" data-theme=\"winter\"\u003e\u003c/html\u003e\n```\n\n## Challenge (15) - Change Theme\n\n- change theme with toggle component\n\n### Navbar.jsx\n\n- Import Dependencies:\n\n  - Import `useEffect` and `useState` from `'react'`.\n\n- Theme Configuration:\n\n  - Define a `themes` object with theme names as keys.\n\n- Local Storage Theme Retrieval:\n\n  - Create a function named `getThemeFromLocalStorage`.\n    - Return the value of the `'theme'` key from `localStorage` or the default theme `'winter'`.\n\n- Logic:\n\n  - Create a state variable `theme` using the `useState` hook and initialize it with the result of `getThemeFromLocalStorage()`.\n  - Define a function `handleTheme` that toggles between the `'winter'` and `'dracula'` themes based on the current theme.\n  - Use the `useEffect` hook to apply the selected theme to the `document.documentElement` and store the theme value in `localStorage`.\n  - ... (rest of the component implementation)\n\n## Solution (15) - Change Theme\n\n```js\nimport { useEffect, useState } from 'react';\n\nconst themes = {\n  winter: 'winter',\n  dracula: 'dracula',\n};\n\nconst getThemeFromLocalStorage = () =\u003e {\n  return localStorage.getItem('theme') || themes.winter;\n};\n\nconst Navbar = () =\u003e {\n  const [theme, setTheme] = useState(getThemeFromLocalStorage());\n\n  const handleTheme = () =\u003e {\n    const { winter, dracula } = themes;\n    const newTheme = theme === winter ? dracula : winter;\n    setTheme(newTheme);\n  };\n\n  useEffect(() =\u003e {\n    document.documentElement.setAttribute('data-theme', theme);\n    localStorage.setItem('theme', theme);\n  }, [theme]);\n\n  ...\n};\n```\n\n## Challenge (16) - About Page\n\n- setup about page\n\n### About.jsx\n\n- About Component:\n  - Define the `About` component.\n    - Return a fragment containing the following elements:\n      - A `div` with classes `flex`, `flex-wrap`, `gap-2`, `sm:gap-x-6`, `items-center`, and `justify-center`.\n        - Inside the `div`, an `h1` with classes `text-4xl`, `font-bold`, `leading-none`, and `tracking-tight`, with the text \"We love\".\n        - Inside the `div`, a `div` with classes `stats`, `bg-primary`, and `shadow`.\n          - Inside the nested `div`, another `div` with class `stat`.\n            - Inside this `div`, a `div` with classes `stat-title`, `text-primary-content`, `text-4xl`, `font-bold`, and `tracking-widest`, containing the text \"comfy\".\n      - A `p` element with classes `mt-6`, `text-lg`, `leading-8`, `max-w-2xl`, and `mx-auto`, containing sample Lorem ipsum text.\n\n## Solution (16) - About Page\n\n```js\nconst About = () =\u003e {\n  return (\n    \u003c\u003e\n      \u003cdiv className=\"flex flex-wrap gap-2 sm:gap-x-6 items-center justify-center\"\u003e\n        \u003ch1 className=\"text-4xl font-bold leading-none tracking-tight sm:text-6xl \"\u003e\n          We love\n        \u003c/h1\u003e\n        \u003cdiv className=\"stats bg-primary shadow\"\u003e\n          \u003cdiv className=\"stat\"\u003e\n            \u003cdiv className=\"stat-title text-primary-content text-4xl font-bold tracking-widest\"\u003e\n              comfy\n            \u003c/div\u003e\n          \u003c/div\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n      \u003cp className=\"mt-6 text-lg leading-8 max-w-2xl mx-auto\"\u003e\n        Lorem ipsum dolor sit amet consectetur adipisicing elit. Hic veniam\n        odit, officiis eos mollitia alias, doloremque, aspernatur ratione\n        asperiores voluptas labore minus dolores reprehenderit corporis quos.\n        Assumenda molestias harum dignissimos?\n      \u003c/p\u003e\n    \u003c/\u003e\n  );\n};\nexport default About;\n```\n\n## Challenge (17) - Hero Component\n\n- setup hero component in landing page\n\n### Hero.jsx\n\n- Import Dependencies:\n\n  - Import `Link` from `'react-router-dom'`.\n\n- Hero Component:\n  - Define the `Hero` component.\n    - Create an array `carouselImages` containing imported image paths.\n    - Return a `div` with classes `grid`, `grid-cols-1`, `lg:grid-cols-2`, `gap-24`, and `items-center`.\n      - Inside this `div`, another `div`.\n        - Inside this `div`, an `h1` with classes `max-w-2xl`, `text-4xl`, `font-bold`, and `tracking-tight`, containing the text \"We’re changing the way people shop.\"\n        - Next, a `p` element with classes `mt-8`, `max-w-xl`, `text-lg`, and `leading-8`, containing sample Lorem ipsum text.\n        - Following that, a `div` with class `mt-10`.\n          - Inside the `div`, a `Link` component with props `to='products'` and classes `btn` and `btn-primary`, containing the text \"Our Products\".\n      - Another `div` with classes `hidden`, `h-[28rem]`, `lg:carousel`, `carousel-center`, `p-4`, `space-x-4`, `bg-neutral`, and `rounded-box`.\n        - Inside this `div`, a JavaScript map function iterating over `carouselImages`.\n          - For each image, a `div` with class `carousel-item`.\n            - Inside the `div`, an `img` element with attributes `src` set to the image path and classes `rounded-box`, `h-full`, `w-80`, and `object-cover`.\n\n## Challenge (17) - Hero Component\n\nHero.jsx\n\n```js\nimport { Link } from \"react-router-dom\";\n\nimport hero1 from \"../assets/hero1.webp\";\nimport hero2 from \"../assets/hero2.webp\";\nimport hero3 from \"../assets/hero3.webp\";\nimport hero4 from \"../assets/hero4.webp\";\n\nconst carouselImages = [hero1, hero2, hero3, hero4];\nconst Hero = () =\u003e {\n  return (\n    \u003cdiv className=\" grid grid-cols-1 lg:grid-cols-2 gap-24 items-center\"\u003e\n      \u003cdiv\u003e\n        \u003ch1 className=\"max-w-2xl text-4xl font-bold tracking-tight  sm:text-6xl \"\u003e\n          We’re changing the way people shop.\n        \u003c/h1\u003e\n\n        \u003cp className=\"mt-8 max-w-xl text-lg leading-8\"\u003e\n          Anim aute id magna aliqua ad ad non deserunt sunt. Qui irure qui lorem\n          cupidatat commodo. Elit sunt amet fugiat veniam occaecat fugiat\n          aliqua. Anim aute id magna aliqua ad ad non deserunt sunt. Qui irure\n          qui lorem cupidatat commodo.\n        \u003c/p\u003e\n        \u003cdiv className=\"mt-10 \"\u003e\n          \u003cLink to=\"products\" className=\"btn btn-primary \"\u003e\n            Our Products\n          \u003c/Link\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv className=\"hidden  h-[28rem] lg:carousel carousel-center   p-4 space-x-4 bg-neutral rounded-box\"\u003e\n        {carouselImages.map((image, index) =\u003e {\n          return (\n            \u003cdiv key={image} className=\"carousel-item\"\u003e\n              \u003cimg\n                src={image}\n                className=\"rounded-box h-full w-80  object-cover\"\n              /\u003e\n            \u003c/div\u003e\n          );\n        })}\n      \u003c/div\u003e\n    \u003c/div\u003e\n  );\n};\nexport default Hero;\n```\n\n## Challenge (18) - Axios Custom Instance\n\n- explore api\n- [API DOCS](https://documenter.getpostman.com/view/18152321/2s9Xy5KpTi)\n- create utils/index.js\n- setup custom axios instance\n- figure out the base url\n- setup thunder client (optional)\n\n## Challenge (18) - Axios Custom Instance\n\n```js\nimport axios from \"axios\";\n\nconst productionUrl = \"https://strapi-store-server.onrender.com/api\";\n\nexport const customFetch = axios.create({\n  baseURL: productionUrl,\n});\n```\n\n## Challenge (19) - Landing Loader\n\n- setup ErrorElement\n- add to Loading Page\n- setup a loader\n- fetch only featured products\n- return products\n\n### ErrorElement.jsx\n\n1. Create ErrorElement Component:\n\n   - Define a functional component named `ErrorElement`.\n\n2. Import Dependencies:\n\n   - Import the `useRouteError` hook from `'react-router-dom'`.\n\n3. Get Route Error:\n\n   - Inside the component, use the `useRouteError` hook to retrieve the error information from the current route.\n\n4. Display Error Message:\n\n   - Return an `h4` element with the classes `font-bold` and `text-4xl`.\n   - Set the content of the `h4` element to \"there was an error...\"\n\n5. Export ErrorElement Component:\n   - Export the `ErrorElement` component as the default export of the module.\n\n## Solution (19) - Landing Loader\n\nErrorElement.jsx\n\n```js\nimport { useRouteError } from \"react-router-dom\";\nconst ErrorElement = () =\u003e {\n  const error = useRouteError();\n  console.log(error);\n\n  return \u003ch4 className=\"font-bold text-4xl\"\u003ethere was an error... \u003c/h4\u003e;\n};\nexport default ErrorElement;\n```\n\nApp.jsx\n\n```js\nimport { ErrorElement } from \"./components\";\n// loaders\nimport { loader as landingLoader } from \"./pages/Landing\";\n// actions\n\nconst router = createBrowserRouter([\n  {\n    path: \"/\",\n    element: \u003cHomeLayout /\u003e,\n    errorElement: \u003cError /\u003e,\n    children: [\n      {\n        index: true,\n        element: \u003cLanding /\u003e,\n        loader: landingLoader,\n        errorElement: ErrorElement,\n      },\n    ],\n  },\n]);\n```\n\nLanding.js\n\n```js\nimport { Hero } from \"../components\";\n\nimport { customFetch } from \"../utils\";\nconst url = \"/products?featured=true\";\n\nexport const loader = async () =\u003e {\n  const response = await customFetch(url);\n  console.log(response);\n  const products = response.data.data;\n  return { products };\n};\n\nconst Landing = () =\u003e {\n  return (\n    \u003c\u003e\n      \u003cHero /\u003e\n    \u003c/\u003e\n  );\n};\nexport default Landing;\n```\n\n## Challenge (20) - Featured Products\n\n- create FeaturedProducts, SectionTitle and ProductsGrid components\n- render SectionTitle and ProductsGrid in FeaturedProducts\n- setup SectionTitle\n- in ProductsGrid access and render products from loader\n\n### SectionTitle.jsx\n\n1. Create SectionTitle Component:\n\n   - Define a functional component named `SectionTitle`.\n\n2. Component Props:\n\n   - The component should accept a prop named `text`.\n\n3. Component Structure:\n\n   - Return a `div` element with the classes `border-b border-base-300 pb-5`.\n   - Inside the `div`, place an `h2` element with the classes `text-3xl`, `font-medium`, `tracking-wider`, and `capitalize`.\n   - Set the content of the `h2` element to the value of the `text` prop.\n\n4. Export SectionTitle Component:\n   - Export the `SectionTitle` component as the default export of the module.\n\n### FeaturedProducts.jsx\n\n1. Import Dependencies:\n\n   - Import `ProductsGrid` from `'./ProductsGrid'`.\n   - Import `SectionTitle` from `'./SectionTitle'`.\n\n2. Create FeaturedProducts Component:\n\n   - Define a functional component named `FeaturedProducts`.\n\n3. Component Structure:\n\n   - Return a `div` element with the class `pt-24`.\n   - Inside the `div`, include a `SectionTitle` component with the prop `text` set to `'featured products'`.\n   - Include a `ProductsGrid` component.\n\n4. Export FeaturedProducts Component:\n   - Export the `FeaturedProducts` component as the default export of the module.\n\n### ProductsGrid.jsx\n\n1. Import Dependencies:\n\n   - Import `Link` and `useLoaderData` from `'react-router-dom'`.\n\n2. Create ProductsGrid Component:\n\n   - Define a functional component named `ProductsGrid`.\n\n3. Component Structure:\n\n   - Inside the component, destructure the `products` data using `useLoaderData`.\n   - Return a `div` element with the classes `pt-12 grid gap-4 md:grid-cols-2 lg:grid-cols-3`.\n   - Use the `.map()` function to iterate through each `product` in the `products` array.\n\n4. Product Card:\n\n   - For each `product`, destructure the attributes such as `title`, `price`, and `image`.\n   - Create a `Link` component that has the following:\n     - `key` attribute set to `product.id`.\n     - `to` attribute set to `/products/${product.id}`.\n     - `className` attribute with classes for styling.\n   - Inside the `Link`, create a `figure` element with the class `px-4 pt-4` to hold the product image.\n   - Within the `figure`, include an `img` element with the `src` attribute set to `image`, `alt` attribute set to `title`, and classes for styling.\n   - Below the `figure`, create a `div` element with the class `card-body items-center text-center`.\n   - Inside the `div`, display the `title` using a `h2` element with classes for styling.\n   - Display the `price` using a `span` element with the class `text-secondary`.\n\n5. Export ProductsGrid Component:\n   - Export the `ProductsGrid` component as the default export of the module.\n\n## Solution (20) - Featured Products\n\nSectionTitle.jsx\n\n```js\nconst SectionTitle = ({ text }) =\u003e {\n  return (\n    \u003cdiv className=\"border-b border-base-300 pb-5\"\u003e\n      \u003ch2 className=\"text-3xl font-medium tracking-wider capitalize\"\u003e{text}\u003c/h2\u003e\n    \u003c/div\u003e\n  );\n};\nexport default SectionTitle;\n```\n\nFeaturedProducts.jsx\n\n```js\nimport ProductsGrid from \"./ProductsGrid\";\nimport SectionTitle from \"./SectionTitle\";\nconst FeaturedProducts = () =\u003e {\n  return (\n    \u003cdiv className=\"pt-24 \"\u003e\n      \u003cSectionTitle text=\"featured products\" /\u003e\n      \u003cProductsGrid /\u003e\n    \u003c/div\u003e\n  );\n};\nexport default FeaturedProducts;\n```\n\nProductsGrid.jsx\n\n```js\nimport { Link, useLoaderData } from \"react-router-dom\";\nconst ProductsGrid = () =\u003e {\n  const { products } = useLoaderData();\n\n  return (\n    \u003cdiv className=\"pt-12 grid gap-4 md:grid-cols-2 lg:grid-cols-3 \"\u003e\n      {products.map((product) =\u003e {\n        const { title, price, image } = product.attributes;\n        const dollarsAmount = price;\n        return (\n          \u003cLink\n            key={product.id}\n            to={`/products/${product.id}`}\n            className=\"card w-full  shadow-xl hover:shadow-2xl transition duration-300 \"\n          \u003e\n            \u003cfigure className=\"px-4 pt-4\"\u003e\n              \u003cimg\n                src={image}\n                alt={title}\n                className=\"rounded-xl h-64 md:h-48 w-full object-cover\"\n              /\u003e\n            \u003c/figure\u003e\n            \u003cdiv className=\"card-body items-center text-center\"\u003e\n              \u003ch2 className=\"card-title capitalize tracking-wider\"\u003e{title}\u003c/h2\u003e\n              \u003cspan className=\"text-secondary\"\u003e{dollarsAmount}\u003c/span\u003e\n            \u003c/div\u003e\n          \u003c/Link\u003e\n        );\n      })}\n    \u003c/div\u003e\n  );\n};\nexport default ProductsGrid;\n```\n\n## Challenge (21) - Format Price\n\n- payment providers need in smallest unit\n  - in this case cents\n- in utils setup a function to format price\n- utilize in ProductsGrid\n\n## Solution (21) - Format Price\n\n- utils/index.js\n\n```js\nexport const formatPrice = (price) =\u003e {\n  const dollarsAmount = new Intl.NumberFormat(\"en-US\", {\n    style: \"currency\",\n    currency: \"USD\",\n  }).format((price / 100).toFixed(2));\n  return dollarsAmount;\n};\n```\n\n## Challenge (22) - Single Product\n\n- complete in multiple steps\n- fetch and render single product\n- don't forget about the colors and amount options\n- extra credit - set amount options dynamically\n\n### SingleProduct.jsx\n\n1. Import Dependencies:\n\n   - Import `useLoaderData` from `'react-router-dom'`.\n   - Import `formatPrice`, `customFetch`, and `useState` from `'../utils'`.\n   - Import `Link` from `'react-router-dom'`.\n\n2. Define Loader Function:\n\n   - Define a loader function that fetches product data based on the `params.id`.\n   - Use `customFetch` to fetch the product data from `/products/${params.id}`.\n   - Return an object containing the fetched product data.\n\n3. Create SingleProduct Component:\n\n   - Define a functional component named `SingleProduct`.\n\n4. Component Structure:\n\n   - Inside the component, destructure the `product` data using `useLoaderData`.\n   - Destructure attributes like `image`, `title`, `price`, `description`, `colors`, and `company`.\n   - Create a `dollarsAmount` variable by formatting the `price` using `formatPrice`.\n   - Use `useState` to manage the `productColor` and `amount` state.\n\n5. Display Product Information:\n\n   - Return a `section` element to encapsulate the component content.\n   - Display breadcrumb navigation using `Link` components for Home and Products pages.\n\n6. Product Display:\n\n   - Create a `div` with classes for styling and a grid layout.\n   - Display the product image using an `img` element with classes for styling.\n\n7. Product Info:\n\n   - Within a `div`, display the product title, company, and `dollarsAmount`.\n\n8. Description:\n\n   - Display the product description using a `p` element.\n\n9. Colors:\n\n   - Display available product colors using a `div` with classes for styling.\n   - Map through the `colors` array and create a `button` for each color.\n   - Add appropriate classes and styles for the color button based on the selected `productColor`.\n\n10. Amount:\n\n    - Display a dropdown for selecting the product amount using a `div`.\n    - Use a `select` element with options for different amounts.\n    - Set the value of the `select` to the `amount` state and handle changes with `handleAmount` function.\n\n11. Cart Button:\n\n    - Display an \"Add to bag\" button using a `button` element with appropriate classes and an `onClick` event handler.\n\n12. Export SingleProduct Component:\n    - Export the `SingleProduct` component as the default export of the module.\n\n## Solution (22) - Single Product\n\n- import and setup loader in the App.jsx\n\n```js\nimport { useLoaderData } from \"react-router-dom\";\nimport { formatPrice, customFetch } from \"../utils\";\nimport { Link } from \"react-router-dom\";\nimport { useState } from \"react\";\n\nexport const loader = async ({ params }) =\u003e {\n  const response = await customFetch(`/products/${params.id}`);\n  return { product: response.data.data };\n};\n\nconst SingleProduct = () =\u003e {\n  const { product } = useLoaderData();\n  const { image, title, price, description, colors, company } =\n    product.attributes;\n  const dollarsAmount = formatPrice(price);\n  const [productColor, setProductColor] = useState(colors[0]);\n  const [amount, setAmount] = useState(1);\n\n  const handleAmount = (e) =\u003e {\n    setAmount(parseInt(e.target.value));\n  };\n\n  return (\n    \u003csection\u003e\n      \u003cdiv className=\"text-md breadcrumbs\"\u003e\n        \u003cul\u003e\n          \u003cli\u003e\n            \u003cLink to=\"/\"\u003eHome\u003c/Link\u003e\n          \u003c/li\u003e\n          \u003cli\u003e\n            \u003cLink to=\"/products\"\u003eProducts\u003c/Link\u003e\n          \u003c/li\u003e\n        \u003c/ul\u003e\n      \u003c/div\u003e\n      {/* PRODUCT */}\n      \u003cdiv className=\"mt-6 grid gap-y-8 lg:grid-cols-2  lg:gap-x-16\"\u003e\n        {/* IMAGE */}\n        \u003cimg\n          src={image}\n          alt={title}\n          className=\"w-96 h-96 object-cover rounded-lg lg:w-full  \"\n        /\u003e\n        {/* PRODUCT INFO */}\n        \u003cdiv\u003e\n          \u003ch1 className=\"capitalize text-3xl font-bold\"\u003e{title}\u003c/h1\u003e\n          \u003ch4 className=\"text-xl text-neutral-content font-bold mt-2\"\u003e\n            {company}\n          \u003c/h4\u003e\n\n          \u003cp className=\"mt-3 text-xl\"\u003e{dollarsAmount}\u003c/p\u003e\n\n          \u003cp className=\"mt-6 leading-8\"\u003e{description}\u003c/p\u003e\n\n          {/* COLORS */}\n          \u003cdiv className=\"mt-6\"\u003e\n            \u003ch4 className=\"text-md font-medium tracking-wider capitalize\"\u003e\n              colors\n            \u003c/h4\u003e\n            \u003cdiv className=\"mt-2\"\u003e\n              {colors.map((color) =\u003e {\n                return (\n                  \u003cbutton\n                    key={color}\n                    type=\"button\"\n                    className={`badge  w-6 h-6 mr-2  ${\n                      color === productColor \u0026\u0026 \"border-2 border-secondary\"\n                    }`}\n                    style={{ backgroundColor: color }}\n                    onClick={() =\u003e setProductColor(color)}\n                  \u003e\u003c/button\u003e\n                );\n              })}\n            \u003c/div\u003e\n          \u003c/div\u003e\n          {/* AMOUNT */}\n          \u003cdiv className=\"form-control w-full max-w-xs\"\u003e\n            \u003clabel className=\"label\"\u003e\n              \u003ch4 className=\"text-md font-medium tracking-wider capitalize\"\u003e\n                amount\n              \u003c/h4\u003e\n            \u003c/label\u003e\n            \u003cselect\n              className=\"select select-secondary select-bordered select-md\"\n              value={amount}\n              onChange={handleAmount}\n            \u003e\n              \u003coption value={1}\u003e1\u003c/option\u003e\n              \u003coption value={2}\u003e2\u003c/option\u003e\n              \u003coption value={3}\u003e3\u003c/option\u003e\n            \u003c/select\u003e\n          \u003c/div\u003e\n          {/* CART BUTTON */}\n          \u003cdiv className=\"mt-10 \"\u003e\n            \u003cbutton\n              className=\"btn btn-secondary btn-md\"\n              onClick={() =\u003e console.log(\"add to bag\")}\n            \u003e\n              Add to bag\n            \u003c/button\u003e\n          \u003c/div\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/section\u003e\n  );\n};\nexport default SingleProduct;\n```\n\n\u003c!-- EXTRA CREDIT --\u003e\n\n- rename to index.jsx\n\nindex.jsx\n\n```js\nexport const generateAmountOptions = (number) =\u003e {\n  return Array.from({ length: number }, (_, index) =\u003e {\n    const amount = index + 1;\n\n    return (\n      \u003coption key={amount} value={amount}\u003e\n        {amount}\n      \u003c/option\u003e\n    );\n  });\n};\n```\n\nArray.from({ length: number }, (_, index) =\u003e { ... }): This part uses the Array.from method to create an array of a specific length, determined by the number parameter. The second argument of the Array.from method is a callback function that will be invoked for each element in the array. The underscore (_) is a placeholder for the current element (which we don't need in this case), and index is the index of the current element.\n\nconst amount = index + 1;: Inside the callback function, this line calculates the amount value based on the index. Since the index starts from 0 but you want amount to start from 1, you add 1 to the index.\n\n## Challenge (23) - Products Page (Setup)\n\n- create following components and render in products page\n  - Filters\n  - ProductsContainer\n  - PaginationContainer\n- in products page loader fetch all products\n\n### Products.jsx\n\n1. Import Dependencies:\n\n   - Import `Filters`, `PaginationContainer`, and `ProductsContainer` from `'../components'`.\n   - Import `customFetch` from `'../utils'`.\n\n2. Define URL and Loader Function:\n\n   - Define a constant `url` containing the URL path to fetch products from.\n   - Define a loader function that fetches product data from the defined URL.\n   - Use `customFetch` to fetch the product data from the `url`.\n   - Extract products and meta information from the response and return them.\n\n3. Create Products Component:\n\n   - Define a functional component named `Products`.\n\n4. Component Structure:\n\n   - Return a `Fragment` element (`\u003c\u003e...\u003c/\u003e`) to wrap the component content.\n\n5. Filters Component:\n\n   - Include the `Filters` component to allow users to apply filters to the product list.\n\n6. ProductsContainer Component:\n\n   - Include the `ProductsContainer` component to display the list of products.\n\n7. PaginationContainer Component:\n\n   - Include the `PaginationContainer` component to manage product list pagination.\n\n8. Export Products Component:\n   - Export the `Products` component as the default export of the module.\n\n## Solution (23) - Products Page (Setup)\n\n- import and setup loader in app.jsx\n\nProducts.jsx\n\n```js\nimport { Filters, PaginationContainer, ProductsContainer } from \"../components\";\nimport { customFetch } from \"../utils\";\n\nconst url = \"/products\";\nexport const loader = async ({ request }) =\u003e {\n  const response = await customFetch(url);\n\n  const products = response.data.data;\n  const meta = response.data.meta;\n  return { products, meta };\n};\n\nconst Products = () =\u003e {\n  return (\n    \u003c\u003e\n      \u003cFilters /\u003e\n      \u003cProductsContainer /\u003e\n      \u003cPaginationContainer /\u003e\n    \u003c/\u003e\n  );\n};\nexport default Products;\n```\n\n## Challenge (24) - Products Container\n\n- create ProductsList and render products in one column\n- setup header (with total jobs and toggle buttons)\n- toggle between ProductsGrid and ProductsList\n\n### ProductsList.jsx\n\n1. Import Dependencies:\n\n   - Import `formatPrice` from `'../utils'`.\n   - Import `Link` and `useLoaderData` from `'react-router-dom'`.\n\n2. Create ProductList Component:\n\n   - Define a functional component named `ProductList`.\n\n3. Component Structure:\n\n   - Return a `div` element containing a list of products.\n\n4. Loop Through Products:\n\n   - Use the `useLoaderData` hook to get the `products` data from the loader.\n   - Use the `map` function to loop through each product in the `products` array.\n\n5. Product Link:\n\n   - For each product, create a `Link` element that links to the individual product page.\n   - Use the `product.id` as the link path (`to={`/products/${product.id}`}`).\n   - Add CSS classes to style the link and apply hover effects.\n\n6. Product Image:\n\n   - Display the product image inside an `img` element.\n   - Apply appropriate classes for styling and responsive design.\n   - Add hover effect to the image using CSS classes.\n\n7. Product Details:\n\n   - Display the product title and company using `h3` and `h4` elements.\n   - Add classes for font styles and responsiveness.\n\n8. Product Price:\n\n   - Display the formatted price using the `formatPrice` function.\n   - Use a `p` element with appropriate classes for styling.\n\n9. Export ProductList Component:\n   - Export the `ProductList` component as the default export of the module.\n\n### ProductsContainer.jsx\n\n1. Import Dependencies:\n\n   - Import `useLoaderData` from `'react-router-dom'`.\n   - Import `ProductsGrid` and `ProductsList` from their respective paths.\n   - Import `useState` from `'react'`.\n   - Import `BsFillGridFill` and `BsList` from `'react-icons/bs'`.\n\n2. Create ProductsContainer Component:\n\n   - Define a functional component named `ProductsContainer`.\n\n3. Component Structure:\n\n   - Return a `div` element containing the products container.\n\n4. Total Products Count:\n\n   - Use the `useLoaderData` hook to get the `meta` data from the loader.\n   - Extract the `total` count of products from `meta.pagination`.\n   - Use a conditional statement to handle the plural form of the word \"product\".\n\n5. Layout State and Styles:\n\n   - Use the `useState` hook to manage the layout state (grid or list).\n   - Create a helper function `setActiveStyles` to generate the CSS classes based on the active layout.\n   - Return appropriate CSS classes for active and inactive layouts.\n\n6. Header Section:\n\n   - Create a `div` for the header section containing the product count and layout buttons.\n   - Display the total number of products using the extracted `totalProducts` count.\n   - Create a button for grid layout and a button for list layout.\n   - Attach click event handlers to the buttons to set the layout state.\n\n7. Products Display:\n\n   - Create a `div` to display the products.\n   - Use conditional rendering to handle cases where no products match the search or when products are present.\n   - If no products match the search, display a message.\n   - If products are present and the layout is 'grid', display the `ProductsGrid` component.\n   - If products are present and the layout is 'list', display the `ProductsList` component.\n\n8. Export ProductsContainer Component:\n   - Export the `ProductsContainer` component as the default export of the module.\n\n## Solution (24) - Products Container\n\nProductsList.jsx\n\n```js\nimport { formatPrice } from \"../utils\";\nimport { Link, useLoaderData } from \"react-router-dom\";\n\nconst ProductList = () =\u003e {\n  const { products } = useLoaderData();\n  return (\n    \u003cdiv className=\"mt-12 grid gap-y-8\"\u003e\n      {products.map((product) =\u003e {\n        const { title, price, image, company } = product.attributes;\n        const dollarsAmount = formatPrice(price);\n\n        return (\n          \u003cLink\n            key={product.id}\n            to={`/products/${product.id}`}\n            className=\"p-8 rounded-lg flex flex-col sm:flex-row gap-y-4 flex-wrap bg-base-100 shadow-xl hover:shadow-2xl duration-300 group\"\n          \u003e\n            \u003cimg\n              src={image}\n              alt={title}\n              className=\"h-24 w-24 rounded-lg sm:h-32 sm:w-32 object-cover group-hover:scale-105 transition duration-300\"\n            /\u003e\n            \u003cdiv className=\"ml-0 sm:ml-16\"\u003e\n              \u003ch3 className=\"capitalize font-medium text-lg\"\u003e{title}\u003c/h3\u003e\n              \u003ch4 className=\"capitalize text-md text-neutral-content\"\u003e\n                {company}\n              \u003c/h4\u003e\n\n              {/* COLOR */}\n            \u003c/div\u003e\n\n            \u003cp className=\"font-medium ml-0 sm:ml-auto text-lg\"\u003e\n              {dollarsAmount}\n            \u003c/p\u003e\n          \u003c/Link\u003e\n        );\n      })}\n    \u003c/div\u003e\n  );\n};\n\nexport default ProductList;\n```\n\nProductsContainer.jsx\n\n```js\nimport { useLoaderData } from \"react-router-dom\";\nimport ProductsGrid from \"./ProductsGrid\";\nimport ProductsList from \"./ProductsList\";\nimport { useState } from \"react\";\nimport { BsFillGridFill, BsList } from \"react-icons/bs\";\n\nconst ProductsContainer = () =\u003e {\n  const { meta } = useLoaderData();\n  const totalProducts = meta.pagination.total;\n  const [layout, setLayout] = useState(\"grid\");\n\n  const setActiveStyles = (pattern) =\u003e {\n    return `text-xl btn btn-circle btn-sm ${\n      pattern === layout\n        ? \"btn-primary text-primary-content\"\n        : \"btn-ghost text-base-content\"\n    }`;\n  };\n\n  return (\n    \u003c\u003e\n      {/* HEADER */}\n      \u003cdiv className=\"flex justify-between items-center mt-8 border-b border-base-300 pb-5\"\u003e\n        \u003ch4 className=\"font-medium text-md\"\u003e\n          {totalProducts} product{totalProducts \u003e 1 \u0026\u0026 \"s\"}\n        \u003c/h4\u003e\n        \u003cdiv className=\"flex gap-x-2\"\u003e\n          \u003cbutton\n            onClick={() =\u003e setLayout(\"grid\")}\n            className={setActiveStyles(\"grid\")}\n          \u003e\n            \u003cBsFillGridFill /\u003e\n          \u003c/button\u003e\n\n          \u003cbutton\n            onClick={() =\u003e setLayout(\"list\")}\n            className={setActiveStyles(\"list\")}\n          \u003e\n            \u003cBsList /\u003e\n          \u003c/button\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n\n      {/* PRODUCTS */}\n      \u003cdiv\u003e\n        {totalProducts === 0 ? (\n          \u003ch5 className=\"text-2xl mt-16\"\u003e\n            Sorry, no products matched your search...\n          \u003c/h5\u003e\n        ) : layout === \"grid\" ? (\n          \u003cProductsGrid /\u003e\n        ) : (\n          \u003cProductsList /\u003e\n        )}\n      \u003c/div\u003e\n    \u003c/\u003e\n  );\n};\n\nexport default ProductsContainer;\n```\n\n## Challenge (25) - Filters (Search Input)\n\n- add size to prop FormInput.jsx\n- render search input, submit button and reset button\n\n## Solution (25) - Filters (Search Input)\n\nFormInput.jsx\n\n```js\nconst FormInput = ({ label, name, type, defaultValue, size }) =\u003e {\n  return (\n    \u003cdiv className=\"form-control\"\u003e\n      \u003clabel htmlFor={name} className=\"label\"\u003e\n        \u003cspan className=\"label-text capitalize\"\u003e{label}\u003c/span\u003e\n      \u003c/label\u003e\n      \u003cinput\n        type={type}\n        name={name}\n        defaultValue={defaultValue}\n        className={`input input-bordered ${size}`}\n      /\u003e\n    \u003c/div\u003e\n  );\n};\nexport default FormInput;\n```\n\nFilters.jsx\n\n```js\nimport { Form, useLoaderData, Link } from \"react-router-dom\";\nimport FormInput from \"./FormInput\";\n\nconst Filters = () =\u003e {\n  return (\n    \u003cForm className=\"bg-base-200 rounded-md px-8 py-4 grid gap-x-4 gap-y-8 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 items-center\"\u003e\n      {/* SEARCH */}\n      \u003cFormInput\n        type=\"search\"\n        label=\"search product\"\n        name=\"search\"\n        size=\"input-sm\"\n      /\u003e\n      {/* BUTTONS */}\n      \u003cbutton type=\"submit\" className=\"btn btn-primary btn-sm \"\u003e\n        search\n      \u003c/button\u003e\n      \u003cLink to=\"/products\" className=\"btn btn-accent btn-sm\"\u003e\n        reset\n      \u003c/Link\u003e\n    \u003c/Form\u003e\n  );\n};\nexport default Filters;\n```\n\n## Challenge (26) - Filters (Select Input)\n\n- setup input for select input\n- render for categories, companies and order\n- companies and categories values are located in meta\n\n### FormSelect.jsx\n\n1. Create FormSelect Component:\n\n   - Define a functional component named `FormSelect`.\n\n2. Component Structure:\n\n   - Return a `div` element containing the form select input.\n\n3. Props:\n\n   - Accept the following props: `label`, `name`, `list`, `defaultValue`, and `size`.\n\n4. Label:\n\n   - Create a `label` element with a `for` attribute matching the `name` prop.\n   - Display the capitalized label text using the `label` prop.\n\n5. Select Input:\n\n   - Create a `select` element for the input field.\n   - Set the `name` and `id` attributes to the value of the `name` prop.\n   - Apply the appropriate CSS classes for the select input using the `size` prop.\n   - Set the `defaultValue` of the select input using the `defaultValue` prop.\n\n6. Options:\n\n   - Map through the `list` prop array to generate individual `option` elements.\n   - Use each item in the `list` as the `key` and `value` attributes of the `option` element.\n\n7. Export FormSelect Component:\n   - Export the `FormSelect` component as the default export of the module.\n\n## Solution (26) - Filters (Select Input)\n\nFormSelect.jsx\n\n```js\nconst FormSelect = ({ label, name, list, defaultValue, size }) =\u003e {\n  return (\n    \u003cdiv className=\"form-control\"\u003e\n      \u003clabel htmlFor={name} className=\"label\"\u003e\n        \u003cspan className=\"label-text capitalize\"\u003e{label}\u003c/span\u003e\n      \u003c/label\u003e\n      \u003cselect\n        name={name}\n        id={name}\n        className={`select select-bordered ${size}`}\n        defaultValue={defaultValue}\n      \u003e\n        {list.map((item) =\u003e {\n          return (\n            \u003coption key={item} value={item}\u003e\n              {item}\n            \u003c/option\u003e\n          );\n        })}\n      \u003c/select\u003e\n    \u003c/div\u003e\n  );\n};\nexport default FormSelect;\n```\n\nFilters.jsx\n\n```js\nconst { meta } = useLoaderData();\n\n{\n  /* CATEGORIES */\n}\n\u003cFormSelect\n  label=\"select category\"\n  name=\"category\"\n  list={meta.categories}\n  size=\"select-sm\"\n/\u003e;\n{\n  /* COMPANIES */\n}\n\u003cFormSelect\n  label=\"select company\"\n  name=\"company\"\n  list={meta.companies}\n  size=\"select-sm\"\n/\u003e;\n{\n  /* ORDER */\n}\n\u003cFormSelect\n  label=\"sort by\"\n  name=\"order\"\n  list={[\"a-z\", \"z-a\", \"high\", \"low\"]}\n  size=\"select-sm\"\n/\u003e;\n```\n\n## Challenge (27) - Filters (Price)\n\n- create range input (hint: you will need local state)\n\n### FormRange.jsx\n\n1. Create FormRange Component:\n\n   - Define a functional component named `FormRange`.\n\n2. Component Structure:\n\n   - Return a `div` element containing the form range input and related elements.\n\n3. Props:\n\n   - Accept the following props: `label`, `name`, and `size`.\n\n4. Default Values:\n\n   - Define default values for `step`, `maxPrice`, and `selectedPrice`.\n\n5. Label and Selected Price Display:\n\n   - Create a `label` element with a `for` attribute matching the `name` prop.\n   - Display the capitalized label text using the `label` prop.\n   - Display the selected price using the `formatPrice` function.\n\n6. Range Input:\n\n   - Create an `input` element with `type` set to `'range'`.\n   - Set the `name`, `min`, `max`, `value`, and `step` attributes.\n   - Use the `selectedPrice` state for the `value` attribute.\n   - Set the `onChange` event handler to update `selectedPrice`.\n\n7. Min and Max Price Display:\n\n   - Create a `div` element for displaying minimum and maximum price values.\n   - Use the `formatPrice` function for formatting and displaying max price.\n\n8. Export FormRange Component:\n   - Export the `FormRange` component as the default export of the module.\n\n## Solution (27) - Filters (Price )\n\nFormRange.jsx\n\n```js\nimport { formatPrice } from \"../utils\";\nimport { useState } from \"react\";\nconst FormRange = ({ label, name, size }) =\u003e {\n  const step = 1000;\n  const maxPrice = 100000;\n  const [selectedPrice, setSelectedPrice] = useState(maxPrice);\n\n  return (\n    \u003cdiv className=\"form-control\"\u003e\n      \u003clabel htmlFor={name} className=\"label cursor-pointer\"\u003e\n        \u003cspan className=\"label-text capitalize\"\u003e{label}\u003c/span\u003e\n        \u003cspan\u003e{formatPrice(selectedPrice)}\u003c/span\u003e\n      \u003c/label\u003e\n      \u003cinput\n        type=\"range\"\n        name={name}\n        min={0}\n        max={maxPrice}\n        value={selectedPrice}\n        onChange={(e) =\u003e setSelectedPrice(e.target.value)}\n        className={`range range-primary ${size}`}\n        step={step}\n      /\u003e\n      \u003cdiv className=\"w-full flex justify-between text-xs px-2 mt-2\"\u003e\n        \u003cspan className=\"font-bold text-md\"\u003e0\u003c/span\u003e\n        \u003cspan className=\"font-bold text-md\"\u003eMax : {formatPrice(maxPrice)}\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  );\n};\nexport default FormRange;\n```\n\nFilters.jsx\n\n```js\n{\n  /* PRICE */\n}\n\u003cFormRange label=\"select price\" name=\"price\" size=\"range-sm\" /\u003e;\n```\n\n## Challenge (28) - Filters (Shipping)\n\n- create checkbox input\n\n### FormCheckbox.jsx\n\n1. Create FormCheckbox Component:\n\n   - Define a functional component named `FormCheckbox`.\n\n2. Component Structure:\n\n   - Return a `div` element containing the form checkbox input and related elements.\n\n3. Props:\n\n   - Accept the following props: `label`, `name`, `defaultValue`, and `size`.\n\n4. Label Display:\n\n   - Create a `label` element with a `for` attribute matching the `name` prop.\n   - Display the capitalized label text using the `label` prop.\n\n5. Checkbox Input:\n\n   - Create an `input` element with `type` set to `'checkbox'`.\n   - Set the `name` attribute to match the `name` prop.\n   - Set the `defaultChecked` attribute using the `defaultValue` prop.\n   - Use the `size` prop to determine the checkbox size class.\n\n6. Styling and Layout:\n\n   - Apply appropriate classes to style and position the form control items.\n\n7. Export FormCheckbox Component:\n   - Export the `FormCheckbox` component as the default export of the module.\n\n## Solution (28) - Filters (Shipping)\n\nFormCheckbox.jsx\n\n```js\nconst FormCheckbox = ({ label, name, defaultValue, size }) =\u003e {\n  return (\n    \u003cdiv className=\"form-control items-center\"\u003e\n      \u003clabel htmlFor={name} className=\"label cursor-pointer\"\u003e\n        \u003cspan className=\"label-text capitalize\"\u003e{label}\u003c/span\u003e\n      \u003c/label\u003e\n      \u003cinput\n        type=\"checkbox\"\n        name={name}\n        defaultChecked={defaultValue}\n        className={`checkbox checkbox-primary ${size}`}\n      /\u003e\n    \u003c/div\u003e\n  );\n};\nexport default FormCheckbox;\n```\n\nFilters.jsx\n\n```js\n{\n  /* SHIPPING */\n}\n\u003cFormCheckbox label=\"free shipping\" name=\"shipping\" size=\"checkbox-sm\" /\u003e;\n```\n\n## Challenge (29) - Global Loading\n\n- create loading component\n- check for loading state in HomeLayout\n- toggle between loading and \u003cOutlet\u003e\n\n### Loading.jsx\n\n1. Create Loading Component:\n\n   - Define a functional component named \"Loading\".\n\n2. Component Structure:\n\n   - Return a \"div\" element with CSS classes to center content both vertically and horizontally.\n\n3. Loading Animation:\n\n   - Inside the \"div\", include a \"span\" element with the classes \"loading loading-ring loading-lg\".\n   - This applies a loading animation to create the visual effect.\n\n4. Styling:\n\n   - Use the provided CSS classes to style the loading animation.\n\n5. Export Loading Component:\n   - Export the \"Loading\" component as the default export of the module.\n\n### HomeLayout.jsx\n\n1. Create HomeLayout Component:\n\n   - Define a functional component named \"HomeLayout\".\n\n2. Import Dependencies:\n\n   - Import \"Outlet\" and \"useNavigation\" from 'react-router-dom'.\n   - Import \"Navbar\", \"Loading\", and \"Header\" from '../components'.\n\n3. Component Structure:\n\n   - Return a fragment ('\u003c\u003e...\u003c/\u003e') to encapsulate the component's content.\n\n4. UseNavigation Hook:\n\n   - Use the \"useNavigation\" hook to access the navigation state.\n   - Store whether the page is currently loading in \"isPageLoading\" variable.\n\n5. Conditional Rendering:\n\n   - Use a ternary operator to conditionally render content:\n     - If \"isPageLoading\" is true, render the \"Loading\" component.\n     - Otherwise, render a \"section\" element with CSS classes and include the \"Outlet\" component.\n\n6. Header and Navbar:\n\n   - Include the \"Header\" and \"Navbar\" components at the beginning of the component.\n\n7. Styling:\n\n   - Apply CSS classes to style the layout and align its elements.\n\n8. Export HomeLayout Component:\n   - Export the \"HomeLayout\" component as the default export of the module.\n\n## Solution (29) - Global Loading\n\nLoading.jsx\n\n```js\nconst Loading = () =\u003e {\n  return (\n    \u003cdiv className=\"h-screen flex items-center justify-center\"\u003e\n      \u003cspan className=\"loading loading-ring loading-lg\" /\u003e\n    \u003c/div\u003e\n  );\n};\nexport default Loading;\n```\n\n```js\nimport { Outlet, useNavigation } from \"react-router-dom\";\nimport { Navbar, Loading, Header } from \"../components\";\nconst HomeLayout = () =\u003e {\n  const navigation = useNavigation();\n  const isPageLoading = navigation.state === \"loading\";\n  return (\n    \u003c\u003e\n      \u003cHeader /\u003e\n      \u003cNavbar /\u003e\n      {isPageLoading ? (\n        \u003cLoading /\u003e\n      ) : (\n        \u003csection className=\"align-element py-20\"\u003e\n          \u003cOutlet /\u003e\n        \u003c/section\u003e\n      )}\n    \u003c/\u003e\n  );\n};\nexport default HomeLayout;\n```\n\n## Challenge (30) - Setup Params\n\n- explore how to filter products\n  [API DOCS](https://documenter.getpostman.com/view/18152321/2s9Xy5KpTi#80a47ff5-cc24-494b-89e0-02cd92acc226)\n- test in Thunder Client\n- access params in loader\n- use params in customFetch\n- pass params down\n- use params as default values (price in FormRange)\n- setup reset button\n\n## Solution (30) - Setup Params\n\nProducts.jsx\n\n```js\nexport const loader = async ({ request }) =\u003e {\n  const params = Object.fromEntries([\n    ...new URL(request.url).searchParams.entries(),\n  ]);\n  const response = await customFetch(url, { params });\n\n  const products = response.data.data;\n  const meta = response.data.meta;\n\n  return { products, meta, params };\n};\n```\n\nFilters.jsx\n\n```js\nimport { Form, useLoaderData, Link } from \"react-router-dom\";\nimport FormInput from \"./FormInput\";\nimport FormSelect from \"./FormSelect\";\nimport FormRange from \"./FormRange\";\nimport FormCheckbox from \"./FormCheckbox\";\nconst Filters = () =\u003e {\n  const { meta, params } = useLoaderData();\n  const { search, company, category, shipping, order, price } = params;\n  return (\n    \u003cForm className=\"bg-base-200 rounded-md px-8 py-4 grid gap-x-4 gap-y-8 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 items-center\"\u003e\n      {/* SEARCH */}\n      \u003cFormInput\n        type=\"search\"\n        label=\"search product\"\n        name=\"search\"\n        defaultValue={search}\n        size=\"input-sm\"\n      /\u003e\n      {/* CATEGORIES */}\n      \u003cFormSelect\n        label=\"select category\"\n        name=\"category\"\n        list={meta.categories}\n        defaultValue={category}\n        size=\"select-sm\"\n      /\u003e\n      {/* COMPANIES */}\n      \u003cFormSelect\n        label=\"select company\"\n        name=\"company\"\n        list={meta.companies}\n        defaultValue={company}\n        size=\"select-sm\"\n      /\u003e\n      {/* ORDER */}\n      \u003cFormSelect\n        label=\"sort by\"\n        name=\"order\"\n        list={[\"a-z\", \"z-a\", \"high\", \"low\"]}\n        defaultValue={order}\n        size=\"select-sm\"\n      /\u003e\n      {/* PRICE */}\n      \u003cFormRange\n        label=\"select price\"\n        name=\"price\"\n        price={price}\n        size=\"range-sm\"\n      /\u003e\n      {/* SHIPPING */}\n      \u003cFormCheckbox\n        label=\"free shipping\"\n        name=\"shipping\"\n        defaultValue={shipping}\n        size=\"checkbox-sm\"\n      /\u003e\n      {/* BUTTONS */}\n      \u003cbutton type=\"submit\" className=\"btn btn-primary btn-sm\"\u003e\n        search\n      \u003c/button\u003e\n      \u003cLink to=\"/products\" className=\"btn btn-accent btn-sm\"\u003e\n        reset\n      \u003c/Link\u003e\n    \u003c/Form\u003e\n  );\n};\nexport default Filters;\n```\n\n```js\nconst params = Object.fromEntries([\n  ...new URL(request.url).searchParams.entries(),\n]);\n```\n\nIt takes a URL string from the request.url property.\nIt creates a URL object from that URL string.\nIt extracts the query parameters using the searchParams property.\nIt converts the query parameters into an iterable of key-value pairs using the entries() method.\nIt spreads these key-value pairs into an array.\nIt uses Object.fromEntries() to create a new object where the key-value pairs become properties of the object.\n\n## Challenge (31) - Pagination\n\n- explore how to paginate\n- test in Thunder Client\n- access meta\n- display next, prev and page buttons\n- add page value to query params\n\n## Building the PaginationContainer Component\n\n1. Import Required Modules:\n\n- Import hooks and modules from `react-router-dom`:\n  - `useLoaderData`\n  - `useLocation`\n  - `useNavigate`\n\n2. Initialize Component:\n\n- Create a functional component named `PaginationContainer`.\n\n3. Retrieve Data with Hooks:\n\n- Use the `useLoaderData` hook to get the `meta` data.\n- Destructure `pageCount` and `page` from `meta.pagination`.\n- Use the `useLocation` hook to get `search` and `pathname`.\n- Use the `useNavigate` hook to get the `navigate` function.\n\n4. Generate Pages Array:\n\n- Create an array called `pages` using `Array.from()`.\n  - This represents all the page numbers.\n\n5. Handle Page Change:\n\n- Create a function `handlePageChange` that takes `pageNumber` as an argument.\n  - Update the URL's query string parameter `page` with the new page number.\n  - Navigate to the updated URL.\n\n6. Conditional Rendering:\n\n- If `pageCount` is less than 2:\n  - Return `null`.\n\n7. Render Pagination Component:\n\n- Render a `div` container with the class `mt-16 flex justify-end`.\n  - Inside this, render another `div` with class `join`.\n    - For \"Prev\" button:\n      - Use the class `btn btn-xs sm:btn-md join-item`.\n    - For page numbers:\n      - Use the class `btn btn-xs sm:btn-md border-none join-item`.\n      - Highlight the current page with classes `bg-base-300 border-base-300`.\n    - For \"Next\" button:\n      - Use the class `btn btn-xs sm:btn-md join-item`.\n\n8. Export Component:\n\n- Export the `PaginationContainer` component.\n\n## Solution (31) - Pagination\n\nPaginationContainer.jsx\n\n```js\nimport { useLoaderData, useLocation, useNavigate } from \"react-router-dom\";\n\nconst PaginationContainer = () =\u003e {\n  const { meta } = useLoaderData();\n  const { pageCount, page } = meta.pagination;\n  const pages = Array.from({ length: pageCount }, (_, index) =\u003e {\n    return index + 1;\n  });\n  const { search, pathname } = useLocation();\n  const navigate = useNavigate();\n\n  const handlePageChange = (pageNumber) =\u003e {\n    const searchParams = new URLSearchParams(search);\n    searchParams.set(\"page\", pageNumber);\n    navigate(`${pathname}?${searchParams.toString()}`);\n  };\n\n  if (pageCount \u003c 2) return null;\n\n  return (\n    \u003cdiv className=\"mt-16 flex justify-end\"\u003e\n      \u003cdiv className=\"join\"\u003e\n        \u003cbutton\n          className=\"btn btn-xs sm:btn-md join-item\"\n          onClick={() =\u003e {\n            let prevPage = page - 1;\n            if (prevPage \u003c 1) prevPage = pageCount;\n            handlePageChange(prevPage);\n          }}\n        \u003e\n          Prev\n        \u003c/button\u003e\n        {pages.map((pageNumber) =\u003e {\n          return (\n            \u003cbutton\n              onClick={() =\u003e handlePageChange(pageNumber)}\n              key={pageNumber}\n              className={`btn btn-xs sm:btn-md border-none join-item ${\n                pageNumber === page ? \"bg-base-300 border-base-300\" : \"\"\n              }`}\n            \u003e\n              {pageNumber}\n            \u003c/button\u003e\n          );\n        })}\n        \u003cbutton\n          className=\"btn btn-xs sm:btn-md join-item\"\n          onClick={() =\u003e {\n            let nextPage = page + 1;\n            if (nextPage \u003e pageCount) nextPage = 1;\n            handlePageChange(nextPage);\n          }}\n        \u003e\n          Next\n        \u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  );\n};\nexport default PaginationContainer;\n```\n\n## Challenge (32) - Setup RTK and react-toastify\n\n- create features/cart/cartSlice.js\n- setup default state (README) and reducers\n- export actions and cartSlice.reducer\n- create store.js and add cartSlice\n- setup RTK and react-toastify in main.jsx\n\n## Building the `cartSlice` Redux Slice\n\n1. **Import Required Modules**:\n\n- Import functions from specific libraries:\n  - `createSlice` from `@reduxjs/toolkit`\n  - `toast` from `react-toastify`\n\n2. **Initialize Default State**:\n\n- Create a `defaultState` object with the following properties:\n  - `cartItems` (array)\n  - `numItemsInCart` (integer)\n  - `cartTotal` (integer)\n  - `shipping` (fixed value: 500)\n  - `tax` (integer)\n  - `orderTotal` (integer)\n\n3. **Create the Redux Slice**:\n\n- Use the `createSlice` function to create the `cartSlice`.\n  - Name it as `'cart'`.\n  - Use the previously created `defaultState` as the `initialState`.\n  - Define several reducer functions within it:\n    - `addItem`: Logs the payload when an item is added.\n    - `clearCart`: Intended to clear all items from the cart.\n    - `removeItem`: Intended to remove a specific item.\n    - `editItem`: Intended to edit a specific item.\n\n4. **Export Actions**:\n\n- Destructure and export the following actions:\n  - `addItem`\n  - `removeItem`\n  - `editItem`\n  - `clearCart`\n\n5. **Export Reducer**:\n\n- Export the reducer generated by `cartSlice` for usage in the Redux store.\n\n## Solution (32) - Setup RTK and react-toastify\n\nfeatures/cart/cartSlice.js\n\n```js\nimport { createSlice } from \"@reduxjs/toolkit\";\nimport { toast } from \"react-toastify\";\n\nconst defaultState = {\n  cartItems: [],\n  numItemsInCart: 0,\n  cartTotal: 0,\n  shipping: 500,\n  tax: 0,\n  orderTotal: 0,\n};\n\nconst cartSlice = createSlice({\n  name: \"cart\",\n  initialState: defaultState,\n  reducers: {\n    addItem: (state, action) =\u003e {\n      console.log(action.payload);\n    },\n    clearCart: (state) =\u003e {},\n\n    removeItem: (state, action) =\u003e {},\n    editItem: (state, action) =\u003e {},\n  },\n});\n\nexport const { addItem, removeItem, editItem, clearCart } = cartSlice.actions;\n\nexport default cartSlice.reducer;\n```\n\nstore.js\n\n```js\nimport { configureStore } from \"@reduxjs/toolkit\";\n\nimport cartReducer from \"./features/cart/cartSlice\";\nexport const store = configureStore({\n  reducer: {\n    cartState: cartReducer,\n  },\n});\n```\n\nmain.jsx\n\n```js\nimport React from \"react\";\nimport ReactDOM from \"react-dom/client\";\nimport App from \"./App.jsx\";\nimport \"react-toastify/dist/ReactToastify.css\";\n// order\nimport \"./index.css\";\n\nimport { ToastContainer } from \"react-toastify\";\nimport { store } from \"./store\";\nimport { Provider } from \"react-redux\";\nReactDOM.createRoot(document.getElementById(\"root\")).render(\n  \u003cProvider store={store}\u003e\n    \u003cApp /\u003e\n    \u003cToastContainer position=\"top-center\" /\u003e\n  \u003c/Provider\u003e\n);\n```\n\n## Challenge (33) - Add Product (SingleProductPage)\n\n- import and dispatch addItem action\n- add item to cart in SingleProduct page\n\n## Solution (33) - Add Product (SingleProductPage)\n\nSingleProduct.jsx\n\n```js\nimport { useDispatch } from \"react-redux\";\nimport { addItem } from \"../features/cart/cartSlice\";\nconst SingleProduct = () =\u003e {\n  const dispatch = useDispatch();\n  const cartProduct = {\n    cartID: product.id + productColor,\n    productID: product.id,\n    image,\n    title,\n    price,\n    amount,\n    productColor,\n    company,\n  };\n\n  const addToCart = () =\u003e {\n    dispatch(addItem({ product: cartProduct }));\n  };\n  return (\n    \u003csection\u003e\n      ....\n      {/* CART BUTTON */}\n      \u003cdiv className=\"mt-10 \"\u003e\n        \u003cbutton className=\"btn btn-secondary btn-md\" onClick={addToCart}\u003e\n          Add to bag\n        \u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/section\u003e\n  );\n};\n```\n\n## Challenge (34) - AddItem Reducer\n\n- display cartItems in Navbar\n- setup addItem functionality\n\n## Building the `addItem` Reducer\n\n1. **Purpose**:\n\n- This reducer updates the cart state when a product is added.\n\n2. **Extract Product from Action**:\n\n- Extract the `product` object from `action.payload`.\n\n3. **Check for Existing Item**:\n\n- Search for the product in the `cartItems` array based on the `cartID`.\n- If the item already exists in the cart:\n  - Increment the `amount` of the item in the cart by the `amount` of the `product`.\n\n4. **Add New Item**:\n\n- If the product does not exist in the cart:\n  - Push the `product` directly into the `cartItems` array.\n\n5. **Update Cart Totals**:\n\n- Increase the `numItemsInCart` by the `amount` of the product.\n- Increase the `cartTotal` by the product's `price` multiplied by its `amount`.\n\n6. **Calculate Tax and Order Total**:\n\n- Set `tax` as 10% of the `cartTotal`.\n- Calculate the `orderTotal` as the sum of `cartTotal`, `shipping`, and `tax`.\n\n7. **Save to Local Storage**:\n\n- Convert the current state to a JSON string and store it in the browser's local storage under the key 'cart'.\n\n8. **Notify User**:\n\n- Use the `toast.success` method to display a success message: 'Item added to cart'.\n\n## Solution (34) - AddItem Reducer\n\nNavbar.jsx\n\n```js\nimport { useSelector } from \"react-redux\";\nconst numItemsInCart = useSelector((state) =\u003e state.cartState.numItemsInCart);\n```\n\ncartSlice.js\n\n```js\n{\n    addItem: (state, action) =\u003e {\n      const { product } = action.payload;\n\n      const item = state.cartItems.find((i) =\u003e i.cartID === product.cartID);\n      if (item) {\n        item.amount += product.amount;\n      } else {\n        state.cartItems.push(product);\n      }\n      state.numItemsInCart += product.amount;\n      state.cartTotal += product.price * product.amount;\n      state.tax = 0.1 * state.cartTotal;\n      state.orderTotal = state.cartTotal + state.shipping + state.tax;\n      localStorage.setItem('cart', JSON.stringify(state));\n      toast.success('Item added to cart');\n    },\n}\n\n```\n\n## Challenge (35) - Refactor and Setup Local Storage\n\n- refactor addItem and get default state from local storage\n\n### calculateTotals Reducer:\n\n1. **Purpose**:\n\n- Re-calculates the tax and order total for the cart.\n\n2. **Calculate Tax and Order Total**:\n\n- Set `tax` as 10% of the `cartTotal`.\n- Determine the `orderTotal` as the sum of `cartTotal`, `shipping`, and `tax`.\n\n3. **Save to Local Storage**:\n\n- Convert the current state to a JSON string and save it in the browser's local storage under the key 'cart'.\n\n## Solution (35) - Refactor and Setup Local Storage\n\n```js\nimport { createSlice } from \"@reduxjs/toolkit\";\nimport { toast } from \"react-toastify\";\n\nconst defaultState = {\n  cartItems: [],\n  numItemsInCart: 0,\n  cartTotal: 0,\n  shipping: 500,\n  tax: 0,\n  orderTotal: 0,\n};\n\nconst getCartFromLocalStorage = () =\u003e {\n  return JSON.parse(localStorage.getItem(\"cart\")) || defaultState;\n};\n\nconst cartSlice = createSlice({\n  name: \"cart\",\n  initialState: getCartFromLocalStorage(),\n  reducers: {\n    addItem: (state, action) =\u003e {\n      const { product } = action.payload;\n\n      const item = state.cartItems.find((i) =\u003e i.cartID === product.cartID);\n      if (item) {\n        item.amount += product.amount;\n      } else {\n        state.cartItems.push(product);\n      }\n      state.numItemsInCart += product.amount;\n      state.cartTotal += product.price * product.amount;\n      cartSlice.caseReducers.calculateTotals(state);\n      toast.success(\"item added to cart\");\n    },\n    clearCart: (state) =\u003e {},\n\n    removeItem: (state, action) =\u003e {},\n    editItem: (state, action) =\u003e {},\n\n    calculateTotals: (state) =\u003e {\n      state.tax = 0.1 * state.cartTotal;\n      state.orderTotal = state.cartTotal + state.shipping + state.tax;\n      localStorage.setItem(\"cart\", JSON.stringify(state));\n    },\n  },\n});\n\nexport const { addItem, removeItem, editItem, clearCart } = cartSlice.actions;\n\nexport default cartSlice.reducer;\n```\n\n## Challenge (36) - Clear Cart, Remove Item and Edit Item\n\n- try to setup reducers for clear cart, remove item and edit item\n\n## Building the Reducers\n\n### clearCart Reducer:\n\n1. **Purpose**:\n\n- Clears the current cart state and resets it to the default state.\n\n2. **Update Local Storage**:\n\n- Store the `defaultState` in the browser's local storage under the key 'cart'.\n\n3. **Return Default State**:\n\n- Reset the entire cart state by returning `defaultState`.\n\n### removeItem Reducer:\n\n1. **Purpose**:\n\n- Removes a specific item from the cart.\n\n2. **Extract cartID from Action**:\n\n- Extract the `cartID` from `action.payload`.\n\n3. **Find Product**:\n\n- Find the product in the `cartItems` array based on the `cartID`.\n\n4. **Update Cart Items**:\n\n- Filter out the product from `cartItems` array based on the `cartID`.\n\n5. **Update Cart Totals**:\n\n- Decrease the `numItemsInCart` by the `amount` of the product.\n- Decrease the `cartTotal` by the product's `price` multiplied by its `amount`.\n- Call the `calculateTotals` reducer to re-evaluate tax and order total.\n\n6. **Notify User**:\n\n- Use the `toast.error` method to display a message: 'Item removed from cart'.\n\n### editItem Reducer:\n\n1. **Purpose**:\n\n- Modifies the amount of a specific item in the cart.\n\n2. **Extract Data from Action**:\n\n- Extract `cartID` and `amount` from `action.payload`.\n\n3. **Find Item**:\n\n- Find the item in the `cartItems` array based on the `cartID`.\n\n4. **Update Cart Totals**:\n\n- Adjust the `numItemsInCart` by the difference between the new amount and the item's previous amount.\n- Update the `cartTotal` based on the item's `price` and the amount difference.\n- Update the item's `amount` to the new amount.\n- Call the `calculateTotals` reducer to re-evaluate tax and order total.\n\n5. **Notify User**:\n\n- Use the `toast.success` method to display a message: 'Cart updated'.\n\n## Solution (36) - Clear Cart, Remove Item and Edit Item\n\n```js\nimport { createSlice } from \"@reduxjs/toolkit\";\nimport { toast } from \"react-toastify\";\n\nconst defaultState = {\n  cartItems: [],\n  numItemsInCart: 0,\n  cartTotal: 0,\n  shipping: 500,\n  tax: 0,\n  orderTotal: 0,\n};\n\nconst getCartFromLocalStorage = () =\u003e {\n  return JSON.parse(localStorage.getItem(\"cart\")) || defaultState;\n};\n\nconst cartSlice = createSlice({\n  name: \"cart\",\n  initialState: getCartFromLocalStorage(),\n  reducers: {\n    addItem: (state, action) =\u003e {\n      const { product } = action.payload;\n\n      const item = state.cartItems.find((i) =\u003e i.cartID === product.cartID);\n      if (item) {\n        item.amount += product.amount;\n      } else {\n        state.cartItems.push(product);\n      }\n      state.numItemsInCart += product.amount;\n      state.cartTotal += product.price * product.amount;\n      cartSlice.caseReducers.calculateTotals(state);\n      toast.success(\"item added to cart\");\n    },\n    clearCart: (state) =\u003e {\n      localStorage.setItem(\"cart\", JSON.stringify(defaultState));\n      return defaultState;\n    },\n\n    removeItem: (state, action) =\u003e {\n      const { cartID } = action.payload;\n      const product = state.cartItems.find((i) =\u003e i.cartID === cartID);\n      state.cartItems = state.cartItems.filter((i) =\u003e i.cartID !== cartID);\n\n      state.numItemsInCart -= product.amount;\n      state.cartTotal -= product.price * product.amount;\n      cartSlice.caseReducers.calculateTotals(state);\n      toast.error(\"Item removed from cart\");\n    },\n    editItem: (state, action) =\u003e {\n      const { cartID, amount } = action.payload;\n      const item = state.cartItems.find((i) =\u003e i.cartID === cartID);\n      state.numItemsInCart += amount - item.amount;\n      state.cartTotal += item.price * (amount - item.amount);\n      item.amount = amount;\n      cartSlice.caseReducers.calculateTotals(state);\n      toast.success(\"Cart updated\");\n    },\n\n    calculateTotals: (state) =\u003e {\n      state.tax = 0.1 * state.cartTotal;\n      state.orderTotal = state.cartTotal + state.shipping + state.tax;\n      localStorage.setItem(\"cart\", JSON.stringify(state));\n    },\n  },\n});\n\nexport const { addItem, removeItem, editItem, clearCart } = cartSlice.actions;\n\nexport default cartSlice.reducer;\n```\n\n```js\nstate.numItemsInCart += amount - item.amount;\n```\n\nThe logic here is to update the total number of items in the cart (state.numItemsInCart) by adjusting it based on the difference between the provided amount and the existing quantity of that item (item.amount). If amount is greater than item.amount, it means that items are being added to the cart. If amount is less than item.amount, it means that items are being removed from the cart. If they are equal, it implies no change to the quantity of that item in the cart.\n\nThe result of the subtraction (amount - item.amount) is then added to the current state.numItemsInCart to reflect the new total number of items in the cart.\n\n```js\nstate.cartTotal += item.price * (amount - item.amount);\n```\n\nIn this line, the logic is calculating the change in the total cost of the cart (state.cartTotal) based on the price of the item (item.price) and the change in the quantity of that item (amount - item.amount). This calculation is then added to the current state.cartTotal.\n\nIf amount is greater than item.amount, it means more items are being added, so the cost of those additional items (difference between amount and item.amount) is calculated by multiplying it with the price of the item. If amount is less than item.amount, it means items are being removed, so the cost of those removed items is subtracted from the state.cartTotal. If they are equal, there is no change in the cost related to that item.\n\n## Challenge (37) - Setup Cart Page\n\n- create CartItemsList, CartTotals, CartItem components\n- export CartItemsList, CartTotals in components/index.js\n- setup two column layout in cart page\n\n### CartPage.jsx\n\n1. **Initialize Necessary Imports**:\n\n   - Import `useSelector` from `react-redux` to enable access to the Redux store.\n   - Bring in `CartItemsList`, `SectionTitle`, and `CartTotals` components from the `components` directory.\n   - Import `Link` from `react-router-dom` for navigation capabilities.\n\n2. **Create Cart Component**:\n\n   - Define a functional component named `Cart`.\n\n3. **Initialize State and Variables**:\n\n   - Set a temporary variable `user` to `null`.\n   - Use the `useSelector` hook to retrieve `numItemsInCart` from the Redux store's `cartState`.\n\n4. **Component Logic**:\n\n   - Check if `numItemsInCart` is zero.\n     - If true, return the `SectionTitle` component with the text 'Your cart is empty'.\n     - If there are items in the cart, continue to display the cart details.\n\n5. **Component Structure**:\n\n   - Render the `SectionTitle` component with the text 'Shopping Cart'.\n   - Set up a grid layout (`mt-8 grid gap-8 lg:grid-cols-12`) to manage cart layout.\n   - For displaying cart items:\n     - Use 8 of 12 columns on large screens (`lg:col-span-8`).\n     - Insert the `CartItemsList` component.\n   - For displaying cart totals and the checkout/login button:\n     - Use 4 of 12 columns on large screens (`lg:col-span-4 lg:pl-4`).\n     - Place the `CartTotals` component.\n     - Check if `user` is defined:\n       - If true, provide a link to '/checkout' with the text 'Proceed to checkout'.\n       - If false, provide a link to '/login' with the text 'please login'.\n\n6. **Export Cart Component**:\n\n   - Export the `Cart` component as the default export of the module.\n\n## Solution (37) - Setup Cart Page\n\npages/Cart.jsx\n\n```js\nimport { useSelector } from \"react-redux\";\nimport { CartItemsList, SectionTitle, CartTotals } from \"../components\";\nimport { Link } from \"react-router-dom\";\n\nconst Cart = () =\u003e {\n  // temp\n  const user = null;\n  const numItemsInCart = useSelector((state) =\u003e state.cartState.numItemsInCart);\n  if (numItemsInCart === 0) {\n    return \u003cSectionTitle text=\"Your cart is empty\" /\u003e;\n  }\n  return (\n    \u003c\u003e\n      \u003cSectionTitle text=\"Shopping Cart\" /\u003e\n      \u003cdiv className=\"mt-8 grid gap-8  lg:grid-cols-12\"\u003e\n        \u003cdiv className=\"lg:col-span-8\"\u003e\n          \u003cCartItemsList /\u003e\n        \u003c/div\u003e\n        \u003cdiv className=\"lg:col-span-4 lg:pl-4\"\u003e\n          \u003cCartTotals /\u003e\n          {user ? (\n            \u003cLink to=\"/checkout\" className=\"btn btn-primary btn-block mt-8\"\u003e\n              Proceed to checkout\n            \u003c/Link\u003e\n          ) : (\n            \u003cLink to=\"/login\" className=\"btn btn-primary btn-block mt-8\"\u003e\n              please login\n            \u003c/Link\u003e\n          )}\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/\u003e\n  );\n};\nexport default Cart;\n```\n\n## Challenge (38) - Cart Totals\n\n- setup cart totals component\n\n### CartTotals.jsx\n\n1. **Initialize Necessary Imports**:\n\n   - Import `useSelector` from `react-redux` for state retrieval from the Redux store.\n   - Bring in `formatPrice` function from the `utils` directory for price formatting.\n\n2. **Create CartTotals Component**:\n\n   - Define a functional component named `CartTotals`.\n\n3. **Retrieve State from Redux**:\n\n   - Use the `useSelector` hook to get `cartTotal`, `shipping`, `tax`, and `orderTotal` from the Redux store's `cartState`.\n\n4. **Component Structure**:\n\n   - Enclose all content inside a `div` with the class `card bg-base-200`.\n   - Use an inner `div` with the class `card-body` for structured content.\n   - Display the Subtotal:\n     - Use a `p` element with classes `flex`, `justify-between`, `text-xs`, `border-b`, and `border-base-300 pb-2`.\n     - Use two nested `span` elements. The first displays \"Subtotal\", and the second displays the formatted `cartTotal`.\n   - Display Shipping charges:\n     - Similar to the Subtotal, but the text reads \"Shipping\" and the value is the formatted `shipping`.\n   - Display Tax:\n     - Similar format, but the text reads \"Tax\" and the value is the formatted `tax`.\n   - Display Order Total:\n     - Use a `p` element with classes `mt-4`, `flex`, `justify-between`, and `text-sm  pb-2`.\n     - Use nested `span` elements with `font-bold` class. The first displays \"Order Total\", and the second displays the formatted `orderTotal`.\n\n5. **Export CartTotals Component**:\n\n   - Export the `CartTotals` component as the default export of the module.\n\n## Solution (38) - Cart Totals\n\n```js\nimport { useSelector } from \"react-redux\";\nimport { formatPrice } from \"../utils\";\nconst CartTotals = () =\u003e {\n  const { cartTotal, shipping, tax, orderTotal } = useSelector(\n    (state) =\u003e state.cartState\n  );\n\n  return (\n    \u003cdiv className=\"card bg-base-200\"\u003e\n      \u003cdiv className=\"card-body\"\u003e\n        {/* SUBTOTAL */}\n        \u003cp className=\"flex justify-between text-xs border-b border-base-300 pb-2\"\u003e\n          \u003cspan\u003eSubtotal\u003c/span\u003e\n          \u003cspan className=\"font-medium\"\u003e{formatPrice(cartTotal)}\u003c/span\u003e\n        \u003c/p\u003e\n        {/* SHIPPING */}\n        \u003cp className=\"flex justify-between text-xs border-b border-base-300 pb-2\"\u003e\n          \u003cspan\u003eShipping\u003c/span\u003e\n          \u003cspan className=\"font-medium\"\u003e{formatPrice(shipping)}\u003c/span\u003e\n        \u003c/p\u003e\n        {/* Tax */}\n        \u003cp className=\"flex justify-between text-xs border-b border-base-300 pb-2\"\u003e\n          \u003cspan\u003eTax\u003c/span\u003e\n          \u003cspan className=\"font-medium\"\u003e{formatPrice(tax)}\u003c/span\u003e\n        \u003c/p\u003e\n        {/* Total */}\n        \u003cp className=\"mt-4 flex justify-between text-sm  pb-2\"\u003e\n          \u003cspan className=\"font-bold\"\u003eOrder Total\u003c/span\u003e\n          \u003cspan className=\"font-bold\"\u003e{formatPrice(orderTotal)}\u003c/span\u003e\n        \u003c/p\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  );\n};\nexport default CartTotals;\n```\n\n## Challenge (39) - Cart Items List\n\n- iterate over cartItems and return CartItem\n- in CartItem display values and implement remove,edit functionality\n\n### CartItemsList.jsx\n\n1. **Initialize Necessary Imports**:\n\n   - Import `useSelector` from `react-redux` to retrieve state from the Redux store.\n   - Bring in `CartItem` component for item rendering.\n\n2. **Create CartItemsList Component**:\n\n   - Define a functional component named `CartItemsList`.\n\n3. **Retrieve State from Redux**:\n\n   - Use the `useSelector` hook to get `cartItems` from the Redux store's `cartState`.\n\n4. **Component Structure**:\n\n   - Create a wrapping `div`.\n   - Use the `map` method on `cartItems` to loop through each item.\n   - For each `item`, return a `CartItem` component:\n     - Pass `item.cartID` as the `key` prop for React's list rendering optimization.\n     - Pass the entire `item` object as the `cartItem` prop to the `CartItem` component.\n\n5. **Export CartItemsList Component**:\n\n   - Export the `CartItemsList` component as the default export of the module.\n\n### CartItem.js\n\n1. **Initialize Necessary Imports**:\n\n   - Import utility functions `formatPrice` and `generateAmountOptions` from the `../utils` directory.\n   - Import `removeItem` and `editItem` from the Redux slice named `cartSlice`.\n   - Bring in `useDispatch` from `react-redux` for dispatching actions to the Redux store.\n\n2. **Create CartItem Component**:\n\n   - Define a functional component named `CartItem` that accepts a `cartItem` prop.\n\n3. **Setup Redux Dispatch**:\n\n   - Use the `useDispatch` hook and store the result in the `dispatch` constant.\n\n4. **Functions for Event Handlers**:\n\n   - Define `removeItemFromTheCart` function:\n     - Dispatch the `removeItem` action, passing the `cartID` from the cart item.\n   - Define `handleAmount` function:\n     - Dispatch the `editItem` action, updating the `amount` for the cart item identified by `cartID`.\n\n5. **Destructure cartItem Prop**:\n\n   - Extract necessary fields from `cartItem` including `cartID`, `title`, `price`, `image`, `amount`, `company`, and `productColor`.\n\n6. **Component Structure**:\n\n   - Create an `article` element wrapping the entire cart item.\n     - Embed the product image using an `img` element.\n     - Display product information using a `div`:\n       - Show the product `title`, `company`, and `color` using corresponding elements.\n     - Render the product amount using a `div` containing a dropdown `select`.\n     - Provide a `button` to remove the item from the cart.\n     - Display the product price using a `p` element.\n\n7. **Export CartItem Component**:\n\n   - Export the `CartItem` component as the default export of the module.\n\n## Solution (39) - Cart Items List\n\nCartItemsList.jsx\n\n```js\nimport { useSelector } from \"react-redux\";\nimport CartItem from \"./CartItem\";\nconst CartItemsList = () =\u003e {\n  const cartItems = useSelector((state) =\u003e state.cartState.cartItems);\n\n  return (\n    \u003cdiv\u003e\n      {cartItems.map((item) =\u003e {\n        return \u003cCartItem key={item.cartID} cartItem={item} /\u003e;\n      })}\n    \u003c/div\u003e\n  );\n};\nexport default CartItemsList;\n```\n\nCartItem.jsx\n\n```js\nimport { formatPrice, generateAmountOptions } from \"../utils\";\nimport { removeItem, editItem } from \"../features/cart/cartSlice\";\nimport { useDispatch } from \"react-redux\";\nconst CartItem = ({ cartItem }) =\u003e {\n  const dispatch = useDispatch();\n\n  const removeItemFromTheCart = () =\u003e {\n    dispatch(removeItem({ cartID }));\n  };\n  const handleAmount = (e) =\u003e {\n    dispatch(editItem({ cartID, amount: parseInt(e.target.value) }));\n  };\n\n  const { cartID, title, price, image, amount, company, productColor } =\n    cartItem;\n\n  return (\n    \u003carticle\n      key={cartID}\n      className=\"mb-12 flex flex-col gap-y-4 sm:flex-row flex-wrap border-b border-base-300 pb-6 last:border-b-0\"\n    \u003e\n      {/* IMAGE */}\n      \u003cimg\n        src={image}\n        alt={title}\n        className=\"h-24 w-24 rounded-lg sm:h-32 sm:w-32 object-cover\"\n      /\u003e\n      {/* INFO */}\n      \u003cdiv className=\"sm:ml-16 sm:w-48\"\u003e\n        {/* TITLE */}\n        \u003ch3 className=\"capitalize font-medium\"\u003e{title}\u003c/h3\u003e\n        {/* COMPANY */}\n        \u003ch4 className=\"mt-2 capitalize text-sm text-neutral-content\"\u003e\n          {company}\n        \u003c/h4\u003e\n        {/* COLOR */}\n        \u003cp className=\"mt-4 text-sm capitalize flex items-center gap-x-2\"\u003e\n          color :\n          \u003cspan\n            className=\"badge badge-sm\"\n            style={{ backgroundColor: productColor }}\n          \u003e\u003c/span\u003e\n        \u003c/p\u003e\n      \u003c/div\u003e\n      \u003cdiv className=\"sm:ml-12\"\u003e\n        {/* AMOUNT */}\n        \u003cdiv className=\"form-control max-w-xs\"\u003e\n          \u003clabel htmlFor=\"amount\" className=\"label p-0\"\u003e\n            \u003cspan className=\"label-text\"\u003eAmount\u003c/span\u003e\n          \u003c/label\u003e\n          \u003cselect\n            name=\"amount\"\n            id=\"amount\"\n            className=\"mt-2 select select-base select-bordered select-xs\"\n            value={amount}\n            onChange={handleAmount}\n          \u003e\n            {generateAmountOptions(amount + 5)}\n          \u003c/select\u003e\n        \u003c/div\u003e\n        {/* REMOVE */}\n        \u003cbutton\n          className=\"mt-2 link link-primary link-hover text-sm\"\n          onClick={removeItemFromTheCart}\n        \u003e\n          remove\n        \u003c/button\u003e\n      \u003c/div\u003e\n\n      {/* PRICE */}\n      \u003cp className=\"font-medium sm:ml-auto\"\u003e{formatPrice(price)}\u003c/p\u003e\n    \u003c/article\u003e\n  );\n};\nexport default CartItem;\n```\n\nCartItem.jsx\n\n```js\n\u003cdiv className=\"sm:ml-12\"\u003e\n  {/* AMOUNT */}\n  {/* REMOVE */}\n\u003c/div\u003e\n```\n\n## Challenge (40) - Setup User Slice\n\n- setup user slice\n- add to store\n\n### userSlice.js\n\n- create features/user/userSlice.js\n- Import Dependencies:\n\n  - Import `createSlice` from `'@reduxjs/toolkit'`.\n  - Import `toast` from `'react-toastify'`.\n\n- Define Initial State:\n\n  - Create an `initialState` object with default values for `user` and `theme`.\n\n- Create Redux Slice:\n\n  - Use `createSlice` to define a Redux slice named `'user'`.\n  - Set the slice name to `'user'`.\n  - Use the `initialState` object as the initial state.\n\n- Define Reducer Functions:\n\n  - Create the `loginUser` reducer function with the signature `(state, action) =\u003e {}`.\n\n    - Inside the function, log a message like `'login'`.\n\n  - Create the `logoutUser` reducer function with the signature `(state) =\u003e {}`.\n\n    - Inside the function, log a message like `'logout'`.\n\n  - Create the `toggleTheme` reducer function with the signature `(state) =\u003e {}`.\n    - Inside the function, log a message like `'toggle theme'`.\n\n- Export Actions:\n  - Export the action creators:\n    - `loginUser`\n    - `logoutUser`\n    - `toggleTheme`\n\n## Solution (40) - Setup User Slice\n\n```js\nimport { createSlice } from \"@reduxjs/toolkit\";\nimport { toast } from \"react-toastify\";\n\nconst initialState = {\n  user: { username: \"coding addict\" },\n  theme: \"dracula\",\n};\n\nconst userSlice = createSlice({\n  name: \"user\",\n  initialState,\n  reducers: {\n    loginUser: (state, action) =\u003e {\n      console.log(\"login\");\n    },\n    logoutUser: (state) =\u003e {\n      console.log(\"logout\");\n    },\n    toggleTheme: (state) =\u003e {\n      console.log(\"toggle theme\");\n    },\n  },\n});\n\nexport const { loginUser, logoutUser, toggleTheme } = userSlice.actions;\n\nexport default userSlice.reducer;\n```\n\nstore.js\n\n```js\nimport { configureStore } from \"@reduxjs/toolkit\";\n\nimport cartReducer from \"./features/cart/cartSlice\";\nimport userReducer from \"./features/user/userSlice\";\n\nexport const store = configureStore({\n  reducer: {\n    cartState: cartReducer,\n    userState: userReducer,\n  },\n});\n```\n\n## Challenge (41) - Move Theme Logic\n\n- move theme logic from Navbar to userSlice\n\n## Solution (41) - Move Theme Logic\n\nuserSlice.js\n\n```js\nimport { createSlice } from \"@reduxjs/toolkit\";\nimport { toast } from \"react-toastify\";\n\nconst themes = {\n  winter: \"winter\",\n  dracula: \"dracula\",\n};\n\nconst getThemeFromLocalStorage = () =\u003e {\n  const theme = localStorage.getItem(\"theme\") || themes.winter;\n  document.documentElement.setAttribute(\"data-theme\", theme);\n  return theme;\n};\n\nconst initialState = {\n  user: { username: \"coding addict\" },\n  theme: getThemeFromLocalStorage(),\n};\n\nconst userSlice = createSlice({\n  name: \"user\",\n  initialState,\n  reducers: {\n    loginUser: (state, action) =\u003e {\n      console.log(\"login\");\n    },\n    logoutUser: (state) =\u003e {\n      console.log(\"logout\");\n    },\n    toggleTheme: (state) =\u003e {\n      const { dracula, winter } = themes;\n      state.theme = state.theme === dracula ? winter : dracula;\n      document.documentElement.setAttribute(\"data-theme\", state.theme);\n      localStorage.setItem(\"theme\", state.theme);\n    },\n  },\n});\n\nexport const { loginUser, logoutUser, toggleTheme } = userSlice.actions;\n\nexport default userSlice.reducer;\n```\n\nNavbar.js\n\n```js\nimport { BsCart3, BsMoonFill, BsSunFill } from \"react-icons/bs\";\nimport { FaBarsStaggered } from \"react-icons/fa6\";\nimport { NavLink } from \"react-router-dom\";\nimport NavLinks from \"./NavLinks\";\n\nimport { useDispatch, useSelector } from \"react-redux\";\nimport { toggleTheme } from \"../features/user/userSlice\";\n\nconst Navbar = () =\u003e {\n  const numItemsInCart = useSelector((state) =\u003e state.cartState.numItemsInCart);\n\n  const dispatch = useDispatch();\n  const handleTheme = () =\u003e {\n    dispatch(toggleTheme());\n  };\n  return (\n    \u003cnav className=\"bg-base-200\"\u003e\n      \u003cdiv className=\"navbar align-element \"\u003e\n        \u003cdiv className=\"navbar-start\"\u003e\n          {/* Title */}\n          \u003cNavLink\n            to=\"/\"\n            className=\"hidden lg:flex btn btn-primary text-3xl items-center \"\n          \u003e\n            C\n          \u003c/NavLink\u003e\n          {/* DROPDOWN */}\n          \u003cdiv className=\"dropdown\"\u003e\n            \u003clabel tabIndex={0} className=\"btn btn-ghost lg:hidden\"\u003e\n              \u003cFaBarsStaggered className=\"h-6 w-6\" /\u003e\n            \u003c/label\u003e\n            \u003cul\n              tabIndex={0}\n              className=\"menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-200 rounded-box w-52\"\n            \u003e\n              \u003cNavLinks /\u003e\n            \u003c/ul\u003e\n          \u003c/div\u003e\n        \u003c/div\u003e\n        \u003cdiv className=\"navbar-center hidden lg:flex\"\u003e\n          \u003cul className=\"menu menu-horizontal \"\u003e\n            \u003cNavLinks /\u003e\n          \u003c/ul\u003e\n        \u003c/div\u003e\n        \u003cdiv className=\"navbar-end\"\u003e\n          {/* THEME ICONS */}\n          \u003clabel className=\"swap swap-rotate \"\u003e\n            {/* this hidden checkbox controls the state */}\n            \u003cinput type=\"checkbox\" onChange={handleTheme} /\u003e\n\n            {/* sun icon */}\n            \u003cBsSunFill className=\"swap-on h-4 w-4\" /\u003e\n\n            {/* moon icon */}\n            \u003cBsMoonFill className=\"swap-off h-4 w-4\" /\u003e\n          \u003c/label\u003e\n          {/* CART LINK*/}\n          \u003cNavLink to=\"cart\" className=\"btn btn-ghost btn-circle btn-md ml-4\"\u003e\n            \u003cdiv className=\"indicator\"\u003e\n              \u003cBsCart3 className=\"h-6 w-6\" /\u003e\n              \u003cspan className=\"badge badge-sm badge-primary indicator-item\"\u003e\n                {numItemsInCart}\n              \u003c/span\u003e\n            \u003c/div\u003e\n          \u003c/NavLink\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/nav\u003e\n  );\n};\nexport default Navbar;\n```\n\n## Additional Logic\n\nNavbar.jsx\n\n```js\nimport { useDispatch, useSelector } from 'react-redux';\nimport { toggleTheme } from '../features/user/userSlice';\n\nconst Navbar = () =\u003e {\n  const theme = useSelector((state) =\u003e state.userState.theme);\n  const isDarkTheme = theme === 'dracula';\n\n  return (...\n          {/* THEME SETUP */}\n          \u003clabel className='swap swap-rotate'\u003e\n            \u003cinput\n              type='checkbox'\n              onChange={handleTheme}\n              defaultChecked={isDarkTheme}\n            /\u003e\n            {/* sun icon*/}\n            \u003cBsSunFill className='swap-on h-4 w-4' /\u003e\n            {/* moon icon*/}\n            \u003cBsMoonFill className='swap-off h-4 w-4' /\u003e\n          \u003c/label\u003e\n          );\n          ...\n};\nexport default Navbar;\n```\n\n## Challenge (42) - Setup Logout and Access User\n\n- setup logout reducer\n- access user in Header, NavLinks and Cart Page\n\n## Solution (42) - Setup Logout And Access User\n\nuserSlice.js\n\n```js\nlogoutUser: (state) =\u003e {\n      state.user = null;\n      // localStorage.clear()\n      localStorage.removeItem('user');\n      toast.success('Logged out successfully');\n    },\n```\n\nHeader.js\n\n```js\nimport { useDispatch, useSelector } from \"react-redux\";\nimport { Link, useNavigate } from \"react-router-dom\";\nimport { logoutUser } from \"../features/user/userSlice\";\nimport { clearCart } from \"../features/cart/cartSlice\";\nconst Header = () =\u003e {\n  const navigate = useNavigate();\n  const dispatch = useDispatch();\n  const user = useSelector((state) =\u003e state.userState.user);\n\n  const handleLogout = () =\u003e {\n    navigate(\"/\");\n    dispatch(clearCart());\n    dispatch(logoutUser());\n  };\n  return (\n    \u003cheader className=\" bg-neutral py-2 text-neutral-content \"\u003e\n      \u003cdiv className=\"align-element flex justify-center sm:justify-end \"\u003e\n        {user ? (\n          \u003cdiv className=\"flex gap-x-2 sm:gap-x-8 items-center\"\u003e\n            \u003cp className=\"text-xs sm:text-sm\"\u003eHello, {user.username}\u003c/p\u003e\n            \u003cbutton\n              className=\"btn btn-xs btn-outline btn-primary \"\n              onClick={handleLogout}\n            \u003e\n              logout\n            \u003c/button\u003e\n          \u003c/div\u003e\n        ) : (\n          \u003cdiv className=\"flex gap-x-6 justify-center items-center\"\u003e\n            \u003cLink to=\"/login\" className=\"link link-hover text-xs sm:text-sm\"\u003e\n              Sign in / Guest\n            \u003c/Link\u003e\n            \u003cLink to=\"/register\" className=\"link link-hover text-xs sm:text-sm\"\u003e\n              Create an Account\n            \u003c/Link\u003e\n          \u003c/div\u003e\n        )}\n      \u003c/div\u003e\n    \u003c/header\u003e\n  );\n};\nexport default Header;\n```\n\nNavLinks.jsx\n\n```js\nimport { useSelector } from \"react-redux\";\nimport { NavLink } from \"react-router-dom\";\n\nconst NavLinks = () =\u003e {\n  const user = useSelector((state) =\u003e state.userState.user);\n\n  return (\n    \u003c\u003e\n      {links.map((link) =\u003e {\n        const { id, url, text } = link;\n        if ((url === \"checkout\" || url === \"orders\") \u0026\u0026 !user) return null;\n        return (\n          \u003cli key={id}\u003e\n            \u003cNavLink className=\"capitalize\" to={url}\u003e\n              {text}\n            \u003c/NavLink\u003e\n          \u003c/li\u003e\n        );\n      })}\n    \u003c/\u003e\n  );\n};\nexport default NavLinks;\n```\n\n```js\nconst Cart = () =\u003e {\n  // temp\n  const { user } = useSelector((state) =\u003e state.userState);\n};\n```\n\n## Challenge (43) - Register User\n\n- [API DOCS](https://documenter.getpostman.com/view/18152321/2s9Xy5KpTi)\n- docs - register request\n- test in Thunder Client\n- setup action in Register\n- add action to Register route in App.jsx\n\n### Register.js\n\n- Import Dependencies:\n\n  - Import `redirect` from `'react-router-dom'`.\n  - Import `customFetch` from `'../utils'`.\n  - Import `toast` from `'react-toastify'`.\n\n- Define an asynchronous function named `action` that takes an object with a property named `request` as its parameter.\n\n- Inside the `action` function:\n\n  - Use the `request` object to get form data using the `formData` method.\n  - Convert the form data to an object using `Object.fromEntries(formData)` and store it in the `data` variable.\n\n- Use a `try` block to handle the registration process:\n\n  - Send a POST request using `customFetch.post` to the `/auth/local/register` endpoint with the `data`.\n  - If the request is successful:\n    - Display a success toast message using `toast.success`.\n    - Redirect the user to the `/login` page using `redirect('/login')`.\n\n- Use a `catch` block to handle errors:\n  - If an error occurs, extract the error message from the response data, if available, or provide a default error message.\n  - Display the error message using `toast.error`.\n  - Return `null` to indicate that an error occurred.\n\n## Solution (43) - Register User\n\nRegister.jsx\n\n```js\nimport { FormInput, SubmitBtn } from \"../components\";\nimport { Form, redirect, Link } from \"react-router-dom\";\n\nimport { customFetch } from \"../utils\";\nimport { toast } from \"react-toastify\";\nexport const action = async ({ request }) =\u003e {\n  const formData = await request.formData();\n  const data = Object.fromEntries(formData);\n  try {\n    const response = await customFetch.post(\"/auth/local/register\", data);\n    toast.success(\"account created successfully\");\n    return redirect(\"/login\");\n  } catch (error) {\n    const errorMessage =\n      error?.response?.data?.error?.message ||\n      \"please double check your credentials\";\n\n    toast.error(errorMessage);\n    return null;\n  }\n};\n\nconst Register = () =\u003e {\n  return (\n    \u003csection className=\"h-screen grid place-items-center\"\u003e\n      \u003cForm\n        method=\"POST\"\n        className=\"card w-96 py-8 px-8 bg-base-100 shadow-lg flex flex-col gap-y-4\"\n      \u003e\n        \u003ch4 className=\"text-center text-3xl font-bold\"\u003eRegister\u003c/h4\u003e\n        \u003cFormInput type=\"text\" label=\"username\" name=\"username\" /\u003e\n        \u003cFormInput type=\"email\" label=\"email\" name=\"email\" /\u003e\n        \u003cFormInput type=\"password\" label=\"password\" name=\"password\" /\u003e\n        \u003cdiv className=\"mt-4\"\u003e\n          \u003cSubmitBtn text=\"register\" /\u003e\n        \u003c/div\u003e\n\n        \u003cp className=\"text-center\"\u003e\n          Already a member?\n          \u003cLink\n            to=\"/login\"\n            className=\"ml-2 link link-hover link-primary capitalize\"\n          \u003e\n            login\n          \u003c/Link\u003e\n        \u003c/p\u003e\n      \u003c/Form\u003e\n    \u003c/section\u003e\n  );\n};\nexport default Register;\n```\n\n## Challenge (44) - Login Setup\n\n- [API DOCS](https://documenter.getpostman.com/view/18152321/2s9Xy5KpTi)\n- docs - login request\n- test in Thunder Client\n- setup action in login and access store\n\n## Solution (44) - Login Setup\n\nApp.jsx\n\n```js\nimport { action as loginAction } from './pages/Login';\n\nimport { store } from './store';\n{\n    path: '/login',\n    element: \u003cLogin /\u003e,\n    errorElement: \u003cError /\u003e,\n    action: loginAction(store),\n  },\n```\n\nLogin.jsx\n\n```js\nexport const action =\n  (store) =\u003e\n  async ({ request }) =\u003e {\n    console.log(store);\n    return store;\n  };\n```\n\n### Challenge (45) - Login User\n\n### Login.jsx\n\n- Import Dependencies:\n\n  - Import `redirect` from `'react-router-dom'`.\n  - Import `customFetch` from `'../utils'`.\n  - Import `toast` from `'react-toastify'`.\n  - Import `loginUser` from `'../features/user/userSlice'`.\n  - Import `useDispatch` from `'react-redux'`.\n\n- Define a function named `action` that takes a parameter `store` and returns an asynchronous function that takes an object with a property named `request`.\n\n- Inside the inner asynchronous function:\n\n  - Use the `request` object to get form data using the `formData` method.\n  - Convert the form data to an object using `Object.fromEntries(formData)` and store it in the `data` variable.\n\n- Use a `try` block to handle the login process:\n\n  - Send a POST request using `customFetch.post` to the `/auth/local` endpoint with the `data`.\n  - If the request is successful:\n    - Dispatch the `loginUser` action with the response data using `store.dispatch`.\n    - Display a success toast message using `toast.success`.\n    - Redirect the user to the home page using `redirect('/')`.\n\n- Use a `catch` block to handle errors:\n  - If an error occurs, log the error to the console.\n  - Extract the error message from the response data, if available, or provide a default error message.\n  - Display the error message using `toast.error`.\n  - Return `null` to indicate that an error occurred.\n\n### Solution (45) - Login User\n\nuserSlice.js\n\n```js\nloginUser: (state, action) =\u003e {\n      console.log(action.payload);\n    },\n```\n\nLogin.jsx\n\n```js\nimport { FormInput, SubmitBtn } from \"../components\";\nimport { Form, Link, redirect, useNavigate } from \"react-router-dom\";\nimport { customFetch } from \"../utils\";\nimport { toast } from \"react-toastify\";\nimport { loginUser } from \"../features/user/userSlice\";\nimport { useDispatch } from \"react-redux\";\n\nexport const action =\n  (store) =\u003e\n  async ({ request }) =\u003e {\n    const formData = await request.formData();\n    const data = Object.fromEntries(formData);\n    try {\n      const response = await customFetch.post(\"/auth/local\", data);\n\n      store.dispatch(loginUser(response.data));\n      toast.success(\"logged in successfully\");\n      return redirect(\"/\");\n    } catch (error) {\n      console.log(error);\n      const errorMessage =\n        error?.response?.data?.error?.message ||\n        \"please double check your credentials\";\n\n      toast.error(errorMessage);\n      return null;\n    }\n  };\n```\n\nuserSlice.js\n\n```js\nconst getUserFromLocalStorage = () =\u003e {\n  return JSON.parse(localStorage.getItem('user')) || null;\n};\n\nconst initialState = {\n  user: getUserFromLocalStorage(),\n  theme: getThemeFromLocalStorage(),\n};\n\nloginUser: (state, action) =\u003e {\n      const user = { ...action.payload.user, token: action.payload.jwt };\n      state.user = user;\n      localStorage.setItem('user', JSON.stringify(user));\n    },\n```\n\n## Challenge (46) - Demo User\n\n- remove defaultValue from inputs\n\n### loginAsGuestUser\n\n- Create a function named `loginAsGuestUser`.\n- Mark the function as `async` to indicate that it contains asynchronous operations.\n- Wrap the entire function body in a `try` block to handle potential errors.\n- Inside the `try` block, use the `customFetch.post` method to send a POST request.\n- Provide the endpoint URL `/auth/local`.\n- Pass an object with `identifier` and `password` properties as the request body.\n- Assign the response from the `customFetch.post` call to the `response` variable.- If the request is successful, dispatch an action (e.g., `loginUser`) with the `response.data`.\n- Use the `toast.success` method to display a success message (e.g., 'welcome guest user').\n- If the action dispatch and toast success are successful, use the `navigate` function to navigate to a specific route (e.g., `'/'`).\n- If any error occurs within the `try` block, it will be caught by the `catch` block.\n- Inside the `catch` block, use `console.log` to log the error for debugging purposes.\n- Display an error message using the `toast.error` method to notify the user about the login error.\n\n## Solution (46) - Demo User\n\nLogin.jsx\n\n```js\nconst Login = () =\u003e {\n  const dispatch = useDispatch();\n  const navigate = useNavigate();\n  const loginAsGuestUser = async () =\u003e {\n    try {\n      const response = await customFetch.post(\"/auth/local\", {\n        identifier: \"test@test.com\",\n        password: \"secret\",\n      });\n      dispatch(loginUser(response.data));\n      toast.success(\"welcome guest user\");\n      navigate(\"/\");\n    } catch (error) {\n      console.log(error);\n      toast.error(\"guest user login error.please try later.\");\n    }\n  };\n};\n\n\u003cbutton\n  type=\"button\"\n  className=\"btn btn-secondary btn-block\"\n  onClick={loginAsGuestUser}\n\u003e\n  guest user\n\u003c/button\u003e;\n```\n\n## Challenge (47) - Checkout Page Setup\n\n- create CheckoutForm component\n\n### Checkout.jsx\n\n- Import Dependencies:\n\n  - Import `useSelector` from `'react-redux'`.\n  - Import `CheckoutForm`, `SectionTitle`, and `CartTotals` from `'../components'`.\n\n- Create the `Checkout` component:\n\n  - Inside the component, use `useSelector` to access the `cartTotal` from the Redux store.\n  - Check if the `cartTotal` is empty.\n  - If the `cartTotal` is empty, return a `SectionTitle` component with the text 'Your cart is empty'.\n  - If the `cartTotal` is not empty:\n    - Return a `SectionTitle` component with the text 'Place your order'.\n    - Render a `\u003cdiv\u003e` element with the class name 'mt-8 grid gap-8 md:grid-cols-2 items-start'.\n    - Inside the `\u003cdiv\u003e`, render the `CheckoutForm` component and the `CartTotals` component.\n\n- Export the `Checkout` component as the default export.\n\n## Solution (47) - Checkout Page Setup\n\nCheckout.jsx\n\n```js\nimport { useSelector } from \"react-redux\";\nimport { CheckoutForm, SectionTitle, CartTotals } from \"../components\";\n\nconst Checkout = () =\u003e {\n  const cartItems = useSelector((state) =\u003e state.cartState.cartTotal);\n  if (cartTotal.length === 0) {\n    return \u003cSectionTitle text=\"Your cart is empty\" /\u003e;\n  }\n  return (\n    \u003c\u003e\n      \u003cSectionTitle text=\"Place your order\" /\u003e\n      \u003cdiv className=\"mt-8 grid gap-8  md:grid-cols-2 items-start\"\u003e\n        \u003cCheckoutForm /\u003e\n        \u003cCartTotals /\u003e\n      \u003c/div\u003e\n    \u003c/\u003e\n  );\n};\nexport default Checkout;\n```\n\n## Challenge (48) - Restrict Access\n\nApp.jsx\n\n- in App.jsx import loader from Checkout page\n- pass store into the checkoutLoader\n- if no user redirect to login\n\n### Checkout.jsx\n\n- Import Dependencies:\n\n  - Import `redirect` from `'react-router-dom'`.\n  - Import `toast` from `'react-toastify'`.\n\n- Create a `loader` function:\n\n  - The `loader` function takes a `store` as a parameter.\n  - Inside the `loader` function:\n    - Get the `user` from the Redux store using `store.getState().userState.user`.\n    - Check if the `user` is falsy (not logged in).\n    - If the `user` is falsy:\n      - Display a toast warning message using `toast.warn()` with the text 'You must be logged in to checkout'.\n      - Return `redirect('/login')` to redirect the user to the login page.\n    - If the `user` is truthy (logged in):\n      - Return `null`.\n\n- Export the `loader` function.\n\n## Solution (48) - Restrict Access\n\nApp.jsx\n\n```js\nimport { loader as checkoutLoader } from './pages/Checkout';\n\nimport { store } from './store';\n\nconst router = createBrowserRouter([\n  {\n   ....\n      {\n        path: 'checkout',\n        element: \u003cCheckout /\u003e,\n        loader: checkoutLoader(store),\n\n      },\n\n  },\n\n]);\n\n```\n\nCheckout.jsx\n\n```js\nimport { redirect } from \"react-router-dom\";\nimport { toast } from \"react-toastify\";\n\nexport const loader = (store) =\u003e async () =\u003e {\n  const user = store.getState().userState.user;\n\n  if (!user) {\n    toast.warn(\"You must be logged in to checkout\");\n    return redirect(\"/login\");\n  }\n  return null;\n};\n```\n\n## Challenge (49) - CheckoutForm\n\n- [API DOCS](https://documenter.getpostman.com/view/18152321/2s9Xy5KpTi)\n- docs - create order request\n- test in Thunder Client\n\n### App.jsx\n\n- in App.jsx import action from CheckoutForm.jsx\n- pass store into the checkoutAction\n\n## CheckoutForm.jsx\n\n- Import Dependencies:\n\n  - Import `Form` and `redirect` from `'react-router-dom'`.\n  - Import `FormInput` and `SubmitBtn` from appropriate paths.\n  - Import other required utilities and actions.\n\n- Create an `action` function:\n\n  - The `action` function takes a `store` as a parameter and returns an asynchronous function that takes a `request` parameter.\n  - Inside the async function:\n    - Await `request.formData()` to get form data.\n    - Destructure the `name` and `address` properties from the form data using `Object.fromEntries(formData)`.\n    - Get the `user` from the Redux store using `store.getState().userState.user`.\n    - Get the `cartItems`, `orderTotal`, and `numItemsInCart` from the Redux store using `store.getState().cartState`.\n    - Create an `info` object containing the gathered information.\n    - Try to make a POST request to '/orders' with the `info` data and the user's token in the headers.\n    - If successful:\n      - Dispatch the `clearCart()` action using `store.dispatch(clearCart())` to clear the cart.\n      - Display a success toast message using `toast.success()` with the text 'order placed successfully'.\n      - Return `redirect('/orders')` to redirect the user to the orders page.\n    - If there's an error:\n      - Log the error.\n      - Get the error message from the response data or provide a default message.\n      - Display an error toast message using `toast.error()` with the error message.\n      - Return `null`.\n\n- Create a `CheckoutForm` component:\n\n  - Inside the component:\n    - Use the `Form` component from 'react-router-dom' to create a form.\n    - Display a heading for the shipping information.\n    - Use the `FormInput` component to create input fields for the first name and address.\n    - Use the `SubmitBtn` component to create a submit button with the text 'Place Your Order'.\n\n- Export the `CheckoutForm` component.\n\n## Solution (49) - CheckoutForm\n\nApp.jsx\n\n```js\nimport { action as checkoutAction } from \"./components/CheckoutForm\";\nimport { store } from \"./store\";\n\nconst router = createBrowserRouter([\n  {\n    path: \"/\",\n    element: \u003cHomeLayout /\u003e,\n    errorElement: \u003cError /\u003e,\n    children: [\n      {\n        path: \"checkout\",\n        element: \u003cCheckout /\u003e,\n        loader: checkoutLoader(store),\n        action: checkoutAction(store),\n      },\n    ],\n  },\n]);\n```\n\nCheckoutForm.jsx\n\n```js\nimport { Form, redirect } from \"react-router-dom\";\nimport FormInput from \"./FormInput\";\nimport SubmitBtn from \"./SubmitBtn\";\nimport { customFetch, formatPrice } from \"../utils\";\nimport { toast } from \"react-toastify\";\nimport { clearCart } from \"../features/cart/cartSlice\";\n\nexport const action =\n  (store) =\u003e\n  async ({ request }) =\u003e {\n    const formData = await request.formData();\n    const { name, address } = Object.fromEntries(formData);\n    const user = store.getState().userState.user;\n    const { cartItems, orderTotal, numItemsInCart } =\n      store.getState().cartState;\n\n    const info = {\n      name,\n      address,\n      chargeTotal: orderTotal,\n      orderTotal: formatPrice(orderTotal),\n      cartItems,\n      numItemsInCart,\n    };\n    try {\n      const response = await customFetch.post(\n        \"/orders\",\n        { data: info },\n        {\n          headers: {\n            Authorization: `Bearer ${user.token}`,\n          },\n        }\n      );\n      store.dispatch(clearCart());\n      toast.success(\"order placed successfully\");\n      return redirect(\"/orders\");\n    } catch (error) {\n      console.log(error);\n      const errorMessage =\n        error?.response?.data?.error?.message ||\n        \"there was an error placing your order\";\n\n      toast.error(errorMessage);\n      return null;\n    }\n  };\nconst CheckoutForm = () =\u003e {\n  return (\n    \u003cForm method=\"POST\" className=\"flex flex-col gap-y-4\"\u003e\n      \u003ch4 className=\"font-medium text-xl\"\u003eShipping Information\u003c/h4\u003e\n      \u003cFormInput label=\"first name\" name=\"name\" type=\"text\" /\u003e\n      \u003cFormInput label=\"address\" name=\"address\" type=\"text\" /\u003e\n      \u003cdiv className=\"mt-4\"\u003e\n        \u003cSubmitBtn text=\"Place Your Order\" /\u003e\n      \u003c/div\u003e\n    \u003c/Form\u003e\n  );\n};\nexport default CheckoutForm;\n```\n\n## Challenge (50) - Auth Error\n\n- handle auth errors\n- check for response.status\n  - if status === 401 redirect to login\n\n## Solution (50) - Auth Error\n\nCheckoutForm.jsx\n\n```js\n\n catch (error) {\n  console.log(error);\n  const errorMessage =\n    error?.response?.data?.error?.message ||\n    'there was an error placing your order';\n  toast.error(errorMessage);\n  if (error?.response?.status === 401 || 403) return redirect('/login');\n\n  return null;\n}\n```\n\n## Challenge (51) - Orders Request Overview\n\n- [API DOCS](https://documenter.getpostman.com/view/18152321/2s9Xy5KpTi)\n- docs - orders request\n- test in Thunder Client\n\n## Solution (51) - Orders Request Overview\n\n## Challenge (52) - Orders Page Setup\n\n- create components/OrdersList.jsx (export)\n- create loader (import/setup in App.jsx and provide store)\n- restrict access to page\n- make a request to get all users\n- grab all the query params\n- return orders and meta\n\n### Orders.jsx\n\n1. **Import Dependencies:**\n\n   - Import the required modules and components from 'react-router-dom', 'react-toastify', and other custom files.\n\n2. **Define Loader Function:**\n\n   - Create a loader function that takes the `store` parameter and an object with a `request` property.\n   - Within the loader function:\n     - Retrieve the user information from the Redux store.\n     - Check if the user is logged in. If not, display a warning toast and redirect to the login page.\n     - Parse query parameters from the URL.\n     - Use the `customFetch` utility to make a GET request to the '/orders' endpoint.\n     - Handle successful responses by returning the fetched orders data and meta information.\n     - Handle errors by displaying appropriate error messages using toast and optionally redirecting if unauthorized.\n     - Return `null` if an error occurs.\n\n3. **Define Orders Component:**\n\n   - Create a functional component named `Orders`.\n   - Within the component:\n     - Return JSX that displays a heading element with the text \"orders\".\n\n4. **Export Loader Function:**\n\n   - Export the defined loader function.\n\n5. **Export Orders Component:**\n   - Export the `Orders` component as the default export of the module.\n\n## Solution (52) - Orders Page Setup\n\nApp.jsx\n\n```js\nimport { loader as ordersLoader } from './pages/Orders';\n\n{\n  path: 'orders',\n  element: \u003cOrders /\u003e,\n  loader: ordersLoader(store),\n},\n```\n\nOrders.jsx\n\n```js\nimport { redirect, useLoaderData } from \"react-router-dom\";\nimport { toast } from \"react-toastify\";\nimport { customFetch } from \"../utils\";\nimport { OrdersList, PaginationContainer, SectionTitle } from \"../components\";\n\nexport const loader =\n  (store) =\u003e\n  async ({ request }) =\u003e {\n    const user = store.getState().userState.user;\n\n    if (!user) {\n      toast.warn(\"You must be logged in to view orders\");\n      return redirect(\"/login\");\n    }\n    const params = Object.fromEntries([\n      ...new URL(request.url).searchParams.entries(),\n    ]);\n    try {\n      const response = await customFetch.get(\"/orders\", {\n        params,\n        headers: {\n          Authorization: `Bearer ${user.token}`,\n        },\n      });\n\n      return { orders: response.data.data, meta: response.data.meta };\n    } catch (error) {\n      console.log(error);\n      const errorMessage =\n        error?.response?.data?.error?.message ||\n        \"there was an error accessing your orders\";\n\n      toast.error(errorMessage);\n      if (error?.response?.status === 401 || 403) return redirect(\"/login\");\n\n      return null;\n    }\n  };\nconst Orders = () =\u003e {\n  return \u003ch1 className=\"text-3xl\"\u003eorders\u003c/h1\u003e;\n};\nexport default Orders;\n```\n\n## Challenge (53) - Render Orders\n\n### Orders.jsx\n\n- Import Dependencies:\n\n  - Import necessary components and hooks from your project's dependencies.\n\n- Define Component:\n\n  - Define a functional component named `Orders`.\n\n- Fetch Data:\n\n  - Use the `useLoaderData` hook to access data from the loader context.\n  - Check if the `meta.pagination.total` value is less than 1 to determine if there are no orders.\n\n- Conditional Rendering:\n\n  - If there are no orders, return a component, such as `\u003cSectionTitle /\u003e`, with a message like 'Please make an order'.\n\n- Orders Rendering:\n\n  - If there are orders, return the following components:\n    - `\u003cSectionTitle /\u003e` with the text 'Your Orders'.\n    - `\u003cOrdersList /\u003e` component to display the list of orders.\n    - `\u003cPaginationContainer /\u003e` component for handling pagination.\n\n- Export Component:\n  - Export the `Orders` component as the default export.\n\n### OrdersList.jsx\n\n- Import Dependencies:\n\n  - Import `useLoaderData` from `'react-router-dom'`.\n  - Import `day` and `advancedFormat` from `'dayjs'`.\n  - Extend dayjs with the `advancedFormat` plugin using `day.extend(advancedFormat)`.\n\n- Create the `OrdersList` component:\n\n  - Inside the component:\n    - Use the `useLoaderData()` hook to get data from loader data.\n    - Destructure `orders` and `meta` from the loaded data.\n    - Return the orders list interface:\n      - Display the total number of orders using the `meta.pagination.total` value.\n      - Create a table to display order information.\n      - Define table headings: Name, Address, Products, Cost, and Date.\n      - Use `.map()` to iterate over each order and generate table rows:\n        - Destructure relevant attributes from the `order` object.\n        - Format the `createdAt` date using dayjs to display in 'hh:mm a - MMM Do, YYYY' format.\n        - Return a table row with the extracted data.\n\n- Export the `OrdersList` component.\n\n## Solution (53) - Render Orders\n\nOrders.jsx\n\n```js\nconst Orders = () =\u003e {\n  const { meta } = useLoaderData();\n  if (meta.pagination.total \u003c 1) {\n    return \u003cSectionTitle text=\"Please make an order\" /\u003e;\n  }\n  return (\n    \u003c\u003e\n      \u003cSectionTitle text=\"Your Orders\" /\u003e\n      \u003cOrdersList /\u003e\n      \u003cPaginationContainer /\u003e\n    \u003c/\u003e\n  );\n};\nexport default Orders;\n```\n\nOrdersList.jsx\n\n```js\nimport { useLoaderData } from \"react-router-dom\";\nimport day from \"dayjs\";\nimport advancedFormat from \"dayjs/plugin/advancedFormat\";\nday.extend(advancedFormat);\n\nconst OrdersList = () =\u003e {\n  const { orders, meta } = useLoaderData();\n  return (\n    \u003cdiv className=\"mt-8\"\u003e\n      \u003ch4 className=\"mb-4 capitalize\"\u003e\n        total orders : {meta.pagination.total}\n      \u003c/h4\u003e\n      \u003cdiv className=\"overflow-x-auto \"\u003e\n        \u003ctable className=\"table table-zebra\"\u003e\n          {/* head */}\n          \u003cthead\u003e\n            \u003ctr\u003e\n              \u003cth\u003eName\u003c/th\u003e\n              \u003cth\u003eAddress\u003c/th\u003e\n              \u003cth\u003eProducts\u003c/th\u003e\n              \u003cth\u003eCost\u003c/th\u003e\n              \u003cth className=\"hidden sm:block\"\u003eDate\u003c/th\u003e\n            \u003c/tr\u003e\n          \u003c/thead\u003e\n          \u003ctbody\u003e\n            {orders.map((order) =\u003e {\n              const id = order.id;\n              const { name, address, numItemsInCart, orderTotal, createdAt } =\n                order.attributes;\n\n              const date = day(createdAt).format(\"hh:mm a - MMM Do, YYYY \");\n              return (\n                \u003ctr key={id}\u003e\n                  \u003ctd\u003e{name}\u003c/td\u003e\n                  \u003ctd\u003e{address}\u003c/td\u003e\n                  \u003ctd\u003e{numItemsInCart}\u003c/td\u003e\n                  \u003ctd\u003e{orderTotal}\u003c/td\u003e\n                  \u003ctd className=\"hidden sm:block\"\u003e{date}\u003c/td\u003e\n                \u003c/tr\u003e\n              );\n            })}\n          \u003c/tbody\u003e\n        \u003c/table\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  );\n};\nexport default OrdersList;\n```\n\n## Challenge (54) - Complex Pagination\n\n- create ComplexPaginationContainer.jsx\n- render in Orders.jsx\n\n### ComplexPaginationContainer.jsx\n\n- Import Dependencies:\n\n  - Import `useLoaderData`, `useLocation`, and `useNavigate` from `'react-router-dom'`.\n\n- Create the `ComplexPaginationContainer` component:\n\n  - Inside the component:\n    - Use the `useLoaderData()` hook to get data from loader data.\n    - Destructure `meta.pagination` to get `pageCount` and `page`.\n    - Use the `useLocation()` hook to get the current location's search and pathname.\n    - Use the `useNavigate()` hook to get the navigation function.\n    - Create a `handlePageChange` function that:\n      - Constructs a new URLSearchParams from the current search.\n      - Sets the 'page' parameter in the search to the specified `pageNumber`.\n      - Uses the navigate function to change the URL with the updated search.\n    - Create an `addPageButton` function that returns a button element with the appropriate classes and onClick handler.\n    - Create a `renderPageButtons` function that generates an array of page buttons, including ellipses and the current page button.\n    - Use conditional checks to handle rendering of first, last, and ellipsis buttons.\n    - Return `null` if `pageCount` is less than 2.\n    - Return the pagination interface containing the \"Prev,\" page buttons, and \"Next\" buttons.\n\n- Export the `ComplexPaginationContainer` component.\n\n## Solution (54) - Complex Pagination\n\nComplexPaginationContainer.jsx\n\n```js\nimport { useLoaderData, useLocation, useNavigate } from \"react-router-dom\";\n\nconst ComplexPaginationContainer = () =\u003e {\n  const { meta } = useLoaderData();\n  const { pageCount, page } = meta.pagination;\n\n  const { search, pathname } = useLocation();\n  const navigate = useNavigate();\n  const handlePageChange = (pageNumber) =\u003e {\n    const searchParams = new URLSearchParams(search);\n    searchParams.set(\"page\", pageNumber);\n    navigate(`${pathname}?${searchParams.toString()}`);\n  };\n\n  const addPageButton = ({ pageNumber, activeClass }) =\u003e {\n    return (\n      \u003cbutton\n        key={pageNumber}\n        onClick={() =\u003e handlePageChange(pageNumber)}\n        className={`btn btn-xs sm:btn-md border-none join-item ${\n          activeClass ? \"bg-base-300 border-base-300 \" : \"\"\n        }`}\n      \u003e\n        {pageNumber}\n      \u003c/button\u003e\n    );\n  };\n\n  const renderPageButtons = () =\u003e {\n    const pageButtons = [];\n    // first button\n    pageButtons.push(addPageButton({ pageNumber: 1, activeClass: page === 1 }));\n\n    // dots\n    if (page \u003e 2) {\n      pageButtons.push(\n        \u003cbutton className=\"join-item btn btn-xs sm:btn-md\" key=\"dots-1\"\u003e\n          ...\n        \u003c/button\u003e\n      );\n    }\n\n    // active/current page\n    if (page !== 1 \u0026\u0026 page !== pageCount) {\n      pageButtons.push(addPageButton({ pageNumber: page, activeClass: true }));\n    }\n    // dots\n    if (page \u003c pageCount - 1) {\n      pageButtons.push(\n        \u003cbutton className=\"join-item btn btn-xs sm:btn-md\" key=\"dots-2\"\u003e\n          ...\n        \u003c/button\u003e\n      );\n    }\n\n    // last button\n    pageButtons.push(\n      addPageButton({ pageNumber: pageCount, activeClass: page === pageCount })\n    );\n    return pageButtons;\n  };\n\n  if (pageCount \u003c 2) return null;\n\n  return (\n    \u003cdiv className=\"mt-16 flex justify-end\"\u003e\n      \u003cdiv className=\"join\"\u003e\n        \u003cbutton\n          className=\"btn btn-xs sm:btn-md join-item\"\n          onClick={() =\u003e {\n            let prevPage = page - 1;\n            if (prevPage \u003c 1) prevPage = pageCount;\n            handlePageChange(prevPage);\n          }}\n        \u003e\n          Prev\n        \u003c/button\u003e\n        {renderPageButtons()}\n        \u003cbutton\n          className=\"btn btn-xs sm:btn-md join-item\"\n          onClick={() =\u003e {\n            let nextPage = page + 1;\n            if (nextPage \u003e pageCount) nextPage = 1;\n            handlePageChange(nextPage);\n          }}\n        \u003e\n          Next\n        \u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  );\n};\nexport default ComplexPaginationContainer;\n```\n\n## Challenge (55) - Setup React Query\n\n- import and setup react query in App.jsx\n- pass query client down to\n  - Landing Page\n  - SingleProduct Page\n  - Products Page\n- refactor loaders\n\n## Solution (55) - Setup React Query\n\nApp.jsx\n\n```js\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport { ReactQueryDevtools } from \"@tanstack/react-query-devtools\";\n\nconst queryClient = new QueryClient({\n  defaultOptions: {\n    queries: {\n      staleTime: 1000 * 60 * 5,\n    },\n  },\n});\n\nconst router = createBrowserRouter([\n  {\n    path: \"/\",\n    element: \u003cHomeLayout /\u003e,\n    errorElement: \u003cError /\u003e,\n    children: [\n      {\n        index: true,\n        element: \u003cLanding /\u003e,\n        loader: landingLoader(queryClient),\n        errorElement: \u003cErrorElement /\u003e,\n      },\n      {\n        path: \"products\",\n        element: \u003cProducts /\u003e,\n        loader: productsLoader(queryClient),\n        errorElement: \u003cErrorElement /\u003e,\n      },\n      {\n        path: \"products/:id\",\n        element: \u003cSingleProduct /\u003e,\n        loader: singleProductLoader(queryClient),\n        errorElement: \u003cErrorElement /\u003e,\n      },\n      {\n        path: \"checkout\",\n        element: \u003cCheckout /\u003e,\n        loader: checkoutLoader(store),\n        action: checkoutAction(store, queryClient),\n      },\n      {\n        path: \"orders\",\n        element: \u003cOrders /\u003e,\n        loader: ordersLoader(store, queryClient),\n      },\n    ],\n  },\n]);\n\nconst App = () =\u003e {\n  return (\n    \u003cQueryClientProvider client={queryClient}\u003e\n      \u003cRouterProvider router={router} /\u003e\n      \u003cReactQueryDevtools initialIsOpen={false} /\u003e\n    \u003c/QueryClientProvider\u003e\n  );\n};\nexport default App;\n```\n\nLanding.js\n\n```js\nexport const loader = (queryClient) =\u003e async () =\u003e {\n  const response = await customFetch(url);\n  const products = response.data.data;\n  return { products };\n};\n```\n\n## Challenge (56) - Landing\n\n- setup react query and invoke in loader\n\n## Solution (56) - Landing\n\nLanding.jsx\n\n```js\nconst featuredProductsQuery = {\n  queryKey: [\"featuredProducts\"],\n  queryFn: () =\u003e customFetch(url),\n};\n\nexport const loader = (queryClient) =\u003e async () =\u003e {\n  const response = await queryClient.ensureQueryData(featuredProductsQuery);\n  const products = response.data.data;\n  return { products };\n};\n```\n\n## Challenge (57) - Single Product\n\n- setup react query and invoke in loader\n\n## Solution (57) - Single Product\n\nSingleProduct.jsx\n\n```js\nconst singleProductQuery = (id) =\u003e {\n  return {\n    queryKey: [\"singleProduct\", id],\n    queryFn: () =\u003e customFetch.get(`/products/${id}`),\n  };\n};\n\nexport const loader =\n  (queryClient) =\u003e\n  async ({ params }) =\u003e {\n    const response = await queryClient.ensureQueryData(\n      singleProductQuery(params.id)\n    );\n    return { product: response.data.data };\n  };\n```\n\n## Challenge (58) - All Products\n\n- setup react query and invoke in loader\n\n## Solution (58) - All Products\n\nProducts.jsx\n\n```js\nconst allProductsQuery = (queryParams) =\u003e {\n  const { search, category, company, sort, price, shipping, page } =\n    queryParams;\n\n  return {\n    queryKey: [\n      \"products\",\n      search ?? \"\",\n      category ?? \"all\",\n      company ?? \"all\",\n      sort ?? \"a-z\",\n      price ?? 100000,\n      shipping ?? false,\n      page ?? 1,\n    ],\n    queryFn: () =\u003e\n      customFetch(url, {\n        params: queryParams,\n      }),\n  };\n};\n\nexport const loader =\n  (queryClient) =\u003e\n  async ({ request }) =\u003e {\n    const params = Object.fromEntries([\n      ...new URL(request.url).searchParams.entries(),\n    ]);\n    const response = await queryClient.ensureQueryData(\n      allProductsQuery(params)\n    );\n\n    const products = response.data.data;\n    const meta = response.data.meta;\n\n    return { products, meta, params };\n  };\n```\n\n?? === This operator is known as the nullish coalescing operator in JavaScript. It is a logical operator that returns its right-hand side operand when its left-hand side operand is null or undefined, and otherwise returns its left-hand side operand.\n\nIn simpler terms, the ?? operator is used to provide a default value for potentially null or undefined variables.\n\n## Challenge (59) - Orders\n\nsetup react query and invoke in loader\n\n## Solution (59) - Orders\n\n```js\nimport { redirect, useLoaderData } from \"react-router-dom\";\nimport { toast } from \"react-toastify\";\nimport { customFetch } from \"../utils\";\nimport {\n  OrdersList,\n  ComplexPaginationContainer,\n  SectionTitle,\n} from \"../components\";\n\nexport const ordersQuery = (params, user) =\u003e {\n  return {\n    queryKey: [\n      \"orders\",\n      user.username,\n      params.page ? parseInt(params.page) : 1,\n    ],\n    queryFn: () =\u003e\n      customFetch.get(\"/orders\", {\n        params,\n        headers: {\n          Authorization: `Bearer ${user.token}`,\n        },\n      }),\n  };\n};\n\nexport const loader =\n  (store, queryClient) =\u003e\n  async ({ request }) =\u003e {\n    const user = store.getState().userState.user;\n\n    if (!user) {\n      toast.warn(\"You must be logged in to view orders\");\n      return redirect(\"/login\");\n    }\n    const params = Object.fromEntries([\n      ...new URL(request.url).searchParams.entries(),\n    ]);\n    try {\n      const response = await queryClient.ensureQueryData(\n        ordersQuery(params, user)\n      );\n\n      return {\n        orders: response.data.data,\n        meta: response.data.meta,\n      };\n    } catch (error) {\n      console.log(error);\n      const errorMessage =\n        error?.response?.data?.error?.message ||\n        \"there was an error accessing your orders\";\n\n      toast.error(errorMessage);\n      if (error?.response?.status === 401 || 403) return redirect(\"/login\");\n      return null;\n    }\n  };\nconst Orders = () =\u003e {\n  const { meta } = useLoaderData();\n\n  if (meta.pagination.total \u003c 1) {\n    return \u003cSectionTitle text=\"Please make an order\" /\u003e;\n  }\n  return (\n    \u003c\u003e\n      \u003cSectionTitle text=\"Your Orders\" /\u003e\n      \u003cOrdersList /\u003e\n      \u003cComplexPaginationContainer /\u003e\n    \u003c/\u003e\n  );\n};\nexport default Orders;\n```\n\n## Challenge (60) - Remove Queries\n\n- remove \"orders\" query in CheckoutForm and Header\n\n## Solution (60) - Remove Queries\n\nCheckoutForm.jsx\n\n```js\nimport { Form, redirect } from 'react-router-dom';\nimport FormInput from './FormInput';\nimport SubmitBtn from './SubmitBtn';\nimport { customFetch, formatPrice } from '../utils';\nimport { toast } from 'react-toastify';\nimport { clearCart } from '../features/cart/cartSlice';\n\nexport const action =\n  (store, queryClient) =\u003e\n  async ({ request }) =\u003e {\n    ...\n    try {\n      const response = await customFetch.post(\n        '/orders',\n        { data: info },\n        {\n          headers: {\n            Authorization: `Bearer ${user.token}`,\n          },\n        }\n      );\n      // remove query\n      queryClient.removeQueries(['orders']);\n      // rest of the code\n      store.dispatch(clearCart());\n      toast.success('order placed successfully');\n      return redirect('/orders');\n    } ...\n  };\n```\n\nHeader.jsx\n\n```js\n\nimport { useQueryClient } from '@tanstack/react-query';\nconst Header = () =\u003e {\n  const navigate = useNavigate();\n  const dispatch = useDispatch();\n  const user = useSelector((state) =\u003e state.userState.user);\n  const queryClient = useQueryClient();\n  const handleLogout = async () =\u003e {\n    navigate('/');\n    dispatch(logoutUser());\n    dispatch(clearCart());\n    queryClient.removeQueries();\n  };\n  ...\n}\n\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farnobt78%2Fecommerce-comfy--reactvite-reduxtoolkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farnobt78%2Fecommerce-comfy--reactvite-reduxtoolkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farnobt78%2Fecommerce-comfy--reactvite-reduxtoolkit/lists"}