├── .eslintrc.cjs
├── .gitignore
├── README.md
├── index.html
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
├── logo.png
├── menu.json
└── reviews.json
├── src
├── App.css
├── App.jsx
├── Layout
│ ├── Dashboard.jsx
│ └── Main.jsx
├── Routes
│ ├── AdminRoute.jsx
│ ├── PrivateRoute.jsx
│ └── Routes.jsx
├── assets
│ ├── 404.gif
│ ├── contact
│ │ ├── banner.jpg
│ │ └── emo.png
│ ├── dashboard
│ │ └── image-5.jpg
│ ├── home
│ │ ├── 01.jpg
│ │ ├── 02.jpg
│ │ ├── 03.png
│ │ ├── 04.jpg
│ │ ├── 05.png
│ │ ├── 06.png
│ │ ├── banner.jpg
│ │ ├── chef-special.jpg
│ │ ├── featured.jpg
│ │ ├── slide1.jpg
│ │ ├── slide2.jpg
│ │ ├── slide3.jpg
│ │ ├── slide4.jpg
│ │ └── slide5.jpg
│ ├── icon
│ │ ├── 151-1511569_cart-notifications-free-shopping-cart-favicon-hd-png-removebg-preview.png
│ │ └── correct.png
│ ├── logo.png
│ ├── menu
│ │ ├── banner.png
│ │ ├── dessert-bg.jpeg
│ │ ├── menu-bg.jpg
│ │ ├── pizza-bg.jpg
│ │ ├── salad-bg.jpg
│ │ └── soup-bg.jpg
│ ├── others
│ │ ├── Illustration.svg
│ │ ├── authentication.gif
│ │ ├── authentication.png
│ │ ├── authentication1.png
│ │ ├── authentication2.png
│ │ ├── cupcake-dribbble.gif
│ │ ├── cupcake.gif
│ │ ├── download (1).jpg
│ │ ├── loader2.gif
│ │ ├── loader3.gif
│ │ └── profile.png
│ ├── reservation
│ │ ├── category-pizza.jpg
│ │ └── wood-grain-pattern-gray1x.png
│ └── shop
│ │ └── order.jpg
├── components
│ ├── FoodCard
│ │ └── FoodCard.jsx
│ ├── SectionTitle
│ │ └── SectionTitle.jsx
│ └── SocialLogin
│ │ └── SocialLogin.jsx
├── firebase
│ └── firebase.config.js
├── hooks
│ ├── useAdmin.jsx
│ ├── useAuth.jsx
│ ├── useAxiosPublic.jsx
│ ├── useAxiosSecure.jsx
│ ├── useCart.jsx
│ └── useMenu.jsx
├── index.css
├── main.jsx
├── pages
│ ├── Dashboard
│ │ ├── AddItems
│ │ │ └── AddItems.jsx
│ │ ├── AllUsers
│ │ │ └── AllUsers.jsx
│ │ ├── Cart
│ │ │ └── Cart.jsx
│ │ ├── ManageItems
│ │ │ └── ManageItems.jsx
│ │ ├── UpdateItem
│ │ │ └── UpdateItem.jsx
│ │ └── __Page__level_security__.js
│ ├── Home
│ │ ├── Banner
│ │ │ └── Banner.jsx
│ │ ├── Category
│ │ │ └── Category.jsx
│ │ ├── Featured
│ │ │ ├── Featured.css
│ │ │ └── Featured.jsx
│ │ ├── Home
│ │ │ └── Home.jsx
│ │ ├── PopularMenu
│ │ │ └── PopularMenu.jsx
│ │ └── Testimonials
│ │ │ └── Testimonials.jsx
│ ├── Login
│ │ └── Login.jsx
│ ├── Menu
│ │ ├── Menu
│ │ │ └── Menu.jsx
│ │ └── MenuCategory
│ │ │ └── MenuCategory.jsx
│ ├── Order
│ │ ├── Order
│ │ │ └── Order.jsx
│ │ └── OrderTab
│ │ │ └── OrderTab.jsx
│ ├── Shared
│ │ ├── Cover
│ │ │ └── Cover.jsx
│ │ ├── Footer
│ │ │ └── Footer.jsx
│ │ ├── MenuItem
│ │ │ └── MenuItem.jsx
│ │ ├── NavBar
│ │ │ └── NavBar.jsx
│ │ └── Secret
│ │ │ └── Secret.jsx
│ └── SignUp
│ │ └── SignUp.jsx
└── providers
│ └── AuthProvider.jsx
├── tailwind.config.js
└── vite.config.js
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es2020: true,
5 | node: true,
6 | },
7 | extends: [
8 | 'eslint:recommended',
9 | 'plugin:react/recommended',
10 | 'plugin:react/jsx-runtime',
11 | 'plugin:react-hooks/recommended',
12 | ],
13 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
14 | settings: { react: { version: '18.2' } },
15 | plugins: ['react-refresh'],
16 | rules: {
17 | 'react-refresh/only-export-components': 'warn',
18 | "react/prop-types": "off"
19 | },
20 | }
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Bistro Boss Client with Image Upload
2 |
3 | Client Repo Link - [Bistro Boss Server with Image Upload](https://github.com/ProgrammingHero1/bistro-boss-server-with-image-upload-part_6)
4 |
5 | ## Overview
6 | BistroBoss is a comprehensive restaurant management system designed to streamline operations, enhance customer experience, and improve overall efficiency. The system leverages modern web technologies to provide a robust, scalable, and high-performance solution for managing complex data structures and interactions within a restaurant environment .
7 |
8 | ## What We Learn Today
9 |
10 | ### Why We Use React-Query, Axios, and React Hook Form
11 | - **React-Query**: Simplifies data fetching and state management, providing caching, synchronization, and background updates to enhance user experience and performance.
12 | - **Axios**: A promise-based HTTP client that makes it easier to make requests to the server, handle responses, and manage errors compared to the native `fetch` API.
13 | - **React Hook Form**: Provides a flexible and efficient way to handle form inputs, validations, and submission, reducing boilerplate code and improving performance with minimal re-renders .
14 |
15 | ### Create Add Item Form Using React Hook Form
16 | We learn how to create a user-friendly and dynamic form for adding new menu items using React Hook Form, ensuring smooth data handling and validation with minimal code.
17 |
18 | ### Design Form Using Daisy UI and React Hook Form
19 | Incorporate Daisy UI to style our forms seamlessly, combining it with React Hook Form to ensure that our forms are both visually appealing and functionally robust.
20 |
21 | ### Upload Image to Image Hosting Server Imgbb and Get Image URL
22 | Learn the process of uploading images to the Imgbb image hosting service, retrieving the image URL, and handling this URL within our application to store image references efficiently.
23 |
24 | ### Save Menu Item to the Server and Make API Secure
25 | Understand how to securely save new menu items to the server, ensuring data integrity and security through proper API design and implementation.
26 |
27 | ### Create Manage Items Admin Route and Load All Menu Items
28 | Develop an administrative route for managing menu items, including loading all items from the server and displaying them in a structured and manageable way.
29 |
30 | ### Delete Menu Item with VerifyAdmin Middleware
31 | Implement functionality to delete menu items securely, using the VerifyAdmin middleware to ensure that only authorized users can perform deletion operations.
32 |
33 | ### Create Update Menu Item Form
34 | Learn to create a form for updating existing menu items, allowing for efficient editing and ensuring that changes are accurately reflected in the system.
35 |
36 |
37 | ---
38 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Bistro Boss Restaurant
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bistro-boss-client",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint src --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "@smastrom/react-rating": "^1.3.1",
14 | "@tanstack/react-query": "^5.8.2",
15 | "axios": "^1.6.1",
16 | "daisyui": "^2.51.6",
17 | "firebase": "^9.22.0",
18 | "localforage": "^1.10.0",
19 | "match-sorter": "^6.3.1",
20 | "react": "^18.2.0",
21 | "react-dom": "^18.2.0",
22 | "react-helmet-async": "^1.3.0",
23 | "react-hook-form": "^7.43.9",
24 | "react-icons": "^4.11.0",
25 | "react-parallax": "^3.5.1",
26 | "react-responsive-carousel": "^3.2.23",
27 | "react-router-dom": "^6.11.2",
28 | "react-simple-captcha": "^9.0.2",
29 | "react-tabs": "^6.0.1",
30 | "sort-by": "^1.2.0",
31 | "sweetalert2": "^11.7.5",
32 | "swiper": "^9.3.2"
33 | },
34 | "devDependencies": {
35 | "@types/react": "^18.0.28",
36 | "@types/react-dom": "^18.0.11",
37 | "@vitejs/plugin-react": "^4.0.0",
38 | "autoprefixer": "^10.4.14",
39 | "eslint": "^8.38.0",
40 | "eslint-plugin-react": "^7.32.2",
41 | "eslint-plugin-react-hooks": "^4.6.0",
42 | "eslint-plugin-react-refresh": "^0.3.4",
43 | "postcss": "^8.4.23",
44 | "tailwindcss": "^3.3.2",
45 | "vite": "^4.3.2"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/public/logo.png
--------------------------------------------------------------------------------
/public/menu.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "_id": "642c155b2c4774f05c36eeaa",
4 | "name": "Haddock",
5 | "recipe": "Chargrilled fresh tuna steak (served medium rare) on classic Niçoise salad with French beans.",
6 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-1-370x247.jpg",
7 | "category": "salad",
8 | "price": 14.7
9 | },
10 | {
11 | "_id": "642c155b2c4774f05c36eeb9",
12 | "name": "Haddock",
13 | "recipe": "Chargrilled fresh tuna steak (served medium rare) on classic Niçoise salad with French beans.",
14 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-1-370x247.jpg",
15 | "category": "drinks",
16 | "price": 14.7
17 | },
18 | {
19 | "_id": "642c155b2c4774f05c36ee7c",
20 | "name": "Escalope de Veau",
21 | "recipe": "Roasted duck breast (served pink) with gratin potato and a griottine cherry sauce",
22 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-5-370x247.jpg",
23 | "category": "popular",
24 | "price": 14.5
25 | },
26 | {
27 | "_id": "642c155b2c4774f05c36ee88",
28 | "name": "Escalope de Veau",
29 | "recipe": "Pan roasted haddock fillet wrapped in smoked French bacon with pea purée and tomato and chive vinaigrette",
30 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-2-370x247.jpg",
31 | "category": "dessert",
32 | "price": 12.5
33 | },
34 | {
35 | "_id": "642c155b2c4774f05c36ee7a",
36 | "name": "Roast Duck Breast",
37 | "recipe": "Roasted duck breast (served pink) with gratin potato and a griottine cherry sauce",
38 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-5-370x247.jpg",
39 | "category": "popular",
40 | "price": 14.5
41 | },
42 | {
43 | "_id": "642c155b2c4774f05c36ee8c",
44 | "name": "Escalope de Veau",
45 | "recipe": "Pan roasted haddock fillet wrapped in smoked French bacon with pea purée and tomato and chive vinaigrette",
46 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-2-370x247.jpg",
47 | "category": "dessert",
48 | "price": 12.5
49 | },
50 | {
51 | "_id": "642c155b2c4774f05c36ee94",
52 | "name": "Escalope de Veau",
53 | "recipe": "Pan roasted haddock fillet wrapped in smoked French bacon with pea purée and tomato and chive vinaigrette",
54 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-2-370x247.jpg",
55 | "category": "pizza",
56 | "price": 12.5
57 | },
58 | {
59 | "_id": "642c155b2c4774f05c36ee9e",
60 | "name": "Breton Fish Stew",
61 | "recipe": "Chargrilled chicken with avocado, baby gem lettuce, baby spinach, shallots, French beans, walnuts, croutons and a mustard dressing",
62 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-8-370x247.jpg",
63 | "category": "pizza",
64 | "price": 12.9
65 | },
66 | {
67 | "_id": "642c155b2c4774f05c36eea6",
68 | "name": "Chicken and Walnut Salad",
69 | "recipe": "Pan roasted pork belly with gratin potato, braised Savoy cabbage, apples, thyme and calvados jus",
70 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-3-370x247.jpg",
71 | "category": "salad",
72 | "price": 13.5
73 | },
74 | {
75 | "_id": "642c155b2c4774f05c36eeaf",
76 | "name": "Escalope de Veau",
77 | "recipe": "Pan roasted haddock fillet wrapped in smoked French bacon with pea purée and tomato and chive vinaigrette",
78 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-2-370x247.jpg",
79 | "category": "soup",
80 | "price": 12.5
81 | },
82 | {
83 | "_id": "642c155b2c4774f05c36ee89",
84 | "name": "Chicken and Walnut Salad",
85 | "recipe": "Pan roasted pork belly with gratin potato, braised Savoy cabbage, apples, thyme and calvados jus",
86 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-3-370x247.jpg",
87 | "category": "dessert",
88 | "price": 13.5
89 | },
90 | {
91 | "_id": "642c155b2c4774f05c36ee8a",
92 | "name": "Escalope de Veau",
93 | "recipe": "Pan roasted haddock fillet wrapped in smoked French bacon with pea purée and tomato and chive vinaigrette",
94 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-2-370x247.jpg",
95 | "category": "dessert",
96 | "price": 12.5
97 | },
98 | {
99 | "_id": "642c155b2c4774f05c36ee96",
100 | "name": "Fish Parmentier",
101 | "recipe": "Sautéed breaded veal escalope with watercress, lemon and veal jus.",
102 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-8-370x247.jpg",
103 | "category": "pizza",
104 | "price": 9.5
105 | },
106 | {
107 | "_id": "642c155b2c4774f05c36ee81",
108 | "name": "Tuna Niçoise",
109 | "recipe": "Warm goats cheese and roasted vegetable salad with black olive tapenade crostini",
110 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-2-370x247.jpg",
111 | "category": "offered",
112 | "price": 10.5
113 | },
114 | {
115 | "_id": "642c155b2c4774f05c36ee84",
116 | "name": "Roast Duck Breast",
117 | "recipe": "Roasted duck breast (served pink) with gratin potato and a griottine cherry sauce",
118 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-4-370x247.jpg",
119 | "category": "dessert",
120 | "price": 14.5
121 | },
122 | {
123 | "_id": "642c155b2c4774f05c36eea4",
124 | "name": "Chicken and Walnut Salad",
125 | "recipe": "Pan roasted pork belly with gratin potato, braised Savoy cabbage, apples, thyme and calvados jus",
126 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-3-370x247.jpg",
127 | "category": "salad",
128 | "price": 13.5
129 | },
130 | {
131 | "_id": "642c155b2c4774f05c36eea5",
132 | "name": "Fish Parmentier",
133 | "recipe": "Sautéed breaded veal escalope with watercress, lemon and veal jus.",
134 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-8-370x247.jpg",
135 | "category": "salad",
136 | "price": 9.5
137 | },
138 | {
139 | "_id": "642c155b2c4774f05c36ee87",
140 | "name": "Chicken and Walnut Salad",
141 | "recipe": "Pan roasted pork belly with gratin potato, braised Savoy cabbage, apples, thyme and calvados jus",
142 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-3-370x247.jpg",
143 | "category": "dessert",
144 | "price": 13.5
145 | },
146 | {
147 | "_id": "642c155b2c4774f05c36ee83",
148 | "name": "Chicken and Walnut Salad",
149 | "recipe": "Pan roasted pork belly with gratin potato, braised Savoy cabbage, apples, thyme and calvados jus",
150 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-3-370x247.jpg",
151 | "category": "offered",
152 | "price": 13.5
153 | },
154 | {
155 | "_id": "642c155b2c4774f05c36ee7f",
156 | "name": "Roasted Pork Belly",
157 | "recipe": "Roasted duck breast (served pink) with gratin potato and a griottine cherry sauce",
158 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-2-370x247.jpg",
159 | "category": "popular",
160 | "price": 14.5
161 | },
162 | {
163 | "_id": "642c155b2c4774f05c36eea8",
164 | "name": "Chicken and Walnut Salad",
165 | "recipe": "Pan roasted pork belly with gratin potato, braised Savoy cabbage, apples, thyme and calvados jus",
166 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-3-370x247.jpg",
167 | "category": "salad",
168 | "price": 13.5
169 | },
170 | {
171 | "_id": "642c155b2c4774f05c36eeba",
172 | "name": "Haddock",
173 | "recipe": "Chargrilled fresh tuna steak (served medium rare) on classic Niçoise salad with French beans.",
174 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-1-370x247.jpg",
175 | "category": "drinks",
176 | "price": 14.7
177 | },
178 | {
179 | "_id": "642c155b2c4774f05c36ee80",
180 | "name": "Roast Duck Breast",
181 | "recipe": "Roasted duck breast (served pink) with gratin potato and a griottine cherry sauce",
182 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-4-370x247.jpg",
183 | "category": "offered",
184 | "price": 14.5
185 | },
186 | {
187 | "_id": "642c155b2c4774f05c36ee90",
188 | "name": "Fish Parmentier",
189 | "recipe": "Sautéed breaded veal escalope with watercress, lemon and veal jus.",
190 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-1-370x247.jpg",
191 | "category": "dessert",
192 | "price": 9.5
193 | },
194 | {
195 | "_id": "642c155b2c4774f05c36eeb0",
196 | "name": "Chicken and Walnut Salad",
197 | "recipe": "Pan roasted pork belly with gratin potato, braised Savoy cabbage, apples, thyme and calvados jus",
198 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-3-370x247.jpg",
199 | "category": "soup",
200 | "price": 13.5
201 | },
202 | {
203 | "_id": "642c155b2c4774f05c36ee8d",
204 | "name": "Chicken and Walnut Salad",
205 | "recipe": "Pan roasted pork belly with gratin potato, braised Savoy cabbage, apples, thyme and calvados jus",
206 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-3-370x247.jpg",
207 | "category": "dessert",
208 | "price": 13.5
209 | },
210 | {
211 | "_id": "642c155b2c4774f05c36ee8e",
212 | "name": "Escalope de Veau",
213 | "recipe": "Pan roasted haddock fillet wrapped in smoked French bacon with pea purée and tomato and chive vinaigrette",
214 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-2-370x247.jpg",
215 | "category": "dessert",
216 | "price": 12.5
217 | },
218 | {
219 | "_id": "642c155b2c4774f05c36eea7",
220 | "name": "Fish Parmentier",
221 | "recipe": "Sautéed breaded veal escalope with watercress, lemon and veal jus.",
222 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-8-370x247.jpg",
223 | "category": "salad",
224 | "price": 9.5
225 | },
226 | {
227 | "_id": "642c155b2c4774f05c36ee99",
228 | "name": "Goats Cheese Pizza",
229 | "recipe": "Roasted duck breast (served pink) with gratin potato and a griottine cherry sauce",
230 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2017/01/bbq-370x247.jpg",
231 | "category": "pizza",
232 | "price": 14.5
233 | },
234 | {
235 | "_id": "642c155b2c4774f05c36eeb3",
236 | "name": "Fish Parmentier",
237 | "recipe": "Sautéed breaded veal escalope with watercress, lemon and veal jus.",
238 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-8-370x247.jpg",
239 | "category": "soup",
240 | "price": 9.5
241 | },
242 | {
243 | "_id": "642c155b2c4774f05c36eeb5",
244 | "name": "Fish Parmentier",
245 | "recipe": "Sautéed breaded veal escalope with watercress, lemon and veal jus.",
246 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-8-370x247.jpg",
247 | "category": "soup",
248 | "price": 9.5
249 | },
250 | {
251 | "_id": "642c155b2c4774f05c36eeb6",
252 | "name": "Haddock",
253 | "recipe": "Chargrilled fresh tuna steak (served medium rare) on classic Niçoise salad with French beans.",
254 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-1-370x247.jpg",
255 | "category": "soup",
256 | "price": 14.7
257 | },
258 | {
259 | "_id": "642c155b2c4774f05c36eeb8",
260 | "name": "Haddock",
261 | "recipe": "Chargrilled fresh tuna steak (served medium rare) on classic Niçoise salad with French beans.",
262 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-1-370x247.jpg",
263 | "category": "soup",
264 | "price": 14.7
265 | },
266 | {
267 | "_id": "642c155b2c4774f05c36ee7e",
268 | "name": "Fish Parmentier",
269 | "recipe": "Roasted duck breast (served pink) with gratin potato and a griottine cherry sauce",
270 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-5-370x247.jpg",
271 | "category": "popular",
272 | "price": 14.5
273 | },
274 | {
275 | "_id": "642c155b2c4774f05c36ee8b",
276 | "name": "Chicken and Walnut Salad",
277 | "recipe": "Pan roasted pork belly with gratin potato, braised Savoy cabbage, apples, thyme and calvados jus",
278 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-3-370x247.jpg",
279 | "category": "dessert",
280 | "price": 13.5
281 | },
282 | {
283 | "_id": "642c155b2c4774f05c36ee8f",
284 | "name": "Chicken and Walnut Salad",
285 | "recipe": "Pan roasted pork belly with gratin potato, braised Savoy cabbage, apples, thyme and calvados jus",
286 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-3-370x247.jpg",
287 | "category": "dessert",
288 | "price": 13.5
289 | },
290 | {
291 | "_id": "642c155b2c4774f05c36ee91",
292 | "name": "Haddock",
293 | "recipe": "Chargrilled fresh tuna steak (served medium rare) on classic Niçoise salad with French beans",
294 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-1-370x247.jpg",
295 | "category": "dessert",
296 | "price": 14.7
297 | },
298 | {
299 | "_id": "642c155b2c4774f05c36ee97",
300 | "name": "Haddock",
301 | "recipe": "Chargrilled fresh tuna steak (served medium rare) on classic Niçoise salad with French beans.",
302 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-1-370x247.jpg",
303 | "category": "pizza",
304 | "price": 14.7
305 | },
306 | {
307 | "_id": "642c155b2c4774f05c36eea0",
308 | "name": "Roasted Pork Belly",
309 | "recipe": "Chargrilled chicken with avocado, baby gem lettuce, baby spinach, shallots, French beans, walnuts, croutons and a mustard dressing",
310 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-1-370x247.jpg",
311 | "category": "pizza",
312 | "price": 14.5
313 | },
314 | {
315 | "_id": "642c155b2c4774f05c36eea9",
316 | "name": "Fish Parmentier",
317 | "recipe": "Sautéed breaded veal escalope with watercress, lemon and veal jus.",
318 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-8-370x247.jpg",
319 | "category": "salad",
320 | "price": 9.5
321 | },
322 | {
323 | "_id": "642c155b2c4774f05c36ee85",
324 | "name": "Tuna Niçoise",
325 | "recipe": "Warm goats cheese and roasted vegetable salad with black olive tapenade crostini",
326 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-2-370x247.jpg",
327 | "category": "dessert",
328 | "price": 10.5
329 | },
330 | {
331 | "_id": "642c155b2c4774f05c36ee93",
332 | "name": "Tuna Niçoise",
333 | "recipe": "Warm goats cheese and roasted vegetable salad with black olive tapenade crostini",
334 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-2-370x247.jpg",
335 | "category": "pizza",
336 | "price": 10.5
337 | },
338 | {
339 | "_id": "642c155b2c4774f05c36ee95",
340 | "name": "Chicken and Walnut Salad",
341 | "recipe": "Pan roasted pork belly with gratin potato, braised Savoy cabbage, apples, thyme and calvados jus",
342 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-3-370x247.jpg",
343 | "category": "pizza",
344 | "price": 13.5
345 | },
346 | {
347 | "_id": "642c155b2c4774f05c36ee9b",
348 | "name": "Goats Cheese Pizza",
349 | "recipe": "Roasted duck breast (served pink) with gratin potato and a griottine cherry sauce",
350 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2017/01/bbq-370x247.jpg",
351 | "category": "pizza",
352 | "price": 14.5
353 | },
354 | {
355 | "_id": "642c155b2c4774f05c36eeb7",
356 | "name": "Fish Parmentier",
357 | "recipe": "Sautéed breaded veal escalope with watercress, lemon and veal jus.",
358 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-8-370x247.jpg",
359 | "category": "soup",
360 | "price": 9.5
361 | },
362 | {
363 | "_id": "642c155b2c4774f05c36ee9c",
364 | "name": "Breton Fish Stew",
365 | "recipe": "Chargrilled chicken with avocado, baby gem lettuce, baby spinach, shallots, French beans, walnuts, croutons and a mustard dressing",
366 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-8-370x247.jpg",
367 | "category": "pizza",
368 | "price": 12.9
369 | },
370 | {
371 | "_id": "642c155b2c4774f05c36ee9d",
372 | "name": "Goats Cheese Pizza",
373 | "recipe": "Roasted duck breast (served pink) with gratin potato and a griottine cherry sauce",
374 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2017/01/bbq-370x247.jpg",
375 | "category": "pizza",
376 | "price": 14.5
377 | },
378 | {
379 | "_id": "642c155b2c4774f05c36eeac",
380 | "name": "Roasted Pork Belly",
381 | "recipe": "Chargrilled chicken with avocado, baby gem lettuce, baby spinach, shallots, French beans, walnuts, croutons and a mustard dressing",
382 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-1-370x247.jpg",
383 | "category": "salad",
384 | "price": 14.5
385 | },
386 | {
387 | "_id": "642c155b2c4774f05c36eeb4",
388 | "name": "Haddock",
389 | "recipe": "Chargrilled fresh tuna steak (served medium rare) on classic Niçoise salad with French beans.",
390 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-1-370x247.jpg",
391 | "category": "soup",
392 | "price": 14.7
393 | },
394 | {
395 | "_id": "642c155b2c4774f05c36ee98",
396 | "name": "Breton Fish Stew",
397 | "recipe": "Chargrilled chicken with avocado, baby gem lettuce, baby spinach, shallots, French beans, walnuts, croutons and a mustard dressing",
398 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-8-370x247.jpg",
399 | "category": "pizza",
400 | "price": 12.9
401 | },
402 | {
403 | "_id": "642c155b2c4774f05c36ee82",
404 | "name": "Escalope de Veau",
405 | "recipe": "Pan roasted haddock fillet wrapped in smoked French bacon with pea purée and tomato and chive vinaigrette",
406 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-2-370x247.jpg",
407 | "category": "offered",
408 | "price": 12.5
409 | },
410 | {
411 | "_id": "642c155b2c4774f05c36ee86",
412 | "name": "Escalope de Veau",
413 | "recipe": "Pan roasted haddock fillet wrapped in smoked French bacon with pea purée and tomato and chive vinaigrette",
414 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-2-370x247.jpg",
415 | "category": "dessert",
416 | "price": 12.5
417 | },
418 | {
419 | "_id": "642c155b2c4774f05c36ee9a",
420 | "name": "Breton Fish Stew",
421 | "recipe": "Chargrilled chicken with avocado, baby gem lettuce, baby spinach, shallots, French beans, walnuts, croutons and a mustard dressing",
422 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-8-370x247.jpg",
423 | "category": "pizza",
424 | "price": 12.9
425 | },
426 | {
427 | "_id": "642c155b2c4774f05c36eeab",
428 | "name": "Goats Cheese Pizza",
429 | "recipe": "Roasted duck breast (served pink) with gratin potato and a griottine cherry sauce",
430 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2017/01/bbq-370x247.jpg",
431 | "category": "salad",
432 | "price": 14.5
433 | },
434 | {
435 | "_id": "642c155b2c4774f05c36eeb1",
436 | "name": "Fish Parmentier",
437 | "recipe": "Sautéed breaded veal escalope with watercress, lemon and veal jus.",
438 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-8-370x247.jpg",
439 | "category": "soup",
440 | "price": 9.5
441 | },
442 | {
443 | "_id": "642c155b2c4774f05c36eead",
444 | "name": "Roast Duck Breast",
445 | "recipe": "Roasted duck breast (served pink) with gratin potato and a griottine cherry sauce",
446 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-4-370x247.jpg",
447 | "category": "soup",
448 | "price": 14.5
449 | },
450 | {
451 | "_id": "642c155b2c4774f05c36eeae",
452 | "name": "Tuna Niçoise",
453 | "recipe": "Warm goats cheese and roasted vegetable salad with black olive tapenade crostini",
454 | "image": "https://cristianonew.ukrdevs.com/wp-content/uploads/2016/08/product-2-370x247.jpg",
455 | "category": "soup",
456 | "price": 10.5
457 | }
458 | ]
--------------------------------------------------------------------------------
/public/reviews.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "_id": "643010e0f5a7e52ce1e8fa65",
4 | "name": "Jane Doe",
5 | "details": "Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like). It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.",
6 | "rating": 3
7 | },
8 | {
9 | "_id": "643010f9f5a7e52ce1e8fa66",
10 | "name": "John Doe",
11 | "details": "It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like)",
12 | "rating": 2
13 | },
14 | {
15 | "_id": "6430110af5a7e52ce1e8fa67",
16 | "name": "MJ Doe",
17 | "details": "It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like)",
18 | "rating": 5
19 | },
20 | {
21 | "_id": "64301123f5a7e52ce1e8fa68",
22 | "name": "Sarah Smith",
23 | "details": "I found the product to be incredibly useful and easy to use. The interface is intuitive, and it has all the features I need. Highly recommend it!",
24 | "rating": 4
25 | },
26 | {
27 | "_id": "6430113af5a7e52ce1e8fa69",
28 | "name": "Robert Johnson",
29 | "details": "This is by far the best service I have ever used. The customer support is outstanding, and the product itself is top-notch. I couldn't be happier!",
30 | "rating": 5
31 | }
32 | ]
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/App.css
--------------------------------------------------------------------------------
/src/App.jsx:
--------------------------------------------------------------------------------
1 | import './App.css'
2 |
3 | function App() {
4 |
5 | return (
6 | <>
7 |
8 | >
9 | )
10 | }
11 |
12 | export default App
13 |
--------------------------------------------------------------------------------
/src/Layout/Dashboard.jsx:
--------------------------------------------------------------------------------
1 | import { FaAd, FaBook, FaCalendar, FaEnvelope, FaHome, FaList, FaSearch, FaShoppingCart, FaUsers, FaUtensils } from "react-icons/fa";
2 | import { NavLink, Outlet } from "react-router-dom";
3 | import useCart from "../hooks/useCart";
4 | import useAdmin from "../hooks/useAdmin";
5 |
6 |
7 | const Dashboard = () => {
8 | const [cart] = useCart();
9 |
10 | // TODO: get isAdmin value from the database
11 | const [isAdmin] = useAdmin();
12 |
13 | return (
14 |
15 | {/* dashboard side bar */}
16 |
17 |
18 | {
19 | isAdmin ? <>
20 |
21 |
22 |
23 | Admin Home
24 |
25 |
26 |
27 |
28 | Add Items
29 |
30 |
31 |
32 |
33 | Manage Items
34 |
35 |
36 |
37 |
38 | Manage Bookings
39 |
40 |
41 |
42 |
43 | All Users
44 |
45 | >
46 | :
47 | <>
48 |
49 |
50 |
51 | User Home
52 |
53 |
54 |
55 |
56 | Reservation
57 |
58 |
59 |
60 |
61 | My Cart ({cart.length})
62 |
63 |
64 |
65 |
66 | Add a Review
67 |
68 |
69 |
70 |
71 | My Bookings
72 |
73 | >
74 | }
75 | {/* shared nav links */}
76 |
77 |
78 |
79 |
80 | Home
81 |
82 |
83 |
84 |
85 | Menu
86 |
87 |
88 |
89 |
90 | Contact
91 |
92 |
93 |
94 | {/* dashboard content */}
95 |
96 |
97 |
98 |
99 | );
100 | };
101 |
102 | export default Dashboard;
--------------------------------------------------------------------------------
/src/Layout/Main.jsx:
--------------------------------------------------------------------------------
1 | import { Outlet, useLocation } from "react-router-dom";
2 | import Footer from "../pages/Shared/Footer/Footer";
3 | import NavBar from "../pages/Shared/NavBar/NavBar";
4 |
5 |
6 | const Main = () => {
7 | const location = useLocation();
8 |
9 | const noHeaderFooter = location.pathname.includes('login') || location.pathname.includes('signup');
10 |
11 | return (
12 |
13 | { noHeaderFooter || }
14 |
15 | { noHeaderFooter || }
16 |
17 | );
18 | };
19 |
20 | export default Main;
--------------------------------------------------------------------------------
/src/Routes/AdminRoute.jsx:
--------------------------------------------------------------------------------
1 | import { Navigate, useLocation } from "react-router-dom";
2 | import useAuth from "../hooks/useAuth";
3 | import useAdmin from "../hooks/useAdmin";
4 |
5 |
6 | const AdminRoute = ({ children }) => {
7 | const { user, loading } = useAuth();
8 | const [isAdmin, isAdminLoading] = useAdmin();
9 | const location = useLocation();
10 |
11 | if (loading || isAdminLoading) {
12 | return
13 | }
14 |
15 | if (user && isAdmin) {
16 | return children;
17 | }
18 |
19 | return
20 |
21 | };
22 |
23 | export default AdminRoute;
--------------------------------------------------------------------------------
/src/Routes/PrivateRoute.jsx:
--------------------------------------------------------------------------------
1 | import { Navigate, useLocation } from "react-router";
2 | import useAuth from "../hooks/useAuth";
3 |
4 |
5 | const PrivateRoute = ({ children }) => {
6 | const { user, loading } = useAuth();
7 | const location = useLocation();
8 |
9 | if(loading){
10 | return
11 | }
12 |
13 | if (user) {
14 | return children;
15 | }
16 | return
17 | };
18 |
19 | export default PrivateRoute;
--------------------------------------------------------------------------------
/src/Routes/Routes.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | createBrowserRouter,
3 | } from "react-router-dom";
4 | import Main from "../Layout/Main";
5 | import Home from "../pages/Home/Home/Home";
6 | import Menu from "../pages/Menu/Menu/Menu";
7 | import Order from "../pages/Order/Order/Order";
8 | import Login from "../pages/Login/Login";
9 | import SignUp from "../pages/SignUp/SignUp";
10 | import PrivateRoute from "./PrivateRoute";
11 | import Secret from "../pages/Shared/Secret/Secret";
12 | import Dashboard from "../Layout/Dashboard";
13 | import Cart from "../pages/Dashboard/Cart/Cart";
14 | import AllUsers from "../pages/Dashboard/AllUsers/AllUsers";
15 | import AddItems from "../pages/Dashboard/AddItems/AddItems";
16 | import AdminRoute from "./AdminRoute";
17 | import ManageItems from "../pages/Dashboard/ManageItems/ManageItems";
18 | import UpdateItem from "../pages/Dashboard/UpdateItem/UpdateItem";
19 |
20 |
21 | export const router = createBrowserRouter([
22 | {
23 | path: "/",
24 | element: ,
25 | children: [
26 | {
27 | path: '/',
28 | element:
29 | },
30 | {
31 | path: 'menu',
32 | element:
33 | },
34 | {
35 | path: 'order/:category',
36 | element:
37 | },
38 | {
39 | path: 'login',
40 | element:
41 | },
42 | {
43 | path: 'signup',
44 | element:
45 | },
46 | {
47 | path: 'secret',
48 | element:
49 | }
50 | ]
51 | },
52 | {
53 | path: 'dashboard',
54 | element: ,
55 | children: [
56 | // normal user routes
57 | {
58 | path: 'cart',
59 | element:
60 | },
61 |
62 | // admin only routes
63 | {
64 | path: 'addItems',
65 | element:
66 | },
67 | {
68 | path: 'manageItems',
69 | element:
70 | },
71 | {
72 | path: 'updateItem/:id',
73 | element: ,
74 | loader: ({params}) => fetch(`http://localhost:5000/menu/${params.id}`)
75 | },
76 | {
77 | path: 'users',
78 | element:
79 | }
80 |
81 | ]
82 | }
83 | ]);
--------------------------------------------------------------------------------
/src/assets/404.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/404.gif
--------------------------------------------------------------------------------
/src/assets/contact/banner.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/contact/banner.jpg
--------------------------------------------------------------------------------
/src/assets/contact/emo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/contact/emo.png
--------------------------------------------------------------------------------
/src/assets/dashboard/image-5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/dashboard/image-5.jpg
--------------------------------------------------------------------------------
/src/assets/home/01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/home/01.jpg
--------------------------------------------------------------------------------
/src/assets/home/02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/home/02.jpg
--------------------------------------------------------------------------------
/src/assets/home/03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/home/03.png
--------------------------------------------------------------------------------
/src/assets/home/04.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/home/04.jpg
--------------------------------------------------------------------------------
/src/assets/home/05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/home/05.png
--------------------------------------------------------------------------------
/src/assets/home/06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/home/06.png
--------------------------------------------------------------------------------
/src/assets/home/banner.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/home/banner.jpg
--------------------------------------------------------------------------------
/src/assets/home/chef-special.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/home/chef-special.jpg
--------------------------------------------------------------------------------
/src/assets/home/featured.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/home/featured.jpg
--------------------------------------------------------------------------------
/src/assets/home/slide1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/home/slide1.jpg
--------------------------------------------------------------------------------
/src/assets/home/slide2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/home/slide2.jpg
--------------------------------------------------------------------------------
/src/assets/home/slide3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/home/slide3.jpg
--------------------------------------------------------------------------------
/src/assets/home/slide4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/home/slide4.jpg
--------------------------------------------------------------------------------
/src/assets/home/slide5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/home/slide5.jpg
--------------------------------------------------------------------------------
/src/assets/icon/151-1511569_cart-notifications-free-shopping-cart-favicon-hd-png-removebg-preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/icon/151-1511569_cart-notifications-free-shopping-cart-favicon-hd-png-removebg-preview.png
--------------------------------------------------------------------------------
/src/assets/icon/correct.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/icon/correct.png
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/logo.png
--------------------------------------------------------------------------------
/src/assets/menu/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/menu/banner.png
--------------------------------------------------------------------------------
/src/assets/menu/dessert-bg.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/menu/dessert-bg.jpeg
--------------------------------------------------------------------------------
/src/assets/menu/menu-bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/menu/menu-bg.jpg
--------------------------------------------------------------------------------
/src/assets/menu/pizza-bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/menu/pizza-bg.jpg
--------------------------------------------------------------------------------
/src/assets/menu/salad-bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/menu/salad-bg.jpg
--------------------------------------------------------------------------------
/src/assets/menu/soup-bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/menu/soup-bg.jpg
--------------------------------------------------------------------------------
/src/assets/others/Illustration.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
--------------------------------------------------------------------------------
/src/assets/others/authentication.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/others/authentication.gif
--------------------------------------------------------------------------------
/src/assets/others/authentication.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/others/authentication.png
--------------------------------------------------------------------------------
/src/assets/others/authentication1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/others/authentication1.png
--------------------------------------------------------------------------------
/src/assets/others/authentication2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/others/authentication2.png
--------------------------------------------------------------------------------
/src/assets/others/cupcake-dribbble.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/others/cupcake-dribbble.gif
--------------------------------------------------------------------------------
/src/assets/others/cupcake.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/others/cupcake.gif
--------------------------------------------------------------------------------
/src/assets/others/download (1).jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/others/download (1).jpg
--------------------------------------------------------------------------------
/src/assets/others/loader2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/others/loader2.gif
--------------------------------------------------------------------------------
/src/assets/others/loader3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/others/loader3.gif
--------------------------------------------------------------------------------
/src/assets/others/profile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/others/profile.png
--------------------------------------------------------------------------------
/src/assets/reservation/category-pizza.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/reservation/category-pizza.jpg
--------------------------------------------------------------------------------
/src/assets/reservation/wood-grain-pattern-gray1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/reservation/wood-grain-pattern-gray1x.png
--------------------------------------------------------------------------------
/src/assets/shop/order.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProgrammingHero1/bistro-boss-client-with-image-upload-part_6/9285d836741cce716342e38ba24ea213a385ceef/src/assets/shop/order.jpg
--------------------------------------------------------------------------------
/src/components/FoodCard/FoodCard.jsx:
--------------------------------------------------------------------------------
1 | import Swal from "sweetalert2";
2 | import useAuth from "../../hooks/useAuth";
3 | import { useLocation, useNavigate } from "react-router-dom";
4 | import useAxiosSecure from "../../hooks/useAxiosSecure";
5 | import useCart from "../../hooks/useCart";
6 |
7 |
8 | const FoodCard = ({ item }) => {
9 | const { name, image, price, recipe, _id } = item;
10 | const { user } = useAuth();
11 | const navigate = useNavigate();
12 | const location = useLocation();
13 | const axiosSecure = useAxiosSecure();
14 | const [, refetch] = useCart();
15 |
16 | const handleAddToCart = () => {
17 | if (user && user.email) {
18 | //send cart item to the database
19 | const cartItem = {
20 | menuId: _id,
21 | email: user.email,
22 | name,
23 | image,
24 | price
25 | }
26 | axiosSecure.post('/carts', cartItem)
27 | .then(res => {
28 | console.log(res.data)
29 | if (res.data.insertedId) {
30 | Swal.fire({
31 | position: "top-end",
32 | icon: "success",
33 | title: `${name} added to your cart`,
34 | showConfirmButton: false,
35 | timer: 1500
36 | });
37 | // refetch cart to update the cart items count
38 | refetch();
39 | }
40 |
41 | })
42 | }
43 | else {
44 | Swal.fire({
45 | title: "You are not Logged In",
46 | text: "Please login to add to the cart?",
47 | icon: "warning",
48 | showCancelButton: true,
49 | confirmButtonColor: "#3085d6",
50 | cancelButtonColor: "#d33",
51 | confirmButtonText: "Yes, login!"
52 | }).then((result) => {
53 | if (result.isConfirmed) {
54 | // send the user to the login page
55 | navigate('/login', { state: { from: location } })
56 | }
57 | });
58 | }
59 | }
60 | return (
61 |
62 |
63 |
${price}
64 |
65 |
{name}
66 |
{recipe}
67 |
68 | Add to Cart
72 |
73 |
74 |
75 | );
76 | };
77 |
78 | export default FoodCard;
--------------------------------------------------------------------------------
/src/components/SectionTitle/SectionTitle.jsx:
--------------------------------------------------------------------------------
1 |
2 |
3 | const SectionTitle = ({heading, subHeading}) => {
4 | return (
5 |
6 |
--- {subHeading} ---
7 |
{heading}
8 |
9 | );
10 | };
11 |
12 | export default SectionTitle;
--------------------------------------------------------------------------------
/src/components/SocialLogin/SocialLogin.jsx:
--------------------------------------------------------------------------------
1 | import { FaGoogle } from "react-icons/fa";
2 | import useAuth from "../../hooks/useAuth";
3 | import useAxiosPublic from "../../hooks/useAxiosPublic";
4 | import { useNavigate } from "react-router-dom";
5 |
6 |
7 | const SocialLogin = () => {
8 | const { googleSignIn } = useAuth();
9 | const axiosPublic = useAxiosPublic();
10 | const navigate = useNavigate();
11 |
12 | const handleGoogleSignIn = () =>{
13 | googleSignIn()
14 | .then(result =>{
15 | console.log(result.user);
16 | const userInfo = {
17 | email: result.user?.email,
18 | name: result.user?.displayName
19 | }
20 | axiosPublic.post('/users', userInfo)
21 | .then(res =>{
22 | console.log(res.data);
23 | navigate('/');
24 | })
25 | })
26 | }
27 |
28 | return (
29 |
30 |
31 |
32 |
33 |
34 | Google
35 |
36 |
37 |
38 | );
39 | };
40 |
41 | export default SocialLogin;
--------------------------------------------------------------------------------
/src/firebase/firebase.config.js:
--------------------------------------------------------------------------------
1 | // Import the functions you need from the SDKs you need
2 | import { initializeApp } from "firebase/app";
3 | // TODO: Add SDKs for Firebase products that you want to use
4 | // https://firebase.google.com/docs/web/setup#available-libraries
5 |
6 | // Your web app's Firebase configuration
7 | const firebaseConfig = {
8 | apiKey: import.meta.env.VITE_apiKey,
9 | authDomain: import.meta.env.VITE_authDomain,
10 | projectId: import.meta.env.VITE_projectId,
11 | storageBucket: import.meta.env.VITE_storageBucket,
12 | messagingSenderId: import.meta.env.VITE_messagingSenderId,
13 | appId: import.meta.env.VITE_appId
14 | };
15 |
16 | // Initialize Firebase
17 | export const app = initializeApp(firebaseConfig);
--------------------------------------------------------------------------------
/src/hooks/useAdmin.jsx:
--------------------------------------------------------------------------------
1 | import { useQuery } from "@tanstack/react-query";
2 | import useAuth from "./useAuth";
3 | import useAxiosSecure from "./useAxiosSecure";
4 |
5 |
6 | const useAdmin = () => {
7 | const { user } = useAuth();
8 | const axiosSecure = useAxiosSecure();
9 | const { data: isAdmin, isPending: isAdminLoading } = useQuery({
10 | queryKey: [user?.email, 'isAdmin'],
11 | queryFn: async () => {
12 | const res = await axiosSecure.get(`/users/admin/${user.email}`);
13 | // console.log(res.data);
14 | return res.data?.admin;
15 | }
16 | })
17 | return [isAdmin, isAdminLoading]
18 | };
19 |
20 | export default useAdmin;
--------------------------------------------------------------------------------
/src/hooks/useAuth.jsx:
--------------------------------------------------------------------------------
1 | import { useContext } from "react";
2 | import { AuthContext } from "../providers/AuthProvider";
3 |
4 |
5 | const useAuth = () => {
6 | const auth = useContext(AuthContext);
7 | return auth;
8 | };
9 |
10 | export default useAuth;
--------------------------------------------------------------------------------
/src/hooks/useAxiosPublic.jsx:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 |
3 | const axiosPublic = axios.create({
4 | baseURL: 'http://localhost:5000'
5 | })
6 |
7 | const useAxiosPublic = () => {
8 | return axiosPublic;
9 | };
10 |
11 | export default useAxiosPublic;
--------------------------------------------------------------------------------
/src/hooks/useAxiosSecure.jsx:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import { useNavigate } from "react-router-dom";
3 | import useAuth from "./useAuth";
4 |
5 | const axiosSecure = axios.create({
6 | baseURL: 'http://localhost:5000'
7 | })
8 | const useAxiosSecure = () => {
9 | const navigate = useNavigate();
10 | const { logOut } = useAuth();
11 |
12 | // request interceptor to add authorization header for every secure call to teh api
13 | axiosSecure.interceptors.request.use(function (config) {
14 | const token = localStorage.getItem('access-token')
15 | // console.log('request stopped by interceptors', token)
16 | config.headers.authorization = `Bearer ${token}`;
17 | return config;
18 | }, function (error) {
19 | // Do something with request error
20 | return Promise.reject(error);
21 | });
22 |
23 |
24 | // intercepts 401 and 403 status
25 | axiosSecure.interceptors.response.use(function (response) {
26 | return response;
27 | }, async (error) => {
28 | const status = error.response.status;
29 | // console.log('status error in the interceptor', status);
30 | // for 401 or 403 logout the user and move the user to the login
31 | if (status === 401 || status === 403) {
32 | await logOut();
33 | navigate('/login');
34 | }
35 | return Promise.reject(error);
36 | })
37 |
38 |
39 | return axiosSecure;
40 | };
41 |
42 | export default useAxiosSecure;
--------------------------------------------------------------------------------
/src/hooks/useCart.jsx:
--------------------------------------------------------------------------------
1 | // api, axios (axios secure), tan stack
2 |
3 | import { useQuery } from "@tanstack/react-query";
4 | import useAxiosSecure from "./useAxiosSecure";
5 | import useAuth from "./useAuth";
6 |
7 | const useCart = () => {
8 | const axiosSecure = useAxiosSecure();
9 | const { user} = useAuth();
10 | const { refetch, data: cart = [] } = useQuery({
11 | queryKey: ['cart', user?.email],
12 | queryFn: async() => {
13 | const res = await axiosSecure.get(`/carts?email=${user.email}`);
14 | return res.data;
15 | }
16 | })
17 |
18 | return [cart, refetch]
19 | };
20 |
21 | export default useCart;
--------------------------------------------------------------------------------
/src/hooks/useMenu.jsx:
--------------------------------------------------------------------------------
1 | // import { useEffect, useState } from "react";
2 |
3 | import { useQuery } from "@tanstack/react-query";
4 | import useAxiosPublic from "./useAxiosPublic";
5 |
6 | const useMenu = () => {
7 | const axiosPublic = useAxiosPublic();
8 | // const [menu, setMenu] = useState([]);
9 | // const [loading, setLoading] = useState(true);
10 | // useEffect(() => {
11 | // fetch('http://localhost:5000/menu')
12 | // .then(res => res.json())
13 | // .then(data => {
14 | // setMenu(data);
15 | // setLoading(false);
16 | // });
17 | // }, [])
18 |
19 | const {data: menu = [], isPending: loading, refetch} = useQuery({
20 | queryKey: ['menu'],
21 | queryFn: async() =>{
22 | const res = await axiosPublic.get('/menu');
23 | return res.data;
24 | }
25 | })
26 |
27 |
28 | return [menu, loading, refetch]
29 | }
30 |
31 | export default useMenu;
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import './index.css'
4 | import {
5 | RouterProvider,
6 | } from "react-router-dom";
7 | import { router } from './Routes/Routes';
8 | import { HelmetProvider } from 'react-helmet-async';
9 | import AuthProvider from './providers/AuthProvider';
10 | import {
11 | QueryClient,
12 | QueryClientProvider,
13 | } from '@tanstack/react-query'
14 |
15 | const queryClient = new QueryClient();
16 |
17 | ReactDOM.createRoot(document.getElementById('root')).render(
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | ,
29 | )
30 |
--------------------------------------------------------------------------------
/src/pages/Dashboard/AddItems/AddItems.jsx:
--------------------------------------------------------------------------------
1 | import { useForm } from "react-hook-form";
2 | import SectionTitle from "../../../components/SectionTitle/SectionTitle";
3 | import { FaUtensils } from "react-icons/fa";
4 | import useAxiosPublic from "../../../hooks/useAxiosPublic";
5 | import useAxiosSecure from "../../../hooks/useAxiosSecure";
6 | import Swal from "sweetalert2";
7 |
8 | const image_hosting_key = import.meta.env.VITE_IMAGE_HOSTING_KEY;
9 | const image_hosting_api = `https://api.imgbb.com/1/upload?key=${image_hosting_key}`;
10 |
11 | const AddItems = () => {
12 | const { register, handleSubmit, reset } = useForm();
13 | const axiosPublic = useAxiosPublic();
14 | const axiosSecure = useAxiosSecure();
15 | const onSubmit = async (data) => {
16 | console.log(data)
17 | // image upload to imgbb and then get an url
18 | const imageFile = { image: data.image[0] }
19 | const res = await axiosPublic.post(image_hosting_api, imageFile, {
20 | headers: {
21 | 'content-type': 'multipart/form-data'
22 | }
23 | });
24 | if (res.data.success) {
25 | // now send the menu item data to the server with the image url
26 | const menuItem = {
27 | name: data.name,
28 | category: data.category,
29 | price: parseFloat(data.price),
30 | recipe: data.recipe,
31 | image: res.data.data.display_url
32 | }
33 | //
34 | const menuRes = await axiosSecure.post('/menu', menuItem);
35 | console.log(menuRes.data)
36 | if(menuRes.data.insertedId){
37 | // show success popup
38 | reset();
39 | Swal.fire({
40 | position: "top-end",
41 | icon: "success",
42 | title: `${data.name} is added to the menu.`,
43 | showConfirmButton: false,
44 | timer: 1500
45 | });
46 | }
47 | }
48 | console.log( 'with image url', res.data);
49 | };
50 |
51 | return (
52 |
115 | );
116 | };
117 |
118 | export default AddItems;
--------------------------------------------------------------------------------
/src/pages/Dashboard/AllUsers/AllUsers.jsx:
--------------------------------------------------------------------------------
1 | import { useQuery } from "@tanstack/react-query";
2 | import useAxiosSecure from "../../../hooks/useAxiosSecure";
3 | import { FaTrashAlt, FaUsers } from "react-icons/fa";
4 | import Swal from "sweetalert2";
5 |
6 |
7 | const AllUsers = () => {
8 | const axiosSecure = useAxiosSecure();
9 | const { data: users = [], refetch } = useQuery({
10 | queryKey: ['users'],
11 | queryFn: async () => {
12 | const res = await axiosSecure.get('/users');
13 | return res.data;
14 | }
15 | })
16 |
17 | const handleMakeAdmin = user =>{
18 | axiosSecure.patch(`/users/admin/${user._id}`)
19 | .then(res =>{
20 | console.log(res.data)
21 | if(res.data.modifiedCount > 0){
22 | refetch();
23 | Swal.fire({
24 | position: "top-end",
25 | icon: "success",
26 | title: `${user.name} is an Admin Now!`,
27 | showConfirmButton: false,
28 | timer: 1500
29 | });
30 | }
31 | })
32 | }
33 |
34 | const handleDeleteUser = user => {
35 | Swal.fire({
36 | title: "Are you sure?",
37 | text: "You won't be able to revert this!",
38 | icon: "warning",
39 | showCancelButton: true,
40 | confirmButtonColor: "#3085d6",
41 | cancelButtonColor: "#d33",
42 | confirmButtonText: "Yes, delete it!"
43 | }).then((result) => {
44 | if (result.isConfirmed) {
45 |
46 | axiosSecure.delete(`/users/${user._id}`)
47 | .then(res => {
48 | if (res.data.deletedCount > 0) {
49 | refetch();
50 | Swal.fire({
51 | title: "Deleted!",
52 | text: "Your file has been deleted.",
53 | icon: "success"
54 | });
55 | }
56 | })
57 | }
58 | });
59 | }
60 |
61 | return (
62 |
63 |
64 |
All Users
65 | Total Users: {users.length}
66 |
67 |
68 |
69 | {/* head */}
70 |
71 |
72 |
73 | Name
74 | Email
75 | Role
76 | Action
77 |
78 |
79 |
80 | {
81 | users.map((user, index) =>
82 | {index + 1}
83 | {user.name}
84 | {user.email}
85 |
86 | { user.role === 'admin' ? 'Admin' : handleMakeAdmin(user)}
88 | className="btn btn-lg bg-orange-500">
89 |
91 | }
92 |
93 |
94 | handleDeleteUser(user)}
96 | className="btn btn-ghost btn-lg">
97 |
98 |
99 |
100 | )
101 | }
102 |
103 |
104 |
105 |
106 |
107 | );
108 | };
109 |
110 | export default AllUsers;
--------------------------------------------------------------------------------
/src/pages/Dashboard/Cart/Cart.jsx:
--------------------------------------------------------------------------------
1 | import { FaTrashAlt } from "react-icons/fa";
2 | import useCart from "../../../hooks/useCart";
3 | import Swal from "sweetalert2";
4 | import useAxiosSecure from "../../../hooks/useAxiosSecure";
5 |
6 |
7 | const Cart = () => {
8 | const [cart, refetch] = useCart();
9 | const totalPrice = cart.reduce((total, item) => total + item.price, 0);
10 | const axiosSecure = useAxiosSecure();
11 |
12 | const handleDelete = id => {
13 | Swal.fire({
14 | title: "Are you sure?",
15 | text: "You won't be able to revert this!",
16 | icon: "warning",
17 | showCancelButton: true,
18 | confirmButtonColor: "#3085d6",
19 | cancelButtonColor: "#d33",
20 | confirmButtonText: "Yes, delete it!"
21 | }).then((result) => {
22 | if (result.isConfirmed) {
23 |
24 | axiosSecure.delete(`/carts/${id}`)
25 | .then(res => {
26 | if (res.data.deletedCount > 0) {
27 | refetch();
28 | Swal.fire({
29 | title: "Deleted!",
30 | text: "Your file has been deleted.",
31 | icon: "success"
32 | });
33 | }
34 | })
35 | }
36 | });
37 | }
38 |
39 | return (
40 |
41 |
42 |
Items: {cart.length}
43 | Total Price: {totalPrice}
44 | Pay
45 |
46 |
47 |
48 |
49 | {/* head */}
50 |
51 |
52 |
53 | #
54 |
55 | Image
56 | Name
57 | Price
58 | Action
59 |
60 |
61 |
62 | {
63 | cart.map((item, index) =>
64 |
65 | {index + 1}
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | {item.name}
78 |
79 | ${item.price}
80 |
81 | handleDelete(item._id)}
83 | className="btn btn-ghost btn-lg">
84 |
85 |
86 |
87 | )
88 | }
89 |
90 |
91 |
92 |
93 |
94 |
95 | );
96 | };
97 |
98 | export default Cart;
--------------------------------------------------------------------------------
/src/pages/Dashboard/ManageItems/ManageItems.jsx:
--------------------------------------------------------------------------------
1 | import { FaEdit, FaTrashAlt } from "react-icons/fa";
2 | import SectionTitle from "../../../components/SectionTitle/SectionTitle";
3 | import useMenu from "../../../hooks/useMenu";
4 | import Swal from "sweetalert2";
5 | import useAxiosSecure from "../../../hooks/useAxiosSecure";
6 | import { Link } from "react-router-dom";
7 |
8 |
9 | const ManageItems = () => {
10 | const [menu, , refetch] = useMenu();
11 | const axiosSecure = useAxiosSecure();
12 |
13 | const handleDeleteItem = (item) => {
14 | Swal.fire({
15 | title: "Are you sure?",
16 | text: "You won't be able to revert this!",
17 | icon: "warning",
18 | showCancelButton: true,
19 | confirmButtonColor: "#3085d6",
20 | cancelButtonColor: "#d33",
21 | confirmButtonText: "Yes, delete it!"
22 | }).then(async (result) => {
23 | if (result.isConfirmed) {
24 | const res = await axiosSecure.delete(`/menu/${item._id}`);
25 | // console.log(res.data);
26 | if (res.data.deletedCount > 0) {
27 | // refetch to update the ui
28 | refetch();
29 | Swal.fire({
30 | position: "top-end",
31 | icon: "success",
32 | title: `${item.name} has been deleted`,
33 | showConfirmButton: false,
34 | timer: 1500
35 | });
36 | }
37 |
38 |
39 | }
40 | });
41 | }
42 |
43 | return (
44 |
45 |
46 |
47 |
48 |
49 | {/* head */}
50 |
51 |
52 |
53 | #
54 |
55 | Image
56 | Item Name
57 | Price
58 | Update
59 | Delete
60 |
61 |
62 |
63 | {
64 | menu.map((item, index) =>
65 |
66 | {index + 1}
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | {item.name}
79 |
80 | ${item.price}
81 |
82 |
83 |
85 |
87 |
88 |
89 |
90 |
91 | handleDeleteItem(item)}
93 | className="btn btn-ghost btn-lg">
94 |
95 |
96 |
97 | )
98 | }
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | );
107 | };
108 |
109 | export default ManageItems;
--------------------------------------------------------------------------------
/src/pages/Dashboard/UpdateItem/UpdateItem.jsx:
--------------------------------------------------------------------------------
1 | import { useLoaderData } from "react-router-dom";
2 | import SectionTitle from "../../../components/SectionTitle/SectionTitle";
3 | import { useForm } from "react-hook-form";
4 | import Swal from "sweetalert2";
5 | import useAxiosPublic from "../../../hooks/useAxiosPublic";
6 | import useAxiosSecure from "../../../hooks/useAxiosSecure";
7 |
8 | const image_hosting_key = import.meta.env.VITE_IMAGE_HOSTING_KEY;
9 | const image_hosting_api = `https://api.imgbb.com/1/upload?key=${image_hosting_key}`;
10 |
11 | const UpdateItem = () => {
12 | const {name, category, recipe, price, _id} = useLoaderData();
13 |
14 | const { register, handleSubmit } = useForm();
15 | const axiosPublic = useAxiosPublic();
16 | const axiosSecure = useAxiosSecure();
17 | const onSubmit = async (data) => {
18 | console.log(data)
19 | // image upload to imgbb and then get an url
20 | const imageFile = { image: data.image[0] }
21 | const res = await axiosPublic.post(image_hosting_api, imageFile, {
22 | headers: {
23 | 'content-type': 'multipart/form-data'
24 | }
25 | });
26 | if (res.data.success) {
27 | // now send the menu item data to the server with the image url
28 | const menuItem = {
29 | name: data.name,
30 | category: data.category,
31 | price: parseFloat(data.price),
32 | recipe: data.recipe,
33 | image: res.data.data.display_url
34 | }
35 | //
36 | const menuRes = await axiosSecure.patch(`/menu/${_id}`, menuItem);
37 | console.log(menuRes.data)
38 | if(menuRes.data.modifiedCount > 0){
39 | // show success popup
40 | // reset();
41 | Swal.fire({
42 | position: "top-end",
43 | icon: "success",
44 | title: `${data.name} is updated to the menu.`,
45 | showConfirmButton: false,
46 | timer: 1500
47 | });
48 | }
49 | }
50 | console.log( 'with image url', res.data);
51 | };
52 |
53 |
54 | return (
55 |
56 |
57 |
58 |
59 |
60 |
61 | Recipe Name*
62 |
63 |
70 |
71 |
72 | {/* category */}
73 |
74 |
75 | Category*
76 |
77 |
79 | Select a category
80 | Salad
81 | Pizza
82 | Soup
83 | Dessert
84 | Drinks
85 |
86 |
87 |
88 | {/* price */}
89 |
90 |
91 | Price*
92 |
93 |
99 |
100 |
101 |
102 | {/* recipe details */}
103 |
104 |
105 | Recipe Details
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 | Update menu Item
116 |
117 |
118 |
119 |
120 | );
121 | };
122 |
123 | export default UpdateItem;
--------------------------------------------------------------------------------
/src/pages/Dashboard/__Page__level_security__.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 1. do not show the link to those user who should not see the link
3 | * 2. even if they gets the link, do not allow them to visit the link
4 | * 3. do not allow user to access the api. check admin in the server as well
5 | * (verifyAdmin)
6 | */
--------------------------------------------------------------------------------
/src/pages/Home/Banner/Banner.jsx:
--------------------------------------------------------------------------------
1 | import { Carousel } from 'react-responsive-carousel';
2 | import "react-responsive-carousel/lib/styles/carousel.min.css";
3 |
4 | import img1 from '../../../assets/home/01.jpg';
5 | import img2 from '../../../assets/home/02.jpg';
6 | import img3 from '../../../assets/home/03.png';
7 | import img4 from '../../../assets/home/04.jpg';
8 | import img5 from '../../../assets/home/05.png';
9 | import img6 from '../../../assets/home/06.png';
10 |
11 | const Banner = () => {
12 | return (
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | );
34 | };
35 |
36 | export default Banner;
--------------------------------------------------------------------------------
/src/pages/Home/Category/Category.jsx:
--------------------------------------------------------------------------------
1 | import { Swiper, SwiperSlide } from "swiper/react";
2 | import { Pagination } from "swiper";
3 |
4 | import "swiper/css";
5 | import "swiper/css/pagination";
6 |
7 | import slide1 from '../../../assets/home/slide1.jpg';
8 | import slide2 from '../../../assets/home/slide2.jpg';
9 | import slide3 from '../../../assets/home/slide3.jpg';
10 | import slide4 from '../../../assets/home/slide4.jpg';
11 | import slide5 from '../../../assets/home/slide5.jpg';
12 | import SectionTitle from "../../../components/SectionTitle/SectionTitle";
13 |
14 | const Category = () => {
15 | return (
16 |
17 |
21 |
31 |
32 |
33 | Salads
34 |
35 |
36 |
37 | Pizzas
38 |
39 |
40 |
41 | Soups
42 |
43 |
44 |
45 | Desserts
46 |
47 |
48 |
49 | Salads
50 |
51 |
52 |
53 | );
54 | };
55 |
56 | export default Category;
--------------------------------------------------------------------------------
/src/pages/Home/Featured/Featured.css:
--------------------------------------------------------------------------------
1 | .featured-item{
2 | background-image: url('../../../assets/home/featured.jpg');
3 | }
--------------------------------------------------------------------------------
/src/pages/Home/Featured/Featured.jsx:
--------------------------------------------------------------------------------
1 | import SectionTitle from "../../../components/SectionTitle/SectionTitle";
2 | import featuredImg from '../../../assets/home/featured.jpg';
3 | import './Featured.css';
4 |
5 |
6 | const Featured = () => {
7 | return (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
Aug 20, 2029
16 |
Where can i get some?
17 |
Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptate expedita hic dolorem, iusto vel suscipit nam excepturi debitis magnam nostrum! Ut eum dignissimos culpa doloremque eligendi consectetur blanditiis laboriosam fugiat ea quia similique quam nisi reprehenderit numquam magnam nemo vitae cupiditate, atque maiores dicta minus pariatur. Perspiciatis nobis vero quas?
18 |
Order Now
19 |
20 |
21 |
22 | );
23 | };
24 |
25 | export default Featured;
--------------------------------------------------------------------------------
/src/pages/Home/Home/Home.jsx:
--------------------------------------------------------------------------------
1 | import { Helmet } from "react-helmet-async";
2 | import Banner from "../Banner/Banner";
3 | import Category from "../Category/Category";
4 | import Featured from "../Featured/Featured";
5 | import PopularMenu from "../PopularMenu/PopularMenu";
6 | import Testimonials from "../Testimonials/Testimonials";
7 |
8 | const Home = () => {
9 | return (
10 |
11 |
12 | Bistro Boss | Home
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | );
21 | };
22 |
23 | export default Home;
--------------------------------------------------------------------------------
/src/pages/Home/PopularMenu/PopularMenu.jsx:
--------------------------------------------------------------------------------
1 | import SectionTitle from "../../../components/SectionTitle/SectionTitle";
2 | import MenuItem from "../../Shared/MenuItem/MenuItem";
3 | import useMenu from "../../../hooks/useMenu";
4 |
5 |
6 | const PopularMenu = () => {
7 | const [menu] = useMenu();
8 | const popular = menu.filter(item => item.category === 'popular');
9 |
10 | return (
11 |
12 |
16 |
17 | {
18 | popular.map(item => )
22 | }
23 |
24 | View Full Menu
25 |
26 | );
27 | };
28 |
29 | export default PopularMenu;
--------------------------------------------------------------------------------
/src/pages/Home/Testimonials/Testimonials.jsx:
--------------------------------------------------------------------------------
1 | import SectionTitle from "../../../components/SectionTitle/SectionTitle";
2 | import { Swiper, SwiperSlide } from "swiper/react";
3 | import { Navigation } from "swiper";
4 |
5 | // Import Swiper styles
6 | import "swiper/css";
7 | import "swiper/css/navigation";
8 | import { useEffect, useState } from "react";
9 | import { Rating } from "@smastrom/react-rating";
10 | import '@smastrom/react-rating/style.css'
11 |
12 |
13 | const Testimonials = () => {
14 | const [reviews, setReviews] = useState([]);
15 |
16 | useEffect(() => {
17 | fetch('http://localhost:5000/reviews')
18 | .then(res => res.json())
19 | .then(data => setReviews(data))
20 | }, [])
21 |
22 | return (
23 |
24 |
28 |
29 |
30 |
31 | {
32 | reviews.map(review =>
35 |
36 |
41 |
{review.details}
42 |
{review.name}
43 |
44 | )
45 | }
46 |
47 |
48 | );
49 | };
50 |
51 | export default Testimonials;
--------------------------------------------------------------------------------
/src/pages/Login/Login.jsx:
--------------------------------------------------------------------------------
1 | import { useContext, useEffect, useState } from 'react';
2 | import { loadCaptchaEnginge, LoadCanvasTemplate, validateCaptcha } from 'react-simple-captcha';
3 | import { AuthContext } from '../../providers/AuthProvider';
4 | import { Link, useLocation, useNavigate } from 'react-router-dom';
5 | import { Helmet } from 'react-helmet-async';
6 | import Swal from 'sweetalert2'
7 | import SocialLogin from '../../components/SocialLogin/SocialLogin';
8 |
9 | const Login = () => {
10 | const [disabled, setDisabled] = useState(true);
11 | const { signIn } = useContext(AuthContext);
12 | const navigate = useNavigate();
13 | const location = useLocation();
14 |
15 | const from = location.state?.from?.pathname || "/";
16 | console.log('state in the location login page', location.state)
17 |
18 | useEffect(() => {
19 | loadCaptchaEnginge(6);
20 | }, [])
21 |
22 | const handleLogin = event => {
23 | event.preventDefault();
24 | const form = event.target;
25 | const email = form.email.value;
26 | const password = form.password.value;
27 | console.log(email, password);
28 | signIn(email, password)
29 | .then(result => {
30 | const user = result.user;
31 | console.log(user);
32 | Swal.fire({
33 | title: 'User Login Successful.',
34 | showClass: {
35 | popup: 'animate__animated animate__fadeInDown'
36 | },
37 | hideClass: {
38 | popup: 'animate__animated animate__fadeOutUp'
39 | }
40 | });
41 | navigate(from, { replace: true });
42 | })
43 | }
44 |
45 | const handleValidateCaptcha = (e) => {
46 | const user_captcha_value = e.target.value;
47 | if (validateCaptcha(user_captcha_value)) {
48 | setDisabled(false);
49 | }
50 | else {
51 | setDisabled(true)
52 | }
53 | }
54 |
55 | return (
56 | <>
57 |
58 | Bistro Boss | Login
59 |
60 |
61 |
62 |
63 |
Login now!
64 |
Provident cupiditate voluptatem et in. Quaerat fugiat ut assumenda excepturi exercitationem quasi. In deleniti eaque aut repudiandae et a id nisi.
65 |
66 |
98 |
99 |
100 | >
101 | );
102 | };
103 |
104 | export default Login;
--------------------------------------------------------------------------------
/src/pages/Menu/Menu/Menu.jsx:
--------------------------------------------------------------------------------
1 | import { Helmet } from 'react-helmet-async';
2 | import Cover from '../../Shared/Cover/Cover';
3 | import menuImg from '../../../assets/menu/menu-bg.jpg'
4 | import soupImg from '../../../assets/menu/soup-bg.jpg'
5 | import saladImg from '../../../assets/menu/salad-bg.jpg'
6 | import pizzaImg from '../../../assets/menu/pizza-bg.jpg'
7 | import dessertImg from '../../../assets/menu/dessert-bg.jpeg'
8 | import useMenu from '../../../hooks/useMenu';
9 | import SectionTitle from '../../../components/SectionTitle/SectionTitle';
10 | import MenuCategory from '../MenuCategory/MenuCategory';
11 |
12 |
13 | const Menu = () => {
14 | const [menu] = useMenu();
15 | const desserts = menu.filter(item => item.category === 'dessert');
16 | const soup = menu.filter(item => item.category === 'soup');
17 | const salad = menu.filter(item => item.category === 'salad');
18 | const pizza = menu.filter(item => item.category === 'pizza');
19 | const offered = menu.filter(item => item.category === 'offered');
20 | return (
21 |
22 |
23 | Bistro Boss | Menu
24 |
25 |
26 | {/* main cover */}
27 |
28 | {/* offered menu items */}
29 |
30 | {/* dessert menu items */}
31 |
32 |
33 |
34 |
35 |
36 | );
37 | };
38 |
39 | export default Menu;
--------------------------------------------------------------------------------
/src/pages/Menu/MenuCategory/MenuCategory.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { Link } from 'react-router-dom';
3 | import Cover from '../../Shared/Cover/Cover';
4 | import MenuItem from '../../Shared/MenuItem/MenuItem';
5 |
6 | const MenuCategory = ({items, title, img}) => {
7 | return (
8 |
9 | { title &&
}
10 |
11 | {
12 | items.map(item => )
16 | }
17 |
18 |
19 |
Order Now
20 |
21 |
22 | );
23 | };
24 |
25 | export default MenuCategory;
--------------------------------------------------------------------------------
/src/pages/Order/Order/Order.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import orderCoverImg from '../../../assets/shop/order.jpg'
3 | import Cover from '../../Shared/Cover/Cover';
4 | import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
5 | import 'react-tabs/style/react-tabs.css';
6 | import useMenu from '../../../hooks/useMenu';
7 | import OrderTab from '../OrderTab/OrderTab';
8 | import { useParams } from 'react-router';
9 | import { Helmet } from 'react-helmet-async';
10 |
11 | const Order = () => {
12 | const categories = ['salad', 'pizza', 'soup', 'dessert', 'drinks'];
13 | const { category } = useParams();
14 | const initialIndex = categories.indexOf(category);
15 | const [tabIndex, setTabIndex] = useState(initialIndex);
16 | const [menu] = useMenu();
17 |
18 | const desserts = menu.filter(item => item.category === 'dessert');
19 | const soup = menu.filter(item => item.category === 'soup');
20 | const salad = menu.filter(item => item.category === 'salad');
21 | const pizza = menu.filter(item => item.category === 'pizza');
22 | const drinks = menu.filter(item => item.category === 'drinks');
23 |
24 | return (
25 |
26 |
27 | Bistro Boss | Order Food
28 |
29 |
30 | setTabIndex(index)}>
31 |
32 | Salad
33 | Pizza
34 | Soup
35 | Dessert
36 | Drinks
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | );
56 | };
57 |
58 | export default Order;
--------------------------------------------------------------------------------
/src/pages/Order/OrderTab/OrderTab.jsx:
--------------------------------------------------------------------------------
1 | import FoodCard from '../../../components/FoodCard/FoodCard';
2 | import { Swiper, SwiperSlide } from "swiper/react";
3 | import { Pagination } from "swiper";
4 | import "swiper/css";
5 | import "swiper/css/pagination";
6 |
7 | const OrderTab = ({ items }) => {
8 | const pagination = {
9 | clickable: true,
10 | renderBullet: function (index, className) {
11 | return '' + (index + 1) + " ";
12 | },
13 | };
14 | return (
15 |
16 |
17 |
22 |
23 |
24 | {
25 | items.map(item => )
29 | }
30 |
31 |
32 |
33 |
34 |
35 |
36 | );
37 | };
38 |
39 | export default OrderTab;
--------------------------------------------------------------------------------
/src/pages/Shared/Cover/Cover.jsx:
--------------------------------------------------------------------------------
1 | import { Parallax } from 'react-parallax';
2 |
3 | const Cover = ({ img, title }) => {
4 | return (
5 |
11 |
12 |
13 |
14 |
15 |
{title}
16 |
Provident cupiditate voluptatem et in. Quaerat fugiat ut assumenda excepturi exercitationem quasi. In deleniti eaque aut repudiandae et a id nisi.
17 |
18 |
19 |
20 |
21 |
22 |
23 | );
24 | };
25 |
26 | export default Cover;
--------------------------------------------------------------------------------
/src/pages/Shared/Footer/Footer.jsx:
--------------------------------------------------------------------------------
1 |
2 | const Footer = () => {
3 | return (
4 |
5 |
6 |
7 |
8 |
ACME Industries Ltd. Providing reliable tech since 1992
9 |
10 |
18 |
19 |
20 |
21 |
Copyright © 2023 - All right reserved by ACME Industries Ltd
22 |
23 |
24 |
25 | );
26 | };
27 |
28 | export default Footer;
--------------------------------------------------------------------------------
/src/pages/Shared/MenuItem/MenuItem.jsx:
--------------------------------------------------------------------------------
1 |
2 |
3 | const MenuItem = ({item}) => {
4 | const {name, image, price, recipe} = item;
5 | return (
6 |
7 |
8 |
9 |
{name}----------
10 |
{recipe}
11 |
12 |
${price}
13 |
14 | );
15 | };
16 |
17 | export default MenuItem;
--------------------------------------------------------------------------------
/src/pages/Shared/NavBar/NavBar.jsx:
--------------------------------------------------------------------------------
1 | import { useContext } from "react";
2 | import { Link } from "react-router-dom";
3 | import { AuthContext } from "../../../providers/AuthProvider";
4 | import { FaShoppingCart } from 'react-icons/fa';
5 | import useCart from "../../../hooks/useCart";
6 |
7 | const NavBar = () => {
8 | const { user, logOut } = useContext(AuthContext);
9 | const [cart] = useCart();
10 |
11 | const handleLogOut = () => {
12 | logOut()
13 | .then(() => { })
14 | .catch(error => console.log(error));
15 | }
16 |
17 | const navOptions = <>
18 | Home
19 | Our Menu
20 | Order Food
21 | Secret
22 |
23 |
24 |
25 |
26 | +{cart.length}
27 |
28 |
29 |
30 | {
31 | user ? <>
32 | {/* {user?.displayName} */}
33 | LogOut
34 | > : <>
35 | Login
36 | >
37 | }
38 | >
39 |
40 | return (
41 | <>
42 |
63 | >
64 | );
65 | };
66 |
67 | export default NavBar;
--------------------------------------------------------------------------------
/src/pages/Shared/Secret/Secret.jsx:
--------------------------------------------------------------------------------
1 |
2 | const Secret = () => {
3 | return (
4 |
5 |
Secret things
6 |
7 | );
8 | };
9 |
10 | export default Secret;
--------------------------------------------------------------------------------
/src/pages/SignUp/SignUp.jsx:
--------------------------------------------------------------------------------
1 | import { useContext } from "react";
2 | import { Helmet } from "react-helmet-async";
3 | import { useForm } from "react-hook-form";
4 | import { AuthContext } from "../../providers/AuthProvider";
5 | import { Link, useNavigate } from "react-router-dom";
6 | import Swal from 'sweetalert2'
7 | import useAxiosPublic from "../../hooks/useAxiosPublic";
8 | import SocialLogin from "../../components/SocialLogin/SocialLogin";
9 |
10 | const SignUp = () => {
11 | const axiosPublic = useAxiosPublic();
12 | const { register, handleSubmit, reset, formState: { errors } } = useForm();
13 | const { createUser, updateUserProfile } = useContext(AuthContext);
14 | const navigate = useNavigate();
15 |
16 | const onSubmit = data => {
17 |
18 | createUser(data.email, data.password)
19 | .then(result => {
20 | const loggedUser = result.user;
21 | console.log(loggedUser);
22 | updateUserProfile(data.name, data.photoURL)
23 | .then(() => {
24 | // create user entry in the database
25 | const userInfo = {
26 | name: data.name,
27 | email: data.email
28 | }
29 | axiosPublic.post('/users', userInfo)
30 | .then(res => {
31 | if (res.data.insertedId) {
32 | console.log('user added to the database')
33 | reset();
34 | Swal.fire({
35 | position: 'top-end',
36 | icon: 'success',
37 | title: 'User created successfully.',
38 | showConfirmButton: false,
39 | timer: 1500
40 | });
41 | navigate('/');
42 | }
43 | })
44 |
45 |
46 | })
47 | .catch(error => console.log(error))
48 | })
49 | };
50 |
51 | return (
52 | <>
53 |
54 | Bistro Boss | Sign Up
55 |
56 |
57 |
58 |
59 |
Sign up now!
60 |
Provident cupiditate voluptatem et in. Quaerat fugiat ut assumenda excepturi exercitationem quasi. In deleniti eaque aut repudiandae et a id nisi.
61 |
62 |
63 |
64 |
65 |
66 | Name
67 |
68 |
69 | {errors.name && Name is required }
70 |
71 |
72 |
73 | Photo URL
74 |
75 |
76 | {errors.photoURL && Photo URL is required }
77 |
78 |
79 |
80 | Email
81 |
82 |
83 | {errors.email && Email is required }
84 |
85 |
86 |
87 | Password
88 |
89 |
95 | {errors.password?.type === 'required' &&
Password is required
}
96 | {errors.password?.type === 'minLength' &&
Password must be 6 characters
}
97 | {errors.password?.type === 'maxLength' &&
Password must be less than 20 characters
}
98 | {errors.password?.type === 'pattern' &&
Password must have one Uppercase one lower case, one number and one special character.
}
99 |
100 | Forgot password?
101 |
102 |
103 |
104 |
105 |
106 |
107 |
Already have an account Login
108 |
109 |
110 |
111 |
112 | >
113 | );
114 | };
115 |
116 | export default SignUp;
--------------------------------------------------------------------------------
/src/providers/AuthProvider.jsx:
--------------------------------------------------------------------------------
1 | import { createContext, useEffect, useState } from "react";
2 | import { GoogleAuthProvider, createUserWithEmailAndPassword, getAuth, onAuthStateChanged, signInWithEmailAndPassword, signInWithPopup, signOut, updateProfile } from "firebase/auth";
3 | import { app } from "../firebase/firebase.config";
4 | import useAxiosPublic from "../hooks/useAxiosPublic";
5 |
6 | export const AuthContext = createContext(null);
7 |
8 | const auth = getAuth(app);
9 |
10 | const AuthProvider = ({ children }) => {
11 | const [user, setUser] = useState(null);
12 | const [loading, setLoading] = useState(true);
13 | const googleProvider = new GoogleAuthProvider();
14 | const axiosPublic = useAxiosPublic();
15 |
16 | const createUser = (email, password) => {
17 | setLoading(true);
18 | return createUserWithEmailAndPassword(auth, email, password)
19 | }
20 |
21 | const signIn = (email, password) => {
22 | setLoading(true);
23 | return signInWithEmailAndPassword(auth, email, password);
24 | }
25 |
26 | const googleSignIn = () => {
27 | setLoading(true);
28 | return signInWithPopup(auth, googleProvider);
29 | }
30 |
31 | const logOut = () => {
32 | setLoading(true);
33 | return signOut(auth);
34 | }
35 |
36 | const updateUserProfile = (name, photo) => {
37 | return updateProfile(auth.currentUser, {
38 | displayName: name, photoURL: photo
39 | });
40 | }
41 |
42 | useEffect(() => {
43 | const unsubscribe = onAuthStateChanged(auth, currentUser => {
44 | setUser(currentUser);
45 | if (currentUser) {
46 | // get token and store client
47 | const userInfo = { email: currentUser.email };
48 | axiosPublic.post('/jwt', userInfo)
49 | .then(res => {
50 | if (res.data.token) {
51 | localStorage.setItem('access-token', res.data.token);
52 | }
53 | })
54 | }
55 | else {
56 | // TODO: remove token (if token stored in the client side: Local storage, caching, in memory)
57 | localStorage.removeItem('access-token');
58 | }
59 | setLoading(false);
60 | });
61 | return () => {
62 | return unsubscribe();
63 | }
64 | }, [axiosPublic])
65 |
66 | const authInfo = {
67 | user,
68 | loading,
69 | createUser,
70 | signIn,
71 | googleSignIn,
72 | logOut,
73 | updateUserProfile
74 | }
75 |
76 | return (
77 |
78 | {children}
79 |
80 | );
81 | };
82 |
83 | export default AuthProvider;
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | export default {
3 | content: [
4 | "./index.html",
5 | "./src/**/*.{js,ts,jsx,tsx}",
6 | ],
7 | theme: {
8 | extend: {},
9 | },
10 | plugins: [require("daisyui")],
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------