├── .gitignore ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── public └── vite.svg ├── src ├── App.css ├── App.tsx ├── Message.tsx ├── assets │ └── react.svg ├── components │ ├── Alert.tsx │ ├── Button │ │ ├── Button.module.css │ │ └── Button.tsx │ ├── Cart.tsx │ ├── ExpandableText.tsx │ ├── Form.tsx │ ├── Like.tsx │ ├── ListGroup │ │ ├── ListGroup.css │ │ └── ListGroup.tsx │ ├── Message.tsx │ ├── NavBar.tsx │ ├── ProductList.tsx │ └── UserList.tsx ├── expense-tracker │ ├── categories.ts │ └── components │ │ ├── ExpenseFilter.tsx │ │ ├── ExpenseForm.tsx │ │ └── ExpenseList.tsx ├── hooks │ └── useUsers.ts ├── index.css ├── main.tsx ├── services │ ├── api-client.ts │ ├── http-service.ts │ └── user-service.ts └── vite-env.d.ts ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /.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 | # The Ultimate React Course - Part 1 2 | 3 | This repository contains all of the code examples and exercise solutions for the first part of my Ultimate React course. 4 | 5 | I have designed this course to teach you everything you need to know to become a proficient React developer. This course is the first part of a two-part series, covering the fundamentals. You'll learn how to: 6 | 7 | - Build front-end apps with React and TypeScript 8 | - Build reusable function components 9 | - Style your components using vanilla CSS, CSS modules, and CSS-in-JS 10 | - Manage component state 11 | - Build forms with React Hook Forms 12 | - Implement form validation using Zod 13 | - Connect your React apps to the backend 14 | - Deploy your React apps 15 | - Use VSCode shortcuts to increase your productivity 16 | - Write clean code like a pro 17 | - Apply best practices 18 | 19 | By the end of this course, you'll have a solid understanding of React and be able to build real-world applications with React and TypeScript. 20 | 21 | You can find the full course at: 22 | 23 | https://codewithmosh.com 24 | 25 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-app", 3 | "version": "0.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "react-app", 9 | "version": "0.0.0", 10 | "dependencies": { 11 | "@hookform/resolvers": "^2.9.11", 12 | "axios": "^1.3.4", 13 | "bootstrap": "^5.2.3", 14 | "immer": "^9.0.19", 15 | "react": "^18.2.0", 16 | "react-dom": "^18.2.0", 17 | "react-hook-form": "7.43", 18 | "react-icons": "^4.7.1", 19 | "zod": "^3.20.6" 20 | }, 21 | "devDependencies": { 22 | "@types/react": "^18.0.27", 23 | "@types/react-dom": "^18.0.10", 24 | "@vitejs/plugin-react": "^3.1.0", 25 | "typescript": "^4.9.3", 26 | "vite": "^4.1.0" 27 | } 28 | }, 29 | "node_modules/@ampproject/remapping": { 30 | "version": "2.2.0", 31 | "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", 32 | "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", 33 | "dev": true, 34 | "dependencies": { 35 | "@jridgewell/gen-mapping": "^0.1.0", 36 | "@jridgewell/trace-mapping": "^0.3.9" 37 | }, 38 | "engines": { 39 | "node": ">=6.0.0" 40 | } 41 | }, 42 | "node_modules/@babel/code-frame": { 43 | "version": "7.18.6", 44 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", 45 | "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", 46 | "dev": true, 47 | "dependencies": { 48 | "@babel/highlight": "^7.18.6" 49 | }, 50 | "engines": { 51 | "node": ">=6.9.0" 52 | } 53 | }, 54 | "node_modules/@babel/compat-data": { 55 | "version": "7.20.14", 56 | "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.14.tgz", 57 | "integrity": "sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw==", 58 | "dev": true, 59 | "engines": { 60 | "node": ">=6.9.0" 61 | } 62 | }, 63 | "node_modules/@babel/core": { 64 | "version": "7.20.12", 65 | "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", 66 | "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", 67 | "dev": true, 68 | "dependencies": { 69 | "@ampproject/remapping": "^2.1.0", 70 | "@babel/code-frame": "^7.18.6", 71 | "@babel/generator": "^7.20.7", 72 | "@babel/helper-compilation-targets": "^7.20.7", 73 | "@babel/helper-module-transforms": "^7.20.11", 74 | "@babel/helpers": "^7.20.7", 75 | "@babel/parser": "^7.20.7", 76 | "@babel/template": "^7.20.7", 77 | "@babel/traverse": "^7.20.12", 78 | "@babel/types": "^7.20.7", 79 | "convert-source-map": "^1.7.0", 80 | "debug": "^4.1.0", 81 | "gensync": "^1.0.0-beta.2", 82 | "json5": "^2.2.2", 83 | "semver": "^6.3.0" 84 | }, 85 | "engines": { 86 | "node": ">=6.9.0" 87 | }, 88 | "funding": { 89 | "type": "opencollective", 90 | "url": "https://opencollective.com/babel" 91 | } 92 | }, 93 | "node_modules/@babel/generator": { 94 | "version": "7.20.14", 95 | "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.14.tgz", 96 | "integrity": "sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg==", 97 | "dev": true, 98 | "dependencies": { 99 | "@babel/types": "^7.20.7", 100 | "@jridgewell/gen-mapping": "^0.3.2", 101 | "jsesc": "^2.5.1" 102 | }, 103 | "engines": { 104 | "node": ">=6.9.0" 105 | } 106 | }, 107 | "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { 108 | "version": "0.3.2", 109 | "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", 110 | "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", 111 | "dev": true, 112 | "dependencies": { 113 | "@jridgewell/set-array": "^1.0.1", 114 | "@jridgewell/sourcemap-codec": "^1.4.10", 115 | "@jridgewell/trace-mapping": "^0.3.9" 116 | }, 117 | "engines": { 118 | "node": ">=6.0.0" 119 | } 120 | }, 121 | "node_modules/@babel/helper-compilation-targets": { 122 | "version": "7.20.7", 123 | "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", 124 | "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", 125 | "dev": true, 126 | "dependencies": { 127 | "@babel/compat-data": "^7.20.5", 128 | "@babel/helper-validator-option": "^7.18.6", 129 | "browserslist": "^4.21.3", 130 | "lru-cache": "^5.1.1", 131 | "semver": "^6.3.0" 132 | }, 133 | "engines": { 134 | "node": ">=6.9.0" 135 | }, 136 | "peerDependencies": { 137 | "@babel/core": "^7.0.0" 138 | } 139 | }, 140 | "node_modules/@babel/helper-environment-visitor": { 141 | "version": "7.18.9", 142 | "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", 143 | "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", 144 | "dev": true, 145 | "engines": { 146 | "node": ">=6.9.0" 147 | } 148 | }, 149 | "node_modules/@babel/helper-function-name": { 150 | "version": "7.19.0", 151 | "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", 152 | "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", 153 | "dev": true, 154 | "dependencies": { 155 | "@babel/template": "^7.18.10", 156 | "@babel/types": "^7.19.0" 157 | }, 158 | "engines": { 159 | "node": ">=6.9.0" 160 | } 161 | }, 162 | "node_modules/@babel/helper-hoist-variables": { 163 | "version": "7.18.6", 164 | "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", 165 | "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", 166 | "dev": true, 167 | "dependencies": { 168 | "@babel/types": "^7.18.6" 169 | }, 170 | "engines": { 171 | "node": ">=6.9.0" 172 | } 173 | }, 174 | "node_modules/@babel/helper-module-imports": { 175 | "version": "7.18.6", 176 | "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", 177 | "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", 178 | "dev": true, 179 | "dependencies": { 180 | "@babel/types": "^7.18.6" 181 | }, 182 | "engines": { 183 | "node": ">=6.9.0" 184 | } 185 | }, 186 | "node_modules/@babel/helper-module-transforms": { 187 | "version": "7.20.11", 188 | "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz", 189 | "integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==", 190 | "dev": true, 191 | "dependencies": { 192 | "@babel/helper-environment-visitor": "^7.18.9", 193 | "@babel/helper-module-imports": "^7.18.6", 194 | "@babel/helper-simple-access": "^7.20.2", 195 | "@babel/helper-split-export-declaration": "^7.18.6", 196 | "@babel/helper-validator-identifier": "^7.19.1", 197 | "@babel/template": "^7.20.7", 198 | "@babel/traverse": "^7.20.10", 199 | "@babel/types": "^7.20.7" 200 | }, 201 | "engines": { 202 | "node": ">=6.9.0" 203 | } 204 | }, 205 | "node_modules/@babel/helper-plugin-utils": { 206 | "version": "7.20.2", 207 | "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", 208 | "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", 209 | "dev": true, 210 | "engines": { 211 | "node": ">=6.9.0" 212 | } 213 | }, 214 | "node_modules/@babel/helper-simple-access": { 215 | "version": "7.20.2", 216 | "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", 217 | "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", 218 | "dev": true, 219 | "dependencies": { 220 | "@babel/types": "^7.20.2" 221 | }, 222 | "engines": { 223 | "node": ">=6.9.0" 224 | } 225 | }, 226 | "node_modules/@babel/helper-split-export-declaration": { 227 | "version": "7.18.6", 228 | "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", 229 | "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", 230 | "dev": true, 231 | "dependencies": { 232 | "@babel/types": "^7.18.6" 233 | }, 234 | "engines": { 235 | "node": ">=6.9.0" 236 | } 237 | }, 238 | "node_modules/@babel/helper-string-parser": { 239 | "version": "7.19.4", 240 | "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", 241 | "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", 242 | "dev": true, 243 | "engines": { 244 | "node": ">=6.9.0" 245 | } 246 | }, 247 | "node_modules/@babel/helper-validator-identifier": { 248 | "version": "7.19.1", 249 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", 250 | "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", 251 | "dev": true, 252 | "engines": { 253 | "node": ">=6.9.0" 254 | } 255 | }, 256 | "node_modules/@babel/helper-validator-option": { 257 | "version": "7.18.6", 258 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", 259 | "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", 260 | "dev": true, 261 | "engines": { 262 | "node": ">=6.9.0" 263 | } 264 | }, 265 | "node_modules/@babel/helpers": { 266 | "version": "7.20.13", 267 | "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.13.tgz", 268 | "integrity": "sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg==", 269 | "dev": true, 270 | "dependencies": { 271 | "@babel/template": "^7.20.7", 272 | "@babel/traverse": "^7.20.13", 273 | "@babel/types": "^7.20.7" 274 | }, 275 | "engines": { 276 | "node": ">=6.9.0" 277 | } 278 | }, 279 | "node_modules/@babel/highlight": { 280 | "version": "7.18.6", 281 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", 282 | "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", 283 | "dev": true, 284 | "dependencies": { 285 | "@babel/helper-validator-identifier": "^7.18.6", 286 | "chalk": "^2.0.0", 287 | "js-tokens": "^4.0.0" 288 | }, 289 | "engines": { 290 | "node": ">=6.9.0" 291 | } 292 | }, 293 | "node_modules/@babel/parser": { 294 | "version": "7.20.15", 295 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.15.tgz", 296 | "integrity": "sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg==", 297 | "dev": true, 298 | "bin": { 299 | "parser": "bin/babel-parser.js" 300 | }, 301 | "engines": { 302 | "node": ">=6.0.0" 303 | } 304 | }, 305 | "node_modules/@babel/plugin-transform-react-jsx-self": { 306 | "version": "7.18.6", 307 | "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.18.6.tgz", 308 | "integrity": "sha512-A0LQGx4+4Jv7u/tWzoJF7alZwnBDQd6cGLh9P+Ttk4dpiL+J5p7NSNv/9tlEFFJDq3kjxOavWmbm6t0Gk+A3Ig==", 309 | "dev": true, 310 | "dependencies": { 311 | "@babel/helper-plugin-utils": "^7.18.6" 312 | }, 313 | "engines": { 314 | "node": ">=6.9.0" 315 | }, 316 | "peerDependencies": { 317 | "@babel/core": "^7.0.0-0" 318 | } 319 | }, 320 | "node_modules/@babel/plugin-transform-react-jsx-source": { 321 | "version": "7.19.6", 322 | "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.19.6.tgz", 323 | "integrity": "sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==", 324 | "dev": true, 325 | "dependencies": { 326 | "@babel/helper-plugin-utils": "^7.19.0" 327 | }, 328 | "engines": { 329 | "node": ">=6.9.0" 330 | }, 331 | "peerDependencies": { 332 | "@babel/core": "^7.0.0-0" 333 | } 334 | }, 335 | "node_modules/@babel/template": { 336 | "version": "7.20.7", 337 | "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", 338 | "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", 339 | "dev": true, 340 | "dependencies": { 341 | "@babel/code-frame": "^7.18.6", 342 | "@babel/parser": "^7.20.7", 343 | "@babel/types": "^7.20.7" 344 | }, 345 | "engines": { 346 | "node": ">=6.9.0" 347 | } 348 | }, 349 | "node_modules/@babel/traverse": { 350 | "version": "7.20.13", 351 | "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz", 352 | "integrity": "sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==", 353 | "dev": true, 354 | "dependencies": { 355 | "@babel/code-frame": "^7.18.6", 356 | "@babel/generator": "^7.20.7", 357 | "@babel/helper-environment-visitor": "^7.18.9", 358 | "@babel/helper-function-name": "^7.19.0", 359 | "@babel/helper-hoist-variables": "^7.18.6", 360 | "@babel/helper-split-export-declaration": "^7.18.6", 361 | "@babel/parser": "^7.20.13", 362 | "@babel/types": "^7.20.7", 363 | "debug": "^4.1.0", 364 | "globals": "^11.1.0" 365 | }, 366 | "engines": { 367 | "node": ">=6.9.0" 368 | } 369 | }, 370 | "node_modules/@babel/types": { 371 | "version": "7.20.7", 372 | "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", 373 | "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", 374 | "dev": true, 375 | "dependencies": { 376 | "@babel/helper-string-parser": "^7.19.4", 377 | "@babel/helper-validator-identifier": "^7.19.1", 378 | "to-fast-properties": "^2.0.0" 379 | }, 380 | "engines": { 381 | "node": ">=6.9.0" 382 | } 383 | }, 384 | "node_modules/@esbuild/android-arm": { 385 | "version": "0.16.17", 386 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.16.17.tgz", 387 | "integrity": "sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==", 388 | "cpu": [ 389 | "arm" 390 | ], 391 | "dev": true, 392 | "optional": true, 393 | "os": [ 394 | "android" 395 | ], 396 | "engines": { 397 | "node": ">=12" 398 | } 399 | }, 400 | "node_modules/@esbuild/android-arm64": { 401 | "version": "0.16.17", 402 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz", 403 | "integrity": "sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==", 404 | "cpu": [ 405 | "arm64" 406 | ], 407 | "dev": true, 408 | "optional": true, 409 | "os": [ 410 | "android" 411 | ], 412 | "engines": { 413 | "node": ">=12" 414 | } 415 | }, 416 | "node_modules/@esbuild/android-x64": { 417 | "version": "0.16.17", 418 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.16.17.tgz", 419 | "integrity": "sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==", 420 | "cpu": [ 421 | "x64" 422 | ], 423 | "dev": true, 424 | "optional": true, 425 | "os": [ 426 | "android" 427 | ], 428 | "engines": { 429 | "node": ">=12" 430 | } 431 | }, 432 | "node_modules/@esbuild/darwin-arm64": { 433 | "version": "0.16.17", 434 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.16.17.tgz", 435 | "integrity": "sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==", 436 | "cpu": [ 437 | "arm64" 438 | ], 439 | "dev": true, 440 | "optional": true, 441 | "os": [ 442 | "darwin" 443 | ], 444 | "engines": { 445 | "node": ">=12" 446 | } 447 | }, 448 | "node_modules/@esbuild/darwin-x64": { 449 | "version": "0.16.17", 450 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.16.17.tgz", 451 | "integrity": "sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==", 452 | "cpu": [ 453 | "x64" 454 | ], 455 | "dev": true, 456 | "optional": true, 457 | "os": [ 458 | "darwin" 459 | ], 460 | "engines": { 461 | "node": ">=12" 462 | } 463 | }, 464 | "node_modules/@esbuild/freebsd-arm64": { 465 | "version": "0.16.17", 466 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.17.tgz", 467 | "integrity": "sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==", 468 | "cpu": [ 469 | "arm64" 470 | ], 471 | "dev": true, 472 | "optional": true, 473 | "os": [ 474 | "freebsd" 475 | ], 476 | "engines": { 477 | "node": ">=12" 478 | } 479 | }, 480 | "node_modules/@esbuild/freebsd-x64": { 481 | "version": "0.16.17", 482 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.16.17.tgz", 483 | "integrity": "sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==", 484 | "cpu": [ 485 | "x64" 486 | ], 487 | "dev": true, 488 | "optional": true, 489 | "os": [ 490 | "freebsd" 491 | ], 492 | "engines": { 493 | "node": ">=12" 494 | } 495 | }, 496 | "node_modules/@esbuild/linux-arm": { 497 | "version": "0.16.17", 498 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.16.17.tgz", 499 | "integrity": "sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==", 500 | "cpu": [ 501 | "arm" 502 | ], 503 | "dev": true, 504 | "optional": true, 505 | "os": [ 506 | "linux" 507 | ], 508 | "engines": { 509 | "node": ">=12" 510 | } 511 | }, 512 | "node_modules/@esbuild/linux-arm64": { 513 | "version": "0.16.17", 514 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.16.17.tgz", 515 | "integrity": "sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==", 516 | "cpu": [ 517 | "arm64" 518 | ], 519 | "dev": true, 520 | "optional": true, 521 | "os": [ 522 | "linux" 523 | ], 524 | "engines": { 525 | "node": ">=12" 526 | } 527 | }, 528 | "node_modules/@esbuild/linux-ia32": { 529 | "version": "0.16.17", 530 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.16.17.tgz", 531 | "integrity": "sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==", 532 | "cpu": [ 533 | "ia32" 534 | ], 535 | "dev": true, 536 | "optional": true, 537 | "os": [ 538 | "linux" 539 | ], 540 | "engines": { 541 | "node": ">=12" 542 | } 543 | }, 544 | "node_modules/@esbuild/linux-loong64": { 545 | "version": "0.16.17", 546 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.16.17.tgz", 547 | "integrity": "sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==", 548 | "cpu": [ 549 | "loong64" 550 | ], 551 | "dev": true, 552 | "optional": true, 553 | "os": [ 554 | "linux" 555 | ], 556 | "engines": { 557 | "node": ">=12" 558 | } 559 | }, 560 | "node_modules/@esbuild/linux-mips64el": { 561 | "version": "0.16.17", 562 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.16.17.tgz", 563 | "integrity": "sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==", 564 | "cpu": [ 565 | "mips64el" 566 | ], 567 | "dev": true, 568 | "optional": true, 569 | "os": [ 570 | "linux" 571 | ], 572 | "engines": { 573 | "node": ">=12" 574 | } 575 | }, 576 | "node_modules/@esbuild/linux-ppc64": { 577 | "version": "0.16.17", 578 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.16.17.tgz", 579 | "integrity": "sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==", 580 | "cpu": [ 581 | "ppc64" 582 | ], 583 | "dev": true, 584 | "optional": true, 585 | "os": [ 586 | "linux" 587 | ], 588 | "engines": { 589 | "node": ">=12" 590 | } 591 | }, 592 | "node_modules/@esbuild/linux-riscv64": { 593 | "version": "0.16.17", 594 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.16.17.tgz", 595 | "integrity": "sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==", 596 | "cpu": [ 597 | "riscv64" 598 | ], 599 | "dev": true, 600 | "optional": true, 601 | "os": [ 602 | "linux" 603 | ], 604 | "engines": { 605 | "node": ">=12" 606 | } 607 | }, 608 | "node_modules/@esbuild/linux-s390x": { 609 | "version": "0.16.17", 610 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.16.17.tgz", 611 | "integrity": "sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==", 612 | "cpu": [ 613 | "s390x" 614 | ], 615 | "dev": true, 616 | "optional": true, 617 | "os": [ 618 | "linux" 619 | ], 620 | "engines": { 621 | "node": ">=12" 622 | } 623 | }, 624 | "node_modules/@esbuild/linux-x64": { 625 | "version": "0.16.17", 626 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.17.tgz", 627 | "integrity": "sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==", 628 | "cpu": [ 629 | "x64" 630 | ], 631 | "dev": true, 632 | "optional": true, 633 | "os": [ 634 | "linux" 635 | ], 636 | "engines": { 637 | "node": ">=12" 638 | } 639 | }, 640 | "node_modules/@esbuild/netbsd-x64": { 641 | "version": "0.16.17", 642 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.16.17.tgz", 643 | "integrity": "sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==", 644 | "cpu": [ 645 | "x64" 646 | ], 647 | "dev": true, 648 | "optional": true, 649 | "os": [ 650 | "netbsd" 651 | ], 652 | "engines": { 653 | "node": ">=12" 654 | } 655 | }, 656 | "node_modules/@esbuild/openbsd-x64": { 657 | "version": "0.16.17", 658 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.16.17.tgz", 659 | "integrity": "sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==", 660 | "cpu": [ 661 | "x64" 662 | ], 663 | "dev": true, 664 | "optional": true, 665 | "os": [ 666 | "openbsd" 667 | ], 668 | "engines": { 669 | "node": ">=12" 670 | } 671 | }, 672 | "node_modules/@esbuild/sunos-x64": { 673 | "version": "0.16.17", 674 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.16.17.tgz", 675 | "integrity": "sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==", 676 | "cpu": [ 677 | "x64" 678 | ], 679 | "dev": true, 680 | "optional": true, 681 | "os": [ 682 | "sunos" 683 | ], 684 | "engines": { 685 | "node": ">=12" 686 | } 687 | }, 688 | "node_modules/@esbuild/win32-arm64": { 689 | "version": "0.16.17", 690 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.16.17.tgz", 691 | "integrity": "sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==", 692 | "cpu": [ 693 | "arm64" 694 | ], 695 | "dev": true, 696 | "optional": true, 697 | "os": [ 698 | "win32" 699 | ], 700 | "engines": { 701 | "node": ">=12" 702 | } 703 | }, 704 | "node_modules/@esbuild/win32-ia32": { 705 | "version": "0.16.17", 706 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.16.17.tgz", 707 | "integrity": "sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==", 708 | "cpu": [ 709 | "ia32" 710 | ], 711 | "dev": true, 712 | "optional": true, 713 | "os": [ 714 | "win32" 715 | ], 716 | "engines": { 717 | "node": ">=12" 718 | } 719 | }, 720 | "node_modules/@esbuild/win32-x64": { 721 | "version": "0.16.17", 722 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz", 723 | "integrity": "sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==", 724 | "cpu": [ 725 | "x64" 726 | ], 727 | "dev": true, 728 | "optional": true, 729 | "os": [ 730 | "win32" 731 | ], 732 | "engines": { 733 | "node": ">=12" 734 | } 735 | }, 736 | "node_modules/@hookform/resolvers": { 737 | "version": "2.9.11", 738 | "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-2.9.11.tgz", 739 | "integrity": "sha512-bA3aZ79UgcHj7tFV7RlgThzwSSHZgvfbt2wprldRkYBcMopdMvHyO17Wwp/twcJasNFischFfS7oz8Katz8DdQ==", 740 | "peerDependencies": { 741 | "react-hook-form": "^7.0.0" 742 | } 743 | }, 744 | "node_modules/@jridgewell/gen-mapping": { 745 | "version": "0.1.1", 746 | "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", 747 | "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", 748 | "dev": true, 749 | "dependencies": { 750 | "@jridgewell/set-array": "^1.0.0", 751 | "@jridgewell/sourcemap-codec": "^1.4.10" 752 | }, 753 | "engines": { 754 | "node": ">=6.0.0" 755 | } 756 | }, 757 | "node_modules/@jridgewell/resolve-uri": { 758 | "version": "3.1.0", 759 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", 760 | "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", 761 | "dev": true, 762 | "engines": { 763 | "node": ">=6.0.0" 764 | } 765 | }, 766 | "node_modules/@jridgewell/set-array": { 767 | "version": "1.1.2", 768 | "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", 769 | "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", 770 | "dev": true, 771 | "engines": { 772 | "node": ">=6.0.0" 773 | } 774 | }, 775 | "node_modules/@jridgewell/sourcemap-codec": { 776 | "version": "1.4.14", 777 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", 778 | "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", 779 | "dev": true 780 | }, 781 | "node_modules/@jridgewell/trace-mapping": { 782 | "version": "0.3.17", 783 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", 784 | "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", 785 | "dev": true, 786 | "dependencies": { 787 | "@jridgewell/resolve-uri": "3.1.0", 788 | "@jridgewell/sourcemap-codec": "1.4.14" 789 | } 790 | }, 791 | "node_modules/@popperjs/core": { 792 | "version": "2.11.6", 793 | "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", 794 | "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==", 795 | "peer": true, 796 | "funding": { 797 | "type": "opencollective", 798 | "url": "https://opencollective.com/popperjs" 799 | } 800 | }, 801 | "node_modules/@types/prop-types": { 802 | "version": "15.7.5", 803 | "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", 804 | "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", 805 | "dev": true 806 | }, 807 | "node_modules/@types/react": { 808 | "version": "18.0.27", 809 | "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.27.tgz", 810 | "integrity": "sha512-3vtRKHgVxu3Jp9t718R9BuzoD4NcQ8YJ5XRzsSKxNDiDonD2MXIT1TmSkenxuCycZJoQT5d2vE8LwWJxBC1gmA==", 811 | "dev": true, 812 | "dependencies": { 813 | "@types/prop-types": "*", 814 | "@types/scheduler": "*", 815 | "csstype": "^3.0.2" 816 | } 817 | }, 818 | "node_modules/@types/react-dom": { 819 | "version": "18.0.10", 820 | "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.10.tgz", 821 | "integrity": "sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg==", 822 | "dev": true, 823 | "dependencies": { 824 | "@types/react": "*" 825 | } 826 | }, 827 | "node_modules/@types/scheduler": { 828 | "version": "0.16.2", 829 | "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", 830 | "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", 831 | "dev": true 832 | }, 833 | "node_modules/@vitejs/plugin-react": { 834 | "version": "3.1.0", 835 | "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-3.1.0.tgz", 836 | "integrity": "sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==", 837 | "dev": true, 838 | "dependencies": { 839 | "@babel/core": "^7.20.12", 840 | "@babel/plugin-transform-react-jsx-self": "^7.18.6", 841 | "@babel/plugin-transform-react-jsx-source": "^7.19.6", 842 | "magic-string": "^0.27.0", 843 | "react-refresh": "^0.14.0" 844 | }, 845 | "engines": { 846 | "node": "^14.18.0 || >=16.0.0" 847 | }, 848 | "peerDependencies": { 849 | "vite": "^4.1.0-beta.0" 850 | } 851 | }, 852 | "node_modules/ansi-styles": { 853 | "version": "3.2.1", 854 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 855 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 856 | "dev": true, 857 | "dependencies": { 858 | "color-convert": "^1.9.0" 859 | }, 860 | "engines": { 861 | "node": ">=4" 862 | } 863 | }, 864 | "node_modules/asynckit": { 865 | "version": "0.4.0", 866 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 867 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 868 | }, 869 | "node_modules/axios": { 870 | "version": "1.3.4", 871 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", 872 | "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", 873 | "dependencies": { 874 | "follow-redirects": "^1.15.0", 875 | "form-data": "^4.0.0", 876 | "proxy-from-env": "^1.1.0" 877 | } 878 | }, 879 | "node_modules/bootstrap": { 880 | "version": "5.2.3", 881 | "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.3.tgz", 882 | "integrity": "sha512-cEKPM+fwb3cT8NzQZYEu4HilJ3anCrWqh3CHAok1p9jXqMPsPTBhU25fBckEJHJ/p+tTxTFTsFQGM+gaHpi3QQ==", 883 | "funding": [ 884 | { 885 | "type": "github", 886 | "url": "https://github.com/sponsors/twbs" 887 | }, 888 | { 889 | "type": "opencollective", 890 | "url": "https://opencollective.com/bootstrap" 891 | } 892 | ], 893 | "peerDependencies": { 894 | "@popperjs/core": "^2.11.6" 895 | } 896 | }, 897 | "node_modules/browserslist": { 898 | "version": "4.21.5", 899 | "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", 900 | "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", 901 | "dev": true, 902 | "funding": [ 903 | { 904 | "type": "opencollective", 905 | "url": "https://opencollective.com/browserslist" 906 | }, 907 | { 908 | "type": "tidelift", 909 | "url": "https://tidelift.com/funding/github/npm/browserslist" 910 | } 911 | ], 912 | "dependencies": { 913 | "caniuse-lite": "^1.0.30001449", 914 | "electron-to-chromium": "^1.4.284", 915 | "node-releases": "^2.0.8", 916 | "update-browserslist-db": "^1.0.10" 917 | }, 918 | "bin": { 919 | "browserslist": "cli.js" 920 | }, 921 | "engines": { 922 | "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" 923 | } 924 | }, 925 | "node_modules/caniuse-lite": { 926 | "version": "1.0.30001450", 927 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001450.tgz", 928 | "integrity": "sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew==", 929 | "dev": true, 930 | "funding": [ 931 | { 932 | "type": "opencollective", 933 | "url": "https://opencollective.com/browserslist" 934 | }, 935 | { 936 | "type": "tidelift", 937 | "url": "https://tidelift.com/funding/github/npm/caniuse-lite" 938 | } 939 | ] 940 | }, 941 | "node_modules/chalk": { 942 | "version": "2.4.2", 943 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 944 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 945 | "dev": true, 946 | "dependencies": { 947 | "ansi-styles": "^3.2.1", 948 | "escape-string-regexp": "^1.0.5", 949 | "supports-color": "^5.3.0" 950 | }, 951 | "engines": { 952 | "node": ">=4" 953 | } 954 | }, 955 | "node_modules/color-convert": { 956 | "version": "1.9.3", 957 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 958 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 959 | "dev": true, 960 | "dependencies": { 961 | "color-name": "1.1.3" 962 | } 963 | }, 964 | "node_modules/color-name": { 965 | "version": "1.1.3", 966 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 967 | "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", 968 | "dev": true 969 | }, 970 | "node_modules/combined-stream": { 971 | "version": "1.0.8", 972 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 973 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 974 | "dependencies": { 975 | "delayed-stream": "~1.0.0" 976 | }, 977 | "engines": { 978 | "node": ">= 0.8" 979 | } 980 | }, 981 | "node_modules/convert-source-map": { 982 | "version": "1.9.0", 983 | "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", 984 | "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", 985 | "dev": true 986 | }, 987 | "node_modules/csstype": { 988 | "version": "3.1.1", 989 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", 990 | "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==", 991 | "dev": true 992 | }, 993 | "node_modules/debug": { 994 | "version": "4.3.4", 995 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 996 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 997 | "dev": true, 998 | "dependencies": { 999 | "ms": "2.1.2" 1000 | }, 1001 | "engines": { 1002 | "node": ">=6.0" 1003 | }, 1004 | "peerDependenciesMeta": { 1005 | "supports-color": { 1006 | "optional": true 1007 | } 1008 | } 1009 | }, 1010 | "node_modules/delayed-stream": { 1011 | "version": "1.0.0", 1012 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 1013 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 1014 | "engines": { 1015 | "node": ">=0.4.0" 1016 | } 1017 | }, 1018 | "node_modules/electron-to-chromium": { 1019 | "version": "1.4.286", 1020 | "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.286.tgz", 1021 | "integrity": "sha512-Vp3CVhmYpgf4iXNKAucoQUDcCrBQX3XLBtwgFqP9BUXuucgvAV9zWp1kYU7LL9j4++s9O+12cb3wMtN4SJy6UQ==", 1022 | "dev": true 1023 | }, 1024 | "node_modules/esbuild": { 1025 | "version": "0.16.17", 1026 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.16.17.tgz", 1027 | "integrity": "sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==", 1028 | "dev": true, 1029 | "hasInstallScript": true, 1030 | "bin": { 1031 | "esbuild": "bin/esbuild" 1032 | }, 1033 | "engines": { 1034 | "node": ">=12" 1035 | }, 1036 | "optionalDependencies": { 1037 | "@esbuild/android-arm": "0.16.17", 1038 | "@esbuild/android-arm64": "0.16.17", 1039 | "@esbuild/android-x64": "0.16.17", 1040 | "@esbuild/darwin-arm64": "0.16.17", 1041 | "@esbuild/darwin-x64": "0.16.17", 1042 | "@esbuild/freebsd-arm64": "0.16.17", 1043 | "@esbuild/freebsd-x64": "0.16.17", 1044 | "@esbuild/linux-arm": "0.16.17", 1045 | "@esbuild/linux-arm64": "0.16.17", 1046 | "@esbuild/linux-ia32": "0.16.17", 1047 | "@esbuild/linux-loong64": "0.16.17", 1048 | "@esbuild/linux-mips64el": "0.16.17", 1049 | "@esbuild/linux-ppc64": "0.16.17", 1050 | "@esbuild/linux-riscv64": "0.16.17", 1051 | "@esbuild/linux-s390x": "0.16.17", 1052 | "@esbuild/linux-x64": "0.16.17", 1053 | "@esbuild/netbsd-x64": "0.16.17", 1054 | "@esbuild/openbsd-x64": "0.16.17", 1055 | "@esbuild/sunos-x64": "0.16.17", 1056 | "@esbuild/win32-arm64": "0.16.17", 1057 | "@esbuild/win32-ia32": "0.16.17", 1058 | "@esbuild/win32-x64": "0.16.17" 1059 | } 1060 | }, 1061 | "node_modules/escalade": { 1062 | "version": "3.1.1", 1063 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 1064 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 1065 | "dev": true, 1066 | "engines": { 1067 | "node": ">=6" 1068 | } 1069 | }, 1070 | "node_modules/escape-string-regexp": { 1071 | "version": "1.0.5", 1072 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 1073 | "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", 1074 | "dev": true, 1075 | "engines": { 1076 | "node": ">=0.8.0" 1077 | } 1078 | }, 1079 | "node_modules/follow-redirects": { 1080 | "version": "1.15.2", 1081 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", 1082 | "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", 1083 | "funding": [ 1084 | { 1085 | "type": "individual", 1086 | "url": "https://github.com/sponsors/RubenVerborgh" 1087 | } 1088 | ], 1089 | "engines": { 1090 | "node": ">=4.0" 1091 | }, 1092 | "peerDependenciesMeta": { 1093 | "debug": { 1094 | "optional": true 1095 | } 1096 | } 1097 | }, 1098 | "node_modules/form-data": { 1099 | "version": "4.0.0", 1100 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 1101 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 1102 | "dependencies": { 1103 | "asynckit": "^0.4.0", 1104 | "combined-stream": "^1.0.8", 1105 | "mime-types": "^2.1.12" 1106 | }, 1107 | "engines": { 1108 | "node": ">= 6" 1109 | } 1110 | }, 1111 | "node_modules/fsevents": { 1112 | "version": "2.3.2", 1113 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 1114 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 1115 | "dev": true, 1116 | "hasInstallScript": true, 1117 | "optional": true, 1118 | "os": [ 1119 | "darwin" 1120 | ], 1121 | "engines": { 1122 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 1123 | } 1124 | }, 1125 | "node_modules/function-bind": { 1126 | "version": "1.1.1", 1127 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 1128 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 1129 | "dev": true 1130 | }, 1131 | "node_modules/gensync": { 1132 | "version": "1.0.0-beta.2", 1133 | "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", 1134 | "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", 1135 | "dev": true, 1136 | "engines": { 1137 | "node": ">=6.9.0" 1138 | } 1139 | }, 1140 | "node_modules/globals": { 1141 | "version": "11.12.0", 1142 | "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", 1143 | "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", 1144 | "dev": true, 1145 | "engines": { 1146 | "node": ">=4" 1147 | } 1148 | }, 1149 | "node_modules/has": { 1150 | "version": "1.0.3", 1151 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 1152 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 1153 | "dev": true, 1154 | "dependencies": { 1155 | "function-bind": "^1.1.1" 1156 | }, 1157 | "engines": { 1158 | "node": ">= 0.4.0" 1159 | } 1160 | }, 1161 | "node_modules/has-flag": { 1162 | "version": "3.0.0", 1163 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 1164 | "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", 1165 | "dev": true, 1166 | "engines": { 1167 | "node": ">=4" 1168 | } 1169 | }, 1170 | "node_modules/immer": { 1171 | "version": "9.0.19", 1172 | "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.19.tgz", 1173 | "integrity": "sha512-eY+Y0qcsB4TZKwgQzLaE/lqYMlKhv5J9dyd2RhhtGhNo2njPXDqU9XPfcNfa3MIDsdtZt5KlkIsirlo4dHsWdQ==", 1174 | "funding": { 1175 | "type": "opencollective", 1176 | "url": "https://opencollective.com/immer" 1177 | } 1178 | }, 1179 | "node_modules/is-core-module": { 1180 | "version": "2.11.0", 1181 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", 1182 | "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", 1183 | "dev": true, 1184 | "dependencies": { 1185 | "has": "^1.0.3" 1186 | }, 1187 | "funding": { 1188 | "url": "https://github.com/sponsors/ljharb" 1189 | } 1190 | }, 1191 | "node_modules/js-tokens": { 1192 | "version": "4.0.0", 1193 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 1194 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 1195 | }, 1196 | "node_modules/jsesc": { 1197 | "version": "2.5.2", 1198 | "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", 1199 | "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", 1200 | "dev": true, 1201 | "bin": { 1202 | "jsesc": "bin/jsesc" 1203 | }, 1204 | "engines": { 1205 | "node": ">=4" 1206 | } 1207 | }, 1208 | "node_modules/json5": { 1209 | "version": "2.2.3", 1210 | "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", 1211 | "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", 1212 | "dev": true, 1213 | "bin": { 1214 | "json5": "lib/cli.js" 1215 | }, 1216 | "engines": { 1217 | "node": ">=6" 1218 | } 1219 | }, 1220 | "node_modules/loose-envify": { 1221 | "version": "1.4.0", 1222 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 1223 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 1224 | "dependencies": { 1225 | "js-tokens": "^3.0.0 || ^4.0.0" 1226 | }, 1227 | "bin": { 1228 | "loose-envify": "cli.js" 1229 | } 1230 | }, 1231 | "node_modules/lru-cache": { 1232 | "version": "5.1.1", 1233 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", 1234 | "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", 1235 | "dev": true, 1236 | "dependencies": { 1237 | "yallist": "^3.0.2" 1238 | } 1239 | }, 1240 | "node_modules/magic-string": { 1241 | "version": "0.27.0", 1242 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", 1243 | "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", 1244 | "dev": true, 1245 | "dependencies": { 1246 | "@jridgewell/sourcemap-codec": "^1.4.13" 1247 | }, 1248 | "engines": { 1249 | "node": ">=12" 1250 | } 1251 | }, 1252 | "node_modules/mime-db": { 1253 | "version": "1.52.0", 1254 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 1255 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 1256 | "engines": { 1257 | "node": ">= 0.6" 1258 | } 1259 | }, 1260 | "node_modules/mime-types": { 1261 | "version": "2.1.35", 1262 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 1263 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 1264 | "dependencies": { 1265 | "mime-db": "1.52.0" 1266 | }, 1267 | "engines": { 1268 | "node": ">= 0.6" 1269 | } 1270 | }, 1271 | "node_modules/ms": { 1272 | "version": "2.1.2", 1273 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1274 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 1275 | "dev": true 1276 | }, 1277 | "node_modules/nanoid": { 1278 | "version": "3.3.4", 1279 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", 1280 | "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", 1281 | "dev": true, 1282 | "bin": { 1283 | "nanoid": "bin/nanoid.cjs" 1284 | }, 1285 | "engines": { 1286 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 1287 | } 1288 | }, 1289 | "node_modules/node-releases": { 1290 | "version": "2.0.10", 1291 | "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", 1292 | "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", 1293 | "dev": true 1294 | }, 1295 | "node_modules/path-parse": { 1296 | "version": "1.0.7", 1297 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 1298 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 1299 | "dev": true 1300 | }, 1301 | "node_modules/picocolors": { 1302 | "version": "1.0.0", 1303 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 1304 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", 1305 | "dev": true 1306 | }, 1307 | "node_modules/postcss": { 1308 | "version": "8.4.21", 1309 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", 1310 | "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", 1311 | "dev": true, 1312 | "funding": [ 1313 | { 1314 | "type": "opencollective", 1315 | "url": "https://opencollective.com/postcss/" 1316 | }, 1317 | { 1318 | "type": "tidelift", 1319 | "url": "https://tidelift.com/funding/github/npm/postcss" 1320 | } 1321 | ], 1322 | "dependencies": { 1323 | "nanoid": "^3.3.4", 1324 | "picocolors": "^1.0.0", 1325 | "source-map-js": "^1.0.2" 1326 | }, 1327 | "engines": { 1328 | "node": "^10 || ^12 || >=14" 1329 | } 1330 | }, 1331 | "node_modules/proxy-from-env": { 1332 | "version": "1.1.0", 1333 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 1334 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 1335 | }, 1336 | "node_modules/react": { 1337 | "version": "18.2.0", 1338 | "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", 1339 | "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", 1340 | "dependencies": { 1341 | "loose-envify": "^1.1.0" 1342 | }, 1343 | "engines": { 1344 | "node": ">=0.10.0" 1345 | } 1346 | }, 1347 | "node_modules/react-dom": { 1348 | "version": "18.2.0", 1349 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", 1350 | "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", 1351 | "dependencies": { 1352 | "loose-envify": "^1.1.0", 1353 | "scheduler": "^0.23.0" 1354 | }, 1355 | "peerDependencies": { 1356 | "react": "^18.2.0" 1357 | } 1358 | }, 1359 | "node_modules/react-hook-form": { 1360 | "version": "7.43.1", 1361 | "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.43.1.tgz", 1362 | "integrity": "sha512-+s3+s8LLytRMriwwuSqeLStVjRXFGxgjjx2jED7Z+wz1J/88vpxieRQGvJVvzrzVxshZ0BRuocFERb779m2kNg==", 1363 | "engines": { 1364 | "node": ">=12.22.0" 1365 | }, 1366 | "funding": { 1367 | "type": "opencollective", 1368 | "url": "https://opencollective.com/react-hook-form" 1369 | }, 1370 | "peerDependencies": { 1371 | "react": "^16.8.0 || ^17 || ^18" 1372 | } 1373 | }, 1374 | "node_modules/react-icons": { 1375 | "version": "4.7.1", 1376 | "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.7.1.tgz", 1377 | "integrity": "sha512-yHd3oKGMgm7zxo3EA7H2n7vxSoiGmHk5t6Ou4bXsfcgWyhfDKMpyKfhHR6Bjnn63c+YXBLBPUql9H4wPJM6sXw==", 1378 | "peerDependencies": { 1379 | "react": "*" 1380 | } 1381 | }, 1382 | "node_modules/react-refresh": { 1383 | "version": "0.14.0", 1384 | "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", 1385 | "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", 1386 | "dev": true, 1387 | "engines": { 1388 | "node": ">=0.10.0" 1389 | } 1390 | }, 1391 | "node_modules/resolve": { 1392 | "version": "1.22.1", 1393 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", 1394 | "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", 1395 | "dev": true, 1396 | "dependencies": { 1397 | "is-core-module": "^2.9.0", 1398 | "path-parse": "^1.0.7", 1399 | "supports-preserve-symlinks-flag": "^1.0.0" 1400 | }, 1401 | "bin": { 1402 | "resolve": "bin/resolve" 1403 | }, 1404 | "funding": { 1405 | "url": "https://github.com/sponsors/ljharb" 1406 | } 1407 | }, 1408 | "node_modules/rollup": { 1409 | "version": "3.14.0", 1410 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.14.0.tgz", 1411 | "integrity": "sha512-o23sdgCLcLSe3zIplT9nQ1+r97okuaiR+vmAPZPTDYB7/f3tgWIYNyiQveMsZwshBT0is4eGax/HH83Q7CG+/Q==", 1412 | "dev": true, 1413 | "bin": { 1414 | "rollup": "dist/bin/rollup" 1415 | }, 1416 | "engines": { 1417 | "node": ">=14.18.0", 1418 | "npm": ">=8.0.0" 1419 | }, 1420 | "optionalDependencies": { 1421 | "fsevents": "~2.3.2" 1422 | } 1423 | }, 1424 | "node_modules/scheduler": { 1425 | "version": "0.23.0", 1426 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", 1427 | "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", 1428 | "dependencies": { 1429 | "loose-envify": "^1.1.0" 1430 | } 1431 | }, 1432 | "node_modules/semver": { 1433 | "version": "6.3.0", 1434 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 1435 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", 1436 | "dev": true, 1437 | "bin": { 1438 | "semver": "bin/semver.js" 1439 | } 1440 | }, 1441 | "node_modules/source-map-js": { 1442 | "version": "1.0.2", 1443 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 1444 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", 1445 | "dev": true, 1446 | "engines": { 1447 | "node": ">=0.10.0" 1448 | } 1449 | }, 1450 | "node_modules/supports-color": { 1451 | "version": "5.5.0", 1452 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 1453 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 1454 | "dev": true, 1455 | "dependencies": { 1456 | "has-flag": "^3.0.0" 1457 | }, 1458 | "engines": { 1459 | "node": ">=4" 1460 | } 1461 | }, 1462 | "node_modules/supports-preserve-symlinks-flag": { 1463 | "version": "1.0.0", 1464 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 1465 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 1466 | "dev": true, 1467 | "engines": { 1468 | "node": ">= 0.4" 1469 | }, 1470 | "funding": { 1471 | "url": "https://github.com/sponsors/ljharb" 1472 | } 1473 | }, 1474 | "node_modules/to-fast-properties": { 1475 | "version": "2.0.0", 1476 | "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", 1477 | "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", 1478 | "dev": true, 1479 | "engines": { 1480 | "node": ">=4" 1481 | } 1482 | }, 1483 | "node_modules/typescript": { 1484 | "version": "4.9.5", 1485 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", 1486 | "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", 1487 | "dev": true, 1488 | "bin": { 1489 | "tsc": "bin/tsc", 1490 | "tsserver": "bin/tsserver" 1491 | }, 1492 | "engines": { 1493 | "node": ">=4.2.0" 1494 | } 1495 | }, 1496 | "node_modules/update-browserslist-db": { 1497 | "version": "1.0.10", 1498 | "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", 1499 | "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", 1500 | "dev": true, 1501 | "funding": [ 1502 | { 1503 | "type": "opencollective", 1504 | "url": "https://opencollective.com/browserslist" 1505 | }, 1506 | { 1507 | "type": "tidelift", 1508 | "url": "https://tidelift.com/funding/github/npm/browserslist" 1509 | } 1510 | ], 1511 | "dependencies": { 1512 | "escalade": "^3.1.1", 1513 | "picocolors": "^1.0.0" 1514 | }, 1515 | "bin": { 1516 | "browserslist-lint": "cli.js" 1517 | }, 1518 | "peerDependencies": { 1519 | "browserslist": ">= 4.21.0" 1520 | } 1521 | }, 1522 | "node_modules/vite": { 1523 | "version": "4.1.1", 1524 | "resolved": "https://registry.npmjs.org/vite/-/vite-4.1.1.tgz", 1525 | "integrity": "sha512-LM9WWea8vsxhr782r9ntg+bhSFS06FJgCvvB0+8hf8UWtvaiDagKYWXndjfX6kGl74keHJUcpzrQliDXZlF5yg==", 1526 | "dev": true, 1527 | "dependencies": { 1528 | "esbuild": "^0.16.14", 1529 | "postcss": "^8.4.21", 1530 | "resolve": "^1.22.1", 1531 | "rollup": "^3.10.0" 1532 | }, 1533 | "bin": { 1534 | "vite": "bin/vite.js" 1535 | }, 1536 | "engines": { 1537 | "node": "^14.18.0 || >=16.0.0" 1538 | }, 1539 | "optionalDependencies": { 1540 | "fsevents": "~2.3.2" 1541 | }, 1542 | "peerDependencies": { 1543 | "@types/node": ">= 14", 1544 | "less": "*", 1545 | "sass": "*", 1546 | "stylus": "*", 1547 | "sugarss": "*", 1548 | "terser": "^5.4.0" 1549 | }, 1550 | "peerDependenciesMeta": { 1551 | "@types/node": { 1552 | "optional": true 1553 | }, 1554 | "less": { 1555 | "optional": true 1556 | }, 1557 | "sass": { 1558 | "optional": true 1559 | }, 1560 | "stylus": { 1561 | "optional": true 1562 | }, 1563 | "sugarss": { 1564 | "optional": true 1565 | }, 1566 | "terser": { 1567 | "optional": true 1568 | } 1569 | } 1570 | }, 1571 | "node_modules/yallist": { 1572 | "version": "3.1.1", 1573 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", 1574 | "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", 1575 | "dev": true 1576 | }, 1577 | "node_modules/zod": { 1578 | "version": "3.20.6", 1579 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.20.6.tgz", 1580 | "integrity": "sha512-oyu0m54SGCtzh6EClBVqDDlAYRz4jrVtKwQ7ZnsEmMI9HnzuZFj8QFwAY1M5uniIYACdGvv0PBWPF2kO0aNofA==", 1581 | "funding": { 1582 | "url": "https://github.com/sponsors/colinhacks" 1583 | } 1584 | } 1585 | } 1586 | } 1587 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-app", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@hookform/resolvers": "^2.9.11", 13 | "axios": "^1.3.4", 14 | "bootstrap": "^5.2.3", 15 | "immer": "^9.0.19", 16 | "react": "^18.2.0", 17 | "react-dom": "^18.2.0", 18 | "react-hook-form": "7.43", 19 | "react-icons": "^4.7.1", 20 | "zod": "^3.20.6" 21 | }, 22 | "devDependencies": { 23 | "@types/react": "^18.0.27", 24 | "@types/react-dom": "^18.0.10", 25 | "@vitejs/plugin-react": "^3.1.0", 26 | "typescript": "^4.9.3", 27 | "vite": "^4.1.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mosh-hamedani/react-course-part1/6f88e918a4e5e75cf5bea81ee54a341287e420e6/src/App.css -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import UserList from "./components/UserList"; 2 | 3 | function App() { 4 | return ; 5 | } 6 | 7 | export default App; 8 | -------------------------------------------------------------------------------- /src/Message.tsx: -------------------------------------------------------------------------------- 1 | 2 | // PascalCasing 3 | function Message() { 4 | // JSX: JavaScript XML 5 | const name = ''; 6 | if (name) 7 | return

