diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 00000000..20df948c --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,15 @@ +module.exports = { + env: { browser: true, es2020: true, node: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + ], + parser: '@typescript-eslint/parser', + parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': 'warn', + 'no-console': ['warn'], + }, +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..a547bf36 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..198338e7 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,9 @@ +{ + "printWidth": 80, + "semi": true, + "singleQuote": true, + "trailingComma": "es5", + "bracketSpacing": true, + "endOfLine": "auto", + "tabWidth": 2 +} diff --git a/README.md b/README.md index c81804bf..6a4d8726 100644 --- a/README.md +++ b/README.md @@ -1,1319 +1,392 @@ -# ๐Ÿค ๊ฑฐ๋ž˜ API ํ™œ์šฉ, ํŒ€ ํ”„๋กœ์ ํŠธ +# ๐Ÿค ํŒจ์ŠคํŠธ์บ ํผ์Šค FE5 ์‡ผํ•‘๋ชฐ ํŒ€ํ”„๋กœ์ ํŠธ -์ฃผ์–ด์ง„ API๋ฅผ ๋ถ„์„ํ•ด ์–ด๋–ค ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰/์™„์„ฑํ•  ๊ฒƒ์ธ์ง€ ํŒ€ ๋‹จ์œ„๋กœ ์ž์œ ๋กญ๊ฒŒ ๊ฒฐ์ •ํ•˜๊ณ  ๋งŒ๋“ค์–ด๋ณด์„ธ์š”. -TypeScript๋ฅผ ํ•„์ˆ˜๋กœ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. -๊ณผ์ œ ์ˆ˜ํ–‰ ๋ฐ ๋ฆฌ๋ทฐ ๊ธฐ๊ฐ„์€ ๋ณ„๋„ ๊ณต์ง€๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”! +

+
+ + Hits + +

-## ๊ณผ์ œ ์ˆ˜ํ–‰ ๋ฐ ์ œ์ถœ ๋ฐฉ๋ฒ• +> ๋ณธ ํ”„๋กœ์ ํŠธ๋Š” ํŒจ์ŠคํŠธ์บ ํผ์Šค ๋ถ€ํŠธ์บ ํ”„ ํ”„๋ก ํŠธ์•ค๋“œ 5๊ธฐ, 5์ฐจ ๊ณผ์ œ์ž…๋‹ˆ๋‹ค. +> ์ €ํฌ 1์กฐ๋Š” ์ฃผ์–ด์ง„ API๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์ถ•๊ตฌํ™” ์˜จ๋ผ์ธ ์‡ผํ•‘๋ชฐ์„ ์ œ์ž‘ํ•˜์˜€์Šต๋‹ˆ๋‹ค. +> ์ฐธ๊ณ  ํ•œ ์‚ฌ์ดํŠธ: [ํฌ๋ ˆ์ด์ง€11](https://crazy11.co.kr/)
+> ๊ฐœ๋ฐœ ๊ธฐ๊ฐ„ : 2023. 5. 31 ~ 2023. 6. 21 -``` -KDT๊ธฐ์ˆ˜๋ฒˆํ˜ธ_์ด๋ฆ„ - -E.g, KDT0_ParkYoungWoong -``` - -1. ํ˜„์žฌ ์ €์žฅ์†Œ๋ฅผ ๋กœ์ปฌ์— ํด๋ก (Clone)ํ•ฉ๋‹ˆ๋‹ค. -1. ์ž์‹ ์˜ ๋ณธ๋ช…์œผ๋กœ ๋ธŒ๋žœ์น˜๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.(๊ตฌ๋ถ„ ๊ฐ€๋Šฅํ•˜๋„๋ก ๋ณธ๋ช…์„ ๊ผญ ํŒŒ์Šค์นผ์ผ€์ด์Šค๋กœ ํ‘œ์‹œํ•˜์„ธ์š”, `git branch KDTX_ParkYoungWoong`) -1. ์ž์‹ ์˜ ๋ณธ๋ช… ๋ธŒ๋žœ์น˜์—์„œ ๊ณผ์ œ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. -1. ๊ณผ์ œ ์ˆ˜ํ–‰์ด ์™„๋ฃŒ๋˜๋ฉด, ์ž์‹ ์˜ ๋ณธ๋ช… ๋ธŒ๋žœ์น˜๋ฅผ ์›๊ฒฉ ์ €์žฅ์†Œ์— ํ‘ธ์‹œ(Push)ํ•ฉ๋‹ˆ๋‹ค.(`main` ๋ธŒ๋žœ์น˜์— ํ‘ธ์‹œํ•˜์ง€ ์•Š๋„๋ก ๊ผญ ์ฃผ์˜ํ•˜์„ธ์š”, `git push origin KDTX_ParkYoungWoong`) -1. ์ €์žฅ์†Œ์—์„œ `main` ๋ธŒ๋žœ์น˜๋ฅผ ๋Œ€์ƒ์œผ๋กœ Pull Request ์ƒ์„ฑํ•˜๋ฉด, ๊ณผ์ œ ์ œ์ถœ์ด ์™„๋ฃŒ๋ฉ๋‹ˆ๋‹ค!(E.g, `main` <== `KDTX_ParkYoungWoong`) - -- `main` ํ˜น์€ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์˜ ๋ธŒ๋žœ์น˜๋กœ ์ ˆ๋Œ€ ๋ณ‘ํ•ฉํ•˜์ง€ ์•Š๋„๋ก ์ฃผ์˜ํ•˜์„ธ์š”! -- Pull Request์—์„œ ๋ณด์ด๋Š” ์„ค๋ช…์„ ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์ด ์ดํ•ดํ•˜๊ธฐ ์‰ฝ๋„๋ก ๊ผผ๊ผผํ•˜๊ฒŒ ์ž‘์„ฑํ•˜์„ธ์š”! -- Pull Request์—์„œ ๊ณผ์ œ ์ œ์ถœ ํ›„ ์ ˆ๋Œ€ ๋ณ‘ํ•ฉ(Merge)ํ•˜์ง€ ์•Š๋„๋ก ์ฃผ์˜ํ•˜์„ธ์š”! -- ๊ณผ์ œ ์ˆ˜ํ–‰ ๋ฐ ์ œ์ถœ ๊ณผ์ •์—์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ, ๋ฐ”๋กœ ๋‹ด๋‹น ๋ฉ˜ํ† ๋‚˜ ๊ฐ•์‚ฌ์—์„œ ์–˜๊ธฐํ•˜์„ธ์š”! - -## API ์‚ฌ์šฉ๋ฒ• - -๋ชจ๋“  API ์š”์ฒญ(Request) `headers`์— ์•„๋ž˜ ์ •๋ณด๊ฐ€ ๊ผญ ํฌํ•จ๋ผ์•ผ ํ•ฉ๋‹ˆ๋‹ค! -`username`์€ `KDT5_TeamX`์™€ ๊ฐ™์ด ๋ณธ๋ช… ํ˜น์€ ํŒ€ ์ด๋ฆ„์„ ํฌํ•จํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค! -ํ™•์ธํ•  ์ˆ˜ ์—†๋Š” ์‚ฌ์šฉ์ž๋‚˜ ํŒ€์˜ DB ์ •๋ณด๋Š” ์ž„์˜๋กœ ์‚ญ์ œ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค! - -```json -{ - "content-type": "application/json", - "apikey": "KDT5_nREmPe9B", - "username": "KDT5_TeamX" -} -``` - -
- -## ์ธ์ฆ - -'์ธ์ฆ' ๊ด€๋ จ API๋Š” ๋ชจ๋‘ ์ผ๋ฐ˜ ์‚ฌ์šฉ์ž ์ „์šฉ์ž…๋‹ˆ๋‹ค. - -### ํšŒ์›๊ฐ€์ž… - -์‚ฌ์šฉ์ž๊ฐ€ `username`์— ์ข…์†๋˜์–ด ํšŒ์›๊ฐ€์ž…ํ•ฉ๋‹ˆ๋‹ค. - -- ์‚ฌ์šฉ์ž ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ์•”ํ˜ธํ™”ํ•ด ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.(๊ด€๋ฆฌ์ž๋Š” ํ™•์ธํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค!) -- ํ”„๋กœํ•„ ์ด๋ฏธ์ง€๋Š” 1MB ์ดํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. - -```curl -curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/auth/signup - \ -X 'POST' -``` - -์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -interface RequestBody { - email: string // ์‚ฌ์šฉ์ž ์•„์ด๋”” (ํ•„์ˆ˜!) - password: string // ์‚ฌ์šฉ์ž ๋น„๋ฐ€๋ฒˆํ˜ธ, 8์ž ์ด์ƒ (ํ•„์ˆ˜!) - displayName: string // ์‚ฌ์šฉ์ž ์ด๋ฆ„, 20์ž ์ดํ•˜ (ํ•„์ˆ˜!) - profileImgBase64?: string // ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์ด๋ฏธ์ง€(base64) - jpg, jpeg, webp, png, gif, svg -} -``` - -```json -{ - "email": "thesecon@gmail.com", - "password": "********", - "displayName": "ParkYoungWoong", - "profileImgBase64": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAf...(์ƒ๋žต)" -} -``` - -์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -interface ResponseValue { - user: { // ํšŒ์›๊ฐ€์ž…ํ•œ ์‚ฌ์šฉ์ž ์ •๋ณด - email: string // ์‚ฌ์šฉ์ž ์•„์ด๋”” - displayName: string // ์‚ฌ์šฉ์ž ํ‘œ์‹œ ์ด๋ฆ„ - profileImg: string | null // ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์ด๋ฏธ์ง€(URL) - } - accessToken: string // ์‚ฌ์šฉ์ž ์ ‘๊ทผ ํ† ํฐ -} -``` - -```json -{ - "user": { - "email": "thesecon@gmail.com", - "displayName": "ParkYoungWoong", - "profileImg": "https://storage.googleapis.com/heropy-api/vjbtIrh5dGv163442.png" - }, - "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IlM3WDhpQ...(์ƒ๋žต)" -} -``` - -### ๋กœ๊ทธ์ธ - -- ๋ฐœ๊ธ‰๋œ `accessToken`์€ 24์‹œ๊ฐ„ ํ›„ ๋งŒ๋ฃŒ๋ฉ๋‹ˆ๋‹ค.(๋งŒ๋ฃŒ ํ›„ ๋‹ค์‹œ ๋กœ๊ทธ์ธ ํ•„์š”) - -```curl -curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/auth/login - \ -X 'POST' -``` - -์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -interface RequestBody { - email: string // ์‚ฌ์šฉ์ž ์•„์ด๋”” (ํ•„์ˆ˜!) - password: string // ์‚ฌ์šฉ์ž ๋น„๋ฐ€๋ฒˆํ˜ธ (ํ•„์ˆ˜!) -} -``` - -```json -{ - "email": "thesecon@gmail.com", - "password": "********" -} -``` - -์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -interface ResponseValue { - user: { // ํšŒ์›๊ฐ€์ž…ํ•œ ์‚ฌ์šฉ์ž ์ •๋ณด - email: string // ์‚ฌ์šฉ์ž ์•„์ด๋”” - displayName: string // ์‚ฌ์šฉ์ž ํ‘œ์‹œ ์ด๋ฆ„ - profileImg: string | null // ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์ด๋ฏธ์ง€(URL) - } - accessToken: string // ์‚ฌ์šฉ์ž ์ ‘๊ทผ ํ† ํฐ -} -``` - -```json -{ - "user": { - "email": "thesecon@gmail.com", - "displayName": "ParkYoungWoong", - "profileImg": "https://storage.googleapis.com/heropy-api/vAKjlJ-Gx5v163442.png" - }, - "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjlQS3I...(์ƒ๋žต)" -} -``` - -### ์ธ์ฆ ํ™•์ธ - -```curl -curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/auth/me - \ -X 'POST' - \ -H 'Authorization: Bearer ' -``` - -์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -- ์—†์Œ - -์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -interface ResponseValue { - email: string // ์‚ฌ์šฉ์ž ์•„์ด๋”” - displayName: string // ์‚ฌ์šฉ์ž ํ‘œ์‹œ ์ด๋ฆ„ - profileImg: string | null // ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์ด๋ฏธ์ง€(URL) -} -``` - -```json -{ - "email": "thesecon@gmail.com", - "displayName": "ParkYoungWoong", - "profileImg": "https://storage.googleapis.com/heropy-api/vAKjlJ-Gx5v163442.png" -} -``` - -### ๋กœ๊ทธ์•„์›ƒ - -```curl -curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/auth/logout - \ -X 'POST' - \ -H 'Authorization: Bearer ' -``` - -์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -- ์—†์Œ - -์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -type ResponseValue = true // ๋กœ๊ทธ์•„์›ƒ ์ฒ˜๋ฆฌ ์ƒํƒœ -``` - -### ์‚ฌ์šฉ์ž ์ •๋ณด ์ˆ˜์ • - -```curl -curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/auth/user - \ -X 'PUT' - \ -H 'Authorization: Bearer ' -``` - -์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -interface RequestBody { - displayName?: string // ์ƒˆ๋กœ์šด ํ‘œ์‹œ ์ด๋ฆ„ - profileImgBase64?: string // ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์ด๋ฏธ์ง€(base64) - jpg, jpeg, webp, png, gif, svg - oldPassword?: string // ๊ธฐ์กด ๋น„๋ฐ€๋ฒˆํ˜ธ - newPassword?: string // ์ƒˆ๋กœ์šด ๋น„๋ฐ€๋ฒˆํ˜ธ -} -``` - -```json -{ - "oldPassword": "********", - "newPassword": "**********" -} -``` - -์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -interface ResponseValue { - email: string // ์‚ฌ์šฉ์ž ์•„์ด๋”” - displayName: string // ์‚ฌ์šฉ์ž ํ‘œ์‹œ ์ด๋ฆ„ - profileImg: string | null // ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์ด๋ฏธ์ง€(URL) -} -``` - -```json -{ - "email": "thesecon@gmail.com", - "displayName": "ParkYoungWoong", - "profileImg": "https://storage.googleapis.com/heropy-api/vAKjlJ-Gx5v163442.png" -} -``` +
-### ์‚ฌ์šฉ์ž ๋ชฉ๋ก ์กฐํšŒ +# ๋ฐฐํฌ์ฃผ์†Œ -- ๊ด€๋ฆฌ์ž ์ „์šฉ API์ž…๋‹ˆ๋‹ค. +https://kdt-5-m5-crazy11.vercel.app +

-```curl -curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/auth/users - \ -X 'GET' - \ -H 'masterKey: true' -``` - -์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: +# ๊ฐœ๋ฐœํŒ€ ์†Œ๊ฐœ -- ์—†์Œ +| ํŒ€์› | ์ •์Šน์› | ๋ฐ•ํ˜„์ค€ | ์ตœ์šฉ์ค€ | ํ™ฉ์ธ์Šน | ์ด์ •์šฐ | +| :----: | :-------------------------------------: | :----------------------------------------------: | :------------------------------------------: | :----------------------------------------------: | :----------------------------------------: | +| ๊นƒํ—ˆ๋ธŒ | [@Tteum00](https://github.com/Tteum00) | [@HyunJunPark0](https://github.com/HyunJunPark0) | [@PelicanStd](https://github.com/PelicanStd) | [@hwanginseung](https://github.com/hwanginseung) | [@howooking](https://github.com/howooking) | +| ๋‹ด๋‹น | ํšŒ์›์ •๋ณด
์ƒํ’ˆ ์ƒ์„ธํŽ˜์ด์ง€
๊ตฌ๋งคํ™•์ • | ๊ฐœ์ธ์ •๋ณด ์ˆ˜์ •
๊ตฌ๋งค๋‚ด์—ญ
๊ตฌ๋งค์ทจ์†Œ | ์ƒํ’ˆ ๊ด€๋ฆฌ
์ƒํ’ˆ ์ถ”๊ฐ€
์ƒํ’ˆ ์ˆ˜์ • | ๊ณ„์ขŒ
๊ฑฐ๋ž˜๋‚ด์—ญ
์ƒํ’ˆ ๊ฒ€์ƒ‰ | ์ธ์ฆ / ์ธ๊ฐ€
์ƒํ’ˆ ๋ฐฐ์น˜
์Šคํƒ€์ผ๋ง | -์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: +
-```ts -type ResponseValue = User[] +# ์‹œ์ž‘ ๊ฐ€์ด๋“œ -interface User { - email: string // ์‚ฌ์šฉ์ž ์•„์ด๋”” - displayName: string // ์‚ฌ์šฉ์ž ํ‘œ์‹œ ์ด๋ฆ„ - profileImg: string // ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์ด๋ฏธ์ง€ URL -} -``` +## Installation -```json -[ - { - "email": "thesecon@gmail.com", - "displayName": "HEROPY", - "profileImg": null - }, - { - "email": "neo@zillinks.com", - "displayName": "๋ฐ•์˜์›…", - "profileImg": "https://storage.googleapis.com/heropy-api/Z_una7lyijv074804.png" - }, - { - "email": "test@test.com", - "displayName": "๊ด€๋ฆฌ์ž", - "profileImg": "https://storage.googleapis.com/heropy-api/ZXcXjwsB7nv121507.png" - } -] +```cli +$ git clone https://github.com/howooking/KDT5-M5 +$ cd KDT5-M5 +$ npm install +$ npm run dev ``` -
- -## ๊ณ„์ขŒ - -'๊ณ„์ขŒ' ๊ด€๋ จ API๋Š” ๋ชจ๋‘ ์ผ๋ฐ˜ ์‚ฌ์šฉ์ž ์ „์šฉ์ž…๋‹ˆ๋‹ค. - -### ์„ ํƒ ๊ฐ€๋Šฅํ•œ ์€ํ–‰ ๋ชฉ๋ก ์กฐํšŒ - -- ์€ํ–‰ ๋‹น ํ•˜๋‚˜์˜ ๊ณ„์ขŒ๋งŒ ํ—ˆ์šฉ๋ฉ๋‹ˆ๋‹ค. -- ์‚ฌ์šฉ์ž๊ฐ€ ๊ณ„์ขŒ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด, ํ•ด๋‹น ์€ํ–‰ ์ •๋ณด `disabled` ์†์„ฑ์ด `true`๋กœ ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค. -- ์€ํ–‰ ์ •๋ณด `digits` ์†์„ฑ์˜ ์ˆซ์ž๋ฅผ ๋ชจ๋‘ ๋”ํ•˜๋ฉด ๊ฐ ์€ํ–‰์˜ ์œ ํšจํ•œ ๊ณ„์ขŒ๋ฒˆํ˜ธ ๊ธธ์ด๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. -- `[3, 2, 4, 3]` => 123-12-1234-123 - -```curl -curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/account/banks - \ -X 'GET' - \ -H 'Authorization: Bearer ' -``` +> ๋ฐฑ์•ค๋“œ ์„œ๋ฒ„ ์‹คํ–‰์€ ๋ถˆํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. -์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: +
-- ์—†์Œ +# ์‚ฌ์šฉํ•œ ๊ธฐ์ˆ , ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ -์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: +## Environment -```ts -type ResponseValue = Bank[] // ์„ ํƒ ๊ฐ€๋Šฅํ•œ ์€ํ–‰ ์ •๋ณด ๋ชฉ๋ก +
+
+
-interface Bank { // ์„ ํƒ ๊ฐ€๋Šฅํ•œ ์€ํ–‰ ์ •๋ณด - name: string // ์€ํ–‰ ์ด๋ฆ„ - code: string // ์€ํ–‰ ์ฝ”๋“œ - digits: number[] // ์€ํ–‰ ๊ณ„์ขŒ ์ž๋ฆฟ์ˆ˜ - disabled: boolean // ์‚ฌ์šฉ์ž๊ฐ€ ์ถ”๊ฐ€ํ•œ ๊ณ„์ขŒ ์—ฌ๋ถ€ -} -``` +## Config -```json -[ - { - "name": "KB๊ตญ๋ฏผ์€ํ–‰", - "code": "004", - "digits": [3, 2, 4, 3], - "disabled": false - }, - { - "name": "์‹ ํ•œ์€ํ–‰", - "code": "088", - "digits": [3, 3, 6], - "disabled": true - }, - { - "name": "์šฐ๋ฆฌ์€ํ–‰", - "code": "020", - "digits": [4, 3, 6], - "disabled": true - }, - { - "name": "ํ•˜๋‚˜์€ํ–‰", - "code": "081", - "digits": [3, 6, 5], - "disabled": false - }, - { - "name": "์ผ€์ด๋ฑ…ํฌ", - "code": "089", - "digits": [3, 3, 6], - "disabled": false - }, - { - "name": "์นด์นด์˜ค๋ฑ…ํฌ", - "code": "090", - "digits": [4, 2, 7], - "disabled": false - }, - { - "name": "NH๋†ํ˜‘์€ํ–‰", - "code": "011", - "digits": [3, 4, 4, 2], - "disabled": false - } -] -``` +
+
-### ๊ณ„์ขŒ ๋ชฉ๋ก ๋ฐ ์ž”์•ก ์กฐํšŒ +## Development -- ๊ณ„์ขŒ๋ฒˆํ˜ธ๋Š” ์ผ๋ถ€๋งŒ ๋…ธ์ถœ๋ฉ๋‹ˆ๋‹ค. E.g. `"123-XXXX-XXXX-XX"` -- ์ž”์•ก์˜ ๋‹จ์œ„๋Š” '์›ํ™”(๏ฟฆ)'์ž…๋‹ˆ๋‹ค. +
+
+
+
+ : ์ „์—ญ ์ƒํƒœ๊ด€๋ฆฌ
+ : ํŒ์—… ์•ˆ๋‚ด ๋ฉ”์‹œ์ง€
+ : ์ด๋ฏธ์ง€ ์Šฌ๋ผ์ด๋”
-```curl -curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/account - \ -X 'GET' - \ -H 'Authorization: Bearer ' -``` +# ํ™”๋ฉด ๊ตฌ์„ฑ -์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: +| ๋ฉ”์ธํŽ˜์ด์ง€ | ๋ชจ๋“ ์ œํ’ˆ | +| :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| | | +| ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ์ƒํ’ˆ | ์ƒํ’ˆ ๊ฒ€์ƒ‰ | +| | | +| ์—ฐ๊ด€ ์ƒํ’ˆ ์ถ”์ฒœ | ์ƒํ’ˆ ์ƒ์„ธ ํŽ˜์ด์ง€ | +| | | +| ํšŒ์› ์ •๋ณด | ์ƒํ’ˆ ๊ด€๋ฆฌ | +| | | +| ์ƒํ’ˆ ์ถ”๊ฐ€ | ์ƒํ’ˆ ์ˆ˜์ • | +| | | +| ๊ฑฐ๋ž˜ ๋‚ด์—ญ | ๋‚ด ์ •๋ณด | +| | | +| ๊ณ„์ขŒ ์กฐํšŒ / ํ•ด์ง€ | ๊ณ„์ขŒ ์—ฐ๊ฒฐ | +| | | +| ๊ตฌ๋งค ๋‚ด์—ญ | ๋กœ๋”ฉํ™”๋ฉด | +| | | +| ๋กœ๊ทธ์ธ | ํšŒ์›๊ฐ€์ž… | +| | | -- ์—†์Œ +# ๊ณ ์ฐฐ, ๋Š๋‚€์  -์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: +- ์ƒํƒœ๊ด€๋ฆฌ ํˆด -```ts -interface ResponseValue { - totalBalance: number // ์‚ฌ์šฉ์ž ๊ณ„์ขŒ ์ž”์•ก ์ดํ•ฉ - accounts: Bank[] // ์‚ฌ์šฉ์ž ๊ณ„์ขŒ ์ •๋ณด ๋ชฉ๋ก -} + - ํŒ€์› ๋‚ด ์ž…๋ฌธ์ž๋ฅผ ๋ฐฐ๋ คํ•˜์—ฌ ์ƒ๋Œ€์ ์œผ๋กœ ์‚ฌ์šฉ์ด ์‰ฌ์šด [ZUSTAND](https://zustand-demo.pmnd.rs/)๋ฅผ ์‚ฌ์šฉ + - context wrappingํ•˜๋Š” ๊ณผ์ •์ด ํ•„์š”ํ•˜์ง€ ์•Š์Œ + - [src/store.ts](https://github.com/howooking/KDT5-M5/blob/00c7b69fb881e9e7c8cf781f5b96cfd1c15e7b6b/src/store.ts#L1C1-L69) -interface Bank { // ์‚ฌ์šฉ์ž ๊ณ„์ขŒ ์ •๋ณด - id: string // ๊ณ„์ขŒ ID - bankName: string // ์€ํ–‰ ์ด๋ฆ„ - bankCode: string // ์€ํ–‰ ์ฝ”๋“œ - accountNumber: string // ๊ณ„์ขŒ ๋ฒˆํ˜ธ - balance: number // ๊ณ„์ขŒ ์ž”์•ก -} -``` + ```js + import { create } from 'zustand'; + import { authenticate } from '@/api/authApi'; + import { ADMINS } from '@/constants/constants'; -```json -{ - "totalBalance": 5999900, - "accounts": [ - { - "id": "jQMfKla8vOIFELA3mAXv", - "bankName": "NH๋†ํ˜‘์€ํ–‰", - "bankCode": "011", - "accountNumber": "356-XXXX-XXXX-XX", - "balance": 2999900 - }, - { - "id": "wiPgsXvMAmcLw8AuRHIi", - "bankName": "KB๊ตญ๋ฏผ์€ํ–‰", - "bankCode": "004", - "accountNumber": "123-XX-XXXX-XXX", - "balance": 3000000 + interface UserState { + userInfo: LocalUser | null; + setUser: (user: LocalUser | null) => void; + authMe: () => Promise; } - ] -} -``` - -### ๊ณ„์ขŒ ์—ฐ๊ฒฐ - -- ์—ฐ๊ฒฐ๋œ ๊ณ„์ขŒ ์ž”์•ก์—๋Š” ์ž๋™์œผ๋กœ ๊ธฐ๋ณธ '3๋ฐฑ๋งŒ์›'์ด ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค. -- ์š”์ฒญํ•˜๋Š” ๊ณ„์ขŒ๋ฒˆํ˜ธ์™€ ์ „ํ™”๋ฒˆํ˜ธ์—๋Š” `-` ๊ตฌ๋ถ„์ด ์—†์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. - -```curl -curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/account - \ -X 'POST' - \ -H 'Authorization: Bearer ' -``` - -์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -interface RequestBody { - bankCode: string // ์—ฐ๊ฒฐํ•  ์€ํ–‰ ์ฝ”๋“œ (ํ•„์ˆ˜!) - accountNumber: string // ์—ฐ๊ฒฐํ•  ๊ณ„์ขŒ๋ฒˆํ˜ธ (ํ•„์ˆ˜!) - phoneNumber: string // ์‚ฌ์šฉ์ž ์ „ํ™”๋ฒˆํ˜ธ (ํ•„์ˆ˜!) - signature: boolean // ์‚ฌ์šฉ์ž ์„œ๋ช… (ํ•„์ˆ˜!) -} -``` - -```json -{ - "bankCode": "088", - "accountNumber": "123456789012", - "phoneNumber": "01012345678", - "signature": true -} -``` - -์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -interface ResponseValue { // ์—ฐ๊ฒฐ๋œ ๊ณ„์ขŒ ์ •๋ณด - id: string // ๊ณ„์ขŒ ID - bankName: string // ์€ํ–‰ ์ด๋ฆ„ - bankCode: string // ์€ํ–‰ ์ฝ”๋“œ - accountNumber: string // ๊ณ„์ขŒ ๋ฒˆํ˜ธ - balance: number // ๊ณ„์ขŒ ์ž”์•ก -} -``` - -```json -{ - "id": "1qRFC6Ey5VkSu6nyj5Ba", - "bankName": "์‹ ํ•œ์€ํ–‰", - "bankCode": "088", - "accountNumber": "123-XXX-XXXXXX", - "balance": 3000000 -} -``` - -### ๊ณ„์ขŒ ํ•ด์ง€ - -- ํ•ด์ง€ํ•œ ๊ณ„์ขŒ๋Š” ๋‹ค์‹œ ์—ฐ๊ฒฐํ•ด๋„ ์ž”์•ก์ด ๋ฐ˜์˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.(๊ธฐ๋ณธ ๊ธˆ์•ก์œผ๋กœ ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค) - -```curl -curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/account - \ -X 'DELETE' - \ -H 'Authorization: Bearer ' -``` - -์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -interface RequestBody { - accountId: string // ๊ณ„์ขŒ ID (ํ•„์ˆ˜!) - signature: boolean // ์‚ฌ์šฉ์ž ์„œ๋ช… (ํ•„์ˆ˜!) -} -``` - -```json -{ - "accountId": "jQMfKla8vOIFELA3mAXv", - "signature": true -} -``` - -์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -type ResponseValue = true // ๊ณ„์ขŒ ํ•ด์ง€ ์ฒ˜๋ฆฌ ์ƒํƒœ -``` - -
- -## ์ œํ’ˆ - -'์ œํ’ˆ' ๊ด€๋ จ API๋Š” ๊ด€๋ฆฌ์ž ์ „์šฉ๊ณผ ์ผ๋ฐ˜ ์‚ฌ์šฉ์ž ์ „์šฉ์œผ๋กœ ๊ตฌ๋ถ„๋ฉ๋‹ˆ๋‹ค.
-๊ณต์šฉ API๋„ ์žˆ์œผ๋‹ˆ ์ฃผ์˜ํ•˜์„ธ์š”! - -### ๋ชจ๋“  ์ œํ’ˆ ์กฐํšŒ - -- ๊ด€๋ฆฌ์ž ์ „์šฉ API์ž…๋‹ˆ๋‹ค. -- ์ƒ์„ธ ์ •๋ณด๊ฐ€ ์•„๋‹Œ ๊ธฐ๋ณธ ์ •๋ณด์˜ ์ œํ’ˆ ์„ค๋ช…์€ 100์ž๊นŒ์ง€๋งŒ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. -- ์ƒ์„ธ ์ •๋ณด๊ฐ€ ์•„๋‹Œ ๊ธฐ๋ณธ ์ •๋ณด์˜ ์ œํ’ˆ ์ƒ์„ธ ์‚ฌ์ง„์€ ํฌํ•จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. -- ์ œํ’ˆ ํ• ์ธ์œจ(`discountRate`)์€ ์ œํ’ˆ ๊ฐ€๊ฒฉ๊ณผ ์ง์ ‘ ๊ด€๊ณ„๊ฐ€ ์—†๋Š” ๋‹จ์ˆœ ๋ฉ”๋ชจ ์†์„ฑ์ž…๋‹ˆ๋‹ค. -- ์ œํ’ˆ ํ• ์ธ์œจ์ด ์—†๋Š” ๊ฒฝ์šฐ, `0`์œผ๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. - -```curl -curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/products - \ -X 'GET' - \ -H 'masterKey: true' -``` - -์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -- ์—†์Œ - -์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -type ResponseValue = Product[] // ๊ด€๋ฆฌํ•˜๋Š” ๋ชจ๋“  ์ œํ’ˆ์˜ ๋ชฉ๋ก - -interface Product { // ์ œํ’ˆ ์ •๋ณด - id: string // ์ œํ’ˆ ID - title: string // ์ œํ’ˆ ์ด๋ฆ„ - price: number // ์ œํ’ˆ ๊ฐ€๊ฒฉ - description: string // ์ œํ’ˆ ์„ค๋ช…(์ตœ๋Œ€ 100์ž) - tags: string[] // ์ œํ’ˆ ํƒœ๊ทธ - thumbnail: string | null // ์ œํ’ˆ ์ธ๋„ค์ผ ์ด๋ฏธ์ง€(URL) - isSoldOut: boolean // ์ œํ’ˆ ๋งค์ง„ ์—ฌ๋ถ€ - discountRate: number // ์ œํ’ˆ ํ• ์ธ์œจ -} -``` - -```json -[ - { - "id": "cFmeC7aY5KjZbBAdJE9y", - "title": "์‚ผ์„ฑ์ „์ž ์Šค๋งˆํŠธ๋ชจ๋‹ˆํ„ฐ M7 S43AM700", - "price": 639000, - "description": "107.9cm(43์ธ์น˜) / ์™€์ด๋“œ(16:9) / ํ‰๋ฉด / VA / 3840 x 2160(4K UHD) / ํ”ฝ์…€ํ”ผ์น˜: 0.2451mm / 8ms(GTG) / 300cd / 5,00", - "tags": [ - "๊ฐ€์ „", - "๋ชจ๋‹ˆํ„ฐ", - "์ปดํ“จํ„ฐ" - ], - "thumbnail": "https://storage.googleapis.com/heropy-api/vBAK4MQdH5v195712.png", - "isSoldOut": false, - "discountRate": 20 - }, - { - "id": "nbqtQvEivYwEXTDet7YM", - "title": "MacBook Pro 16", - "price": 3360000, - "description": "์—ญ๋Œ€ ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ MacBook Pro๊ฐ€ ๋“ฑ์žฅํ–ˆ์Šต๋‹ˆ๋‹ค. ์ตœ์ดˆ์˜ ํ”„๋กœ์šฉ Apple Silicon์ธ M1 Pro ๋˜๋Š” M1 Max ์นฉ์„ ํƒ‘์žฌํ•ด ์œ์‚ด๊ฐ™์ด ๋น ๋ฅธ ์†๋„๋Š” ๋ฌผ๋ก , ํš๊ธฐ์ ์ธ ์„ฑ", - "tags": [ - "๊ฐ€์ „", - "๋…ธํŠธ๋ถ", - "์ปดํ“จํ„ฐ" - ], - "thumbnail": "https://storage.googleapis.com/heropy-api/vIKMk_jy4Yv195256.png", - "isSoldOut": false, - "discountRate": 0 - } -] -``` - -### ์ „์ฒด ๊ฑฐ๋ž˜(ํŒ๋งค) ๋‚ด์—ญ - -- ๊ด€๋ฆฌ์ž ์ „์šฉ API์ž…๋‹ˆ๋‹ค. - -```curl -curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/products/transactions/all - \ -X 'GET' - \ -H 'masterKey: true' -``` - -์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -- ์—†์Œ - -์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -type RequestValue = TransactionDetail[] // ๋ชจ๋“  ๊ฑฐ๋ž˜ ๋‚ด์—ญ์˜ ๋ชฉ๋ก -interface TransactionDetail { // ๊ฑฐ๋ž˜ ๋‚ด์—ญ ์ •๋ณด - detailId: string // ๊ฑฐ๋ž˜ ๋‚ด์—ญ ID - user: { // ๊ฑฐ๋ž˜ํ•œ ์‚ฌ์šฉ์ž ์ •๋ณด - email: string - displayName: string - profileImg: string | null - } - account: { // ๊ฑฐ๋ž˜ํ•œ ์‚ฌ์šฉ์ž์˜ ๊ณ„์ขŒ ์ •๋ณด - bankName: string - bankCode: string - accountNumber: string - } - product: { // ๊ฑฐ๋ž˜ํ•œ ์ œํ’ˆ ์ •๋ณด - productId: string - title: string - price: number - description: string - tags: string[] - thumbnail: string | null - discountRate: number - } - reservation: Reservation | null // ๊ฑฐ๋ž˜ํ•œ ์ œํ’ˆ์˜ ์˜ˆ์•ฝ ์ •๋ณด - timePaid: string // ์ œํ’ˆ์„ ๊ฑฐ๋ž˜ํ•œ ์‹œ๊ฐ„ - isCanceled: boolean // ๊ฑฐ๋ž˜ ์ทจ์†Œ ์—ฌ๋ถ€ - done: boolean // ๊ฑฐ๋ž˜ ์™„๋ฃŒ ์—ฌ๋ถ€ -} - -interface Reservation { - start: string // ์˜ˆ์•ฝ ์‹œ์ž‘ ์‹œ๊ฐ„ - end: string // ์˜ˆ์•ฝ ์ข…๋ฃŒ ์‹œ๊ฐ„ - isCanceled: boolean // ์˜ˆ์•ฝ ์ทจ์†Œ ์—ฌ๋ถ€ - isExpired: boolean // ์˜ˆ์•ฝ ๋งŒ๋ฃŒ ์—ฌ๋ถ€ -} -``` - -```json -[ - { - "detailId": "dMhfxyrAupQP18OYmywy", - "user": { - "email": "thesecon@gmail.com", - "displayName": "ParkYoungWoong", - "profileImg": "https://storage.googleapis.com/heropy-api/vsLRqTlPO5v200111.png" - }, - "account": { - "bankName": "KB๊ตญ๋ฏผ์€ํ–‰", - "bankCode": "004", - "accountNumber": "123-XX-XXXX-XXX" - }, - "product": { - "productId": "cFmeC7aY5KjZbBAdJE9y", - "title": "์‚ผ์„ฑ์ „์ž ์Šค๋งˆํŠธ๋ชจ๋‹ˆํ„ฐ M7 S43AM700", - "price": 639000, - "description": "107.9cm(43์ธ์น˜) / ์™€์ด๋“œ(16:9) / ํ‰๋ฉด / VA / 3840 x 2160(4K UHD) / ํ”ฝ์…€ํ”ผ์น˜: 0.2451mm / 8ms(GTG) / 300cd / 5,00", - "tags": [ - "๊ฐ€์ „", - "๋ชจ๋‹ˆํ„ฐ", - "์ปดํ“จํ„ฐ" - ], - "thumbnail": "https://storage.googleapis.com/heropy-api/vBAK4MQdH5v195712.png", - "discountRate": 0 - }, - "reservation": null, - "timePaid": "2021-11-07T20:01:49.100Z", - "isCanceled": false, - "done": false - } -] -``` - -์˜ˆ์•ฝ ์ •๋ณด(`reservation`)๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ: - -```json -[ - { - "reservation": { - "start": "2021-11-12T06:00:00.000Z", - "end": "2021-11-12T07:00:00.000Z", - "isCanceled": false, - "isExpired": true + export const userStore = create((set) => ({ + userInfo: localStorage.getItem('user') + ? JSON.parse(localStorage.getItem('user') as string) + : null, + + setUser: (user: LocalUser | null) => + set({ + userInfo: user, + }), + + authMe: async () => { + const userInfo: LocalUser | null = localStorage.getItem('user') + ? JSON.parse(localStorage.getItem('user') as string) + : null; + if (!userInfo) { + set({ + userInfo: null, + }); + return '๋กœ๊ทธ์ธ์„ ํ•ด์ฃผ์„ธ์š”.'; + } + const res = await authenticate(userInfo.accessToken); + if (res.statusCode === 200) { + const user = res.data as AuthenticateResponseValue; + const isAdmin = ADMINS.includes(user.email); + set({ + userInfo: { + user: user, + accessToken: userInfo.accessToken, + isAdmin, + }, + }); + localStorage.setItem( + 'user', + JSON.stringify({ user, accessToken: userInfo.accessToken, isAdmin }) + ); + return; + } + set({ + userInfo: null, + }); + localStorage.removeItem('user'); + return '๋กœ๊ทธ์ธ ํ•˜์‹ ์ง€ 24์‹œ๊ฐ„์ด ์ง€๋‚˜์…จ์–ด์š”! ๋‹ค์‹œ ๋กœ๊ทธ์ธํ•ด์ฃผ์„ธ์š”.'; + }, + })); + + + (ํ•„์š”ํ•œ ๊ณณ์—์„œ ์‚ฌ์šฉ) + import { userStore } from '@/store'; + const { userInfo, setUser, authMe } = userStore(); + ``` + +- ๊ด€๋ฆฌ์ž ํ™•์ธ + + - ๋กœ๊ทธ์ธ ์‹œ ์„œ๋ฒ„๋กœ ๋ถ€ํ„ฐ ๋ฐ›๋Š” ๋ฐ์ดํ„ฐ๋Š” ์•„๋ž˜์™€ ๊ฐ™์œผ๋ฉฐ ํ•ด๋‹น ์ •๋ณด๋กœ๋Š” ๊ด€๋ฆฌ์ž ์—ฌ๋ถ€๋ฅผ ์•Œ ์ˆ˜ ์—†๋‹ค. + + ```ts + interface ResponseValue { + user: { + email: string; + displayName: string; + profileImg: string | null; + }; + accessToken: string; } - } -] -``` - -### ๊ฑฐ๋ž˜(ํŒ๋งค) ๋‚ด์—ญ ์™„๋ฃŒ/์ทจ์†Œ ๋ฐ ํ•ด์ œ - -- ๊ด€๋ฆฌ์ž ์ „์šฉ API์ž…๋‹ˆ๋‹ค. -- ๊ฑฐ๋ž˜ ๋‚ด์—ญ์„ ์ทจ์†Œํ•˜๋ฉด, ์˜ˆ์•ฝ๋„ ๊ฐ™์ด ์ทจ์†Œ๋ฉ๋‹ˆ๋‹ค. -- ๊ฑฐ๋ž˜ ๋‚ด์—ญ์„ ์ทจ์†Œ ํ•ด์ œํ•˜๋ฉด, ์˜ˆ์•ฝ๋„ ๊ฐ™์ด ์ทจ์†Œ๊ฐ€ ํ•ด์ œ๋ฉ๋‹ˆ๋‹ค. - -```curl -curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/products/transactions/:detailId - \ -X 'PUT' - \ -H 'masterKey: true' -``` - -์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -interface RequestBody { - isCanceled?: boolean // ๊ฑฐ๋ž˜ ์ทจ์†Œ ์—ฌ๋ถ€ (์‚ฌ์šฉ์ž์˜ '์ œํ’ˆ ๊ฑฐ๋ž˜(๊ตฌ๋งค) ์ทจ์†Œ' ์ƒํƒœ์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค) - done?: boolean // ๊ฑฐ๋ž˜ ์™„๋ฃŒ ์—ฌ๋ถ€ (์‚ฌ์šฉ์ž์˜ '์ œํ’ˆ ๊ฑฐ๋ž˜(๊ตฌ๋งค) ํ™•์ •' ์ƒํƒœ์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค) -} -``` - -```json -{ - "isCanceled": true -} -``` - -์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - - -```ts -type ResponseValue = true // ๊ฑฐ๋ž˜ ๋‚ด์—ญ ์™„๋ฃŒ/์ทจ์†Œ ๋ฐ ํ•ด์ œ ์ฒ˜๋ฆฌ ์ƒํƒœ -``` - -### ์ œํ’ˆ ์ถ”๊ฐ€ - -- ๊ด€๋ฆฌ์ž ์ „์šฉ API์ž…๋‹ˆ๋‹ค. -- ํŒŒ์ผ(์‚ฌ์ง„)์€ Base64๋กœ ์š”์ฒญํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. -- ์ œํ’ˆ ์ธ๋„ค์ผ ์‚ฌ์ง„์€ 1MB ์ดํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. -- ์ œํ’ˆ ์ƒ์„ธ ์‚ฌ์ง„์€ 4MB ์ดํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. -- ์ œํ’ˆ ํ• ์ธ์œจ(`discountRate`)์€ ์ œํ’ˆ ๊ฐ€๊ฒฉ๊ณผ ์ง์ ‘ ๊ด€๊ณ„๊ฐ€ ์—†๋Š” ๋‹จ์ˆœ ๋ฉ”๋ชจ ์†์„ฑ์ž…๋‹ˆ๋‹ค. -- ์ œํ’ˆ ํ• ์ธ์œจ์€ `0`~`99` ์‚ฌ์ด ์ˆซ์ž๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”. ๋งŒ์•ฝ ํ• ์ธ์œจ์ด '20%'์ธ ๊ฒฝ์šฐ, `20`์œผ๋กœ ์ž…๋ ฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. -- ์ œํ’ˆ ํ• ์ธ์œจ์„ ์ž…๋ ฅํ•˜์ง€ ์•Š์œผ๋ฉด, `0`์œผ๋กœ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. - -```js -// ํ• ์ธ ์ „ ๊ฐ€๊ฒฉ์„ ๊ณ„์‚ฐ! -const priceBeforeDiscount = price * 100 / (100 - discountRate) -``` - -```curl -curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/products - \ -X 'POST' - \ -H 'masterKey: true' -``` - -์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -interface RequestBody { - title: string // ์ œํ’ˆ ์ด๋ฆ„ (ํ•„์ˆ˜!) - price: number // ์ œํ’ˆ ๊ฐ€๊ฒฉ (ํ•„์ˆ˜!) - description: string // ์ œํ’ˆ ์ƒ์„ธ ์„ค๋ช… (ํ•„์ˆ˜!) - tags?: string[] // ์ œํ’ˆ ํƒœ๊ทธ - thumbnailBase64?: string // ์ œํ’ˆ ์ธ๋„ค์ผ(๋Œ€ํ‘œ) ์‚ฌ์ง„(base64) - jpg, jpeg, webp, png, gif, svg - photoBase64?: string // ์ œํ’ˆ ์ƒ์„ธ ์‚ฌ์ง„(base64) - jpg, jpeg, webp, png, gif, svg - discountRate?: number // ์ œํ’ˆ ํ• ์ธ์œจ -} -``` - -```json -{ - "title": "MacBook Pro 16", - "price": 3360000, - "description": "์—ญ๋Œ€ ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ MacBook Pro๊ฐ€ ๋“ฑ์žฅํ–ˆ์Šต๋‹ˆ๋‹ค. ์ตœ์ดˆ์˜ ํ”„๋กœ์šฉ Apple Silicon์ธ M1 Pro ๋˜๋Š” M1 Max ์นฉ์„ ํƒ‘์žฌํ•ด ์œ์‚ด๊ฐ™์ด ๋น ๋ฅธ ์†๋„๋Š” ๋ฌผ๋ก , ํš๊ธฐ์ ์ธ ์„ฑ๋Šฅ๊ณผ ๋†€๋ผ์šด ๋ฐฐํ„ฐ๋ฆฌ ์‚ฌ์šฉ ์‹œ๊ฐ„์„ ์ž๋ž‘ํ•˜์ฃ . ์—ฌ๊ธฐ์— ์‹œ์„ ์„ ์‚ฌ๋กœ์žก๋Š” Liquid Retina XDR ๋””์Šคํ”Œ๋ ˆ์ด, Mac ๋…ธํŠธ๋ถ ์‚ฌ์ƒ ์ตœ๊ณ ์˜ ์นด๋ฉ”๋ผ ๋ฐ ์˜ค๋””์˜ค ๊ทธ๋ฆฌ๊ณ  ๋”ํ•  ๋‚˜์œ„ ์—†์ด ๋‹ค์–‘ํ•œ ํฌํŠธ๊นŒ์ง€. ๊ธฐ์กด ๊ทธ ์–ด๋–ค ์นดํ…Œ๊ณ ๋ฆฌ์—๋„ ์†ํ•˜์ง€ ์•Š๋Š” ๋…ธํŠธ๋ถ. ์ƒˆ๋กœ์šด MacBook Pro๋Š” ๊ทธ์•ผ๋ง๋กœ ์•ผ์ˆ˜์ž…๋‹ˆ๋‹ค.", - "tags": [ - "๊ฐ€์ „", - "๋…ธํŠธ๋ถ", - "์ปดํ“จํ„ฐ" - ], - "thumbnailBase64": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...(์ƒ๋žต)" -} -``` - -์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -interface ResponseValue { // ์ถ”๊ฐ€ํ•œ ์ œํ’ˆ์˜ ์ƒ์„ธ ๋‚ด์šฉ - id: string // ์ œํ’ˆ ID - title: string // ์ œํ’ˆ ์ด๋ฆ„ - price: number // ์ œํ’ˆ ๊ฐ€๊ฒฉ - description: string // ์ œํ’ˆ ์ƒ์„ธ ์„ค๋ช… - tags: string[] // ์ œํ’ˆ ํƒœ๊ทธ - thumbnail: string | null // ์ œํ’ˆ ์ธ๋„ค์ผ ์ด๋ฏธ์ง€(URL) - photo: string | null // ์ œํ’ˆ ์ƒ์„ธ ์ด๋ฏธ์ง€(URL) - isSoldOut: boolean // ์ œํ’ˆ ๋งค์ง„ ์—ฌ๋ถ€ - discountRate: number // ์ œํ’ˆ ํ• ์ธ์œจ -} -``` - -```json -{ - "id": "nbqtQvEivYwEXTDet7YM", - "title": "MacBook Pro 16", - "price": 3360000, - "description": "์—ญ๋Œ€ ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ MacBook Pro๊ฐ€ ๋“ฑ์žฅํ–ˆ์Šต๋‹ˆ๋‹ค. ์ตœ์ดˆ์˜ ํ”„๋กœ์šฉ Apple Silicon์ธ M1 Pro ๋˜๋Š” M1 Max ์นฉ์„ ํƒ‘์žฌํ•ด ์œ์‚ด๊ฐ™์ด ๋น ๋ฅธ ์†๋„๋Š” ๋ฌผ๋ก , ํš๊ธฐ์ ์ธ ์„ฑ๋Šฅ๊ณผ ๋†€๋ผ์šด ๋ฐฐํ„ฐ๋ฆฌ ์‚ฌ์šฉ ์‹œ๊ฐ„์„ ์ž๋ž‘ํ•˜์ฃ . ์—ฌ๊ธฐ์— ์‹œ์„ ์„ ์‚ฌ๋กœ์žก๋Š” Liquid Retina XDR ๋””์Šคํ”Œ๋ ˆ์ด, Mac ๋…ธํŠธ๋ถ ์‚ฌ์ƒ ์ตœ๊ณ ์˜ ์นด๋ฉ”๋ผ ๋ฐ ์˜ค๋””์˜ค ๊ทธ๋ฆฌ๊ณ  ๋”ํ•  ๋‚˜์œ„ ์—†์ด ๋‹ค์–‘ํ•œ ํฌํŠธ๊นŒ์ง€. ๊ธฐ์กด ๊ทธ ์–ด๋–ค ์นดํ…Œ๊ณ ๋ฆฌ์—๋„ ์†ํ•˜์ง€ ์•Š๋Š” ๋…ธํŠธ๋ถ. ์ƒˆ๋กœ์šด MacBook Pro๋Š” ๊ทธ์•ผ๋ง๋กœ ์•ผ์ˆ˜์ž…๋‹ˆ๋‹ค.", - "tags": [ - "๊ฐ€์ „", - "๋…ธํŠธ๋ถ", - "์ปดํ“จํ„ฐ" - ], - "thumbnail": "https://storage.googleapis.com/heropy-api/vIKMk_jy4Yv195256.png", - "photo": "https://storage.googleapis.com/heropy-api/voihKb3NLGcv195257.png", - "isSoldOut": false, - "discountRate": 0 -} -``` - -### ์ œํ’ˆ ์ˆ˜์ • - -- ๊ด€๋ฆฌ์ž ์ „์šฉ API์ž…๋‹ˆ๋‹ค. -- ์‚ฌ์šฉ์ž์˜ ๊ตฌ๋งค ๋‚ด์—ญ ํ™•์ธ์„ ์œ„ํ•ด, ์ œํ’ˆ์„ ์‹ค์ œ๋กœ๋Š” ์‚ญ์ œํ•˜์ง€ ์•Š๊ณ  ๋งค์ง„(Sold Out) ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. -- ๋งค์ง„์€ ๋‹ค์‹œ ํ•ด์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. - -```curl -curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/products/:productId - \ -X 'PUT' - \ -H 'masterKey: true' -``` - -์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -interface RequestBody { - title?: string // ์ œํ’ˆ ์ด๋ฆ„ - price?: number // ์ œํ’ˆ ๊ฐ€๊ฒฉ - description?: string // ์ œํ’ˆ ์ƒ์„ธ ์„ค๋ช… - tags?: string[] // ์ œํ’ˆ ํƒœ๊ทธ - thumbnailBase64?: string // ์ œํ’ˆ ์ธ๋„ค์ผ(๋Œ€ํ‘œ) ์‚ฌ์ง„(base64) - jpg, jpeg, webp, png, gif, svg - photoBase64?: string // ์ œํ’ˆ ์ƒ์„ธ ์‚ฌ์ง„(base64) - jpg, jpeg, webp, png, gif, svg - isSoldOut?: boolean // ์ œํ’ˆ ๋งค์ง„ ์—ฌ๋ถ€ - discountRate?: number // ์ œํ’ˆ ํ• ์ธ์œจ -} -``` - -```json -{ - "price": 1500 -} -``` - -์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -interface ResponseValue { // ์ˆ˜์ •ํ•œ ์ œํ’ˆ์˜ ์ƒ์„ธ ๋‚ด์šฉ - id: string // ์ œํ’ˆ ID - title: string // ์ œํ’ˆ ์ด๋ฆ„ - price: number // ์ œํ’ˆ ๊ฐ€๊ฒฉ - description: string // ์ œํ’ˆ ์ƒ์„ธ ์„ค๋ช… - tags: string[] // ์ œํ’ˆ ํƒœ๊ทธ - thumbnail: string | null // ์ œํ’ˆ ์ธ๋„ค์ผ ์ด๋ฏธ์ง€(URL) - photo: string | null // ์ œํ’ˆ ์ƒ์„ธ ์ด๋ฏธ์ง€(URL) - isSoldOut: boolean // ์ œํ’ˆ ๋งค์ง„ ์—ฌ๋ถ€ - discountRate: number // ์ œํ’ˆ ํ• ์ธ์œจ -} -``` - -```json -{ - "id": "nbqtQvEivYwEXTDet7YM", - "title": "MacBook Pro 16", - "price": 1500, - "description": "์—ญ๋Œ€ ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ MacBook Pro๊ฐ€ ๋“ฑ์žฅํ–ˆ์Šต๋‹ˆ๋‹ค. ์ตœ์ดˆ์˜ ํ”„๋กœ์šฉ Apple Silicon์ธ M1 Pro ๋˜๋Š” M1 Max ์นฉ์„ ํƒ‘์žฌํ•ด ์œ์‚ด๊ฐ™์ด ๋น ๋ฅธ ์†๋„๋Š” ๋ฌผ๋ก , ํš๊ธฐ์ ์ธ ์„ฑ๋Šฅ๊ณผ ๋†€๋ผ์šด ๋ฐฐํ„ฐ๋ฆฌ ์‚ฌ์šฉ ์‹œ๊ฐ„์„ ์ž๋ž‘ํ•˜์ฃ . ์—ฌ๊ธฐ์— ์‹œ์„ ์„ ์‚ฌ๋กœ์žก๋Š” Liquid Retina XDR ๋””์Šคํ”Œ๋ ˆ์ด, Mac ๋…ธํŠธ๋ถ ์‚ฌ์ƒ ์ตœ๊ณ ์˜ ์นด๋ฉ”๋ผ ๋ฐ ์˜ค๋””์˜ค ๊ทธ๋ฆฌ๊ณ  ๋”ํ•  ๋‚˜์œ„ ์—†์ด ๋‹ค์–‘ํ•œ ํฌํŠธ๊นŒ์ง€. ๊ธฐ์กด ๊ทธ ์–ด๋–ค ์นดํ…Œ๊ณ ๋ฆฌ์—๋„ ์†ํ•˜์ง€ ์•Š๋Š” ๋…ธํŠธ๋ถ. ์ƒˆ๋กœ์šด MacBook Pro๋Š” ๊ทธ์•ผ๋ง๋กœ ์•ผ์ˆ˜์ž…๋‹ˆ๋‹ค.", - "tags": [ - "๊ฐ€์ „", - "๋…ธํŠธ๋ถ", - "์ปดํ“จํ„ฐ" - ], - "thumbnail": "https://storage.googleapis.com/heropy-api/vIKMk_jy4Yv195256.png", - "photo": "https://storage.googleapis.com/heropy-api/voihKb3NLGcv195257.png", - "isSoldOut": false, - "discountRate": 0 -} -``` - -### ์ œํ’ˆ ์‚ญ์ œ - -- ๊ด€๋ฆฌ์ž ์ „์šฉ API์ž…๋‹ˆ๋‹ค. - -```curl -curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/products/:productId - \ -X 'DELETE' - \ -H 'masterKey: true' -``` - -์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -- ์—†์Œ - -์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -type ResponseValue = true // ์ œํ’ˆ ์‚ญ์ œ ์ฒ˜๋ฆฌ ์ƒํƒœ -``` - -### ๋‹จ์ผ ์ œํ’ˆ ์ƒ์„ธ ์กฐํšŒ - -- ๊ณต์šฉ API์ž…๋‹ˆ๋‹ค. - -```curl -curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/products/:productId - \ -X 'GET' -``` - -์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -- ์—†์Œ - -์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -interface ResponseValue { // ์ œํ’ˆ์˜ ์ƒ์„ธ ๋‚ด์šฉ - id: string // ์ œํ’ˆ ID - title: string // ์ œํ’ˆ ์ด๋ฆ„ - price: number // ์ œํ’ˆ ๊ฐ€๊ฒฉ - description: string // ์ œํ’ˆ ์ƒ์„ธ ์„ค๋ช… - tags: string[] // ์ œํ’ˆ ํƒœ๊ทธ - thumbnail: string | null // ์ œํ’ˆ ์ธ๋„ค์ผ ์ด๋ฏธ์ง€(URL) - photo: string | null // ์ œํ’ˆ ์ƒ์„ธ ์ด๋ฏธ์ง€(URL) - isSoldOut: boolean // ์ œํ’ˆ ๋งค์ง„ ์—ฌ๋ถ€ - reservations: Reservation[] // ์ œํ’ˆ์˜ ๋ชจ๋“  ์˜ˆ์•ฝ ์ •๋ณด ๋ชฉ๋ก - discountRate: number // ์ œํ’ˆ ํ• ์ธ์œจ -} - -interface Reservation { - start: string // ์˜ˆ์•ฝ ์‹œ์ž‘ ์‹œ๊ฐ„ - end: string // ์˜ˆ์•ฝ ์ข…๋ฃŒ ์‹œ๊ฐ„ - isCanceled: boolean // ์˜ˆ์•ฝ ์ทจ์†Œ ์—ฌ๋ถ€ - isExpired: boolean // ์˜ˆ์•ฝ ๋งŒ๋ฃŒ ์—ฌ๋ถ€ -} -``` - -```json -{ - "id": "nbqtQvEivYwEXTDet7YM", - "title": "MacBook Pro 16", - "price": 3360000, - "description": "์—ญ๋Œ€ ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ MacBook Pro๊ฐ€ ๋“ฑ์žฅํ–ˆ์Šต๋‹ˆ๋‹ค. ์ตœ์ดˆ์˜ ํ”„๋กœ์šฉ Apple Silicon์ธ M1 Pro ๋˜๋Š” M1 Max ์นฉ์„ ํƒ‘์žฌํ•ด ์œ์‚ด๊ฐ™์ด ๋น ๋ฅธ ์†๋„๋Š” ๋ฌผ๋ก , ํš๊ธฐ์ ์ธ ์„ฑ๋Šฅ๊ณผ ๋†€๋ผ์šด ๋ฐฐํ„ฐ๋ฆฌ ์‚ฌ์šฉ ์‹œ๊ฐ„์„ ์ž๋ž‘ํ•˜์ฃ . ์—ฌ๊ธฐ์— ์‹œ์„ ์„ ์‚ฌ๋กœ์žก๋Š” Liquid Retina XDR ๋””์Šคํ”Œ๋ ˆ์ด, Mac ๋…ธํŠธ๋ถ ์‚ฌ์ƒ ์ตœ๊ณ ์˜ ์นด๋ฉ”๋ผ ๋ฐ ์˜ค๋””์˜ค ๊ทธ๋ฆฌ๊ณ  ๋”ํ•  ๋‚˜์œ„ ์—†์ด ๋‹ค์–‘ํ•œ ํฌํŠธ๊นŒ์ง€. ๊ธฐ์กด ๊ทธ ์–ด๋–ค ์นดํ…Œ๊ณ ๋ฆฌ์—๋„ ์†ํ•˜์ง€ ์•Š๋Š” ๋…ธํŠธ๋ถ. ์ƒˆ๋กœ์šด MacBook Pro๋Š” ๊ทธ์•ผ๋ง๋กœ ์•ผ์ˆ˜์ž…๋‹ˆ๋‹ค.", - "tags": [ - "๊ฐ€์ „", - "๋…ธํŠธ๋ถ", - "์ปดํ“จํ„ฐ" - ], - "thumbnail": "https://storage.googleapis.com/heropy-api/vIKMk_jy4Yv195256.png", - "photo": "https://storage.googleapis.com/heropy-api/voihKb3NLGcv195257.png", - "isSoldOut": false, - "reservations": [], - "discountRate": 0 -} -``` - -์˜ˆ์•ฝ ์ •๋ณด(`reservation`)๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ: - -```json -{ - "reservations": [ - { - "reservation": { - "start": "2021-11-12T06:00:00.000Z", - "end": "2021-11-12T07:00:00.000Z", - "isCanceled": false, - "isExpired": true + ``` + + - ๋”ฐ๋ผ์„œ ํด๋ผ์ด์–ธํŠธ ๋‹จ์—์„œ ๊ด€๋ฆฌ์ž ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๊ณ  isAdmin property๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์ „์—ญ์ƒํƒœ์™€ ๋กœ์ปฌ์ €์žฅ์†Œ์— ์ €์žฅํ•œ๋‹ค. + + ```ts + interface LocalUser { + user: { + email: string; + displayName: string; + profileImg: string | null; + }; + accessToken: string; + isAdmin: boolean; + } + ``` + + - ์ด ๋ฐฉ๋ฒ•์€ ๋ณด์•ˆ์ƒ ์œ„ํ—˜ํ•˜์ง€๋งŒ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋Œ€์‘ ์ „๋žต์„ ์ทจํ•  ์ˆ˜ ์žˆ๋‹ค. + + - ๋น„๊ฑด์ „ํ•œ ์‚ฌ์šฉ์ž๊ฐ€ local storage์— ์ ‘๊ทผํ•˜์—ฌ isAdmin์„ true๋กœ ๋ฐ”๊ฟ€ ๊ฒฝ์šฐ
๐Ÿ‘‰ ๊ด€๋ฆฌ์ž๋งŒ ์ ‘๊ทผ ํ•  ์ˆ˜ ์žˆ๋Š” route ๋ถ„๊ธฐ์ ์— ์ธ์ฆ api๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž์˜ ์‹ ์›์„ ํ™•์ธํ•œ๋‹ค. + - [src/routes/admin/Admin.tsx](https://github.com/howooking/KDT5-M5/blob/0172a31077634c42139005c52c4e62156e3ab2ba/src/routes/admin/Admin.tsx#L1C1-L26) + + ```js + export default function Admin() { + const { authMe } = userStore(); + useEffect(() => { + async function auth() { + const errorMessage = await authMe(); + if (errorMessage) { + toast.error(errorMessage, { id: 'authMe' }); + } + } + auth(); + }, []); + return ( + <> + + + + ); } + ``` + + - ๋น„๊ฑด์ „ํ•œ ์‚ฌ์šฉ์ž๊ฐ€ ํŒŒ์ผ์— ์ €์žฅ๋œ ๊ด€๋ฆฌ์ž ์ด๋ฉ”์ผ ์ฃผ์†Œ๋ฅผ ๋ณด๋Š” ๊ฒฝ์šฐ
๐Ÿ‘‰ ๊ด€๋ฆฌ์ž์˜ ๋ฉ”์ผ ์ฃผ์†Œ๋ฅผ ์•Œ๋”๋ผ๋„ ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ๋ชจ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ๊ดœ์ฐฎ๋‹ค. ๊ด€๋ฆฌ์ž ๋ฉ”์ผ ์ฃผ์†Œ๋ฅผ ํ™˜๊ฒฝ๋ณ€์ˆ˜์— ์ €์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๋‹ค. + +- ๋ถ€์กฑํ•œ ์ƒํ’ˆ ์ •๋ณด + + - ์ƒํ’ˆ์˜ ์Šคํ‚ค๋งˆ๋Š” ์•„๋ž˜์™€ ๊ฐ™์œผ๋ฉฐ ๋ณธ ํ”„๋กœ์ ํŠธ์—์„œ ํ•„์š”ํ•œ 'category'์™€ 'brand' ํ•ญ๋ชฉ์ด ์—†๋‹ค. + ```ts + interface Product { + id: string; + title: string; + price: number; + description: string; + tags: string[]; + thumbnail: string | null; + photo: string | null; + isSoldOut: boolean; + discountRate: number; } - ] -} -``` + ``` + - tags ํ•ญ๋ชฉ์—์„œ ๋ฐฐ์—ด์˜ ์ฒซ๋ฒˆ์งธ ์š”์†Œ๋ฅผ category, ๋‘๋ฒˆ์งธ ์š”์†Œ๋ฅผ brand๋กœ ์ง€์ •ํ•˜์˜€๋‹ค. -### ์ œํ’ˆ ๊ฒ€์ƒ‰ + ```js + tags: ['soccer', 'nike'], + ``` -- ์‚ฌ์šฉ์ž ์ „์šฉ API์ž…๋‹ˆ๋‹ค. -- ์ œํ’ˆ ์ด๋ฆ„๊ณผ ํƒœ๊ทธ๋ฅผ ๋™์‹œ์— ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ๊ณ , 'And'(๊ฒ€์ƒ‰ํ•œ ์ด๋ฆ„๊ณผ ํƒœ๊ทธ ๋ชจ๋‘ ํฌํ•จ๋œ ์ œํ’ˆ) ์กฐ๊ฑด์œผ๋กœ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. -- ์ œํ’ˆ ์ด๋ฆ„๊ณผ ํƒœ๊ทธ ๋ชจ๋‘ ํฌํ•จํ•˜์ง€ ์•Š์œผ๋ฉด, ๋ชจ๋“  ์ œํ’ˆ์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. -- ์ œํ’ˆ์˜ ๊ธฐ๋ณธ ์ •๋ณด๋งŒ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. -- ๋งค์ง„๋œ ์ œํ’ˆ์€ ๊ฒ€์ƒ‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. +- ๋ผ์šฐํŠธ ๋ณดํ˜ธ -```curl -curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/products/search - \ -X 'POST' -``` + - ๋กœ๊ทธ์ธ ์ƒํƒœ, ๊ด€๋ฆฌ์ž ์—ฌ๋ถ€์— ๋”ฐ๋ผ์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ํŽ˜์ด์ง€๋ฅผ ์ œํ•œํ•ด์•ผ ํ•œ๋‹ค. + - ProdtectedRoute์—์„œ ์ „์—ญ User ์ƒํƒœ์™€ adminRequired props ์†์„ฑ์— ๋”ฐ๋ผ์„œ ์ ‘๊ทผ์„ ์ œํ•œํ•˜๊ฒŒ ํ•˜์˜€๋‹ค. + - [src/routes/ProtectedRoute.tsx](https://github.com/howooking/KDT5-M5/blob/0172a31077634c42139005c52c4e62156e3ab2ba/src/routes/ProtectedRoute.tsx#L1-L22) -์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: + ```js + import { Navigate } from 'react-router-dom'; + import { userStore } from '@/store'; -```ts -interface RequestBody { - searchText?: string // ๊ฒ€์ƒ‰ํ•  ์ œํ’ˆ ์ด๋ฆ„ - searchTags?: string[] // ๊ฒ€์ƒ‰ํ•  ์ œํ’ˆ ํƒœ๊ทธ -} -``` + type ProtectedRouteProps = { + element: React.ReactNode, + adminRequired?: boolean, + }; -```json -{ - "searchText": "์‚ผ์„ฑ์ „์ž", - "searchTags": ["๊ฐ€์ „"] -} -``` - -์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: + export default function ProtectedRoute({ + element, + adminRequired, + }: ProtectedRouteProps) { + const { userInfo } = userStore(); -```ts -type ResponseValue = Product[] // ๊ด€๋ฆฌํ•˜๋Š” ๋ชจ๋“  ์ œํ’ˆ์˜ ๋ชฉ๋ก - -interface Product { // ์ œํ’ˆ ์ •๋ณด - id: string // ์ œํ’ˆ ID - title: string // ์ œํ’ˆ ์ด๋ฆ„ - price: number // ์ œํ’ˆ ๊ฐ€๊ฒฉ - description: string // ์ œํ’ˆ ์„ค๋ช…(์ตœ๋Œ€ 100์ž) - tags: string[] // ์ œํ’ˆ ํƒœ๊ทธ - thumbnail: string | null // ์ œํ’ˆ ์ธ๋„ค์ผ ์ด๋ฏธ์ง€(URL) - discountRate: number // ์ œํ’ˆ ํ• ์ธ์œจ -} -``` - -```json -[ - { - "id": "cFmeC7aY5KjZbBAdJE9y", - "title": "์‚ผ์„ฑ์ „์ž ์Šค๋งˆํŠธ๋ชจ๋‹ˆํ„ฐ M7 S43AM700", - "price": 639000, - "description": "107.9cm(43์ธ์น˜) / ์™€์ด๋“œ(16:9) / ํ‰๋ฉด / VA / 3840 x 2160(4K UHD) / ํ”ฝ์…€ํ”ผ์น˜: 0.2451mm / 8ms(GTG) / 300cd / 5,00", - "tags": [ - "๊ฐ€์ „", - "๋ชจ๋‹ˆํ„ฐ", - "์ปดํ“จํ„ฐ" - ], - "thumbnail": "https://storage.googleapis.com/heropy-api/vBAK4MQdH5v195712.png", - "discountRate": 0 - } -] -``` - -### ์ œํ’ˆ ๊ฑฐ๋ž˜(๊ตฌ๋งค) ์‹ ์ฒญ - -- ์‚ฌ์šฉ์ž ์ „์šฉ API์ž…๋‹ˆ๋‹ค. -- ๊ฑฐ๋ž˜(๊ตฌ๋งค) ์‹ ์ฒญ์‹œ ์—ฐ๊ฒฐ๋œ ๊ณ„์ขŒ์—์„œ ๊ฒฐ์ œ๋ฉ๋‹ˆ๋‹ค. -- ๊ฒฐ์ œํ•  ๊ณ„์ขŒ(ID)๋ฅผ ๊ผญ ์„ ํƒํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.(`๊ณ„์ขŒ ๋ชฉ๋ก ๋ฐ ์ž”์•ก ์กฐํšŒ` API๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”) -- ์„ ํƒํ•œ ๊ณ„์ขŒ์˜ ์ž”์•ก๋ณด๋‹ค ๊ฒฐ์ œ ๊ธˆ์•ก์ด ํฌ๋ฉด ๊ฒฐ์ œ๊ฐ€ ์ฒ˜๋ฆฌ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.(์—๋Ÿฌ ๋ฐ˜ํ™˜) - -```curl -curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/products/buy - \ -X 'POST' - \ -H 'Authorization: Bearer ' -``` - -์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -interface RequestBody { - productId: string // ๊ฑฐ๋ž˜ํ•  ์ œํ’ˆ ID (ํ•„์ˆ˜!) - accountId: string // ๊ฒฐ์ œํ•  ์‚ฌ์šฉ์ž ๊ณ„์ขŒ ID (ํ•„์ˆ˜!) - reservation?: { // ์˜ˆ์•ฝ ์ •๋ณด(์˜ˆ์•ฝ ์‹œ์Šคํ…œ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋งŒ ํ•„์š”) - start: string // ์˜ˆ์•ฝ ์‹œ์ž‘ ์‹œ๊ฐ„(ISO) - end: string // ์˜ˆ์•ฝ ์ข…๋ฃŒ ์‹œ๊ฐ„(ISO) - } -} -``` - -```js -const isoString = new Date().toISOString() -``` - -```json -{ - "productId": "nbqtQvEivYwEXTDet7YM", - "accountId": "Mq2KKHk8vlmr6Xkg58Fa", - "reservation": { - "start": "2021-11-12T06:00:00.000Z", - "end": "2021-11-12T07:00:00.000Z" - } -} -``` - -์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -type ResponseValue = true // ๊ฑฐ๋ž˜ ์‹ ์ฒญ ์ฒ˜๋ฆฌ ์—ฌ๋ถ€ -``` - -### ์ œํ’ˆ ๊ฑฐ๋ž˜(๊ตฌ๋งค) ์ทจ์†Œ - -- ์‚ฌ์šฉ์ž ์ „์šฉ API์ž…๋‹ˆ๋‹ค. -- '๊ฑฐ๋ž˜ ์ทจ์†Œ'์‹œ ๊ฒฐ์ œํ•œ ์‚ฌ์šฉ์ž ๊ณ„์ขŒ๋กœ ๊ธˆ์•ก์ด ํ™˜๋ถˆ๋ฉ๋‹ˆ๋‹ค. - -```curl -curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/products/cancel - \ -X 'POST' - \ -H 'Authorization: Bearer ' -``` - -์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -interface RequestBody { - detailId: string // ์ทจ์†Œํ•  ์ œํ’ˆ์˜ ๊ฑฐ๋ž˜ ๋‚ด์—ญ ID -} -``` - -```json -{ - "detailId": "dMhfxyrAupQP18OYmywy" -} -``` - -์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -type ResponseValue = true // ๊ฑฐ๋ž˜ ์ทจ์†Œ ์ฒ˜๋ฆฌ ์—ฌ๋ถ€ -``` - -### ์ œํ’ˆ ๊ฑฐ๋ž˜(๊ตฌ๋งค) ํ™•์ • - -- ์‚ฌ์šฉ์ž ์ „์šฉ API์ž…๋‹ˆ๋‹ค. -- '๊ฑฐ๋ž˜(๊ตฌ๋งค) ํ™•์ •' ํ›„์—๋Š” '๊ฑฐ๋ž˜ ์ทจ์†Œ'๋ฅผ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. - -```curl -curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/products/ok - \ -X 'POST' - \ -H 'Authorization: Bearer ' -``` - -์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -interface RequestBody { - detailId: string // ๊ฑฐ๋ž˜(๊ตฌ๋งค) ํ™•์ •ํ•  ์ œํ’ˆ์˜ ๊ฑฐ๋ž˜ ๋‚ด์—ญ ID -} -``` - -```json -{ - "detailId": "dMhfxyrAupQP18OYmywy" -} -``` - -์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -type ResponseValue = true // ๊ฑฐ๋ž˜(๊ตฌ๋งค) ํ™•์ • ์ฒ˜๋ฆฌ ์—ฌ๋ถ€ -``` - -### ์ œํ’ˆ ์ „์ฒด ๊ฑฐ๋ž˜(๊ตฌ๋งค) ๋‚ด์—ญ - -- ์‚ฌ์šฉ์ž ์ „์šฉ API์ž…๋‹ˆ๋‹ค. -- ๊ฑฐ๋ž˜ ๋‚ด์—ญ์˜ ๊ธฐ๋ณธ ์ •๋ณด๋งŒ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. - -```curl -curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/products/transactions/details - \ -X 'GET' - \ -H 'Authorization: Bearer ' -``` - -์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -- ์—†์Œ - -์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -type RequestValue = TransactionDetail[] // ๋ชจ๋“  ๊ฑฐ๋ž˜ ๋‚ด์—ญ์˜ ๋ชฉ๋ก - -interface TransactionDetail { // ๊ฑฐ๋ž˜ ๋‚ด์—ญ ์ •๋ณด - detailId: string // ๊ฑฐ๋ž˜ ๋‚ด์—ญ ID - product: { // ๊ฑฐ๋ž˜ํ•œ ์ œํ’ˆ ์ •๋ณด - productId: string - title: string - price: number - description: string - tags: string[] - thumbnail: string | null - discountRate: number // ์ œํ’ˆ ํ• ์ธ์œจ - } - reservation: Reservation | null // ๊ฑฐ๋ž˜ํ•œ ์ œํ’ˆ์˜ ์˜ˆ์•ฝ ์ •๋ณด - timePaid: string // ์ œํ’ˆ์„ ๊ฑฐ๋ž˜ํ•œ ์‹œ๊ฐ„ - isCanceled: boolean // ๊ฑฐ๋ž˜ ์ทจ์†Œ ์—ฌ๋ถ€ - done: boolean // ๊ฑฐ๋ž˜ ์™„๋ฃŒ ์—ฌ๋ถ€ -} - -interface Reservation { - start: string // ์˜ˆ์•ฝ ์‹œ์ž‘ ์‹œ๊ฐ„ - end: string // ์˜ˆ์•ฝ ์ข…๋ฃŒ ์‹œ๊ฐ„ - isCanceled: boolean // ์˜ˆ์•ฝ ์ทจ์†Œ ์—ฌ๋ถ€ - isExpired: boolean // ์˜ˆ์•ฝ ๋งŒ๋ฃŒ ์—ฌ๋ถ€ -} -``` - -```json -[ - { - "detailId": "9jAoagzrZBkSWI5NctEB", - "product": { - "productId": "nbqtQvEivYwEXTDet7YM", - "title": "MacBook Pro 16", - "price": 3360000, - "description": "์—ญ๋Œ€ ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ MacBook Pro๊ฐ€ ๋“ฑ์žฅํ–ˆ์Šต๋‹ˆ๋‹ค. ์ตœ์ดˆ์˜ ํ”„๋กœ์šฉ Apple Silicon์ธ M1 Pro ๋˜๋Š” M1 Max ์นฉ์„ ํƒ‘์žฌํ•ด ์œ์‚ด๊ฐ™์ด ๋น ๋ฅธ ์†๋„๋Š” ๋ฌผ๋ก , ํš๊ธฐ์ ์ธ ์„ฑ", - "tags": [ - "๊ฐ€์ „", - "๋…ธํŠธ๋ถ", - "์ปดํ“จํ„ฐ" - ], - "thumbnail": "https://storage.googleapis.com/heropy-api/vIKMk_jy4Yv195256.png", - "discountRate": 0 - }, - "reservation": null, - "timePaid": "2021-11-07T20:17:32.112Z", - "isCanceled": true, - "done": false - }, - { - "detailId": "dMhfxyrAupQP18OYmywy", - "product": { - "productId": "cFmeC7aY5KjZbBAdJE9y", - "title": "์‚ผ์„ฑ์ „์ž ์Šค๋งˆํŠธ๋ชจ๋‹ˆํ„ฐ M7 S43AM700", - "price": 639000, - "description": "107.9cm(43์ธ์น˜) / ์™€์ด๋“œ(16:9) / ํ‰๋ฉด / VA / 3840 x 2160(4K UHD) / ํ”ฝ์…€ํ”ผ์น˜: 0.2451mm / 8ms(GTG) / 300cd / 5,00", - "tags": [ - "๊ฐ€์ „", - "๋ชจ๋‹ˆํ„ฐ", - "์ปดํ“จํ„ฐ" - ], - "thumbnail": "https://storage.googleapis.com/heropy-api/vBAK4MQdH5v195712.png", - "discountRate": 0 - }, - "reservation": { - "start": "2021-11-12T06:00:00.000Z", - "end": "2021-11-12T07:00:00.000Z", - "isCanceled": false, - "isExpired": true - }, - "timePaid": "2021-11-07T20:01:49.100Z", - "isCanceled": false, - "done": true - } -] -``` - -### ๋‹จ์ผ ์ œํ’ˆ ์ƒ์„ธ ๊ฑฐ๋ž˜(๊ตฌ๋งค) ๋‚ด์—ญ - -- ์‚ฌ์šฉ์ž ์ „์šฉ API์ž…๋‹ˆ๋‹ค. - -```curl -curl https://asia-northeast3-heropy-api.cloudfunctions.net/api/products/transactions/detail - \ -X 'POST' - \ -H 'Authorization: Bearer ' -``` - -์š”์ฒญ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -interface RequestBody { - detailId: string // ์ƒ์„ธ ๋‚ด์šฉ์„ ํ™•์ธํ•  ๊ฑฐ๋ž˜(๊ตฌ๋งค) ๋‚ด์—ญ ID -} -``` - -```json -{ - "detailId": "dMhfxyrAupQP18OYmywy" -} -``` - -์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ์˜ˆ์‹œ: - -```ts -interface TransactionDetail { // ์ƒ์„ธ ๊ฑฐ๋ž˜ ์ •๋ณด - detailId: string // ๊ฑฐ๋ž˜ ๋‚ด์—ญ ID - account: { // ๊ฑฐ๋ž˜ํ•œ ์‚ฌ์šฉ์ž์˜ ๊ณ„์ขŒ ์ •๋ณด - bankName: string - bankCode: string - accountNumber: string - } - product: { // ๊ฑฐ๋ž˜ํ•œ ์ œํ’ˆ ์ •๋ณด - productId: string - title: string - price: number - description: string - tags: string[] - thumbnail: string | null - photo: string | null - discountRate: number // ์ œํ’ˆ ํ• ์ธ์œจ - } - reservation: Reservation | null // ๊ฑฐ๋ž˜ํ•œ ์ œํ’ˆ์˜ ์˜ˆ์•ฝ ์ •๋ณด - timePaid: string // ์ œํ’ˆ์„ ๊ฑฐ๋ž˜ํ•œ ์‹œ๊ฐ„ - isCanceled: boolean // ๊ฑฐ๋ž˜ ์ทจ์†Œ ์—ฌ๋ถ€ - done: boolean // ๊ฑฐ๋ž˜ ์™„๋ฃŒ ์—ฌ๋ถ€ -} - -interface Reservation { - start: string // ์˜ˆ์•ฝ ์‹œ์ž‘ ์‹œ๊ฐ„ - end: string // ์˜ˆ์•ฝ ์ข…๋ฃŒ ์‹œ๊ฐ„ - isCanceled: boolean // ์˜ˆ์•ฝ ์ทจ์†Œ ์—ฌ๋ถ€ - isExpired: boolean // ์˜ˆ์•ฝ ๋งŒ๋ฃŒ ์—ฌ๋ถ€ -} -``` - -```json -{ - "detailId": "dMhfxyrAupQP18OYmywy", - "account": { - "bankName": "KB๊ตญ๋ฏผ์€ํ–‰", - "bankCode": "004", - "accountNumber": "123-XX-XXXX-XXX" - }, - "product": { - "productId": "cFmeC7aY5KjZbBAdJE9y", - "title": "์‚ผ์„ฑ์ „์ž ์Šค๋งˆํŠธ๋ชจ๋‹ˆํ„ฐ M7 S43AM700", - "price": 639000, - "description": "107.9cm(43์ธ์น˜) / ์™€์ด๋“œ(16:9) / ํ‰๋ฉด / VA / 3840 x 2160(4K UHD) / ํ”ฝ์…€ํ”ผ์น˜: 0.2451mm / 8ms(GTG) / 300cd / 5,000:1 / ์ตœ๋Œ€ ์ฃผ์‚ฌ์œจ: 60Hz / HDMI 2.0 / USB Type-C / ํ”Œ๋ฆฌ์ปค ํ”„๋ฆฌ / ๋ธ”๋ฃจ๋ผ์ดํŠธ ์ฐจ๋‹จ / ๊ฒŒ์ž„๋ชจ๋“œ ์ง€์› / ์Šคํ”ผ์ปค / ๋ฆฌ๋ชจ์ปจ / USBํ—ˆ๋ธŒ / Wi-Fi(๋ฌด์„ ) / ์Šค๋งˆํŠธTV / ๋ธ”๋ฃจํˆฌ์Šค / ํ‹ธํŠธ(์ƒํ•˜) / 200 x 200mm / HDR / HDR10 / 10.6kg ๊ธฐํš์ „ ์ฐจ์„ธ๋Œ€ ๊ฒŒ์ž„ ๋ผ์ดํ”„ PS5 ๋งค๋ ฅ๋ถ„์„ ๊ด€๋ จ๊ธฐ์‚ฌ ํ์†Œ๋‹‰, 43์ธ์น˜ 4K UHD ์Šค๋งˆํŠธ ๋ชจ๋‹ˆํ„ฐ โ€˜์‚ผ์„ฑ์ „์ž M7 S43AM700โ€™ ์ถœ์‹œ ๋ฐ ํ• ์ธ ํ–‰์‚ฌ ์‚ฌ์šฉ๊ธฐ ์‚ผ์„ฑ ์Šค๋งˆํŠธ๋ชจ๋‹ˆํ„ฐ m7 s43am700", - "tags": [ - "๊ฐ€์ „", - "๋ชจ๋‹ˆํ„ฐ", - "์ปดํ“จํ„ฐ" - ], - "thumbnail": "https://storage.googleapis.com/heropy-api/vBAK4MQdH5v195712.png", - "photo": "https://storage.googleapis.com/heropy-api/vVLP-ox_zSDv195712.jpg", - "discountRate": 0 - }, - "reservation": null, - "timePaid": "2021-11-07T20:01:49.100Z", - "isCanceled": false, - "done": true -} -``` + if (!userInfo) { + return ; + } + if (adminRequired && !userInfo.isAdmin) { + return ; + } + return <>{element}; + } + ``` + +- ์ƒํƒœ์— ๋”ฐ๋ฅธ UI์˜ ๋™์  ๋ณ€ํ™” + - ๊ด€๋ฆฌ์ž + - ๊ด€๋ฆฌ์ž์˜ ๊ฒฝ์šฐ Navbar์— "๊ด€๋ฆฌ์ž" ๋ฒ„ํŠผ์ด ๋ณด์ธ๋‹ค. + - ๊ด€๋ฆฌ์ž์˜ ๊ฒฝ์šฐ ๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€์— ์ ‘๊ทผ ํ•  ์ˆ˜ ์žˆ๋‹ค. + - ๊ด€๋ฆฌ์ž์˜ ๊ฒฝ์šฐ ๋กœ๊ทธ์ธ์‹œ "์ฃผ์ธ๋‹˜ ์˜ค์…จ์Šต๋‹ˆ๋‹ค" ์•Œ๋ฆผ ๋ฉ”์„ธ์ง€๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค. + - ๊ด€๋ฆฌ์ž์˜ ๊ฒฝ์šฐ ์ƒํ’ˆ ์ƒ์„ธ ํŽ˜์ด์ง€์—์„œ ์ƒํ’ˆ ์ˆ˜์ • ์•„์ด์ฝ˜์ด ๋ณด์ธ๋‹ค. + - ๋กœ๊ทธ์ธ + - ๋กœ๊ทธ์ธํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ๊ฐœ์ธ์ •๋ณด ํŽ˜์ด์ง€์— ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋‹ค. + - ๋กœ๊ทธ์ธํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ์ƒํ’ˆ ์ƒ์„ธ ํŽ˜์ด์ง€์—์„œ ๊ฒฐ์ œ ๋ฒ„ํŠผ ๋Œ€์‹  "๋กœ๊ทธ์ธ ํ•˜๋Ÿฌ๊ฐ€๊ธฐ" ๋ฒ„ํŠผ์ด ๋ณด์ธ๋‹ค. + - ๋กœ๊ทธ์ธ์„ ํ•œ ๊ฒฝ์šฐ login ํŽ˜์ด์ง€์™€ signup ํŽ˜์ด์ง€์— ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋‹ค. + - ๊ณ„์ขŒ + - ๊ณ„์ขŒ๋ฅผ ํ•˜๋‚˜๋„ ๋“ฑ๋กํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ์ƒํ’ˆ ์ƒ์„ธ ํŽ˜์ด์ง€์—์„œ "์›ํด๋ฆฐ ๊ฐ„ํŽธ ๊ฒฐ์ œ" ๋ฒ„ํŠผ ๋Œ€์‹  "๊ณ„์ขŒ ๋“ฑ๋กํ•˜๋Ÿฌ ๊ฐ€๊ธฐ" ๋ฒ„ํŠผ์ด ๋ณด์ธ๋‹ค. + - ๊ณ„์ขŒ ์—ฐ๊ฒฐ ํŽ˜์ด์ง€์—์„œ ์€ํ–‰ ์„ ํƒ์‹œ ์ž…๋ ฅ์ฐฝ์— ํ•ด๋‹น ์€ํ–‰์˜ ๊ณ„์ขŒ๋ฒˆํ˜ธ์ˆ˜๋ฅผ ์•Œ๋ ค์ฃผ๋ฉฐ ๊ทธ ์ˆ˜๋ฅผ input ์š”์†Œ์˜ maxLength๋กœ ์ง€์ •ํ•œ๋‹ค. + - ์ƒํ’ˆ + - ์ƒํ’ˆ ์ƒ์„ธ ํŽ˜์ด์ง€ ํ•˜๋‹จ์— ํ•ด๋‹น ์ƒํ’ˆ๊ณผ ๊ฐ™์€ ์นดํ…Œ๊ณ ๋ฆฌ์— ์žˆ๋Š” ์ œํ’ˆ 10๊ฐœ๋ฅผ ๋žœ๋ค์œผ๋กœ ์ถ”์ฒœํ•œ๋‹ค. + - ์ƒํ’ˆ์ด ๋งค์ง„์ธ ๊ฒฝ์šฐ "SOLD OUT" ์ด๋ฏธ์ง€๋ฅผ ์ƒํ’ˆ ์ด๋ฏธ์ง€ ์œ„์— ํ‘œ์‹œํ•œ๋‹ค. + - ์ƒํ’ˆ์ด ๋งค์ง„์ธ ๊ฒฝ์šฐ "์ž…๊ณ  ์•Œ๋ฆผ" ๋ฒ„ํŠผ์ด ๋ณด์ธ๋‹ค. +

+- ์ฒซ ํ˜‘์—… ํ”„๋กœ์ ํŠธ + - ์ฒซ ํŒ€ํ”„๋กœ์ ํŠธ๋‹ค ๋ณด๋‹ˆ ์ง„ํ–‰๊ณผ์ •์—์„œ ์•„์‰ฌ์› ๋˜ ๋ถ€๋ถ„์ด ๋งŽ์•˜์Œ + - ๋ธŒ๋žœ์น˜ ์ „๋žต + - 5๋ช…์ด ๊ฐ์ž ๋งก์€ ๊ธฐ๋Šฅ์˜ branch๋ฅผ ์ƒ์„ฑํ•˜์—ฌ develope ๋ธŒ๋žœ์น˜์— mergeํ•˜๊ณ  ์ตœ์ข…์ ์œผ๋กœ main ๋ธŒ๋žœ์น˜์— mergeํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ง„ํ–‰ + - ์ด ๋ณด๋‹ค๋Š” git hub์—์„œ pull request๋ฅผ ํ•˜๊ณ  ๋‹ค๊ฐ™์ด ๋ฆฌ๋ทฐ๋ฅผ ํ•œ ํ›„ mergeํ•˜๋Š” ๋ฐฉ์‹์ด ๋ฐ”๋žŒ์งํ•˜๋‹ค. + - ์ •๊ธฐ์ ์œผ๋กœ develope ๋ธŒ๋ Œ์น˜๋ฅผ pullํ•ด์•ผ ํ•œ๊บผ๋ฒˆ์— ๋งŽ์€ ์–‘์˜ conflict๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค. + - commit ๋‹จ์œ„ & commit message + - commit์˜ ๋‹จ์œ„๋Š” ๊ธฐ๋Šฅ ๋‹จ์œ„์—ฌ์•ผ ํ•œ๋‹ค. + - commit message๋ฅผ ์ ๊ธฐ ํž˜๋“ค๋‹ค๋ฉด ํ•ด๋‹น commit์€ ๋„ˆ๋ฌด ๋งŽ์€ ๊ธฐ๋Šฅ์„ ๋‹ด๊ณ  ์žˆ์„ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๋‹ค. + - commit ๋‹จ์œ„๋Š” ํŒŒ์ผ ๋‹จ์œ„๊ฐ€ ์•„๋‹ˆ์—ฌ๋„ ๋œ๋‹ค. ์ค„ ๋‹จ์œ„๋กœ commit์ด ๊ฐ€๋Šฅํ•˜๋‹ค. + - 5๋ช…์˜ commit message๊ฐ€ ์ œ๊ฐ๊ฐ์ด๋ผ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์˜ commit์„ ํ•œ๋ฒˆ์— ์ดํ•ดํ•˜๊ธฐ ์–ด๋ ค์› ๋‹ค. + - ํ˜‘์—…์„ ์ง„ํ–‰ํ•˜๊ธฐ ์ „ commit ๊ทœ์น™์„ ๋ฐ˜๋“œ์‹œ ์„ธ์šฐ๊ณ  ์‹œ์ž‘ํ•ด์•ผ ํ•จ +

+ +# ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ + +``` +KDT5-M5 +โ”ฃ public +โ”ฃ src +โ”ƒ โ”ฃ api +โ”ƒ โ”ƒ โ”ฃ adminApi.ts +โ”ƒ โ”ƒ โ”ฃ authApi.ts +โ”ƒ โ”ƒ โ”ฃ bankApi.ts +โ”ƒ โ”ƒ โ”— transactionApi.ts +โ”ƒ โ”ฃ components +โ”ƒ โ”ƒ โ”ฃ product +โ”ƒ โ”ƒ โ”ƒ โ”ฃ ProductBar.tsx +โ”ƒ โ”ƒ โ”ƒ โ”ฃ ProductCard.tsx +โ”ƒ โ”ƒ โ”ƒ โ”ฃ ProductSection.tsx +โ”ƒ โ”ƒ โ”ƒ โ”— ProductSortOptions.tsx +โ”ƒ โ”ƒ โ”ฃ ui +โ”ƒ โ”ƒ โ”ƒ โ”ฃ Breadcrumbs.tsx +โ”ƒ โ”ƒ โ”ƒ โ”ฃ Button.tsx +โ”ƒ โ”ƒ โ”ƒ โ”ฃ CrazyLoading.tsx +โ”ƒ โ”ƒ โ”ƒ โ”ฃ ImageUpload.tsx +โ”ƒ โ”ƒ โ”ƒ โ”ฃ Input.tsx +โ”ƒ โ”ƒ โ”ƒ โ”ฃ LoadingSpinner.tsx +โ”ƒ โ”ƒ โ”ƒ โ”ฃ ProfileImage.tsx +โ”ƒ โ”ƒ โ”ƒ โ”ฃ SectionTitle.tsx +โ”ƒ โ”ƒ โ”ƒ โ”ฃ Select.tsx +โ”ƒ โ”ƒ โ”ƒ โ”— Skeleton.tsx +โ”ƒ โ”ƒ โ”ฃ Footer.tsx +โ”ƒ โ”ƒ โ”ฃ ImageSlider.tsx +โ”ƒ โ”ƒ โ”ฃ Layout.tsx +โ”ƒ โ”ƒ โ”ฃ Navbar.tsx +โ”ƒ โ”ƒ โ”ฃ Search.tsx +โ”ƒ โ”ƒ โ”ฃ SingleUser.tsx +โ”ƒ โ”ƒ โ”— SubNavbar.tsx +โ”ƒ โ”ฃ constants +โ”ƒ โ”ƒ โ”ฃ constants.ts +โ”ƒ โ”ƒ โ”— library.ts +โ”ƒ โ”ฃ lib +โ”ƒ โ”ƒ โ”ฃ ceilPrice.ts +โ”ƒ โ”ƒ โ”— time.ts +โ”ƒ โ”ฃ routes +โ”ƒ โ”ƒ โ”ฃ admin +โ”ƒ โ”ƒ โ”ƒ โ”ฃ AddProduct.tsx +โ”ƒ โ”ƒ โ”ƒ โ”ฃ Admin.tsx +โ”ƒ โ”ƒ โ”ƒ โ”ฃ AdminClients.tsx +โ”ƒ โ”ƒ โ”ƒ โ”ฃ AdminProducts.tsx +โ”ƒ โ”ƒ โ”ƒ โ”ฃ AllTransactions.tsx +โ”ƒ โ”ƒ โ”ƒ โ”— EditProduct.tsx +โ”ƒ โ”ƒ โ”ฃ myAccount +โ”ƒ โ”ƒ โ”ƒ โ”ฃ bank +โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”ฃ BankAccounts.tsx +โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”— ConnectBankAccount.tsx +โ”ƒ โ”ƒ โ”ƒ โ”ฃ ChangeName.tsx +โ”ƒ โ”ƒ โ”ƒ โ”ฃ ChangePassword.tsx +โ”ƒ โ”ƒ โ”ƒ โ”ฃ Info.tsx +โ”ƒ โ”ƒ โ”ƒ โ”ฃ Login.tsx +โ”ƒ โ”ƒ โ”ƒ โ”ฃ LogoutNeededRoute.tsx +โ”ƒ โ”ƒ โ”ƒ โ”ฃ MyAccount.tsx +โ”ƒ โ”ƒ โ”ƒ โ”ฃ OrderDetail.tsx +โ”ƒ โ”ƒ โ”ƒ โ”ฃ OrderList.tsx +โ”ƒ โ”ƒ โ”ƒ โ”— SignUp.tsx +โ”ƒ โ”ƒ โ”ฃ Home.tsx +โ”ƒ โ”ƒ โ”ฃ Login.tsx +โ”ƒ โ”ƒ โ”ฃ LogoutNeededRoute.tsx +โ”ƒ โ”ƒ โ”ฃ NotFound.tsx +โ”ƒ โ”ƒ โ”ฃ ProductDetail.tsx +โ”ƒ โ”ƒ โ”ฃ Products.tsx +โ”ƒ โ”ƒ โ”ฃ ProtectedRoute.tsx +โ”ƒ โ”ƒ โ”ฃ SearchProducts.tsx +โ”ƒ โ”ƒ โ”— SignUp.tsx +โ”ƒ โ”ฃ App.tsx +โ”ƒ โ”ฃ index.css +โ”ƒ โ”ฃ main.tsx +โ”ƒ โ”ฃ store.ts +โ”ƒ โ”— vite-env.d.ts +โ”ฃ .eslintrc.cjs +โ”ฃ .gitignore +โ”ฃ .prettierrc +โ”ฃ custom.d.ts +โ”ฃ index.html +โ”ฃ package-lock.json +โ”ฃ package.json +โ”ฃ postcss.config.js +โ”ฃ README.md +โ”ฃ tailwind.config.js +โ”ฃ tsconfig.json +โ”ฃ tsconfig.node.json +โ”— vite.config.ts + ``` \ No newline at end of file diff --git a/custom.d.ts b/custom.d.ts new file mode 100644 index 00000000..160c2e89 --- /dev/null +++ b/custom.d.ts @@ -0,0 +1,228 @@ +// !!Auth๊ด€๋ จ ํƒ€์ž…๋“ค + +// ๋กœ๊ทธ์ธ ์š”์ฒญ์‹œ or ํšŒ์›๊ฐ€์ž… ์š”์ฒญ์‹œ ์„ฑ๊ณตํ•˜๋ฉด ์„œ๋ฒ„์—์„œ ์˜ค๋Š” ์œ ์ ธ ๋ฐ์ดํ„ฐ +interface UserResponseValue { + user: { + email: string; + displayName: string; + profileImg: string | null; + }; + accessToken: string; +} + +// ์ˆ˜์ •์— ์„ฑ๊ณตํ•˜๋ฉด ์„œ๋ฒ„์—์„œ ๋ณด๋‚ด์ฃผ๋Š” ์œ ์ ธ๊ฐ’ +interface UpdatedUserResponseValue { + email: string; + displayName: string; + profileImg: string | null; +} + +interface LocalUser { + user: { + email: string; + displayName: string; + profileImg: string | null; + }; + accessToken: string | null; + isAdmin: boolean; +} + +// !! ์–ด๋“œ๋ฏผ ๊ด€๋ฆฌ ๊ด€๋ จ api + +// ์–ด๋“œ๋ฏผ์—์„œ ์‚ฌ์šฉ์ž๋“ค ์ •๋ณด๋ฅผ ์กฐํšŒํ•  ๋•Œ ์˜ค๋Š” ์‚ฌ์šฉ์ž ์ •๋ณด ํƒ€์ž… +interface Client { + email: string; // ์‚ฌ์šฉ์ž ์•„์ด๋”” + displayName: string; // ์‚ฌ์šฉ์ž ํ‘œ์‹œ ์ด๋ฆ„ + profileImg: string; // ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์ด๋ฏธ์ง€ URL +} +interface SpentMoneyIncludedClient extends Client { + spentMoney: number; +} + +// ์ƒํ’ˆ ์ถ”๊ฐ€์‹œ ์„œ๋ฒ„๋กœ ๋ถ€ํ„ฐ ๋ฐ›๋Š” ์‘๋‹ต +interface AddProductResponseValue { + id: string; + title: string; + price: number; + description: string; + tags: string[]; + thumbnail: string | null; + photo: string | null; + isSoldOut: boolean; + discountRate: number; +} + +//์ œํ’ˆ ์ถ”๊ฐ€์‹œ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•˜๋Š” ๊ฐ’ +interface ProductInputData { + title: string; + price: string; + description: string; + tags: string[]; + thumbnailBase64?: string; + photoBase64?: string; + discountRate?: string; + isSoldOut: boolean; +} + +// ์‹ค์ œ ์ œํ’ˆ ์ถ”๊ฐ€์‹œ ์š”๊ตฌ๋˜๋Š” ๊ฐ’ +interface AddProductData { + title: string; + price: number; + description: string; + tags?: string[]; + thumbnailBase64?: string; + photoBase64?: string; + discountRate?: number; +} + +//์ œํ’ˆ ์ˆ˜์ •์‹œ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•˜๋Š” ๊ฐ’ +interface EditProductInputData { + title: string; + price: string; + description: string; + tags: string[]; + isSoldOut: string; + thumbnailBase64?: string | null; + photoBase64?: string | null; + discountRate?: string; +} + +// ์‹ค์ œ ์ˆ˜์ •์‹œ ์š”๊ตฌ๋˜๋Š” ๊ฐ’ +interface UpdateProductBodyData { + title?: string; + price?: number; + description?: string; + tags?: string[]; + thumbnailBase64?: string | null; + photoBase64?: string | null; + isSoldOut?: boolean; + discountRate?: number; +} + +// ์ˆ˜์ •์‹œ ์„œ๋ฒ„๋กœ ๋ถ€ํ„ฐ ๋ฐ›๋Š” ์ˆ˜์ •๋œ ์ƒํ’ˆ ๊ฐ’ +interface UpdatedProduct { + id: string; // ์ œํ’ˆ ID + title: string; // ์ œํ’ˆ ์ด๋ฆ„ + price: number; // ์ œํ’ˆ ๊ฐ€๊ฒฉ + description: string; // ์ œํ’ˆ ์ƒ์„ธ ์„ค๋ช… + tags: string[]; // ์ œํ’ˆ ํƒœ๊ทธ + thumbnail: string | null; // ์ œํ’ˆ ์ธ๋„ค์ผ ์ด๋ฏธ์ง€(URL) + photo: string | null; // ์ œํ’ˆ ์ƒ์„ธ ์ด๋ฏธ์ง€(URL) + isSoldOut: boolean; // ์ œํ’ˆ ๋งค์ง„ ์—ฌ๋ถ€ + discountRate: number; // ์ œํ’ˆ ํ• ์ธ์œจ +} + +// ๊ฑฐ๋ž˜ ์„ธ๋ถ€ ๋‚ด์—ญ +interface TransactionDetail { + detailId: string; + user: { + email: string; + displayName: string; + profileImg: string | null; + }; + account: { + bankName: string; + bankCode: string; + accountNumber: string; + }; + product: { + productId: string; + title: string; + price: number; + description: string; + tags: string[]; + thumbnail: string | null; + discountRate: number; + }; + timePaid: string; + isCanceled: boolean; + done: boolean; +} + +interface Bank { + name: string; + code: string; + digits: number[]; + disabled: boolean; +} + +interface AccountsAndBalance { + totalBalance: number; + accounts: UserAccount[]; +} +interface UserAccount { + id: string; + bankName: string; + bankCode: string; + accountNumber: string; + balance: number; + delete?: boolean; +} + +interface ConnectAccount { + bankCode: string; // ์—ฐ๊ฒฐํ•  ์€ํ–‰ ์ฝ”๋“œ (ํ•„์ˆ˜!) + accountNumber: string; // ์—ฐ๊ฒฐํ•  ๊ณ„์ขŒ๋ฒˆํ˜ธ (ํ•„์ˆ˜!) + phoneNumber: string; // ์‚ฌ์šฉ์ž ์ „ํ™”๋ฒˆํ˜ธ (ํ•„์ˆ˜!) + signature: boolean; // ์‚ฌ์šฉ์ž ์„œ๋ช… (ํ•„์ˆ˜!) +} + +interface ProductDetail { + id: string; + title: string; + price: number; + description: string; + tags: string[]; // ์ œํ’ˆ ํƒœ๊ทธ + thumbnail?: string; // ์ œํ’ˆ ์ธ๋„ค์ผ + photo?: string; //์ƒ์„ธ์ด๋ฏธ์ง€ URL + isSoldOut: boolean; // ์ œํ’ˆ ๋งค์ง„์—ฌ๋ถ€ + discountRate: number; +} + +// ๊ด€๋ฆฌ์žํŒจ๋„์—์„œ ์ƒํ’ˆ ๋ชฉ๋ก ์กฐํšŒ์‹œ ๊ฐœ๋ณ„ ์ƒํ’ˆ +interface Product { + id: string; // ์ œํ’ˆ ID + title: string; // ์ œํ’ˆ ์ด๋ฆ„ + price: number; // ์ œํ’ˆ ๊ฐ€๊ฒฉ + description: string; // ์ œํ’ˆ ์„ค๋ช…(์ตœ๋Œ€ 100์ž) + tags: string[]; // ์ œํ’ˆ ํƒœ๊ทธ + thumbnail: string | null; // ์ œํ’ˆ ์ธ๋„ค์ผ ์ด๋ฏธ์ง€(URL) + isSoldOut: boolean; // ์ œํ’ˆ ๋งค์ง„ ์—ฌ๋ถ€ + discountRate: number; // ์ œํ’ˆ ํ• ์ธ์œจ +} + +interface Window { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + my_modal_2: any; +} + +interface ConnectAccountBody { + bankCode: string; + accountNumber: string; + phoneNumber: string; + signature: boolean; +} + +// ์ธ์ฆํ™•์ธ ์„ฑ๊ณต์‹œ ์‘๋‹ต๊ฐ’์˜ ํƒ€์ž… +interface AuthenticateResponseValue { + email: string; + displayName: string; + profileImg: string | null; +} + +// ์‚ฌ์šฉ์ž๊ฐ€ ์ž์‹ ์˜ ์ฃผ๋ฌธ๋ชฉ๋ก์„ ์š”์ฒญํ•˜๋ฉด ๋ฐ›๋Š” ๊ฐœ๋ณ„ ์ฃผ๋ฌธ์ •๋ณด +interface TransactionDetail { + detailId: string; + product: { + productId: string; + title: string; + price: number; + description: string; + tags: string[]; + thumbnail: string | null; + discountRate: number; + }; + reservation: Reservation | null; + timePaid: string; + isCanceled: boolean; + done: boolean; +} diff --git a/index.html b/index.html new file mode 100644 index 00000000..bc08b7c8 --- /dev/null +++ b/index.html @@ -0,0 +1,19 @@ + + + + + + + + ์ถ•๊ตฌํ™”๋Š” ์—ญ์‹œ CRAZY11 + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..ae98a76c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6091 @@ +{ + "name": "kdt5-m5", + "version": "0.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "kdt5-m5", + "version": "0.0.0", + "dependencies": { + "axios": "^1.4.0", + "nuka-carousel": "^6.0.3", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-hot-toast": "^2.4.1", + "react-icons": "^4.9.0", + "react-router-dom": "^6.11.2", + "zustand": "^4.3.8" + }, + "devDependencies": { + "@types/node": "^20.2.5", + "@types/react": "^18.0.37", + "@types/react-dom": "^18.0.11", + "@typescript-eslint/eslint-plugin": "^5.59.0", + "@typescript-eslint/parser": "^5.59.0", + "@vitejs/plugin-react-swc": "^3.0.0", + "autoprefixer": "^10.4.14", + "daisyui": "^3.0.20", + "eslint": "^8.41.0", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.3.4", + "postcss": "^8.4.24", + "prettier": "^2.8.8", + "prettier-plugin-tailwindcss": "^0.3.0", + "tailwindcss": "^3.3.2", + "ts-node": "^10.9.1", + "typescript": "^5.0.2", + "vite": "^4.3.9" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", + "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", + "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", + "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", + "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", + "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", + "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", + "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", + "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", + "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", + "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", + "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", + "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", + "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", + "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", + "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", + "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", + "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", + "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", + "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", + "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", + "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", + "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.5.2", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.41.0.tgz", + "integrity": "sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@remix-run/router": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.6.2.tgz", + "integrity": "sha512-LzqpSrMK/3JBAVBI9u3NWtOhWNw5AMQfrUFYB0+bDHTSw17z++WJLsPsxAuK+oSddsxk4d7F/JcdDPM1M5YAhA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@swc/core": { + "version": "1.3.61", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.61.tgz", + "integrity": "sha512-p58Ltdjo7Yy8CU3zK0cp4/eAgy5qkHs35znGedqVGPiA67cuYZM63DuTfmyrOntMRwQnaFkMLklDAPCizDdDng==", + "dev": true, + "hasInstallScript": true, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.3.61", + "@swc/core-darwin-x64": "1.3.61", + "@swc/core-linux-arm-gnueabihf": "1.3.61", + "@swc/core-linux-arm64-gnu": "1.3.61", + "@swc/core-linux-arm64-musl": "1.3.61", + "@swc/core-linux-x64-gnu": "1.3.61", + "@swc/core-linux-x64-musl": "1.3.61", + "@swc/core-win32-arm64-msvc": "1.3.61", + "@swc/core-win32-ia32-msvc": "1.3.61", + "@swc/core-win32-x64-msvc": "1.3.61" + }, + "peerDependencies": { + "@swc/helpers": "^0.5.0" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.3.61", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.61.tgz", + "integrity": "sha512-Ra1CZIYYyIp/Y64VcKyaLjIPUwT83JmGduvHu8vhUZOvWV4dWL4s5DrcxQVaQJjjb7Z2N/IUYYS55US1TGnxZw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.3.61", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.61.tgz", + "integrity": "sha512-LUia75UByUFkYH1Ddw7IE0X9usNVGJ7aL6+cgOTju7P0dsU0f8h/OGc/GDfp1E4qnKxDCJE+GwDRLoi4SjIxpg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.3.61", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.61.tgz", + "integrity": "sha512-aalPlicYxHAn2PxNlo3JFEZkMXzCtUwjP27AgMqnfV4cSz7Omo56OtC+413e/kGyCH86Er9gJRQQsxNKP8Qbsg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.3.61", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.61.tgz", + "integrity": "sha512-9hGdsbQrYNPo1c7YzWF57yl17bsIuuEQi3I1fOFSv3puL3l5M/C/oCD0Bz6IdKh6mEDC5UNJE4LWtV1gFA995A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.3.61", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.61.tgz", + "integrity": "sha512-mVmcNfFQRP4SYbGC08IPB3B9Xox+VpGIQqA3Qg7LMCcejLAQLi4Lfe8CDvvBPlQzXHso0Cv+BicJnQVKs8JLOA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.3.61", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.61.tgz", + "integrity": "sha512-ZkRHs7GEikN6JiVL1/stvq9BVHKrSKoRn9ulVK2hMr+mAGNOKm3Y06NSzOO+BWwMaFOgnO2dWlszCUICsQ0kpg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.3.61", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.61.tgz", + "integrity": "sha512-zK7VqQ5JlK20+7fxI4AgvIUckeZyX0XIbliGXNMR3i+39SJq1vs9scYEmq8VnAfvNdMU5BG+DewbFJlMfCtkxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.3.61", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.61.tgz", + "integrity": "sha512-e9kVVPk5iVNhO41TvLvcExDHn5iATQ5/M4U7/CdcC7s0fK19TKSEUqkdoTLIJvHBFhgR7w3JJSErfnauO0xXoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.3.61", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.61.tgz", + "integrity": "sha512-7cJULfa6HvKqvFh6M/f7mKiNRhE2AjgFUCZfdOuy5r8vbtpk+qBK94TXwaDjJYDUGKzDVZw/tJ1eN4Y9n9Ls/Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.3.61", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.61.tgz", + "integrity": "sha512-Jx8S+21WcKF/wlhW+sYpystWUyymDTEsbBpOgBRpXZelakVcNBCIIYSZOKW/A9PwWTpu6S8yvbs9nUOzKiVPqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.2.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz", + "integrity": "sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==", + "dev": true + }, + "node_modules/@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", + "dev": true + }, + "node_modules/@types/react": { + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.7.tgz", + "integrity": "sha512-ojrXpSH2XFCmHm7Jy3q44nXDyN54+EYKP2lBhJ2bqfyPj6cIUW/FZW/Csdia34NQgq7KYcAlHi5184m4X88+yw==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.2.4", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.4.tgz", + "integrity": "sha512-G2mHoTMTL4yoydITgOGwWdWMVd8sNgyEP85xVmMKAPUBwQWm9wBPQUmvbeF4V3WBY1P7mmL4BkjQ0SqUpf1snw==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.8.tgz", + "integrity": "sha512-JDMOmhXteJ4WVKOiHXGCoB96ADWg9q7efPWHRViT/f09bA8XOMLAVHHju3l0MkZnG1izaWXYmgvQcUjTRcpShQ==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.59.8", + "@typescript-eslint/type-utils": "5.59.8", + "@typescript-eslint/utils": "5.59.8", + "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.8.tgz", + "integrity": "sha512-AnR19RjJcpjoeGojmwZtCwBX/RidqDZtzcbG3xHrmz0aHHoOcbWnpDllenRDmDvsV0RQ6+tbb09/kyc+UT9Orw==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.59.8", + "@typescript-eslint/types": "5.59.8", + "@typescript-eslint/typescript-estree": "5.59.8", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.8.tgz", + "integrity": "sha512-/w08ndCYI8gxGf+9zKf1vtx/16y8MHrZs5/tnjHhMLNSixuNcJavSX4wAiPf4aS5x41Es9YPCn44MIe4cxIlig==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.8", + "@typescript-eslint/visitor-keys": "5.59.8" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.8.tgz", + "integrity": "sha512-+5M518uEIHFBy3FnyqZUF3BMP+AXnYn4oyH8RF012+e7/msMY98FhGL5SrN29NQ9xDgvqCgYnsOiKp1VjZ/fpA==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.59.8", + "@typescript-eslint/utils": "5.59.8", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.8.tgz", + "integrity": "sha512-+uWuOhBTj/L6awoWIg0BlWy0u9TyFpCHrAuQ5bNfxDaZ1Ppb3mx6tUigc74LHcbHpOHuOTOJrBoAnhdHdaea1w==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.8.tgz", + "integrity": "sha512-Jy/lPSDJGNow14vYu6IrW790p7HIf/SOV1Bb6lZ7NUkLc2iB2Z9elESmsaUtLw8kVqogSbtLH9tut5GCX1RLDg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.8", + "@typescript-eslint/visitor-keys": "5.59.8", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.8.tgz", + "integrity": "sha512-Tr65630KysnNn9f9G7ROF3w1b5/7f6QVCJ+WK9nhIocWmx9F+TmCAcglF26Vm7z8KCTwoKcNEBZrhlklla3CKg==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.59.8", + "@typescript-eslint/types": "5.59.8", + "@typescript-eslint/typescript-estree": "5.59.8", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.8.tgz", + "integrity": "sha512-pJhi2ms0x0xgloT7xYabil3SGGlojNNKjK/q6dB3Ey0uJLMjK2UDGJvHieiyJVW/7C3KI+Z4Q3pEHkm4ejA+xQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.8", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react-swc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.3.1.tgz", + "integrity": "sha512-ZoYjGxMniXP7X+5ry/W1tpY7w0OeLUEsBF5RHFPmAhpgwwNWie8OF4056MRXRi9QgvYYoZPDzdOXGK3wlCoTfQ==", + "dev": true, + "dependencies": { + "@swc/core": "^1.3.56" + }, + "peerDependencies": { + "vite": "^4" + } + }, + "node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/autoprefixer": { + "version": "10.4.14", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", + "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + ], + "dependencies": { + "browserslist": "^4.21.5", + "caniuse-lite": "^1.0.30001464", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axios": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.21.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.7.tgz", + "integrity": "sha512-BauCXrQ7I2ftSqd2mvKHGo85XR0u7Ru3C/Hxsy/0TkfCtjrmAbPdzLGasmoiBxplpDXlPvdjX9u7srIMfgasNA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001489", + "electron-to-chromium": "^1.4.411", + "node-releases": "^2.0.12", + "update-browserslist-db": "^1.0.11" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001495", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001495.tgz", + "integrity": "sha512-F6x5IEuigtUfU5ZMQK2jsy5JqUUlEFRVZq8bO2a+ysq5K7jD6PPc9YXZj78xDNS3uNchesp1Jw47YXEqr+Viyg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-selector-tokenizer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz", + "integrity": "sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + }, + "node_modules/daisyui": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.1.0.tgz", + "integrity": "sha512-G4dz/bRZVvlhQ/FtezXSg0rXOXzDJ0PcnMqeLSwCYNWXxf46fNJ8LWeV5qwsYOdJbXiXoZLnwyy+BsNoWZ+Bjg==", + "dev": true, + "dependencies": { + "colord": "^2.9", + "css-selector-tokenizer": "^0.8", + "postcss-js": "^4", + "tailwindcss": "^3" + }, + "engines": { + "node": ">=16.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/daisyui" + }, + "peerDependencies": { + "postcss": "^8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.425", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.425.tgz", + "integrity": "sha512-wv1NufHxu11zfDbY4fglYQApMswleE9FL/DSeyOyauVXDZ+Kco96JK/tPfBUaDqfRarYp2WH2hJ/5UnVywp9Jg==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", + "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.17.19", + "@esbuild/android-arm64": "0.17.19", + "@esbuild/android-x64": "0.17.19", + "@esbuild/darwin-arm64": "0.17.19", + "@esbuild/darwin-x64": "0.17.19", + "@esbuild/freebsd-arm64": "0.17.19", + "@esbuild/freebsd-x64": "0.17.19", + "@esbuild/linux-arm": "0.17.19", + "@esbuild/linux-arm64": "0.17.19", + "@esbuild/linux-ia32": "0.17.19", + "@esbuild/linux-loong64": "0.17.19", + "@esbuild/linux-mips64el": "0.17.19", + "@esbuild/linux-ppc64": "0.17.19", + "@esbuild/linux-riscv64": "0.17.19", + "@esbuild/linux-s390x": "0.17.19", + "@esbuild/linux-x64": "0.17.19", + "@esbuild/netbsd-x64": "0.17.19", + "@esbuild/openbsd-x64": "0.17.19", + "@esbuild/sunos-x64": "0.17.19", + "@esbuild/win32-arm64": "0.17.19", + "@esbuild/win32-ia32": "0.17.19", + "@esbuild/win32-x64": "0.17.19" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.41.0.tgz", + "integrity": "sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.41.0", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", + "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", + "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "eslint": ">=7.28.0", + "prettier": ">=2.0.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.3.5.tgz", + "integrity": "sha512-61qNIsc7fo9Pp/mju0J83kzvLm0Bsayu7OQSLEoJxLDCBjIIyb87bkzufoOvdDxLkSlMfkF7UxomC4+eztUBSA==", + "dev": true, + "peerDependencies": { + "eslint": ">=7" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/espree": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "dev": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/goober": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.13.tgz", + "integrity": "sha512-jFj3BQeleOoy7t93E9rZ2de+ScC4lQICLwiAQmKMg9F6roKGaLSHoCDYKkWlSafg138jejvq/mTdvmnwDQgqoQ==", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jiti": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz", + "integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", + "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nuka-carousel": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/nuka-carousel/-/nuka-carousel-6.0.3.tgz", + "integrity": "sha512-DYxCTp4xIv9/vfrfOr9FAmYizMr7AefL7mbqsBaBTwdrZ77mAsR/SNZtk+/KHJSuH+9gZLrugj/RC6qM8MJr+Q==", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.4.24", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz", + "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", + "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", + "dev": true, + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^2.1.1" + }, + "engines": { + "node": ">= 14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.11" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", + "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/prettier-plugin-tailwindcss": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.3.0.tgz", + "integrity": "sha512-009/Xqdy7UmkcTBpwlq7jsViDqXAYSOMLDrHAdTMlVZOrKfM2o9Ci7EMWTMZ7SkKBFTG04UM9F9iM2+4i6boDA==", + "dev": true, + "engines": { + "node": ">=12.17.0" + }, + "peerDependencies": { + "@ianvs/prettier-plugin-sort-imports": "*", + "@prettier/plugin-pug": "*", + "@shopify/prettier-plugin-liquid": "*", + "@shufo/prettier-plugin-blade": "*", + "@trivago/prettier-plugin-sort-imports": "*", + "prettier": ">=2.2.0", + "prettier-plugin-astro": "*", + "prettier-plugin-css-order": "*", + "prettier-plugin-import-sort": "*", + "prettier-plugin-jsdoc": "*", + "prettier-plugin-marko": "*", + "prettier-plugin-organize-attributes": "*", + "prettier-plugin-organize-imports": "*", + "prettier-plugin-style-order": "*", + "prettier-plugin-svelte": "*", + "prettier-plugin-twig-melody": "*" + }, + "peerDependenciesMeta": { + "@ianvs/prettier-plugin-sort-imports": { + "optional": true + }, + "@prettier/plugin-pug": { + "optional": true + }, + "@shopify/prettier-plugin-liquid": { + "optional": true + }, + "@shufo/prettier-plugin-blade": { + "optional": true + }, + "@trivago/prettier-plugin-sort-imports": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + }, + "prettier-plugin-css-order": { + "optional": true + }, + "prettier-plugin-import-sort": { + "optional": true + }, + "prettier-plugin-jsdoc": { + "optional": true + }, + "prettier-plugin-marko": { + "optional": true + }, + "prettier-plugin-organize-attributes": { + "optional": true + }, + "prettier-plugin-organize-imports": { + "optional": true + }, + "prettier-plugin-style-order": { + "optional": true + }, + "prettier-plugin-svelte": { + "optional": true + }, + "prettier-plugin-twig-melody": { + "optional": true + } + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-hot-toast": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz", + "integrity": "sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==", + "dependencies": { + "goober": "^2.1.10" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/react-icons": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.9.0.tgz", + "integrity": "sha512-ijUnFr//ycebOqujtqtV9PFS7JjhWg0QU6ykURVHuL4cbofvRCf3f6GMn9+fBktEFQOIVZnuAYLZdiyadRQRFg==", + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-router": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.11.2.tgz", + "integrity": "sha512-74z9xUSaSX07t3LM+pS6Un0T55ibUE/79CzfZpy5wsPDZaea1F8QkrsiyRnA2YQ7LwE/umaydzXZV80iDCPkMg==", + "dependencies": { + "@remix-run/router": "1.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.11.2.tgz", + "integrity": "sha512-JNbKtAeh1VSJQnH6RvBDNhxNwemRj7KxCzc5jb7zvDSKRnPWIFj9pO+eXqjM69gQJ0r46hSz1x4l9y0651DKWw==", + "dependencies": { + "@remix-run/router": "1.6.2", + "react-router": "6.11.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "dev": true, + "dependencies": { + "is-core-module": "^2.11.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.23.0.tgz", + "integrity": "sha512-h31UlwEi7FHihLe1zbk+3Q7z1k/84rb9BSwmBSr/XjOCEaBJ2YyedQDuM0t/kfOS0IxM+vk1/zI9XxYj9V+NJQ==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sucrase": { + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz", + "integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "7.1.6", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz", + "integrity": "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==", + "dev": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.12", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.18.2", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "postcss-value-parser": "^4.2.0", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=12.20" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/vite": { + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz", + "integrity": "sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==", + "dev": true, + "dependencies": { + "esbuild": "^0.17.5", + "postcss": "^8.4.23", + "rollup": "^3.21.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@types/node": ">= 14", + "less": "*", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yaml": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", + "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zustand": { + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.3.8.tgz", + "integrity": "sha512-4h28KCkHg5ii/wcFFJ5Fp+k1J3gJoasaIbppdgZFO4BPJnsNxL0mQXBSFgOgAdCdBj35aDTPvdAJReTMntFPGg==", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "immer": ">=9.0", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + } + }, + "dependencies": { + "@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true + }, + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } + } + }, + "@esbuild/android-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", + "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", + "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", + "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", + "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", + "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", + "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", + "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", + "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", + "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", + "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", + "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", + "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", + "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", + "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", + "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", + "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", + "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", + "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", + "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", + "dev": true, + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", + "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", + "dev": true, + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", + "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", + "dev": true, + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", + "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", + "dev": true, + "optional": true + }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.5.2", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + } + }, + "@eslint/js": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.41.0.tgz", + "integrity": "sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==", + "dev": true + }, + "@humanwhocodes/config-array": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + }, + "dependencies": { + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + } + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@remix-run/router": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.6.2.tgz", + "integrity": "sha512-LzqpSrMK/3JBAVBI9u3NWtOhWNw5AMQfrUFYB0+bDHTSw17z++WJLsPsxAuK+oSddsxk4d7F/JcdDPM1M5YAhA==" + }, + "@swc/core": { + "version": "1.3.61", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.61.tgz", + "integrity": "sha512-p58Ltdjo7Yy8CU3zK0cp4/eAgy5qkHs35znGedqVGPiA67cuYZM63DuTfmyrOntMRwQnaFkMLklDAPCizDdDng==", + "dev": true, + "requires": { + "@swc/core-darwin-arm64": "1.3.61", + "@swc/core-darwin-x64": "1.3.61", + "@swc/core-linux-arm-gnueabihf": "1.3.61", + "@swc/core-linux-arm64-gnu": "1.3.61", + "@swc/core-linux-arm64-musl": "1.3.61", + "@swc/core-linux-x64-gnu": "1.3.61", + "@swc/core-linux-x64-musl": "1.3.61", + "@swc/core-win32-arm64-msvc": "1.3.61", + "@swc/core-win32-ia32-msvc": "1.3.61", + "@swc/core-win32-x64-msvc": "1.3.61" + } + }, + "@swc/core-darwin-arm64": { + "version": "1.3.61", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.61.tgz", + "integrity": "sha512-Ra1CZIYYyIp/Y64VcKyaLjIPUwT83JmGduvHu8vhUZOvWV4dWL4s5DrcxQVaQJjjb7Z2N/IUYYS55US1TGnxZw==", + "dev": true, + "optional": true + }, + "@swc/core-darwin-x64": { + "version": "1.3.61", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.61.tgz", + "integrity": "sha512-LUia75UByUFkYH1Ddw7IE0X9usNVGJ7aL6+cgOTju7P0dsU0f8h/OGc/GDfp1E4qnKxDCJE+GwDRLoi4SjIxpg==", + "dev": true, + "optional": true + }, + "@swc/core-linux-arm-gnueabihf": { + "version": "1.3.61", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.61.tgz", + "integrity": "sha512-aalPlicYxHAn2PxNlo3JFEZkMXzCtUwjP27AgMqnfV4cSz7Omo56OtC+413e/kGyCH86Er9gJRQQsxNKP8Qbsg==", + "dev": true, + "optional": true + }, + "@swc/core-linux-arm64-gnu": { + "version": "1.3.61", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.61.tgz", + "integrity": "sha512-9hGdsbQrYNPo1c7YzWF57yl17bsIuuEQi3I1fOFSv3puL3l5M/C/oCD0Bz6IdKh6mEDC5UNJE4LWtV1gFA995A==", + "dev": true, + "optional": true + }, + "@swc/core-linux-arm64-musl": { + "version": "1.3.61", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.61.tgz", + "integrity": "sha512-mVmcNfFQRP4SYbGC08IPB3B9Xox+VpGIQqA3Qg7LMCcejLAQLi4Lfe8CDvvBPlQzXHso0Cv+BicJnQVKs8JLOA==", + "dev": true, + "optional": true + }, + "@swc/core-linux-x64-gnu": { + "version": "1.3.61", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.61.tgz", + "integrity": "sha512-ZkRHs7GEikN6JiVL1/stvq9BVHKrSKoRn9ulVK2hMr+mAGNOKm3Y06NSzOO+BWwMaFOgnO2dWlszCUICsQ0kpg==", + "dev": true, + "optional": true + }, + "@swc/core-linux-x64-musl": { + "version": "1.3.61", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.61.tgz", + "integrity": "sha512-zK7VqQ5JlK20+7fxI4AgvIUckeZyX0XIbliGXNMR3i+39SJq1vs9scYEmq8VnAfvNdMU5BG+DewbFJlMfCtkxQ==", + "dev": true, + "optional": true + }, + "@swc/core-win32-arm64-msvc": { + "version": "1.3.61", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.61.tgz", + "integrity": "sha512-e9kVVPk5iVNhO41TvLvcExDHn5iATQ5/M4U7/CdcC7s0fK19TKSEUqkdoTLIJvHBFhgR7w3JJSErfnauO0xXoA==", + "dev": true, + "optional": true + }, + "@swc/core-win32-ia32-msvc": { + "version": "1.3.61", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.61.tgz", + "integrity": "sha512-7cJULfa6HvKqvFh6M/f7mKiNRhE2AjgFUCZfdOuy5r8vbtpk+qBK94TXwaDjJYDUGKzDVZw/tJ1eN4Y9n9Ls/Q==", + "dev": true, + "optional": true + }, + "@swc/core-win32-x64-msvc": { + "version": "1.3.61", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.61.tgz", + "integrity": "sha512-Jx8S+21WcKF/wlhW+sYpystWUyymDTEsbBpOgBRpXZelakVcNBCIIYSZOKW/A9PwWTpu6S8yvbs9nUOzKiVPqA==", + "dev": true, + "optional": true + }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "@types/json-schema": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "dev": true + }, + "@types/node": { + "version": "20.2.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz", + "integrity": "sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==", + "dev": true + }, + "@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", + "dev": true + }, + "@types/react": { + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.7.tgz", + "integrity": "sha512-ojrXpSH2XFCmHm7Jy3q44nXDyN54+EYKP2lBhJ2bqfyPj6cIUW/FZW/Csdia34NQgq7KYcAlHi5184m4X88+yw==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-dom": { + "version": "18.2.4", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.4.tgz", + "integrity": "sha512-G2mHoTMTL4yoydITgOGwWdWMVd8sNgyEP85xVmMKAPUBwQWm9wBPQUmvbeF4V3WBY1P7mmL4BkjQ0SqUpf1snw==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/scheduler": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", + "dev": true + }, + "@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.8.tgz", + "integrity": "sha512-JDMOmhXteJ4WVKOiHXGCoB96ADWg9q7efPWHRViT/f09bA8XOMLAVHHju3l0MkZnG1izaWXYmgvQcUjTRcpShQ==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.59.8", + "@typescript-eslint/type-utils": "5.59.8", + "@typescript-eslint/utils": "5.59.8", + "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/parser": { + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.8.tgz", + "integrity": "sha512-AnR19RjJcpjoeGojmwZtCwBX/RidqDZtzcbG3xHrmz0aHHoOcbWnpDllenRDmDvsV0RQ6+tbb09/kyc+UT9Orw==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.59.8", + "@typescript-eslint/types": "5.59.8", + "@typescript-eslint/typescript-estree": "5.59.8", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.8.tgz", + "integrity": "sha512-/w08ndCYI8gxGf+9zKf1vtx/16y8MHrZs5/tnjHhMLNSixuNcJavSX4wAiPf4aS5x41Es9YPCn44MIe4cxIlig==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.59.8", + "@typescript-eslint/visitor-keys": "5.59.8" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.8.tgz", + "integrity": "sha512-+5M518uEIHFBy3FnyqZUF3BMP+AXnYn4oyH8RF012+e7/msMY98FhGL5SrN29NQ9xDgvqCgYnsOiKp1VjZ/fpA==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "5.59.8", + "@typescript-eslint/utils": "5.59.8", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/types": { + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.8.tgz", + "integrity": "sha512-+uWuOhBTj/L6awoWIg0BlWy0u9TyFpCHrAuQ5bNfxDaZ1Ppb3mx6tUigc74LHcbHpOHuOTOJrBoAnhdHdaea1w==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.8.tgz", + "integrity": "sha512-Jy/lPSDJGNow14vYu6IrW790p7HIf/SOV1Bb6lZ7NUkLc2iB2Z9elESmsaUtLw8kVqogSbtLH9tut5GCX1RLDg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.59.8", + "@typescript-eslint/visitor-keys": "5.59.8", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/utils": { + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.8.tgz", + "integrity": "sha512-Tr65630KysnNn9f9G7ROF3w1b5/7f6QVCJ+WK9nhIocWmx9F+TmCAcglF26Vm7z8KCTwoKcNEBZrhlklla3CKg==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.59.8", + "@typescript-eslint/types": "5.59.8", + "@typescript-eslint/typescript-estree": "5.59.8", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.8.tgz", + "integrity": "sha512-pJhi2ms0x0xgloT7xYabil3SGGlojNNKjK/q6dB3Ey0uJLMjK2UDGJvHieiyJVW/7C3KI+Z4Q3pEHkm4ejA+xQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.59.8", + "eslint-visitor-keys": "^3.3.0" + } + }, + "@vitejs/plugin-react-swc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.3.1.tgz", + "integrity": "sha512-ZoYjGxMniXP7X+5ry/W1tpY7w0OeLUEsBF5RHFPmAhpgwwNWie8OF4056MRXRi9QgvYYoZPDzdOXGK3wlCoTfQ==", + "dev": true, + "requires": { + "@swc/core": "^1.3.56" + } + }, + "acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "autoprefixer": { + "version": "10.4.14", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", + "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "dev": true, + "requires": { + "browserslist": "^4.21.5", + "caniuse-lite": "^1.0.30001464", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + } + }, + "axios": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.21.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.7.tgz", + "integrity": "sha512-BauCXrQ7I2ftSqd2mvKHGo85XR0u7Ru3C/Hxsy/0TkfCtjrmAbPdzLGasmoiBxplpDXlPvdjX9u7srIMfgasNA==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001489", + "electron-to-chromium": "^1.4.411", + "node-releases": "^2.0.12", + "update-browserslist-db": "^1.0.11" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001495", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001495.tgz", + "integrity": "sha512-F6x5IEuigtUfU5ZMQK2jsy5JqUUlEFRVZq8bO2a+ysq5K7jD6PPc9YXZj78xDNS3uNchesp1Jw47YXEqr+Viyg==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "css-selector-tokenizer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz", + "integrity": "sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + }, + "daisyui": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.1.0.tgz", + "integrity": "sha512-G4dz/bRZVvlhQ/FtezXSg0rXOXzDJ0PcnMqeLSwCYNWXxf46fNJ8LWeV5qwsYOdJbXiXoZLnwyy+BsNoWZ+Bjg==", + "dev": true, + "requires": { + "colord": "^2.9", + "css-selector-tokenizer": "^0.8", + "postcss-js": "^4", + "tailwindcss": "^3" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "electron-to-chromium": { + "version": "1.4.425", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.425.tgz", + "integrity": "sha512-wv1NufHxu11zfDbY4fglYQApMswleE9FL/DSeyOyauVXDZ+Kco96JK/tPfBUaDqfRarYp2WH2hJ/5UnVywp9Jg==", + "dev": true + }, + "esbuild": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", + "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", + "dev": true, + "requires": { + "@esbuild/android-arm": "0.17.19", + "@esbuild/android-arm64": "0.17.19", + "@esbuild/android-x64": "0.17.19", + "@esbuild/darwin-arm64": "0.17.19", + "@esbuild/darwin-x64": "0.17.19", + "@esbuild/freebsd-arm64": "0.17.19", + "@esbuild/freebsd-x64": "0.17.19", + "@esbuild/linux-arm": "0.17.19", + "@esbuild/linux-arm64": "0.17.19", + "@esbuild/linux-ia32": "0.17.19", + "@esbuild/linux-loong64": "0.17.19", + "@esbuild/linux-mips64el": "0.17.19", + "@esbuild/linux-ppc64": "0.17.19", + "@esbuild/linux-riscv64": "0.17.19", + "@esbuild/linux-s390x": "0.17.19", + "@esbuild/linux-x64": "0.17.19", + "@esbuild/netbsd-x64": "0.17.19", + "@esbuild/openbsd-x64": "0.17.19", + "@esbuild/sunos-x64": "0.17.19", + "@esbuild/win32-arm64": "0.17.19", + "@esbuild/win32-ia32": "0.17.19", + "@esbuild/win32-x64": "0.17.19" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.41.0.tgz", + "integrity": "sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.41.0", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "dependencies": { + "eslint-scope": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "eslint-config-prettier": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", + "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", + "dev": true, + "requires": {} + }, + "eslint-plugin-prettier": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", + "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, + "eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "dev": true, + "requires": {} + }, + "eslint-plugin-react-refresh": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.3.5.tgz", + "integrity": "sha512-61qNIsc7fo9Pp/mju0J83kzvLm0Bsayu7OQSLEoJxLDCBjIIyb87bkzufoOvdDxLkSlMfkF7UxomC4+eztUBSA==", + "dev": true, + "requires": {} + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "dev": true + }, + "espree": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "dev": true, + "requires": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + } + }, + "esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, + "fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "goober": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.13.tgz", + "integrity": "sha512-jFj3BQeleOoy7t93E9rZ2de+ScC4lQICLwiAQmKMg9F6roKGaLSHoCDYKkWlSafg138jejvq/mTdvmnwDQgqoQ==", + "requires": {} + }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-core-module": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "jiti": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz", + "integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true + }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "requires": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "node-releases": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", + "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true + }, + "nuka-carousel": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/nuka-carousel/-/nuka-carousel-6.0.3.tgz", + "integrity": "sha512-DYxCTp4xIv9/vfrfOr9FAmYizMr7AefL7mbqsBaBTwdrZ77mAsR/SNZtk+/KHJSuH+9gZLrugj/RC6qM8MJr+Q==", + "requires": {} + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true + }, + "object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true + }, + "pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true + }, + "postcss": { + "version": "8.4.24", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz", + "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==", + "dev": true, + "requires": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, + "postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "requires": { + "camelcase-css": "^2.0.1" + } + }, + "postcss-load-config": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", + "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", + "dev": true, + "requires": { + "lilconfig": "^2.0.5", + "yaml": "^2.1.1" + } + }, + "postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.11" + } + }, + "postcss-selector-parser": { + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", + "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, + "prettier-plugin-tailwindcss": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.3.0.tgz", + "integrity": "sha512-009/Xqdy7UmkcTBpwlq7jsViDqXAYSOMLDrHAdTMlVZOrKfM2o9Ci7EMWTMZ7SkKBFTG04UM9F9iM2+4i6boDA==", + "dev": true, + "requires": {} + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "requires": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + } + }, + "react-hot-toast": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz", + "integrity": "sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==", + "requires": { + "goober": "^2.1.10" + } + }, + "react-icons": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.9.0.tgz", + "integrity": "sha512-ijUnFr//ycebOqujtqtV9PFS7JjhWg0QU6ykURVHuL4cbofvRCf3f6GMn9+fBktEFQOIVZnuAYLZdiyadRQRFg==", + "requires": {} + }, + "react-router": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.11.2.tgz", + "integrity": "sha512-74z9xUSaSX07t3LM+pS6Un0T55ibUE/79CzfZpy5wsPDZaea1F8QkrsiyRnA2YQ7LwE/umaydzXZV80iDCPkMg==", + "requires": { + "@remix-run/router": "1.6.2" + } + }, + "react-router-dom": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.11.2.tgz", + "integrity": "sha512-JNbKtAeh1VSJQnH6RvBDNhxNwemRj7KxCzc5jb7zvDSKRnPWIFj9pO+eXqjM69gQJ0r46hSz1x4l9y0651DKWw==", + "requires": { + "@remix-run/router": "1.6.2", + "react-router": "6.11.2" + } + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "requires": { + "pify": "^2.3.0" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "resolve": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "dev": true, + "requires": { + "is-core-module": "^2.11.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "rollup": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.23.0.tgz", + "integrity": "sha512-h31UlwEi7FHihLe1zbk+3Q7z1k/84rb9BSwmBSr/XjOCEaBJ2YyedQDuM0t/kfOS0IxM+vk1/zI9XxYj9V+NJQ==", + "dev": true, + "requires": { + "fsevents": "~2.3.2" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "sucrase": { + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz", + "integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "7.1.6", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "tailwindcss": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz", + "integrity": "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==", + "dev": true, + "requires": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.12", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.18.2", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "postcss-value-parser": "^4.2.0", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, + "ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "dependencies": { + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + } + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "typescript": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "dev": true + }, + "update-browserslist-db": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "requires": {} + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "vite": { + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz", + "integrity": "sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==", + "dev": true, + "requires": { + "esbuild": "^0.17.5", + "fsevents": "~2.3.2", + "postcss": "^8.4.23", + "rollup": "^3.21.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yaml": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", + "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "dev": true + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + }, + "zustand": { + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.3.8.tgz", + "integrity": "sha512-4h28KCkHg5ii/wcFFJ5Fp+k1J3gJoasaIbppdgZFO4BPJnsNxL0mQXBSFgOgAdCdBj35aDTPvdAJReTMntFPGg==", + "requires": { + "use-sync-external-store": "1.2.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..66129049 --- /dev/null +++ b/package.json @@ -0,0 +1,43 @@ +{ + "name": "kdt5-m5", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" + }, + "dependencies": { + "nuka-carousel": "^6.0.3", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-hot-toast": "^2.4.1", + "react-icons": "^4.9.0", + "react-router-dom": "^6.11.2", + "zustand": "^4.3.8" + }, + "devDependencies": { + "@types/node": "^20.2.5", + "@types/react": "^18.0.37", + "@types/react-dom": "^18.0.11", + "@typescript-eslint/eslint-plugin": "^5.59.0", + "@typescript-eslint/parser": "^5.59.0", + "@vitejs/plugin-react-swc": "^3.0.0", + "autoprefixer": "^10.4.14", + "daisyui": "^3.0.20", + "eslint": "^8.41.0", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.3.4", + "postcss": "^8.4.24", + "prettier": "^2.8.8", + "prettier-plugin-tailwindcss": "^0.3.0", + "tailwindcss": "^3.3.2", + "ts-node": "^10.9.1", + "typescript": "^5.0.2", + "vite": "^4.3.9" + } +} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 00000000..2e7af2b7 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/public/defaultProfile.jpg b/public/defaultProfile.jpg new file mode 100644 index 00000000..c146f1ce Binary files /dev/null and b/public/defaultProfile.jpg differ diff --git a/public/defaultThumb.jpg b/public/defaultThumb.jpg new file mode 100644 index 00000000..8e84d66e Binary files /dev/null and b/public/defaultThumb.jpg differ diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 00000000..36f1309c Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/mainIcon.png b/public/mainIcon.png new file mode 100644 index 00000000..6f44fa07 Binary files /dev/null and b/public/mainIcon.png differ diff --git a/public/mainlogo.png b/public/mainlogo.png new file mode 100644 index 00000000..93ec14a7 Binary files /dev/null and b/public/mainlogo.png differ diff --git a/public/products/dummy.jpg b/public/products/dummy.jpg new file mode 100644 index 00000000..65518424 Binary files /dev/null and b/public/products/dummy.jpg differ diff --git a/public/readme/main.png b/public/readme/main.png new file mode 100644 index 00000000..cd8fdd31 Binary files /dev/null and b/public/readme/main.png differ diff --git "a/public/readme/\352\261\260\353\236\230\353\202\264\354\227\255.png" "b/public/readme/\352\261\260\353\236\230\353\202\264\354\227\255.png" new file mode 100644 index 00000000..e8242584 Binary files /dev/null and "b/public/readme/\352\261\260\353\236\230\353\202\264\354\227\255.png" differ diff --git "a/public/readme/\352\262\200\354\203\211.png" "b/public/readme/\352\262\200\354\203\211.png" new file mode 100644 index 00000000..42145feb Binary files /dev/null and "b/public/readme/\352\262\200\354\203\211.png" differ diff --git "a/public/readme/\352\263\204\354\242\214 \354\241\260\355\232\214.png" "b/public/readme/\352\263\204\354\242\214 \354\241\260\355\232\214.png" new file mode 100644 index 00000000..1c705d29 Binary files /dev/null and "b/public/readme/\352\263\204\354\242\214 \354\241\260\355\232\214.png" differ diff --git "a/public/readme/\352\263\204\354\242\214\354\227\260\352\262\260.png" "b/public/readme/\352\263\204\354\242\214\354\227\260\352\262\260.png" new file mode 100644 index 00000000..5ef57023 Binary files /dev/null and "b/public/readme/\352\263\204\354\242\214\354\227\260\352\262\260.png" differ diff --git "a/public/readme/\352\265\254\353\247\244\353\202\264\354\227\255.png" "b/public/readme/\352\265\254\353\247\244\353\202\264\354\227\255.png" new file mode 100644 index 00000000..acd56977 Binary files /dev/null and "b/public/readme/\352\265\254\353\247\244\353\202\264\354\227\255.png" differ diff --git "a/public/readme/\353\202\264\354\240\225\353\263\264.png" "b/public/readme/\353\202\264\354\240\225\353\263\264.png" new file mode 100644 index 00000000..e992c214 Binary files /dev/null and "b/public/readme/\353\202\264\354\240\225\353\263\264.png" differ diff --git "a/public/readme/\353\241\234\352\267\270\354\235\270.png" "b/public/readme/\353\241\234\352\267\270\354\235\270.png" new file mode 100644 index 00000000..366257c9 Binary files /dev/null and "b/public/readme/\353\241\234\352\267\270\354\235\270.png" differ diff --git "a/public/readme/\353\241\234\353\224\251.gif" "b/public/readme/\353\241\234\353\224\251.gif" new file mode 100644 index 00000000..b3ad6fe5 Binary files /dev/null and "b/public/readme/\353\241\234\353\224\251.gif" differ diff --git "a/public/readme/\353\252\250\353\223\240\354\240\234\355\222\210.png" "b/public/readme/\353\252\250\353\223\240\354\240\234\355\222\210.png" new file mode 100644 index 00000000..3b704b3c Binary files /dev/null and "b/public/readme/\353\252\250\353\223\240\354\240\234\355\222\210.png" differ diff --git "a/public/readme/\354\203\201\354\204\270\355\216\230\354\235\264\354\247\200.png" "b/public/readme/\354\203\201\354\204\270\355\216\230\354\235\264\354\247\200.png" new file mode 100644 index 00000000..ef1d0954 Binary files /dev/null and "b/public/readme/\354\203\201\354\204\270\355\216\230\354\235\264\354\247\200.png" differ diff --git "a/public/readme/\354\203\201\355\222\210\352\264\200\353\246\254.png" "b/public/readme/\354\203\201\355\222\210\352\264\200\353\246\254.png" new file mode 100644 index 00000000..613234c1 Binary files /dev/null and "b/public/readme/\354\203\201\355\222\210\352\264\200\353\246\254.png" differ diff --git "a/public/readme/\354\203\201\355\222\210\354\210\230\354\240\225.png" "b/public/readme/\354\203\201\355\222\210\354\210\230\354\240\225.png" new file mode 100644 index 00000000..92dca3e7 Binary files /dev/null and "b/public/readme/\354\203\201\355\222\210\354\210\230\354\240\225.png" differ diff --git "a/public/readme/\354\203\201\355\222\210\354\266\224\352\260\200.png" "b/public/readme/\354\203\201\355\222\210\354\266\224\352\260\200.png" new file mode 100644 index 00000000..3d1d09fa Binary files /dev/null and "b/public/readme/\354\203\201\355\222\210\354\266\224\352\260\200.png" differ diff --git "a/public/readme/\354\227\260\352\264\200\354\203\201\355\222\210.png" "b/public/readme/\354\227\260\352\264\200\354\203\201\355\222\210.png" new file mode 100644 index 00000000..d807c1cd Binary files /dev/null and "b/public/readme/\354\227\260\352\264\200\354\203\201\355\222\210.png" differ diff --git "a/public/readme/\354\271\264\355\205\214\352\263\240\353\246\254\353\263\204.png" "b/public/readme/\354\271\264\355\205\214\352\263\240\353\246\254\353\263\204.png" new file mode 100644 index 00000000..7e523c6d Binary files /dev/null and "b/public/readme/\354\271\264\355\205\214\352\263\240\353\246\254\353\263\204.png" differ diff --git "a/public/readme/\355\232\214\354\233\220\352\260\200\354\236\205.png" "b/public/readme/\355\232\214\354\233\220\352\260\200\354\236\205.png" new file mode 100644 index 00000000..b330415b Binary files /dev/null and "b/public/readme/\355\232\214\354\233\220\352\260\200\354\236\205.png" differ diff --git "a/public/readme/\355\232\214\354\233\220\354\240\225\353\263\264.png" "b/public/readme/\355\232\214\354\233\220\354\240\225\353\263\264.png" new file mode 100644 index 00000000..6a7fe14c Binary files /dev/null and "b/public/readme/\355\232\214\354\233\220\354\240\225\353\263\264.png" differ diff --git a/public/slider/mainSlide0.jpg b/public/slider/mainSlide0.jpg new file mode 100644 index 00000000..055729d6 Binary files /dev/null and b/public/slider/mainSlide0.jpg differ diff --git a/public/slider/mainSlide1.jpg b/public/slider/mainSlide1.jpg new file mode 100644 index 00000000..5279dfdc Binary files /dev/null and b/public/slider/mainSlide1.jpg differ diff --git a/public/slider/mainSlide2.jpg b/public/slider/mainSlide2.jpg new file mode 100644 index 00000000..bec48c89 Binary files /dev/null and b/public/slider/mainSlide2.jpg differ diff --git a/public/slider/mainSlide3.jpg b/public/slider/mainSlide3.jpg new file mode 100644 index 00000000..27c4e054 Binary files /dev/null and b/public/slider/mainSlide3.jpg differ diff --git a/public/slider/mainSlide4.jpg b/public/slider/mainSlide4.jpg new file mode 100644 index 00000000..e2e7ed1a Binary files /dev/null and b/public/slider/mainSlide4.jpg differ diff --git a/public/soldout.png b/public/soldout.png new file mode 100644 index 00000000..39e88840 Binary files /dev/null and b/public/soldout.png differ diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 00000000..8be54eed --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,82 @@ +import { Route, Routes } from 'react-router-dom'; +import Layout from '@/components/Layout'; +import Home from '@/routes/Home'; +import NotFound from '@/routes/NotFound'; +import Login from '@/routes/Login'; +import Admin from '@/routes/admin/Admin'; +import AddProduct from '@/routes/admin/AddProduct'; +import ProtectedRoute from '@/routes/ProtectedRoute'; +import MyAccount from '@/routes/myAccount/MyAccount'; +import AdminClients from '@/routes/admin/AdminClients'; +import ChangeName from '@/routes/myAccount/ChangeName'; +import ChangePassword from '@/routes/myAccount/ChangePassword'; +import Info from '@/routes/myAccount/Info'; +import AdminProduct from '@/routes/admin/AdminProducts'; +import ConnectBankAccount from '@/routes/myAccount/bank/ConnectBankAccount'; +import Products from '@/routes/Products'; +import ProductDetail from '@/routes/ProductDetail'; +import BankAccounts from '@/routes/myAccount/bank/BankAccounts'; +import SearchProducts from '@/routes/SearchProducts'; +import OrderList from '@/routes/myAccount/OrderList'; +import AllTransactions from '@/routes/admin/AllTransactions'; +import OrderDetail from '@/routes/myAccount/OrderDetail'; +import EditProduct from '@/routes/admin/EditProduct'; +import LogoutNeededRoute from '@/routes/LogoutNeededRoute'; +import SignUp from '@/routes/SignUp'; + +export default function App() { + return ( + + }> + {/* ํ™ˆ */} + } /> + } /> + } /> + } + /> + {/* ๋กœ๊ทธ์ธ */} + } />} + /> + {/* ํšŒ์›๊ฐ€์ž… */} + } />} + /> + {/* ๋‚ด ์ •๋ณด */} + } />} + > + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + {/* ๊ด€๋ฆฌ์ž */} + } />} + > + {/*์œ ์ €๊ด€๋ฆฌ*/} + } /> + {/*์ƒํ’ˆ๊ด€๋ฆฌ*/} + } /> + {/*์ƒํ’ˆ์ถ”๊ฐ€*/} + } /> + {/*์ƒํ’ˆ์ˆ˜์ •*/} + } /> + {/*๊ฑฐ๋ž˜๋‚ด์—ญ*/} + } /> + + } /> + + + ); +} diff --git a/src/api/adminApi.ts b/src/api/adminApi.ts new file mode 100644 index 00000000..9982e34b --- /dev/null +++ b/src/api/adminApi.ts @@ -0,0 +1,227 @@ +import { API_URL, HEADERS } from '@/constants/constants'; +const MASTER_HEADERS = { + ...HEADERS, + masterKey: 'true', +}; + +export const addProduct = async (productData: AddProductData) => { + try { + const response = await fetch(`${API_URL}/products`, { + method: 'POST', + headers: MASTER_HEADERS, + body: JSON.stringify(productData), + }); + if (response.ok) { + const data: AddProductResponseValue = await response.json(); + return { + data, + statusCode: response.status, + message: `${data.title}๋ฅผ ์ถ”๊ฐ€ํ•˜์˜€์Šต๋‹ˆ๋‹ค.`, + }; + } + const errorMessage: string = await response.json(); + return { + data: null, + statusCode: response.status, + message: errorMessage, + }; + } catch (error) { + console.log('Error while adding product: ', error); + return { + data: null, + statusCode: 400, + message: '์ƒํ’ˆ์„ ์ถ”๊ฐ€ ์ค‘ ์—๋Ÿฌ๋ฐœ์ƒ, ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”', + }; + } +}; + +// ์ˆ˜์ • ์˜ˆ์ • +export const updateProduct = async ( + productId: string, + updateData: UpdateProductBodyData +) => { + try { + const response = await fetch(`${API_URL}/products/${productId}`, { + method: 'PUT', + headers: MASTER_HEADERS, + body: JSON.stringify(updateData), + }); + if (response.ok) { + const data: UpdatedProduct = await response.json(); + return { + data, + statusCode: response.status, + message: `${data.title} ์ƒํ’ˆ์„ ์ˆ˜์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค.`, + }; + } + const errorMessage = await response.json(); + return { + data: null, + statusCode: response.status, + message: errorMessage, + }; + } catch (error) { + console.log('error while updating a product'); + return { + data: null, + statusCode: 400, + message: '์ œํ’ˆ ์ˆ˜์ • ์ค‘ ์—๋Ÿฌ๋ฐœ์ƒ, ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.', + }; + } +}; + +export const deleteProduct = async (productId: string) => { + try { + const response = await fetch(`${API_URL}/products/${productId}`, { + method: 'DELETE', + headers: MASTER_HEADERS, + }); + if (response.ok) { + const data: true = await response.json(); + return { + data, + statusCode: response.status, + message: '', + }; + } + const errorMessage: string = await response.json(); + return { + data: null, + statusCode: response.status, + message: errorMessage, + }; + } catch (error) { + return { + data: null, + statusCode: 400, + message: '์ƒํ’ˆ ์‚ญ์ œ ์ค‘ ์—๋Ÿฌ๋ฐœ์ƒ, ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.', + }; + } +}; + +// ์œ ์ €์กฐํšŒ +export const getClients = async () => { + try { + const res = await fetch(`${API_URL}/auth/users`, { + method: 'GET', + headers: MASTER_HEADERS, + }); + // ์œ ์ €๋“ค ์กฐํšŒ๊ฐ€ ์„ฑ๊ณตํ•œ ๊ฒฝ์šฐ + if (res.ok) { + const data: Client[] = await res.json(); + return { + data, + statusCode: res.status, + message: '', + }; + } + // ์œ ์ €๋“ค ์กฐํšŒ๊ฐ€ ์‹คํŒจํ•œ ๊ฒฝ์šฐ(masterkey๊ฐ€ ์—†๋Š”๊ฒฝ์šฐ) + const errorMessage: string = await res.json(); + return { + data: null, + statusCode: res.status, + message: errorMessage, + }; + // ๊ธฐํƒ€ ์˜ค๋ฅ˜(url์ด ์ž˜๋ชป๋œ๊ฒฝ์šฐ, ์„œ๋ฒ„๋‹ค์šด) + } catch (error) { + console.log('Error while getUser: ', error); + return { + data: null, + statusCode: 400, + message: 'ํšŒ์› ์กฐํšŒ ์ค‘ ์—๋Ÿฌ๋ฐœ์ƒ, ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.', + }; + } +}; + +// ์ƒํ’ˆ ์กฐํšŒ +export async function getProducts() { + try { + const res = await fetch(`${API_URL}/products`, { + method: 'GET', + headers: MASTER_HEADERS, + }); + if (res.ok) { + const data: Product[] = await res.json(); + return { + data, + statusCode: res.status, + message: '', + }; + } + const error: string = await res.json(); + return { + data: null, + statusCode: res.status, + message: error, + }; + } catch (error) { + return { + data: null, + statusCode: 400, + message: '์ƒํ’ˆ ๋ชฉ๋ก ์กฐํšŒ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ, ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.', + }; + } +} + +export async function getProductDetail(productId: string) { + try { + const res = await fetch(`${API_URL}/products/${productId}`, { + method: 'GET', + headers: MASTER_HEADERS, + }); + if (res.ok) { + const data: ProductDetail = await res.json(); + return { + data, + statusCode: res.status, + message: '', + }; + } + const errorMessage: string = await res.json(); + return { + data: null, + statusCode: res.status, + message: errorMessage, + }; + } catch (error) { + console.log('error while getting a product', error); + return { + data: null, + statusCode: 400, + message: '๊ฐœ๋ณ„ ์ƒํ’ˆ ์กฐํšŒ ์ค‘ ์—๋Ÿฌ๋ฐœ์ƒ, ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.', + }; + } +} + +export const getAllTransactions = async () => { + try { + const response = await fetch(`${API_URL}/products/transactions/all`, { + method: 'GET', + headers: MASTER_HEADERS, + }); + + if (response.ok) { + const data: TransactionDetail[] = await response.json(); + return { + data, + statusCode: response.status, + message: '', + }; + } + + const errorMessage: string = await response.json(); + return { + data: null, + statusCode: response.status, + message: errorMessage, + }; + } catch (error) { + console.log('Error while fetching all transactions: ', error); + return { + data: null, + statusCode: 400, + message: + '์ „์ฒด ๊ฑฐ๋ž˜ ๋‚ด์—ญ์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ, ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.', + }; + } +}; diff --git a/src/api/authApi.ts b/src/api/authApi.ts new file mode 100644 index 00000000..6cd32138 --- /dev/null +++ b/src/api/authApi.ts @@ -0,0 +1,203 @@ +import { API_URL, HEADERS } from '@/constants/constants'; + +// 1. ๋กœ๊ทธ์ธ +export const signIn = async (loginData: { + email: string; + password: string; +}) => { + try { + const response = await fetch(`${API_URL}/auth/login`, { + method: 'POST', + headers: HEADERS, + body: JSON.stringify({ + email: loginData.email, + password: loginData.password, + }), + }); + // ๋กœ๊ทธ์ธ ์„ฑ๊ณต + if (response.ok) { + const data: UserResponseValue = await response.json(); + return { + data, + statusCode: response.status, //200 + message: `${data.user.displayName}๋‹˜ ์ฆ๊ฑฐ์šด ์‡ผํ•‘ ๋˜์„ธ์š”!`, + }; + } + // ๋กœ๊ทธ์ธ ์‹คํŒจ(์—†๋Š” ์ด๋ฉ”์ผ or ๋น„๋ฒˆ ์ž…๋ ฅ ์˜ค๋ฅ˜ or ์œ ํšจ์„ฑ ์˜ค๋ฅ˜(ํด๋ผ์ด์–ธํŠธ์—์„œ ์œ ํšจ์„ฑ๊ฒ€์‚ฌํ•จ) or apiํ‚ค๊ฐ€ ์ž˜๋ชป๋œ ๊ฒฝ์šฐ) + const errorMessage: string = await response.json(); + return { data: null, statusCode: response.status, message: errorMessage }; + + // ๊ธฐํƒ€ ์˜ค๋ฅ˜(์„œ๋ฒ„ ๋ฌธ์ œ, url์ด ์ž˜๋ชป๋œ ๊ฒฝ์šฐ) + } catch (error) { + console.log('Error while login: ', error); + return { + data: null, + statusCode: 400, + message: '๋กœ๊ทธ์ธ ๋„์ค‘ ์˜ค๋ฅ˜๋ฐœ์ƒ, ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.', + }; + } +}; + +// 2. ํšŒ์›๊ฐ€์ž… +export const signUp = async (signUpData: { + email: string; + password: string; + displayName: string; + profileImgBase64?: string; +}) => { + try { + const response = await fetch(`${API_URL}/auth/signup`, { + method: 'POST', + headers: HEADERS, + body: JSON.stringify({ + email: signUpData.email, + password: signUpData.password, + displayName: signUpData.displayName, + profileImgBase64: signUpData.profileImgBase64, + }), + }); + // ํšŒ์›๊ฐ€์ž… ์„ฑ๊ณต + if (response.ok) { + const data: UserResponseValue = await response.json(); + return { + data, + statusCode: response.status, + message: `${data.user.displayName}๋‹˜ ์ฆ๊ฑฐ์šด ์‡ผํ•‘ ๋˜์„ธ์š”!`, + }; + } + // ํšŒ์›๊ฐ€์ž… ์‹คํŒจ(์ด๋ฏธ ๋“ฑ๋ก๋œ ์ด๋ฉ”์ผ or ์œ ํšจ์„ฑ ์˜ค๋ฅ˜(ํด๋ผ์ด์–ธํŠธ ์œ ํšจ์„ฑ์—์„œ ๋ง‰์Œ) or apikey์˜ค๋ฅ˜) + const errorMessage: string = await response.json(); + return { data: null, statusCode: response.status, message: errorMessage }; + + // ๊ธฐํƒ€ ์˜ค๋ฅ˜(์„œ๋ฒ„ ๋ฌธ์ œ, url์ด ์ž˜๋ชป๋œ ๊ฒฝ์šฐ) + } catch (error) { + console.log('Error while signup: ', error); + return { + data: null, + statusCode: 400, + message: 'ํšŒ์›๊ฐ€์ž… ๋„์ค‘ ์—๋Ÿฌ ๋ฐœ์ƒ, ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.', + }; + } +}; + +// 3. ๋กœ๊ทธ์•„์›ƒ +export const logOut = async (accessToken: string) => { + // accessToken ์ด ์—†๋‹ค๋ฉด ๋กœ๊ทธ์•„์›ƒ์ƒํƒœ์ด๋ฏ€๋กœ ํ•จ์ˆ˜ ์ข…๋ฃŒ + try { + const response = await fetch(`${API_URL}/auth/logout`, { + method: 'POST', + headers: { + ...HEADERS, + Authorization: `Bearer ${accessToken}`, + }, + }); + if (response.ok) { + const data: true = await response.json(); // ๋‹น์—ฐํžˆ true + return { + data, + statusCode: response.status, //200 + message: '์•ˆ๋…•ํžˆ๊ฐ€์„ธ์š”!๐Ÿ–๏ธ๐Ÿ–๏ธ', + }; + } + const errorMessage: string = await response.json(); + return { + data: null, + statusCode: response.status, + message: errorMessage, + }; + } catch (error) { + console.log('error while logout'); + return { + data: null, + statusCode: 400, + message: '๋กœ๊ทธ์•„์›ƒ ๋„์ค‘ ์—๋Ÿฌ ๋ฐœ์ƒ, ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.', + }; + } +}; + +// 4. ์ธ์ฆํ™•์ธ +export const authenticate = async (accessToken: string | null) => { + // ํ† ํฐ์ด ์—†๋Š” ๊ฒฝ์šฐ + if (!accessToken) { + localStorage.removeItem('user'); + return { data: null, statusCode: 400, message: '๋กœ๊ทธ์ธ ํ•ด์ฃผ์„ธ์š”' }; + } + + // ํ† ํฐ์ด ์žˆ๋Š”๊ฒฝ์šฐ + try { + const response = await fetch(`${API_URL}/auth/me`, { + method: 'POST', + headers: { + ...HEADERS, + Authorization: `Bearer ${accessToken}`, + }, + }); + + // ์œ ํšจํ•œ ํ† ํฐ์ด ๋งž๋Š” ๊ฒฝ์šฐ + if (response.ok) { + const data: AuthenticateResponseValue = await response.json(); + return { data, statusCode: response.status, message: '' }; + } + + // ์œ ํšจํ•œ ํ† ํฐ์ด ์•„๋‹Œ๊ฒฝ์šฐ(expired ๋˜๋Š” ์ž„์˜์˜ ํ† ํฐ์„ ์ž…๋ ฅํ•œ ๊ฒฝ์šฐ) + const errorMessage: string = await response.json(); + return { data: null, statusCode: response.status, message: errorMessage }; + // ๊ธฐํƒ€ ์˜ค๋ฅ˜(์„œ๋ฒ„ ๋ฌธ์ œ, url์ด ์ž˜๋ชป๋œ ๊ฒฝ์šฐ) + } catch (error) { + console.log('Error while authenticate: ', error); + return { + data: null, + statusCode: 400, + message: '์ธ์ฆ ๋„์ค‘ ์—๋Ÿฌ ๋ฐœ์ƒ, ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.', + }; + } +}; + +// 5. ์‚ฌ์šฉ์ž ์ •๋ณด์ˆ˜์ • +export const editUser = async ( + accessToken: string, + editData: { + displayName?: string; + profileImgBase64?: string; + oldPassword?: string; + newPassword?: string; + } +) => { + try { + const response = await fetch(`${API_URL}/auth/user`, { + method: 'PUT', + headers: { + ...HEADERS, + Authorization: `Bearer ${accessToken}`, + }, + body: JSON.stringify({ + displayName: editData.displayName, + oldPassword: editData.oldPassword, + newPassword: editData.newPassword, + profileImgBase64: editData.profileImgBase64, + }), + }); + if (response.ok) { + const data: UpdatedUserResponseValue = await response.json(); + return { + data, + statusCode: response.status, + message: '', + }; + } + // ๊ธฐ์กด ๋น„๋ฒˆ์ด ์•ˆ๋งž๋Š”๊ฒฝ์šฐ, ๋“ฑ๋“ฑ + const errorMessage: string = await response.json(); + return { + data: null, + statusCode: response.status, + message: errorMessage, + }; + } catch (error) { + console.log('Error while EditUser: ', error); + return { + data: null, + statusCode: 400, + message: 'ํšŒ์›์ •๋ณด ์ˆ˜์ • ๋„์ค‘ ์˜ค๋ฅ˜๋ฐœ์ƒ, ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.', + }; + } +}; diff --git a/src/api/bankApi.ts b/src/api/bankApi.ts new file mode 100644 index 00000000..2ed86e46 --- /dev/null +++ b/src/api/bankApi.ts @@ -0,0 +1,153 @@ +import { API_URL, HEADERS } from '@/constants/constants'; + +// ์„ ํƒ ๊ฐ€๋Šฅํ•œ ์€ํ–‰ ๋ชฉ๋ก ์กฐํšŒ +export const getBankList = async (accessToken: string) => { + try { + const response = await fetch(`${API_URL}/account/banks`, { + method: 'GET', + headers: { + ...HEADERS, + Authorization: `Bearer ${accessToken}`, + }, + }); + if (response.ok) { + const data: Bank[] = await response.json(); + return { + data, + statusCode: response.status, + message: '', + }; + } + const errorMessage: string = await response.json(); + return { + data: null, + statusCode: response.status, + message: errorMessage, + }; + } catch (error) { + console.log('error while getting banks', error); + return { + data: null, + statusCode: 400, + message: '์€ํ–‰ ๋ชฉ๋ก ์กฐํšŒ ์ค‘ ์—๋Ÿฌ ๋ฐœ์ƒ, ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.', + }; + } +}; + +// ๊ณ„์ขŒ ๋ชฉ๋ก ๋ฐ ์ž”์•ก ์กฐํšŒ +export const getAccountListAndBalance = async (accessToken: string) => { + try { + const response = await fetch(`${API_URL}/account`, { + method: 'GET', + headers: { + ...HEADERS, + Authorization: `Bearer ${accessToken}`, + }, + }); + if (response.ok) { + const data: AccountsAndBalance = await response.json(); + return { + data, + statusCode: response.status, + message: '', + }; + } + const errorMessage: string = await response.json(); + return { + data: null, + statusCode: response.status, + message: errorMessage, + }; + } catch (error) { + console.log('error while getting account lists and balance', error); + return { + data: null, + statusCode: 400, + message: '๊ณ„์ขŒ ์กฐํšŒ ์ค‘ ์—๋Ÿฌ ๋ฐœ์ƒ, ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.', + }; + } +}; + +// ๊ณ„์ขŒ ์—ฐ๊ฒฐ +export const connectAccount = async ( + requestBody: { + bankCode: string; + accountNumber: string; + phoneNumber: string; + signature: boolean; + }, + accessToken: string +) => { + try { + const response = await fetch(`${API_URL}/account`, { + method: 'POST', + headers: { + ...HEADERS, + Authorization: `Bearer ${accessToken}`, + }, + body: JSON.stringify(requestBody), + }); + if (response.ok) { + const data: UserAccount = await response.json(); + return { + data, + statusCode: response.status, + message: `${data.bankName}๊ณ„์ขŒ๋ฅผ ์ถ”๊ฐ€ํ•˜์…จ์Šต๋‹ˆ๋‹ค.`, + }; + } + const errorMessage: string = await response.json(); + return { + data: null, + statusCode: response.status, + message: errorMessage, + }; + } catch (error) { + console.log('error while conncting bank account', error); + return { + data: null, + statusCode: 400, + message: '๊ณ„์ขŒ ์ถ”๊ฐ€ ์ค‘ ์—๋Ÿฌ๋ฐœ์ƒ, ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.', + }; + } +}; + +// ๊ณ„์ขŒ ํ•ด์ง€ +export const deleteAccount = async ( + requestBody: { + accountId: string; + signature: boolean; + }, + accessToken: string +) => { + try { + const response = await fetch(`${API_URL}/account`, { + method: 'DELETE', + headers: { + ...HEADERS, + Authorization: `Bearer ${accessToken}`, + }, + body: JSON.stringify(requestBody), + }); + if (response.ok) { + const data: true = await response.json(); + return { + data, + statusCode: response.status, + message: '', + }; + } + const errorMessage: string = await response.json(); + return { + data: null, + statusCode: response.status, + message: errorMessage, + }; + } catch (error) { + console.log('error while deleting an account', error); + return { + data: null, + statusCode: 400, + message: '๊ณ„์ขŒ ์‚ญ์ œ ์ค‘ ์—๋Ÿฌ๋ฐœ์ƒ, ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.', + }; + } +}; diff --git a/src/api/transactionApi.ts b/src/api/transactionApi.ts new file mode 100644 index 00000000..e7e5573c --- /dev/null +++ b/src/api/transactionApi.ts @@ -0,0 +1,171 @@ +// ์ œํ’ˆ ๊ฑฐ๋ž˜์™€ ๊ด€๋ จ๋œ api + +import { API_URL, HEADERS } from '@/constants/constants'; + +export async function searchProducts({ searchText }: { searchText: string }) { + try { + const response = await fetch(`${API_URL}/products/search`, { + method: 'POST', + headers: HEADERS, + body: JSON.stringify({ searchText }), + }); + if (response.ok) { + const data: Product[] = await response.json(); + return { data, statusCode: response.status, message: '' }; + } + const errorMessage: string = await response.json(); + return { data: null, statusCode: response.status, message: errorMessage }; + } catch (error) { + console.log('Error while searching products: ', error); + return { + data: null, + statusCode: 400, + message: '์ƒํ’ˆ ๊ฒ€์ƒ‰ ๋„์ค‘ ์—๋Ÿฌ๋ฐœ์ƒ, ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.', + }; + } +} + +export async function buyProduct( + productId: string, + accountId: string, + accessToken: string +) { + try { + const requestBody = { + productId, + accountId, + }; + + const response = await fetch(`${API_URL}/products/buy`, { + method: 'POST', + headers: { + ...HEADERS, + Authorization: `Bearer ${accessToken}`, + }, + body: JSON.stringify(requestBody), + }); + + if (response.ok) { + const data: true = await response.json(); + return { data, statusCode: response.status, message: '' }; + } + + const errorMessage: string = await response.json(); + return { data: null, statusCode: response.status, message: errorMessage }; + } catch (error) { + console.log('Error while buyProduct: ', error); + return { + data: null, + statusCode: 400, + message: '์ œํ’ˆ ๊ฑฐ๋ž˜ ๋„์ค‘ ์—๋Ÿฌ ๋ฐœ์ƒ, ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.', + }; + } +} + +export async function getOrderList(accessToken: string) { + try { + const response = await fetch(`${API_URL}/products/transactions/details`, { + method: 'GET', + headers: { + ...HEADERS, + Authorization: `Bearer ${accessToken}`, + }, + }); + if (response.ok) { + const data: TransactionDetail[] = await response.json(); + return { data, statusCode: response.status, message: '' }; + } + const errorMessage: string = await response.json(); + return { data: null, statusCode: response.status, message: errorMessage }; + } catch (error) { + console.log('Error while getting order list: ', error); + return { + data: null, + statusCode: 400, + message: '์ฃผ๋ฌธ๋‚ด์—ญ ์กฐํšŒ ์ค‘ ์—๋Ÿฌ ๋ฐœ์ƒ, ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.', + }; + } +} + +export async function getOrderDetail(accessToken: string, detailId: string) { + try { + const response = await fetch(`${API_URL}/products/transactions/detail`, { + method: 'POST', + headers: { + ...HEADERS, + Authorization: `Bearer ${accessToken}`, + }, + body: JSON.stringify({ detailId }), + }); + if (response.ok) { + const data: TransactionDetail = await response.json(); + return { data, statusCode: response.status, message: '' }; + } + const errorMessage: string = await response.json(); + return { data: null, statusCode: response.status, message: errorMessage }; + } catch (error) { + console.log('Error while getting transaction detail: ', error); + return { + data: null, + statusCode: 400, + message: '๊ฑฐ๋ž˜ ๋‚ด์—ญ ์ƒ์„ธ ์กฐํšŒ ์ค‘ ์—๋Ÿฌ ๋ฐœ์ƒ, ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.', + }; + } +} + +export const confirmOrder = async (accessToken: string, detailId: string) => { + try { + const res = await fetch(`${API_URL}/products/ok`, { + method: 'POST', + headers: { + ...HEADERS, + Authorization: `Bearer ${accessToken}`, + }, + body: JSON.stringify({ detailId }), + }); + + if (res.ok) { + const data: true = await res.json(); + return { data, statusCode: res.status, message: '' }; + } + const errorMessage: string = await res.json(); + return { data: null, statusCode: res.status, message: errorMessage }; + } catch (error) { + console.log('Error while confirming a order: ', error); + return { + data: null, + statusCode: 400, + message: '๊ตฌ๋งคํ™•์ • ์‹œ๋„ ์ค‘ ์—๋Ÿฌ ๋ฐœ์ƒ, ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.', + }; + } +}; +// ์ œํ’ˆ์‚ญ์ œ api +export async function cancelOrder(detailId: string, accessToken: string) { + try { + console.log(detailId, accessToken); + const response = await fetch( + 'https://asia-northeast3-heropy-api.cloudfunctions.net/api/products/cancel', + { + method: 'POST', + headers: { + ...HEADERS, + Authorization: `Bearer ${accessToken}`, + }, + body: JSON.stringify({ detailId }), + } + ); + if (response.ok) { + const data: true = await response.json(); + return { data, statusCode: response.status, message: 'success' }; + } + const errorMessage: string = await response.json(); + return { data: null, statusCode: response.status, message: errorMessage }; + } catch (error) { + console.log('error while cancelling order', error); + return { + data: null, + statusCode: 400, + message: '๊ฑฐ๋ž˜ ์ทจ์†Œ ์ค‘ ์—๋Ÿฌ๋ฐœ์ƒ ์ž ์‹œํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.', + }; + } +} diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx new file mode 100644 index 00000000..fff9e081 --- /dev/null +++ b/src/components/Footer.tsx @@ -0,0 +1,17 @@ +export default function Footer() { + return ( +
+
+
+ mainLogo +

+ ๋ฏธ์นœ11 ์ฃผ์‹ํšŒ์‚ฌ | ๋Œ€ํ‘œ์ž ์ดํŒจ์บ  | [๊ตํ™˜/๋ฐ˜ํ’ˆ] ๋ถ€์‚ฐ๊ด‘์—ญ์‹œ ์ˆ˜์˜๊ตฌ ์ˆ˜์˜๋กœ + ์ˆ˜์˜์žฅ ์ˆ˜์˜๋ณต | ์‚ฌ์—…์ž๋“ฑ๋ก๋ฒˆํ˜ธ 123-45-6789 |
+ ๋Œ€ํ‘œ์ „ํ™” 1234-1234 ํ†ต์‹ ํŒ๋งค์—… ์‹ ๊ณ ๋ฒˆํ˜ธ ์ œ1111-๋ถ€์‚ฐ์ˆ˜์˜-1111 | + ๊ฐœ์ธ์ •๋ณด๊ด€๋ฆฌ ์ฑ…์ž„์ž ๊น€ํŒจ์บ  | Copyright 2021 ๋ฏธ์นœ11 All Rights + Reserved. +

+
+
+ ); +} diff --git a/src/components/ImageSlider.tsx b/src/components/ImageSlider.tsx new file mode 100644 index 00000000..996776c6 --- /dev/null +++ b/src/components/ImageSlider.tsx @@ -0,0 +1,42 @@ +import { SLIDES } from '@/constants/constants'; +import Carousel from 'nuka-carousel'; +import { useNavigate } from 'react-router-dom'; + +export default function ImageSlider() { + const navigate = useNavigate(); + return ( + + {SLIDES.map((slide) => ( + {`/mainSlide${slide.label}`} navigate(`${slide.href}`)} + /> + ))} + + ); +} diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx new file mode 100644 index 00000000..e1f17267 --- /dev/null +++ b/src/components/Layout.tsx @@ -0,0 +1,23 @@ +import { Outlet } from 'react-router-dom'; +import Navbar from '@/components/Navbar'; +import { Toaster } from 'react-hot-toast'; +import Footer from '@/components/Footer'; + +export default function Layout() { + return ( + <> + +
+ +
+ +
+
+
+ + ); +} diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx new file mode 100644 index 00000000..5a5bf6df --- /dev/null +++ b/src/components/Navbar.tsx @@ -0,0 +1,88 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import { Link } from 'react-router-dom'; +import { userStore } from '@/store'; +import Search from '@/components/Search'; +import SubNavbar from '@/components/SubNavbar'; +import { SUB_MENUS } from '@/constants/constants'; +import ProfileImage from '@/components/ui/ProfileImage'; +import { useState } from 'react'; +import { logOut } from '@/api/authApi'; +import LoadingSpinner from '@/components/ui/LoadingSpinner'; +import toast from 'react-hot-toast'; + +export default function Navbar() { + // store์—์„œ ํ•„์š”ํ•œ ๋ฉ”์†Œ๋“œ(๋กœ๊ทธ์•„์›ƒ, ์ธ์ฆ), ์œ ์ ธ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ด + const { userInfo, setUser } = userStore(); + const [isLogoutting, setIsLogoutting] = useState(false); + + const handleLogout = async () => { + setIsLogoutting(true); + toast.loading('๋กœ๊ทธ์•„์›ƒ ์ค‘', { id: 'logout' }); + const res = await logOut(userInfo?.accessToken as string); + // ๋กœ๊ทธ์•„์›ƒ์„ฑ๊ณต + if (res.statusCode === 200) { + toast.success(res.message, { id: 'logout' }); + setIsLogoutting(false); + // ํด๋ผ์ด์–ธํŠธ์ƒ ์ „์—ญ user๋ฅผ null๋กœ + setUser(null); + // ๋กœ์ปฌ์ €์žฅ์†Œ user์‚ญ์ œ + localStorage.removeItem('user'); + return; + } + // ๋กœ๊ทธ์•„์›ƒ ์‹คํŒจ + toast.error(res.message, { id: 'logout' }); + setIsLogoutting(false); + }; + + return ( + <> +
+ + logo + + +
    + {/* ์œ ์ ธ๊ฐ€ ๋กœ๊ทธ์ธ ์—ฌ๋ถ€ */} + {userInfo ? ( + // ๋กœ๊ทธ์ธ ๋˜์–ด์žˆ๋Š” ๊ฒฝ์šฐ + <> +
  • + + {isLogoutting ? ( + + ) : ( + '๋กœ๊ทธ์•„์›ƒ' + )} + +
  • + + {/* ๊ด€๋ฆฌ์ž ์—ฌ๋ถ€ */} + {userInfo.isAdmin ? ( + // ๊ด€๋ฆฌ์ž์ธ ๊ฒฝ์šฐ + ๊ด€๋ฆฌ์ž + ) : ( + // ๊ด€๋ฆฌ์ž๊ฐ€ ์•„๋‹Œ๊ฒฝ์šฐ + <> + )} + + {/* ๋กœ๊ทธ์ธํ•œ ํšŒ์›์ •๋ณด */} + {userInfo.user.displayName}๋‹˜ + + + ) : ( + // ๋กœ๊ทธ์ธ์ด ๋˜์–ด์žˆ์ง€ ์•Š์€ ๊ฒฝ์šฐ + <> +
  • + ๋กœ๊ทธ์ธ +
  • +
  • + ํšŒ์›๊ฐ€์ž… +
  • + + )} +
+
+ + + ); +} diff --git a/src/components/Search.tsx b/src/components/Search.tsx new file mode 100644 index 00000000..46c948f2 --- /dev/null +++ b/src/components/Search.tsx @@ -0,0 +1,44 @@ +import { useState } from 'react'; +import { BiSearch } from 'react-icons/bi'; +import { useNavigate } from 'react-router-dom'; + +export default function Search() { + const navigate = useNavigate(); + // ๊ฒ€์ƒ‰์–ด๋ฅผ state์— ์ €์žฅ + const [searchText, setSearchText] = useState(''); + + // ๊ฒ€์ƒ‰ input์—์„œ ๋ณ€ํ™”๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ ์ž…๋ ฅ๊ฐ’์„ state์— ์ €์žฅ + const handleChange = (event: React.ChangeEvent) => { + setSearchText(event.target.value); + }; + + // ๊ฒ€์ƒ‰(๊ฒ€์ƒ‰ ์•„์ด์ฝ˜์„ ๋ˆ„๋ฅด๊ฑฐ๋‚˜ ์—”ํ„ฐ๋ฅผ ๋ˆŒ๋ €์„ ๋•Œ) + const handleSearch = async (event: React.FormEvent) => { + event.preventDefault(); + if (searchText.trim() === '') { + setSearchText(''); + return; + } + navigate(`/products/search`, { state: searchText }); + setSearchText(''); + return; + }; + + return ( +
+ +
+ +
+
+ ); +} diff --git a/src/components/SingleUser.tsx b/src/components/SingleUser.tsx new file mode 100644 index 00000000..b84d477e --- /dev/null +++ b/src/components/SingleUser.tsx @@ -0,0 +1,36 @@ +import ProfileImage from '@/components/ui/ProfileImage'; + +interface SingleUserProps { + email: string; + displayName: string; + profileImg: string; + spentMoney: number; +} + +export default function SingleUser({ + email, + displayName, + profileImg, + spentMoney, +}: SingleUserProps) { + return ( + + + + + {email} + {displayName} + + {spentMoney >= 300000 ? ( + spentMoney >= 500000 ? ( + ๐Ÿ’ฐVVIP๐Ÿ’ฐ + ) : ( + ๐Ÿ’ฐVIP + ) + ) : ( + '์ผ๋ฐ˜ํšŒ์›' + )} + + + ); +} diff --git a/src/components/SubNavbar.tsx b/src/components/SubNavbar.tsx new file mode 100644 index 00000000..266996ab --- /dev/null +++ b/src/components/SubNavbar.tsx @@ -0,0 +1,29 @@ +import { Link } from 'react-router-dom'; + +interface SubNavbarProps { + menus: { + label: string; + href: string; + }[]; + gray?: boolean; +} + +export default function SubNavbar({ menus, gray }: SubNavbarProps) { + return ( + + ); +} diff --git a/src/components/product/ProductBar.tsx b/src/components/product/ProductBar.tsx new file mode 100644 index 00000000..57cbc2fb --- /dev/null +++ b/src/components/product/ProductBar.tsx @@ -0,0 +1,62 @@ +import { DICTIONARY_SHOES, PRODUCT_BRAND } from '@/constants/constants'; +import Breadcrumbs from '@/components/ui/Breadcrumbs'; +import SectionTitle from '@/components/ui/SectionTitle'; +import Button from '@/components/ui/Button'; +import ProductSortOptions from '@/components/product/ProductSortOptions'; + +interface ProductBarProps { + searchText?: string; + category?: string; + productNumber?: number; + handleBrand?: (brandValue: string) => void; + selectedBrand?: string; + handleSortByPrice: (sort: string) => void; + brand?: string; +} + +export default function ProductBar({ + searchText, + category, + productNumber, + handleBrand, + selectedBrand, + handleSortByPrice, + brand, +}: ProductBarProps) { + return ( +
+ {searchText ? ( + + ) : ( + <> + + + )} + +
+ +
+ + ์ด {productNumber} ๊ฐœ +
+
+
+ {searchText ? ( + <> + ) : ( +
+ {PRODUCT_BRAND.map((brand) => ( +
+ )} +
+ ); +} diff --git a/src/components/product/ProductCard.tsx b/src/components/product/ProductCard.tsx new file mode 100644 index 00000000..6ac4db92 --- /dev/null +++ b/src/components/product/ProductCard.tsx @@ -0,0 +1,47 @@ +import { priceBeforeDiscount } from '@/lib/ceilPrice'; + +interface ProductCardProps { + title: string; + price: number; + thumbnail: string | null; + discountRate: number; + isSoldOut: boolean; +} + +export default function ProductCard({ + discountRate, + price, + thumbnail, + title, + isSoldOut, +}: ProductCardProps) { + return ( +
+ {title} +

+ {title} +

+
+ {price.toLocaleString('ko-KR')}์› + + {priceBeforeDiscount(price, discountRate)}์› + +
+ soldout +
+ ); +} diff --git a/src/components/product/ProductSection.tsx b/src/components/product/ProductSection.tsx new file mode 100644 index 00000000..0ffd9230 --- /dev/null +++ b/src/components/product/ProductSection.tsx @@ -0,0 +1,155 @@ +import { useEffect, useMemo, useState } from 'react'; +import { getProducts } from '@/api/adminApi'; +import { Link } from 'react-router-dom'; +import ProductBar from '@/components/product/ProductBar'; +import ProductCard from '@/components/product/ProductCard'; +import Skeleton from '@/components/ui/Skeleton'; +import toast from 'react-hot-toast'; +import SectionTitle from '@/components/ui/SectionTitle'; + +interface ProductSectionProps { + category?: string; + related?: boolean; + productId?: string; + productTitle?: string; +} + +export default function ProductSection({ + category, + related, + productId, + productTitle, +}: ProductSectionProps) { + const [products, setProducts] = useState(); + const [isLoading, setIsLoading] = useState(false); + const [brand, setBrand] = useState('all'); + const [sortByPrice, setSortByPrice] = useState(''); + + // ๊ฐ€๊ฒฉ ์ •๋ ฌ ์„ ํƒ + const handleSortByPrice = (sort: string) => { + setSortByPrice(sort); + }; + + // ๋ธŒ๋žœ๋“œ ์„ ํƒ + const handleBrand = (brandValue: string) => { + setBrand(brandValue); + }; + + useEffect(() => { + setBrand('all'); + setIsLoading(true); + async function fetchData() { + const res = await getProducts(); + if (res.statusCode === 200) { + // ์นดํ…Œ๊ณ ๋ฆฌ์— ๋”ฐ๋ผ์„œ products์„ setting + const categoryFilteredProducts = (res.data as Product[]).filter( + (product) => (category ? product.tags[0] === category : product) + ); + setProducts(categoryFilteredProducts); + + // ์Šค์ผˆ๋ ˆํ†ค์˜ ๊ฐฏ์ˆ˜๋ฅผ ๋ฏธ๋ฆฌ ์•Œ๊ธฐ ์œ„ํ•ด ๋กœ์ปฌ์ €์žฅ์†Œ์— ๊ฐ ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ์ƒํ’ˆ์ˆ˜๋ฅผ ์ €์žฅ + localStorage.setItem( + category ? category : 'all', + JSON.stringify(categoryFilteredProducts.length) + ); + setIsLoading(false); + return; + } + setIsLoading(false); + toast.error(res.message, { id: 'getProducts' }); + } + fetchData(); + }, [category]); + + // ๋กœ์ปฌ ์ €์žฅ์†Œ์— ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ์ƒํ’ˆ ๊ฐฏ์ˆ˜๋ฅผ ๊ฐ€์ ธ์˜ด / ์—†๋Š” ๊ฒฝ์šฐ 10๊ฐœ + const skeletonLength = new Array( + JSON.parse(localStorage.getItem(category ? category : 'all') ?? '10') + ).fill(0); + + // ํ•„ํ„ฐ๋ง ์™„๋ฃŒ๋œ ์ƒํ’ˆ๋“ค + const filteredProducts = useMemo( + () => + products?.filter((product) => + brand === 'all' ? product : product.tags[1] === brand + ), + [brand, products] + ); + // ๊ฐ€๊ฒฉ์ˆœ sorting + const sortedFilteredProducts = useMemo( + () => + filteredProducts?.sort((a, b) => + sortByPrice === 'lowPrice' ? a.price - b.price : b.price - a.price + ), + [filteredProducts, sortByPrice] + ); + + return ( +
+ {/* ProductDetail ์ปดํฌ๋„ŒํŠธ์—์„œ related ์˜ต์…˜์„ props๋กœ ์ „๋‹ฌํ•  ๊ฒฝ์šฐ */} + {related ? ( + + ) : ( + + )} + +
    + {isLoading ? ( + <> + {skeletonLength.map((_el, inex) => ( + + ))} + + ) : ( + <> + {related // ProductDetail ์ปดํฌ๋„ŒํŠธ์—์„œ related ์˜ต์…˜์„ props๋กœ ์ „๋‹ฌํ•  ๊ฒฝ์šฐ + ? sortedFilteredProducts + ?.filter((product) => product.id !== productId) // ์ž๊ธฐ ์ž์‹ ์„ ์ œ์™ธ + .sort(() => Math.random() - 0.5) // ์ˆœ์„œ ์„ž์–ด์„œ ์ถ”์ฒœ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ฒ˜๋Ÿผ ๋ณด์ด๊ฒŒ + .slice(0, 10) // ์ƒ์œ„ 10๊ฐœ๋งŒ ๋ณด์—ฌ์คŒ + .map((product) => ( +
  • + + + +
  • + )) + : sortedFilteredProducts?.map((product) => ( +
  • + + + +
  • + ))} + + )} +
+
+ ); +} diff --git a/src/components/product/ProductSortOptions.tsx b/src/components/product/ProductSortOptions.tsx new file mode 100644 index 00000000..7f920d36 --- /dev/null +++ b/src/components/product/ProductSortOptions.tsx @@ -0,0 +1,23 @@ +import { PRODUCT_SORT } from '@/constants/constants'; + +interface SortOptionsProps { + handleSortByPrice: (brandValue: string) => void; +} + +export default function ProductSortOptions({ + handleSortByPrice, +}: SortOptionsProps) { + return ( +
+ {PRODUCT_SORT.map((sort) => ( +
handleSortByPrice(sort.value)} + > + {sort.name} +
+ ))} +
+ ); +} diff --git a/src/components/ui/Breadcrumbs.tsx b/src/components/ui/Breadcrumbs.tsx new file mode 100644 index 00000000..ef453789 --- /dev/null +++ b/src/components/ui/Breadcrumbs.tsx @@ -0,0 +1,37 @@ +import { DICTIONARY_SHOES } from '@/constants/constants'; +import { Link } from 'react-router-dom'; + +interface BreadcrumbsProps { + category?: string; + brand?: string; + name?: string; +} + +export default function Breadcrumbs({ + category, + brand, + name, +}: BreadcrumbsProps) { + return ( + <> + {category ? ( +
+
+ ํฌ๋ ˆ์ด์ง€ 11 +
+
+ + {` / ${DICTIONARY_SHOES[category]}`} + +
+
{brand ? ` / ${brand}` : ''}
+
{name ? ` / ${name}` : ''}
+
+ ) : ( + + ํฌ๋ ˆ์ด์ง€ 11 + + )} + + ); +} diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx new file mode 100644 index 00000000..17d4e5dd --- /dev/null +++ b/src/components/ui/Button.tsx @@ -0,0 +1,35 @@ +interface ButtonProps { + text: string | JSX.Element; + secondary?: boolean; + value?: string; + onClick?: ( + event: React.MouseEvent + ) => void | Promise; + disabled?: boolean; + submit?: boolean; +} + +export default function Button({ + text, + secondary, + disabled, + onClick, + value, + submit, +}: ButtonProps) { + return ( + + ); +} diff --git a/src/components/ui/CrazyLoading.tsx b/src/components/ui/CrazyLoading.tsx new file mode 100644 index 00000000..01ec4b21 --- /dev/null +++ b/src/components/ui/CrazyLoading.tsx @@ -0,0 +1,11 @@ +export default function CrazyLoading() { + return ( +
+

+ CRAZY 1oad + 1 + ng... +

+
+ ); +} diff --git a/src/components/ui/ImageUpload.tsx b/src/components/ui/ImageUpload.tsx new file mode 100644 index 00000000..4c11dfa9 --- /dev/null +++ b/src/components/ui/ImageUpload.tsx @@ -0,0 +1,19 @@ +interface ImageUploadProps { + onChange: (event: React.ChangeEvent) => void; + name?: string; + korName: string; +} + +export default function ImageUpload({ + name, + onChange, + korName, +}: ImageUploadProps) { + return ( +
+ +
+ +
+ ); +} diff --git a/src/components/ui/Input.tsx b/src/components/ui/Input.tsx new file mode 100644 index 00000000..b3f766ac --- /dev/null +++ b/src/components/ui/Input.tsx @@ -0,0 +1,30 @@ +interface InputProps { + maxLength?: number; + placeholder?: string; + type?: string; + name: string; + value?: string | number; + onChange?: (event: React.ChangeEvent) => void; +} + +export default function Input({ + placeholder, + type, + name, + value, + maxLength, + onChange, +}: InputProps) { + return ( + + ); +} diff --git a/src/components/ui/LoadingSpinner.tsx b/src/components/ui/LoadingSpinner.tsx new file mode 100644 index 00000000..d566851f --- /dev/null +++ b/src/components/ui/LoadingSpinner.tsx @@ -0,0 +1,7 @@ +interface LoadingSpinnerProps { + color: 'white' | 'accent'; +} + +export default function LoadingSpinner({ color }: LoadingSpinnerProps) { + return
; +} diff --git a/src/components/ui/ProfileImage.tsx b/src/components/ui/ProfileImage.tsx new file mode 100644 index 00000000..194d9f0c --- /dev/null +++ b/src/components/ui/ProfileImage.tsx @@ -0,0 +1,16 @@ +interface ProfileImageProps { + src?: string | null; + small?: boolean; +} + +export default function ProfileImage({ src, small }: ProfileImageProps) { + return ( + profile + ); +} diff --git a/src/components/ui/SectionTitle.tsx b/src/components/ui/SectionTitle.tsx new file mode 100644 index 00000000..d9b932b5 --- /dev/null +++ b/src/components/ui/SectionTitle.tsx @@ -0,0 +1,7 @@ +export default function SectionTitle({ + text = '๋ชจ๋“  ์ƒํ’ˆ', +}: { + text?: string; +}) { + return
{text}
; +} diff --git a/src/components/ui/Select.tsx b/src/components/ui/Select.tsx new file mode 100644 index 00000000..f9192b56 --- /dev/null +++ b/src/components/ui/Select.tsx @@ -0,0 +1,38 @@ +import { IoIosArrowDown } from 'react-icons/io'; + +interface SelectProps { + name: string; + options: { + name: string; + value: string; + }[]; + onChange: (event: React.ChangeEvent) => void; + value: string; +} + +export default function Select({ + options, + onChange, + value, + name, +}: SelectProps) { + return ( +
+ +
+ +
+
+ ); +} diff --git a/src/components/ui/Skeleton.tsx b/src/components/ui/Skeleton.tsx new file mode 100644 index 00000000..2a84bfea --- /dev/null +++ b/src/components/ui/Skeleton.tsx @@ -0,0 +1,7 @@ +export default function Skeleton() { + return ( +
+ ); +} diff --git a/src/constants/constants.ts b/src/constants/constants.ts new file mode 100644 index 00000000..9b06d287 --- /dev/null +++ b/src/constants/constants.ts @@ -0,0 +1,159 @@ +// API +export const API_URL = + 'https://asia-northeast3-heropy-api.cloudfunctions.net/api'; + +export const HEADERS = { + 'content-type': 'application/json', + apikey: 'KDT5_nREmPe9B', + username: 'KDT5_Team1', +}; + +// ์ด๋ฏธ์ง€ ์Šฌ๋ผ์ด๋” + +export const SLIDES = [ + { + id: 0, + href: '/products/soccer/PQDbxl8TerMV7dbcHjFM', + label: 'ํ‘ธ๋งˆํ“จ์ณ', + }, + { + id: 1, + href: '/products/soccer/TswEFE3gibavTlGMFUuA', + label: '์•„๋””๋‹ค์Šคํฌํƒˆ', + }, + { + id: 2, + href: '#', + label: 'ํŒจ์บ ', + }, + { + id: 3, + href: '/products/soccer/ea3COhwuw0YHtY5ADuyg', + label: '์•„๋””๋‹ค์Šคํ”„๋ ˆ๋ฐํ„ฐ', + }, + { + id: 4, + href: '/products/soccer/bZ5fc4ywRgWCxIJJEowC', + label: '๋‚˜์ดํ‚ค๋จธํ๋ฆฌ์–ผ', + }, +]; + +// ์–ด๋“œ๋ฏผ ์ด๋ฉ”์ผ ๋ชฉ๋ก +export const ADMINS = [ + 'admin@naver.com', + 'admin1@naver.com', + 'admin2@naver.com', + 'admin3@naver.com', + 'admin4@naver.com', +]; + +export const EMAIL_REGEX = + /^[0-9a-zA-Z]([-_\\.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_\\.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,3}$/i; + +export const SUB_MENUS = [ + { + label: '์ถ•๊ตฌํ™”', + href: '/products/soccer', + }, + { + label: 'ํ’‹์‚ดํ™”', + href: '/products/footsal', + }, + { + label: '์šด๋™ํ™”/์Šฌ๋ฆฌํผ', + href: '/products/sneakers', + }, +]; + +// myAccount nav menu +export const SUB_MENUS_MYACCOUNT = [ + { + label: '๋‚ด ์ •๋ณด', + href: '/myaccount/info', + }, + { + label: '๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ', + href: '/myaccount/changepw', + }, + { + label: '๊ณ„์ขŒ ์กฐํšŒ / ํ•ด์ง€', + href: '/myaccount/accountList', + }, + { + label: '๊ณ„์ขŒ ์—ฐ๊ฒฐ', + href: '/myaccount/connectAccount', + }, + { + label: '๊ตฌ๋งค ๋‚ด์—ญ', + href: '/myaccount/orderList', + }, +]; + +// admin ํŒจ๋„ sub anvbar +export const SUB_MENUS_ADMIN = [ + { + label: 'ํšŒ์› ์ •๋ณด', + href: '/admin/clients', + }, + { + label: '์ƒํ’ˆ ๊ด€๋ฆฌ', + href: '/admin/products', + }, + { + label: '์ƒํ’ˆ ์ถ”๊ฐ€', + href: '/admin/addproduct', + }, + { + label: '๊ฑฐ๋ž˜ ๋‚ด์—ญ', + href: '/admin/alltransactions', + }, +]; + +export const SELECT_CATEGORY = [ + { name: '์นดํ…Œ๊ณ ๋ฆฌ ์„ ํƒ*', value: 'category' }, + { name: '์ถ•๊ตฌํ™”', value: 'soccer' }, + { name: 'ํ’‹์‚ดํ™”', value: 'footsal' }, + { name: '์šด๋™ํ™”/์Šฌ๋ฆฌํผ', value: 'sneakers' }, +]; +export const SELECT_BRAND = [ + { name: '๋ธŒ๋žœ๋“œ ์„ ํƒ*', value: 'brand' }, + { name: '๋‚˜์ดํ‚ค', value: 'nike' }, + { name: '์•„๋””๋‹ค์Šค', value: 'adidas' }, + { name: '๋ฏธ์ฆˆ๋…ธ', value: 'mizno' }, + { name: 'ํ‘ธ๋งˆ', value: 'puma' }, +]; + +interface Translate { + [key: string]: string; +} +export const DICTIONARY_SHOES: Translate = { + soccer: '์ถ•๊ตฌํ™”', + footsal: 'ํ’‹์‚ดํ™”', + sneakers: '์šด๋™ํ™”/์Šฌ๋ฆฌํผ', +}; + +// ์€ํ–‰ ์ •๋ณด +export const BANKS = [ + { name: 'KB๊ตญ๋ฏผ์€ํ–‰', code: '004', digits: [3, 2, 4, 3] }, + { name: '์‹ ํ•œ์€ํ–‰', code: '088', digits: [3, 3, 6] }, + { name: '์šฐ๋ฆฌ์€ํ–‰', code: '020', digits: [4, 3, 6] }, + { name: 'ํ•˜๋‚˜์€ํ–‰', code: '081', digits: [3, 6, 5] }, + { name: '์ผ€์ด๋ฑ…ํฌ', code: '089', digits: [3, 3, 6] }, + { name: '์นด์นด์˜ค๋ฑ…ํฌ', code: '090', digits: [4, 2, 7] }, + { name: 'NH๋†ํ˜‘์€ํ–‰', code: '011', digits: [3, 4, 4, 2] }, +]; + +// ์ƒํ’ˆ sorting ์˜ต์…˜ +export const PRODUCT_SORT = [ + { name: '๋‚ฎ์€๊ฐ€๊ฒฉ', value: 'lowPrice' }, + { name: '๋†’์€๊ฐ€๊ฒฉ', value: 'highPrice' }, +]; + +// ์ƒํ’ˆ brand sorting ์˜ต์…˜ +export const PRODUCT_BRAND = [ + { name: '๋ชจ๋‘', value: 'all' }, + { name: '๋‚˜์ดํ‚ค', value: 'nike' }, + { name: '์•„๋””๋‹ค์Šค', value: 'adidas' }, + { name: '๋ฏธ์ฆˆ๋…ธ', value: 'mizno' }, + { name: 'ํ‘ธ๋งˆ', value: 'puma' }, +]; diff --git a/src/index.css b/src/index.css new file mode 100644 index 00000000..ce9cf86d --- /dev/null +++ b/src/index.css @@ -0,0 +1,53 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +.loading-white { + width: 30px; + height: 30px; + margin: auto; + border: 4px solid white; + border-top-color: transparent; + border-radius: 50%; + animation: loader 1s infinite; +} +.loading-accent { + width: 30px; + height: 30px; + margin: auto; + border: 4px solid #e41717; + border-top-color: transparent; + border-radius: 50%; + animation: loader 1s infinite; +} +@keyframes loader { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +table { + width: 100%; + border-collapse: collapse; + table-layout: auto; +} + +thead { + background-color: #efe9e9; +} + +th, +td { + padding: 16px 8px; + border: 1px solid #ddd; + text-align: center; +} + +.toaster { + border: 2px solid #320b0b; + color: '#713200'; + width: 500px; +} diff --git a/src/lib/ceilPrice.ts b/src/lib/ceilPrice.ts new file mode 100644 index 00000000..63cba31b --- /dev/null +++ b/src/lib/ceilPrice.ts @@ -0,0 +1,5 @@ +export function priceBeforeDiscount(price: number, discountRate: number) { + return ( + Math.ceil((price * 100) / (100 - discountRate) / 1000) * 1000 + ).toLocaleString('ko-KR'); +} diff --git a/src/lib/time.ts b/src/lib/time.ts new file mode 100644 index 00000000..ab470608 --- /dev/null +++ b/src/lib/time.ts @@ -0,0 +1,36 @@ +export function convertToMilliseconds(dateString: string) { + // Convert string to Date object + const date = new Date(dateString); + + // Get timestamp in milliseconds + const timestampMs = date.getTime(); + + return timestampMs; +} + +export function convertToHumanReadable(dateString: string) { + // Convert string to Date object + const date = new Date(dateString); + + // Extract date components + const year = date.getFullYear(); + const month = date.getMonth() + 1; // Months are zero-based + const day = date.getDate(); + + // Extract time components + const hours = date.getHours(); + const minutes = date.getMinutes(); + + // Format the date and time + const formattedDate = `${year}.${month.toString().padStart(2, '0')}.${day + .toString() + .padStart(2, '0')}`; + const formattedTime = `${hours.toString().padStart(2, '0')}:${minutes + .toString() + .padStart(2, '0')}`; + + // Combine the formatted date and time + const humanReadableDateTime = `${formattedDate} / ${formattedTime}`; + + return humanReadableDateTime; +} diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 00000000..34671f92 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import '@/index.css'; +import { BrowserRouter } from 'react-router-dom'; +import App from '@/App'; + +ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( + + + + + +); diff --git a/src/routes/Home.tsx b/src/routes/Home.tsx new file mode 100644 index 00000000..b5b5a692 --- /dev/null +++ b/src/routes/Home.tsx @@ -0,0 +1,12 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import ImageSlider from '@/components/ImageSlider'; +import ProductSection from '@/components/product/ProductSection'; + +export default function Home() { + return ( + <> + + + + ); +} diff --git a/src/routes/Login.tsx b/src/routes/Login.tsx new file mode 100644 index 00000000..12af5685 --- /dev/null +++ b/src/routes/Login.tsx @@ -0,0 +1,111 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { userStore } from '@/store'; +import { signIn } from '@/api/authApi'; +import { ADMINS, EMAIL_REGEX } from '@/constants/constants'; +import Input from '@/components/ui/Input'; +import Button from '@/components/ui/Button'; +import LoadingSpinner from '@/components/ui/LoadingSpinner'; +import SectionTitle from '@/components/ui/SectionTitle'; +import toast from 'react-hot-toast'; + +export default function Login() { + // ์ „์—ญ ๋กœ์ปฌ ์œ ์ €๋ฅผ ์„ธํŒ…ํ•ด์ฃผ๋Š” ํ•จ์ˆ˜ + const { setUser } = userStore(); + + //๋กœ๊ทธ์ธ ํ›„ ์ง์ „์˜ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•˜๊ธฐ ์œ„ํ•ด + const navigate = useNavigate(); + + const [isSending, setIsSending] = useState(false); + const [loginData, setLoginData] = useState({ email: '', password: '' }); + + const handleChange = (event: React.ChangeEvent) => { + const { name, value } = event.target; + setLoginData({ + ...loginData, + [name]: value, + }); + }; + + const handleLogin = async (event: React.FormEvent) => { + // form์ด๋ฒคํŠธ์˜ ๊ธฐ๋ณธ ์ƒˆ๋กœ๊ณ ์นจ์„ ๋ง‰์Œ + event.preventDefault(); + + // ์ด๋ฉ”์ผ๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์ง€ ์•Š์€๊ฒฝ์šฐ + if (loginData.email.trim() === '' || loginData.password.trim() === '') { + toast.error('์ด๋ฉ”์ผ ๋˜๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.', { id: 'login' }); + return; + } + + // ์ด๋ฉ”์ผ์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ + if (!EMAIL_REGEX.test(loginData.email)) { + toast.error('์˜ฌ๋ฐ”๋ฅธ ์ด๋ฉ”์ผ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.', { id: 'login' }); + return; + } + + // ํ†ต์‹  ์‹œ์ž‘ + setIsSending(true); + toast.loading('๋กœ๊ทธ์ธ ์ค‘', { id: 'login' }); + const res = await signIn(loginData); + + // ๋กœ๊ทธ์ธ ์„ฑ๊ณต + if (res.statusCode === 200) { + const user = res.data as UserResponseValue; + // ์–ด๋“œ๋ฏผ ์—ฌ๋ถ€ ํ™•์ธ(๋ณด์•ˆ์ƒ ๋งค์šฐ ์•ˆ์ข‹์Œ) + const isAdmin = ADMINS.includes(user.user.email); + // // ๋กœ์ปฌ ์ €์žฅ์†Œ์— user์ •๋ณด์™€ isAdmin์„ ๋ฌธ์ž์—ดํ™”์‹œ์ผœ์„œ ์ €์žฅ + localStorage.setItem('user', JSON.stringify({ ...user, isAdmin })); + // ๋กœ์ปฌ user์˜ ์ƒํƒœ๋„ ์ €์žฅ + setUser({ ...user, isAdmin }); + setIsSending(false); + navigate(-1); + toast.success(isAdmin ? '์ฃผ์ธ๋‹˜ ์˜ค์…จ์Šต๋‹ˆ๋‹ค!๐Ÿ‘ธ๐Ÿ‘ธ' : res.message, { + id: 'login', + }); + return; + } + + // ๋กœ๊ทธ์ธ ์‹คํŒจ + const errorMessage = res.message; + toast.error(errorMessage, { id: 'login' }); //ํ† ์ŠคํŠธ ๋ฉ”์„ธ์ง€ + setIsSending(false); + }; + + return ( +
+
+ +
+
+ + +
+
+
+
+
+
+ ); +} diff --git a/src/routes/LogoutNeededRoute.tsx b/src/routes/LogoutNeededRoute.tsx new file mode 100644 index 00000000..6b2b685b --- /dev/null +++ b/src/routes/LogoutNeededRoute.tsx @@ -0,0 +1,15 @@ +import { Navigate } from 'react-router-dom'; +import { userStore } from '@/store'; + +type LogoutNeededRouteProps = { + element: React.ReactNode; +}; + +export default function LogoutNeededRoute({ element }: LogoutNeededRouteProps) { + const { userInfo } = userStore(); + + if (userInfo) { + return ; + } + return <>{element}; +} diff --git a/src/routes/NotFound.tsx b/src/routes/NotFound.tsx new file mode 100644 index 00000000..4df7f61e --- /dev/null +++ b/src/routes/NotFound.tsx @@ -0,0 +1,20 @@ +import Button from '@/components/ui/Button'; +import { useNavigate } from 'react-router-dom'; + +export default function NotFound() { + const navigate = useNavigate(); + return ( +
+
+

404

+

+ ํŽ˜๋‹น ํŽ˜์ด์ง€๋Š” ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. +

+

+ Sorry, the page you are looking for does not exist. +

+
+
+ ); +} diff --git a/src/routes/ProductDetail.tsx b/src/routes/ProductDetail.tsx new file mode 100644 index 00000000..df042b85 --- /dev/null +++ b/src/routes/ProductDetail.tsx @@ -0,0 +1,242 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import { getProductDetail } from '@/api/adminApi'; +import SectionTitle from '@/components/ui/SectionTitle'; +import { useEffect, useMemo, useState } from 'react'; +import { getAccountListAndBalance } from '@/api/bankApi'; +import { userStore } from '@/store'; +import { buyProduct } from '@/api/transactionApi'; +import { useNavigate, useParams } from 'react-router-dom'; +import Button from '@/components/ui/Button'; +import toast from 'react-hot-toast'; +import CrazyLoading from '@/components/ui/CrazyLoading'; +import Breadcrumbs from '@/components/ui/Breadcrumbs'; +import Select from '@/components/ui/Select'; +import LoadingSpinner from '@/components/ui/LoadingSpinner'; +import { AiOutlineEdit } from 'react-icons/ai'; +import ProductSection from '@/components/product/ProductSection'; +import { priceBeforeDiscount } from '@/lib/ceilPrice'; + +export default function ProductDetail() { + const navigate = useNavigate(); + const { productId } = useParams(); + const [isLoading, setIsLoading] = useState(false); + const [product, setProduct] = useState(); + const [accounts, setAccounts] = useState([]); + const { userInfo } = userStore(); + const [selectedAccount, setSelectedAccount] = useState(''); + const [isPurchasing, setIsPurchasing] = useState(false); + + useEffect(() => { + async function fetchData() { + setIsLoading(true); + const res = await getProductDetail(productId as string); + if (res.statusCode === 200) { + setProduct(res.data as ProductDetail); + setIsLoading(false); + return; + } + toast.error(res.message, { id: 'getProduct' }); + navigate('/'); + setIsLoading(false); + } + fetchData(); + }, [productId]); + + useEffect(() => { + async function fetchData() { + const res = await getAccountListAndBalance( + userInfo?.accessToken as string + ); + if (res.statusCode === 200) { + setAccounts((res.data as AccountsAndBalance).accounts); + return; + } + } + fetchData(); + }, [userInfo?.accessToken, isPurchasing]); + + const accountOptions = useMemo( + () => + accounts.length > 0 + ? [ + { name: '๊ฒฐ์ œํ•  ๊ณ„์ขŒ ์„ ํƒ', value: '' }, + ...accounts.map((account) => ({ + name: `${account.bankName} / ${ + account.accountNumber + } / ์ž”์•ก : ${account.balance.toLocaleString('ko-KR')}์›`, + value: account.id, + })), + ] + : [{ name: '๊ณ„์ขŒ๋ฅผ ๋จผ์ € ๋“ฑ๋กํ•ด์ฃผ์„ธ์š”', value: '' }], + [accounts] + ); + + const handlePurchase = async () => { + if (!selectedAccount) { + toast.error('๊ณ„์ขŒ๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š”.', { id: 'buyProduct' }); + return; + } + setIsPurchasing(true); + toast.loading('๊ฒฐ์ œ ์š”์ฒญ ์ค‘...', { id: 'buyProduct' }); + const res = await buyProduct( + productId as string, + selectedAccount, + userInfo?.accessToken as string + ); + if (res.statusCode === 200) { + setIsPurchasing(false); + toast.success(`${product?.title}๋ฅผ ๊ตฌ๋งคํ•˜์˜€์Šต๋‹ˆ๋‹ค!`, { + id: 'buyProduct', + }); + return; + } + setIsPurchasing(false); + toast.error(res.message, { id: 'buyProduct' }); + }; + + const toEditPage = (productId: string, productTitle: string) => { + navigate('/admin/editproduct', { state: { productId, productTitle } }); + }; + + const handleAlarm = () => { + setIsPurchasing(true); + setTimeout(() => { + toast.success(`${product?.title} ์ž…๊ณ ์‹œ ์•Œ๋ฆผ๋ฌธ์ž ๋ฐœ์†ก!`); + setIsPurchasing(false); + }, 500); + }; + + return ( +
+ {isLoading ? ( + + ) : ( +
+ +
+
+ {product?.title} + soldout +
+
+
+ {product?.title} + {userInfo?.isAdmin ? ( + + toEditPage( + product?.id as string, + product?.title as string + ) + } + /> + ) : ( + <> + )} +
+
+ + {product?.price.toLocaleString('ko-KR')}์› + + {product?.discountRate === 0 ? ( + <> + ) : ( + <> + + {priceBeforeDiscount( + product?.price as number, + product?.discountRate as number + )} + ์› + + + {product?.discountRate}% + + + )} +
+
{product?.description}
+ {userInfo ? ( +
+ + + + + +
+
+
+ +
+
+ ); +} diff --git a/src/routes/admin/AddProduct.tsx b/src/routes/admin/AddProduct.tsx new file mode 100644 index 00000000..6ce4856f --- /dev/null +++ b/src/routes/admin/AddProduct.tsx @@ -0,0 +1,190 @@ +import { useState } from 'react'; +import { addProduct } from '@/api/adminApi'; +import Input from '@/components/ui/Input'; +import Button from '@/components/ui/Button'; +import LoadingSpinner from '@/components/ui/LoadingSpinner'; +import ImageUpload from '@/components/ui/ImageUpload'; +import Select from '@/components/ui/Select'; +import { SELECT_BRAND, SELECT_CATEGORY } from '@/constants/constants'; +import SectionTitle from '@/components/ui/SectionTitle'; +import toast from 'react-hot-toast'; +import { useNavigate } from 'react-router-dom'; + +export default function AddProduct() { + const navigate = useNavigate(); + + // ์ƒํ’ˆ์˜ input๊ฐ’ state, price์™€ discountRate์˜ ์›๋ž˜ ํƒ€์ž…์€ number์ด๋‚˜ ์ผ๋‹จ string์œผ๋กœ ๋ฐ›๊ณ  ํ†ต์‹ ์„ ๋ณด๋‚ผ ๋•Œ number๋กœ ๋ฐ”๊ฟ”์ค„ ์˜ˆ์ • + const [productInputData, setProductInputData] = useState({ + title: '', + price: '', + description: '', + tags: ['', ''], + thumbnailBase64: '', + photoBase64: '', + discountRate: '', + isSoldOut: false, + }); + + const [isSending, setIsSending] = useState(false); + + const handleChange = ( + // event ์ค‘ input ์š”์†Œ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ select ์š”์†Œ๋„ ์žˆ์œผ๋ฏ€๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด generic์œผ๋กœ ์ง€์ • + event: React.ChangeEvent + ) => { + const { name, value, type } = event.target; + + // ์ด๋ฏธ์ง€ ํŒŒ์ผ + if (type === 'file') { + const files = (event.target as HTMLInputElement).files as FileList; + const reader = new FileReader(); + reader.readAsDataURL(files[0]); + reader.onloadend = () => { + setProductInputData((prevData) => ({ + ...prevData, + [name]: reader.result as string, + })); + }; + // select๋กœ tags์˜ element๋ฅผ ์ง€์ •ํ•˜๋Š” ๋กœ์ง + // tags = ["์นดํ…Œ๊ณ ๋ฆฌ(์ถ•๊ตฌํ™”, ์กฑ๊ตฌํ™”...)", "๋ธŒ๋žœ๋“œ(๋‚˜์ดํ‚ค, ์•„๋””๋‹ค์Šค...)"] + } else if (name === 'category') { + setProductInputData((prevData) => ({ + ...prevData, + tags: [value, prevData.tags?.[1] || ''], + })); + } else if (name === 'brand') { + setProductInputData((prevData) => ({ + ...prevData, + tags: [prevData.tags?.[0] || '', value], + })); + } else { + setProductInputData((prevData) => ({ + ...prevData, + [name]: value, + })); + } + }; + + const handleSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + + // ์ƒํ’ˆ์ด๋ฆ„ or ์ƒํ’ˆ๊ฐ€๊ฒฉ or ์ƒํ’ˆ์„ค๋ช…์„ ์ž…๋ ฅํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ + if ( + productInputData.title.trim() === '' || + productInputData.price.trim() === '' || + productInputData.description.trim() === '' || + productInputData.tags[0] === '' || + productInputData.tags[0] === 'category' || + productInputData.tags[1] === '' || + productInputData.tags[1] === 'brand' + ) { + toast.error( + '์นดํ…Œ๊ณ ๋ฆฌ, ๋ธŒ๋žœ๋“œ, ์ƒํ’ˆ์ด๋ฆ„, ๊ฐ€๊ฒฉ, ์ƒํ’ˆ์„ค๋ช…์„ ๋ชจ๋‘ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.', + { id: 'addProduct' } + ); + return; + } + + // ํ• ์ธ์œจ 0 ~ 99 ๊ฐ€ ์•„๋‹Œ๊ฒฝ์šฐ + if (productInputData.discountRate) { + if ( + !Number(productInputData.discountRate) || + Number(productInputData.discountRate) <= 0 || + Number(productInputData.discountRate) >= 100 + ) { + toast.error('ํ• ์ธ์œจ์€ 0 ~ 99๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.', { id: 'addProduct' }); + return; + } + } + + setIsSending(true); + toast.loading('์ƒํ’ˆ ๋“ฑ๋ก ์ค‘ ์ž…๋‹ˆ๋‹ค.', { id: 'addProduct' }); + const res = await addProduct({ + ...productInputData, + discountRate: Number(productInputData.discountRate), + price: Number(productInputData.price), + }); + + // ์ƒํ’ˆ๋“ฑ๋ก์ด ์„ฑ๊ณตํ•œ ๊ฒฝ์šฐ + if (res.statusCode === 200) { + toast.success(res.message, { id: 'addProduct' }); + setIsSending(false); + navigate(`/products/${res.data?.tags[0]}/${res.data?.id}`); + return; + } + // ์ƒํ’ˆ๋“ฑ๋ก์ด ์‹คํŒจํ•œ ๊ฒฝ์šฐ + toast.error(res.message, { id: 'addProduct' }); + setIsSending(false); + }; + + return ( +
+
+ +
+
+
+ + + + + +
+
+ + +
+
+
+
+
+ +
+
+ ); +} diff --git a/src/routes/admin/Admin.tsx b/src/routes/admin/Admin.tsx new file mode 100644 index 00000000..eb570539 --- /dev/null +++ b/src/routes/admin/Admin.tsx @@ -0,0 +1,26 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import SubNavbar from '@/components/SubNavbar'; +import { SUB_MENUS_ADMIN } from '@/constants/constants'; +import { userStore } from '@/store'; +import { useEffect } from 'react'; +import { toast } from 'react-hot-toast'; +import { Outlet } from 'react-router-dom'; + +export default function Admin() { + const { authMe } = userStore(); + useEffect(() => { + async function auth() { + const errorMessage = await authMe(); + if (errorMessage) { + toast.error(errorMessage, { id: 'authMe' }); + } + } + auth(); + }, []); + return ( + <> + + + + ); +} diff --git a/src/routes/admin/AdminClients.tsx b/src/routes/admin/AdminClients.tsx new file mode 100644 index 00000000..ea40f19f --- /dev/null +++ b/src/routes/admin/AdminClients.tsx @@ -0,0 +1,98 @@ +import { useEffect, useMemo, useState } from 'react'; +import { getAllTransactions, getClients } from '@/api/adminApi'; +import SingleUser from '@/components/SingleUser'; +import { ADMINS } from '@/constants/constants'; +import toast from 'react-hot-toast'; +import SectionTitle from '@/components/ui/SectionTitle'; +import CrazyLoading from '@/components/ui/CrazyLoading'; + +export default function AdminClients() { + const [clients, setClients] = useState([]); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + setIsLoading(true); + async function fetchData() { + const promiseClients = getClients(); + const promiseTansactions = getAllTransactions(); + // ํ†ต์‹ ์„ ๋‘๊ฐœ ๋Œ๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์— ๋กœ๋”ฉ์‹œ๊ฐ„์ด ๊ธป๋‹ˆ๋‹ค. + const [res1, res2] = await Promise.all([ + promiseClients, + promiseTansactions, + ]); + if (res1.statusCode === 200 && res2.statusCode === 200) { + const spentMoneyIncludedClients = (res1.data as Client[]).map( + (client) => { + // ๊ฐ ๊ณ ๊ฐ๋งˆ๋‹ค ์†Œ๋น„๊ธˆ์•ก์„ ๊ตฌํ•˜๋Š” ๋กœ์ง + const spentMoney = (res2.data as TransactionDetail[]) + // ๊ณ ๊ฐ์ด๋ฉ”์ผ๊ณผ ๊ฑฐ๋ž˜์ •๋ณด ์ด๋ฉ”์ผ์ด ์ผ์น˜ํ•˜๋ฉด์„œ ์™„๋ฃŒ๋œ ๊ฑฐ๋ž˜ ํ•„ํ„ฐ๋ง + .filter( + (transaction) => + transaction.user.email === client.email && transaction.done + ) + // ๊ฑฐ๋ž˜ ๊ธˆ์•ก์„ ๋‹ค ๋”ํ•จ + .reduce((acc, curr) => acc + curr.product.price, 0); + + return { + ...client, + spentMoney, + }; + } + ); + setClients(spentMoneyIncludedClients); + setIsLoading(false); + return; + } + toast.error(res1.message, { id: 'getClients' }); + setIsLoading(false); + } + fetchData(); + }, []); + + const adminCount = useMemo( + () => clients.filter((user) => ADMINS.includes(user.email)).length, + [clients] + ); + + return ( + <> + {isLoading ? ( + + ) : ( +
+
+ + +
+ + + + + + + + + + + + {clients.map((user) => { + return ( + + ); + })} + +
ํ”„๋กœํ•„์‚ฌ์ง„์ด๋ฉ”์ผ๋‹‰๋„ค์ž„ + ๋“ฑ๊ธ‰(๐Ÿ’ฐVIP : 30๋งŒ์›, + ๐Ÿ’ฐVVIP๐Ÿ’ฐ : 50๋งŒ์›) +
+
+ )} + + ); +} diff --git a/src/routes/admin/AdminProducts.tsx b/src/routes/admin/AdminProducts.tsx new file mode 100644 index 00000000..d1ca049e --- /dev/null +++ b/src/routes/admin/AdminProducts.tsx @@ -0,0 +1,138 @@ +import { useEffect, useState } from 'react'; +import { deleteProduct, getProducts } from '@/api/adminApi.ts'; +import { useNavigate } from 'react-router-dom'; +import Button from '@/components/ui/Button.tsx'; +import SectionTitle from '@/components/ui/SectionTitle'; +import { DICTIONARY_SHOES } from '@/constants/constants'; +import toast from 'react-hot-toast'; +import CrazyLoading from '@/components/ui/CrazyLoading'; + +export default function AdminProduct() { + const [products, setProducts] = useState(); + const [isLoading, setIsLoading] = useState(false); + const navigate = useNavigate(); + + useEffect(() => { + setIsLoading(true); + const fetchData = async () => { + const res = await getProducts(); + if (res.statusCode === 200) { + setProducts(res.data as Product[]); + setIsLoading(false); + return; + } + toast.error(res.message, { id: 'getProducts' }); + setIsLoading(false); + }; + fetchData(); + }, []); + + const toDetailPage = (category: string, productId: string) => { + navigate(`/products/${category}/${productId}`); + }; + + const toEditPage = ( + event: React.MouseEvent, + productId: string, + productTitle: string + ) => { + // ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋•Œ ์ƒ์„ธ ํŽ˜์ด์ง€๋กœ ๊ฐ€์ง€ ์•Š๊ธฐ ์œ„ํ•ด + event.stopPropagation(); + // ์ƒ์„ธ ํŽ˜์ด์ง€๋กœ state๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‘๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. + // 1. "/admin/editproduct/:productId"์™€ ๊ฐ™์ด url๋กœ ๋ณด๋‚ด๋Š” ๋ฐฉ๋ฒ• + // 2. history api์˜ state๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•, ์ด ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. + navigate('/admin/editproduct', { state: { productId, productTitle } }); + }; + + const handleDelete = async ( + event: React.MouseEvent, + productId: string, + productTitle: string + ) => { + event.stopPropagation(); + if (confirm(`${productTitle}๋ฅผ ์‚ญ์ œํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?`)) { + toast.loading(`${productTitle} ์‚ญ์ œ ์ค‘...`, { + id: 'deleteProduct', + }); + const res = await deleteProduct(productId); + if (res.statusCode === 200) { + toast.success(`${productTitle}๋ฅผ ์‚ญ์ œํ•˜์˜€์Šต๋‹ˆ๋‹ค.`, { + id: 'deleteProduct', + }); + setProducts(products?.filter((product) => product.id !== productId)); + return; + } + toast.error(res.message, { id: 'deleteProduct' }); + } + }; + + return ( + <> + {isLoading ? ( + + ) : ( +
+ + + + + + + + + + + + + + + {products?.map((product) => { + return ( + toDetailPage(product.tags[0], product.id)} + className="cursor-pointer hover:opacity-70" + > + + + + + + + + + + ); + })} + +
์‚ฌ์ง„์ƒํ’ˆ๋ช…์ƒํ’ˆ๊ฐ€๊ฒฉ(์›)์นดํ…Œ๊ณ ๋ฆฌ๋ธŒ๋žœ๋“œ์žฌ๊ณ ํ• ์ธ์œจ +
+ {product.title} + +

+ {product.title} +

+
{product.price.toLocaleString('ko-KR')}{DICTIONARY_SHOES[product.tags[0]]}{product.tags[1].toUpperCase()}{product.isSoldOut ? 'โŒ' : '๐Ÿ”˜'}{product.discountRate}% +
+
+ )} + + ); +} diff --git a/src/routes/admin/AllTransactions.tsx b/src/routes/admin/AllTransactions.tsx new file mode 100644 index 00000000..f79aa453 --- /dev/null +++ b/src/routes/admin/AllTransactions.tsx @@ -0,0 +1,89 @@ +import { useEffect, useMemo, useState } from 'react'; +import { getAllTransactions } from '@/api/adminApi'; +import toast from 'react-hot-toast'; +import CrazyLoading from '@/components/ui/CrazyLoading'; +import { convertToHumanReadable } from '@/lib/time'; +import SectionTitle from '@/components/ui/SectionTitle'; + +export default function TransactionsPage() { + const [transactions, setTransactions] = useState([]); + const [searchTerm, setSearchTerm] = useState(''); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + const fetchData = async () => { + setIsLoading(true); + const res = await getAllTransactions(); + if (res.statusCode === 200) { + setTransactions(res.data as TransactionDetail[]); + setIsLoading(false); + return; + } + toast.error(res.message, { id: 'getTransactions' }); + setIsLoading(false); + }; + fetchData(); + }, []); + + const filteredTransactions = useMemo( + () => + transactions.filter( + (transaction) => + transaction.user.displayName.includes(searchTerm) || + transaction.product.title.includes(searchTerm) + ), + [searchTerm, transactions] + ); + + return ( + <> + {isLoading ? ( + + ) : ( +
+ +
+ setSearchTerm(e.target.value)} + placeholder="์œ ์ €๋ช… ๋˜๋Š” ์ƒํ’ˆ ์ด๋ฆ„ ๊ฒ€์ƒ‰" + className="w-full border px-3 py-2" + /> +
+ + + + + + + + + + + + + + + {filteredTransactions.map((transaction, index) => ( + + + + + + + + + + ))} + +
์ƒํ’ˆ ์ด๋ฏธ์ง€๊ณ ๊ฐ์ƒํ’ˆ ์ด๋ฆ„๊ฐ€๊ฒฉ(์›)๊ฑฐ๋ž˜ ์‹œ๊ฐ„๊ฑฐ๋ž˜ ์ทจ์†Œ๊ฑฐ๋ž˜ ์™„๋ฃŒ
+ thumbnail + {transaction.user.displayName}{transaction.product.title}{transaction.product.price.toLocaleString('ko-KR')}{convertToHumanReadable(transaction.timePaid)}{transaction.isCanceled ? '์ทจ์†Œํ•จ' : '์ทจ์†Œํ•˜์ง€ ์•Š์Œ'}{transaction.done ? '๐Ÿ”˜' : 'โŒ'}
+
+ )} + + ); +} diff --git a/src/routes/admin/EditProduct.tsx b/src/routes/admin/EditProduct.tsx new file mode 100644 index 00000000..242c25a5 --- /dev/null +++ b/src/routes/admin/EditProduct.tsx @@ -0,0 +1,254 @@ +import { useEffect, useState } from 'react'; +import { getProductDetail, updateProduct } from '@/api/adminApi'; +import Input from '@/components/ui/Input'; +import Button from '@/components/ui/Button'; +import LoadingSpinner from '@/components/ui/LoadingSpinner'; +import Select from '@/components/ui/Select'; +import { SELECT_BRAND, SELECT_CATEGORY } from '@/constants/constants'; +import { useLocation, useNavigate } from 'react-router-dom'; +import { toast } from 'react-hot-toast'; +import CrazyLoading from '@/components/ui/CrazyLoading'; +import SectionTitle from '@/components/ui/SectionTitle'; +import ImageUpload from '@/components/ui/ImageUpload'; + +export default function EditProduct() { + const navigate = useNavigate(); + // navigate๋กœ๋ถ€ํ„ฐ ๋ฐ›์€ state์ •๋ณด๋“ค + const { + state: { productId, productTitle }, + } = useLocation(); + + // ์–˜๋„ ์ผ๋‹จ์€ ๋ชจ๋‘ string์œผ๋กœ ์ดˆ๊ธฐ๊ฐ’ ์„ธํŒ… + const [detailProduct, setDetailProduct] = useState({ + title: '', + price: '', + description: '', + tags: ['', ''], + thumbnailBase64: '', + photoBase64: '', + discountRate: '', + isSoldOut: '', + }); + const [isLoading, setIsLoading] = useState(false); + const [isSending, setIsSending] = useState(false); + const [isImageUpdate, setIsImageUpdate] = useState(false); + + useEffect(() => { + setIsLoading(true); + const fetchProducts = async () => { + const res = await getProductDetail(productId); + if (res.statusCode === 200) { + const data = res.data as ProductDetail; + setDetailProduct({ + // string์ด ์•„๋‹Œ ํ•ญ๋ชฉ์€ string์œผ๋กœ ๋ณ€ํ™˜ํ›„ ์„ธํŒ…ํ•ด์คŒ + title: data.title, + price: data.price.toString(), + description: data.description, + tags: data.tags, + thumbnailBase64: data.thumbnail, + photoBase64: data.photo, + discountRate: data.discountRate.toString(), + isSoldOut: data.isSoldOut.toString(), + }); + setIsLoading(false); + return; + } + toast.error(res.message, { id: 'getProductDetail' }); + setIsLoading(false); + }; + fetchProducts(); + }, [productId]); + + const handleChange = ( + event: React.ChangeEvent + ) => { + const { name, value, type } = event.target; + setIsImageUpdate(false); + if (type === 'file') { + // ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œ ์—ฌ๋ถ€๋ฅผ state๋กœ ๊ด€๋ฆฌ + setIsImageUpdate(true); + const files = (event.target as HTMLInputElement).files as FileList; + const reader = new FileReader(); + reader.readAsDataURL(files[0]); + reader.onloadend = () => { + setDetailProduct((prevData) => { + return { + ...prevData, + [name]: reader.result as string, + }; + }); + }; + } else if (name === 'category') { + setDetailProduct((prevData) => { + return { + ...prevData, + tags: [value, prevData?.tags?.[1] || ''], + }; + }); + } else if (name === 'brand') { + setDetailProduct((prevData) => { + return { + ...prevData, + tags: [prevData?.tags?.[0] || '', value], + }; + }); + } else { + setDetailProduct((prevdata) => { + return { + ...prevdata, + [name]: value, + }; + }); + } + }; + + const handleSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + + // ์ƒํ’ˆ์ด๋ฆ„ or ์ƒํ’ˆ๊ฐ€๊ฒฉ or ์ƒํ’ˆ์„ค๋ช…์„ ์ž…๋ ฅํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ + if ( + detailProduct?.title.trim() === '' || + detailProduct?.price.trim() === '' || + detailProduct?.description.trim() === '' + ) { + toast.error('์ƒํ’ˆ์ด๋ฆ„, ๊ฐ€๊ฒฉ, ์ƒํ’ˆ์„ค๋ช…์„ ๋ชจ๋‘ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.', { + id: 'updateProduct', + }); + return; + } + + // ํ• ์ธ์œจ 0 ~ 99 ๊ฐ€ ์•„๋‹Œ๊ฒฝ์šฐ + if ( + Number(detailProduct.discountRate) < 0 || + Number(detailProduct.discountRate) >= 100 + ) { + toast.error('ํ• ์ธ์œจ์€ 0 ~ 99๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.', { id: 'updateProduct' }); + return; + } + setIsSending(true); + const res = await updateProduct( + productId, + // ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œ ํ•œ ๊ฒฝ์šฐ์—๋งŒ 'thumbnailBase64', 'photoBase64' ํ‚ค๊ฐ’์„ ์ถ”๊ฐ€ํ•ด์„œ ๋ณด๋ƒ„ + // ๊ทธ๋Ÿฐ๋ฐ ๋‘๊ฐœ์˜ ์ด๋ฏธ์ง€ ์ค‘์— ํ•˜๋‚˜๋งŒ ์—…๋กœ๋“œ๋ฅผ ํ•  ๊ฒฝ์šฐ ๋‚˜๋จธ์ง€ ํ•˜๋‚˜์˜ ๊ฐ’์ด base64๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•จ. + // ๋”ฐ๋ผ์„œ ์ˆ˜์ •ํ• ๊ฑฐ๋ผ๋ฉด ๋‘๊ฐœ ๋‹ค ์—…๋กœ๋“œํ•ด์•ผํ•จ. + isImageUpdate + ? { + title: detailProduct.title, + price: Number(detailProduct.price), + description: detailProduct.description, + tags: detailProduct.tags, + discountRate: Number(detailProduct?.discountRate), + isSoldOut: detailProduct.isSoldOut === 'true', + thumbnailBase64: detailProduct?.thumbnailBase64, + photoBase64: detailProduct?.photoBase64, + } + : { + title: detailProduct.title, + price: Number(detailProduct.price), + description: detailProduct.description, + tags: detailProduct.tags, + discountRate: Number(detailProduct?.discountRate), + isSoldOut: detailProduct.isSoldOut === 'true', + } + ); + if (res.statusCode === 200) { + toast.success(res.message, { id: 'updateProduct' }); + setIsSending(false); + navigate(`/products/${detailProduct.tags[0]}/${productId}`); + return; + } + toast.error(res.message, { id: 'updateProduct' }); + setIsSending(false); + }; + + return ( + <> + {isLoading ? ( + + ) : ( +
+
+ +
+
+
+ + + + + + +
+
+ ); +} diff --git a/src/routes/myAccount/ChangePassword.tsx b/src/routes/myAccount/ChangePassword.tsx new file mode 100644 index 00000000..c2a99724 --- /dev/null +++ b/src/routes/myAccount/ChangePassword.tsx @@ -0,0 +1,132 @@ +import { useState } from 'react'; +import { userStore } from '@/store'; +import { editUser } from '@/api/authApi'; +import Input from '@/components/ui/Input'; +import Button from '@/components/ui/Button'; +import LoadingSpinner from '@/components/ui/LoadingSpinner'; +import toast from 'react-hot-toast'; +import SectionTitle from '@/components/ui/SectionTitle'; +import { useNavigate } from 'react-router-dom'; + +export default function ChangePassword() { + const navigate = useNavigate(); + const [isSending, setIsSending] = useState(false); + const { userInfo } = userStore(); + const [editData, setEditData] = useState({ + oldPassword: '', + newPassword: '', + checkPassword: '', + }); + + // ์„œ๋ฒ„์™€ ์ „์†ก์ƒํƒœ์— ๋”ฐ๋ผ ๋ฒ„ํŠผ์˜ ์ƒํƒœ๋ฅผ ๋ฐ”๊ฟ”์ฃผ๊ธฐ ์œ„ํ•ด์„œ ์Šคํ…Œ์ดํŠธ ์ง€์ • + // ์—๋Ÿฌ๋ฉ”์„ธ์ง€ ํƒ€์ž„์•„์›ƒ + const [timeoutId, setTimeoutId] = useState(null); + + const handleChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setEditData({ + ...editData, + [name]: value, + }); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + // ์ด์ „ ํƒ€์ž„์•„์›ƒ์ด ์•„์ง ์ž‘๋™์ค‘์ด ์ดˆ๊ธฐํ™” + if (timeoutId) { + clearTimeout(timeoutId); + setTimeoutId(null); + } + + // ๋ณ€๊ฒฝ์ „ ๋น„๋ฒˆ, ๋ณ€๊ฒฝ ํ›„ ๋น„๋ฒˆ ์ž…๋ ฅ ์•ˆํ–ˆ์„ ๋•Œ + if ( + editData.newPassword.trim() === '' || + editData.oldPassword.trim() === '' + ) { + toast.error('๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.', { id: 'changePassword' }); + return; + } + // ๋ณ€๊ฒฝ ๋น„๋ฒˆ ํ™•์ธ + if (editData.checkPassword !== editData.newPassword) { + toast.error('๋ณ€๊ฒฝ ํ•  ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.', { + id: 'changePassword', + }); + return; + } + // ๋ณ€๊ฒฝ ๋น„๋ฒˆ ๊ธธ์ด ์ˆ˜ + if (editData.newPassword.length <= 7) { + toast.error('๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ 8์ž๋ฆฌ ์ด์ƒ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.', { + id: 'changePassword', + }); + return; + } + + setIsSending(true); + toast.loading('๋น„๋ฐ€๋ฒˆํ˜ธ ์ˆ˜์ • ์ค‘', { + id: 'changePassword', + }); + const res = await editUser(userInfo?.accessToken as string, { + newPassword: editData.newPassword, + oldPassword: editData.oldPassword, + }); + + if (res.statusCode === 200) { + const data = res.data as UpdatedUserResponseValue; + toast.success(`${data.displayName}๋‹˜์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.`, { + id: 'changePassword', + }); + setEditData({ + oldPassword: '', + newPassword: '', + checkPassword: '', + }); + setIsSending(false); + navigate('/myaccount/info'); + return; + } + toast.error(res.message, { + id: 'changePassword', + }); + setIsSending(false); + }; + + return ( +
+ +
+
+ + + +
+
+ ); +} diff --git a/src/routes/myAccount/Info.tsx b/src/routes/myAccount/Info.tsx new file mode 100644 index 00000000..e7b3242b --- /dev/null +++ b/src/routes/myAccount/Info.tsx @@ -0,0 +1,94 @@ +import { useState } from 'react'; +import ImageUpload from '@/components/ui/ImageUpload'; +import ProfileImage from '@/components/ui/ProfileImage'; +import { userStore } from '@/store'; +import Button from '@/components/ui/Button'; +import { editUser } from '@/api/authApi'; +import LoadingSpinner from '@/components/ui/LoadingSpinner'; +import toast from 'react-hot-toast'; +import { AiOutlineEdit } from 'react-icons/ai'; +import { useNavigate } from 'react-router-dom'; +import SectionTitle from '@/components/ui/SectionTitle'; + +export default function Info() { + const navigate = useNavigate(); + const { authMe } = userStore(); + const { userInfo } = userStore(); + const [profileImage, setProfileImage] = useState( + userInfo?.user.profileImg as string + ); + const [isSending, setIsSending] = useState(false); + + const handleChange = (event: React.ChangeEvent) => { + const files = event.target.files as FileList; + const reader = new FileReader(); + reader.readAsDataURL(files[0]); + reader.onloadend = () => { + setProfileImage(reader.result as string); + }; + }; + + const handleSubmit = async ( + event: React.MouseEvent + ) => { + event.preventDefault(); + + if (profileImage === '') { + toast.error('๋ณ€๊ฒฝํ•  ์ด๋ฏธ์ง€๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š”.', { id: 'profileImage' }); + return; + } + + // ์‚ฌ์ง„์„ ์„ ํƒํ•˜์ง€ ์•Š์•˜์„ ๊ฒฝ์šฐ ๋ฌธ์ œ ๋ฐœ์ƒํ•จ..!! + setIsSending(true); + toast.loading('ํ”„๋กœํ•„ ์‚ฌ์ง„ ๋ณ€๊ฒฝ ์ค‘', { id: 'profileImage' }); + const res = await editUser(userInfo?.accessToken as string, { + profileImgBase64: profileImage as string, + }); + if (res.statusCode === 200) { + const updatedUser = res.data as UpdatedUserResponseValue; + toast.success(`${updatedUser.displayName}๋‹˜! ์ƒˆ๋กœ์šด ์‚ฌ์ง„ ๋ฉ‹์ ธ์š”!๐Ÿ˜๐Ÿ˜`, { + id: 'profileImage', + }); + setIsSending(false); + // navbar์— ์žˆ๋Š” ํ”„๋กœํ•„ ์‚ฌ์ง„๋„ ์ƒˆ๋กœ์šด ์‚ฌ์ง„์œผ๋กœ ๋ฐ”๊ฟ”์ฃผ๊ธฐ ์œ„ํ•ด + setProfileImage(updatedUser.profileImg as string); + await authMe(); + return; + } + const errorMessage = res.message; + toast.error(errorMessage, { id: 'profileImage' }); + setIsSending(false); + }; + + return ( +
+ +
+
+ +
+

+ {userInfo?.user.displayName} +

+ { + navigate('/myaccount/changename'); + }} + /> +
+ + +
+
+
+ ); +} diff --git a/src/routes/myAccount/Login.tsx b/src/routes/myAccount/Login.tsx new file mode 100644 index 00000000..83d53131 --- /dev/null +++ b/src/routes/myAccount/Login.tsx @@ -0,0 +1,111 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { userStore } from '../../store'; +import { signIn } from '../../api/authApi'; +import { ADMINS, EMAIL_REGEX } from '../../constants/constants'; +import Input from '../../components/ui/Input'; +import Button from '../../components/ui/Button'; +import LoadingSpinner from '../../components/ui/LoadingSpinner'; +import SectionTitle from '@/components/ui/SectionTitle'; +import toast from 'react-hot-toast'; + +export default function Login() { + // ์ „์—ญ ๋กœ์ปฌ ์œ ์ €๋ฅผ ์„ธํŒ…ํ•ด์ฃผ๋Š” ํ•จ์ˆ˜ + const { setUser } = userStore(); + + //๋กœ๊ทธ์ธ ํ›„ ์ง์ „์˜ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•˜๊ธฐ ์œ„ํ•ด + const navigate = useNavigate(); + + const [isSending, setIsSending] = useState(false); + const [loginData, setLoginData] = useState({ email: '', password: '' }); + + const handleChange = (event: React.ChangeEvent) => { + const { name, value } = event.target; + setLoginData({ + ...loginData, + [name]: value, + }); + }; + + const handleLogin = async (event: React.FormEvent) => { + // form์ด๋ฒคํŠธ์˜ ๊ธฐ๋ณธ ์ƒˆ๋กœ๊ณ ์นจ์„ ๋ง‰์Œ + event.preventDefault(); + + // ์ด๋ฉ”์ผ๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์ง€ ์•Š์€๊ฒฝ์šฐ + if (loginData.email.trim() === '' || loginData.password.trim() === '') { + toast.error('์ด๋ฉ”์ผ ๋˜๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.', { id: 'login' }); + return; + } + + // ์ด๋ฉ”์ผ์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ + if (!EMAIL_REGEX.test(loginData.email)) { + toast.error('์˜ฌ๋ฐ”๋ฅธ ์ด๋ฉ”์ผ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.', { id: 'login' }); + return; + } + + // ํ†ต์‹  ์‹œ์ž‘ + setIsSending(true); + toast.loading('๋กœ๊ทธ์ธ ์ค‘', { id: 'login' }); + const res = await signIn(loginData); + + // ๋กœ๊ทธ์ธ ์„ฑ๊ณต + if (res.statusCode === 200) { + const user = res.data as UserResponseValue; + // ์–ด๋“œ๋ฏผ ์—ฌ๋ถ€ ํ™•์ธ(๋ณด์•ˆ์ƒ ๋งค์šฐ ์•ˆ์ข‹์Œ) + const isAdmin = ADMINS.includes(user.user.email); + // // ๋กœ์ปฌ ์ €์žฅ์†Œ์— user์ •๋ณด์™€ isAdmin์„ ๋ฌธ์ž์—ดํ™”์‹œ์ผœ์„œ ์ €์žฅ + localStorage.setItem('user', JSON.stringify({ ...user, isAdmin })); + // ๋กœ์ปฌ user์˜ ์ƒํƒœ๋„ ์ €์žฅ + setUser({ ...user, isAdmin }); + setIsSending(false); + navigate(-1); + toast.success(isAdmin ? '์ฃผ์ธ๋‹˜ ์˜ค์…จ์Šต๋‹ˆ๋‹ค!๐Ÿ‘ธ๐Ÿ‘ธ' : res.message, { + id: 'login', + }); + return; + } + + // ๋กœ๊ทธ์ธ ์‹คํŒจ + const errorMessage = res.message; + toast.error(errorMessage, { id: 'login' }); + setIsSending(false); + }; + + return ( +
+
+ +
+
+ + +
+
+
+
+
+
+ ); +} diff --git a/src/routes/myAccount/LogoutNeededRoute.tsx b/src/routes/myAccount/LogoutNeededRoute.tsx new file mode 100644 index 00000000..6b2b685b --- /dev/null +++ b/src/routes/myAccount/LogoutNeededRoute.tsx @@ -0,0 +1,15 @@ +import { Navigate } from 'react-router-dom'; +import { userStore } from '@/store'; + +type LogoutNeededRouteProps = { + element: React.ReactNode; +}; + +export default function LogoutNeededRoute({ element }: LogoutNeededRouteProps) { + const { userInfo } = userStore(); + + if (userInfo) { + return ; + } + return <>{element}; +} diff --git a/src/routes/myAccount/MyAccount.tsx b/src/routes/myAccount/MyAccount.tsx new file mode 100644 index 00000000..c33626e9 --- /dev/null +++ b/src/routes/myAccount/MyAccount.tsx @@ -0,0 +1,27 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import { Outlet } from 'react-router-dom'; +import SubNavbar from '@/components/SubNavbar'; +import { SUB_MENUS_MYACCOUNT } from '@/constants/constants'; +import { userStore } from '@/store'; +import { useEffect } from 'react'; +import toast from 'react-hot-toast'; + +export default function MyAccount() { + const { authMe } = userStore(); + useEffect(() => { + async function auth() { + const errorMessage = await authMe(); + if (errorMessage) { + toast.error(errorMessage, { id: 'authMe' }); + } + } + auth(); + }, []); + + return ( + <> + + + + ); +} diff --git a/src/routes/myAccount/OrderDetail.tsx b/src/routes/myAccount/OrderDetail.tsx new file mode 100644 index 00000000..2e9cdb88 --- /dev/null +++ b/src/routes/myAccount/OrderDetail.tsx @@ -0,0 +1,114 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import { useEffect, useState } from 'react'; +import { useNavigate, useParams } from 'react-router-dom'; +import { getOrderDetail } from '@/api/transactionApi'; +import { userStore } from '@/store'; +import { convertToHumanReadable } from '@/lib/time'; +import CrazyLoading from '@/components/ui/CrazyLoading'; +import SectionTitle from '@/components/ui/SectionTitle'; +import toast from 'react-hot-toast'; +import { DICTIONARY_SHOES } from '@/constants/constants'; +import Button from '@/components/ui/Button'; + +export default function OrderDetail() { + const navigate = useNavigate(); + const [isLoading, setIsLoading] = useState(false); + const { detailId } = useParams(); + const [detail, setDetail] = useState(); + const { userInfo } = userStore(); + + console.log(detail); + useEffect(() => { + const fetchData = async () => { + setIsLoading(true); + const res = await getOrderDetail( + userInfo?.accessToken as string, + detailId as string + ); + if (res.statusCode === 200) { + setDetail(res.data as TransactionDetail); + setIsLoading(false); + return; + } + toast.error(res.message, { id: 'getOrderDetail' }); + navigate('/'); + setIsLoading(false); + }; + fetchData(); + }, [detailId, userInfo?.accessToken]); + + return ( + <> + {isLoading ? ( + + ) : ( +
+
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
์€ํ–‰{detail?.account.bankName}
๊ณ„์ขŒ ๋ฒˆํ˜ธ{detail?.account.accountNumber}
์ƒํ’ˆ ์ด๋ฏธ์ง€ + {detail?.product.title} +
์ƒํ’ˆ ์ด๋ฆ„{detail?.product.title}
์ƒํ’ˆ ๊ฐ€๊ฒฉ(์›){detail?.product.price.toLocaleString('ko-KR')}
์นดํ…Œ๊ณ ๋ฆฌ{DICTIONARY_SHOES[detail?.product.tags[0] as string]}
๋ธŒ๋žœ๋“œ{detail?.product.tags[1].toUpperCase()}
ํ• ์ธ์œจ{detail?.product.discountRate}%
๊ตฌ๋งค ์‹œ๊ฐ„{convertToHumanReadable(detail?.timePaid || '')}
๊ตฌ๋งค ์™„๋ฃŒ ์—ฌ๋ถ€{detail?.done ? '์™„๋ฃŒ๋จ' : '์™„๋ฃŒ๋˜์ง€ ์•Š์Œ'}
+
+ )} + + ); +} diff --git a/src/routes/myAccount/OrderList.tsx b/src/routes/myAccount/OrderList.tsx new file mode 100644 index 00000000..4d42bca7 --- /dev/null +++ b/src/routes/myAccount/OrderList.tsx @@ -0,0 +1,162 @@ +import { confirmOrder, getOrderList, cancelOrder } from '@/api/transactionApi'; +import Button from '@/components/ui/Button'; +import CrazyLoading from '@/components/ui/CrazyLoading'; +import SectionTitle from '@/components/ui/SectionTitle'; +import { convertToHumanReadable, convertToMilliseconds } from '@/lib/time'; +import { userStore } from '@/store'; +import { useEffect, useState } from 'react'; +import { toast } from 'react-hot-toast'; +import { useNavigate } from 'react-router-dom'; + +export default function OrderList() { + const { userInfo } = userStore(); + const [orders, setOrders] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [isOrdered, setIsOrdered] = useState(false); + const navigate = useNavigate(); + + useEffect(() => { + async function fetchData() { + setIsLoading(true); + const res = await getOrderList(userInfo?.accessToken as string); + if (res.statusCode === 200) { + setOrders( + (res.data as TransactionDetail[]) + .filter((order) => !order.isCanceled) + .sort( + (a, b) => + convertToMilliseconds(b.timePaid) - + convertToMilliseconds(a.timePaid) + ) + ); + setIsLoading(false); + return; + } + toast.error(res.message, { id: 'fetchOrderList' }); + setIsLoading(false); + } + fetchData(); + }, [userInfo?.accessToken, isOrdered]); + + const handleConfirmOrder = async ( + event: React.MouseEvent, + orderId: string, + productTitle: string + ) => { + event.stopPropagation(); + setIsOrdered(false); + if (confirm(`${productTitle} ๊ตฌ๋งค๋ฅผ ํ™•์ •ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?`)) { + toast.loading('๊ตฌ๋งคํ™•์ • ์š”์ฒญ ์ค‘...', { id: 'confirmOrder' }); + const res = await confirmOrder(userInfo?.accessToken as string, orderId); + if (res.statusCode === 200) { + toast.success(`${productTitle} ๊ตฌ๋งค๋ฅผ ํ™•์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค.`, { + id: 'confirmOrder', + }); + setIsOrdered(true); + return; + } + toast.error(res.message, { id: 'confirmOrder' }); + } + }; + + const toOrderDetail = (orderId: string) => { + navigate(`/myaccount/order/${orderId}`); + }; + + const handleCancel = async ( + event: React.MouseEvent, + detailId: string, + productTitle: string + ) => { + event.stopPropagation(); + if (confirm(`${productTitle} ๊ตฌ๋งค๋ฅผ ์ทจ์†Œํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?`)) { + toast.loading('๊ตฌ๋งค ์ทจ์†Œ ์š”์ฒญ ์ค‘', { id: 'cancelOrder' }); + const res = await cancelOrder(detailId, userInfo?.accessToken as string); + + if (res.statusCode === 200) { + const updatedOrders = orders.filter( + (order) => order.detailId !== detailId + ); + setOrders(updatedOrders); + toast.success(`${productTitle} ๊ตฌ๋งค๋ฅผ ์ทจ์†Œํ•˜์˜€์Šต๋‹ˆ๋‹ค.`, { + id: 'cancelOrder', + }); + return; + } + toast.error(res.message, { id: 'cancelOrder' }); + } + }; + + return ( + <> + {isLoading ? ( + + ) : ( +
+ + + + + + + + + + + + + {orders?.map((order) => ( + toOrderDetail(order.detailId)} + className="cursor-pointer hover:opacity-80" + > + + + + + + + + ))} + +
์ƒํ’ˆ์ด๋ฏธ์ง€์ƒํ’ˆ๋ช…์ƒํ’ˆ๊ฐ€๊ฒฉ(์›)์ฃผ๋ฌธ์‹œ๊ฐ„๊ตฌ๋งคํ™•์ • +
+ {order.product.title} + {order.product.title}{order.product.price.toLocaleString('ko-KR')}{convertToHumanReadable(order.timePaid)}{order.done ? '๐Ÿ”˜' : 'โŒ'} +
+
+ )} + + ); +} diff --git a/src/routes/myAccount/SignUp.tsx b/src/routes/myAccount/SignUp.tsx new file mode 100644 index 00000000..7b7b499f --- /dev/null +++ b/src/routes/myAccount/SignUp.tsx @@ -0,0 +1,171 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { userStore } from '@/store'; +import { signUp } from '@/api/authApi'; +import { EMAIL_REGEX } from '@/constants/constants'; +import Button from '@/components/ui/Button'; +import Input from '@/components/ui/Input'; +import LoadingSpinner from '@/components/ui/LoadingSpinner'; +import ImageUpload from '@/components/ui/ImageUpload'; +import SectionTitle from '@/components/ui/SectionTitle'; +import toast from 'react-hot-toast'; + +export default function SignUp() { + const navigate = useNavigate(); + const { setUser } = userStore(); + + const [signUpData, setSignData] = useState({ + email: '', + password: '', + passwordRepeat: '', + displayName: '', + profileImgBase64: '', + }); + + const [isSending, setIsSending] = useState(false); + + const handleChange = (event: React.ChangeEvent) => { + const { name, value } = event.target; + // ์ด๋ฏธ์ง€ ํŒŒ์ผ ๋‹ค๋ฃจ๋Š” ๋กœ์ง + if (name === 'profileImgBase64') { + const files = event.target.files as FileList; + const reader = new FileReader(); + reader.readAsDataURL(files[0]); + reader.onloadend = () => { + setSignData({ ...signUpData, [name]: reader.result as string }); + }; + } + setSignData((prevData) => ({ + ...prevData, + [name]: value, + })); + }; + + const handleSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + + // ์ด๋ฉ”์ผ or ๋น„๋ฐ€๋ฒˆํ˜ธ or ์ด๋ฆ„์„ ์ž…๋ ฅํ•˜์ง€ ์•Š์€๊ฒฝ์šฐ + if ( + signUpData.email.trim() === '' || + signUpData.password.trim() === '' || + signUpData.displayName.trim() === '' + ) { + toast.error('์ด๋ฉ”์ผ, ๋น„๋ฐ€๋ฒˆํ˜ธ, ๋‹‰๋„ค์ž„์„ ๋ชจ๋‘ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.', { + id: 'signUp', + }); + return; + } + + // ์ด๋ฉ”์ผ์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ + if (!EMAIL_REGEX.test(signUpData.email)) { + toast.error('์˜ฌ๋ฐ”๋ฅธ ์ด๋ฉ”์ผ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.', { + id: 'signUp', + }); + return; + } + + // ๋น„๋ฒˆ 8์ž๋ฆฌ ์œ ํšจ์„ฑ๊ฒ€์‚ฌ + if (signUpData.password.length < 7) { + toast.error('๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ 8์ž๋ฆฌ ์ด์ƒ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.', { + id: 'signUp', + }); + return; + } + + // ์ด๋ฆ„ ๊ธธ์ด ์œ ํšจ์„ฑ๊ฒ€์‚ฌ + if (signUpData.displayName.length > 20) { + toast.error('๋‹‰๋„ค์ž„์€ 20์ž ์ดํ•˜๋กœ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”.', { + id: 'signUp', + }); + return; + } + + // ๋น„๋ฐ€๋ฒˆํ˜ธ ํ™•์ธ + if (signUpData.password !== signUpData.passwordRepeat) { + toast.error('๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.', { + id: 'signUp', + }); + return; + } + + // ํ†ต์‹  ์‹œ์ž‘ + setIsSending(true); + toast.loading('ํšŒ์›๊ฐ€์ž… ์š”์ฒญ ์ค‘...', { id: 'signUp' }); + const res = await signUp(signUpData); + // ํšŒ์›๊ฐ€์ž…์— ์„ฑ๊ณตํ•˜๋Š” ๊ฒฝ์šฐ + if (res.statusCode === 200) { + const user = res.data as UserResponseValue; + setIsSending(false); + toast.success(`${user.user.displayName}๋‹˜ ์ฆ๊ฑฐ์šด ์‡ผํ•‘ ๋˜์„ธ์š”!`, { + id: 'signUp', + }); + // ์ƒˆ๋กœ ๊ฐ€์ž…ํ•˜๋Š” ์‚ฌ๋žŒ์ด ๊ด€๋ฆฌ์ž์ผ๋ฆฌ ์—†์Œ + localStorage.setItem('user', JSON.stringify({ ...user, isAdmin: false })); + setUser({ ...user, isAdmin: false }); + navigate('/', { replace: true }); + return; + } + // ํšŒ์›๊ฐ€์ž… ์‹คํŒจ + toast.error(res.message, { + id: 'signUp', + }); + setIsSending(false); + }; + + return ( +
+
+ +
+
+ + + + + +
+
+
+
+
+
+ ); +} diff --git a/src/routes/myAccount/bank/BankAccounts.tsx b/src/routes/myAccount/bank/BankAccounts.tsx new file mode 100644 index 00000000..58ce41b9 --- /dev/null +++ b/src/routes/myAccount/bank/BankAccounts.tsx @@ -0,0 +1,120 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import { useState, useEffect } from 'react'; +import { getAccountListAndBalance, deleteAccount } from '@/api/bankApi'; +import Button from '@/components/ui/Button'; +import { useNavigate } from 'react-router-dom'; +import LoadingSpinner from '@/components/ui/LoadingSpinner'; +import { userStore } from '@/store'; +import toast from 'react-hot-toast'; +import CrazyLoading from '@/components/ui/CrazyLoading'; +import SectionTitle from '@/components/ui/SectionTitle'; + +export default function BankAccounts() { + const navigate = useNavigate(); + const [totalBalance, setTotalBalance] = useState(0); + const [accounts, setAccounts] = useState(); + const [isLoading, setIsLoading] = useState(false); + const [isDeleting, setIsDeleting] = useState(false); + const { userInfo } = userStore(); + + useEffect(() => { + setIsLoading(true); + async function fetchAccountList() { + const res = await getAccountListAndBalance( + userInfo?.accessToken as string + ); + if (res.statusCode === 200) { + if (res.data?.accounts.length === 0) { + setIsLoading(false); + toast.error('๋จผ์ € ๊ณ„์ขŒ๋ฅผ ๋“ฑ๋กํ•ด์ฃผ์„ธ์š”', { id: 'getAccounts' }); + navigate('/myaccount/connectAccount'); + return; + } + setTotalBalance((res.data as AccountsAndBalance).totalBalance); + setAccounts((res.data as AccountsAndBalance).accounts); + setIsLoading(false); + return; + } + toast.error(res.message, { id: 'getAccounts' }); + setIsLoading(false); + } + fetchAccountList(); + }, [userInfo?.accessToken]); + + const handleDeleteAccount = async ( + accountId: string, + accountName: string + ) => { + if (confirm(`${accountName} ๊ณ„์ขŒ๋ฅผ ํ•ด์ง€ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?`)) { + setIsDeleting(true); + setAccounts((prevAccounts) => + prevAccounts?.map((account) => + account.id === accountId ? { ...account, delete: true } : account + ) + ); + const res = await deleteAccount( + { accountId, signature: true }, + userInfo?.accessToken as string + ); + if (res.statusCode === 200) { + toast.success(`${accountName} ๊ณ„์ขŒ๋ฅผ ํ•ด์ง€ํ•˜์˜€์Šต๋‹ˆ๋‹ค.`, { + id: 'deleteAccount', + }); + setAccounts((prev) => prev?.filter((bank) => bank.id !== accountId)); + setIsDeleting(false); + return; + } + toast.error(res.message, { id: 'deleteAccount' }); + setIsDeleting(false); + } + }; + + return ( + <> + {isLoading ? ( + + ) : ( +
+ + + + + + + + + + + + {accounts?.map((account) => ( + + + + + + + ))} + +
์€ํ–‰๋ช…๊ณ„์ขŒ๋ฒˆํ˜ธ์ž”์•ก(์›)ํ•ด์ง€
{account.bankName}{account.accountNumber}{account.balance.toLocaleString('ko-KR')} +
+

+ ์ž”์•ก : {totalBalance.toLocaleString('kr-KO')}์› +

+
+ )} + + ); +} diff --git a/src/routes/myAccount/bank/ConnectBankAccount.tsx b/src/routes/myAccount/bank/ConnectBankAccount.tsx new file mode 100644 index 00000000..0fc6f196 --- /dev/null +++ b/src/routes/myAccount/bank/ConnectBankAccount.tsx @@ -0,0 +1,157 @@ +import { useState, useEffect, useMemo } from 'react'; +import { connectAccount, getBankList } from '@/api/bankApi'; +import Select from '@/components/ui/Select'; +import { userStore } from '@/store'; +import Input from '@/components/ui/Input'; +import { BANKS } from '@/constants/constants'; +import Button from '@/components/ui/Button'; +import { useNavigate } from 'react-router-dom'; +import LoadingSpinner from '@/components/ui/LoadingSpinner'; +import { toast } from 'react-hot-toast'; +import SectionTitle from '@/components/ui/SectionTitle'; + +export default function ConnectBankAccount() { + const navigate = useNavigate(); + const [isSending, setIsSending] = useState(false); + const { userInfo } = userStore(); + const [banks, setBanks] = useState([]); + + useEffect(() => { + async function fetchData() { + const res = await getBankList(userInfo?.accessToken as string); + if (res.statusCode === 200) { + setBanks(res.data as Bank[]); + return; + } + toast.error(res.message, { id: 'getBankList' }); + } + fetchData(); + }, [userInfo?.accessToken]); + + // ๊ฐ€์ ธ์˜จ ์€ํ–‰๋“ค ์ค‘ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๊ฒƒ๋งŒ option์œผ๋กœ ์‚ฌ์šฉ + const bankOptions = useMemo( + () => [ + { name: '์€ํ–‰์„ ํƒ*', value: '' }, + ...banks + .filter((bank) => !bank.disabled) + .map((bank) => ({ + name: bank.name, + value: bank.code, + })), + ], + [banks] + ); + + // ๊ณ„์ขŒ๋“ฑ๋ก์— ํ•„์š”ํ•œ data ์Šคํ…Œ์ดํŠธ + const [bankInputData, setBankInputData] = useState({ + accountNumber: '', + bankCode: '', + phoneNumber: '', + signature: false, + }); + + const handleChange = ( + event: React.ChangeEvent + ) => { + const { value, name } = event.target; + if (event.target.type === 'checkbox') { + setBankInputData((prev) => ({ + ...prev, + [name]: (event.target as HTMLInputElement).checked, + })); + } else { + setBankInputData((prev) => ({ ...prev, [name]: value })); + } + }; + + // ์€ํ–‰์„ ํƒ์‹œ digits์˜ ํ•ฉ์„ ๊ตฌํ•˜๋Š” ๋กœ์ง + const selectedBankDigits = useMemo( + () => + BANKS.find((bank) => bank.code === bankInputData.bankCode)?.digits.reduce( + (acc, curr) => acc + curr + ), + [bankInputData.bankCode] + ); + + // ์ „์†กํ•จ์ˆ˜ + async function handleSubmit(event: React.FormEvent) { + event.preventDefault(); + if ( + bankInputData.bankCode.trim() === '' || + bankInputData.accountNumber.trim() === '' || + bankInputData.phoneNumber.trim() === '' + ) { + toast.error('์€ํ–‰, ๊ณ„์ขŒ๋ฒˆํ˜ธ, ํœด๋Œ€ํฐ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.'); + return; + } + if (!bankInputData.signature) { + toast.error('๊ฐ„ํŽธ๊ฒฐ์ œ ๋“ฑ๋ก์— ๋™์˜ํ•ด์ฃผ์„ธ์š”.'); + return; + } + + setIsSending(true); + toast.loading('๊ณ„์ขŒ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.', { id: 'connectAccount' }); + const res = await connectAccount( + bankInputData, + userInfo?.accessToken as string + ); + if (res.statusCode === 200) { + toast.success(res.message, { id: 'connectAccount' }); + setIsSending(false); + navigate('/myaccount/accountList'); + return; + } + toast.error(res.message, { id: 'connectAccount' }); + setIsSending(false); + } + + return ( +
+ +
+
+ + +
+ + +
+
+
+ ); +} diff --git a/src/store.ts b/src/store.ts new file mode 100644 index 00000000..166b425b --- /dev/null +++ b/src/store.ts @@ -0,0 +1,69 @@ +import { create } from 'zustand'; +import { authenticate } from '@/api/authApi'; +import { ADMINS } from '@/constants/constants'; + +// user๊ด€๋ จ ์ „์—ญstate(store)์— ๋ฌด์—‡์ด ๋“ค์–ด๊ฐ€๋Š”์ง€ ํƒ€์ž…์ง€์ • +interface UserState { + userInfo: LocalUser | null; + setUser: (user: LocalUser | null) => void; + authMe: () => Promise; +} + +export const userStore = create((set) => ({ + //์œ ์ ธ์ •๋ณด(useState์ฒ˜๋Ÿผ ์ดˆ๊ธฐ๊ฐ’์„ ์ง€์ •ํ•ด์คŒ) + // ๋กœ์ปฌ์ €์žฅ์†Œ์— 'user'๊ฐ’์ด ์žˆ์œผ๋ฉด usrInfo์— ํ•ด๋‹น๊ฐ’์„ JSON.parseํ•œ ๊ฐ์ฒด๋ฅผ ํ• ๋‹น, ์—†์œผ๋ฉด null + userInfo: localStorage.getItem('user') + ? JSON.parse(localStorage.getItem('user') as string) + : null, + + // ์‚ฌ์šฉ์ž ์„ธํŒ… + setUser: (user: LocalUser | null) => + set({ + userInfo: user, + }), + + // ์ธ์ฆ + authMe: async () => { + // ๋กœ์ปฌ์ €์žฅ์†Œ์— 'user'๊ฐ€ ์žˆ๋‹ค๋ฉด json.parse์‹œ์ผœ ๊ฐ์ฒด๋กœ ๋งŒ๋“ค๊ณ  useInfo์— ์ €์žฅ / ์—†๋‹ค๋ฉด useInfo์— null + const userInfo: LocalUser | null = localStorage.getItem('user') + ? JSON.parse(localStorage.getItem('user') as string) + : null; + + // ๋กœ์ปฌ์ €์žฅ์†Œ์— ์œ ์ ธ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ + if (!userInfo) { + // ์œ ์ ธ ์ดˆ๊ธฐํ™” + set({ + userInfo: null, + }); + return '๋กœ๊ทธ์ธ์„ ํ•ด์ฃผ์„ธ์š”.'; + } + // ๋กœ์ปฌ์ €์žฅ์†Œ์— user๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ + const res = await authenticate(userInfo.accessToken); + // ์ธ์ฆ์— ์„ฑ๊ณตํ•˜๋Š” ๊ฒฝ์šฐ + if (res.statusCode === 200) { + const user = res.data as AuthenticateResponseValue; + const isAdmin = ADMINS.includes(user.email); + // ํ•ด๋‹น์œ ์ €๋ฅผ ํด๋ผ์ด์–ธํŠธ ์ „์—ญ ์œ ์ €์ƒํƒœ์— ์„ธํŒ… + set({ + userInfo: { + user: user, + accessToken: userInfo.accessToken, + isAdmin, + }, + }); + // ๋กœ์ปฌ ์ €์žฅ์†Œ์—๋„ ์„ธํŒ… + localStorage.setItem( + 'user', + JSON.stringify({ user, accessToken: userInfo.accessToken, isAdmin }) + ); + + return; + } + // ํ† ํฐ์€ ์žˆ์œผ๋‚˜ ์ธ์ฆ์— ์‹คํŒจํ•œ ๊ฒฝ์šฐ (expired or ํ† ํฐ ๊ฐ’ ์ธ์œ„์  ์กฐ์ž‘) + set({ + userInfo: null, + }); + localStorage.removeItem('user'); + return '๋กœ๊ทธ์ธ ํ•˜์‹ ์ง€ 24์‹œ๊ฐ„์ด ์ง€๋‚˜์…จ์–ด์š”! ๋‹ค์‹œ ๋กœ๊ทธ์ธํ•ด์ฃผ์„ธ์š”.'; + }, +})); diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 00000000..967ca12b --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,14 @@ +import daisyui from 'daisyui'; + +/** @type {import('tailwindcss').Config} */ +export default { + content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], + theme: { + extend: { + colors: { + accent: '#e41717', + }, + }, + }, + plugins: [daisyui], +}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..6a965703 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "node", + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + }, + "allowSyntheticDefaultImports": true, + /* Bundler mode */ + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src", "**/*.ts", "**/*.tsx"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 00000000..42872c59 --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 00000000..445e88c4 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react-swc'; +import path from 'path'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + }, + }, +});