├── README.md ├── next-env.d.ts ├── next.config.mjs ├── package-lock.json ├── package.json ├── public ├── logo.svg ├── next.svg └── vercel.svg ├── src ├── app │ ├── dashboard │ │ ├── category │ │ │ ├── page.tsx │ │ │ └── styles.module.scss │ │ ├── components │ │ │ ├── button │ │ │ │ ├── index.tsx │ │ │ │ └── styles.module.scss │ │ │ ├── header │ │ │ │ ├── index.tsx │ │ │ │ └── styles.module.scss │ │ │ ├── modal │ │ │ │ ├── index.tsx │ │ │ │ └── styles.module.scss │ │ │ └── orders │ │ │ │ ├── index.tsx │ │ │ │ └── styles.module.scss │ │ ├── layout.tsx │ │ ├── page.tsx │ │ └── product │ │ │ ├── components │ │ │ └── form │ │ │ │ ├── index.tsx │ │ │ │ └── styles.module.scss │ │ │ └── page.tsx │ ├── favicon.ico │ ├── globals.scss │ ├── layout.tsx │ ├── page.module.scss │ ├── page.tsx │ └── signup │ │ └── page.tsx ├── lib │ ├── cookieClient.ts │ ├── cookieServer.ts │ ├── helper.ts │ └── order.type.ts ├── middleware.ts ├── providers │ └── order.tsx └── services │ └── api.ts └── tsconfig.json /README.md: -------------------------------------------------------------------------------- 1 | ## Getting Started 2 | 3 | First, run the development server: 4 | 5 | ```bash 6 | npm run dev 7 | # or 8 | yarn dev 9 | # or 10 | pnpm dev 11 | # or 12 | bun dev 13 | ``` 14 | 15 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 16 | 17 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 18 | 19 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 20 | 21 | ## Learn More 22 | 23 | To learn more about Next.js, take a look at the following resources: 24 | 25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 27 | 28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 29 | 30 | ## Deploy on Vercel 31 | 32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 33 | 34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 35 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | images:{ 4 | remotePatterns:[ 5 | { 6 | //protocol: 'https', 7 | hostname: 'res.cloudinary.com' 8 | } 9 | ] 10 | } 11 | }; 12 | 13 | export default nextConfig; 14 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "frontend", 9 | "version": "0.1.0", 10 | "dependencies": { 11 | "axios": "^1.7.5", 12 | "cookies-next": "^4.2.1", 13 | "lucide-react": "^0.436.0", 14 | "next": "14.2.6", 15 | "react": "^18", 16 | "react-dom": "^18", 17 | "sass": "^1.77.8", 18 | "sonner": "^1.5.0" 19 | }, 20 | "devDependencies": { 21 | "@types/node": "^20", 22 | "@types/react": "^18", 23 | "@types/react-dom": "^18", 24 | "typescript": "^5" 25 | } 26 | }, 27 | "node_modules/@next/env": { 28 | "version": "14.2.6", 29 | "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.6.tgz", 30 | "integrity": "sha512-bs5DFKV+08EjWrl8EB+KKqev1ZTNONH1vFCaHh911aaB362NnP32UDTbE9VQhyiAgbFqJsfDkSxFERNDDb3j0g==", 31 | "license": "MIT" 32 | }, 33 | "node_modules/@next/swc-darwin-arm64": { 34 | "version": "14.2.6", 35 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.6.tgz", 36 | "integrity": "sha512-BtJZb+hYXGaVJJivpnDoi3JFVn80SHKCiiRUW3kk1SY6UCUy5dWFFSbh+tGi5lHAughzeduMyxbLt3pspvXNSg==", 37 | "cpu": [ 38 | "arm64" 39 | ], 40 | "license": "MIT", 41 | "optional": true, 42 | "os": [ 43 | "darwin" 44 | ], 45 | "engines": { 46 | "node": ">= 10" 47 | } 48 | }, 49 | "node_modules/@next/swc-darwin-x64": { 50 | "version": "14.2.6", 51 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.6.tgz", 52 | "integrity": "sha512-ZHRbGpH6KHarzm6qEeXKSElSXh8dS2DtDPjQt3IMwY8QVk7GbdDYjvV4NgSnDA9huGpGgnyy3tH8i5yHCqVkiQ==", 53 | "cpu": [ 54 | "x64" 55 | ], 56 | "license": "MIT", 57 | "optional": true, 58 | "os": [ 59 | "darwin" 60 | ], 61 | "engines": { 62 | "node": ">= 10" 63 | } 64 | }, 65 | "node_modules/@next/swc-linux-arm64-gnu": { 66 | "version": "14.2.6", 67 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.6.tgz", 68 | "integrity": "sha512-O4HqUEe3ZvKshXHcDUXn1OybN4cSZg7ZdwHJMGCXSUEVUqGTJVsOh17smqilIjooP/sIJksgl+1kcf2IWMZWHg==", 69 | "cpu": [ 70 | "arm64" 71 | ], 72 | "license": "MIT", 73 | "optional": true, 74 | "os": [ 75 | "linux" 76 | ], 77 | "engines": { 78 | "node": ">= 10" 79 | } 80 | }, 81 | "node_modules/@next/swc-linux-arm64-musl": { 82 | "version": "14.2.6", 83 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.6.tgz", 84 | "integrity": "sha512-xUcdhr2hfalG8RDDGSFxQ75yOG894UlmFS4K2M0jLrUhauRBGOtUOxoDVwiIIuZQwZ3Y5hDsazNjdYGB0cQ9yQ==", 85 | "cpu": [ 86 | "arm64" 87 | ], 88 | "license": "MIT", 89 | "optional": true, 90 | "os": [ 91 | "linux" 92 | ], 93 | "engines": { 94 | "node": ">= 10" 95 | } 96 | }, 97 | "node_modules/@next/swc-linux-x64-gnu": { 98 | "version": "14.2.6", 99 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.6.tgz", 100 | "integrity": "sha512-InosKxw8UMcA/wEib5n2QttwHSKHZHNSbGcMepBM0CTcNwpxWzX32KETmwbhKod3zrS8n1vJ+DuJKbL9ZAB0Ag==", 101 | "cpu": [ 102 | "x64" 103 | ], 104 | "license": "MIT", 105 | "optional": true, 106 | "os": [ 107 | "linux" 108 | ], 109 | "engines": { 110 | "node": ">= 10" 111 | } 112 | }, 113 | "node_modules/@next/swc-linux-x64-musl": { 114 | "version": "14.2.6", 115 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.6.tgz", 116 | "integrity": "sha512-d4QXfJmt5pGJ7cG8qwxKSBnO5AXuKAFYxV7qyDRHnUNvY/dgDh+oX292gATpB2AAHgjdHd5ks1wXxIEj6muLUQ==", 117 | "cpu": [ 118 | "x64" 119 | ], 120 | "license": "MIT", 121 | "optional": true, 122 | "os": [ 123 | "linux" 124 | ], 125 | "engines": { 126 | "node": ">= 10" 127 | } 128 | }, 129 | "node_modules/@next/swc-win32-arm64-msvc": { 130 | "version": "14.2.6", 131 | "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.6.tgz", 132 | "integrity": "sha512-AlgIhk4/G+PzOG1qdF1b05uKTMsuRatFlFzAi5G8RZ9h67CVSSuZSbqGHbJDlcV1tZPxq/d4G0q6qcHDKWf4aQ==", 133 | "cpu": [ 134 | "arm64" 135 | ], 136 | "license": "MIT", 137 | "optional": true, 138 | "os": [ 139 | "win32" 140 | ], 141 | "engines": { 142 | "node": ">= 10" 143 | } 144 | }, 145 | "node_modules/@next/swc-win32-ia32-msvc": { 146 | "version": "14.2.6", 147 | "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.6.tgz", 148 | "integrity": "sha512-hNukAxq7hu4o5/UjPp5jqoBEtrpCbOmnUqZSKNJG8GrUVzfq0ucdhQFVrHcLRMvQcwqqDh1a5AJN9ORnNDpgBQ==", 149 | "cpu": [ 150 | "ia32" 151 | ], 152 | "license": "MIT", 153 | "optional": true, 154 | "os": [ 155 | "win32" 156 | ], 157 | "engines": { 158 | "node": ">= 10" 159 | } 160 | }, 161 | "node_modules/@next/swc-win32-x64-msvc": { 162 | "version": "14.2.6", 163 | "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.6.tgz", 164 | "integrity": "sha512-NANtw+ead1rSDK1jxmzq3TYkl03UNK2KHqUYf1nIhNci6NkeqBD4s1njSzYGIlSHxCK+wSaL8RXZm4v+NF/pMw==", 165 | "cpu": [ 166 | "x64" 167 | ], 168 | "license": "MIT", 169 | "optional": true, 170 | "os": [ 171 | "win32" 172 | ], 173 | "engines": { 174 | "node": ">= 10" 175 | } 176 | }, 177 | "node_modules/@swc/counter": { 178 | "version": "0.1.3", 179 | "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", 180 | "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", 181 | "license": "Apache-2.0" 182 | }, 183 | "node_modules/@swc/helpers": { 184 | "version": "0.5.5", 185 | "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", 186 | "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", 187 | "license": "Apache-2.0", 188 | "dependencies": { 189 | "@swc/counter": "^0.1.3", 190 | "tslib": "^2.4.0" 191 | } 192 | }, 193 | "node_modules/@types/cookie": { 194 | "version": "0.6.0", 195 | "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", 196 | "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", 197 | "license": "MIT" 198 | }, 199 | "node_modules/@types/node": { 200 | "version": "20.16.5", 201 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.5.tgz", 202 | "integrity": "sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA==", 203 | "dev": true, 204 | "license": "MIT", 205 | "dependencies": { 206 | "undici-types": "~6.19.2" 207 | } 208 | }, 209 | "node_modules/@types/prop-types": { 210 | "version": "15.7.12", 211 | "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", 212 | "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", 213 | "dev": true, 214 | "license": "MIT" 215 | }, 216 | "node_modules/@types/react": { 217 | "version": "18.3.5", 218 | "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz", 219 | "integrity": "sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==", 220 | "dev": true, 221 | "license": "MIT", 222 | "dependencies": { 223 | "@types/prop-types": "*", 224 | "csstype": "^3.0.2" 225 | } 226 | }, 227 | "node_modules/@types/react-dom": { 228 | "version": "18.3.0", 229 | "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", 230 | "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", 231 | "dev": true, 232 | "license": "MIT", 233 | "dependencies": { 234 | "@types/react": "*" 235 | } 236 | }, 237 | "node_modules/anymatch": { 238 | "version": "3.1.3", 239 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 240 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 241 | "license": "ISC", 242 | "dependencies": { 243 | "normalize-path": "^3.0.0", 244 | "picomatch": "^2.0.4" 245 | }, 246 | "engines": { 247 | "node": ">= 8" 248 | } 249 | }, 250 | "node_modules/asynckit": { 251 | "version": "0.4.0", 252 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 253 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", 254 | "license": "MIT" 255 | }, 256 | "node_modules/axios": { 257 | "version": "1.7.7", 258 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", 259 | "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", 260 | "license": "MIT", 261 | "dependencies": { 262 | "follow-redirects": "^1.15.6", 263 | "form-data": "^4.0.0", 264 | "proxy-from-env": "^1.1.0" 265 | } 266 | }, 267 | "node_modules/binary-extensions": { 268 | "version": "2.3.0", 269 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", 270 | "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", 271 | "license": "MIT", 272 | "engines": { 273 | "node": ">=8" 274 | }, 275 | "funding": { 276 | "url": "https://github.com/sponsors/sindresorhus" 277 | } 278 | }, 279 | "node_modules/braces": { 280 | "version": "3.0.3", 281 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", 282 | "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", 283 | "license": "MIT", 284 | "dependencies": { 285 | "fill-range": "^7.1.1" 286 | }, 287 | "engines": { 288 | "node": ">=8" 289 | } 290 | }, 291 | "node_modules/busboy": { 292 | "version": "1.6.0", 293 | "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", 294 | "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", 295 | "dependencies": { 296 | "streamsearch": "^1.1.0" 297 | }, 298 | "engines": { 299 | "node": ">=10.16.0" 300 | } 301 | }, 302 | "node_modules/caniuse-lite": { 303 | "version": "1.0.30001660", 304 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz", 305 | "integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==", 306 | "funding": [ 307 | { 308 | "type": "opencollective", 309 | "url": "https://opencollective.com/browserslist" 310 | }, 311 | { 312 | "type": "tidelift", 313 | "url": "https://tidelift.com/funding/github/npm/caniuse-lite" 314 | }, 315 | { 316 | "type": "github", 317 | "url": "https://github.com/sponsors/ai" 318 | } 319 | ], 320 | "license": "CC-BY-4.0" 321 | }, 322 | "node_modules/chokidar": { 323 | "version": "3.6.0", 324 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", 325 | "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", 326 | "license": "MIT", 327 | "dependencies": { 328 | "anymatch": "~3.1.2", 329 | "braces": "~3.0.2", 330 | "glob-parent": "~5.1.2", 331 | "is-binary-path": "~2.1.0", 332 | "is-glob": "~4.0.1", 333 | "normalize-path": "~3.0.0", 334 | "readdirp": "~3.6.0" 335 | }, 336 | "engines": { 337 | "node": ">= 8.10.0" 338 | }, 339 | "funding": { 340 | "url": "https://paulmillr.com/funding/" 341 | }, 342 | "optionalDependencies": { 343 | "fsevents": "~2.3.2" 344 | } 345 | }, 346 | "node_modules/client-only": { 347 | "version": "0.0.1", 348 | "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", 349 | "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", 350 | "license": "MIT" 351 | }, 352 | "node_modules/combined-stream": { 353 | "version": "1.0.8", 354 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 355 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 356 | "license": "MIT", 357 | "dependencies": { 358 | "delayed-stream": "~1.0.0" 359 | }, 360 | "engines": { 361 | "node": ">= 0.8" 362 | } 363 | }, 364 | "node_modules/cookie": { 365 | "version": "0.6.0", 366 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", 367 | "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", 368 | "license": "MIT", 369 | "engines": { 370 | "node": ">= 0.6" 371 | } 372 | }, 373 | "node_modules/cookies-next": { 374 | "version": "4.2.1", 375 | "resolved": "https://registry.npmjs.org/cookies-next/-/cookies-next-4.2.1.tgz", 376 | "integrity": "sha512-qsjtZ8TLlxCSX2JphMQNhkm3V3zIMQ05WrLkBKBwu50npBbBfiZWIdmSMzBGcdGKfMK19E0PIitTfRFAdMGHXg==", 377 | "license": "MIT", 378 | "dependencies": { 379 | "@types/cookie": "^0.6.0", 380 | "cookie": "^0.6.0" 381 | } 382 | }, 383 | "node_modules/csstype": { 384 | "version": "3.1.3", 385 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", 386 | "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", 387 | "dev": true, 388 | "license": "MIT" 389 | }, 390 | "node_modules/delayed-stream": { 391 | "version": "1.0.0", 392 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 393 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 394 | "license": "MIT", 395 | "engines": { 396 | "node": ">=0.4.0" 397 | } 398 | }, 399 | "node_modules/fill-range": { 400 | "version": "7.1.1", 401 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", 402 | "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", 403 | "license": "MIT", 404 | "dependencies": { 405 | "to-regex-range": "^5.0.1" 406 | }, 407 | "engines": { 408 | "node": ">=8" 409 | } 410 | }, 411 | "node_modules/follow-redirects": { 412 | "version": "1.15.9", 413 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", 414 | "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", 415 | "funding": [ 416 | { 417 | "type": "individual", 418 | "url": "https://github.com/sponsors/RubenVerborgh" 419 | } 420 | ], 421 | "license": "MIT", 422 | "engines": { 423 | "node": ">=4.0" 424 | }, 425 | "peerDependenciesMeta": { 426 | "debug": { 427 | "optional": true 428 | } 429 | } 430 | }, 431 | "node_modules/form-data": { 432 | "version": "4.0.0", 433 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 434 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 435 | "license": "MIT", 436 | "dependencies": { 437 | "asynckit": "^0.4.0", 438 | "combined-stream": "^1.0.8", 439 | "mime-types": "^2.1.12" 440 | }, 441 | "engines": { 442 | "node": ">= 6" 443 | } 444 | }, 445 | "node_modules/fsevents": { 446 | "version": "2.3.3", 447 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 448 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 449 | "hasInstallScript": true, 450 | "license": "MIT", 451 | "optional": true, 452 | "os": [ 453 | "darwin" 454 | ], 455 | "engines": { 456 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 457 | } 458 | }, 459 | "node_modules/glob-parent": { 460 | "version": "5.1.2", 461 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 462 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 463 | "license": "ISC", 464 | "dependencies": { 465 | "is-glob": "^4.0.1" 466 | }, 467 | "engines": { 468 | "node": ">= 6" 469 | } 470 | }, 471 | "node_modules/graceful-fs": { 472 | "version": "4.2.11", 473 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", 474 | "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", 475 | "license": "ISC" 476 | }, 477 | "node_modules/immutable": { 478 | "version": "4.3.7", 479 | "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", 480 | "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", 481 | "license": "MIT" 482 | }, 483 | "node_modules/is-binary-path": { 484 | "version": "2.1.0", 485 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 486 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 487 | "license": "MIT", 488 | "dependencies": { 489 | "binary-extensions": "^2.0.0" 490 | }, 491 | "engines": { 492 | "node": ">=8" 493 | } 494 | }, 495 | "node_modules/is-extglob": { 496 | "version": "2.1.1", 497 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 498 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 499 | "license": "MIT", 500 | "engines": { 501 | "node": ">=0.10.0" 502 | } 503 | }, 504 | "node_modules/is-glob": { 505 | "version": "4.0.3", 506 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 507 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 508 | "license": "MIT", 509 | "dependencies": { 510 | "is-extglob": "^2.1.1" 511 | }, 512 | "engines": { 513 | "node": ">=0.10.0" 514 | } 515 | }, 516 | "node_modules/is-number": { 517 | "version": "7.0.0", 518 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 519 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 520 | "license": "MIT", 521 | "engines": { 522 | "node": ">=0.12.0" 523 | } 524 | }, 525 | "node_modules/js-tokens": { 526 | "version": "4.0.0", 527 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 528 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 529 | "license": "MIT" 530 | }, 531 | "node_modules/loose-envify": { 532 | "version": "1.4.0", 533 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 534 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 535 | "license": "MIT", 536 | "dependencies": { 537 | "js-tokens": "^3.0.0 || ^4.0.0" 538 | }, 539 | "bin": { 540 | "loose-envify": "cli.js" 541 | } 542 | }, 543 | "node_modules/lucide-react": { 544 | "version": "0.436.0", 545 | "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.436.0.tgz", 546 | "integrity": "sha512-N292bIxoqm1aObAg0MzFtvhYwgQE6qnIOWx/GLj5ONgcTPH6N0fD9bVq/GfdeC9ZORBXozt/XeEKDpiB3x3vlQ==", 547 | "license": "ISC", 548 | "peerDependencies": { 549 | "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" 550 | } 551 | }, 552 | "node_modules/mime-db": { 553 | "version": "1.52.0", 554 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 555 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 556 | "license": "MIT", 557 | "engines": { 558 | "node": ">= 0.6" 559 | } 560 | }, 561 | "node_modules/mime-types": { 562 | "version": "2.1.35", 563 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 564 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 565 | "license": "MIT", 566 | "dependencies": { 567 | "mime-db": "1.52.0" 568 | }, 569 | "engines": { 570 | "node": ">= 0.6" 571 | } 572 | }, 573 | "node_modules/nanoid": { 574 | "version": "3.3.7", 575 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", 576 | "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", 577 | "funding": [ 578 | { 579 | "type": "github", 580 | "url": "https://github.com/sponsors/ai" 581 | } 582 | ], 583 | "license": "MIT", 584 | "bin": { 585 | "nanoid": "bin/nanoid.cjs" 586 | }, 587 | "engines": { 588 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 589 | } 590 | }, 591 | "node_modules/next": { 592 | "version": "14.2.6", 593 | "resolved": "https://registry.npmjs.org/next/-/next-14.2.6.tgz", 594 | "integrity": "sha512-57Su7RqXs5CBKKKOagt8gPhMM3CpjgbeQhrtei2KLAA1vTNm7jfKS+uDARkSW8ZETUflDCBIsUKGSyQdRs4U4g==", 595 | "license": "MIT", 596 | "dependencies": { 597 | "@next/env": "14.2.6", 598 | "@swc/helpers": "0.5.5", 599 | "busboy": "1.6.0", 600 | "caniuse-lite": "^1.0.30001579", 601 | "graceful-fs": "^4.2.11", 602 | "postcss": "8.4.31", 603 | "styled-jsx": "5.1.1" 604 | }, 605 | "bin": { 606 | "next": "dist/bin/next" 607 | }, 608 | "engines": { 609 | "node": ">=18.17.0" 610 | }, 611 | "optionalDependencies": { 612 | "@next/swc-darwin-arm64": "14.2.6", 613 | "@next/swc-darwin-x64": "14.2.6", 614 | "@next/swc-linux-arm64-gnu": "14.2.6", 615 | "@next/swc-linux-arm64-musl": "14.2.6", 616 | "@next/swc-linux-x64-gnu": "14.2.6", 617 | "@next/swc-linux-x64-musl": "14.2.6", 618 | "@next/swc-win32-arm64-msvc": "14.2.6", 619 | "@next/swc-win32-ia32-msvc": "14.2.6", 620 | "@next/swc-win32-x64-msvc": "14.2.6" 621 | }, 622 | "peerDependencies": { 623 | "@opentelemetry/api": "^1.1.0", 624 | "@playwright/test": "^1.41.2", 625 | "react": "^18.2.0", 626 | "react-dom": "^18.2.0", 627 | "sass": "^1.3.0" 628 | }, 629 | "peerDependenciesMeta": { 630 | "@opentelemetry/api": { 631 | "optional": true 632 | }, 633 | "@playwright/test": { 634 | "optional": true 635 | }, 636 | "sass": { 637 | "optional": true 638 | } 639 | } 640 | }, 641 | "node_modules/normalize-path": { 642 | "version": "3.0.0", 643 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 644 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 645 | "license": "MIT", 646 | "engines": { 647 | "node": ">=0.10.0" 648 | } 649 | }, 650 | "node_modules/picocolors": { 651 | "version": "1.1.0", 652 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", 653 | "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", 654 | "license": "ISC" 655 | }, 656 | "node_modules/picomatch": { 657 | "version": "2.3.1", 658 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 659 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 660 | "license": "MIT", 661 | "engines": { 662 | "node": ">=8.6" 663 | }, 664 | "funding": { 665 | "url": "https://github.com/sponsors/jonschlinkert" 666 | } 667 | }, 668 | "node_modules/postcss": { 669 | "version": "8.4.31", 670 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", 671 | "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", 672 | "funding": [ 673 | { 674 | "type": "opencollective", 675 | "url": "https://opencollective.com/postcss/" 676 | }, 677 | { 678 | "type": "tidelift", 679 | "url": "https://tidelift.com/funding/github/npm/postcss" 680 | }, 681 | { 682 | "type": "github", 683 | "url": "https://github.com/sponsors/ai" 684 | } 685 | ], 686 | "license": "MIT", 687 | "dependencies": { 688 | "nanoid": "^3.3.6", 689 | "picocolors": "^1.0.0", 690 | "source-map-js": "^1.0.2" 691 | }, 692 | "engines": { 693 | "node": "^10 || ^12 || >=14" 694 | } 695 | }, 696 | "node_modules/proxy-from-env": { 697 | "version": "1.1.0", 698 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 699 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", 700 | "license": "MIT" 701 | }, 702 | "node_modules/react": { 703 | "version": "18.3.1", 704 | "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", 705 | "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", 706 | "license": "MIT", 707 | "dependencies": { 708 | "loose-envify": "^1.1.0" 709 | }, 710 | "engines": { 711 | "node": ">=0.10.0" 712 | } 713 | }, 714 | "node_modules/react-dom": { 715 | "version": "18.3.1", 716 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", 717 | "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", 718 | "license": "MIT", 719 | "dependencies": { 720 | "loose-envify": "^1.1.0", 721 | "scheduler": "^0.23.2" 722 | }, 723 | "peerDependencies": { 724 | "react": "^18.3.1" 725 | } 726 | }, 727 | "node_modules/readdirp": { 728 | "version": "3.6.0", 729 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 730 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 731 | "license": "MIT", 732 | "dependencies": { 733 | "picomatch": "^2.2.1" 734 | }, 735 | "engines": { 736 | "node": ">=8.10.0" 737 | } 738 | }, 739 | "node_modules/sass": { 740 | "version": "1.78.0", 741 | "resolved": "https://registry.npmjs.org/sass/-/sass-1.78.0.tgz", 742 | "integrity": "sha512-AaIqGSrjo5lA2Yg7RvFZrlXDBCp3nV4XP73GrLGvdRWWwk+8H3l0SDvq/5bA4eF+0RFPLuWUk3E+P1U/YqnpsQ==", 743 | "license": "MIT", 744 | "dependencies": { 745 | "chokidar": ">=3.0.0 <4.0.0", 746 | "immutable": "^4.0.0", 747 | "source-map-js": ">=0.6.2 <2.0.0" 748 | }, 749 | "bin": { 750 | "sass": "sass.js" 751 | }, 752 | "engines": { 753 | "node": ">=14.0.0" 754 | } 755 | }, 756 | "node_modules/scheduler": { 757 | "version": "0.23.2", 758 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", 759 | "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", 760 | "license": "MIT", 761 | "dependencies": { 762 | "loose-envify": "^1.1.0" 763 | } 764 | }, 765 | "node_modules/sonner": { 766 | "version": "1.5.0", 767 | "resolved": "https://registry.npmjs.org/sonner/-/sonner-1.5.0.tgz", 768 | "integrity": "sha512-FBjhG/gnnbN6FY0jaNnqZOMmB73R+5IiyYAw8yBj7L54ER7HB3fOSE5OFiQiE2iXWxeXKvg6fIP4LtVppHEdJA==", 769 | "license": "MIT", 770 | "peerDependencies": { 771 | "react": "^18.0.0", 772 | "react-dom": "^18.0.0" 773 | } 774 | }, 775 | "node_modules/source-map-js": { 776 | "version": "1.2.1", 777 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", 778 | "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", 779 | "license": "BSD-3-Clause", 780 | "engines": { 781 | "node": ">=0.10.0" 782 | } 783 | }, 784 | "node_modules/streamsearch": { 785 | "version": "1.1.0", 786 | "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", 787 | "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", 788 | "engines": { 789 | "node": ">=10.0.0" 790 | } 791 | }, 792 | "node_modules/styled-jsx": { 793 | "version": "5.1.1", 794 | "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", 795 | "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", 796 | "license": "MIT", 797 | "dependencies": { 798 | "client-only": "0.0.1" 799 | }, 800 | "engines": { 801 | "node": ">= 12.0.0" 802 | }, 803 | "peerDependencies": { 804 | "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" 805 | }, 806 | "peerDependenciesMeta": { 807 | "@babel/core": { 808 | "optional": true 809 | }, 810 | "babel-plugin-macros": { 811 | "optional": true 812 | } 813 | } 814 | }, 815 | "node_modules/to-regex-range": { 816 | "version": "5.0.1", 817 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 818 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 819 | "license": "MIT", 820 | "dependencies": { 821 | "is-number": "^7.0.0" 822 | }, 823 | "engines": { 824 | "node": ">=8.0" 825 | } 826 | }, 827 | "node_modules/tslib": { 828 | "version": "2.7.0", 829 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", 830 | "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", 831 | "license": "0BSD" 832 | }, 833 | "node_modules/typescript": { 834 | "version": "5.6.2", 835 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", 836 | "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", 837 | "dev": true, 838 | "license": "Apache-2.0", 839 | "bin": { 840 | "tsc": "bin/tsc", 841 | "tsserver": "bin/tsserver" 842 | }, 843 | "engines": { 844 | "node": ">=14.17" 845 | } 846 | }, 847 | "node_modules/undici-types": { 848 | "version": "6.19.8", 849 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", 850 | "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", 851 | "dev": true, 852 | "license": "MIT" 853 | } 854 | } 855 | } 856 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "axios": "^1.7.5", 13 | "cookies-next": "^4.2.1", 14 | "lucide-react": "^0.436.0", 15 | "next": "14.2.6", 16 | "react": "^18", 17 | "react-dom": "^18", 18 | "sass": "^1.77.8", 19 | "sonner": "^1.5.0" 20 | }, 21 | "devDependencies": { 22 | "@types/node": "^20", 23 | "@types/react": "^18", 24 | "@types/react-dom": "^18", 25 | "typescript": "^5" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/dashboard/category/page.tsx: -------------------------------------------------------------------------------- 1 | import styles from './styles.module.scss' 2 | import { Button } from "@/app/dashboard/components/button" 3 | import { api } from '@/services/api' 4 | import { redirect } from 'next/navigation' 5 | import { getCookieServer } from '@/lib/cookieServer' 6 | 7 | export default function Category(){ 8 | 9 | async function handleRegisterCategory(formData: FormData){ 10 | "use server" 11 | 12 | const name = formData.get("name") 13 | 14 | if(name === "") return; 15 | 16 | const data = { 17 | name: name, 18 | } 19 | 20 | const token = getCookieServer(); 21 | 22 | await api.post("/category", data, { 23 | headers:{ 24 | Authorization: `Bearer ${token}` 25 | } 26 | }) 27 | .catch((err) => { 28 | console.log(err); 29 | return; 30 | }) 31 | 32 | redirect("/dashboard") 33 | 34 | } 35 | 36 | 37 | return( 38 |
39 |

