├── .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 |
13 | {cartItems.map(item => - {item}
)}
14 |
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 |
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 |
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 |
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 | Description |
23 | Amount |
24 | Category |
25 | |
26 |
27 |
28 |
29 | {expenses.map((expense) => (
30 |
31 | {expense.description} |
32 | ${expense.amount.toFixed(2)} |
33 | {expense.category} |
34 |
35 |
41 | |
42 |
43 | ))}
44 |
45 |
46 |
47 | Total |
48 | ${expenses.reduce((acc, expense) => expense.amount + acc, 0).toFixed(2)} |
49 | |
50 | |
51 |
52 |
53 |
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 |
--------------------------------------------------------------------------------