Hello {name}

; 8 | return

Hello World

; 9 | } 10 | 11 | export default Message; -------------------------------------------------------------------------------- /src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Alert.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | interface Props { 4 | children: ReactNode; 5 | onClose: () => void; 6 | } 7 | 8 | const Alert = ({ children, onClose }: Props) => { 9 | return ( 10 |
11 | {children} 12 | 19 |
20 | ); 21 | }; 22 | 23 | export default Alert; 24 | -------------------------------------------------------------------------------- /src/components/Button/Button.module.css: -------------------------------------------------------------------------------- 1 | 2 | .btn { 3 | padding: 8px 12px; 4 | border-radius: 3px; 5 | border: 0; 6 | } 7 | 8 | .btn-primary { 9 | background-color: #0d6efd; 10 | color: white; 11 | } -------------------------------------------------------------------------------- /src/components/Button/Button.tsx: -------------------------------------------------------------------------------- 1 | import styles from './Button.module.css'; 2 | 3 | interface Props { 4 | children: string; 5 | color?: "primary" | "secondary" | "danger"; 6 | onClick: () => void; 7 | } 8 | 9 | const Button = ({ children, onClick, color = "primary" }: Props) => { 10 | return ( 11 | 14 | ); 15 | }; 16 | 17 | export default Button; 18 | -------------------------------------------------------------------------------- /src/components/Cart.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | interface Props { 4 | cartItems: string[]; 5 | onClear: () => void; 6 | } 7 | 8 | const Cart = ({ cartItems, onClear }: Props) => { 9 | return ( 10 | <> 11 |
Cart
12 | 15 | 16 | 17 | ) 18 | } 19 | 20 | export default Cart -------------------------------------------------------------------------------- /src/components/ExpandableText.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | 3 | interface Props { 4 | children: string; 5 | maxChars?: number; 6 | } 7 | 8 | const ExpandableText = ({ children, maxChars = 100 }: Props) => { 9 | const [isExpanded, setExpanded] = useState(false); 10 | 11 | if (children.length <= maxChars) return

{children}

; 12 | 13 | const text = isExpanded ? children : children.substring(0, maxChars); 14 | 15 | return

{text}...

; 16 | }; 17 | 18 | export default ExpandableText; 19 | -------------------------------------------------------------------------------- /src/components/Form.tsx: -------------------------------------------------------------------------------- 1 | import { FieldValues, useForm } from "react-hook-form"; 2 | import { z } from "zod"; 3 | import { zodResolver } from "@hookform/resolvers/zod"; 4 | 5 | const schema = z.object({ 6 | name: z.string().min(3, { message: "Name must be at least 3 characters." }), 7 | age: z 8 | .number({ invalid_type_error: "Age field is required." }) 9 | .min(18, { message: "Age must be at least 18." }), 10 | }); 11 | 12 | type FormData = z.infer; 13 | 14 | const Form = () => { 15 | const { 16 | register, 17 | handleSubmit, 18 | formState: { errors, isValid }, 19 | } = useForm({ resolver: zodResolver(schema) }); 20 | 21 | const onSubmit = (data: FieldValues) => console.log(data); 22 | 23 | return ( 24 |
25 |
26 | 29 | 35 | {errors.name &&

{errors.name.message}

} 36 |
37 |
38 | 41 | 47 | {errors.age &&

{errors.age.message}

} 48 |
49 | 52 |
53 | ); 54 | }; 55 | 56 | export default Form; 57 | -------------------------------------------------------------------------------- /src/components/Like.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { AiFillHeart, AiOutlineHeart } from "react-icons/ai"; 3 | 4 | interface Props { 5 | onClick: () => void; 6 | } 7 | 8 | const Like = ({ onClick }: Props) => { 9 | const [status, setStatus] = useState(false); 10 | 11 | const toggle = () => { 12 | setStatus(!status); 13 | onClick(); 14 | } 15 | 16 | if (status) return ; 17 | return 18 | }; 19 | 20 | export default Like; 21 | -------------------------------------------------------------------------------- /src/components/ListGroup/ListGroup.css: -------------------------------------------------------------------------------- 1 | .list-group { 2 | list-style: none; 3 | padding: 0; 4 | } 5 | 6 | -------------------------------------------------------------------------------- /src/components/ListGroup/ListGroup.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import "./ListGroup.css"; 3 | 4 | interface Props { 5 | items: string[]; 6 | heading: string; 7 | onSelectItem: (item: string) => void; 8 | } 9 | 10 | function ListGroup({ items, heading, onSelectItem }: Props) { 11 | const [selectedIndex, setSelectedIndex] = useState(-1); 12 | 13 | return ( 14 | <> 15 |

{heading}

16 | {items.length === 0 &&

No item found

} 17 |
    18 | {items.map((item, index) => ( 19 |
  • { 27 | setSelectedIndex(index); 28 | onSelectItem(item); 29 | }} 30 | > 31 | {item} 32 |
  • 33 | ))} 34 |
35 | 36 | ); 37 | } 38 | 39 | export default ListGroup; 40 | -------------------------------------------------------------------------------- /src/components/Message.tsx: -------------------------------------------------------------------------------- 1 | 2 | let count = 0; 3 | 4 | const Message = () => { 5 | console.log('Message called', count); 6 | count++; 7 | return
Message {count}
; 8 | }; 9 | 10 | export default Message; 11 | -------------------------------------------------------------------------------- /src/components/NavBar.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | interface Props { 4 | cartItemsCount: number; 5 | } 6 | 7 | const NavBar = ({ cartItemsCount }: Props) => { 8 | return
NavBar: {cartItemsCount}
; 9 | }; 10 | 11 | export default NavBar; 12 | -------------------------------------------------------------------------------- /src/components/ProductList.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | 3 | const ProductList = ({ category }: { category: string }) => { 4 | const [products, setProducts] = useState([]); 5 | 6 | useEffect(() => { 7 | console.log('Fetching products in ', category); 8 | setProducts(['Clothing', 'Household']); 9 | }, [category]); 10 | 11 | return ( 12 |
ProductList
13 | ) 14 | } 15 | 16 | export default ProductList -------------------------------------------------------------------------------- /src/components/UserList.tsx: -------------------------------------------------------------------------------- 1 | import useUsers from "../hooks/useUsers"; 2 | import userService, { User } from "../services/user-service"; 3 | 4 | const UserList = () => { 5 | const { users, error, isLoading, setUsers, setError } = useUsers(); 6 | 7 | const deleteUser = (user: User) => { 8 | const originalUsers = [...users]; 9 | setUsers(users.filter((u) => u.id !== user.id)); 10 | 11 | userService.delete(user.id).catch((err) => { 12 | setError(err.message); 13 | setUsers(originalUsers); 14 | }); 15 | }; 16 | 17 | const addUser = () => { 18 | const originalUsers = [...users]; 19 | const newUser = { id: 0, name: "Mosh" }; 20 | setUsers([newUser, ...users]); 21 | 22 | userService 23 | .create(newUser) 24 | .then(({ data: savedUser }) => setUsers([savedUser, ...users])) 25 | .catch((err) => { 26 | setError(err.message); 27 | setUsers(originalUsers); 28 | }); 29 | }; 30 | 31 | const updateUser = (user: User) => { 32 | const originalUsers = [...users]; 33 | const updatedUser = { ...user, name: user.name + "!" }; 34 | setUsers(users.map((u) => (u.id === user.id ? updatedUser : u))); 35 | 36 | userService.update(updatedUser).catch((err) => { 37 | setError(err.message); 38 | setUsers(originalUsers); 39 | }); 40 | }; 41 | 42 | return ( 43 | <> 44 | {error &&

{error}

} 45 | {isLoading &&
} 46 | 49 |
    50 | {users.map((user) => ( 51 |
  • 55 | {user.name} 56 |
    57 | 63 | 69 |
    70 |
  • 71 | ))} 72 |
73 | 74 | ); 75 | }; 76 | 77 | export default UserList; 78 | -------------------------------------------------------------------------------- /src/expense-tracker/categories.ts: -------------------------------------------------------------------------------- 1 | const categories = ["Groceries", "Utilities", "Entertainment"] as const; 2 | 3 | export default categories; 4 | -------------------------------------------------------------------------------- /src/expense-tracker/components/ExpenseFilter.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import categories from '../categories'; 3 | 4 | interface Props { 5 | onSelectCategory: (category: string) => void; 6 | } 7 | 8 | const ExpenseFilter = ({ onSelectCategory }: Props) => { 9 | return ( 10 | 14 | ) 15 | } 16 | 17 | export default ExpenseFilter -------------------------------------------------------------------------------- /src/expense-tracker/components/ExpenseForm.tsx: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { useForm } from "react-hook-form"; 3 | import { zodResolver } from "@hookform/resolvers/zod"; 4 | import categories from "../categories"; 5 | 6 | const schema = z.object({ 7 | description: z.string().min(3, { message: 'Description should be at least 3 characters.'}).max(50), 8 | amount: z.number({ invalid_type_error: 'Amount is required.'}).min(0.01).max(100_000), 9 | category: z.enum(categories, { 10 | errorMap: () => ({ message: 'Category is required.'}) 11 | }), 12 | }); 13 | 14 | type ExpenseFormData = z.infer; 15 | 16 | interface Props { 17 | onSubmit: (data: ExpenseFormData) => void; 18 | } 19 | 20 | const ExpenseForm = ({ onSubmit }: Props) => { 21 | const { 22 | register, 23 | handleSubmit, 24 | reset, 25 | formState: { errors }, 26 | } = useForm({ resolver: zodResolver(schema) }); 27 | 28 | return ( 29 |
{ 30 | onSubmit(data); 31 | reset(); 32 | })}> 33 |
34 | 37 | 43 | {errors.description && ( 44 |

{errors.description.message}

45 | )} 46 |
47 |
48 | 51 | 57 | {errors.amount && ( 58 |

{errors.amount.message}

59 | )} 60 |
61 |
62 | 65 | 73 | {errors.category && ( 74 |

{errors.category.message}

75 | )} 76 |
77 | 78 |
79 | ); 80 | }; 81 | 82 | export default ExpenseForm; 83 | -------------------------------------------------------------------------------- /src/expense-tracker/components/ExpenseList.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | interface Expense { 4 | id: number; 5 | description: string; 6 | amount: number; 7 | category: string; 8 | } 9 | 10 | interface Props { 11 | expenses: Expense[]; 12 | onDelete: (id: number) => void; 13 | } 14 | 15 | const ExpenseList = ({ expenses, onDelete }: Props) => { 16 | if (expenses.length === 0) return null; 17 | 18 | return ( 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | {expenses.map((expense) => ( 30 | 31 | 32 | 33 | 34 | 42 | 43 | ))} 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 |
DescriptionAmountCategory
{expense.description}${expense.amount.toFixed(2)}{expense.category} 35 | 41 |
Total${expenses.reduce((acc, expense) => expense.amount + acc, 0).toFixed(2)}
54 | ); 55 | }; 56 | 57 | export default ExpenseList; 58 | -------------------------------------------------------------------------------- /src/hooks/useUsers.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { CanceledError } from "../services/api-client"; 3 | import userService, { User } from "../services/user-service"; 4 | 5 | const useUsers = () => { 6 | const [users, setUsers] = useState([]); 7 | const [error, setError] = useState(""); 8 | const [isLoading, setLoading] = useState(false); 9 | 10 | useEffect(() => { 11 | setLoading(true); 12 | const { request, cancel } = userService.getAll(); 13 | request 14 | .then((res) => { 15 | setUsers(res.data); 16 | setLoading(false); 17 | }) 18 | .catch((err) => { 19 | if (err instanceof CanceledError) return; 20 | setError(err.message); 21 | setLoading(false); 22 | }); 23 | 24 | return () => cancel(); 25 | }, []); 26 | 27 | return { users, error, isLoading, setUsers, setError }; 28 | } 29 | 30 | export default useUsers; -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 20px; 3 | } -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import 'bootstrap/dist/css/bootstrap.css' 5 | import './index.css' 6 | 7 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 8 | 9 | 10 | , 11 | ) 12 | -------------------------------------------------------------------------------- /src/services/api-client.ts: -------------------------------------------------------------------------------- 1 | import axios, { CanceledError } from 'axios'; 2 | 3 | export default axios.create({ 4 | baseURL: 'https://jsonplaceholder.typicode.com', 5 | }) 6 | 7 | export { CanceledError }; -------------------------------------------------------------------------------- /src/services/http-service.ts: -------------------------------------------------------------------------------- 1 | import apiClient from "./api-client"; 2 | 3 | interface Entity { 4 | id: number; 5 | } 6 | 7 | class HttpService { 8 | endpoint: string; 9 | 10 | constructor(endpoint: string) { 11 | this.endpoint = endpoint; 12 | } 13 | 14 | getAll() { 15 | const controller = new AbortController(); 16 | const request = apiClient.get(this.endpoint, { 17 | signal: controller.signal, 18 | }); 19 | return { request, cancel: () => controller.abort() }; 20 | } 21 | 22 | delete(id: number) { 23 | return apiClient.delete(this.endpoint + "/" + id); 24 | } 25 | 26 | create(entity: T) { 27 | return apiClient.post(this.endpoint, entity); 28 | } 29 | 30 | update(entity: T) { 31 | return apiClient.patch(this.endpoint + "/" + entity.id, entity); 32 | } 33 | } 34 | 35 | const create = (endpoint: string) => new HttpService(endpoint); 36 | 37 | export default create; 38 | -------------------------------------------------------------------------------- /src/services/user-service.ts: -------------------------------------------------------------------------------- 1 | import create from "./http-service"; 2 | 3 | export interface User { 4 | id: number; 5 | name: string; 6 | } 7 | 8 | export default create('/users'); 9 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 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 | --------------------------------------------------------------------------------