Nova Categoria

40 | 41 |
45 | 52 | 53 |
56 | ) 57 | } -------------------------------------------------------------------------------- /src/app/dashboard/category/styles.module.scss: -------------------------------------------------------------------------------- 1 | .container{ 2 | max-width: 720px; 3 | margin: 20px auto; 4 | padding: 0 16px; 5 | 6 | display: flex; 7 | flex-direction: column; 8 | 9 | h1{ 10 | color: var(--white); 11 | } 12 | 13 | } 14 | 15 | .form{ 16 | display: flex; 17 | gap: 16px; 18 | flex-direction: column; 19 | margin: 16px 0; 20 | } 21 | 22 | .input{ 23 | height: 40px; 24 | border:0; 25 | background-color: var(--dark-900); 26 | border-radius: 8px; 27 | padding: 0 16px; 28 | color: var(--white); 29 | border: 1px solid var(--gray-100) 30 | } -------------------------------------------------------------------------------- /src/app/dashboard/components/button/index.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import styles from './styles.module.scss' 4 | import { useFormStatus } from 'react-dom' 5 | 6 | interface Props{ 7 | name: string; 8 | } 9 | 10 | export function Button({ name }: Props){ 11 | const { pending } = useFormStatus(); 12 | 13 | return ( 14 | 17 | ) 18 | } -------------------------------------------------------------------------------- /src/app/dashboard/components/button/styles.module.scss: -------------------------------------------------------------------------------- 1 | 2 | .button{ 3 | height: 40px; 4 | border-radius: 8px; 5 | border:0; 6 | background-color: var(--green-900); 7 | font-weight: bold; 8 | font-size: 16px; 9 | color: var(--white); 10 | } -------------------------------------------------------------------------------- /src/app/dashboard/components/header/index.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import Link from 'next/link' 4 | import styles from './styles.module.scss' 5 | import Image from 'next/image' 6 | import logoImg from '/public/logo.svg' 7 | import { LogOutIcon } from 'lucide-react' 8 | import { deleteCookie } from 'cookies-next' 9 | import { useRouter } from 'next/navigation' 10 | import { toast, ToastT } from 'sonner' 11 | 12 | export function Header(){ 13 | const router = useRouter(); 14 | 15 | async function handleLogout(){ 16 | deleteCookie("session", { path: "/" } ) 17 | toast.success("Logout feito com sucesso!") 18 | router.replace("/") 19 | } 20 | 21 | return( 22 |
23 |
24 | 25 | Logo Sujeito Pizza 33 | 34 | 35 | 49 |
50 |
51 | ) 52 | } -------------------------------------------------------------------------------- /src/app/dashboard/components/header/styles.module.scss: -------------------------------------------------------------------------------- 1 | .headerContainer{ 2 | height: 5rem; 3 | } 4 | 5 | .headerContent{ 6 | max-width: 1280px; 7 | height: 5rem; 8 | margin: 0 auto; 9 | padding: 0 16px; 10 | 11 | display: flex; 12 | align-items: center; 13 | justify-content: space-between; 14 | 15 | 16 | nav{ 17 | display: flex; 18 | align-items: center; 19 | gap: 14px; 20 | 21 | a{ 22 | color: var(--white); 23 | text-decoration: none; 24 | transition: color .8s; 25 | 26 | &:hover{ 27 | color: var(--red-900); 28 | } 29 | } 30 | 31 | button{ 32 | border:0; 33 | background: transparent; 34 | margin-left: 16px; 35 | transition: transform 0.5s; 36 | 37 | &:hover{ 38 | transform: scale(1.2); 39 | } 40 | } 41 | 42 | 43 | } 44 | } -------------------------------------------------------------------------------- /src/app/dashboard/components/modal/index.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import styles from './styles.module.scss' 3 | import { X } from 'lucide-react' 4 | import {use} from 'react' 5 | import { OrderContext } from '@/providers/order' 6 | import { calculateTotalOrder } from '@/lib/helper' 7 | 8 | 9 | export function Modalorder(){ 10 | 11 | const {onRequestClose, order, finishOrder} = use(OrderContext); 12 | 13 | async function handleFinishOrder() { 14 | await finishOrder(order[0].order.id) 15 | } 16 | return( 17 | 18 | 19 |
20 | 23 | 24 |
25 |

Detalhes do pedido

26 | 27 | 28 | Mesa {order[0].order.table} 29 | 30 | 31 | { order[0].order?.name && ( 32 | 33 | {order[0].order.name} 34 | 35 | )} 36 | 37 | {order.map(item =>( 38 | 39 |
40 | 41 | Qtd: {item.amount} - {item.product.name} - R$ {parseFloat(item.product.price) * item.amount} 42 | 43 | 44 | {item.product.description} 45 | 46 |
47 | ))} 48 | 49 |

Valor total: R$ {calculateTotalOrder(order)}

50 | 51 | 52 | 55 | 56 |
57 | 58 |
59 | 60 |
61 | ) 62 | } -------------------------------------------------------------------------------- /src/app/dashboard/components/modal/styles.module.scss: -------------------------------------------------------------------------------- 1 | .dialogContainer{ 2 | position: fixed; 3 | left: 0; 4 | top: 0; 5 | width: 100%; 6 | height: 100%; 7 | background: rgba(0,0,0,0.50); 8 | z-index: 999; 9 | overflow: auto; 10 | display: flex; 11 | justify-content: center; 12 | align-items: center; 13 | padding: 0 16px; 14 | backdrop-filter: blur(4px); 15 | } 16 | 17 | .dialogContent{ 18 | background-color: var(--dark-700); 19 | margin: auto; 20 | padding: 16px; 21 | border-radius: 8px; 22 | width: 100%; 23 | max-width: 600px; 24 | position: relative; 25 | color: var(--white); 26 | 27 | h2{ 28 | margin-bottom: 16px; 29 | margin-top: 8px; 30 | } 31 | } 32 | 33 | .table{ 34 | background-color: var(--dark-900); 35 | padding: 8px; 36 | border-radius: 4px; 37 | } 38 | 39 | .dialogBack{ 40 | background-color: transparent; 41 | border: 0; 42 | } 43 | 44 | .item{ 45 | display: flex; 46 | flex-direction: column; 47 | margin: 16px 0; 48 | font-size: 16px; 49 | } 50 | 51 | .description{ 52 | color: #CCC; 53 | margin-top: 4px; 54 | } 55 | 56 | .buttonOrder{ 57 | background-color: var(--green-900); 58 | padding: 8px; 59 | border-radius: 4px; 60 | border: 0; 61 | font-weight: bold; 62 | } 63 | 64 | .name{ 65 | margin-left: 16px; 66 | } 67 | 68 | .total{ 69 | margin: 8px 0 16px 0; 70 | } 71 | -------------------------------------------------------------------------------- /src/app/dashboard/components/orders/index.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import {use} from 'react' 3 | import styles from './styles.module.scss' 4 | import { RefreshCw } from 'lucide-react' 5 | import { OrderProps } from '@/lib/order.type' 6 | import { Modalorder } from '@/app/dashboard/components/modal' 7 | import { OrderContext } from '@/providers/order' 8 | import { useRouter } from 'next/navigation' 9 | import { toast } from 'sonner' 10 | 11 | interface Props{ 12 | orders: OrderProps[] 13 | } 14 | 15 | export function Orders({orders}: Props){ 16 | const {isOpen, onRequestOpen} = use(OrderContext) 17 | const router = useRouter(); 18 | 19 | async function handleDetailOrder(order_id: string){ 20 | await onRequestOpen(order_id) 21 | } 22 | 23 | function handleRefresh(){ 24 | router.refresh(); 25 | toast.success("Pedidos atualizados com sucesso!") 26 | } 27 | return( 28 | <> 29 |
30 | 31 |
32 |

Últimos pedidos

33 | 36 |
37 | 38 |
39 | {orders.length === 0 && ( 40 | 41 | Nenhum pedido aberto... 42 | 43 | )} 44 | {orders.map(order => ( 45 | 53 | ))} 54 |
55 | 56 |
57 | 58 | {isOpen && } 59 | 60 | 61 | ) 62 | } -------------------------------------------------------------------------------- /src/app/dashboard/components/orders/styles.module.scss: -------------------------------------------------------------------------------- 1 | 2 | .container{ 3 | max-width: 720px; 4 | margin: 20px auto; 5 | padding: 0 16px; 6 | 7 | display: flex; 8 | justify-content: space-between; 9 | flex-direction: column; 10 | 11 | h1{ 12 | color: var(--white); 13 | } 14 | } 15 | 16 | .containerHeader{ 17 | display: flex; 18 | align-items: center; 19 | margin-bottom: 16px; 20 | margin-top: 24px; 21 | gap: 14px; 22 | 23 | button{ 24 | background: transparent; 25 | border:0; 26 | } 27 | } 28 | 29 | .listOrders{ 30 | display: flex; 31 | flex-direction: column; 32 | gap: 16px; 33 | } 34 | 35 | .orderItem{ 36 | display: flex; 37 | background-color: var(--dark-900); 38 | align-items: center; 39 | border:0; 40 | font-size: 18px; 41 | border-radius: 8px; 42 | color: var(--white); 43 | transition: all 0.5s; 44 | 45 | &:hover{ 46 | filter: brightness(1.40); 47 | } 48 | } 49 | 50 | .tag{ 51 | width: 9px; 52 | height: 60px; 53 | background-color: var(--green-900); 54 | border-radius: 6px 0 0 6px; 55 | margin-right: 16px; 56 | } 57 | 58 | .emptyItem{ 59 | color: var(--gray-100) 60 | } -------------------------------------------------------------------------------- /src/app/dashboard/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Header } from "./components/header" 2 | import { OrderProvider } from "@/providers/order" 3 | 4 | export default function DashboardLayout({ children }: 5 | { children: React.ReactNode} 6 | ){ 7 | return( 8 | <> 9 |
10 | 11 | {children} 12 | 13 | 14 | 15 | ) 16 | } -------------------------------------------------------------------------------- /src/app/dashboard/page.tsx: -------------------------------------------------------------------------------- 1 | import { Orders } from "./components/orders"; 2 | import { api } from "@/services/api"; 3 | import { getCookieServer } from "@/lib/cookieServer"; 4 | import { OrderProps } from "@/lib/order.type"; 5 | 6 | async function getOrders() : Promise { 7 | try{ 8 | const token = getCookieServer(); 9 | const response = await api.get("/orders",{ 10 | headers:{ 11 | Authorization:`Bearer ${token}` 12 | } 13 | }) 14 | return response.data || [] 15 | }catch(err){ 16 | console.log(err); 17 | return[]; 18 | } 19 | } 20 | 21 | export default async function Dashboard(){ 22 | 23 | const orders = await getOrders(); 24 | 25 | return( 26 | <> 27 | 28 | 29 | ) 30 | } -------------------------------------------------------------------------------- /src/app/dashboard/product/components/form/index.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { ChangeEvent, useState } from 'react' 4 | import styles from './styles.module.scss' 5 | import { UploadCloud } from 'lucide-react' 6 | import Image from 'next/image' 7 | import { Button } from '@/app/dashboard/components/button' 8 | import { api } from '@/services/api' 9 | import { getCookieClient } from '@/lib/cookieClient' 10 | import { toast } from 'sonner' 11 | import { useRouter } from 'next/navigation' 12 | 13 | interface CategoryProps{ 14 | id: string; 15 | name: string; 16 | } 17 | 18 | interface Props{ 19 | categories: CategoryProps[] 20 | } 21 | 22 | export function Form({ categories }: Props ){ 23 | const router = useRouter(); 24 | const [image, setImage] = useState() 25 | const [previewImage, setPreviewImage] = useState("") 26 | 27 | async function handleRegisterProduct(formData: FormData){ 28 | 29 | const categoryIndex = formData.get("category") 30 | const name = formData.get("name") 31 | const price = formData.get("price") 32 | const description = formData.get("description") 33 | 34 | if(!name || !categoryIndex || !price || !description || !image){ 35 | toast.warning("Preencha todos os campos") 36 | return; 37 | } 38 | 39 | const data = new FormData(); 40 | 41 | data.append("name", name) 42 | data.append("price", price) 43 | data.append("description", description) 44 | data.append("category_id", categories[Number(categoryIndex)].id) 45 | data.append("file", image) 46 | 47 | const token = getCookieClient(); 48 | 49 | await api.post("/product", data, { 50 | headers:{ 51 | Authorization: `Bearer ${token}` 52 | } 53 | }) 54 | .catch((err) => { 55 | console.log(err); 56 | toast.warning("Falha ao cadastrar o produto") 57 | return; 58 | }) 59 | 60 | toast.success("Produto cadastrado com sucesso!") 61 | router.push("/dashboard") 62 | 63 | 64 | } 65 | 66 | function handleFile(e: ChangeEvent){ 67 | if(e.target.files && e.target.files[0]){ 68 | const image = e.target.files[0]; 69 | 70 | if(image.type !== "image/jpeg" && image.type !== "image/png"){ 71 | toast.warning("Formato não permitido!") 72 | return; 73 | } 74 | 75 | setImage(image); 76 | setPreviewImage(URL.createObjectURL(image)) 77 | 78 | } 79 | } 80 | 81 | 82 | return( 83 |
84 |

Novo produto

85 | 86 |
87 | 88 | 113 | 114 | 121 | 122 | 129 | 130 | 137 | 138 | 144 | 145 |
149 | ) 150 | } -------------------------------------------------------------------------------- /src/app/dashboard/product/components/form/styles.module.scss: -------------------------------------------------------------------------------- 1 | .container{ 2 | max-width: 720px; 3 | margin: 20px auto; 4 | padding: 0 16px; 5 | display: flex; 6 | flex-direction: column; 7 | 8 | h1{ 9 | color: var(--white); 10 | } 11 | } 12 | 13 | .form{ 14 | display: flex; 15 | flex-direction: column; 16 | margin: 16px 0; 17 | gap: 16px; 18 | 19 | select { 20 | width: 100%; 21 | height: 40px; 22 | border-radius: 8px; 23 | background-color: var(--dark-900); 24 | color: var(--white); 25 | padding: 0 8px; 26 | border: 1px solid var(--gray-100); 27 | } 28 | 29 | textarea{ 30 | width: 100%; 31 | min-height: 120px; 32 | resize: none; 33 | padding: 8px; 34 | color: var(--white); 35 | } 36 | 37 | } 38 | 39 | .labelImage{ 40 | width: 100%; 41 | height: 280px; 42 | position: relative; 43 | background-color: var(--dark-900); 44 | border-radius: 8px; 45 | display: flex; 46 | align-items: center; 47 | justify-content: center; 48 | cursor: pointer; 49 | flex-direction: column; 50 | margin-bottom: 16px; 51 | border: 1px solid var(--gray-100); 52 | 53 | input{ 54 | display: none; 55 | } 56 | 57 | span{ 58 | z-index: 99; 59 | opacity: 0.8; 60 | transition: all 0.6s; 61 | 62 | &:hover{ 63 | opacity: 1; 64 | transform: scale(1.1); 65 | } 66 | } 67 | } 68 | 69 | .preview{ 70 | width: 100%; 71 | height: 100%; 72 | border-radius: 8px; 73 | object-fit: cover; 74 | } 75 | 76 | .input{ 77 | border:0; 78 | height: 40px; 79 | background-color: var(--dark-900); 80 | border: 1px solid var(--gray-100); 81 | border-radius: 8px; 82 | padding: 0 8px; 83 | color: var(--white); 84 | } -------------------------------------------------------------------------------- /src/app/dashboard/product/page.tsx: -------------------------------------------------------------------------------- 1 | import { Form } from './components/form' 2 | import { api } from '@/services/api' 3 | import { getCookieServer } from '@/lib/cookieServer' 4 | 5 | export default async function Product(){ 6 | 7 | const token = getCookieServer(); 8 | 9 | const response = await api.get("/category", { 10 | headers: { 11 | Authorization: `Bearer ${token}` 12 | } 13 | }) 14 | 15 | return( 16 |
17 | ) 18 | } -------------------------------------------------------------------------------- /src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sm4rtdev/pizza_frontend/458c5d16c0363e23e3d4b04791c982fcb5be5bb5/src/app/favicon.ico -------------------------------------------------------------------------------- /src/app/globals.scss: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | outline: 0; 6 | } 7 | 8 | :root{ 9 | 10 | --white: #FFF; 11 | --black: #000; 12 | 13 | --dark-900: #101026; 14 | --dark-700: #1d1d2e; 15 | 16 | --gray-100: #8a8a8a; 17 | --green-900: #3fffa3; 18 | --red-900: #FF3f4b; 19 | 20 | } 21 | 22 | button{ 23 | cursor: pointer; 24 | } 25 | 26 | a{ 27 | color: inherit; 28 | text-rendering: none; 29 | } 30 | 31 | body{ 32 | background: var(--dark-700); 33 | } 34 | 35 | body, input, textarea, select, button{ 36 | font: 400, 1rem sans-serif; // 1rem = 16px 37 | } 38 | 39 | @media (max-width: 720px){ 40 | html{ 41 | font-size: 87.5%; 42 | } 43 | } 44 | 45 | @media (max-width: 1080px){ 46 | html{ 47 | font-size: 93.75%; 48 | } 49 | } -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import "./globals.scss"; 4 | import { Toaster } from "sonner"; 5 | 6 | const inter = Inter({ subsets: ["latin"] }); 7 | 8 | export const metadata: Metadata = { 9 | title: "Sujeito Pizza - A melhor pizzaria", 10 | description: "A melhor pizzaria do Brasil", 11 | }; 12 | 13 | export default function RootLayout({ 14 | children, 15 | }: Readonly<{ 16 | children: React.ReactNode; 17 | }>) { 18 | return ( 19 | 20 | 21 | 31 | {children} 32 | 33 | 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /src/app/page.module.scss: -------------------------------------------------------------------------------- 1 | 2 | .containerCenter{ 3 | min-height: 100vh; 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | justify-content: center; 8 | } 9 | 10 | .login{ 11 | margin-top: 24px; 12 | display: flex; 13 | flex-direction: column; 14 | align-items: center; 15 | justify-content: center; 16 | gap: 16px; 17 | width: 600px; 18 | 19 | h1{ 20 | color: var(--white); 21 | } 22 | 23 | 24 | form{ 25 | color: var(--white); 26 | padding-bottom: 16px; 27 | font-size: 18px; 28 | display: flex; 29 | flex-direction: column; 30 | width: 90%; 31 | gap: 16px; 32 | } 33 | 34 | form button{ 35 | height: 40px; 36 | font-size: 16px; 37 | background-color: var(--red-900); 38 | border:0; 39 | border-radius: 8px; 40 | color: var(--white); 41 | display: flex; 42 | align-items: center; 43 | justify-content: center; 44 | transition: all 0.5s; 45 | } 46 | 47 | 48 | form button:hover{ 49 | transform: scale(1.05); 50 | } 51 | } 52 | 53 | .input{ 54 | height: 40px; 55 | border: 1px solid var(--gray-100); 56 | padding: 0 16px; 57 | border-radius: 8px; 58 | background-color: var(--dark-900); 59 | color: var(--white); 60 | font-size: 16px; 61 | } 62 | 63 | .input::placeholder{ 64 | color: rgba(255, 255, 255, 0.700); 65 | } 66 | 67 | .text{ 68 | color: var(--white); 69 | text-decoration: none; 70 | } 71 | 72 | @media (max-width: 620px){ 73 | .login{ 74 | width: 90%; 75 | } 76 | } -------------------------------------------------------------------------------- /src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import styles from './page.module.scss' 2 | import logoImg from '/public/logo.svg' 3 | import Image from 'next/image' 4 | import Link from 'next/link' 5 | import { api } from '@/services/api' 6 | import { redirect } from 'next/navigation' 7 | import { cookies } from 'next/headers' 8 | 9 | export default function Page(){ 10 | 11 | async function handleLogin(formData: FormData){ 12 | "use server" 13 | 14 | const email = formData.get("email") 15 | const password = formData.get("password") 16 | 17 | if(email === "" || password === ""){ 18 | return; 19 | } 20 | 21 | try{ 22 | 23 | const response = await api.post("/session", { 24 | email, 25 | password 26 | }) 27 | 28 | if(!response.data.token){ 29 | return; 30 | } 31 | 32 | console.log(response.data); 33 | 34 | const expressTime = 60 * 60 * 24 * 30 * 1000; 35 | cookies().set("session", response.data.token, { 36 | maxAge: expressTime, 37 | path: "/", 38 | httpOnly: false, 39 | secure: process.env.NODE_ENV === "production" 40 | }) 41 | 42 | }catch(err){ 43 | console.log(err); 44 | return; 45 | } 46 | 47 | redirect("/dashboard") 48 | 49 | } 50 | 51 | return( 52 | <> 53 |
54 | Logo da pizzaria 58 | 59 |
60 | 61 | 68 | 69 | 76 | 77 | 80 | 81 | 82 | 83 | Não possui uma conta? Cadastre-se 84 | 85 | 86 |
87 | 88 |
89 | 90 | ) 91 | } -------------------------------------------------------------------------------- /src/app/signup/page.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image" 2 | import Link from "next/link" 3 | import styles from '../page.module.scss' 4 | import logoImg from '/public/logo.svg' 5 | import { api } from '@/services/api' 6 | import { redirect } from 'next/navigation' 7 | 8 | export default function Signup(){ 9 | 10 | async function handleRegister(formData: FormData){ 11 | "use server" 12 | 13 | const name = formData.get("name") 14 | const email = formData.get("email") 15 | const password = formData.get("password") 16 | 17 | if( name === "" || email === "" || password === ""){ 18 | console.log("PREENCHA TODOS OS CAMPOS") 19 | return; 20 | } 21 | 22 | try{ 23 | await api.post("/users", { 24 | name, 25 | email, 26 | password 27 | }) 28 | 29 | }catch(err){ 30 | console.log("error") 31 | console.log(err) 32 | } 33 | 34 | redirect("/") 35 | } 36 | 37 | return( 38 | <> 39 |
40 | Logo da pizzaria 44 | 45 |
46 |

