diff --git a/.gitignore b/.gitignore index a547bf3..16534a6 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,8 @@ dist-ssr *.njsproj *.sln *.sw? + +# .env +.env +.env.local +.env.*.local \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit index 289ded8..bf37bb7 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,7 +1,6 @@ #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" -npx eslint . --fix npx prettier --write . npx lint-staged npx tsc --noEmit diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..a88c980 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,12 @@ +{ + "trailingComma": "all", + "semi": true, + "tabWidth": 2, + "jsxSingleQuote": false, + "useTabs": false, + "printWidth": 80, + "bracketSpacing": true, + "endOfLine": "auto", + "arrowParens": "always", + "plugins": ["prettier-plugin-tailwindcss"] +} diff --git a/README.md b/README.md index e5c631c..97e48c8 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,164 @@ +## **๐Ÿ”— ๋ฐฐํฌ ๋งํฌ** + +[[The Monitor](https://the-monitor.vercel.app/)](https://the-monitor.vercel.app/) +โ€”โ€” +![์„œ๋น„์Šค ์†Œ๊ฐœ](https://github.com/user-attachments/assets/e8d433a8-409b-4d9b-8291-ec33306d16bf) + +![ํŒ€ ์†Œ๊ฐœ 1](https://github.com/user-attachments/assets/26e42796-f199-4002-923a-15d11fedeae0) + +![๋ฐฐ๊ฒฝ](https://github.com/user-attachments/assets/60a87162-fb1b-4cb2-81b8-06762ed36de9) +![๋ฐฐ๊ฒฝ1](https://github.com/user-attachments/assets/8e4c9d72-2e1e-4419-a415-dd871f8b9a04) + + + +# ๐Ÿง‘โ€๐Ÿ’ป ํƒ€๊ฒŸ ์ธ์‚ฌ์ดํŠธ + +![1 ์„œ๋น„์Šคํƒ€๊ฒŸ](https://github.com/user-attachments/assets/ef96237c-3de2-4882-aefd-cb674364729a) + +์„œ๋น„์Šค ํŠน์„ฑ ์ƒ User ์™€ Customer ๊ฐ€ ๋‹ค๋ฅด๋‹ค๊ณ  ํŒ๋‹จํ•˜์˜€์Šต๋‹ˆ๋‹ค. +![์„œ๋น„์Šคํƒ€๊ฒŸ](https://github.com/user-attachments/assets/660a4ee0-dd8e-475c-9097-4d901aee32fd) + +![2 ๋ฆฌ์„œ์น˜](https://github.com/user-attachments/assets/a2d78264-69c0-4c99-b170-c2c655ead159) +![๋ฆฌ์„œ์น˜](https://github.com/user-attachments/assets/9cc11924-8061-4da8-9637-174380a49486) +![2-1๋ฐ์Šคํฌ๋ฆฌ์„œ์น˜](https://github.com/user-attachments/assets/02f6c121-599f-4b7d-a16d-e7164a3d6df2) +![๋ฐ์Šคํฌ๋ฆฌ์„œ์น˜-1](https://github.com/user-attachments/assets/ee0e190c-ae64-4ee2-9342-d55c77b14a26) +![2-2 ๋ฐ์Šคํฌ๋ฆฌ์„œ์น˜](https://github.com/user-attachments/assets/84549f50-516f-4c8f-9973-7393405a6adf) +![๋ฐ์Šคํฌ๋ฆฌ์„œ์น˜-2](https://github.com/user-attachments/assets/e1ba65c5-a7ec-45f8-a38c-55effbe7ff1c) + +![3 ์—…๋ฌด์˜์ƒ๋ถ„์„](https://github.com/user-attachments/assets/b1cf035c-3701-430b-98d9-674601a6e0c0) +![์—…๋ฌด์˜์ƒ๋ถ„์„](https://github.com/user-attachments/assets/792e13b2-e9cf-4674-874f-19aac5ee990a) +![4 ์œ ์ €๋ฆฌ์„œ์น˜](https://github.com/user-attachments/assets/dd68a0c0-89a5-4709-958e-65edcdc8a3d4) + + + +![์ธํ„ฐ๋ทฐ์ด](https://github.com/user-attachments/assets/a118b2ef-9bf5-4a7d-90db-9a9451947cf6) + + + +- ๊ฐ€์„ค 1 ํ˜„์žฌ ์‹œ์žฅ์— ์กด์žฌํ•˜๋Š” ๋ชจ๋‹ˆํ„ฐ๋ง ์„œ๋น„์Šค๋Š” ๋Œ€ํ–‰์‚ฌ์˜ ์ง์›๋“ค์—๊ฒŒ๋Š” ์ ํ•ฉํ•˜์ง€ ์•Š์„ ๊ฒƒ์ด๋‹ค + 1. ๋ชจ๋‹ˆํ„ฐ๋ง ์—…๋ฌด ์‹œ ๋ณด์กฐ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜์‹œ๋‚˜์š”? + 2. โ€™๋‰ด์Šค๋Ÿดโ€˜์ด๋ผ๋Š” ์„œ๋น„์Šค๊ฐ€ ์กด์žฌํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์œผ์‹ ๊ฐ€์š”? + 3. ๋ชจ๋‹ˆํ„ฐ๋ง ์—…๋ฌด์—์„œ ๊ฐ€์žฅ ์‹œ๊ฐ„์ด ๋งŽ์ด ํ•„์š”ํ•œ ๋ถ€๋ถ„์€ ์–ด๋А ๋ถ€๋ถ„์ธ๊ฐ€์š”? +- ๊ฐ€์„ค 2. ๊ณ ๊ฐ์‚ฌ ๋ณ„๋กœ ๋ชจ๋‹ˆํ„ฐ๋ง์— ๋Œ€ํ•œ ์š”๊ตฌ์‚ฌํ•ญ์ด ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ๋Œ€ํ–‰์‚ฌ์—์„œ๋Š” ์ˆ˜๊ธฐ ์—…๋ฌด๋ฅผ ์„ ํ˜ธํ•  ๊ฒƒ์ด๋‹ค + 1. ๊ณ ๊ฐ์‚ฌ ๋ณ„๋กœ ๋ชจ๋‹ˆํ„ฐ๋ง ๋ณด๊ณ ์„œ์˜ ์–‘์‹์ด ๋‹ค๋ฅธ ์ด์œ ๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”? + 2. ๋ณด๊ณ ์„œ์˜ ์–‘์‹์€ ๋Œ€ํ–‰์‚ฌ์™€ ๊ณ ๊ฐ์‚ฌ ์ค‘ ์–ด๋А ์ชฝ์—์„œ ์ œ๊ณตํ•˜๋‚˜์š”? + 3. ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ํšŒ์‚ฌ ๋ฉ”์ผ์ด ์•„๋‹Œ, ์„œ๋น„์Šค์˜ ๋ฉ”์ผ๋กœ ๋ชจ๋‹ˆํ„ฐ๋ง์ด ๊ฐˆํ…๋ฐ ๊ดœ์ฐฎ๋‚˜์š”? +- ๊ฐ€์„ค 3. ์„ฑ๊ณผ ๋ณด๊ณ ๋ฅผ ๊ฐ„ํŽธํ™”ํ•˜๊ธฐ ์œ„ํ•œ ๋ชจ๋‹ˆํ„ฐ๋ง ์„œ๋น„์Šค๊ฐ€ ์žˆ๋‹ค๋ฉด ์‚ฌ์šฉํ•  ์˜ํ–ฅ์ด ์žˆ์„ ๊ฒƒ์ด๋‹ค. + 1. ๋ชจ๋‹ˆํ„ฐ๋ง ๋ณด๊ณ ์„œ๋ฅผ ํ†ตํ•ด ๋งŒ๋“ค์–ด์ง€๋Š” 2์ฐจ ์‚ฐ์ถœ๋ฌผ์—๋Š” ๋ฌด์—‡์ด ์žˆ๋‚˜์š”? + 2. ๋ชจ๋‹ˆํ„ฐ๋ง ๊ฒฐ๊ณผ๋ฌผ์„ ํ•ด๋‹น ์‚ฐ์ถœ๋ฌผ๋กœ ์˜ฎ๊ธฐ๊ธฐ ์œ„ํ•ด ์ˆ˜๊ธฐ ์ด์™ธ์˜ ๋ฐฉ๋ฒ•์„ ํ™œ์šฉํ•˜๊ณ  ์žˆ๋‚˜์š”? +- ๊ฐ€์„ค 4. ์„œ๋น„์Šค๋ฅผ ๊ตฌ๋งคํ•˜๋Š” customer ๋Š” ์—…๋ฌด ์ž๋™ํ™” ์ด์ƒ์˜ ๊ฐ€์น˜๋ฅผ ์ œ๊ณตํ•ด์•ผ๋งŒ ๊ตฌ๋งคํ•  ๊ฒƒ์ด๋‹ค. + 1. ๋ชจ๋‹ˆํ„ฐ๋ง์— ๋ณด๊ณ  ์ด์™ธ์˜ ๋ชฉ์ ๋„ ์žˆ๋‚˜์š”? (์ €์—ฐ์ฐจ ์ง์›๋“ค์„ ๊ต์œก์‹œํ‚ค๊ธฐ ์œ„ํ•œ) + 2. AI ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•ด ์ „๋žต์ œ์•ˆ์— ๋ชจ๋‹ˆํ„ฐ๋ง ๋ณด๊ณ ์„œ์— ๋Œ€ํ•œ ์ธ์‚ฌ์ดํŠธ๋ฅผ ๋„์ถœํ•˜๋Š” ๊ธฐ๋Šฅ์€ ๋น„๋”ฉ๊ณผ์ •์—์„œ ์„ค๋“๋ ฅ์„ ๋”ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์‹œ๋‚˜์š”? +- ๊ฐ€์„ค ์™ธ์˜ ์งˆ๋ฌธ + 1. ๋ชจ๋‹ˆํ„ฐ๋ง ์™ธ์— ๊ฐœ์„ ๋˜์—ˆ์œผ๋ฉด ํ•˜๋Š” ์—…๋ฌด๊ฐ€ ์žˆ๋‚˜์š”? + 2. ๋‰ด์Šค๋Ÿด ์‚ฌ์šฉ์„ฑ ํ‰๊ฐ€ + 3. ๋Œ€ํ–‰์‚ฌ์—๊ฒŒ ํ•„์š”ํ•œ ๋‰ด์Šค๋ถ„์„์€ ๋ฌด์—‡์ธ๊ฐ€์š”? + 4. ์—…๋ฌด๋ณด์กฐ ์„œ๋น„์Šค๋ฅผ ํšŒ์‚ฌ์—์„œ๋Š” ์–ด๋–ค ๊ณผ์ •์œผ๋กœ ๊ตฌ๋งค / ๊ฒฐ์ • ํ•˜๋‚˜์š”? + +# ๐Ÿ–ฅ๏ธย ํ•ต์‹ฌ ๊ธฐ๋Šฅ + +![ํ”„๋กœ์ ํŠธ ๋ชฉํ‘œ](https://github.com/user-attachments/assets/7069212b-8989-4237-b14b-beda72afbc07) + +![Frame 2085665689](https://github.com/user-attachments/assets/b46aef08-3bf6-4650-8a38-b25202e79aa7) + +![ํšŒ์›๊ฐ€์ž…](https://github.com/user-attachments/assets/a09a43d1-2e61-4e60-9fa1-8788433c3f7a) + +- ๋ณด์•ˆ์„ ์œ„ํ•ด ๋ฉ”์ผ ์ธ์ฆ ๊ธฐ๋Šฅ์„ ํฌํ•จํ•œ ํšŒ์›๊ฐ€์ž… ๊ธฐ๋Šฅ +- ๋Œ€ํ‘œ ๋ฉ”์ผ๋กœ ๋กœ๊ทธ์ธ ํ›„, ๊ด€๋ฆฌ์ž๊ฐ€ ์ถ”ํ›„ ์–ด๋“œ๋ฏผ ํŽ˜์ด์ง€์—์„œ ๊ฐ ๊ณ ๊ฐ์‚ฌ ๋ณ„ ๋‹ด๋‹น์ž์—๊ฒŒ ๊ถŒํ•œ์„ ๋ถ€์—ฌ + +![๊ณ ๊ฐ์‚ฌ ๋ฆฌ์ŠคํŠธ](https://github.com/user-attachments/assets/fd98ff64-3ee9-4005-858c-be88ef11ac78) + +- ๊ฐ„๋‹จํ•œ ์ •๋ณด ์ž…๋ ฅ์„ ํ†ตํ•ด ๊ณ ๊ฐ์‚ฌ ์›Œํฌ์ŠคํŽ˜์ด์Šค ์ƒ์„ฑ +- ์ž์‚ฌ/๊ฒฝ์Ÿ์‚ฌ/์—…๊ณ„์— ๋Œ€ํ•œ ๋ชจ๋‹ˆํ„ฐ๋ง ํ‚ค์›Œ๋“œ์™€ ์ˆ˜์‹ ์ธ ๋ฉ”์ผ์„ ์‚ฌ์ „์— ๋“ฑ๋ก +- ์ถ”๊ฐ€ํ•œ ๊ณ ๊ฐ์‚ฌ๋“ค์˜ ์›Œํฌ์ŠคํŽ˜์ด์Šค๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ๋ชจ์•„๋ณผ ์ˆ˜ ์žˆ๋Š” ๋ฉ”์ธ ํ™”๋ฉด + +![๋ฐ์ผ๋ฆฌ ๋ชจ๋‹ˆํ„ฐ๋ง ๊ธฐ๋Šฅ](https://github.com/user-attachments/assets/5b35e06b-c016-47c9-8825-e7b10cbd38f4) + +- ํ‚ค์›Œ๋“œ๋ณ„๋กœ ๊ฒ€์ƒ‰๋œ ๊ธฐ์‚ฌ ์ค‘์—์„œ ์›ํ•˜๋Š” ๊ธฐ์‚ฌ๋ฅผ ์„ ํƒํ•˜์—ฌ ๋ณด๊ณ ์„œ์— ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋Š” ํŽ˜์ด์ง€๋กœ, + **The Monitor**์˜ **๊ฐ€์žฅ ํ•ต์‹ฌ์ ์ธ ๊ธฐ๋Šฅ**์ด ํฌํ•จ๋จ + +![๋ณด๊ณ ์„œ ํŽธ์ง‘ํ•˜๊ธฐ](https://github.com/user-attachments/assets/0d52e922-6a8f-4141-8e8e-9390d495df16) + +- ๋ฐ์ผ๋ฆฌ ๋ชจ๋‹ˆํ„ฐ๋ง์—์„œ ์Šคํฌ๋žฉํ•œ ๊ธฐ์‚ฌ์˜ ์ œ๋ชฉ, ํ—ค๋“œ๋ผ์ธ, URL, ๊ธฐ์ž๋ช… ๋“ฑ์ด ํด๋ฆญ ํ•œ ๋ฒˆ์œผ๋กœ ๋ณด๊ณ ์„œ ํ˜•์‹์— ๋ฐ˜์˜๋จ +- ๋กœ๊ณ , ์ œ๋ชฉ, ์ƒ‰์ƒ, ์นดํ…Œ๊ณ ๋ฆฌ ๋“ฑ์„ ์ปค์Šคํ…€ ํ•˜์—ฌ ํ†ต์ผ๋œ ๋ณด๊ณ ์„œ ํ˜•์‹์—์„œ ๋ฒ ๋ฆฌ์—์ด์…˜์„ ์ถ”๊ฐ€ +- ๋ณด๊ณ ์„œ ํŽธ์ง‘ ํ›„ ๊ณ ๊ฐ์‚ฌ ๋‹ด๋‹น์ž์—๊ฒŒ ๋น ๋ฅด๊ฒŒ ๋ฉ”์ผ ์ „์†ก ๊ฐ€๋Šฅ + +![๋ณด๊ณ ์„œ ๊ด€๋ฆฌํ•˜๊ธฐ](https://github.com/user-attachments/assets/1530df13-5cde-4b21-846c-bed4d30b952c) + +- ์ตœ๋Œ€ 1๋…„ ๊ฐ„์˜ ๋ชจ๋‹ˆํ„ฐ๋ง ๋ณด๊ณ ์„œ๋ฅผ ๋ชจ์•„๋ณด๋Š” ๊ธฐ๋Šฅ +- ์ด์ „์— ์ž‘์—…ํ•œ ๋ณด๊ณ ์„œ๋„ ํŽธ์ง‘ ๊ฐ€๋Šฅํ•˜๋ฉฐ ์Šคํฌ๋žฉํ•œ ๋‚ด์—ญ๋„ ํ•จ๊ป˜ ์ €์žฅ๋จ + +# ๐Ÿ’พย ๊ฐœ๋ฐœ + + + + + +## ERD + +![themonitor-erd](https://github.com/user-attachments/assets/7e7fe6ef-4a58-44d6-8019-698ca80d7a9b) + +## System Architecture + +![themonitorsa](https://github.com/user-attachments/assets/f001efe3-eada-498a-b87f-e496aad435ea) + ## Ground Rule - **์ฝ”๋“œ ์ˆ˜์ • ์•Œ๋ฆผ**: ๋‹ค๋ฅธ ์‚ฌ๋žŒ์˜ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•  ๋•Œ๋Š” ๋ฐ˜๋“œ์‹œ ๋งํ•ด์ค๋‹ˆ๋‹ค. @@ -13,11 +174,9 @@ src/ โ”œโ”€โ”€ main.tsx โ”œโ”€โ”€ App.tsx โ”œโ”€โ”€ TYPES/ - โ”œโ”€โ”€ APP/ - โ”‚ โ””โ”€โ”€ api/ + โ”œโ”€โ”€ API/ โ”œโ”€โ”€ CONSTANTS/ โ”œโ”€โ”€ UTILS/ - โ”œโ”€โ”€ LIB/ โ”œโ”€โ”€ STYLES/ โ”œโ”€โ”€ COMPONENTS/ โ”œโ”€โ”€ HOOKS/ @@ -47,6 +206,7 @@ src/ | refactor | ์ฝ”๋“œ ๋ฆฌํŒฉํ† ๋ง | | revert | ์ด์ „ ์ปค๋ฐ‹ ๋˜๋Œ๋ฆฌ๊ธฐ | | style | ์Šคํƒ€์ผ ์ฝ”๋“œ ๋ณ€๊ฒฝ | +| asset | svg, ์ด๋ฏธ์ง€ ๋“ฑ ๋””์ž์ธ ํŒŒ์ผ ์ถ”๊ฐ€ | | test | ํ…Œ์ŠคํŠธ ์ฝ”๋“œ, ๋ฆฌํŒฉํ† ๋ง ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ถ”๊ฐ€ | | type | ํƒ€์ž… ์ˆ˜์ • | | rename | ํŒŒ์ผ ๋˜๋Š” ํด๋” ๋ช…์„ ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜ ์˜ฎ๊ธฐ๋Š” ์ž‘์—…๋งŒ์ธ ๊ฒฝ์šฐ | @@ -90,8 +250,8 @@ const Welcome: React.FC = ({ name }) => { - ๋”•์…”๋„ˆ๋ฆฌ, ๋ฐฐ์—ด ๋“ฑ์˜ ๋ณต์ˆ˜์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด๋Š” ์ž๋ฃŒ๊ตฌ์กฐ์— ๋Œ€ํ•œ ๋ณ€์ˆ˜๋ช… s ๋ถ™์ด๊ธฐ `ex) heights` - ๋กœ๋”ฉ์ด๋‚˜ ๋ชจ๋‹ฌ์ฐฝ ๋“ฑ์˜ ๋ณด์—ฌ์ง ์—ฌ๋ถ€์— ๋Œ€ํ•œ boolean state์˜ ๊ฒฝ์šฐ is ๋ถ™์ด๊ธฐ `ex) isModalOpen, isLoading` - ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ํ•จ์ˆ˜์˜ ๊ฒฝ์šฐ get์œผ๋กœ ์‹œ์ž‘ํ•˜๊ธฐ `ex) getUserData` -- ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ ๋™์ž‘ํ•˜๋Š” ํ•จ์ˆ˜๋Š” handle ๋ถ™์ด๊ธฐ `ex) handleClick, handleModalOpen` -- prop์œผ๋กœ ๋ฐ›์•„์„œ ๋™์ž‘ํ•˜๋Š” ํ•จ์ˆ˜๋Š” on ๋ถ™์ด๊ธฐ `ex) onClick, onClose` +- ์ด๋ฒคํŠธ๋ฅผ ๊ฐ์ง€ํ•˜๋Š” ํ•จ์ˆ˜๋Š” on์œผ๋กœ ์‹œ์ž‘ํ•˜๋ฉฐ, ์ด ํ•จ์ˆ˜๋Š” ์ฃผ๋กœ ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์‹œ์ ์— ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค ex) onClick, onClose +- ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ๋กœ์ง์„ ๋‹ด๋Š” ํ•จ์ˆ˜๋Š” handle๋กœ ์‹œ์ž‘ํ•˜๋ฉฐ, ์ฃผ๋กœ on ํ•จ์ˆ˜ ๋‚ด์—์„œ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค ex) handleClick, handleModalOpen | ํ•จ์ˆ˜๋ช… | ๋™์‚ฌ๊ตฌ ex) getUserData | | ------ | ---------------------- | diff --git a/commitlint.config.cjs b/commitlint.config.cjs index e1cefa8..15ea5c1 100644 --- a/commitlint.config.cjs +++ b/commitlint.config.cjs @@ -16,6 +16,7 @@ module.exports = { "revert", "style", "test", + "asset", // Custom type "type", // Custom type "rename", // Custom type "remove", // Custom type diff --git a/package-lock.json b/package-lock.json index 192afbf..93ad859 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,22 +8,33 @@ "name": "the-monitor-fe", "version": "0.0.0", "dependencies": { + "@chakra-ui/react": "^2.10.4", + "@dnd-kit/core": "^6.1.0", + "@dnd-kit/sortable": "^8.0.0", "@tanstack/react-query": "^5.59.9", "@tanstack/react-query-devtools": "^5.59.9", + "aos": "^2.3.4", "axios": "^1.7.7", + "class-variance-authority": "^0.7.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.2.1", + "lodash": "^4.17.21", "prettier-plugin-tailwindcss": "^0.6.8", "react": "^18.3.1", + "react-colorful": "^5.6.1", + "react-date-range": "^2.0.1", "react-dom": "^18.3.1", "react-hook-form": "^7.53.0", - "react-router-dom": "^6.26.2" + "react-router-dom": "^6.26.2", + "tailwind-merge": "^2.5.4" }, "devDependencies": { "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", "@eslint/js": "^9.11.1", + "@types/aos": "^3.0.7", "@types/react": "^18.3.10", + "@types/react-date-range": "^1.4.9", "@types/react-dom": "^18.3.0", "@vitejs/plugin-react": "^4.3.2", "autoprefixer": "^10.4.20", @@ -39,7 +50,7 @@ "typescript": "^5.5.3", "typescript-eslint": "^8.7.0", "vite": "^5.4.8", - "vite-plugin-next-react-router": "^0.7.1", + "vite-plugin-svgr": "^4.2.0", "vite-tsconfig-paths": "^5.0.1" } }, @@ -74,7 +85,6 @@ "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", - "dev": true, "license": "MIT", "dependencies": { "@babel/highlight": "^7.25.7", @@ -129,7 +139,6 @@ "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz", "integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.25.7", @@ -162,7 +171,6 @@ "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz", "integrity": "sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.25.7", @@ -219,7 +227,6 @@ "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -229,7 +236,6 @@ "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -263,7 +269,6 @@ "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.25.7", @@ -279,7 +284,6 @@ "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.7.tgz", "integrity": "sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.25.7" @@ -323,11 +327,22 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", "integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.25.7", @@ -342,7 +357,6 @@ "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz", "integrity": "sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.25.7", @@ -361,7 +375,6 @@ "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -371,7 +384,6 @@ "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.7.tgz", "integrity": "sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.7", @@ -382,6 +394,103 @@ "node": ">=6.9.0" } }, + "node_modules/@chakra-ui/anatomy": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@chakra-ui/anatomy/-/anatomy-2.3.5.tgz", + "integrity": "sha512-3im33cUOxCbISjaBlINE2u8BOwJSCdzpjCX0H+0JxK2xz26UaVA5xeI3NYHUoxDnr/QIrgfrllGxS0szYwOcyg==", + "license": "MIT" + }, + "node_modules/@chakra-ui/hooks": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@chakra-ui/hooks/-/hooks-2.4.3.tgz", + "integrity": "sha512-Sr2zsoTZw3p7HbrUy4aLpTIkE2XXUelAUgg3NGwMzrmx75bE0qVyiuuTFOuyEzGxYVV2Fe8QtcKKilm6RwzTGg==", + "license": "MIT", + "dependencies": { + "@chakra-ui/utils": "2.2.3", + "@zag-js/element-size": "0.31.1", + "copy-to-clipboard": "3.3.3", + "framesync": "6.1.2" + }, + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/react": { + "version": "2.10.4", + "resolved": "https://registry.npmjs.org/@chakra-ui/react/-/react-2.10.4.tgz", + "integrity": "sha512-XyRWnuZ1Uw7Mlj5pKUGO5/WhnIHP/EOrpy6lGZC1yWlkd0eIfIpYMZ1ALTZx4KPEdbBaes48dgiMT2ROCqLhkA==", + "license": "MIT", + "dependencies": { + "@chakra-ui/hooks": "2.4.3", + "@chakra-ui/styled-system": "2.12.1", + "@chakra-ui/theme": "3.4.7", + "@chakra-ui/utils": "2.2.3", + "@popperjs/core": "^2.11.8", + "@zag-js/focus-visible": "^0.31.1", + "aria-hidden": "^1.2.3", + "react-fast-compare": "3.2.2", + "react-focus-lock": "^2.9.6", + "react-remove-scroll": "^2.5.7" + }, + "peerDependencies": { + "@emotion/react": ">=11", + "@emotion/styled": ">=11", + "framer-motion": ">=4.0.0", + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/@chakra-ui/styled-system": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/styled-system/-/styled-system-2.12.1.tgz", + "integrity": "sha512-DQph1nDiCPtgze7nDe0a36530ByXb5VpPosKGyWMvKocVeZJcDtYG6XM0+V5a0wKuFBXsViBBRIFUTiUesJAcg==", + "license": "MIT", + "dependencies": { + "@chakra-ui/utils": "2.2.3", + "csstype": "^3.1.2" + } + }, + "node_modules/@chakra-ui/theme": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/@chakra-ui/theme/-/theme-3.4.7.tgz", + "integrity": "sha512-pfewthgZTFNUYeUwGvhPQO/FTIyf375cFV1AT8N1y0aJiw4KDe7YTGm7p0aFy4AwAjH2ydMgeEx/lua4tx8qyQ==", + "license": "MIT", + "dependencies": { + "@chakra-ui/anatomy": "2.3.5", + "@chakra-ui/theme-tools": "2.2.7", + "@chakra-ui/utils": "2.2.3" + }, + "peerDependencies": { + "@chakra-ui/styled-system": ">=2.8.0" + } + }, + "node_modules/@chakra-ui/theme-tools": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@chakra-ui/theme-tools/-/theme-tools-2.2.7.tgz", + "integrity": "sha512-K/VJd0QcnKik7m+qZTkggqNLep6+MPUu8IP5TUpHsnSM5R/RVjsJIR7gO8IZVAIMIGLLTIhGshHxeMekqv6LcQ==", + "license": "MIT", + "dependencies": { + "@chakra-ui/anatomy": "2.3.5", + "@chakra-ui/utils": "2.2.3", + "color2k": "^2.0.2" + }, + "peerDependencies": { + "@chakra-ui/styled-system": ">=2.0.0" + } + }, + "node_modules/@chakra-ui/utils": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@chakra-ui/utils/-/utils-2.2.3.tgz", + "integrity": "sha512-cldoCQuexZ6e07/9hWHKD4l1QXXlM1Nax9tuQOBvVf/EgwNZt3nZu8zZRDFlhAOKCTQDkmpLTTu+eXXjChNQOw==", + "license": "MIT", + "dependencies": { + "@types/lodash.mergewith": "4.6.9", + "lodash.mergewith": "4.6.2" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/@commitlint/cli": { "version": "19.5.0", "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.5.0.tgz", @@ -814,6 +923,238 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/@dnd-kit/accessibility": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.0.tgz", + "integrity": "sha512-ea7IkhKvlJUv9iSHJOnxinBcoOI3ppGnnL+VDJ75O45Nss6HtZd8IdN8touXPDtASfeI2T2LImb8VOZcL47wjQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/core": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.1.0.tgz", + "integrity": "sha512-J3cQBClB4TVxwGo3KEjssGEXNJqGVWx17aRTZ1ob0FliR5IjYgTxl5YJbKTzA6IzrtelotH19v6y7uoIRUZPSg==", + "license": "MIT", + "dependencies": { + "@dnd-kit/accessibility": "^3.1.0", + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/sortable": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-8.0.0.tgz", + "integrity": "sha512-U3jk5ebVXe1Lr7c2wU7SBZjcWdQP+j7peHJfCspnA81enlu88Mgd7CC8Q+pub9ubP7eKVETzJW+IBAhsqbSu/g==", + "license": "MIT", + "dependencies": { + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@dnd-kit/core": "^6.1.0", + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/utilities": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz", + "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz", + "integrity": "sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.2.0", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT", + "peer": true + }, + "node_modules/@emotion/babel-plugin/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==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@emotion/cache": { + "version": "11.13.1", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.1.tgz", + "integrity": "sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.0", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT", + "peer": true + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", + "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT", + "peer": true + }, + "node_modules/@emotion/react": { + "version": "11.13.3", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.13.3.tgz", + "integrity": "sha512-lIsdU6JNrmYfJ5EbUCf4xW1ovy5wKQ2CkPRM4xogziOxH1nXxBSjpC9YqbFAP7circxMfYp+6x676BqWcEiixg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.12.0", + "@emotion/cache": "^11.13.0", + "@emotion/serialize": "^1.3.1", + "@emotion/use-insertion-effect-with-fallbacks": "^1.1.0", + "@emotion/utils": "^1.4.0", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.2.tgz", + "integrity": "sha512-grVnMvVPK9yUVE6rkKfAJlYZgo0cu3l9iMC77V7DW6E1DUIrU68pSEXRmFZFOFB1QFo57TncmOcvcbMDWsL4yA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.1", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT", + "peer": true + }, + "node_modules/@emotion/styled": { + "version": "11.13.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.13.0.tgz", + "integrity": "sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.12.0", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.0", + "@emotion/use-insertion-effect-with-fallbacks": "^1.1.0", + "@emotion/utils": "^1.4.0" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT", + "peer": true + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz", + "integrity": "sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.1.tgz", + "integrity": "sha512-BymCXzCG3r72VKJxaYVwOXATqXIZ85cuvg0YOUDxMGNrKc1DJRZk8MgV5wyXRyEayIMd4FuXJIUgTBXvDNW5cA==", + "license": "MIT", + "peer": true + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT", + "peer": true + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -1399,7 +1740,6 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.2.1", @@ -1414,7 +1754,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -1424,7 +1763,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -1434,14 +1772,12 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1509,6 +1845,16 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@remix-run/router": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.2.tgz", @@ -1518,6 +1864,29 @@ "node": ">=14.0.0" } }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.2.tgz", + "integrity": "sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", @@ -1742,6 +2111,258 @@ "win32" ] }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/core": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/core/node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.21.3", + "entities": "^4.4.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", + "svg-parser": "^2.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, "node_modules/@tanstack/query-core": { "version": "5.59.9", "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.59.9.tgz", @@ -1795,6 +2416,13 @@ "react": "^18 || ^19" } }, + "node_modules/@types/aos": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/aos/-/aos-3.0.7.tgz", + "integrity": "sha512-sEhyFqvKauUJZDbvAB3Pggynrq6g+2PS4XB3tmUr+mDL1gfDJnwslUC4QQ7/l8UD+LWpr3RxZVR/rHoZrLqZVg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1862,6 +2490,21 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "license": "MIT" }, + "node_modules/@types/lodash": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.13.tgz", + "integrity": "sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==", + "license": "MIT" + }, + "node_modules/@types/lodash.mergewith": { + "version": "4.6.9", + "resolved": "https://registry.npmjs.org/@types/lodash.mergewith/-/lodash.mergewith-4.6.9.tgz", + "integrity": "sha512-fgkoCAOF47K7sxrQ7Mlud2TH023itugZs2bUg8h/KzT+BnZNrR2jAOmaokbLunHNnobXVWOezAeNn/lZqwxkcw==", + "license": "MIT", + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/node": { "version": "22.7.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", @@ -1872,24 +2515,59 @@ "undici-types": "~6.19.2" } }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT", + "peer": true + }, "node_modules/@types/prop-types": { "version": "15.7.13", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@types/react": { "version": "18.3.11", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.11.tgz", "integrity": "sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, + "node_modules/@types/react-date-range": { + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/@types/react-date-range/-/react-date-range-1.4.9.tgz", + "integrity": "sha512-5oVEDW0ElYmY1+YVSzdMUR8stxSI5QrRJCgCFUvuEAV5197t412vimD9aVTW6g4JTaxCnMmB1BdEOT/odpaBxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*", + "date-fns": "^2.16.1" + } + }, + "node_modules/@types/react-date-range/node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/@types/react-dom": { "version": "18.3.0", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", @@ -2162,6 +2840,27 @@ "vite": "^4.2.0 || ^5.0.0" } }, + "node_modules/@zag-js/dom-query": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-0.31.1.tgz", + "integrity": "sha512-oiuohEXAXhBxpzzNm9k2VHGEOLC1SXlXSbRPcfBZ9so5NRQUA++zCE7cyQJqGLTZR0t3itFLlZqDbYEXRrefwg==", + "license": "MIT" + }, + "node_modules/@zag-js/element-size": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@zag-js/element-size/-/element-size-0.31.1.tgz", + "integrity": "sha512-4T3yvn5NqqAjhlP326Fv+w9RqMIBbNN9H72g5q2ohwzhSgSfZzrKtjL4rs9axY/cw9UfMfXjRjEE98e5CMq7WQ==", + "license": "MIT" + }, + "node_modules/@zag-js/focus-visible": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-0.31.1.tgz", + "integrity": "sha512-dbLksz7FEwyFoANbpIlNnd3bVm0clQSUsnP8yUVQucStZPsuWjCrhL2jlAbGNrTrahX96ntUMXHb/sM68TibFg==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "0.31.1" + } + }, "node_modules/acorn": { "version": "8.12.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", @@ -2232,7 +2931,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^1.9.0" @@ -2262,6 +2960,17 @@ "node": ">= 8" } }, + "node_modules/aos": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/aos/-/aos-2.3.4.tgz", + "integrity": "sha512-zh/ahtR2yME4I51z8IttIt4lC1Nw0ktsFtmeDzID1m9naJnWXhCoARaCgNOGXb5CLy3zm+wqmRAEgMYB5E2HUw==", + "license": "MIT", + "dependencies": { + "classlist-polyfill": "^1.0.3", + "lodash.debounce": "^4.0.6", + "lodash.throttle": "^4.0.1" + } + }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -2275,6 +2984,18 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "license": "Python-2.0" }, + "node_modules/aria-hidden": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", + "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/array-ify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", @@ -2337,6 +3058,49 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/babel-plugin-macros/node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/babel-plugin-macros/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -2421,6 +3185,19 @@ "node": ">=6" } }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/camelcase-css": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", @@ -2456,7 +3233,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", @@ -2505,6 +3281,30 @@ "node": ">= 6" } }, + "node_modules/class-variance-authority": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.0.tgz", + "integrity": "sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "2.0.0" + }, + "funding": { + "url": "https://joebell.co.uk" + } + }, + "node_modules/classlist-polyfill": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/classlist-polyfill/-/classlist-polyfill-1.2.0.tgz", + "integrity": "sha512-GzIjNdcEtH4ieA2S8NmrSxv7DfEV5fmixQeyTmqmRmRJPGpRBaSnA2a0VrCjyT8iW8JjEdMbKzDotAJf+ajgaQ==", + "license": "Unlicense" + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, "node_modules/cli-cursor": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", @@ -2677,11 +3477,19 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "1.1.3" @@ -2691,7 +3499,12 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, + "license": "MIT" + }, + "node_modules/color2k": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/color2k/-/color2k-2.0.3.tgz", + "integrity": "sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog==", "license": "MIT" }, "node_modules/colorette": { @@ -2740,16 +3553,6 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "license": "MIT" }, - "node_modules/consola": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.2.3.tgz", - "integrity": "sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.10.0" - } - }, "node_modules/conventional-changelog-angular": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", @@ -2802,6 +3605,15 @@ "dev": true, "license": "MIT" }, + "node_modules/copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "license": "MIT", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, "node_modules/cosmiconfig": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", @@ -2878,7 +3690,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true, "license": "MIT" }, "node_modules/dargs": { @@ -2891,7 +3702,18 @@ "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", + "peer": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" } }, "node_modules/debug": { @@ -2926,6 +3748,12 @@ "node": ">=0.4.0" } }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -2940,6 +3768,17 @@ "dev": true, "license": "MIT" }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/dot-prop": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", @@ -2974,6 +3813,19 @@ "dev": true, "license": "MIT" }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", @@ -3001,7 +3853,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" @@ -3060,7 +3911,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.8.0" @@ -3351,6 +4201,13 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -3487,6 +4344,13 @@ "node": ">=8" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT", + "peer": true + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -3522,6 +4386,18 @@ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "license": "ISC" }, + "node_modules/focus-lock": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-1.3.5.tgz", + "integrity": "sha512-QFaHbhv9WPUeLYBDe/PAuLKJ4Dd9OPvKs9xZBr3yLXnUrDNaVXKu2baDBXe3naPY30hgHYSsf2JW4jzas2mDEQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/follow-redirects": { "version": "1.15.9", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", @@ -3587,6 +4463,47 @@ "url": "https://github.com/sponsors/rawify" } }, + "node_modules/framer-motion": { + "version": "11.11.8", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.11.8.tgz", + "integrity": "sha512-mnGQNEoz99GtFXBBPw+Ag5K4FcfP5XrXxrxHz+iE4Lmg7W3sf2gKmGuvfkZCW/yIfcdv5vJd6KiSPETH1Pw68Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/framesync": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.1.2.tgz", + "integrity": "sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==", + "license": "MIT", + "dependencies": { + "tslib": "2.4.0" + } + }, + "node_modules/framesync/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "license": "0BSD" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -3606,7 +4523,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3645,6 +4561,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/get-stream": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", @@ -3782,7 +4707,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -3792,7 +4716,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -3801,6 +4724,16 @@ "node": ">= 0.4" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/human-signals": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", @@ -3882,11 +4815,19 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, "license": "MIT" }, "node_modules/is-binary-path": { @@ -3906,7 +4847,6 @@ "version": "2.15.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", - "dev": true, "license": "MIT", "dependencies": { "hasown": "^2.0.2" @@ -4049,7 +4989,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "dev": true, "license": "MIT", "bin": { "jsesc": "bin/jsesc" @@ -4068,7 +5007,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, "license": "MIT" }, "node_modules/json-schema-traverse": { @@ -4159,7 +5097,6 @@ "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, "license": "MIT" }, "node_modules/lint-staged": { @@ -4315,6 +5252,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", @@ -4322,6 +5265,12 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" + }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", @@ -4346,7 +5295,6 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", - "dev": true, "license": "MIT" }, "node_modules/lodash.snakecase": { @@ -4363,6 +5311,12 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "license": "MIT" + }, "node_modules/lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", @@ -4498,6 +5452,16 @@ "loose-envify": "cli.js" } }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -4674,6 +5638,17 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "license": "MIT" }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, "node_modules/node-releases": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", @@ -4734,7 +5709,6 @@ "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, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -4836,7 +5810,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", @@ -4873,7 +5846,6 @@ "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, "license": "MIT" }, "node_modules/path-scurry": { @@ -4900,11 +5872,19 @@ "dev": true, "license": "ISC" }, + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/picocolors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", - "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -5230,6 +6210,17 @@ } } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -5278,6 +6269,44 @@ "node": ">=0.10.0" } }, + "node_modules/react-clientside-effect": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz", + "integrity": "sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.13" + }, + "peerDependencies": { + "react": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-colorful": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz", + "integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/react-date-range": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/react-date-range/-/react-date-range-2.0.1.tgz", + "integrity": "sha512-jwKYc9zcjYMg2hWbPht+6BF2wjGG5DkRVNJLRXn2Y0B/QCOOnvQX6YXziZVujVADWmgsBaoQnILdmzYw+Bwh0g==", + "license": "MIT", + "dependencies": { + "classnames": "^2.2.6", + "prop-types": "^15.7.2", + "react-list": "^0.8.13", + "shallow-equal": "^1.2.1" + }, + "peerDependencies": { + "date-fns": "3.0.6 || >=3.0.0", + "react": "^0.14 || ^15.0.0-rc || >=15.0" + } + }, "node_modules/react-dom": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", @@ -5291,6 +6320,35 @@ "react": "^18.3.1" } }, + "node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", + "license": "MIT" + }, + "node_modules/react-focus-lock": { + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.13.2.tgz", + "integrity": "sha512-T/7bsofxYqnod2xadvuwjGKHOoL5GH7/EIPI5UyEvaU/c2CcphvGI371opFtuY/SYdbMsNiuF4HsHQ50nA/TKQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.0.0", + "focus-lock": "^1.3.5", + "prop-types": "^15.6.2", + "react-clientside-effect": "^1.2.6", + "use-callback-ref": "^1.3.2", + "use-sidecar": "^1.1.2" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-hook-form": { "version": "7.53.0", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.53.0.tgz", @@ -5307,6 +6365,24 @@ "react": "^16.8.0 || ^17 || ^18 || ^19" } }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/react-list": { + "version": "0.8.17", + "resolved": "https://registry.npmjs.org/react-list/-/react-list-0.8.17.tgz", + "integrity": "sha512-pgmzGi0G5uGrdHzMhgO7KR1wx5ZXVvI3SsJUmkblSAKtewIhMwbQiMuQiTE83ozo04BQJbe0r3WIWzSO0dR1xg==", + "license": "MIT", + "dependencies": { + "prop-types": "15" + }, + "peerDependencies": { + "react": "0.14 || 15 - 18" + } + }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", @@ -5317,6 +6393,53 @@ "node": ">=0.10.0" } }, + "node_modules/react-remove-scroll": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.0.tgz", + "integrity": "sha512-I2U4JVEsQenxDAKaVa3VZ/JeJZe0/2DxPWL8Tj8yLKctQJQiZM52pn/GWFpSp8dftjM3pSAHVJZscAnC/y+ySQ==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.6", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz", + "integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-router": { "version": "6.26.2", "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.2.tgz", @@ -5349,6 +6472,29 @@ "react-dom": ">=16.8" } }, + "node_modules/react-style-singleton": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", + "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "invariant": "^2.2.4", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -5372,6 +6518,12 @@ "node": ">=8.10.0" } }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -5396,7 +6548,6 @@ "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, "license": "MIT", "dependencies": { "is-core-module": "^2.13.0", @@ -5549,6 +6700,12 @@ "semver": "bin/semver.js" } }, + "node_modules/shallow-equal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", + "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5626,6 +6783,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -5785,6 +6963,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT", + "peer": true + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", @@ -5812,7 +6997,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "license": "MIT", "dependencies": { "has-flag": "^3.0.0" @@ -5825,7 +7009,6 @@ "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, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5834,6 +7017,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", + "dev": true, + "license": "MIT" + }, "node_modules/synckit": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", @@ -5850,6 +7040,16 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/tailwind-merge": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.5.4.tgz", + "integrity": "sha512-0q8cfZHMu9nuYP/b5Shb7Y7Sh1B7Nnl5GqNr1U+n2p6+mybvRtayrQ+0042Z5byvTA8ihjlP8Odo8/VnHbZu4Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { "version": "3.4.13", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.13.tgz", @@ -5948,7 +7148,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -5967,6 +7166,12 @@ "node": ">=8.0" } }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==", + "license": "MIT" + }, "node_modules/ts-api-utils": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", @@ -6124,6 +7329,49 @@ "punycode": "^2.1.0" } }, + "node_modules/use-callback-ref": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", + "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", + "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -6191,18 +7439,19 @@ } } }, - "node_modules/vite-plugin-next-react-router": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/vite-plugin-next-react-router/-/vite-plugin-next-react-router-0.7.1.tgz", - "integrity": "sha512-LrwyTN+PRmCxI38zQl3eZOCOMhMu7RwbqnVU/NDyCezuTopF6caOc4FC48HhtgpQL4RUwqaL7y1IhfdPqe3GJw==", + "node_modules/vite-plugin-svgr": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-4.2.0.tgz", + "integrity": "sha512-SC7+FfVtNQk7So0XMjrrtLAbEC8qjFPifyD7+fs/E6aaNdVde6umlVVh0QuwDLdOMu7vp5RiGFsB70nj5yo0XA==", "dev": true, + "license": "MIT", "dependencies": { - "consola": "^3.1.0", - "fast-glob": "^3.2.12", - "react-router-dom": "^6.10.0" + "@rollup/pluginutils": "^5.0.5", + "@svgr/core": "^8.1.0", + "@svgr/plugin-jsx": "^8.1.0" }, "peerDependencies": { - "vite": ">=2" + "vite": "^2.6.0 || 3 || 4 || 5" } }, "node_modules/vite-tsconfig-paths": { diff --git a/package.json b/package.json index 5001d0a..becf01f 100644 --- a/package.json +++ b/package.json @@ -14,22 +14,33 @@ "prepare": "husky install" }, "dependencies": { + "@chakra-ui/react": "^2.10.4", + "@dnd-kit/core": "^6.1.0", + "@dnd-kit/sortable": "^8.0.0", "@tanstack/react-query": "^5.59.9", "@tanstack/react-query-devtools": "^5.59.9", + "aos": "^2.3.4", "axios": "^1.7.7", + "class-variance-authority": "^0.7.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.2.1", + "lodash": "^4.17.21", "prettier-plugin-tailwindcss": "^0.6.8", "react": "^18.3.1", + "react-colorful": "^5.6.1", + "react-date-range": "^2.0.1", "react-dom": "^18.3.1", "react-hook-form": "^7.53.0", - "react-router-dom": "^6.26.2" + "react-router-dom": "^6.26.2", + "tailwind-merge": "^2.5.4" }, "devDependencies": { "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", "@eslint/js": "^9.11.1", + "@types/aos": "^3.0.7", "@types/react": "^18.3.10", + "@types/react-date-range": "^1.4.9", "@types/react-dom": "^18.3.0", "@vitejs/plugin-react": "^4.3.2", "autoprefixer": "^10.4.20", @@ -45,11 +56,16 @@ "typescript": "^5.5.3", "typescript-eslint": "^8.7.0", "vite": "^5.4.8", - "vite-plugin-next-react-router": "^0.7.1", + "vite-plugin-svgr": "^4.2.0", "vite-tsconfig-paths": "^5.0.1" }, "lint-staged": { - "*.js": "eslint --fix", + "*.{js,jsx,ts,tsx}": "eslint --fix", "*.{js,jsx,ts,tsx,css,md}": "prettier --write" + }, + "prettier": { + "plugins": [ + "prettier-plugin-tailwindcss" + ] } } diff --git a/src/App.tsx b/src/App.tsx index 4ec014c..fc32999 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,9 +1,49 @@ -import { useRoutes } from "react-router-dom"; -import { routes } from "./routes"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; +import { Outlet, useLocation } from "react-router-dom"; +import Header from "@components/Header"; +import { ChakraProvider } from "@chakra-ui/react"; +import SideMenu from "@components/SideMenu"; +import routes from "@constants/routes"; -const App: React.FC = () => { - const elem = useRoutes(routes); - return elem; -}; +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: false, + throwOnError: true, + }, + }, +}); + +function App() { + const location = useLocation(); + + const paths = [ + routes.monitoring, + routes.report, + routes.settingKeyword, + routes.settingEmail, + ]; + const isPathInPaths = paths.includes( + location.pathname as (typeof paths)[number], + ); + + return ( + + +
+
+
+ {isPathInPaths && } +
+ +
+
+
+
+ +
+ ); +} export default App; diff --git a/src/_layout.tsx b/src/_layout.tsx deleted file mode 100644 index 63fbb9d..0000000 --- a/src/_layout.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React, { Suspense } from "react"; -import { Outlet } from "react-router-dom"; - -const Layout: React.FC = () => { - return ( -
- - - -
- ); -}; - -export default Layout; diff --git a/src/api/accountsAPI.ts b/src/api/accountsAPI.ts new file mode 100644 index 0000000..84206ba --- /dev/null +++ b/src/api/accountsAPI.ts @@ -0,0 +1,38 @@ +import { apiPost, authApiPost, authApiGet } from "./apiUtils"; +import { + PostSendEmailConfirmData, + PostSendPasswordChangeEmailData, + PostSignInData, + PostSignUpData, + PostVerifyCodeData, +} from "./types/accounts"; + +export const postSendEmailConfirm = async (data: PostSendEmailConfirmData) => { + return apiPost("/accounts/sendEmailConfirm", data); +}; + +export const postVerifyCode = async (data: PostVerifyCodeData) => { + return apiPost("/accounts/verifyCode", data); +}; + +export const postSignUp = async (data: PostSignUpData) => { + return apiPost("/accounts/signUp", data); +}; + +export const postSignIn = async (data: PostSignInData) => { + return authApiPost("/accounts/signIn", data); +}; + +export const postSendPasswordChangeEmail = async ( + data: PostSendPasswordChangeEmailData, +) => { + return apiPost("/accounts/sendPasswordChangeEmail", data); +}; + +export const postSetClient = async (clientId: number) => { + return authApiPost("accounts/set-client", undefined, { clientId }); +}; + +export const checkToken = async () => { + return authApiGet("/accounts/tokenValidity"); +}; diff --git a/src/api/apiUtils.ts b/src/api/apiUtils.ts new file mode 100644 index 0000000..60f9457 --- /dev/null +++ b/src/api/apiUtils.ts @@ -0,0 +1,68 @@ +import axiosInstance from "./axiosInstance"; +import tokenInstance from "./tokenInstance"; +import { ApiResponse } from "./types/response"; + +export const apiGet = async ( + url: string, + params?: any, +): Promise> => { + const response = await axiosInstance.get>(url, { params }); + return response.data; +}; + +export const apiPost = async ( + url: string, + data?: any, +): Promise> => { + const response = await axiosInstance.post>(url, data); + return response.data; +}; + +export const authApiGet = async ( + url: string, + params?: any, +): Promise> => { + const response = await tokenInstance.get>(url, { params }); + return response.data; +}; + +export const authApiPost = async ( + url: string, + data?: any, + params?: any, +): Promise> => { + const response = await tokenInstance.post>(url, data, { + params, + }); + return response.data; +}; + +export const authApiDelete = async ( + url: string, + params?: any, +): Promise> => { + const response = await tokenInstance.delete>(url, { params }); + return response.data; +}; + +export const authApiPatch = async ( + url: string, + data?: any, + params?: any, +): Promise> => { + const response = await tokenInstance.patch>(url, data, { + params, + }); + return response.data; +}; + +export const authApiPut = async ( + url: string, + data?: any, + params?: any, +): Promise> => { + const response = await tokenInstance.put>(url, data, { + params, + }); + return response.data; +}; diff --git a/src/api/articlesAPI.ts b/src/api/articlesAPI.ts new file mode 100644 index 0000000..b42fe53 --- /dev/null +++ b/src/api/articlesAPI.ts @@ -0,0 +1,32 @@ +import { authApiGet, authApiPatch } from "./apiUtils"; +import { + GetArticlesByKeywordParams, + GetArticlesParams, + GetArticlesResponse, +} from "./types/articles"; + +export const getArticles = async ({ + categoryType, + page, +}: GetArticlesParams) => { + return authApiGet("/articles", { + categoryType, + page, + }); +}; + +export const getArticlesByKeyword = async ({ + keywordId, + categoryType, + page, +}: GetArticlesByKeywordParams) => { + return authApiGet("/articles/keyword", { + keywordId, + categoryType, + page, + }); +}; + +export const patchArticleRead = async (articleId: number) => { + return authApiPatch("/articles/read", undefined, { articleId }); +}; diff --git a/src/api/axiosInstance.ts b/src/api/axiosInstance.ts new file mode 100644 index 0000000..2e6fa15 --- /dev/null +++ b/src/api/axiosInstance.ts @@ -0,0 +1,34 @@ +import { createStandaloneToast } from "@chakra-ui/react"; +import axios, { AxiosInstance } from "axios"; + +const baseURL = import.meta.env.VITE_BASE_URL; + +const { toast } = createStandaloneToast(); + +const axiosInstance: AxiosInstance = axios.create({ + baseURL, + headers: { + Accept: "*/*", + "Content-Type": "application/json", + }, +}); + +axiosInstance.interceptors.response.use( + (response) => response, + async (error) => { + if (error.response?.data?.message) { + toast({ + title: `${error.response?.data?.message}`, + status: "error", + }); + } else { + toast({ + title: "์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.", + status: "error", + }); + } + return Promise.reject(error); + }, +); + +export default axiosInstance; diff --git a/src/api/clientsAPI.ts b/src/api/clientsAPI.ts new file mode 100644 index 0000000..191d037 --- /dev/null +++ b/src/api/clientsAPI.ts @@ -0,0 +1,52 @@ +import { authApiDelete, authApiGet, authApiPost, authApiPut } from "./apiUtils"; +import { + Client, + GetClientInfo, + PostClientParams, + PutClientParams, + SearchClientResponse, +} from "./types/clients"; + +export const getClients = async () => { + return authApiGet("/clients"); +}; + +export const postClient = async ({ data, logo }: PostClientParams) => { + const formData = new FormData(); + + formData.append("clientRequest", JSON.stringify(data)); + + if (logo) { + formData.append("logo", logo); + } + + return authApiPost("/clients", formData); +}; + +export const getClientInfo = async (clientId: number) => { + return authApiGet("/clients/info", { clientId }); +}; + +export const deleteClient = (clientId: number) => { + return authApiDelete("/clients", { + clientId, + }); +}; + +export const putClient = ({ clientId, data, logo }: PutClientParams) => { + const formData = new FormData(); + + formData.append("clientData", JSON.stringify(data)); + + if (logo) { + formData.append("logo", logo); + } + + return authApiPut(`/clients/update`, formData, { clientId }); +}; + +export const postSearchClient = (searchText: string) => { + return authApiPost("/clients/search", { + searchText, + }); +}; diff --git a/src/api/emailsAPI.ts b/src/api/emailsAPI.ts new file mode 100644 index 0000000..11bbffd --- /dev/null +++ b/src/api/emailsAPI.ts @@ -0,0 +1,28 @@ +import { authApiGet, authApiPost, authApiPut } from "./apiUtils"; +import { + GetEmailsResponse, + PostSendEmailParams, + PutEmailsParams, +} from "./types/emails"; + +export const postSendEmail = ({ reportId, data }: PostSendEmailParams) => { + return authApiPost("/emails/send", data, { + reportId, + }); +}; + +export const putEmails = async ({ data, img }: PutEmailsParams) => { + const formData = new FormData(); + + formData.append("emailUpdate", JSON.stringify(data)); + + if (img) { + formData.append("signatureImage", img); + } + + return authApiPut("/emails", formData); +}; + +export const getEmails = async () => { + return authApiGet("/emails"); +}; diff --git a/src/api/excelAPI.ts b/src/api/excelAPI.ts new file mode 100644 index 0000000..9484b6b --- /dev/null +++ b/src/api/excelAPI.ts @@ -0,0 +1,10 @@ +import tokenInstance from "./tokenInstance"; +import { ReportParams } from "./types/reports"; + +export const getExcel = async ({ reportId }: ReportParams) => { + const response = await tokenInstance.get("/excel/generate", { + params: { reportId }, + responseType: "blob", + }); + return response; +}; diff --git a/src/api/hooks/accounts/useCheckToken.ts b/src/api/hooks/accounts/useCheckToken.ts new file mode 100644 index 0000000..1a22c5e --- /dev/null +++ b/src/api/hooks/accounts/useCheckToken.ts @@ -0,0 +1,27 @@ +import { checkToken } from "@api/accountsAPI"; +import routes from "@constants/routes"; +import { useQuery } from "@tanstack/react-query"; +import { useEffect } from "react"; +import { useNavigate } from "react-router-dom"; + +const useCheckToken = () => { + const navigate = useNavigate(); + + const query = useQuery({ + queryKey: ["tokenValidity"], + queryFn: checkToken, + select: (data) => data.isSuccess, + refetchOnWindowFocus: false, + retry: false, + }); + + useEffect(() => { + if (query.isSuccess) { + navigate(routes.dashboard); + } + }, [query.isSuccess, navigate]); + + return query; +}; + +export default useCheckToken; diff --git a/src/api/hooks/accounts/usePostSendEmailConfirm.ts b/src/api/hooks/accounts/usePostSendEmailConfirm.ts new file mode 100644 index 0000000..a1fa30a --- /dev/null +++ b/src/api/hooks/accounts/usePostSendEmailConfirm.ts @@ -0,0 +1,19 @@ +import { postSendEmailConfirm } from "@api/accountsAPI"; +import { PostSendEmailConfirmData } from "@api/types/accounts"; +import { useToast } from "@chakra-ui/react"; +import { useMutation } from "@tanstack/react-query"; + +const usePostSendEmailConfirm = () => { + const toast = useToast(); + return useMutation({ + mutationFn: (data: PostSendEmailConfirmData) => postSendEmailConfirm(data), + onSuccess: () => { + toast({ + title: "์ธ์ฆ๋ฉ”์ผ์ด ๋ฐœ์†ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", + status: "success", + }); + }, + }); +}; + +export default usePostSendEmailConfirm; diff --git a/src/api/hooks/accounts/usePostSendPasswordChangeEmail.ts b/src/api/hooks/accounts/usePostSendPasswordChangeEmail.ts new file mode 100644 index 0000000..61f45aa --- /dev/null +++ b/src/api/hooks/accounts/usePostSendPasswordChangeEmail.ts @@ -0,0 +1,12 @@ +import { postSendPasswordChangeEmail } from "@api/accountsAPI"; +import { PostSendPasswordChangeEmailData } from "@api/types/accounts"; +import { useMutation } from "@tanstack/react-query"; + +const usePostSendPasswordChangeEmail = () => { + return useMutation({ + mutationFn: (data: PostSendPasswordChangeEmailData) => + postSendPasswordChangeEmail(data), + }); +}; + +export default usePostSendPasswordChangeEmail; diff --git a/src/api/hooks/accounts/usePostSetClient.ts b/src/api/hooks/accounts/usePostSetClient.ts new file mode 100644 index 0000000..f7ad7a9 --- /dev/null +++ b/src/api/hooks/accounts/usePostSetClient.ts @@ -0,0 +1,10 @@ +import { postSetClient } from "@api/accountsAPI"; +import { useMutation } from "@tanstack/react-query"; + +const usePostSetClient = () => { + return useMutation({ + mutationFn: (clientId: number) => postSetClient(clientId), + }); +}; + +export default usePostSetClient; diff --git a/src/api/hooks/accounts/usePostSignIn.ts b/src/api/hooks/accounts/usePostSignIn.ts new file mode 100644 index 0000000..df74959 --- /dev/null +++ b/src/api/hooks/accounts/usePostSignIn.ts @@ -0,0 +1,27 @@ +import { postSignIn } from "@api/accountsAPI"; +import { PostSignInData } from "@api/types/accounts"; +import { useToast } from "@chakra-ui/react"; +import routes from "@constants/routes"; +import { useMutation } from "@tanstack/react-query"; +import { useNavigate } from "react-router-dom"; + +const usePostSignIn = () => { + const navigate = useNavigate(); + const toast = useToast(); + + return useMutation({ + mutationFn: (singInData: PostSignInData) => postSignIn(singInData), + onSuccess: (res) => { + if (res.code === "COMMON200") { + navigate(routes.dashboard); + } else { + toast({ + title: `${res.message}`, + status: "error", + }); + } + }, + }); +}; + +export default usePostSignIn; diff --git a/src/api/hooks/accounts/usePostSignUp.ts b/src/api/hooks/accounts/usePostSignUp.ts new file mode 100644 index 0000000..dc299a7 --- /dev/null +++ b/src/api/hooks/accounts/usePostSignUp.ts @@ -0,0 +1,23 @@ +import { postSignUp } from "@api/accountsAPI"; +import { PostSignUpData } from "@api/types/accounts"; +import { useToast } from "@chakra-ui/react"; +import routes from "@constants/routes"; +import { useMutation } from "@tanstack/react-query"; +import { useNavigate } from "react-router-dom"; + +const usePostSignUp = () => { + const toast = useToast(); + const navigate = useNavigate(); + return useMutation({ + mutationFn: (data: PostSignUpData) => postSignUp(data), + onSuccess: () => { + toast({ + title: "ํšŒ์›๊ฐ€์ž…์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", + status: "success", + }); + navigate(routes.signIn); + }, + }); +}; + +export default usePostSignUp; diff --git a/src/api/hooks/accounts/usePostVerifyCode.ts b/src/api/hooks/accounts/usePostVerifyCode.ts new file mode 100644 index 0000000..c597277 --- /dev/null +++ b/src/api/hooks/accounts/usePostVerifyCode.ts @@ -0,0 +1,11 @@ +import { postVerifyCode } from "@api/accountsAPI"; +import { PostVerifyCodeData } from "@api/types/accounts"; +import { useMutation } from "@tanstack/react-query"; + +const usePostVerifyCode = () => { + return useMutation({ + mutationFn: (data: PostVerifyCodeData) => postVerifyCode(data), + }); +}; + +export default usePostVerifyCode; diff --git a/src/api/hooks/articles/useGetArticles.ts b/src/api/hooks/articles/useGetArticles.ts new file mode 100644 index 0000000..e868b1a --- /dev/null +++ b/src/api/hooks/articles/useGetArticles.ts @@ -0,0 +1,15 @@ +import { getArticles } from "@api/articlesAPI"; +import { GetArticlesParams } from "@api/types/articles"; +import { keepPreviousData, useQuery } from "@tanstack/react-query"; + +const useGetArticles = ({ categoryType, page }: GetArticlesParams) => { + return useQuery({ + queryKey: ["articles", categoryType, page], + queryFn: () => getArticles({ categoryType, page }), + select: (data) => data.result, + placeholderData: keepPreviousData, + enabled: !!categoryType, + }); +}; + +export default useGetArticles; diff --git a/src/api/hooks/articles/useGetArticlesByKeyword.ts b/src/api/hooks/articles/useGetArticlesByKeyword.ts new file mode 100644 index 0000000..7b11911 --- /dev/null +++ b/src/api/hooks/articles/useGetArticlesByKeyword.ts @@ -0,0 +1,19 @@ +import { getArticlesByKeyword } from "@api/articlesAPI"; +import { GetArticlesByKeywordParams } from "@api/types/articles"; +import { keepPreviousData, useQuery } from "@tanstack/react-query"; + +const useGetArticlesByKeyword = ({ + keywordId, + categoryType, + page, +}: GetArticlesByKeywordParams) => { + return useQuery({ + queryKey: ["articles", keywordId, categoryType, page], + queryFn: () => getArticlesByKeyword({ keywordId, categoryType, page }), + select: (data) => data.result, + placeholderData: keepPreviousData, + enabled: !!keywordId, + }); +}; + +export default useGetArticlesByKeyword; diff --git a/src/api/hooks/articles/usePatchArticleRead.ts b/src/api/hooks/articles/usePatchArticleRead.ts new file mode 100644 index 0000000..ace3338 --- /dev/null +++ b/src/api/hooks/articles/usePatchArticleRead.ts @@ -0,0 +1,14 @@ +import { patchArticleRead } from "@api/articlesAPI"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +export const usePatchReportRead = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (articleId: number) => patchArticleRead(articleId), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["articles"] }); + queryClient.invalidateQueries({ queryKey: ["articlesByKeyword"] }); + }, + }); +}; diff --git a/src/api/hooks/clients/useDeleteClient.ts b/src/api/hooks/clients/useDeleteClient.ts new file mode 100644 index 0000000..f1e15cd --- /dev/null +++ b/src/api/hooks/clients/useDeleteClient.ts @@ -0,0 +1,15 @@ +import { deleteClient } from "@api/clientsAPI"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +const useDeleteClient = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (params: number) => deleteClient(params), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["clients"] }); + }, + }); +}; + +export default useDeleteClient; diff --git a/src/api/hooks/clients/useGetClientInfo.ts b/src/api/hooks/clients/useGetClientInfo.ts new file mode 100644 index 0000000..583e71e --- /dev/null +++ b/src/api/hooks/clients/useGetClientInfo.ts @@ -0,0 +1,12 @@ +import { getClientInfo } from "@api/clientsAPI"; +import { useQuery } from "@tanstack/react-query"; + +const useGetClientInfo = (clientId: number) => { + return useQuery({ + queryKey: ["clientInfo", clientId], + queryFn: () => getClientInfo(clientId), + select: (data) => data.result, + }); +}; + +export default useGetClientInfo; diff --git a/src/api/hooks/clients/useGetClients.ts b/src/api/hooks/clients/useGetClients.ts new file mode 100644 index 0000000..113955b --- /dev/null +++ b/src/api/hooks/clients/useGetClients.ts @@ -0,0 +1,10 @@ +import { useQuery } from "@tanstack/react-query"; +import { getClients } from "@api/clientsAPI"; + +export const useGetClients = () => { + return useQuery({ + queryKey: ["clients"], + queryFn: getClients, + select: (data) => data.result, + }); +}; diff --git a/src/api/hooks/clients/usePostClient.ts b/src/api/hooks/clients/usePostClient.ts new file mode 100644 index 0000000..1684e0a --- /dev/null +++ b/src/api/hooks/clients/usePostClient.ts @@ -0,0 +1,24 @@ +import { useQueryClient, useMutation } from "@tanstack/react-query"; +import { postClient } from "@api/clientsAPI"; +import { useToast } from "@chakra-ui/react"; +import { PostClientParams } from "@api/types/clients"; + +const usePostClient = () => { + const toast = useToast(); + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (params: PostClientParams) => { + return postClient(params); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["clients"] }); + toast({ + title: "๊ณ ๊ฐ์‚ฌ ์ •๋ณด๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", + status: "success", + }); + }, + }); +}; + +export default usePostClient; diff --git a/src/api/hooks/clients/usePostSearchClient.ts b/src/api/hooks/clients/usePostSearchClient.ts new file mode 100644 index 0000000..9b66646 --- /dev/null +++ b/src/api/hooks/clients/usePostSearchClient.ts @@ -0,0 +1,13 @@ +import { postSearchClient } from "@api/clientsAPI"; +import { keepPreviousData, useQuery } from "@tanstack/react-query"; + +const usePostSearchClient = (searchText: string) => { + return useQuery({ + queryKey: ["searchClient", searchText], + queryFn: () => postSearchClient(searchText), + select: (data) => data.result, + placeholderData: keepPreviousData, + }); +}; + +export default usePostSearchClient; diff --git a/src/api/hooks/clients/usePostSetClient.ts b/src/api/hooks/clients/usePostSetClient.ts new file mode 100644 index 0000000..f7ad7a9 --- /dev/null +++ b/src/api/hooks/clients/usePostSetClient.ts @@ -0,0 +1,10 @@ +import { postSetClient } from "@api/accountsAPI"; +import { useMutation } from "@tanstack/react-query"; + +const usePostSetClient = () => { + return useMutation({ + mutationFn: (clientId: number) => postSetClient(clientId), + }); +}; + +export default usePostSetClient; diff --git a/src/api/hooks/clients/usePutClient.ts b/src/api/hooks/clients/usePutClient.ts new file mode 100644 index 0000000..f5254e5 --- /dev/null +++ b/src/api/hooks/clients/usePutClient.ts @@ -0,0 +1,22 @@ +import { putClient } from "@api/clientsAPI"; +import { PutClientParams } from "@api/types/clients"; +import { useToast } from "@chakra-ui/react"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +const usePutClient = () => { + const toast = useToast(); + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (params: PutClientParams) => putClient(params), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["clients"] }); + toast({ + title: "๊ณ ๊ฐ์‚ฌ ์ •๋ณด๊ฐ€ ์ˆ˜์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", + status: "success", + }); + }, + }); +}; + +export default usePutClient; diff --git a/src/api/hooks/emails/useGetEmails.ts b/src/api/hooks/emails/useGetEmails.ts new file mode 100644 index 0000000..e33356d --- /dev/null +++ b/src/api/hooks/emails/useGetEmails.ts @@ -0,0 +1,12 @@ +import { getEmails } from "@api/emailsAPI"; +import { useQuery } from "@tanstack/react-query"; + +const useGetEmails = () => { + return useQuery({ + queryKey: ["emails"], + queryFn: getEmails, + select: (data) => data.result, + }); +}; + +export default useGetEmails; diff --git a/src/api/hooks/emails/usePostSendEmail.ts b/src/api/hooks/emails/usePostSendEmail.ts new file mode 100644 index 0000000..b36d765 --- /dev/null +++ b/src/api/hooks/emails/usePostSendEmail.ts @@ -0,0 +1,19 @@ +import { postSendEmail } from "@api/emailsAPI"; +import { PostSendEmailParams } from "@api/types/emails"; +import { useToast } from "@chakra-ui/react"; +import { useMutation } from "@tanstack/react-query"; + +const usePostSendEmail = () => { + const toast = useToast(); + return useMutation({ + mutationFn: (params: PostSendEmailParams) => postSendEmail(params), + onSuccess: () => { + toast({ + title: "๋ฉ”์ผ์ด ์ „์†ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", + status: "success", + }); + }, + }); +}; + +export default usePostSendEmail; diff --git a/src/api/hooks/emails/usePutEmails.ts b/src/api/hooks/emails/usePutEmails.ts new file mode 100644 index 0000000..03e9721 --- /dev/null +++ b/src/api/hooks/emails/usePutEmails.ts @@ -0,0 +1,22 @@ +import { putEmails } from "@api/emailsAPI"; +import { PutEmailsParams } from "@api/types/emails"; +import { useToast } from "@chakra-ui/react"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +const usePutEmails = () => { + const toast = useToast(); + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (params: PutEmailsParams) => putEmails(params), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["emails"] }); + toast({ + title: "๋ฉ”์ผ ์„ค์ •์ด ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", + status: "success", + }); + }, + }); +}; + +export default usePutEmails; diff --git a/src/api/hooks/excel/useGetExcel.ts b/src/api/hooks/excel/useGetExcel.ts new file mode 100644 index 0000000..63a9ebe --- /dev/null +++ b/src/api/hooks/excel/useGetExcel.ts @@ -0,0 +1,26 @@ +import { getExcel } from "@api/excelAPI"; +import { ReportParams } from "@api/types/reports"; +import { useToast } from "@chakra-ui/react"; +import { useMutation } from "@tanstack/react-query"; + +const useGetExcel = () => { + const toast = useToast(); + return useMutation({ + mutationFn: (params: ReportParams) => getExcel(params), + onSuccess: (res) => { + const url = window.URL.createObjectURL(new Blob([res.data])); + const link = document.createElement("a"); + link.href = url; + link.setAttribute("download", "11.30 ๋ฌด์‹ ์‚ฌ ๋ฐ์ผ๋ฆฌ ๋ชจ๋‹ˆํ„ฐ๋ง.xlsx"); + document.body.appendChild(link); + link.click(); + link.remove(); + toast({ + title: "์—‘์…€์ด ์ถ”์ถœ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", + status: "success", + }); + }, + }); +}; + +export default useGetExcel; diff --git a/src/api/hooks/keywords/useGetKeywords.ts b/src/api/hooks/keywords/useGetKeywords.ts new file mode 100644 index 0000000..201e278 --- /dev/null +++ b/src/api/hooks/keywords/useGetKeywords.ts @@ -0,0 +1,12 @@ +import { getKeywords } from "@api/keywordsAPI"; +import { useQuery } from "@tanstack/react-query"; + +const useGetKeywords = () => { + return useQuery({ + queryKey: ["keywords"], + queryFn: getKeywords, + select: (data) => data.result, + }); +}; + +export default useGetKeywords; diff --git a/src/api/hooks/keywords/usePutKeyword.ts b/src/api/hooks/keywords/usePutKeyword.ts new file mode 100644 index 0000000..181c008 --- /dev/null +++ b/src/api/hooks/keywords/usePutKeyword.ts @@ -0,0 +1,21 @@ +import { putKeywords } from "@api/keywordsAPI"; +import { PutKeywordsParams } from "@api/types/keywords"; +import { useToast } from "@chakra-ui/react"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +const usePutKeywords = () => { + const toast = useToast(); + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: (params: PutKeywordsParams) => putKeywords(params), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["keywords"] }); + toast({ + title: "ํ‚ค์›Œ๋“œ๊ฐ€ ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", + status: "success", + }); + }, + }); +}; + +export default usePutKeywords; diff --git a/src/api/hooks/reports/useDeleteReport.ts b/src/api/hooks/reports/useDeleteReport.ts new file mode 100644 index 0000000..02ac8a4 --- /dev/null +++ b/src/api/hooks/reports/useDeleteReport.ts @@ -0,0 +1,22 @@ +import { deleteReport } from "@api/reportsAPI"; +import { ReportParams } from "@api/types/reports"; +import { useToast } from "@chakra-ui/react"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +const useDeleteReport = () => { + const toast = useToast(); + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (params: ReportParams) => deleteReport(params), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["reports"] }); + toast({ + title: "๋ณด๊ณ ์„œ๊ฐ€ ์‚ญ์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", + status: "success", + }); + }, + }); +}; + +export default useDeleteReport; diff --git a/src/api/hooks/reports/useDeleteReportArticle.ts b/src/api/hooks/reports/useDeleteReportArticle.ts new file mode 100644 index 0000000..f06d94e --- /dev/null +++ b/src/api/hooks/reports/useDeleteReportArticle.ts @@ -0,0 +1,16 @@ +import { deleteReportArticle } from "@api/reportsAPI"; +import { DeleteReportArticleParams } from "@api/types/reports"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +const useDeleteReportArticle = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: (params: DeleteReportArticleParams) => + deleteReportArticle(params), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["reportDetails"] }); + }, + }); +}; + +export default useDeleteReportArticle; diff --git a/src/api/hooks/reports/useDeleteReportArticleCategory.ts b/src/api/hooks/reports/useDeleteReportArticleCategory.ts new file mode 100644 index 0000000..a1a409c --- /dev/null +++ b/src/api/hooks/reports/useDeleteReportArticleCategory.ts @@ -0,0 +1,16 @@ +import { deleteReportArticleCategory } from "@api/reportsAPI"; +import { DeleteReportArticleCategoryPrams } from "@api/types/reports"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +const useDeleteReportArticleCategory = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: (params: DeleteReportArticleCategoryPrams) => + deleteReportArticleCategory(params), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["reportDetails"] }); + }, + }); +}; + +export default useDeleteReportArticleCategory; diff --git a/src/api/hooks/reports/useGetReportArticlesOptions.ts b/src/api/hooks/reports/useGetReportArticlesOptions.ts new file mode 100644 index 0000000..3aa6a58 --- /dev/null +++ b/src/api/hooks/reports/useGetReportArticlesOptions.ts @@ -0,0 +1,13 @@ +import { getReportArticlesOptions } from "@api/reportsAPI"; +import { ReportParams } from "@api/types/reports"; +import { useQuery } from "@tanstack/react-query"; + +const useGetReportArticlesOptions = ({ reportId }: ReportParams) => { + return useQuery({ + queryKey: ["reportArticlesOptions", reportId], + queryFn: () => getReportArticlesOptions({ reportId }), + select: (data) => data.result, + }); +}; + +export default useGetReportArticlesOptions; diff --git a/src/api/hooks/reports/useGetReportCategories.ts b/src/api/hooks/reports/useGetReportCategories.ts new file mode 100644 index 0000000..2ac4482 --- /dev/null +++ b/src/api/hooks/reports/useGetReportCategories.ts @@ -0,0 +1,13 @@ +import { getReportCategories } from "@api/reportsAPI"; +import { ReportParams } from "@api/types/reports"; +import { useQuery } from "@tanstack/react-query"; + +const useGetReportCategories = ({ reportId }: ReportParams) => { + return useQuery({ + queryKey: ["reportCategories", reportId], + queryFn: () => getReportCategories({ reportId }), + select: (data) => data.result, + }); +}; + +export default useGetReportCategories; diff --git a/src/api/hooks/reports/useGetReportDetails.ts b/src/api/hooks/reports/useGetReportDetails.ts new file mode 100644 index 0000000..52d8000 --- /dev/null +++ b/src/api/hooks/reports/useGetReportDetails.ts @@ -0,0 +1,13 @@ +import { getReportDetails } from "@api/reportsAPI"; +import { ReportParams } from "@api/types/reports"; +import { useQuery } from "@tanstack/react-query"; + +const useGetReportDetails = ({ reportId }: ReportParams) => { + return useQuery({ + queryKey: ["reportDetails", reportId], + queryFn: () => getReportDetails({ reportId }), + select: (data) => data.result, + }); +}; + +export default useGetReportDetails; diff --git a/src/api/hooks/reports/useGetReports.ts b/src/api/hooks/reports/useGetReports.ts new file mode 100644 index 0000000..3546089 --- /dev/null +++ b/src/api/hooks/reports/useGetReports.ts @@ -0,0 +1,12 @@ +import { getReports } from "@api/reportsAPI"; +import { useQuery } from "@tanstack/react-query"; + +const useGetReports = () => { + return useQuery({ + queryKey: ["reports"], + queryFn: getReports, + select: (data) => data.result, + }); +}; + +export default useGetReports; diff --git a/src/api/hooks/reports/useGetScrap.ts b/src/api/hooks/reports/useGetScrap.ts new file mode 100644 index 0000000..431c8b0 --- /dev/null +++ b/src/api/hooks/reports/useGetScrap.ts @@ -0,0 +1,12 @@ +import { getScrap } from "@api/scrapAPI"; +import { useQuery } from "@tanstack/react-query"; + +const useGetScrap = () => { + return useQuery({ + queryKey: ["scrap"], + queryFn: getScrap, + select: (data) => data.result, + }); +}; + +export default useGetScrap; diff --git a/src/api/hooks/reports/usePatchReportArticleCategory.ts b/src/api/hooks/reports/usePatchReportArticleCategory.ts new file mode 100644 index 0000000..7ccfd70 --- /dev/null +++ b/src/api/hooks/reports/usePatchReportArticleCategory.ts @@ -0,0 +1,16 @@ +import { patchReportArticleCategory } from "@api/reportsAPI"; +import { PatchReportArticleCategoryParams } from "@api/types/reports"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +const usePatchReportArticleCategory = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: (params: PatchReportArticleCategoryParams) => + patchReportArticleCategory(params), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["reportDetails"] }); + }, + }); +}; + +export default usePatchReportArticleCategory; diff --git a/src/api/hooks/reports/usePatchReportArticleSummary.ts b/src/api/hooks/reports/usePatchReportArticleSummary.ts new file mode 100644 index 0000000..af7fd17 --- /dev/null +++ b/src/api/hooks/reports/usePatchReportArticleSummary.ts @@ -0,0 +1,16 @@ +import { patchReportArticleSummary } from "@api/reportsAPI"; +import { PatchReportArticleSummaryParams } from "@api/types/reports"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +const usePatchReportArticleSummary = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: (params: PatchReportArticleSummaryParams) => + patchReportArticleSummary(params), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["reportDetails"] }); + }, + }); +}; + +export default usePatchReportArticleSummary; diff --git a/src/api/hooks/reports/usePatchReportArticlesOptions.ts b/src/api/hooks/reports/usePatchReportArticlesOptions.ts new file mode 100644 index 0000000..1f96575 --- /dev/null +++ b/src/api/hooks/reports/usePatchReportArticlesOptions.ts @@ -0,0 +1,17 @@ +import { patchReportArticlesOptions } from "@api/reportsAPI"; +import { PatchReportArticlesOptionsParams } from "@api/types/reports"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +const usePatchReportArticlesOptions = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: (params: PatchReportArticlesOptionsParams) => + patchReportArticlesOptions(params), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["reportDetails"] }); + window.location.reload(); + }, + }); +}; + +export default usePatchReportArticlesOptions; diff --git a/src/api/hooks/reports/usePatchReportColor.ts b/src/api/hooks/reports/usePatchReportColor.ts new file mode 100644 index 0000000..64fdb6d --- /dev/null +++ b/src/api/hooks/reports/usePatchReportColor.ts @@ -0,0 +1,15 @@ +import { patchReportColor } from "@api/reportsAPI"; +import { PatchReportColorParams } from "@api/types/reports"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +const usePatchReportColor = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: (params: PatchReportColorParams) => patchReportColor(params), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["reportDetails"] }); + }, + }); +}; + +export default usePatchReportColor; diff --git a/src/api/hooks/reports/usePatchReportLogo.ts b/src/api/hooks/reports/usePatchReportLogo.ts new file mode 100644 index 0000000..deca1d1 --- /dev/null +++ b/src/api/hooks/reports/usePatchReportLogo.ts @@ -0,0 +1,15 @@ +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { patchReportLogo } from "@api/reportsAPI"; +import { PatchReportLogoParams } from "@api/types/reports"; +const usePatchReportLogo = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (params: PatchReportLogoParams) => patchReportLogo(params), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["reportDetails"] }); + }, + }); +}; + +export default usePatchReportLogo; diff --git a/src/api/hooks/reports/usePatchReportTitle.ts b/src/api/hooks/reports/usePatchReportTitle.ts new file mode 100644 index 0000000..a796a0a --- /dev/null +++ b/src/api/hooks/reports/usePatchReportTitle.ts @@ -0,0 +1,15 @@ +import { patchReportTitle } from "@api/reportsAPI"; +import { PatchReportTitleParams } from "@api/types/reports"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +const usePatchReportTitle = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: (params: PatchReportTitleParams) => patchReportTitle(params), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["reportDetails"] }); + }, + }); +}; + +export default usePatchReportTitle; diff --git a/src/api/hooks/reports/usePostReport.ts b/src/api/hooks/reports/usePostReport.ts new file mode 100644 index 0000000..1b83840 --- /dev/null +++ b/src/api/hooks/reports/usePostReport.ts @@ -0,0 +1,11 @@ +import { postReport } from "@api/reportsAPI"; +import { PostReportParams } from "@api/types/reports"; +import { useMutation } from "@tanstack/react-query"; + +const usePostReport = () => { + return useMutation({ + mutationFn: (params: PostReportParams) => postReport(params), + }); +}; + +export default usePostReport; diff --git a/src/api/hooks/reports/usePostReportArticle.ts b/src/api/hooks/reports/usePostReportArticle.ts new file mode 100644 index 0000000..e20d4ec --- /dev/null +++ b/src/api/hooks/reports/usePostReportArticle.ts @@ -0,0 +1,15 @@ +import { postReportArticle } from "@api/reportsAPI"; +import { PostReportArticleParams } from "@api/types/reports"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +const usePostReportArticle = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: (params: PostReportArticleParams) => postReportArticle(params), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["reportDetails"] }); + }, + }); +}; + +export default usePostReportArticle; diff --git a/src/api/hooks/reports/usePostReportArticleCategory.ts b/src/api/hooks/reports/usePostReportArticleCategory.ts new file mode 100644 index 0000000..7ccfe8b --- /dev/null +++ b/src/api/hooks/reports/usePostReportArticleCategory.ts @@ -0,0 +1,16 @@ +import { postReportArticleCategory } from "@api/reportsAPI"; +import { PostReportArticleCategoryParams } from "@api/types/reports"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +const usePostReportArticleCategory = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: (params: PostReportArticleCategoryParams) => + postReportArticleCategory(params), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["reportDetails"] }); + }, + }); +}; + +export default usePostReportArticleCategory; diff --git a/src/api/hooks/reports/usePostSearchReports.ts b/src/api/hooks/reports/usePostSearchReports.ts new file mode 100644 index 0000000..cd4a307 --- /dev/null +++ b/src/api/hooks/reports/usePostSearchReports.ts @@ -0,0 +1,14 @@ +import { postSearchReports } from "@api/reportsAPI"; +import { keepPreviousData, useQuery } from "@tanstack/react-query"; + +const usePostSearchReports = (searchTitle: string) => { + return useQuery({ + queryKey: ["searchReports", searchTitle], + queryFn: () => postSearchReports(searchTitle), + select: (data) => data.result, + placeholderData: keepPreviousData, + enabled: !!searchTitle, + }); +}; + +export default usePostSearchReports; diff --git a/src/api/hooks/scrap/useGetScrappedArticle.ts b/src/api/hooks/scrap/useGetScrappedArticle.ts new file mode 100644 index 0000000..2690ea3 --- /dev/null +++ b/src/api/hooks/scrap/useGetScrappedArticle.ts @@ -0,0 +1,12 @@ +import { getScrappedArticle } from "@api/scrapAPI"; +import { useQuery } from "@tanstack/react-query"; + +const useGetScrappedArticle = (scrapId: number) => { + return useQuery({ + queryKey: ["scrappedArticle", scrapId], + queryFn: () => getScrappedArticle(scrapId), + select: (data) => data.result, + }); +}; + +export default useGetScrappedArticle; diff --git a/src/api/hooks/scrap/usePatchUnScrap.ts b/src/api/hooks/scrap/usePatchUnScrap.ts new file mode 100644 index 0000000..538ac90 --- /dev/null +++ b/src/api/hooks/scrap/usePatchUnScrap.ts @@ -0,0 +1,10 @@ +import { patchUnScrap } from "@api/scrapAPI"; +import { useMutation } from "@tanstack/react-query"; + +const usePatchUnScrap = () => { + return useMutation({ + mutationFn: patchUnScrap, + }); +}; + +export default usePatchUnScrap; diff --git a/src/api/hooks/scrap/usePostScrap.ts b/src/api/hooks/scrap/usePostScrap.ts new file mode 100644 index 0000000..6ec44a5 --- /dev/null +++ b/src/api/hooks/scrap/usePostScrap.ts @@ -0,0 +1,17 @@ +import { postScrap } from "@api/scrapAPI"; +import { ScrapParam } from "@api/types/scrap"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +const usePostScrap = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (param: ScrapParam) => postScrap(param), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["articles"] }); + queryClient.invalidateQueries({ queryKey: ["articlesByKeyword"] }); + }, + }); +}; + +export default usePostScrap; diff --git a/src/api/keywordsAPI.ts b/src/api/keywordsAPI.ts new file mode 100644 index 0000000..55473cb --- /dev/null +++ b/src/api/keywordsAPI.ts @@ -0,0 +1,10 @@ +import { authApiGet, authApiPut } from "./apiUtils"; +import { GetKeywordsResponse, PutKeywordsParams } from "./types/keywords"; + +export const getKeywords = async () => { + return authApiGet("/keywords"); +}; + +export const putKeywords = async ({ data }: PutKeywordsParams) => { + return authApiPut("/keywords", data); +}; diff --git a/src/api/reportsAPI.ts b/src/api/reportsAPI.ts new file mode 100644 index 0000000..cfd6c1e --- /dev/null +++ b/src/api/reportsAPI.ts @@ -0,0 +1,169 @@ +import { + authApiDelete, + authApiGet, + authApiPatch, + authApiPost, +} from "./apiUtils"; +import { + DeleteReportArticleCategoryPrams, + DeleteReportArticleParams, + GetReportArticleCategoriesResponse, + GetReportArticlesOptionsResponse, + GetReportDetailsResponse, + PatchReportArticleCategoryParams, + PatchReportArticlesOptionsParams, + PatchReportArticleSummaryParams, + PatchReportColorParams, + PatchReportLogoParams, + PatchReportTitleParams, + PostReportArticleCategoryParams, + PostReportArticleParams, + PostReportParams, + PostReportResponse, + ReportParams, + ReportResponse, +} from "./types/reports"; + +export const getReports = () => { + return authApiGet("/reports"); +}; + +export const getReportDetails = ({ reportId }: ReportParams) => { + return authApiGet("/reports/details", { + reportId, + }); +}; + +export const getReportCategories = ({ reportId }: ReportParams) => { + return authApiGet("/reports/categories", { + reportId, + }); +}; + +export const getReportArticlesOptions = ({ reportId }: ReportParams) => { + return authApiGet( + "/reports/articles/options", + { + reportId, + }, + ); +}; + +export const postSearchReports = (searchTitle: string) => { + return authApiPost("/reports/search", { + searchTitle, + }); +}; + +export const postReport = ({ data, logo }: PostReportParams) => { + const formData = new FormData(); + + formData.append("request", JSON.stringify(data)); + + if (logo) { + formData.append("logo", logo); + } + + return authApiPost("/reports", formData); +}; + +export const postReportArticle = ({ + reportId, + data, +}: PostReportArticleParams) => { + return authApiPost("/reports/articles/update", data, { + reportId, + }); +}; + +export const postReportArticleCategory = ({ + reportId, + data, +}: PostReportArticleCategoryParams) => { + return authApiPost("/reports/category", data, { + reportId, + }); +}; + +export const deleteReport = ({ reportId }: ReportParams) => { + return authApiDelete("/reports", { + reportId, + }); +}; + +export const patchReportTitle = ({ + reportId, + data, +}: PatchReportTitleParams) => { + return authApiPatch("/reports/title", data, { + reportId, + }); +}; + +export const patchReportColor = ({ + reportId, + data, +}: PatchReportColorParams) => { + return authApiPatch("/reports/color", data, { + reportId, + }); +}; + +export const patchReportLogo = ({ reportId, logo }: PatchReportLogoParams) => { + const formData = new FormData(); + formData.append("logo", logo); + + return authApiPatch("/reports/logo", formData, { + reportId, + }); +}; + +export const patchReportArticleSummary = ({ + reportId, + reportArticleId, + data, +}: PatchReportArticleSummaryParams) => { + return authApiPatch("/reports/articles/summary", data, { + reportId, + reportArticleId, + }); +}; + +export const patchReportArticleCategory = ({ + reportId, + reportArticleId, + newCategoryId, +}: PatchReportArticleCategoryParams) => { + return authApiPatch("/reports/articles/category", undefined, { + reportId, + reportArticleId, + newCategoryId, + }); +}; + +export const patchReportArticlesOptions = ({ + reportId, + data, +}: PatchReportArticlesOptionsParams) => { + return authApiPatch("/reports/articles/options", data, { reportId }); +}; + +export const deleteReportArticle = ({ + reportId, + reportArticleId, +}: DeleteReportArticleParams) => { + return authApiDelete("/reports/articles/delete", { + reportId, + reportArticleId, + }); +}; + +export const deleteReportArticleCategory = ({ + reportId, + categoryId, +}: DeleteReportArticleCategoryPrams) => { + return authApiPatch("/reports/category/delete", undefined, { + reportId, + categoryId, + }); +}; diff --git a/src/api/scrapAPI.ts b/src/api/scrapAPI.ts new file mode 100644 index 0000000..de2b99e --- /dev/null +++ b/src/api/scrapAPI.ts @@ -0,0 +1,18 @@ +import { authApiGet, authApiPatch, authApiPost } from "./apiUtils"; +import { GetScrapResponse, ScrapParam, ScrappedArticle } from "./types/scrap"; + +export const getScrap = () => { + return authApiGet("/scrap"); +}; + +export const getScrappedArticle = (scrapId: number) => { + return authApiGet("/scrap/info", { scrapId }); +}; + +export const postScrap = ({ articleId }: ScrapParam) => { + return authApiPost("/scrap", undefined, { articleId }); +}; + +export const patchUnScrap = () => { + return authApiPatch("/scrap/unscrap"); +}; diff --git a/src/api/tokenInstance.ts b/src/api/tokenInstance.ts new file mode 100644 index 0000000..a1550e9 --- /dev/null +++ b/src/api/tokenInstance.ts @@ -0,0 +1,25 @@ +import axios from "axios"; + +const baseURL = import.meta.env.VITE_BASE_URL; + +const tokenInstance = axios.create({ + baseURL, + headers: { + Accept: "*/*", + }, + withCredentials: true, +}); + +tokenInstance.interceptors.response.use( + (response) => response, + async (error) => { + if (error.response?.status === 401) { + window.location.href = "/"; + } else { + // window.location.href = "/404"; + } + return Promise.reject(error); + }, +); + +export default tokenInstance; diff --git a/src/api/types/accounts.ts b/src/api/types/accounts.ts new file mode 100644 index 0000000..ed1cf74 --- /dev/null +++ b/src/api/types/accounts.ts @@ -0,0 +1,24 @@ +export interface PostSendEmailConfirmData { + email: string; +} + +export interface PostVerifyCodeData { + email: string; + verificationCode: string; +} + +export interface PostSignUpData { + email: string; + password: string; + companyName: string; + managerName: string; + managerPhone: string; + agreement: boolean; +} +export interface PostSignInData { + email: string; + password: string; +} +export interface PostSendPasswordChangeEmailData { + email: string; +} diff --git a/src/api/types/articles.ts b/src/api/types/articles.ts new file mode 100644 index 0000000..10c2a77 --- /dev/null +++ b/src/api/types/articles.ts @@ -0,0 +1,37 @@ +import { CategoryTypeEn } from "types/category"; + +export interface Article { + articleId: number; + title: string; + body: string; + url: string; + imageUrl: string; + publisherName: string; + publishDate: string; + reporterName: string; + scrapped: boolean; + added: boolean; + read: boolean; +} + +export interface ListPageResponse { + googleArticles: Article[]; + totalResults: number; + keyword: string; +} + +export interface GetArticlesResponse { + listPageResponse: ListPageResponse[]; + totalCount: number; + size: number; +} + +export interface GetArticlesParams { + categoryType: CategoryTypeEn | undefined; + page: number; +} +export interface GetArticlesByKeywordParams { + keywordId: number | undefined; + categoryType: CategoryTypeEn | undefined; + page: number; +} diff --git a/src/api/types/clients.ts b/src/api/types/clients.ts new file mode 100644 index 0000000..9cfcd27 --- /dev/null +++ b/src/api/types/clients.ts @@ -0,0 +1,42 @@ +export interface GetClientInfo { + name: string; + logoUrl: string; +} + +export interface Client { + clientId: number; + name: string; + managerName: string; + logoUrl: string; +} + +export interface PostClientParams { + data: { + name: string; + manager_name: string; + category_keywords: { + SELF: string[]; + COMPETITOR: string[]; + INDUSTRY: string[]; + }; + recipient_emails: string[]; + cc_emails: string[]; + }; + logo: File | null; +} + +export interface PutClientParams { + clientId: number; + data: { + name: string; + managerName: string; + }; + logo: File | null; +} + +export interface SearchClientResponse { + clientId: number; + name: string; + managerName: string; + logoUrl: string; +} diff --git a/src/api/types/emails.ts b/src/api/types/emails.ts new file mode 100644 index 0000000..8200f72 --- /dev/null +++ b/src/api/types/emails.ts @@ -0,0 +1,21 @@ +export interface PostSendEmailParams { + reportId: number; + data: { + subject: string; + content?: string; + }; +} + +export interface PutEmailsParams { + data: { + recipients: string[]; + ccs: string[]; + }; + img: File | null; +} + +export interface GetEmailsResponse { + recipients: string[]; + ccs: string[]; + signatureImageUrl: string | null; +} diff --git a/src/api/types/keywords.ts b/src/api/types/keywords.ts new file mode 100644 index 0000000..37a53ad --- /dev/null +++ b/src/api/types/keywords.ts @@ -0,0 +1,25 @@ +import { CategoryTypeEn } from "types/category"; + +export interface Keyword { + keywordId: number; + keywordName: string; + categoryType: CategoryTypeEn; +} + +export type GetKeywordsResponse = { + [key: string]: Keyword[]; +}; + +interface KeywordsByCategory { + SELF: string[]; + COMPETITOR: string[]; + INDUSTRY: string[]; +} + +export interface PutKeywordsData { + keywordsByCategory: KeywordsByCategory; +} + +export interface PutKeywordsParams { + data: PutKeywordsData; +} diff --git a/src/api/types/reports.ts b/src/api/types/reports.ts new file mode 100644 index 0000000..5b42c51 --- /dev/null +++ b/src/api/types/reports.ts @@ -0,0 +1,147 @@ +// Response + +export interface ReportResponse { + reportId: number; + title: string; + createdAt: string; + updatedAt: string; +} +export interface ReportArticle { + reportArticleId: number; + keyword: string; + publishedDate: string; + headLine: string; + url: string; + media: string; + reporter: string; + summary: string; +} + +export interface ReportCategory { + reportCategoryId: number; + reportCategoryName: string; + reportCategoryDescription: string; + reportArticlesResponses: ReportArticle[]; + default: boolean; +} + +export interface GetReportDetailsResponse { + title: string; + color: string; + logo: string; + articles: [ + { + SELF: ReportCategory[]; + COMPETITOR: ReportCategory[]; + INDUSTRY: ReportCategory[]; + }, + ]; +} + +interface ReportCategoryInfo { + reportCategoryId: number; + reportCategoryName: string; + reportCategoryDescription: string; + isDefault: string; +} + +export interface GetReportArticleCategoriesResponse { + SELF: ReportCategoryInfo[]; + COMPETITOR: ReportCategoryInfo[]; + INDUSTRY: ReportCategoryInfo[]; +} + +export interface GetReportArticlesOptionsResponse { + media: boolean; + reporter: boolean; +} +export interface PostReportResponse { + reportId: number; +} + +// Params +export interface ReportParams { + reportId: number; +} + +export interface PostReportArticleParams extends ReportParams { + data: { + categoryType: string; + keyword: string; + headLine: string; + url: string; + publishedDate: string; + media: string; + reporter: string; + }; +} + +interface PostReportCategory { + reportCategoryName: string; + reportCategoryDescription: string; + articleId: number[]; +} + +export interface PostReportParams { + data: { + reportTitle: string; + color: string; + media: boolean; + reporter: boolean; + articles: { + SELF: PostReportCategory[]; + COMPETITOR: PostReportCategory[]; + INDUSTRY: PostReportCategory[]; + }; + }; + logo: File | null; +} + +export interface PostReportArticleCategoryParams extends ReportParams { + data: { + reportCategoryName: string; + reportCategoryDescription: string; + reportCategoryType: string; + }; +} + +export interface PatchReportTitleParams extends ReportParams { + data: { + title: string; + }; +} +export interface PatchReportColorParams extends ReportParams { + data: { + color: string; + }; +} +export interface PatchReportLogoParams extends ReportParams { + logo: File; +} + +export interface PatchReportArticleSummaryParams extends ReportParams { + reportArticleId: number; + data: { + summary: string; + }; +} + +export interface PatchReportArticleCategoryParams extends ReportParams { + reportArticleId: number; + newCategoryId: number; +} + +export interface PatchReportArticlesOptionsParams extends ReportParams { + data: { + media: boolean; + reporter: boolean; + }; +} + +export interface DeleteReportArticleParams extends ReportParams { + reportArticleId: number; +} + +export interface DeleteReportArticleCategoryPrams extends ReportParams { + categoryId: number; +} diff --git a/src/api/types/response.ts b/src/api/types/response.ts new file mode 100644 index 0000000..e209e03 --- /dev/null +++ b/src/api/types/response.ts @@ -0,0 +1,6 @@ +export interface ApiResponse { + isSuccess: boolean; + code: string; + message: string; + result?: T; +} diff --git a/src/api/types/scrap.ts b/src/api/types/scrap.ts new file mode 100644 index 0000000..a011b8f --- /dev/null +++ b/src/api/types/scrap.ts @@ -0,0 +1,23 @@ +export interface ScrappedArticle { + originalArticleId: number; + scrapId: number; + keyword: string; + title: string; + body: string; + url: string; + imageUrl: string; + publisherName: string; + publishDate: string; + reporterName: string; + categoryType: string; +} + +export interface GetScrapResponse { + SELF: ScrappedArticle[]; + COMPETITOR: ScrappedArticle[]; + INDUSTRY: ScrappedArticle[]; +} + +export interface ScrapParam { + articleId: number; +} diff --git a/src/assets/images/clientManagement.webp b/src/assets/images/clientManagement.webp new file mode 100644 index 0000000..0e8270c Binary files /dev/null and b/src/assets/images/clientManagement.webp differ diff --git a/src/assets/images/copyright.webp b/src/assets/images/copyright.webp new file mode 100644 index 0000000..4ad6961 Binary files /dev/null and b/src/assets/images/copyright.webp differ diff --git a/src/assets/images/dashboard.webp b/src/assets/images/dashboard.webp new file mode 100644 index 0000000..3e5be07 Binary files /dev/null and b/src/assets/images/dashboard.webp differ diff --git a/src/assets/images/defaultImage.webp b/src/assets/images/defaultImage.webp new file mode 100644 index 0000000..3426e95 Binary files /dev/null and b/src/assets/images/defaultImage.webp differ diff --git a/src/assets/images/function1.webp b/src/assets/images/function1.webp new file mode 100644 index 0000000..8948004 Binary files /dev/null and b/src/assets/images/function1.webp differ diff --git a/src/assets/images/function2.webp b/src/assets/images/function2.webp new file mode 100644 index 0000000..7a68f41 Binary files /dev/null and b/src/assets/images/function2.webp differ diff --git a/src/assets/images/function3.webp b/src/assets/images/function3.webp new file mode 100644 index 0000000..23bad97 Binary files /dev/null and b/src/assets/images/function3.webp differ diff --git a/src/assets/images/index.tsx b/src/assets/images/index.tsx new file mode 100644 index 0000000..bd420a5 --- /dev/null +++ b/src/assets/images/index.tsx @@ -0,0 +1,11 @@ +export { default as DefaultImage } from "./defaultImage.webp"; +export { default as DashboardImage } from "./dashboard.webp"; +export { default as Function1Image } from "./function1.webp"; +export { default as Function2Image } from "./function2.webp"; +export { default as Function3Image } from "./function3.webp"; +export { default as TestimonialsImage } from "./testimonials.webp"; +export { default as ClientManagementImage } from "./clientManagement.webp"; +export { default as InsightsImage } from "./insights.webp"; +export { default as OutputImage } from "./output.webp"; +export { default as PerformanceImage } from "./performance.webp"; +export { default as CopyrightIcon } from "./copyright.webp"; diff --git a/src/assets/images/insights.webp b/src/assets/images/insights.webp new file mode 100644 index 0000000..2ff6ade Binary files /dev/null and b/src/assets/images/insights.webp differ diff --git a/src/assets/images/output.webp b/src/assets/images/output.webp new file mode 100644 index 0000000..c34dc34 Binary files /dev/null and b/src/assets/images/output.webp differ diff --git a/src/assets/images/performance.webp b/src/assets/images/performance.webp new file mode 100644 index 0000000..0ec371d Binary files /dev/null and b/src/assets/images/performance.webp differ diff --git a/src/assets/images/testimonials.webp b/src/assets/images/testimonials.webp new file mode 100644 index 0000000..628c269 Binary files /dev/null and b/src/assets/images/testimonials.webp differ diff --git a/src/assets/svgs/add.svg b/src/assets/svgs/add.svg new file mode 100644 index 0000000..8b956c7 --- /dev/null +++ b/src/assets/svgs/add.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/addCircleFill.svg b/src/assets/svgs/addCircleFill.svg new file mode 100644 index 0000000..63bedfe --- /dev/null +++ b/src/assets/svgs/addCircleFill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/svgs/addCircleThin.svg b/src/assets/svgs/addCircleThin.svg new file mode 100644 index 0000000..668ee94 --- /dev/null +++ b/src/assets/svgs/addCircleThin.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/added.svg b/src/assets/svgs/added.svg new file mode 100644 index 0000000..1731036 --- /dev/null +++ b/src/assets/svgs/added.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/analyze.svg b/src/assets/svgs/analyze.svg new file mode 100644 index 0000000..92bf6f0 --- /dev/null +++ b/src/assets/svgs/analyze.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/arrowBeforeThick.svg b/src/assets/svgs/arrowBeforeThick.svg new file mode 100644 index 0000000..22a3b67 --- /dev/null +++ b/src/assets/svgs/arrowBeforeThick.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/arrowDown.svg b/src/assets/svgs/arrowDown.svg new file mode 100644 index 0000000..a3a522f --- /dev/null +++ b/src/assets/svgs/arrowDown.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/arrowLeft.svg b/src/assets/svgs/arrowLeft.svg new file mode 100644 index 0000000..088e38c --- /dev/null +++ b/src/assets/svgs/arrowLeft.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/arrowNextFill.svg b/src/assets/svgs/arrowNextFill.svg new file mode 100644 index 0000000..81a071f --- /dev/null +++ b/src/assets/svgs/arrowNextFill.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/arrowRight.svg b/src/assets/svgs/arrowRight.svg new file mode 100644 index 0000000..5ba3b17 --- /dev/null +++ b/src/assets/svgs/arrowRight.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/arrowUp.svg b/src/assets/svgs/arrowUp.svg new file mode 100644 index 0000000..50b39c2 --- /dev/null +++ b/src/assets/svgs/arrowUp.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/attention.svg b/src/assets/svgs/attention.svg new file mode 100644 index 0000000..55bf86b --- /dev/null +++ b/src/assets/svgs/attention.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/barChart.svg b/src/assets/svgs/barChart.svg new file mode 100644 index 0000000..c0b87e6 --- /dev/null +++ b/src/assets/svgs/barChart.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/blog.svg b/src/assets/svgs/blog.svg new file mode 100644 index 0000000..da9641e --- /dev/null +++ b/src/assets/svgs/blog.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/svgs/bookmarkBlank.svg b/src/assets/svgs/bookmarkBlank.svg new file mode 100644 index 0000000..9335a50 --- /dev/null +++ b/src/assets/svgs/bookmarkBlank.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/bookmarkFill.svg b/src/assets/svgs/bookmarkFill.svg new file mode 100644 index 0000000..1db7acb --- /dev/null +++ b/src/assets/svgs/bookmarkFill.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/checkCircleFill.svg b/src/assets/svgs/checkCircleFill.svg new file mode 100644 index 0000000..da72938 --- /dev/null +++ b/src/assets/svgs/checkCircleFill.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/checkCircleOutline.svg b/src/assets/svgs/checkCircleOutline.svg new file mode 100644 index 0000000..6667a1a --- /dev/null +++ b/src/assets/svgs/checkCircleOutline.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/checkboxBlank.svg b/src/assets/svgs/checkboxBlank.svg new file mode 100644 index 0000000..428f469 --- /dev/null +++ b/src/assets/svgs/checkboxBlank.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/checkboxError.svg b/src/assets/svgs/checkboxError.svg new file mode 100644 index 0000000..53df340 --- /dev/null +++ b/src/assets/svgs/checkboxError.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/checkboxFill.svg b/src/assets/svgs/checkboxFill.svg new file mode 100644 index 0000000..e262e67 --- /dev/null +++ b/src/assets/svgs/checkboxFill.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/clock.svg b/src/assets/svgs/clock.svg new file mode 100644 index 0000000..7393ec6 --- /dev/null +++ b/src/assets/svgs/clock.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/close.svg b/src/assets/svgs/close.svg new file mode 100644 index 0000000..6b27307 --- /dev/null +++ b/src/assets/svgs/close.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/dashboard.svg b/src/assets/svgs/dashboard.svg new file mode 100644 index 0000000..7a3a007 --- /dev/null +++ b/src/assets/svgs/dashboard.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/svgs/delete.svg b/src/assets/svgs/delete.svg new file mode 100644 index 0000000..d070f89 --- /dev/null +++ b/src/assets/svgs/delete.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/doner.svg b/src/assets/svgs/doner.svg new file mode 100644 index 0000000..f50fdf6 --- /dev/null +++ b/src/assets/svgs/doner.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/doubleArrowLeft.svg b/src/assets/svgs/doubleArrowLeft.svg new file mode 100644 index 0000000..7115261 --- /dev/null +++ b/src/assets/svgs/doubleArrowLeft.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/svgs/doubleArrowRight.svg b/src/assets/svgs/doubleArrowRight.svg new file mode 100644 index 0000000..92c0c97 --- /dev/null +++ b/src/assets/svgs/doubleArrowRight.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/svgs/editNote.svg b/src/assets/svgs/editNote.svg new file mode 100644 index 0000000..2287794 --- /dev/null +++ b/src/assets/svgs/editNote.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/editSquare.svg b/src/assets/svgs/editSquare.svg new file mode 100644 index 0000000..fa2147d --- /dev/null +++ b/src/assets/svgs/editSquare.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/error.svg b/src/assets/svgs/error.svg new file mode 100644 index 0000000..5d1f070 --- /dev/null +++ b/src/assets/svgs/error.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/grayLogo.svg b/src/assets/svgs/grayLogo.svg new file mode 100644 index 0000000..d7ecf97 --- /dev/null +++ b/src/assets/svgs/grayLogo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/svgs/hamburger.svg b/src/assets/svgs/hamburger.svg new file mode 100644 index 0000000..4347263 --- /dev/null +++ b/src/assets/svgs/hamburger.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/svgs/handle.svg b/src/assets/svgs/handle.svg new file mode 100644 index 0000000..7ec2f72 --- /dev/null +++ b/src/assets/svgs/handle.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svgs/index.tsx b/src/assets/svgs/index.tsx new file mode 100644 index 0000000..3a73442 --- /dev/null +++ b/src/assets/svgs/index.tsx @@ -0,0 +1,56 @@ +export { default as LogoIcon } from "./logo.svg?react"; +export { default as VisibilityOffIcon } from "./visibilityOff.svg?react"; +export { default as VisibilityOnIcon } from "./visibilityOn.svg?react"; +export { default as CheckboxBlankIcon } from "./checkboxBlank.svg?react"; +export { default as CheckboxFillIcon } from "./checkboxFill.svg?react"; +export { default as CheckboxErrorIcon } from "./checkboxError.svg?react"; +export { default as ErrorIcon } from "./error.svg?react"; +export { default as LoadingIcon } from "./loading.svg?react"; +export { default as CloseIcon } from "./close.svg?react"; +export { default as SearchIcon } from "./search.svg?react"; +export { default as AddCircleFillIcon } from "./addCircleFill.svg?react"; +export { default as PersonIcon } from "./person.svg?react"; +export { default as MoreHorizIcon } from "./moreHoriz.svg?react"; +export { default as EditSquareIcon } from "./editSquare.svg?react"; +export { default as DeleteIcon } from "./delete.svg?react"; +export { default as AttentionIcon } from "./attention.svg?react"; +export { default as AddCircleThinIcon } from "./addCircleThin.svg?react"; +export { default as ArrowBeforeThick } from "./arrowBeforeThick.svg?react"; +export { default as NewsIcon } from "./news.svg?react"; +export { default as AnalyzeIcon } from "./analyze.svg?react"; +export { default as HamburgerIcon } from "./hamburger.svg?react"; +export { default as ArrowDownIcon } from "./arrowDown.svg?react"; +export { default as InternetIcon } from "./internet.svg?react"; +export { default as ReportCheckIcon } from "./reportCheck.svg?react"; +export { default as ClockIcon } from "./clock.svg?react"; +export { default as BookmarkBlankIcon } from "./bookmarkBlank.svg?react"; +export { default as BookmarkFillIcon } from "./bookmarkFill.svg?react"; +export { default as ProfileIcon } from "./profile.svg?react"; +export { default as UndoIcon } from "./undo.svg?react"; +export { default as ReportIcon } from "./report.svg?react"; +export { default as MailIcon } from "./mail.svg?react"; +export { default as ArrowUpIcon } from "./arrowUp.svg?react"; +export { default as CheckCircleOutlineIcon } from "./checkCircleOutline.svg?react"; +export { default as CheckCircleFillIcon } from "./checkCircleFill.svg?react"; +export { default as DonerIcon } from "./doner.svg?react"; +export { default as AddIcon } from "./add.svg?react"; +export { default as SummarizeIcon } from "./summarize.svg?react"; +export { default as HandleIcon } from "./handle.svg?react"; +export { default as NotFoundIcon } from "./notFound.svg?react"; +export { default as EditNoteIcon } from "./editNote.svg?react"; +export { default as ArrowLeftIcon } from "./arrowLeft.svg?react"; +export { default as DoubleArrowLeftIcon } from "./doubleArrowLeft.svg?react"; +export { default as ArrowRightIcon } from "./arrowRight.svg?react"; +export { default as DoubleArrowRightIcon } from "./doubleArrowRight.svg?react"; +export { default as PencilIcon } from "./pencil.svg?react"; +export { default as DashboardIcon } from "./dashboard.svg?react"; +export { default as ArrowNextFillIcon } from "./arrowNextFill.svg?react"; +export { default as LightBulbIcon } from "./lightBulb.svg?react"; +export { default as UnlimitedIcon } from "./unlimited.svg?react"; +export { default as BarChartIcon } from "./barChart.svg?react"; +export { default as GrayLogoIcon } from "./grayLogo.svg?react"; +export { default as InstagramIcon } from "./instagram.svg?react"; +export { default as BlogIcon } from "./blog.svg?react"; +export { default as YoutubeIcon } from "./youtube.svg?react"; +export { default as AddedIcon } from "./added.svg?react"; +export { default as PersonCheckIcon } from "./personCheck.svg?react"; diff --git a/src/assets/svgs/instagram.svg b/src/assets/svgs/instagram.svg new file mode 100644 index 0000000..a4e552b --- /dev/null +++ b/src/assets/svgs/instagram.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/svgs/internet.svg b/src/assets/svgs/internet.svg new file mode 100644 index 0000000..5d6c603 --- /dev/null +++ b/src/assets/svgs/internet.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/svgs/lightBulb.svg b/src/assets/svgs/lightBulb.svg new file mode 100644 index 0000000..7578b10 --- /dev/null +++ b/src/assets/svgs/lightBulb.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/loading.svg b/src/assets/svgs/loading.svg new file mode 100644 index 0000000..db11bb3 --- /dev/null +++ b/src/assets/svgs/loading.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/svgs/logo.svg b/src/assets/svgs/logo.svg new file mode 100644 index 0000000..b461559 --- /dev/null +++ b/src/assets/svgs/logo.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/assets/svgs/mail.svg b/src/assets/svgs/mail.svg new file mode 100644 index 0000000..11c625f --- /dev/null +++ b/src/assets/svgs/mail.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/moreHoriz.svg b/src/assets/svgs/moreHoriz.svg new file mode 100644 index 0000000..35045cf --- /dev/null +++ b/src/assets/svgs/moreHoriz.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/svgs/news.svg b/src/assets/svgs/news.svg new file mode 100644 index 0000000..9cb1f99 --- /dev/null +++ b/src/assets/svgs/news.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/svgs/notFound.svg b/src/assets/svgs/notFound.svg new file mode 100644 index 0000000..689f7af --- /dev/null +++ b/src/assets/svgs/notFound.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/svgs/pencil.svg b/src/assets/svgs/pencil.svg new file mode 100644 index 0000000..d9f8095 --- /dev/null +++ b/src/assets/svgs/pencil.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/person.svg b/src/assets/svgs/person.svg new file mode 100644 index 0000000..4828fb7 --- /dev/null +++ b/src/assets/svgs/person.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/personCheck.svg b/src/assets/svgs/personCheck.svg new file mode 100644 index 0000000..dcabfd8 --- /dev/null +++ b/src/assets/svgs/personCheck.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/svgs/profile.svg b/src/assets/svgs/profile.svg new file mode 100644 index 0000000..1405b56 --- /dev/null +++ b/src/assets/svgs/profile.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/assets/svgs/report.svg b/src/assets/svgs/report.svg new file mode 100644 index 0000000..825e089 --- /dev/null +++ b/src/assets/svgs/report.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/reportCheck.svg b/src/assets/svgs/reportCheck.svg new file mode 100644 index 0000000..956bd0d --- /dev/null +++ b/src/assets/svgs/reportCheck.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/svgs/search.svg b/src/assets/svgs/search.svg new file mode 100644 index 0000000..eba6434 --- /dev/null +++ b/src/assets/svgs/search.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/summarize.svg b/src/assets/svgs/summarize.svg new file mode 100644 index 0000000..e9d693f --- /dev/null +++ b/src/assets/svgs/summarize.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/undo.svg b/src/assets/svgs/undo.svg new file mode 100644 index 0000000..e12f70d --- /dev/null +++ b/src/assets/svgs/undo.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/unlimited.svg b/src/assets/svgs/unlimited.svg new file mode 100644 index 0000000..74907b7 --- /dev/null +++ b/src/assets/svgs/unlimited.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/visibilityOff.svg b/src/assets/svgs/visibilityOff.svg new file mode 100644 index 0000000..49959c3 --- /dev/null +++ b/src/assets/svgs/visibilityOff.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/visibilityOn.svg b/src/assets/svgs/visibilityOn.svg new file mode 100644 index 0000000..ec135a8 --- /dev/null +++ b/src/assets/svgs/visibilityOn.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/youtube.svg b/src/assets/svgs/youtube.svg new file mode 100644 index 0000000..f796e2c --- /dev/null +++ b/src/assets/svgs/youtube.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx new file mode 100644 index 0000000..2fe280b --- /dev/null +++ b/src/components/Button/index.tsx @@ -0,0 +1,50 @@ +import cn from "@utils/cn"; +import { cva, VariantProps } from "class-variance-authority"; +import { ButtonHTMLAttributes, ReactNode } from "react"; + +const ButtonVariants = cva( + "rounded text-md font-semibold disabled:bg-surface-disable disabled:text-disable", + { + variants: { + style: { + filled: "bg-primary-500 text-white hover:bg-primary-700", + tonal: + "bg-surface-secondary text-primary-700 border-[0.5px] border-primary-200 hover:bg-primary-100 ", + "outline-m": + "bg-white text-primary-700 border-[0.5px] border-primary-200 hover:bg-surface-secondary", + "outline-s": + "rounded-[2px] border-[0.5px] border-primary-500 bg-white text-primary-700 hover:bg-surface-secondary", + "outline-l": + "border-1 border-primary-500 bg-white text-primary-700 hover:bg-surface-secondary", + tertiary: + "rounded-[2px] bg-white text-primary-700 hover:bg-surface-primary", + }, + }, + }, +); + +interface ButtonProps + extends Omit, "style">, + VariantProps { + children?: ReactNode; + className?: string; +} + +const Button: React.FC = ({ + style, + children, + className, + ...props +}) => { + return ( + + ); +}; + +export default Button; diff --git a/src/components/CancelModal/index.tsx b/src/components/CancelModal/index.tsx new file mode 100644 index 0000000..439379e --- /dev/null +++ b/src/components/CancelModal/index.tsx @@ -0,0 +1,56 @@ +import { CloseIcon, ErrorIcon } from "@assets/svgs"; + +interface CancelModalProps { + onClose: () => void; + handleCancel: () => void; + headingText?: string; + bodyText?: React.ReactNode; + closeButtonText?: string; + cancelButtonText?: string; +} + +const CancelModal: React.FC = ({ + onClose, + handleCancel, + headingText, + bodyText, + closeButtonText, + cancelButtonText, +}) => { + return ( +
+
+ + +

+ {headingText} +

+

+ {bodyText} +

+
+ + +
+
+
+ ); +}; + +export default CancelModal; diff --git a/src/components/Dropdown/index.tsx b/src/components/Dropdown/index.tsx new file mode 100644 index 0000000..13febf4 --- /dev/null +++ b/src/components/Dropdown/index.tsx @@ -0,0 +1,54 @@ +import { ArrowDownIcon } from "@assets/svgs"; +import { Button, Menu, MenuButton, MenuItem, MenuList } from "@chakra-ui/react"; + +interface DropdownProps { + width: number; + options: string[]; + selectedOption: string; + onSelectOption: (option: string) => void; +} + +const Dropdown: React.FC = ({ + width, + options, + selectedOption, + onSelectOption, +}) => { + return ( + + } + className="border-1 border-neutral-200 py-2 pl-3 pr-2 text-left text-sm font-semibold text-title" + sx={{ + bg: "white", + }} + _hover={{ bg: "#f5f5f5" }} + _expanded={{ bg: "#f5f5f5" }} + > + {selectedOption} + + + {options.map((option) => ( + onSelectOption(option)} + className="font-medium" + > + {option} + + ))} + + + ); +}; + +export default Dropdown; diff --git a/src/components/Footer/index.tsx b/src/components/Footer/index.tsx new file mode 100644 index 0000000..8a5be81 --- /dev/null +++ b/src/components/Footer/index.tsx @@ -0,0 +1,71 @@ +import { CopyrightIcon } from "@assets/images"; +import { + BlogIcon, + GrayLogoIcon, + InstagramIcon, + YoutubeIcon, +} from "@assets/svgs"; + +const Footer = () => { + return ( +
+
+ +
+

์ด์šฉ์•ฝ๊ด€

+
+

๊ฐœ์ธ์ •๋ณด์ฒ˜๋ฆฌ๋ฐฉ์นจ

+
+

๊ณต์ง€์‚ฌํ•ญ

+
+

๋ฌธ์˜ํ•˜๊ธฐ

+
+
+
+

+ ์„œ์šธํŠน๋ณ„์‹œ ๊ฐ•๋‚จ๊ตฌ 123๋กœ 45๋ฒˆ์ง€ 6F +

+

+ ์‚ฌ์—…์ž๋“ฑ๋ก๋ฒˆํ˜ธ : 123-45-67890 +

+

+ ๋Œ€ํ‘œ์ด์‚ฌ : ์ดํ˜„์ˆ˜ +

+
+
+

+ ๋Œ€ํ‘œ ์ „ํ™”: 010-8516-2372 +

+

+ ํ‰์ผ: 09:00 - 18:00 +

+

+ themonitor2024@gmail.com +

+
+
+
+
+ +
+
+ +
+
+ +
+
+
+ +

+ The Monitor. All rights reserved +

+
+
+
+
+
+ ); +}; + +export default Footer; diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx new file mode 100644 index 0000000..93394b9 --- /dev/null +++ b/src/components/Header/index.tsx @@ -0,0 +1,54 @@ +import { ArrowDownIcon, LogoIcon, ProfileIcon } from "@assets/svgs"; +import Button from "@components/Button"; +import routes from "@constants/routes"; +import React from "react"; +import { useLocation, useNavigate } from "react-router-dom"; + +const Header: React.FC = () => { + const navigate = useNavigate(); + const location = useLocation(); + + const paths = [routes.main, routes.signIn, routes.signUp, routes.password]; + const isPathInPaths = paths.includes( + location.pathname as (typeof paths)[number], + ); + + return ( +
+ navigate(isPathInPaths ? routes.main : routes.dashboard)} + /> + {isPathInPaths && ( +
+ + +
+ )} + {!isPathInPaths && ( +
+ +
name
+ +
+ )} +
+ ); +}; + +export default Header; diff --git a/src/components/Input/index.tsx b/src/components/Input/index.tsx new file mode 100644 index 0000000..445f63d --- /dev/null +++ b/src/components/Input/index.tsx @@ -0,0 +1,69 @@ +import { ErrorIcon, VisibilityOffIcon, VisibilityOnIcon } from "@assets/svgs"; +import { forwardRef, InputHTMLAttributes, useState } from "react"; +import { twMerge } from "tailwind-merge"; + +interface InputProps extends InputHTMLAttributes { + isInvalid?: boolean; +} + +const style = + "h-14 rounded w-full px-4 text-md font-regular focus:outline focus:outline-1 placeholder:text-disable"; + +const Input = forwardRef( + ({ className, type, isInvalid, disabled, ...props }, ref) => { + const [showPassword, setShowPassword] = useState(false); + + const inputStyle = twMerge( + style, + disabled + ? "bg-surface-disable " + : "bg-surface-primary focus:outline-primary-500", + isInvalid ? "outline outline-1 outline-red-500" : "", + ); + + const hideEdgePasswordIcon = ` + input::-ms-reveal { + display: none; + } + `; + + return ( +
+ + + {type === "password" && + !disabled && + (showPassword ? ( + setShowPassword((prev) => !prev)} + className="absolute right-6 top-1/2 -translate-y-1/2 transform cursor-pointer" + /> + ) : ( + setShowPassword((prev) => !prev)} + className="absolute right-6 top-1/2 -translate-y-1/2 transform cursor-pointer" + /> + ))} + {isInvalid && ( + + )} +
+ ); + }, +); + +Input.displayName = "Input"; + +export default Input; diff --git a/src/components/KeywordInput/index.tsx b/src/components/KeywordInput/index.tsx new file mode 100644 index 0000000..6bc4d99 --- /dev/null +++ b/src/components/KeywordInput/index.tsx @@ -0,0 +1,105 @@ +import React, { useState } from "react"; +import { CloseIcon } from "@assets/svgs"; + +interface KeywordInputProps { + placeholder: string; + keywords: string[]; + onAddKeyword?: (keyword: string) => void; + onDeleteKeyword?: (keyword: string) => void; + validateKeyword?: (keyword: string) => boolean; + errorMessage?: string; + duplicateErrorMessage?: string; + type?: "default" | "modal"; + resetInput?: boolean; +} + +const KeywordInput: React.FC = ({ + placeholder, + keywords, + onAddKeyword, + onDeleteKeyword, + validateKeyword, + errorMessage, + duplicateErrorMessage, + type = "default", + resetInput = false, +}) => { + const [inputValue, setInputValue] = useState(""); + const [errorType, setErrorType] = useState<"invalid" | "duplicate" | null>( + null, + ); + + React.useEffect(() => { + if (resetInput) { + setInputValue(""); + } + }, [resetInput]); + + const handleInputChange = (e: React.ChangeEvent) => { + setInputValue(e.target.value.trim()); + setErrorType(null); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.nativeEvent.isComposing || e.key !== "Enter" || !inputValue) return; + + e.preventDefault(); + const keyword = inputValue; + + if (validateKeyword && !validateKeyword(keyword)) { + setErrorType("invalid"); + return; + } + + if (keywords.includes(keyword)) { + setErrorType("duplicate"); + return; + } + + if (onAddKeyword) { + onAddKeyword(keyword); + } + + setInputValue(""); + setErrorType(null); + }; + + return ( +
+ + + {errorType && ( +

+ {errorType === "invalid" ? errorMessage : duplicateErrorMessage} +

+ )} +
+ {keywords.map((keyword, index) => ( +
+ + {keyword} + + {onDeleteKeyword && ( + onDeleteKeyword(keyword)} + /> + )} +
+ ))} +
+
+ ); +}; + +export default KeywordInput; diff --git a/src/components/SearchBar/index.tsx b/src/components/SearchBar/index.tsx new file mode 100644 index 0000000..915b8ba --- /dev/null +++ b/src/components/SearchBar/index.tsx @@ -0,0 +1,66 @@ +import { CloseIcon, SearchIcon } from "@assets/svgs"; +import cn from "@utils/cn"; +import React, { ChangeEvent, KeyboardEvent } from "react"; + +interface SearchBarProps { + placeholder: string; + bgColor: string; + value: string; + onChange: (value: string) => void; + onSearch: () => void; +} + +const SearchBar: React.FC = ({ + placeholder, + bgColor, + value, + onChange, + onSearch, +}) => { + const handleChange = (e: ChangeEvent) => { + onChange(e.target.value); + }; + + const handleClear = () => { + onChange(""); + }; + + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === "Enter") { + onSearch(); + } + }; + + return ( +
+ + {value.trim() && ( + + )} + +
+ ); +}; + +export default SearchBar; diff --git a/src/components/SideMenu/index.tsx b/src/components/SideMenu/index.tsx new file mode 100644 index 0000000..3c9c794 --- /dev/null +++ b/src/components/SideMenu/index.tsx @@ -0,0 +1,78 @@ +import { DefaultImage } from "@assets/images"; +import { AnalyzeIcon, HamburgerIcon, NewsIcon } from "@assets/svgs"; +import { Link, useLocation } from "react-router-dom"; +import routes from "@constants/routes"; +import useGetClientInfo from "@api/hooks/clients/useGetClientInfo"; +import { clientId } from "@constants/clientId"; + +const SideMenu: React.FC = () => { + const location = useLocation(); + + const { data: clientData } = useGetClientInfo(clientId); + + const isActive = (paths: string[]) => + paths.some((path) => location.pathname.startsWith(`/${path}`)) + ? "bg-surface-primary text-neutral-700" + : "text-body3"; + + return ( +
+
+
+
+
+ Client Logo + + {clientData?.name} + +
+
+ + + ๋‰ด์Šค ๋ชจ๋‹ˆํ„ฐ๋ง + +
+
+ + ๋ฐ์ผ๋ฆฌ ๋ชจ๋‹ˆํ„ฐ๋ง + + + ๋ณด๊ณ ์„œ ๊ด€๋ฆฌ + + + ๋ชจ๋‹ˆํ„ฐ๋ง ์„ค์ • + +
+
+ + ๋‰ด์Šค๋ถ„์„ +
+
+ + + + ๊ณ ๊ฐ์‚ฌ ๋ชฉ๋ก์œผ๋กœ + + +
+
+
+ ); +}; + +export default SideMenu; diff --git a/src/components/test/index.tsx b/src/components/test/index.tsx deleted file mode 100644 index 48e7dc4..0000000 --- a/src/components/test/index.tsx +++ /dev/null @@ -1,5 +0,0 @@ -function Test() { - return
ํ•œ๊ธ€
; -} - -export default Test; diff --git a/src/constants/category.ts b/src/constants/category.ts new file mode 100644 index 0000000..0ef3d45 --- /dev/null +++ b/src/constants/category.ts @@ -0,0 +1,11 @@ +export const enToKrCategoryMap: { [key: string]: string } = { + SELF: "์ž์‚ฌ", + COMPETITOR: "๊ฒฝ์Ÿ์‚ฌ", + INDUSTRY: "์—…๊ณ„", +}; + +export const krToEnCategoryMap: { [key: string]: string } = { + ์ž์‚ฌ: "SELF", + ๊ฒฝ์Ÿ์‚ฌ: "COMPETITOR", + ์—…๊ณ„: "INDUSTRY", +}; diff --git a/src/constants/clientId.ts b/src/constants/clientId.ts new file mode 100644 index 0000000..270a494 --- /dev/null +++ b/src/constants/clientId.ts @@ -0,0 +1 @@ +export const clientId = 1; diff --git a/src/constants/routes.ts b/src/constants/routes.ts new file mode 100644 index 0000000..9a398be --- /dev/null +++ b/src/constants/routes.ts @@ -0,0 +1,15 @@ +const routes = { + main: "/", + signIn: "/sign-in", + signUp: "/sign-up", + password: "/password", + dashboard: "/dashboard", + monitoring: "/monitoring", + report: "/report", + reportNew: "/report/new", + reportEdit: "/report/edit", + settingKeyword: "/setting/keyword", + settingEmail: "/setting/email", +} as const; + +export default routes; diff --git a/src/features/report/ArticleTable/CategoryAddModal.tsx b/src/features/report/ArticleTable/CategoryAddModal.tsx new file mode 100644 index 0000000..417166e --- /dev/null +++ b/src/features/report/ArticleTable/CategoryAddModal.tsx @@ -0,0 +1,71 @@ +import React, { useRef, useState } from "react"; +import { useOutsideClick } from "@chakra-ui/react"; +import Button from "@components/Button"; + +interface CategoryAddModalProps { + onClose: () => void; + onAddCategory: (category: string) => void; +} + +const CategoryAddModal: React.FC = ({ + onClose, + onAddCategory, +}) => { + const ref = useRef(null); + const [selectedCategory, setSelectedCategory] = useState(""); + + useOutsideClick({ + ref: ref, + handler: onClose, + }); + + const categories = ["๋ณด๋„ ์ž๋ฃŒ", "๊ธฐํš ๊ธฐ์‚ฌ", "์‚ฌ์šฉ์ž ์ž…๋ ฅ"]; + + const handleCategoryChange = (category: string) => { + setSelectedCategory(category); + }; + + const handleAddCategory = () => { + if (selectedCategory) { + onAddCategory(selectedCategory); + onClose(); + } + }; + + return ( +
+
+ ๊ธฐ์‚ฌ ์นดํ…Œ๊ณ ๋ฆฌ +
+
+ {categories.map((category) => ( + + ))} +
+
+ +
+
+ ); +}; + +export default CategoryAddModal; diff --git a/src/features/report/ArticleTable/CategorySelectModal.tsx b/src/features/report/ArticleTable/CategorySelectModal.tsx new file mode 100644 index 0000000..e834d84 --- /dev/null +++ b/src/features/report/ArticleTable/CategorySelectModal.tsx @@ -0,0 +1,100 @@ +import React, { useState } from "react"; +import Button from "@components/Button"; +import useGetReportCategories from "@api/hooks/reports/useGetReportCategories"; +import { CategoryTypeEn } from "types/category"; +import usePatchReportArticleCategory from "@api/hooks/reports/usePatchReportArticleCategory"; + +interface CategorySelectModalProps { + reportId: number; + reportArticleId: number; + categoryType: CategoryTypeEn; + articleCategoryId: number; + onClose: () => void; +} + +const CategorySelectModal: React.FC = ({ + reportId, + reportArticleId, + categoryType, + articleCategoryId, + onClose, +}) => { + const [selectedCategory, setSelectedCategory] = + useState(articleCategoryId); + + const { data: categories } = useGetReportCategories({ reportId }); + + const filteredCategories = categories?.[categoryType] || []; + + const handleCategoryChange = (categoryId: number) => { + setSelectedCategory(categoryId); + }; + + const { mutate: updateCategory } = usePatchReportArticleCategory(); + + const handleApply = () => { + if (selectedCategory !== null) { + updateCategory({ + reportId, + reportArticleId, + newCategoryId: selectedCategory, + }); + onClose(); + } + }; + + return ( +
+
+
+ ์นดํ…Œ๊ณ ๋ฆฌ ์„ ํƒํ•˜๊ธฐ +
+
+ {filteredCategories.map((category) => ( + + ))} +
+
+ + +
+
+
+ ); +}; + +export default CategorySelectModal; diff --git a/src/features/report/ReportHeader/ExitModal/index.tsx b/src/features/report/ReportHeader/ExitModal/index.tsx new file mode 100644 index 0000000..fff554a --- /dev/null +++ b/src/features/report/ReportHeader/ExitModal/index.tsx @@ -0,0 +1,54 @@ +import { CloseIcon, ErrorIcon } from "@assets/svgs"; + +interface ExitModalProps { + onClose: () => void; + handleSaveExit: () => void; + handleDeleteExit: () => void; +} + +const ExitModal: React.FC = ({ + onClose, + handleSaveExit, + handleDeleteExit, +}) => { + return ( +
+
+ + +

+ ์ž„์‹œ์ €์žฅ ํ•˜์‹œ๊ฒ ์–ด์š”? +

+

+ ์ €์žฅ์„ ํ•˜์ง€ ์•Š์œผ์‹ค ๊ฒฝ์šฐ +
+ ์ž‘์„ฑ ์ค‘์ด๋˜ ๋ชจ๋“  ๊ธฐ๋ก์€ ์ง€์›Œ์ง€๋ฉฐ +
+ ์ดํ›„ ๋ณต๊ตฌ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•ด์š”. +

+
+ + +
+
+
+ ); +}; + +export default ExitModal; diff --git a/src/features/report/ReportHeader/index.tsx b/src/features/report/ReportHeader/index.tsx new file mode 100644 index 0000000..a8d04e5 --- /dev/null +++ b/src/features/report/ReportHeader/index.tsx @@ -0,0 +1,125 @@ +import React, { useState } from "react"; +import Button from "@components/Button"; +import { UndoIcon, ReportIcon, MailIcon } from "@assets/svgs"; +import { useLocation, useNavigate } from "react-router-dom"; +import routes from "@constants/routes"; +import SendEmailModal from "../SendEmailModal"; +import useGetExcel from "@api/hooks/excel/useGetExcel"; +import usePatchUnScrap from "@api/hooks/scrap/usePatchUnScrap"; +import ExitModal from "./ExitModal"; + +interface ReportHeaderProps { + reportId?: number; + onSave?: () => void; + hasArticles?: boolean; +} + +const ReportHeader: React.FC = ({ + reportId, + onSave, + hasArticles, +}) => { + const navigate = useNavigate(); + const location = useLocation(); + + const [isExitModalOpen, setIsExitModalOpen] = useState(false); + const [isSendEmailModalOpen, setIsSendEmailModalOpen] = useState(false); + + const { mutate: exportExcel } = useGetExcel(); + const { mutate: patchUnScrap } = usePatchUnScrap(); + + const handleExitModalClose = () => { + setIsExitModalOpen(false); + }; + + const handleDeleteExit = () => { + patchUnScrap(undefined, { + onSuccess: () => { + navigate(routes.monitoring); + }, + }); + }; + + const handleSaveExit = () => { + navigate(routes.monitoring); + }; + + const handleOpenExitModal = () => { + if (!reportId && hasArticles) { + setIsExitModalOpen(true); + } else { + navigate(routes.monitoring); + } + }; + + const handleSendEmailModalClose = () => { + setIsSendEmailModalOpen(false); + }; + + return ( +
+ +
+ {reportId && ( + <> + + + + )} + {isSendEmailModalOpen && reportId && ( + + )} + {location.pathname === routes.reportNew && ( + + )} +
+ {isExitModalOpen && ( + + )} +
+ ); +}; + +export default ReportHeader; diff --git a/src/features/report/SendEmailModal/index.tsx b/src/features/report/SendEmailModal/index.tsx new file mode 100644 index 0000000..89d1217 --- /dev/null +++ b/src/features/report/SendEmailModal/index.tsx @@ -0,0 +1,116 @@ +import usePostSendEmail from "@api/hooks/emails/usePostSendEmail"; +import { CloseIcon, LoadingIcon } from "@assets/svgs"; +import Button from "@components/Button"; +import Input from "@components/Input"; +import { SubmitHandler, useForm } from "react-hook-form"; + +interface SendEmailModalProps { + reportId: number; + onClose: () => void; +} + +type SendEmailFormData = { + subject: string; + content: string; +}; + +const SendEmailModal: React.FC = ({ + reportId, + onClose, +}) => { + const { register, handleSubmit, watch } = useForm(); + const [subject, content] = watch(["subject", "content"]); + const { mutate, isPending } = usePostSendEmail(); + const onSubmit: SubmitHandler = ({ subject, content }) => { + mutate({ + reportId, + data: { + subject, + + content, + }, + }); + }; + + return ( +
+
+
+ +
+
+ + ๋ฉ”์ผ ์ „์†กํ•˜๊ธฐ + +
+ +  •  ๋ฏธ๋ฆฌ ์„ค์ •๋œ ์ˆ˜์‹ ์ธ์—๊ฒŒ ๋ฉ”์ผ์ด ๋ฐœ์†ก๋ฉ๋‹ˆ๋‹ค. + + +  •  ํŒŒ์ผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ์™€ ์—‘์…€ ํŒŒ์ผ์ด ํฌํ•จ๋˜์–ด + ์ „์†ก๋ฉ๋‹ˆ๋‹ค. + +
+
+
+ + +
+
+ +
+