Criando sua conta

47 |
48 | 55 | 56 | 63 | 64 | 71 | 72 | 75 |
76 | 77 | 78 | Já possui uma conta? Faça o ligin 79 | 80 | 81 |
82 | 83 |
84 | 85 | ) 86 | } -------------------------------------------------------------------------------- /src/lib/cookieClient.ts: -------------------------------------------------------------------------------- 1 | import { getCookie } from "cookies-next" 2 | 3 | export function getCookieClient(){ 4 | const token = getCookie("session") 5 | return token; 6 | } -------------------------------------------------------------------------------- /src/lib/cookieServer.ts: -------------------------------------------------------------------------------- 1 | import { cookies } from 'next/headers' 2 | 3 | export function getCookieServer(){ 4 | const token = cookies().get("session")?.value; 5 | 6 | return token || null; 7 | } -------------------------------------------------------------------------------- /src/lib/helper.ts: -------------------------------------------------------------------------------- 1 | import { OrderItemProps } from "@/providers/order"; 2 | 3 | 4 | export function calculateTotalOrder(orders: OrderItemProps[]){ 5 | return orders.reduce((total, item) =>{ 6 | const itemTotal = parseFloat(item.product.price) * item.amount; 7 | return total + itemTotal 8 | }, 0) 9 | } -------------------------------------------------------------------------------- /src/lib/order.type.ts: -------------------------------------------------------------------------------- 1 | export interface OrderProps{ 2 | id: string; 3 | table: string; 4 | name: string; 5 | draft: boolean; 6 | status: boolean; 7 | } -------------------------------------------------------------------------------- /src/middleware.ts: -------------------------------------------------------------------------------- 1 | import { NextRequest, NextResponse } from 'next/server' 2 | import { getCookieServer } from '@/lib/cookieServer' 3 | import { api } from "@/services/api" 4 | 5 | export async function middleware(req: NextRequest){ 6 | const { pathname } = req.nextUrl 7 | 8 | if(pathname.startsWith("/_next") || pathname === "/"){ 9 | return NextResponse.next(); 10 | } 11 | 12 | const token = getCookieServer(); 13 | 14 | if(pathname.startsWith("/dashboard")){ 15 | if(!token){ 16 | return NextResponse.redirect(new URL("/", req.url)) 17 | } 18 | 19 | const isValid = await validateToken(token) 20 | console.log(isValid); 21 | 22 | if(!isValid){ 23 | return NextResponse.redirect(new URL("/", req.url)) 24 | } 25 | } 26 | 27 | return NextResponse.next(); 28 | 29 | } 30 | 31 | 32 | async function validateToken(token: string){ 33 | if (!token) return false; 34 | 35 | try{ 36 | await api.get("/me", { 37 | headers:{ 38 | Authorization: `Bearer ${token}` 39 | } 40 | }) 41 | 42 | return true; 43 | }catch(err){ 44 | return false; 45 | } 46 | } -------------------------------------------------------------------------------- /src/providers/order.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { createContext, ReactNode, useState} from "react" 4 | import { api } from "@/services/api"; 5 | import { getCookieClient } from "@/lib/cookieClient"; 6 | import { toast } from "sonner"; 7 | import { useRouter } from "next/navigation"; 8 | 9 | export interface OrderItemProps{ 10 | id: string; 11 | amount: number; 12 | create_at: string; 13 | order_id: string; 14 | product_id: string; 15 | product:{ 16 | id: string; 17 | name: string; 18 | price: string; 19 | description: string; 20 | banner: string; 21 | category_id: string; 22 | }; 23 | order:{ 24 | id: string; 25 | table: number; 26 | name: string | null; 27 | draft: boolean; 28 | status: boolean; 29 | } 30 | } 31 | 32 | 33 | type OrderContextData = { 34 | isOpen: boolean; 35 | onRequestOpen: (order_id: string) => Promise; 36 | onRequestClose: () => void; 37 | order: OrderItemProps[]; 38 | finishOrder: (order_id: string) => Promise; 39 | } 40 | 41 | type OrderProviderProps = { 42 | children: ReactNode; 43 | } 44 | 45 | export const OrderContext = createContext({} as OrderContextData) 46 | 47 | export function OrderProvider({children}: OrderProviderProps){ 48 | const [isOpen, setIsOpen] = useState(false); 49 | const [order, setOrder] = useState([]) 50 | const router = useRouter(); 51 | 52 | async function onRequestOpen(order_id: string){ 53 | //console.log(order_id) 54 | const token = getCookieClient(); 55 | const response = await api.get('/order/detail', { 56 | headers:{ 57 | Authorization:`Bearer ${token}` 58 | }, 59 | params:{ 60 | order_id: order_id 61 | } 62 | }) 63 | setOrder(response.data); 64 | setIsOpen(true); 65 | } 66 | 67 | function onRequestClose(){ 68 | setIsOpen(false); 69 | } 70 | 71 | async function finishOrder(order_id: string) { 72 | const token = getCookieClient(); 73 | const data = { 74 | order_id: order_id, 75 | } 76 | try{ 77 | await api.put('/order/finish', data,{ 78 | headers:{ 79 | Authorization:`Bearer ${token}` 80 | } 81 | }) 82 | }catch(err){ 83 | console.log(err); 84 | toast.error("Falha ao finalizar o pedido!") 85 | return; 86 | } 87 | toast.success("Pedido finalizado!") 88 | router.refresh(); 89 | setIsOpen(false); 90 | 91 | } 92 | 93 | return( 94 | 103 | {children} 104 | 105 | ) 106 | } -------------------------------------------------------------------------------- /src/services/api.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | export const api = axios.create({ 4 | baseURL: "http://localhost:3333" 5 | }) -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./src/*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | --------------------------------------------------------------------------------