diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 2a88ed1..5f35e97 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,6 +1,6 @@ on: push: - branches: [main] + branches: [main, develop] jobs: build: @@ -12,17 +12,30 @@ jobs: run: apk add ruby && gem install mustache - name: creates output run: sh ./build.sh + - name: Determine destination repository (non-secret only) + id: set-destination + run: | + if [ "${{ github.ref }}" = "refs/heads/main" ]; then + echo "username=Chasyuss" >> $GITHUB_OUTPUT + echo "repo=STARLIGHT_FE" >> $GITHUB_OUTPUT + echo "branch=main" >> $GITHUB_OUTPUT + elif [ "${{ github.ref }}" = "refs/heads/develop" ]; then + echo "username=parknari02" >> $GITHUB_OUTPUT + echo "repo=STARLIGHT_FE" >> $GITHUB_OUTPUT + echo "branch=main" >> $GITHUB_OUTPUT + fi + - name: Pushes to another repository - id: push_directory uses: cpina/github-action-push-to-another-repository@main env: - API_TOKEN_GITHUB: ${{ secrets.AUTO_ACTIONS }} + API_TOKEN_GITHUB: ${{ github.ref == 'refs/heads/main' && secrets.AUTO_ACTIONS || secrets.DEVELOP_ACTIONS }} with: source-directory: "output" - destination-github-username: Chasyuss - destination-repository-name: STARLIGHT_FE - user-email: ${{ secrets.EMAIL }} + destination-github-username: ${{ steps.set-destination.outputs.username }} + destination-repository-name: ${{ steps.set-destination.outputs.repo }} + user-email: ${{ github.ref == 'refs/heads/main' && secrets.EMAIL || secrets.DEVELOP_EMAIL }} commit-message: ${{ github.event.commits[0].message }} - target-branch: main + target-branch: ${{ steps.set-destination.outputs.branch }} + - name: Test get variable exported by push-to-another-repository run: echo $DESTINATION_CLONED_DIRECTORY diff --git a/README.md b/README.md index 59ce680..cc39634 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,119 @@ -# ๐Ÿš€ STARLIGHT_FE +# โญ๏ธ ์Šคํƒ€ํŠธ์—…์— ๋น›์„ ๋น„์ถฐ์ฃผ๋Š” ์‚ฌ์—…๊ณ„ํš์„œ ํ”Œ๋žซํผ, ์Šคํƒ€๋ผ์ดํŠธ(STARLIGHT) -## โš’๏ธ ๊ธฐ์ˆ  ์Šคํƒ -- Next.js (App router) -- TypeScript -- Tailwind +1 +Frame 2147226362 + +> ์Šคํƒ€๋ผ์ดํŠธ๋Š” AI์˜ ๋ถ„์„๋ ฅ๊ณผ ํ˜„์—… ์ „๋ฌธ๊ฐ€์˜ ๊ฒ€ํ† ๋ฅผ ๊ฒฐํ•ฉํ•ด, ์˜ˆ๋น„ยท์ดˆ๊ธฐ ์ฐฝ์—…์ž๊ฐ€ ์‹ค์ œ ์‹ฌ์‚ฌ ๊ธฐ์ค€์— ๋งž๋Š” ์‚ฌ์—…๊ณ„ํš์„œ๋ฅผ ์™„์„ฑํ•˜๋„๋ก ๋•๋Š” ํ”Œ๋žซํผ์ž…๋‹ˆ๋‹ค. + +# ๐Ÿ‘ฉโ€๐Ÿผย ํŒ€์†Œ๊ฐœ & RnR + +> **์ŠคํฌํŠธ๋ผ์ดํŠธ** + +**์ŠคํฌํŠธ๋ผ์ดํŠธ**๋Š” ๋ฌด๋Œ€ ์œ„ ์ฃผ์ธ๊ณต์„ ๋น„์ถ”๋Š” ๋น›์ž…๋‹ˆ๋‹ค. +์ €ํฌ ํŒ€์€ ๋‹จ์ˆœํžˆ ์ฐฝ์—…์ž์˜ ์—ฌ์ •์„ ๋น„์ถฐ์ฃผ๋Š” ์—ญํ• ์„ ๋„˜์–ด, **ํŒ€์› ๊ฐ์ž๋„ ์„œ๋กœ์˜ ๋ฌด๋Œ€๋ฅผ ๋ฐํ˜€์ฃผ๋Š” ์กฐ๋ช…**์ด ๋˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค. + +**Star-Light** ์„œ๋น„์Šค๊ฐ€ ์ฐฝ์—…์ž์˜ ๋ง‰๋ง‰ํ•œ ๊ธธ์„ ๋ฐํžˆ๋Š” โ€˜๋ณ„๋น›โ€™์ด๋ผ๋ฉด, +์ €ํฌ ํŒ€ **์ŠคํฌํŠธ๋ผ์ดํŠธ**๋Š” ๊ทธ ๋ณ„๋น›๋“ค์„ ๋ชจ์•„ **์„ธ์ƒ ์•ž์— ๋‹น๋‹นํžˆ ์„ค ์ˆ˜ ์žˆ๋Š” ๋ฌด๋Œ€๋ฅผ ํ•จ๊ป˜ ๋งŒ๋“ค์–ด๊ฐ€๋Š” ์กฐ๋ช…**์ž…๋‹ˆ๋‹ค. +์ €ํฌ๊ฐ€ ๋งŒ๋“  ํ”Œ๋žซํผ์€ ์ฐฝ์—…์ž๋“ค์—๊ฒŒ โ€˜์„ฑ์žฅ์˜ ์ŠคํฌํŠธ๋ผ์ดํŠธโ€™๋ฅผ ๋น„์ถฐ์ฃผ๋Š” ๋™์‹œ์—, **์šฐ๋ฆฌ ํŒ€์› ๋ชจ๋‘๊ฐ€ ์„œ๋กœ์˜ ๋น›์ด ๋˜์–ด ํ•จ๊ป˜ ์„ฑ์žฅํ•˜๋Š” ์—ฌ์ •์„ ์ƒ์ง•**ํ•ฉ๋‹ˆ๋‹ค. + +
+ +| **๋ถ„์•ผ** | **์ด๋ฆ„** | **ํฌ์ง€์…˜** | +| ------------- | -------- | --------------------------------------------------------- | +| ๐Ÿ“‹ ๊ธฐํš | ๋ฐฑ์ค€ | ๐Ÿ“ˆย PM, ์„œ๋น„์Šค ๊ธฐํš - ์„œ๋น„์Šค ์ •์ฑ… ํ™•๋ฆฝ, ๋น„์ฆˆ๋‹ˆ์Šค ๋ชจ๋ธ ๊ตฌ์ถ• | +| ๐Ÿ“‹ ๊ธฐํš | ๋ฐฉ์‚ฐ๋“ค | ๐Ÿ“‹ ์„œ๋น„์Šค ๊ธฐํš - ์„œ๋น„์Šค ์ •์ฑ… ํ™•๋ฆฝ, ๋น„์ฆˆ๋‹ˆ์Šค ๋ชจ๋ธ ๊ตฌ์ถ• | +| ๐ŸŽจ ๋””์ž์ธ | ์†ํ™”์˜ | ๐Ÿ” ๋””์ž์ธ ๋ฆฌ๋“œ, ux/ui๋””์ž์ธ, gui ๋””์ž์ธ | +| ๐ŸŽจ ๋””์ž์ธ | ํ˜„๋ช…ํ™” | ๐Ÿ“ข ํ”„๋กœ์ ํŠธ ๋งค๋‹ˆ์ง•, ์„œ๋น„์Šค ๋””์ž์ธ | +| ๐Ÿ“ฒ ํ”„๋ก ํŠธ์—”๋“œ | ๋ฐ•์ฑ„์ˆ˜ | ๐Ÿ”ฆ ํ”„๋ก ํŠธ์—”๋“œ ๋ฆฌ๋“œ, ํ™”๋ฉด UI ๊ตฌํ˜„, ์„œ๋ฒ„ ์—ฐ๋™ | +| ๐Ÿ“ฒ ํ”„๋ก ํŠธ์—”๋“œ | ๋ฐ•๋‚˜๋ฆฌ | ๐Ÿ“ฑ ํ™”๋ฉด UI ๊ตฌํ˜„, ์„œ๋ฒ„ ์—ฐ๋™ | +| ๐Ÿ–ฅ๏ธ ๋ฐฑ์—”๋“œ | ์ •์„ฑํ˜ธ | ๐Ÿ’ป ๋ฐฑ์—”๋“œ ๋ฆฌ๋“œ, DB ๋ฐ API ๊ตฌ์ถ•, ์„œ๋ฒ„ ๋ฐฐํฌ | +| ๐Ÿ–ฅ๏ธ ๋ฐฑ์—”๋“œ | ์ดํ˜ธ๊ทผ | ๐Ÿ–ฅ๏ธ DB ๋ฐ API ๊ตฌ์ถ•, ์„œ๋ฒ„ ๋ฐฐํฌ | + +
+ +## โญ๏ธ ์„œ๋น„์Šค ์†Œ๊ฐœ + +### ๐Ÿ’ซ ํ™ˆํ™”๋ฉด + +Frame 2147226471 + +### ๐Ÿ’ซ ์‚ฌ์—…๊ณ„ํš์„œ ์ž‘์„ฑ ํŽ˜์ด์ง€ + +- ์ง€์›์‚ฌ์—… ์–‘์‹์— ๋งž์ถ˜ ํ•ญ๋ชฉ ๊ตฌ์กฐ ์ž๋™ ์ œ๊ณต +- ๋ฌธ์ œ ์ธ์‹ยท์‹œ์žฅ ๋ถ„์„ยทBMยท์„ฑ์žฅ์ „๋žตยทํŒ€์—ญ๋Ÿ‰ ๋“ฑ ํ•ญ๋ชฉ๋ณ„ **ํ•„์ˆ˜ ์ฒดํฌ๋ฆฌ์ŠคํŠธ ๋‚ด์žฅ** + ๏ผˆํ•ด๋‹น ์ฒดํฌ๋ฆฌ์ŠคํŠธ๋Š” ์‹ค์ œ ์ฐฝ์—… ์ƒํƒœ๊ณ„์— ๊ณ„์‹  ์‹ฌ์‚ฌ์œ„์›/๋ฉ˜ํ†  ๋ถ„๋“ค์˜ ๊ฒ€์ˆ˜๋ฅผ ๊ฑฐ์ณ ๊ตฌ์„ฑ๏ผ‰ +- ์‚ฌ์šฉ์ž๋Š” โ€œ์ตœ์†Œํ•œ ๋ฌด์—‡์„ ์ ์–ด์•ผ ํ•˜๋Š”์ง€โ€๋ฅผ ๋ณด๋ฉด์„œ ์ž‘์„ฑ ๊ฐ€๋Šฅ + +Frame 2147226441 + +### ๐Ÿ’ซ PDF ์—…๋กœ๋“œ + +Frame 2147226473 + +### ๐Ÿ’ซ AI๋ฆฌํฌํŠธ + +- ๊ฐ ํ•ญ๋ชฉ ์ฑ„์  โ†’ 100์  ๋งŒ์  ๋ฐฐ์ . +- **๊ฐ•์ ยท์•ฝ์ ยท๋ˆ„๋ฝ ์š”์†Œยท๋…ผ๋ฆฌ ํ๋ฆ„**์„ ์‹œ๊ฐ์ ์œผ๋กœ ๋ณด์—ฌ์ฃผ๋Š” ๋ฆฌํฌํŠธ ์ œ๊ณต +- "์ง€๊ธˆ ๋‚ด ์‚ฌ์—…๊ณ„ํš์„œ๊ฐ€ ์–ด๋А ์ˆ˜์ค€์ธ์ง€โ€ ๊ฐ๊ด€์ ์œผ๋กœ ํŒ๋‹จ ๊ฐ€๋Šฅ + Frame 2147226461 + +### ๐Ÿ’ซ ์ „๋ฌธ๊ฐ€ ์—ฐ๊ฒฐ + +- AI ๊ธฐ์ค€์„ ํ†ต๊ณผํ•œ ๋ฌธ์„œ๋ฅผ ๋Œ€์ƒ์œผ๋กœ **์ „๋ฌธ๊ฐ€ ๋งค์นญ** +- ํˆฌ์ž/์ง€์›์‚ฌ์—… ์‹ฌ์‚ฌ ๊ฒฝํ—˜์ด ์žˆ๋Š” ์ „๋ฌธ๊ฐ€๊ฐ€ ํ•ญ๋ชฉ๋ณ„ ์‹ฌ์ธต ์ฝ”๋ฉ˜ํŠธ์™€ ๊ฐœ์„  ๋ฐฉํ–ฅ ์ œ์‹œ +- AI ๋ฆฌํฌํŠธ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ํ•ต์‹ฌ ์˜์—ญ์— ์ง‘์ค‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, **์‹œ๊ฐ„ยท๋น„์šฉ ๋Œ€๋น„ ํšจ์œจ์ด ๋†’์Œ** +- ์ „๋ฌธ๊ฐ€๋Š” ์ด๋ฉ”์ผ๋กœ ์‚ฌ์—…๊ณ„ํš์„œ๋ฅผ ์ „์†ก๋ฐ›๊ณ  ์Šคํƒ€๋ผ์ดํŠธ ์‚ฌ์ดํŠธ์— ๋“ค์–ด๊ฐ€ ์‚ฌ์—…๊ณ„ํš์„œ ํ”ผ๋“œ๋ฐฑ์„ ๋‹ค๋Š” ๊ตฌ์กฐ๋กœ ๋ฐ˜์˜ + +Frame 2147226462 + +### ๐Ÿ’ซ ์š”๊ธˆ์ œ + +- ๊ฒฐ์ œํ•˜๊ธฐ + Frame 2147226463 + +### ๐Ÿ’ซ ๋งˆ์ดํŽ˜์ด์ง€ + +- ์ „๋ฌธ๊ฐ€ ํ”ผ๋“œ๋ฐฑ ํ™•์ธ ๊ฐ€๋Šฅ +- ๊ฒฐ์ œ ๋‚ด์—ญ ํ™•์ธ ๊ฐ€๋Šฅ +- ๋ฆฌํฌํŠธ ํ™•์ธ ๊ฐ€๋Šฅ + Frame 2147226472 + +--- ## ๐Ÿ‘ฅ ํŒ€ ์ž‘์—… ๋ฐฉ์‹ > **Issue ๊ธฐ๋ฐ˜ ๊ฐœ๋ฐœ ์›Œํฌํ”Œ๋กœ์šฐ**๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค. ๋ชจ๋“  ์ž‘์—…์€ Issue์—์„œ ์‹œ์ž‘ํ•˜์—ฌ PR๋กœ ์™„๋ฃŒ๋ฉ๋‹ˆ๋‹ค. ### ๐ŸŒณ ๋ธŒ๋žœ์น˜ ์‚ฌ์šฉ๋ฒ• + > JIRA๋ฅผ ํ†ตํ•ด์„œ ์ž๋™์ƒ์„ฑ -### ๐Ÿ’ฌ ์ปค๋ฐ‹ ๋ฉ”์‹œ์ง€ (Issue ๋ฒˆํ˜ธ ํฌํ•จ) -> docs: (SRLT-25) README ์—…๋ฐ์ดํŠธ -> feat: (SRLT-12) ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•œ๋‹ค -> fix: (SRLT-23) ํšŒ์›๊ฐ€์ž… ์˜ค๋ฅ˜๋ฅผ ์ˆ˜์ •ํ•œ๋‹ค +### ๐Ÿ› ๏ธ ๊ธฐ์ˆ  ์Šคํƒ +- #### Language, Framework, Library -### ๐Ÿ’ฌ PR ๋ฐ ISSUE ์ œ๋ชฉ -> [SRLT-25] ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•œ๋‹ค -> [SRLT-23] ํšŒ์›๊ฐ€์ž… ์˜ค๋ฅ˜๋ฅผ ์ˆ˜์ •ํ•œ๋‹ค -> [SRLT-12] README ์—…๋ฐ์ดํŠธ + ![Next.js](https://img.shields.io/badge/Next.js-000000?style=flat-square&logo=Next.js&logoColor=white) + ![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?style=flat-square&logo=TypeScript&logoColor=white) + ![pnpm](https://img.shields.io/badge/pnpm-F69220?style=flat-square&logo=pnpm&logoColor=white) + ![TanStack Query](https://img.shields.io/badge/TanStack_Query-FF4154?style=flat-square&logo=ReactQuery&logoColor=white) + - **Next.js (App Router)** : ํŽ˜์ด์ง€ยท๋ ˆ์ด์•„์›ƒยท๋ชจ๋‹ฌ์˜ ๊ตฌ์กฐ์  ๋ถ„๋ฆฌ๋ฅผ ํ†ตํ•ด ์œ ์ง€๋ณด์ˆ˜์„ฑ๊ณผ ์žฌ์‚ฌ์šฉ์„ฑ์„ ๊ฐ•ํ™” + - **TypeScript** : Props์™€ API ํƒ€์ž… ๋ช…์„ธ๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ์˜ ์•ˆ์ •์„ฑ๊ณผ ์˜ˆ์ธก ๊ฐ€๋Šฅ์„ฑ ํ™•๋ณด + - **pnpm** : ๋ณ‘๋ ฌ ๋นŒ๋“œ๋ฅผ ํ†ตํ•ด ํŒจํ‚ค์ง€ ๊ฐ„ ์˜์กด์„ฑ ๊ด€๋ฆฌ์™€ ๋นŒ๋“œ ์†๋„ ํ–ฅ์ƒ + - **TanStack Query (React Query)** : ์„œ๋ฒ„ ์ƒํƒœ ๊ด€๋ฆฌ ์ž๋™ํ™”๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ ์š”์ฒญ ์ตœ์ ํ™” ๋ฐ CSR/SSR ํ†ตํ•ฉ ์ฒ˜๋ฆฌ + - **html2canvas** : ๋ Œ๋”๋ง๋œ DOM์„ ๊ทธ๋Œ€๋กœ ์ด๋ฏธ์ง€๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ + - **Tiptap** ์‚ฌ์—…๊ณ„ํš์„œ์˜ ํ…์ŠคํŠธ์—๋””ํ„ฐ๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋ฉฐ ๋ฌธ์„œ๋ฅผ ๊ตฌ์กฐํ™”๋œ JSON ํ˜•ํƒœ๋กœ ๊ด€๋ฆฌ -## ๐Ÿ”„ ๊ฐœ๋ฐœ ํ๋ฆ„ (Issue โ†’ Branch โ†’ PR) +- #### CI/CD + ![Github Actions](https://img.shields.io/badge/Github_Actions-2088FF.svg?style=flat-square&logo=GithubActions&logoColor=white) + - **GitHub Actions** : PR๋งˆ๋‹ค ๊ณตํ†ต ์ปดํฌ๋„ŒํŠธ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์‹œ๊ฐํ™” ๋ฐฐํฌํ•˜์—ฌ ํ˜‘์—… ๊ฐ„ ํ”ผ๋“œ๋ฐฑ ์‚ฌ์ดํด ๋‹จ์ถ• +- #### ํ˜‘์—… ํˆด + ![Discord](https://img.shields.io/badge/Discord-5865F2.svg?style=flat-square&logo=discord&logoColor=white) + ![Notion](https://img.shields.io/badge/Notion-000000.svg?style=flat-square&logo=notion&logoColor=white) +
-> 1. **Issue ์ƒ์„ฑ/ํ™•์ธ** โ†’ GitHub Issues์—์„œ ์ž‘์—…ํ•  ์ด์Šˆ ์ƒ์„ฑ ๋˜๋Š” ํ• ๋‹น๋ฐ›๊ธฐ -> 2. **๋ธŒ๋žœ์น˜ ์ƒ์„ฑ** โ†’ `feat/์ด์Šˆ๋ฒˆํ˜ธ-๊ธฐ๋Šฅ๋ช…` ์œผ๋กœ Issue ๊ธฐ๋ฐ˜ ๋ธŒ๋žœ์น˜ ๋งŒ๋“ค๊ธฐ -> 3. **์ฝ”๋”ฉ** โ†’ Issue ์š”๊ตฌ์‚ฌํ•ญ์— ๋งž๋Š” ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ -> 4. **ํ…Œ์ŠคํŠธ** โ†’ ๋กœ์ปฌ์—์„œ ์ž˜ ๋Œ์•„๊ฐ€๋Š”์ง€ ํ™•์ธ -> 5. **PR ์ƒ์„ฑ** โ†’ Pull Request ์˜ฌ๋ฆฌ๊ธฐ (Issue ๋ฒˆํ˜ธ ์—ฐ๊ฒฐ) -> 6. **์ฝ”๋“œ ๋ฆฌ๋ทฐ** โ†’ ํŒ€์›๋“ค์ด ์ฝ”๋“œ ํ™•์ธ -> 7. **๋จธ์ง€** โ†’ dev ๋ธŒ๋žœ์น˜์— ํ•ฉ์น˜๊ธฐ ํ›„ Issue ์ž๋™ ๋‹ซํž˜ (์•ฑ์Šคํ† ์–ด ๋ฐฐํฌ ์ „๊นŒ์ง€ main์— ๋ฐ”๋กœ ๋ฐ˜์˜) +## ๐Ÿ‘ฌ FrontEnd Developer +| ๋ฐ•์ฑ„์ˆ˜ | ๋ฐ•๋‚˜๋ฆฌ | +| :-----------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------: | +| ๋ฐ•์ฑ„์ˆ˜ | ๋ฐ•๋‚˜๋ฆฌ | +| [@ChaeSu](https://github.com/Chasyuss) | [@parknari02](https://github.com/parknari02) | diff --git a/next.config.ts b/next.config.ts index 44a7789..5a59ab4 100644 --- a/next.config.ts +++ b/next.config.ts @@ -15,6 +15,7 @@ const nextConfig = { pathname: '/**', }, ], + qualities: [60, 75, 90, 100], }, turbopack: { diff --git a/package.json b/package.json index 3ed4adb..624d4c5 100644 --- a/package.json +++ b/package.json @@ -27,9 +27,10 @@ "axios": "^1.13.1", "html2canvas": "^1.4.1", "jspdf": "^3.0.3", - "next": "15.5.4", - "react": "19.1.0", - "react-dom": "19.1.0", + "lottie-react": "^2.4.1", + "next": "16.1.1", + "react": "19.2.3", + "react-dom": "19.2.3", "recharts": "^3.3.0", "zustand": "^5.0.8" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 60a4e57..ad4e77d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ importers: dependencies: '@tanstack/react-query': specifier: ^5.90.5 - version: 5.90.10(react@19.1.0) + version: 5.90.12(react@19.2.3) '@tiptap/core': specifier: ^2.26.3 version: 2.27.1(@tiptap/pm@2.27.1) @@ -46,7 +46,7 @@ importers: version: 2.27.1 '@tiptap/react': specifier: ^2.6.6 - version: 2.27.1(@tiptap/core@2.27.1(@tiptap/pm@2.27.1))(@tiptap/pm@2.27.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 2.27.1(@tiptap/core@2.27.1(@tiptap/pm@2.27.1))(@tiptap/pm@2.27.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@tiptap/starter-kit': specifier: ^2.6.6 version: 2.27.1 @@ -61,59 +61,62 @@ importers: version: 1.4.1 jspdf: specifier: ^3.0.3 - version: 3.0.3 + version: 3.0.4 + lottie-react: + specifier: ^2.4.1 + version: 2.4.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) next: - specifier: 15.5.4 - version: 15.5.4(@babel/core@7.28.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + specifier: 16.1.1 + version: 16.1.1(@babel/core@7.28.5)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react: - specifier: 19.1.0 - version: 19.1.0 + specifier: 19.2.3 + version: 19.2.3 react-dom: - specifier: 19.1.0 - version: 19.1.0(react@19.1.0) + specifier: 19.2.3 + version: 19.2.3(react@19.2.3) recharts: specifier: ^3.3.0 - version: 3.4.1(@types/react@19.2.5)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)(redux@5.0.1) + version: 3.6.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react-is@16.13.1)(react@19.2.3)(redux@5.0.1) zustand: specifier: ^5.0.8 - version: 5.0.8(@types/react@19.2.5)(immer@10.2.0)(react@19.1.0)(use-sync-external-store@1.6.0(react@19.1.0)) + version: 5.0.9(@types/react@19.2.7)(immer@11.0.1)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)) devDependencies: '@eslint/eslintrc': specifier: ^3 - version: 3.3.1 + version: 3.3.3 '@svgr/webpack': specifier: ^8.1.0 version: 8.1.0(typescript@5.9.3) '@tailwindcss/postcss': specifier: ^4 - version: 4.1.17 + version: 4.1.18 '@types/html2canvas': specifier: ^1.0.0 version: 1.0.0 '@types/node': specifier: ^20 - version: 20.19.25 + version: 20.19.27 '@types/react': specifier: ^19 - version: 19.2.5 + version: 19.2.7 '@types/react-dom': specifier: ^19 - version: 19.2.3(@types/react@19.2.5) + version: 19.2.3(@types/react@19.2.7) eslint: specifier: ^9 - version: 9.39.1(jiti@2.6.1) + version: 9.39.2(jiti@2.6.1) eslint-config-next: specifier: 15.5.4 - version: 15.5.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + version: 15.5.4(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) prettier: specifier: ^3.6.2 - version: 3.6.2 + version: 3.7.4 prettier-plugin-tailwindcss: specifier: ^0.7.1 - version: 0.7.1(prettier@3.6.2) + version: 0.7.2(prettier@3.7.4) tailwindcss: specifier: ^4 - version: 4.1.17 + version: 4.1.18 typescript: specifier: ^5 version: 5.9.3 @@ -710,12 +713,12 @@ packages: resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@3.3.1': - resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + '@eslint/eslintrc@3.3.3': + resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.39.1': - resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==} + '@eslint/js@9.39.2': + resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.7': @@ -898,56 +901,56 @@ packages: '@napi-rs/wasm-runtime@0.2.12': resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} - '@next/env@15.5.4': - resolution: {integrity: sha512-27SQhYp5QryzIT5uO8hq99C69eLQ7qkzkDPsk3N+GuS2XgOgoYEeOav7Pf8Tn4drECOVDsDg8oj+/DVy8qQL2A==} + '@next/env@16.1.1': + resolution: {integrity: sha512-3oxyM97Sr2PqiVyMyrZUtrtM3jqqFxOQJVuKclDsgj/L728iZt/GyslkN4NwarledZATCenbk4Offjk1hQmaAA==} '@next/eslint-plugin-next@15.5.4': resolution: {integrity: sha512-SR1vhXNNg16T4zffhJ4TS7Xn7eq4NfKfcOsRwea7RIAHrjRpI9ALYbamqIJqkAhowLlERffiwk0FMvTLNdnVtw==} - '@next/swc-darwin-arm64@15.5.4': - resolution: {integrity: sha512-nopqz+Ov6uvorej8ndRX6HlxCYWCO3AHLfKK2TYvxoSB2scETOcfm/HSS3piPqc3A+MUgyHoqE6je4wnkjfrOA==} + '@next/swc-darwin-arm64@16.1.1': + resolution: {integrity: sha512-JS3m42ifsVSJjSTzh27nW+Igfha3NdBOFScr9C80hHGrWx55pTrVL23RJbqir7k7/15SKlrLHhh/MQzqBBYrQA==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@15.5.4': - resolution: {integrity: sha512-QOTCFq8b09ghfjRJKfb68kU9k2K+2wsC4A67psOiMn849K9ZXgCSRQr0oVHfmKnoqCbEmQWG1f2h1T2vtJJ9mA==} + '@next/swc-darwin-x64@16.1.1': + resolution: {integrity: sha512-hbyKtrDGUkgkyQi1m1IyD3q4I/3m9ngr+V93z4oKHrPcmxwNL5iMWORvLSGAf2YujL+6HxgVvZuCYZfLfb4bGw==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@15.5.4': - resolution: {integrity: sha512-eRD5zkts6jS3VfE/J0Kt1VxdFqTnMc3QgO5lFE5GKN3KDI/uUpSyK3CjQHmfEkYR4wCOl0R0XrsjpxfWEA++XA==} + '@next/swc-linux-arm64-gnu@16.1.1': + resolution: {integrity: sha512-/fvHet+EYckFvRLQ0jPHJCUI5/B56+2DpI1xDSvi80r/3Ez+Eaa2Yq4tJcRTaB1kqj/HrYKn8Yplm9bNoMJpwQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@15.5.4': - resolution: {integrity: sha512-TOK7iTxmXFc45UrtKqWdZ1shfxuL4tnVAOuuJK4S88rX3oyVV4ZkLjtMT85wQkfBrOOvU55aLty+MV8xmcJR8A==} + '@next/swc-linux-arm64-musl@16.1.1': + resolution: {integrity: sha512-MFHrgL4TXNQbBPzkKKur4Fb5ICEJa87HM7fczFs2+HWblM7mMLdco3dvyTI+QmLBU9xgns/EeeINSZD6Ar+oLg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@15.5.4': - resolution: {integrity: sha512-7HKolaj+481FSW/5lL0BcTkA4Ueam9SPYWyN/ib/WGAFZf0DGAN8frNpNZYFHtM4ZstrHZS3LY3vrwlIQfsiMA==} + '@next/swc-linux-x64-gnu@16.1.1': + resolution: {integrity: sha512-20bYDfgOQAPUkkKBnyP9PTuHiJGM7HzNBbuqmD0jiFVZ0aOldz+VnJhbxzjcSabYsnNjMPsE0cyzEudpYxsrUQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@15.5.4': - resolution: {integrity: sha512-nlQQ6nfgN0nCO/KuyEUwwOdwQIGjOs4WNMjEUtpIQJPR2NUfmGpW2wkJln1d4nJ7oUzd1g4GivH5GoEPBgfsdw==} + '@next/swc-linux-x64-musl@16.1.1': + resolution: {integrity: sha512-9pRbK3M4asAHQRkwaXwu601oPZHghuSC8IXNENgbBSyImHv/zY4K5udBusgdHkvJ/Tcr96jJwQYOll0qU8+fPA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@15.5.4': - resolution: {integrity: sha512-PcR2bN7FlM32XM6eumklmyWLLbu2vs+D7nJX8OAIoWy69Kef8mfiN4e8TUv2KohprwifdpFKPzIP1njuCjD0YA==} + '@next/swc-win32-arm64-msvc@16.1.1': + resolution: {integrity: sha512-bdfQkggaLgnmYrFkSQfsHfOhk/mCYmjnrbRCGgkMcoOBZ4n+TRRSLmT/CU5SATzlBJ9TpioUyBW/vWFXTqQRiA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@15.5.4': - resolution: {integrity: sha512-1ur2tSHZj8Px/KMAthmuI9FMp/YFusMMGoRNJaRZMOlSkgvLjzosSdQI0cJAKogdHl3qXUQKL9MGaYvKwA7DXg==} + '@next/swc-win32-x64-msvc@16.1.1': + resolution: {integrity: sha512-Ncwbw2WJ57Al5OX0k4chM68DKhEPlrXBaSXDCi2kPi5f4d8b3ejr3RRJGfKBLrn2YJL5ezNS7w2TZLHSti8CMw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -971,8 +974,8 @@ packages: '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} - '@reduxjs/toolkit@2.10.1': - resolution: {integrity: sha512-/U17EXQ9Do9Yx4DlNGU6eVNfZvFJfYpUtRRdLf19PbPjdWBxNlxGZXywQZ1p1Nz8nMkWplTI7iD/23m07nolDA==} + '@reduxjs/toolkit@2.11.2': + resolution: {integrity: sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==} peerDependencies: react: ^16.9.0 || ^17.0.0 || ^18 || ^19 react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 @@ -991,8 +994,8 @@ packages: '@rushstack/eslint-patch@1.15.0': resolution: {integrity: sha512-ojSshQPKwVvSMR8yT2L/QtUkV5SXi/IfDiJ4/8d6UbTPjiHVmxZzUAzGD8Tzks1b9+qQkZa0isUOvYObedITaw==} - '@standard-schema/spec@1.0.0': - resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} '@standard-schema/utils@0.3.0': resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} @@ -1078,65 +1081,65 @@ packages: '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} - '@tailwindcss/node@4.1.17': - resolution: {integrity: sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==} + '@tailwindcss/node@4.1.18': + resolution: {integrity: sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==} - '@tailwindcss/oxide-android-arm64@4.1.17': - resolution: {integrity: sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==} + '@tailwindcss/oxide-android-arm64@4.1.18': + resolution: {integrity: sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==} engines: {node: '>= 10'} cpu: [arm64] os: [android] - '@tailwindcss/oxide-darwin-arm64@4.1.17': - resolution: {integrity: sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==} + '@tailwindcss/oxide-darwin-arm64@4.1.18': + resolution: {integrity: sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.1.17': - resolution: {integrity: sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==} + '@tailwindcss/oxide-darwin-x64@4.1.18': + resolution: {integrity: sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.1.17': - resolution: {integrity: sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==} + '@tailwindcss/oxide-freebsd-x64@4.1.18': + resolution: {integrity: sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==} engines: {node: '>= 10'} cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17': - resolution: {integrity: sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==} + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': + resolution: {integrity: sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.1.17': - resolution: {integrity: sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==} + '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': + resolution: {integrity: sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-arm64-musl@4.1.17': - resolution: {integrity: sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==} + '@tailwindcss/oxide-linux-arm64-musl@4.1.18': + resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-x64-gnu@4.1.17': - resolution: {integrity: sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==} + '@tailwindcss/oxide-linux-x64-gnu@4.1.18': + resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-linux-x64-musl@4.1.17': - resolution: {integrity: sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==} + '@tailwindcss/oxide-linux-x64-musl@4.1.18': + resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-wasm32-wasi@4.1.17': - resolution: {integrity: sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==} + '@tailwindcss/oxide-wasm32-wasi@4.1.18': + resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==} engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: @@ -1147,30 +1150,30 @@ packages: - '@emnapi/wasi-threads' - tslib - '@tailwindcss/oxide-win32-arm64-msvc@4.1.17': - resolution: {integrity: sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==} + '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': + resolution: {integrity: sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.1.17': - resolution: {integrity: sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==} + '@tailwindcss/oxide-win32-x64-msvc@4.1.18': + resolution: {integrity: sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@tailwindcss/oxide@4.1.17': - resolution: {integrity: sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==} + '@tailwindcss/oxide@4.1.18': + resolution: {integrity: sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==} engines: {node: '>= 10'} - '@tailwindcss/postcss@4.1.17': - resolution: {integrity: sha512-+nKl9N9mN5uJ+M7dBOOCzINw94MPstNR/GtIhz1fpZysxL/4a+No64jCBD6CPN+bIHWFx3KWuu8XJRrj/572Dw==} + '@tailwindcss/postcss@4.1.18': + resolution: {integrity: sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g==} - '@tanstack/query-core@5.90.10': - resolution: {integrity: sha512-EhZVFu9rl7GfRNuJLJ3Y7wtbTnENsvzp+YpcAV7kCYiXni1v8qZh++lpw4ch4rrwC0u/EZRnBHIehzCGzwXDSQ==} + '@tanstack/query-core@5.90.12': + resolution: {integrity: sha512-T1/8t5DhV/SisWjDnaiU2drl6ySvsHj1bHBCWNXd+/T+Hh1cf6JodyEYMd5sgwm+b/mETT4EV3H+zCVczCU5hg==} - '@tanstack/react-query@5.90.10': - resolution: {integrity: sha512-BKLss9Y8PQ9IUjPYQiv3/Zmlx92uxffUOX8ZZNoQlCIZBJPT5M+GOMQj7xislvVQ6l1BstBjcX0XB/aHfFYVNw==} + '@tanstack/react-query@5.90.12': + resolution: {integrity: sha512-graRZspg7EoEaw0a8faiUASCyJrqjKPdqJ9EwuDRUF9mEYJ1YPczI9H+/agJ0mOJkPCJDk0lsz5QTrLZ/jQ2rg==} peerDependencies: react: ^18 || ^19 @@ -1410,8 +1413,8 @@ packages: '@types/mdurl@2.0.0': resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} - '@types/node@20.19.25': - resolution: {integrity: sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==} + '@types/node@20.19.27': + resolution: {integrity: sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==} '@types/pako@2.0.4': resolution: {integrity: sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==} @@ -1424,8 +1427,8 @@ packages: peerDependencies: '@types/react': ^19.2.0 - '@types/react@19.2.5': - resolution: {integrity: sha512-keKxkZMqnDicuvFoJbzrhbtdLSPhj/rZThDlKWCDbgXmUg0rEUFtRssDXKYmtXluZlIqiC5VqkCgRwzuyLHKHw==} + '@types/react@19.2.7': + resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} @@ -1433,63 +1436,63 @@ packages: '@types/use-sync-external-store@0.0.6': resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} - '@typescript-eslint/eslint-plugin@8.46.4': - resolution: {integrity: sha512-R48VhmTJqplNyDxCyqqVkFSZIx1qX6PzwqgcXn1olLrzxcSBDlOsbtcnQuQhNtnNiJ4Xe5gREI1foajYaYU2Vg==} + '@typescript-eslint/eslint-plugin@8.50.0': + resolution: {integrity: sha512-O7QnmOXYKVtPrfYzMolrCTfkezCJS9+ljLdKW/+DCvRsc3UAz+sbH6Xcsv7p30+0OwUbeWfUDAQE0vpabZ3QLg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.46.4 + '@typescript-eslint/parser': ^8.50.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.46.4': - resolution: {integrity: sha512-tK3GPFWbirvNgsNKto+UmB/cRtn6TZfyw0D6IKrW55n6Vbs7KJoZtI//kpTKzE/DUmmnAFD8/Ca46s7Obs92/w==} + '@typescript-eslint/parser@8.50.0': + resolution: {integrity: sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.46.4': - resolution: {integrity: sha512-nPiRSKuvtTN+no/2N1kt2tUh/HoFzeEgOm9fQ6XQk4/ApGqjx0zFIIaLJ6wooR1HIoozvj2j6vTi/1fgAz7UYQ==} + '@typescript-eslint/project-service@8.50.0': + resolution: {integrity: sha512-Cg/nQcL1BcoTijEWyx4mkVC56r8dj44bFDvBdygifuS20f3OZCHmFbjF34DPSi07kwlFvqfv/xOLnJ5DquxSGQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.46.4': - resolution: {integrity: sha512-tMDbLGXb1wC+McN1M6QeDx7P7c0UWO5z9CXqp7J8E+xGcJuUuevWKxuG8j41FoweS3+L41SkyKKkia16jpX7CA==} + '@typescript-eslint/scope-manager@8.50.0': + resolution: {integrity: sha512-xCwfuCZjhIqy7+HKxBLrDVT5q/iq7XBVBXLn57RTIIpelLtEIZHXAF/Upa3+gaCpeV1NNS5Z9A+ID6jn50VD4A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.46.4': - resolution: {integrity: sha512-+/XqaZPIAk6Cjg7NWgSGe27X4zMGqrFqZ8atJsX3CWxH/jACqWnrWI68h7nHQld0y+k9eTTjb9r+KU4twLoo9A==} + '@typescript-eslint/tsconfig-utils@8.50.0': + resolution: {integrity: sha512-vxd3G/ybKTSlm31MOA96gqvrRGv9RJ7LGtZCn2Vrc5htA0zCDvcMqUkifcjrWNNKXHUU3WCkYOzzVSFBd0wa2w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.46.4': - resolution: {integrity: sha512-V4QC8h3fdT5Wro6vANk6eojqfbv5bpwHuMsBcJUJkqs2z5XnYhJzyz9Y02eUmF9u3PgXEUiOt4w4KHR3P+z0PQ==} + '@typescript-eslint/type-utils@8.50.0': + resolution: {integrity: sha512-7OciHT2lKCewR0mFoBrvZJ4AXTMe/sYOe87289WAViOocEmDjjv8MvIOT2XESuKj9jp8u3SZYUSh89QA4S1kQw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.46.4': - resolution: {integrity: sha512-USjyxm3gQEePdUwJBFjjGNG18xY9A2grDVGuk7/9AkjIF1L+ZrVnwR5VAU5JXtUnBL/Nwt3H31KlRDaksnM7/w==} + '@typescript-eslint/types@8.50.0': + resolution: {integrity: sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.46.4': - resolution: {integrity: sha512-7oV2qEOr1d4NWNmpXLR35LvCfOkTNymY9oyW+lUHkmCno7aOmIf/hMaydnJBUTBMRCOGZh8YjkFOc8dadEoNGA==} + '@typescript-eslint/typescript-estree@8.50.0': + resolution: {integrity: sha512-W7SVAGBR/IX7zm1t70Yujpbk+zdPq/u4soeFSknWFdXIFuWsBGBOUu/Tn/I6KHSKvSh91OiMuaSnYp3mtPt5IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.46.4': - resolution: {integrity: sha512-AbSv11fklGXV6T28dp2Me04Uw90R2iJ30g2bgLz529Koehrmkbs1r7paFqr1vPCZi7hHwYxYtxfyQMRC8QaVSg==} + '@typescript-eslint/utils@8.50.0': + resolution: {integrity: sha512-87KgUXET09CRjGCi2Ejxy3PULXna63/bMYv72tCAlDJC3Yqwln0HiFJ3VJMst2+mEtNtZu5oFvX4qJGjKsnAgg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.46.4': - resolution: {integrity: sha512-/++5CYLQqsO9HFGLI7APrxBJYo+5OCMpViuhV8q5/Qa3o5mMrF//eQHks+PXcsAVaLdn817fMuS7zqoXNNZGaw==} + '@typescript-eslint/visitor-keys@8.50.0': + resolution: {integrity: sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@unrs/resolver-binding-android-arm-eabi@1.11.1': @@ -1690,8 +1693,8 @@ packages: resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==} engines: {node: '>= 0.6.0'} - baseline-browser-mapping@2.8.29: - resolution: {integrity: sha512-sXdt2elaVnhpDNRDz+1BDx1JQoJRuNk7oVlAlbGiFkLikHCAQiccexF/9e91zVi6RCgqspl04aP+6Cnl9zRLrA==} + baseline-browser-mapping@2.9.11: + resolution: {integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==} hasBin: true boolbase@1.0.0: @@ -1707,8 +1710,8 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.28.0: - resolution: {integrity: sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==} + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -1732,8 +1735,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001755: - resolution: {integrity: sha512-44V+Jm6ctPj7R52Na4TLi3Zri4dWUljJd+RDm+j8LtNCc/ihLCT+X1TzoOAkRETEWqjuLnh9581Tl80FvK7jVA==} + caniuse-lite@1.0.30001761: + resolution: {integrity: sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==} canvg@3.0.11: resolution: {integrity: sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==} @@ -1771,11 +1774,11 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - core-js-compat@3.46.0: - resolution: {integrity: sha512-p9hObIIEENxSV8xIu+V68JjSeARg6UVMG5mR+JEUguG3sI6MsiS1njz2jHmyJDvA+8jX/sytkBHup6kxhM9law==} + core-js-compat@3.47.0: + resolution: {integrity: sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ==} - core-js@3.46.0: - resolution: {integrity: sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA==} + core-js@3.47.0: + resolution: {integrity: sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==} cosmiconfig@8.3.6: resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} @@ -1934,8 +1937,8 @@ packages: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} - dompurify@3.3.0: - resolution: {integrity: sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==} + dompurify@3.3.1: + resolution: {integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==} domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} @@ -1947,14 +1950,14 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - electron-to-chromium@1.5.254: - resolution: {integrity: sha512-DcUsWpVhv9svsKRxnSCZ86SjD+sp32SGidNB37KpqXJncp1mfUgKbHvBomE89WJDbfVKw1mdv5+ikrvd43r+Bg==} + electron-to-chromium@1.5.267: + resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - enhanced-resolve@5.18.3: - resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} + enhanced-resolve@5.18.4: + resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==} engines: {node: '>=10.13.0'} entities@4.5.0: @@ -1964,8 +1967,8 @@ packages: error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} - es-abstract@1.24.0: - resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} + es-abstract@1.24.1: + resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} engines: {node: '>= 0.4'} es-define-property@1.0.1: @@ -1976,8 +1979,8 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-iterator-helpers@1.2.1: - resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} + es-iterator-helpers@1.2.2: + resolution: {integrity: sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==} engines: {node: '>= 0.4'} es-object-atoms@1.1.1: @@ -1996,8 +1999,8 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - es-toolkit@1.42.0: - resolution: {integrity: sha512-SLHIyY7VfDJBM8clz4+T2oquwTQxEzu263AyhVK4jREOAwJ+8eebaa4wM3nlvnAqhDrMm2EsA6hWHaQsMPQ1nA==} + es-toolkit@1.43.0: + resolution: {integrity: sha512-SKCT8AsWvYzBBuUqMk4NPwFlSdqLpJwmy6AP322ERn8W2YLIB6JBXnwMI2Qsh2gfphT3q7EKAxKb23cvFHFwKA==} escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} @@ -2093,8 +2096,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.39.1: - resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==} + eslint@9.39.2: + resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -2133,10 +2136,6 @@ packages: resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} engines: {node: '>=8.6.0'} - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -2253,9 +2252,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} @@ -2298,6 +2294,9 @@ packages: immer@10.2.0: resolution: {integrity: sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==} + immer@11.0.1: + resolution: {integrity: sha512-naDCyggtcBWANtIrjQEajhhBEuL9b0Zg4zmlWK2CzS6xCWSE39/vvf4LqnMjUAWHBhot4m9MHCM/Z+mfWhUkiA==} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -2470,8 +2469,8 @@ packages: engines: {node: '>=6'} hasBin: true - jspdf@3.0.3: - resolution: {integrity: sha512-eURjAyz5iX1H8BOYAfzvdPfIKK53V7mCpBTe7Kb16PaM8JSXEcUQNBQaiWMI8wY5RvNOPj4GccMjTlfwRBd+oQ==} + jspdf@3.0.4: + resolution: {integrity: sha512-dc6oQ8y37rRcHn316s4ngz/nOjayLF/FFxBF4V9zamQKRqXxyiH1zagkCdktdWhtoQId5K20xt1lB90XzkB+hQ==} jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} @@ -2581,6 +2580,15 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + lottie-react@2.4.1: + resolution: {integrity: sha512-LQrH7jlkigIIv++wIyrOYFLHSKQpEY4zehPicL9bQsrt1rnoKRYCYgpCUe5maqylNtacy58/sQDZTkwMcTRxZw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + lottie-web@5.13.0: + resolution: {integrity: sha512-+gfBXl6sxXMPe8tKQm7qzLnUy5DUPJPKIyRHwtpCpyUEYjHYRJC/5gjUvdkuO2c3JllrPtHXH5UJJK8LRYl5yQ==} + lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} @@ -2649,9 +2657,9 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - next@15.5.4: - resolution: {integrity: sha512-xH4Yjhb82sFYQfY3vbkJfgSDgXvBB6a8xPs9i35k6oZJRoQRihZH+4s9Yo2qsWpzBmZ3lPXaJ2KPXLfkvW4LnA==} - engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + next@16.1.1: + resolution: {integrity: sha512-QI+T7xrxt1pF6SQ/JYFz95ro/mg/1Znk5vBebsWwbpejj1T0A23hO7GYEaVac9QUOT2BIMiuzm0L99ooq7k0/w==} + engines: {node: '>=20.9.0'} hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 @@ -2786,8 +2794,8 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier-plugin-tailwindcss@0.7.1: - resolution: {integrity: sha512-Bzv1LZcuiR1Sk02iJTS1QzlFNp/o5l2p3xkopwOrbPmtMeh3fK9rVW5M3neBQzHq+kGKj/4LGQMTNcTH4NGPtQ==} + prettier-plugin-tailwindcss@0.7.2: + resolution: {integrity: sha512-LkphyK3Fw+q2HdMOoiEHWf93fNtYJwfamoKPl7UwtjFQdei/iIBoX11G6j706FzN3ymX9mPVi97qIY8328vdnA==} engines: {node: '>=20.19'} peerDependencies: '@ianvs/prettier-plugin-sort-imports': '*' @@ -2841,8 +2849,8 @@ packages: prettier-plugin-svelte: optional: true - prettier@3.6.2: - resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + prettier@3.7.4: + resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} engines: {node: '>=14'} hasBin: true @@ -2891,8 +2899,8 @@ packages: prosemirror-state@1.4.4: resolution: {integrity: sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==} - prosemirror-tables@1.8.1: - resolution: {integrity: sha512-DAgDoUYHCcc6tOGpLVPSU1k84kCUWTWnfWX3UDy2Delv4ryH0KqTD6RBI6k4yi9j9I8gl3j8MkPpRD/vWPZbug==} + prosemirror-tables@1.8.3: + resolution: {integrity: sha512-wbqCR/RlRPRe41a4LFtmhKElzBEfBTdtAYWNIGHM6X2e24NN/MTNUKyXjjphfAfdQce37Kh/5yf765mLPYDe7Q==} prosemirror-trailing-node@3.0.0: resolution: {integrity: sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==} @@ -2904,8 +2912,8 @@ packages: prosemirror-transform@1.10.5: resolution: {integrity: sha512-RPDQCxIDhIBb1o36xxwsaeAvivO8VLJcgBtzmOwQ64bMtsVFh5SSuJ6dWSxO1UsHTiTXPCgQm3PDJt7p6IOLbw==} - prosemirror-view@1.41.3: - resolution: {integrity: sha512-SqMiYMUQNNBP9kfPhLO8WXEk/fon47vc52FQsUiJzTBuyjKgEcoAwMyF04eQ4WZ2ArMn7+ReypYL60aKngbACQ==} + prosemirror-view@1.41.4: + resolution: {integrity: sha512-WkKgnyjNncri03Gjaz3IFWvCAE94XoiEgvtr0/r2Xw7R8/IjK3sKLSiDoCHWcsXSAinVaKlGRZDvMCsF1kbzjA==} proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} @@ -2924,10 +2932,10 @@ packages: raf@3.4.1: resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==} - react-dom@19.1.0: - resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==} + react-dom@19.2.3: + resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} peerDependencies: - react: ^19.1.0 + react: ^19.2.3 react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -2944,12 +2952,12 @@ packages: redux: optional: true - react@19.1.0: - resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} + react@19.2.3: + resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} engines: {node: '>=0.10.0'} - recharts@3.4.1: - resolution: {integrity: sha512-35kYg6JoOgwq8sE4rhYkVWwa6aAIgOtT+Ob0gitnShjwUwZmhrmy7Jco/5kJNF4PnLXgt9Hwq+geEMS+WrjU1g==} + recharts@3.6.0: + resolution: {integrity: sha512-L5bjxvQRAe26RlToBAziKUB7whaGKEwD3znoM6fz3DrTowCIC/FnJYnuq1GEzB8Zv2kdTfaxQfi5GoH0tBinyg==} engines: {node: '>=18'} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -3038,8 +3046,8 @@ packages: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} - scheduler@0.26.0: - resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} @@ -3172,8 +3180,8 @@ packages: engines: {node: '>=14.0.0'} hasBin: true - tailwindcss@4.1.17: - resolution: {integrity: sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==} + tailwindcss@4.1.18: + resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==} tapable@2.3.0: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} @@ -3262,8 +3270,8 @@ packages: unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} - update-browserslist-db@1.1.4: - resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==} + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -3317,8 +3325,8 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - zustand@5.0.8: - resolution: {integrity: sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==} + zustand@5.0.9: + resolution: {integrity: sha512-ALBtUj0AfjJt3uNRQoL1tL2tMvj6Gp/6e39dnfT6uzpelGru8v1tPOGBzayOWbPJvujM8JojDk3E1LxeFisBNg==} engines: {node: '>=12.20.0'} peerDependencies: '@types/react': '>=18.0.0' @@ -3383,7 +3391,7 @@ snapshots: dependencies: '@babel/compat-data': 7.28.5 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.0 + browserslist: 4.28.1 lru-cache: 5.1.1 semver: 6.3.1 @@ -4011,7 +4019,7 @@ snapshots: babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.28.5) babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.5) babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.28.5) - core-js-compat: 3.46.0 + core-js-compat: 3.47.0 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -4087,9 +4095,9 @@ snapshots: tslib: 2.8.1 optional: true - '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1(jiti@2.6.1))': + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.2(jiti@2.6.1))': dependencies: - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.2(jiti@2.6.1) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} @@ -4110,7 +4118,7 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.1': + '@eslint/eslintrc@3.3.3': dependencies: ajv: 6.12.6 debug: 4.4.3 @@ -4124,7 +4132,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.39.1': {} + '@eslint/js@9.39.2': {} '@eslint/object-schema@2.1.7': {} @@ -4267,34 +4275,34 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true - '@next/env@15.5.4': {} + '@next/env@16.1.1': {} '@next/eslint-plugin-next@15.5.4': dependencies: fast-glob: 3.3.1 - '@next/swc-darwin-arm64@15.5.4': + '@next/swc-darwin-arm64@16.1.1': optional: true - '@next/swc-darwin-x64@15.5.4': + '@next/swc-darwin-x64@16.1.1': optional: true - '@next/swc-linux-arm64-gnu@15.5.4': + '@next/swc-linux-arm64-gnu@16.1.1': optional: true - '@next/swc-linux-arm64-musl@15.5.4': + '@next/swc-linux-arm64-musl@16.1.1': optional: true - '@next/swc-linux-x64-gnu@15.5.4': + '@next/swc-linux-x64-gnu@16.1.1': optional: true - '@next/swc-linux-x64-musl@15.5.4': + '@next/swc-linux-x64-musl@16.1.1': optional: true - '@next/swc-win32-arm64-msvc@15.5.4': + '@next/swc-win32-arm64-msvc@16.1.1': optional: true - '@next/swc-win32-x64-msvc@15.5.4': + '@next/swc-win32-x64-msvc@16.1.1': optional: true '@nodelib/fs.scandir@2.1.5': @@ -4313,17 +4321,17 @@ snapshots: '@popperjs/core@2.11.8': {} - '@reduxjs/toolkit@2.10.1(react-redux@9.2.0(@types/react@19.2.5)(react@19.1.0)(redux@5.0.1))(react@19.1.0)': + '@reduxjs/toolkit@2.11.2(react-redux@9.2.0(@types/react@19.2.7)(react@19.2.3)(redux@5.0.1))(react@19.2.3)': dependencies: - '@standard-schema/spec': 1.0.0 + '@standard-schema/spec': 1.1.0 '@standard-schema/utils': 0.3.0 - immer: 10.2.0 + immer: 11.0.1 redux: 5.0.1 redux-thunk: 3.1.0(redux@5.0.1) reselect: 5.1.1 optionalDependencies: - react: 19.1.0 - react-redux: 9.2.0(@types/react@19.2.5)(react@19.1.0)(redux@5.0.1) + react: 19.2.3 + react-redux: 9.2.0(@types/react@19.2.7)(react@19.2.3)(redux@5.0.1) '@remirror/core-constants@3.0.0': {} @@ -4331,7 +4339,7 @@ snapshots: '@rushstack/eslint-patch@1.15.0': {} - '@standard-schema/spec@1.0.0': {} + '@standard-schema/spec@1.1.0': {} '@standard-schema/utils@0.3.0': {} @@ -4432,81 +4440,81 @@ snapshots: dependencies: tslib: 2.8.1 - '@tailwindcss/node@4.1.17': + '@tailwindcss/node@4.1.18': dependencies: '@jridgewell/remapping': 2.3.5 - enhanced-resolve: 5.18.3 + enhanced-resolve: 5.18.4 jiti: 2.6.1 lightningcss: 1.30.2 magic-string: 0.30.21 source-map-js: 1.2.1 - tailwindcss: 4.1.17 + tailwindcss: 4.1.18 - '@tailwindcss/oxide-android-arm64@4.1.17': + '@tailwindcss/oxide-android-arm64@4.1.18': optional: true - '@tailwindcss/oxide-darwin-arm64@4.1.17': + '@tailwindcss/oxide-darwin-arm64@4.1.18': optional: true - '@tailwindcss/oxide-darwin-x64@4.1.17': + '@tailwindcss/oxide-darwin-x64@4.1.18': optional: true - '@tailwindcss/oxide-freebsd-x64@4.1.17': + '@tailwindcss/oxide-freebsd-x64@4.1.18': optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17': + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.1.17': + '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.1.17': + '@tailwindcss/oxide-linux-arm64-musl@4.1.18': optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.1.17': + '@tailwindcss/oxide-linux-x64-gnu@4.1.18': optional: true - '@tailwindcss/oxide-linux-x64-musl@4.1.17': + '@tailwindcss/oxide-linux-x64-musl@4.1.18': optional: true - '@tailwindcss/oxide-wasm32-wasi@4.1.17': + '@tailwindcss/oxide-wasm32-wasi@4.1.18': optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.1.17': + '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.1.17': + '@tailwindcss/oxide-win32-x64-msvc@4.1.18': optional: true - '@tailwindcss/oxide@4.1.17': + '@tailwindcss/oxide@4.1.18': optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.1.17 - '@tailwindcss/oxide-darwin-arm64': 4.1.17 - '@tailwindcss/oxide-darwin-x64': 4.1.17 - '@tailwindcss/oxide-freebsd-x64': 4.1.17 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.17 - '@tailwindcss/oxide-linux-arm64-gnu': 4.1.17 - '@tailwindcss/oxide-linux-arm64-musl': 4.1.17 - '@tailwindcss/oxide-linux-x64-gnu': 4.1.17 - '@tailwindcss/oxide-linux-x64-musl': 4.1.17 - '@tailwindcss/oxide-wasm32-wasi': 4.1.17 - '@tailwindcss/oxide-win32-arm64-msvc': 4.1.17 - '@tailwindcss/oxide-win32-x64-msvc': 4.1.17 - - '@tailwindcss/postcss@4.1.17': + '@tailwindcss/oxide-android-arm64': 4.1.18 + '@tailwindcss/oxide-darwin-arm64': 4.1.18 + '@tailwindcss/oxide-darwin-x64': 4.1.18 + '@tailwindcss/oxide-freebsd-x64': 4.1.18 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.18 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.18 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.18 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.18 + '@tailwindcss/oxide-linux-x64-musl': 4.1.18 + '@tailwindcss/oxide-wasm32-wasi': 4.1.18 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.18 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.18 + + '@tailwindcss/postcss@4.1.18': dependencies: '@alloc/quick-lru': 5.2.0 - '@tailwindcss/node': 4.1.17 - '@tailwindcss/oxide': 4.1.17 + '@tailwindcss/node': 4.1.18 + '@tailwindcss/oxide': 4.1.18 postcss: 8.5.6 - tailwindcss: 4.1.17 + tailwindcss: 4.1.18 - '@tanstack/query-core@5.90.10': {} + '@tanstack/query-core@5.90.12': {} - '@tanstack/react-query@5.90.10(react@19.1.0)': + '@tanstack/react-query@5.90.12(react@19.2.3)': dependencies: - '@tanstack/query-core': 5.90.10 - react: 19.1.0 + '@tanstack/query-core': 5.90.12 + react: 19.2.3 '@tiptap/core@2.27.1(@tiptap/pm@2.27.1)': dependencies: @@ -4656,12 +4664,12 @@ snapshots: prosemirror-schema-basic: 1.2.4 prosemirror-schema-list: 1.5.1 prosemirror-state: 1.4.4 - prosemirror-tables: 1.8.1 - prosemirror-trailing-node: 3.0.0(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.3) + prosemirror-tables: 1.8.3 + prosemirror-trailing-node: 3.0.0(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.4) prosemirror-transform: 1.10.5 - prosemirror-view: 1.41.3 + prosemirror-view: 1.41.4 - '@tiptap/react@2.27.1(@tiptap/core@2.27.1(@tiptap/pm@2.27.1))(@tiptap/pm@2.27.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@tiptap/react@2.27.1(@tiptap/core@2.27.1(@tiptap/pm@2.27.1))(@tiptap/pm@2.27.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@tiptap/core': 2.27.1(@tiptap/pm@2.27.1) '@tiptap/extension-bubble-menu': 2.27.1(@tiptap/core@2.27.1(@tiptap/pm@2.27.1))(@tiptap/pm@2.27.1) @@ -4669,9 +4677,9 @@ snapshots: '@tiptap/pm': 2.27.1 '@types/use-sync-external-store': 0.0.6 fast-deep-equal: 3.1.3 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - use-sync-external-store: 1.6.0(react@19.1.0) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + use-sync-external-store: 1.6.0(react@19.2.3) '@tiptap/starter-kit@2.27.1': dependencies: @@ -4752,7 +4760,7 @@ snapshots: '@types/mdurl@2.0.0': {} - '@types/node@20.19.25': + '@types/node@20.19.27': dependencies: undici-types: 6.21.0 @@ -4761,11 +4769,11 @@ snapshots: '@types/raf@3.4.3': optional: true - '@types/react-dom@19.2.3(@types/react@19.2.5)': + '@types/react-dom@19.2.3(@types/react@19.2.7)': dependencies: - '@types/react': 19.2.5 + '@types/react': 19.2.7 - '@types/react@19.2.5': + '@types/react@19.2.7': dependencies: csstype: 3.2.3 @@ -4774,16 +4782,15 @@ snapshots: '@types/use-sync-external-store@0.0.6': {} - '@typescript-eslint/eslint-plugin@8.46.4(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.46.4 - '@typescript-eslint/type-utils': 8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.46.4 - eslint: 9.39.1(jiti@2.6.1) - graphemer: 1.4.0 + '@typescript-eslint/parser': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/type-utils': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.50.0 + eslint: 9.39.2(jiti@2.6.1) ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.1.0(typescript@5.9.3) @@ -4791,80 +4798,79 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.46.4 - '@typescript-eslint/types': 8.46.4 - '@typescript-eslint/typescript-estree': 8.46.4(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.46.4 + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.50.0 debug: 4.4.3 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.46.4(typescript@5.9.3)': + '@typescript-eslint/project-service@8.50.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.46.4(typescript@5.9.3) - '@typescript-eslint/types': 8.46.4 + '@typescript-eslint/tsconfig-utils': 8.50.0(typescript@5.9.3) + '@typescript-eslint/types': 8.50.0 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.46.4': + '@typescript-eslint/scope-manager@8.50.0': dependencies: - '@typescript-eslint/types': 8.46.4 - '@typescript-eslint/visitor-keys': 8.46.4 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/visitor-keys': 8.50.0 - '@typescript-eslint/tsconfig-utils@8.46.4(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.50.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.46.4 - '@typescript-eslint/typescript-estree': 8.46.4(typescript@5.9.3) - '@typescript-eslint/utils': 8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) debug: 4.4.3 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.2(jiti@2.6.1) ts-api-utils: 2.1.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.46.4': {} + '@typescript-eslint/types@8.50.0': {} - '@typescript-eslint/typescript-estree@8.46.4(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.50.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.46.4(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.46.4(typescript@5.9.3) - '@typescript-eslint/types': 8.46.4 - '@typescript-eslint/visitor-keys': 8.46.4 + '@typescript-eslint/project-service': 8.50.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.50.0(typescript@5.9.3) + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/visitor-keys': 8.50.0 debug: 4.4.3 - fast-glob: 3.3.3 - is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.7.3 + tinyglobby: 0.2.15 ts-api-utils: 2.1.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/utils@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.46.4 - '@typescript-eslint/types': 8.46.4 - '@typescript-eslint/typescript-estree': 8.46.4(typescript@5.9.3) - eslint: 9.39.1(jiti@2.6.1) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.46.4': + '@typescript-eslint/visitor-keys@8.50.0': dependencies: - '@typescript-eslint/types': 8.46.4 + '@typescript-eslint/types': 8.50.0 eslint-visitor-keys: 4.2.1 '@unrs/resolver-binding-android-arm-eabi@1.11.1': @@ -4957,7 +4963,7 @@ snapshots: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 is-string: 1.1.1 @@ -4967,7 +4973,7 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 es-shim-unscopables: 1.1.0 @@ -4977,7 +4983,7 @@ snapshots: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 es-shim-unscopables: 1.1.0 @@ -4986,21 +4992,21 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-shim-unscopables: 1.1.0 array.prototype.flatmap@1.3.3: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-shim-unscopables: 1.1.0 array.prototype.tosorted@1.1.4: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-shim-unscopables: 1.1.0 @@ -5009,7 +5015,7 @@ snapshots: array-buffer-byte-length: 1.0.2 call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 @@ -5049,7 +5055,7 @@ snapshots: dependencies: '@babel/core': 7.28.5 '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.5) - core-js-compat: 3.46.0 + core-js-compat: 3.47.0 transitivePeerDependencies: - supports-color @@ -5064,7 +5070,7 @@ snapshots: base64-arraybuffer@1.0.2: {} - baseline-browser-mapping@2.8.29: {} + baseline-browser-mapping@2.9.11: {} boolbase@1.0.0: {} @@ -5081,13 +5087,13 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.28.0: + browserslist@4.28.1: dependencies: - baseline-browser-mapping: 2.8.29 - caniuse-lite: 1.0.30001755 - electron-to-chromium: 1.5.254 + baseline-browser-mapping: 2.9.11 + caniuse-lite: 1.0.30001761 + electron-to-chromium: 1.5.267 node-releases: 2.0.27 - update-browserslist-db: 1.1.4(browserslist@4.28.0) + update-browserslist-db: 1.2.3(browserslist@4.28.1) call-bind-apply-helpers@1.0.2: dependencies: @@ -5110,13 +5116,13 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001755: {} + caniuse-lite@1.0.30001761: {} canvg@3.0.11: dependencies: '@babel/runtime': 7.28.4 '@types/raf': 3.4.3 - core-js: 3.46.0 + core-js: 3.47.0 raf: 3.4.1 regenerator-runtime: 0.13.11 rgbcolor: 1.0.1 @@ -5149,11 +5155,11 @@ snapshots: convert-source-map@2.0.0: {} - core-js-compat@3.46.0: + core-js-compat@3.47.0: dependencies: - browserslist: 4.28.0 + browserslist: 4.28.1 - core-js@3.46.0: + core-js@3.47.0: optional: true cosmiconfig@8.3.6(typescript@5.9.3): @@ -5307,7 +5313,7 @@ snapshots: dependencies: domelementtype: 2.3.0 - dompurify@3.3.0: + dompurify@3.3.1: optionalDependencies: '@types/trusted-types': 2.0.7 optional: true @@ -5329,11 +5335,11 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - electron-to-chromium@1.5.254: {} + electron-to-chromium@1.5.267: {} emoji-regex@9.2.2: {} - enhanced-resolve@5.18.3: + enhanced-resolve@5.18.4: dependencies: graceful-fs: 4.2.11 tapable: 2.3.0 @@ -5344,7 +5350,7 @@ snapshots: dependencies: is-arrayish: 0.2.1 - es-abstract@1.24.0: + es-abstract@1.24.1: dependencies: array-buffer-byte-length: 1.0.2 arraybuffer.prototype.slice: 1.0.4 @@ -5405,12 +5411,12 @@ snapshots: es-errors@1.3.0: {} - es-iterator-helpers@1.2.1: + es-iterator-helpers@1.2.2: dependencies: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-set-tostringtag: 2.1.0 function-bind: 1.1.2 @@ -5445,25 +5451,25 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - es-toolkit@1.42.0: {} + es-toolkit@1.43.0: {} escalade@3.2.0: {} escape-string-regexp@4.0.0: {} - eslint-config-next@15.5.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3): + eslint-config-next@15.5.4(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3): dependencies: '@next/eslint-plugin-next': 15.5.4 '@rushstack/eslint-patch': 1.15.0 - '@typescript-eslint/eslint-plugin': 8.46.4(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - eslint: 9.39.1(jiti@2.6.1) + '@typescript-eslint/eslint-plugin': 8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-react-hooks: 5.2.0(eslint@9.39.1(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-react-hooks: 5.2.0(eslint@9.39.2(jiti@2.6.1)) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -5479,33 +5485,33 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.2(jiti@2.6.1) get-tsconfig: 4.13.0 is-bun-module: 2.0.0 stable-hash: 0.0.5 tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - eslint: 9.39.1(jiti@2.6.1) + '@typescript-eslint/parser': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -5514,9 +5520,9 @@ snapshots: array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -5528,13 +5534,13 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.2(jiti@2.6.1)): dependencies: aria-query: 5.3.2 array-includes: 3.1.9 @@ -5544,7 +5550,7 @@ snapshots: axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.2(jiti@2.6.1) hasown: 2.0.2 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 @@ -5553,19 +5559,19 @@ snapshots: safe-regex-test: 1.1.0 string.prototype.includes: 2.0.1 - eslint-plugin-react-hooks@5.2.0(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-react-hooks@5.2.0(eslint@9.39.2(jiti@2.6.1)): dependencies: - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.2(jiti@2.6.1) - eslint-plugin-react@7.37.5(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-react@7.37.5(eslint@9.39.2(jiti@2.6.1)): dependencies: array-includes: 3.1.9 array.prototype.findlast: 1.2.5 array.prototype.flatmap: 1.3.3 array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 - es-iterator-helpers: 1.2.1 - eslint: 9.39.1(jiti@2.6.1) + es-iterator-helpers: 1.2.2 + eslint: 9.39.2(jiti@2.6.1) estraverse: 5.3.0 hasown: 2.0.2 jsx-ast-utils: 3.3.5 @@ -5588,15 +5594,15 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.39.1(jiti@2.6.1): + eslint@9.39.2(jiti@2.6.1): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@2.6.1)) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.21.1 '@eslint/config-helpers': 0.4.2 '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.39.1 + '@eslint/eslintrc': 3.3.3 + '@eslint/js': 9.39.2 '@eslint/plugin-kit': 0.4.1 '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 @@ -5659,14 +5665,6 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} @@ -5785,8 +5783,6 @@ snapshots: graceful-fs@4.2.11: {} - graphemer@1.4.0: {} - has-bigints@1.1.0: {} has-flag@4.0.0: {} @@ -5820,6 +5816,8 @@ snapshots: immer@10.2.0: {} + immer@11.0.1: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -5988,15 +5986,15 @@ snapshots: json5@2.2.3: {} - jspdf@3.0.3: + jspdf@3.0.4: dependencies: '@babel/runtime': 7.28.4 fast-png: 6.4.0 fflate: 0.8.2 optionalDependencies: canvg: 3.0.11 - core-js: 3.46.0 - dompurify: 3.3.0 + core-js: 3.47.0 + dompurify: 3.3.1 html2canvas: 1.4.1 jsx-ast-utils@3.3.5: @@ -6088,6 +6086,14 @@ snapshots: dependencies: js-tokens: 4.0.0 + lottie-react@2.4.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + lottie-web: 5.13.0 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + lottie-web@5.13.0: {} + lower-case@2.0.2: dependencies: tslib: 2.8.1 @@ -6148,24 +6154,25 @@ snapshots: natural-compare@1.4.0: {} - next@15.5.4(@babel/core@7.28.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + next@16.1.1(@babel/core@7.28.5)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: - '@next/env': 15.5.4 + '@next/env': 16.1.1 '@swc/helpers': 0.5.15 - caniuse-lite: 1.0.30001755 + baseline-browser-mapping: 2.9.11 + caniuse-lite: 1.0.30001761 postcss: 8.4.31 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - styled-jsx: 5.1.6(@babel/core@7.28.5)(react@19.1.0) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + styled-jsx: 5.1.6(@babel/core@7.28.5)(react@19.2.3) optionalDependencies: - '@next/swc-darwin-arm64': 15.5.4 - '@next/swc-darwin-x64': 15.5.4 - '@next/swc-linux-arm64-gnu': 15.5.4 - '@next/swc-linux-arm64-musl': 15.5.4 - '@next/swc-linux-x64-gnu': 15.5.4 - '@next/swc-linux-x64-musl': 15.5.4 - '@next/swc-win32-arm64-msvc': 15.5.4 - '@next/swc-win32-x64-msvc': 15.5.4 + '@next/swc-darwin-arm64': 16.1.1 + '@next/swc-darwin-x64': 16.1.1 + '@next/swc-linux-arm64-gnu': 16.1.1 + '@next/swc-linux-arm64-musl': 16.1.1 + '@next/swc-linux-x64-gnu': 16.1.1 + '@next/swc-linux-x64-musl': 16.1.1 + '@next/swc-win32-arm64-msvc': 16.1.1 + '@next/swc-win32-x64-msvc': 16.1.1 sharp: 0.34.5 transitivePeerDependencies: - '@babel/core' @@ -6208,14 +6215,14 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 object.groupby@1.0.3: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 object.values@1.2.1: dependencies: @@ -6295,11 +6302,11 @@ snapshots: prelude-ls@1.2.1: {} - prettier-plugin-tailwindcss@0.7.1(prettier@3.6.2): + prettier-plugin-tailwindcss@0.7.2(prettier@3.7.4): dependencies: - prettier: 3.6.2 + prettier: 3.7.4 - prettier@3.6.2: {} + prettier@3.7.4: {} prop-types@15.8.1: dependencies: @@ -6325,20 +6332,20 @@ snapshots: dependencies: prosemirror-state: 1.4.4 prosemirror-transform: 1.10.5 - prosemirror-view: 1.41.3 + prosemirror-view: 1.41.4 prosemirror-gapcursor@1.4.0: dependencies: prosemirror-keymap: 1.2.3 prosemirror-model: 1.25.4 prosemirror-state: 1.4.4 - prosemirror-view: 1.41.3 + prosemirror-view: 1.41.4 prosemirror-history@1.5.0: dependencies: prosemirror-state: 1.4.4 prosemirror-transform: 1.10.5 - prosemirror-view: 1.41.3 + prosemirror-view: 1.41.4 rope-sequence: 1.3.4 prosemirror-inputrules@1.5.1: @@ -6382,29 +6389,29 @@ snapshots: dependencies: prosemirror-model: 1.25.4 prosemirror-transform: 1.10.5 - prosemirror-view: 1.41.3 + prosemirror-view: 1.41.4 - prosemirror-tables@1.8.1: + prosemirror-tables@1.8.3: dependencies: prosemirror-keymap: 1.2.3 prosemirror-model: 1.25.4 prosemirror-state: 1.4.4 prosemirror-transform: 1.10.5 - prosemirror-view: 1.41.3 + prosemirror-view: 1.41.4 - prosemirror-trailing-node@3.0.0(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.3): + prosemirror-trailing-node@3.0.0(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.4): dependencies: '@remirror/core-constants': 3.0.0 escape-string-regexp: 4.0.0 prosemirror-model: 1.25.4 prosemirror-state: 1.4.4 - prosemirror-view: 1.41.3 + prosemirror-view: 1.41.4 prosemirror-transform@1.10.5: dependencies: prosemirror-model: 1.25.4 - prosemirror-view@1.41.3: + prosemirror-view@1.41.4: dependencies: prosemirror-model: 1.25.4 prosemirror-state: 1.4.4 @@ -6423,39 +6430,39 @@ snapshots: performance-now: 2.1.0 optional: true - react-dom@19.1.0(react@19.1.0): + react-dom@19.2.3(react@19.2.3): dependencies: - react: 19.1.0 - scheduler: 0.26.0 + react: 19.2.3 + scheduler: 0.27.0 react-is@16.13.1: {} - react-redux@9.2.0(@types/react@19.2.5)(react@19.1.0)(redux@5.0.1): + react-redux@9.2.0(@types/react@19.2.7)(react@19.2.3)(redux@5.0.1): dependencies: '@types/use-sync-external-store': 0.0.6 - react: 19.1.0 - use-sync-external-store: 1.6.0(react@19.1.0) + react: 19.2.3 + use-sync-external-store: 1.6.0(react@19.2.3) optionalDependencies: - '@types/react': 19.2.5 + '@types/react': 19.2.7 redux: 5.0.1 - react@19.1.0: {} + react@19.2.3: {} - recharts@3.4.1(@types/react@19.2.5)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)(redux@5.0.1): + recharts@3.6.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react-is@16.13.1)(react@19.2.3)(redux@5.0.1): dependencies: - '@reduxjs/toolkit': 2.10.1(react-redux@9.2.0(@types/react@19.2.5)(react@19.1.0)(redux@5.0.1))(react@19.1.0) + '@reduxjs/toolkit': 2.11.2(react-redux@9.2.0(@types/react@19.2.7)(react@19.2.3)(redux@5.0.1))(react@19.2.3) clsx: 2.1.1 decimal.js-light: 2.5.1 - es-toolkit: 1.42.0 + es-toolkit: 1.43.0 eventemitter3: 5.0.1 immer: 10.2.0 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) react-is: 16.13.1 - react-redux: 9.2.0(@types/react@19.2.5)(react@19.1.0)(redux@5.0.1) + react-redux: 9.2.0(@types/react@19.2.7)(react@19.2.3)(redux@5.0.1) reselect: 5.1.1 tiny-invariant: 1.3.3 - use-sync-external-store: 1.6.0(react@19.1.0) + use-sync-external-store: 1.6.0(react@19.2.3) victory-vendor: 37.3.6 transitivePeerDependencies: - '@types/react' @@ -6471,7 +6478,7 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 @@ -6559,7 +6566,7 @@ snapshots: es-errors: 1.3.0 is-regex: 1.2.1 - scheduler@0.26.0: {} + scheduler@0.27.0: {} semver@6.3.1: {} @@ -6674,14 +6681,14 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 string.prototype.matchall@4.0.12: dependencies: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 @@ -6695,7 +6702,7 @@ snapshots: string.prototype.repeat@1.0.0: dependencies: define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 string.prototype.trim@1.2.10: dependencies: @@ -6703,7 +6710,7 @@ snapshots: call-bound: 1.0.4 define-data-property: 1.1.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 has-property-descriptors: 1.0.2 @@ -6724,10 +6731,10 @@ snapshots: strip-json-comments@3.1.1: {} - styled-jsx@5.1.6(@babel/core@7.28.5)(react@19.1.0): + styled-jsx@5.1.6(@babel/core@7.28.5)(react@19.2.3): dependencies: client-only: 0.0.1 - react: 19.1.0 + react: 19.2.3 optionalDependencies: '@babel/core': 7.28.5 @@ -6752,7 +6759,7 @@ snapshots: csso: 5.0.5 picocolors: 1.1.1 - tailwindcss@4.1.17: {} + tailwindcss@4.1.18: {} tapable@2.3.0: {} @@ -6873,9 +6880,9 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 - update-browserslist-db@1.1.4(browserslist@4.28.0): + update-browserslist-db@1.2.3(browserslist@4.28.1): dependencies: - browserslist: 4.28.0 + browserslist: 4.28.1 escalade: 3.2.0 picocolors: 1.1.1 @@ -6883,9 +6890,9 @@ snapshots: dependencies: punycode: 2.3.1 - use-sync-external-store@1.6.0(react@19.1.0): + use-sync-external-store@1.6.0(react@19.2.3): dependencies: - react: 19.1.0 + react: 19.2.3 utrie@1.0.2: dependencies: @@ -6961,9 +6968,9 @@ snapshots: yocto-queue@0.1.0: {} - zustand@5.0.8(@types/react@19.2.5)(immer@10.2.0)(react@19.1.0)(use-sync-external-store@1.6.0(react@19.1.0)): + zustand@5.0.9(@types/react@19.2.7)(immer@11.0.1)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)): optionalDependencies: - '@types/react': 19.2.5 - immer: 10.2.0 - react: 19.1.0 - use-sync-external-store: 1.6.0(react@19.1.0) + '@types/react': 19.2.7 + immer: 11.0.1 + react: 19.2.3 + use-sync-external-store: 1.6.0(react@19.2.3) diff --git a/public/images/expert_detail.png b/public/images/expert_detail.png new file mode 100644 index 0000000..47c5148 Binary files /dev/null and b/public/images/expert_detail.png differ diff --git a/public/images/landing/landing_expert1.png b/public/images/landing/landing_expert1.png index 2fb9866..5abf570 100644 Binary files a/public/images/landing/landing_expert1.png and b/public/images/landing/landing_expert1.png differ diff --git a/public/images/landing/landing_expert2.png b/public/images/landing/landing_expert2.png index 987e5f1..0e9ee14 100644 Binary files a/public/images/landing/landing_expert2.png and b/public/images/landing/landing_expert2.png differ diff --git a/public/images/landing/landing_expert3.png b/public/images/landing/landing_expert3.png index d528bc1..2b70cbd 100644 Binary files a/public/images/landing/landing_expert3.png and b/public/images/landing/landing_expert3.png differ diff --git a/public/images/landing/landing_feedback.png b/public/images/landing/landing_feedback.png index 01c455a..c39055f 100644 Binary files a/public/images/landing/landing_feedback.png and b/public/images/landing/landing_feedback.png differ diff --git a/public/images/price/example_expert_1.png b/public/images/price/example_expert_1.png new file mode 100644 index 0000000..beff58a Binary files /dev/null and b/public/images/price/example_expert_1.png differ diff --git a/public/images/price/example_expert_2.png b/public/images/price/example_expert_2.png new file mode 100644 index 0000000..6b550d7 Binary files /dev/null and b/public/images/price/example_expert_2.png differ diff --git a/public/images/price/example_expert_3.png b/public/images/price/example_expert_3.png new file mode 100644 index 0000000..3f00f8c Binary files /dev/null and b/public/images/price/example_expert_3.png differ diff --git a/public/images/price/example_report_1.webp b/public/images/price/example_report_1.webp new file mode 100644 index 0000000..96a9b60 Binary files /dev/null and b/public/images/price/example_report_1.webp differ diff --git a/public/images/price/example_report_2.webp b/public/images/price/example_report_2.webp new file mode 100644 index 0000000..4a7be33 Binary files /dev/null and b/public/images/price/example_report_2.webp differ diff --git a/public/images/price/example_report_3.webp b/public/images/price/example_report_3.webp new file mode 100644 index 0000000..0b86ee5 Binary files /dev/null and b/public/images/price/example_report_3.webp differ diff --git a/public/images/video_thumbnail.png b/public/images/video_thumbnail.png new file mode 100644 index 0000000..c3e2d51 Binary files /dev/null and b/public/images/video_thumbnail.png differ diff --git a/src/api/api.ts b/src/api/api.ts index 37575b8..113549c 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -7,6 +7,9 @@ const api = axios.create({ }, }); +// refresh ํ† ํฐ ์žฌ๋ฐœ๊ธ‰ ์š”์ฒญ์„ ๋‹จ์ผํ™”ํ•˜๊ธฐ ์œ„ํ•œ Promise ์ถ”์  +let refreshTokenPromise: Promise | null = null; + api.interceptors.request.use( (config) => { if (typeof window !== 'undefined') { @@ -23,6 +26,11 @@ api.interceptors.request.use( api.interceptors.response.use( (response) => response, async (error) => { + // ์š”์ฒญ ์ทจ์†Œ ์—๋Ÿฌ๋Š” ๊ทธ๋Œ€๋กœ ์ „ํŒŒ (์ •์ƒ์ ์ธ ์ทจ์†Œ์ด๋ฏ€๋กœ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์Œ) + if (error?.code === 'ERR_CANCELED' || error?.name === 'CanceledError' || error?.message?.includes('canceled')) { + return Promise.reject(error); + } + const originalRequest = error.config; // 401 ์—๋Ÿฌ์ด๊ณ , ์žฌ์‹œ๋„ ํ”Œ๋ž˜๊ทธ๊ฐ€ ์—†์„ ๋•Œ๋งŒ ์ฒ˜๋ฆฌ @@ -34,27 +42,52 @@ api.interceptors.response.use( if (refreshToken) { try { - // refreshToken์œผ๋กœ ์ƒˆ๋กœ์šด accessToken ์žฌ๋ฐœ๊ธ‰ - const response = await axios.post( - `${process.env.NEXT_PUBLIC_BASE_URL}/v1/auth/recreate`, - { refreshToken } - ); + // ์žฌ๋ฐœ๊ธ‰์ด ์ด๋ฏธ ์ง„ํ–‰ ์ค‘์ด๋ฉด ๊ธฐ์กด Promise๋ฅผ ์žฌ์‚ฌ์šฉ + if (!refreshTokenPromise) { + refreshTokenPromise = (async () => { + try { + // refreshToken์œผ๋กœ ์ƒˆ๋กœ์šด accessToken ์žฌ๋ฐœ๊ธ‰ + const response = await axios.get( + `${process.env.NEXT_PUBLIC_BASE_URL}/v1/auth/recreate`, + { + headers: { + Authorization: `Bearer ${refreshToken}`, + }, + } + ); - const newAccessToken = response.data.accessToken || response.data.access; + const responseData = response.data?.data ?? response.data; + const newAccessToken = responseData?.accessToken || responseData?.access; - if (newAccessToken) { - localStorage.setItem('accessToken', newAccessToken); - if (response.data.refreshToken || response.data.refresh) { - localStorage.setItem('refreshToken', response.data.refreshToken || response.data.refresh); - } - originalRequest.headers.Authorization = `Bearer ${newAccessToken}`; - return api(originalRequest); + if (newAccessToken) { + localStorage.setItem('accessToken', newAccessToken); + if (responseData?.refreshToken || responseData?.refresh) { + localStorage.setItem('refreshToken', responseData?.refreshToken || responseData?.refresh); + } + return newAccessToken; + } + throw new Error('์ƒˆ๋กœ์šด accessToken์„ ๋ฐ›์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.'); + } catch (refreshError) { + // refreshToken๋„ ๋งŒ๋ฃŒ๋˜์—ˆ๊ฑฐ๋‚˜ ์žฌ๋ฐœ๊ธ‰ ์‹คํŒจ + console.error('ํ† ํฐ ์žฌ๋ฐœ๊ธ‰ ์‹คํŒจ:', refreshError); + localStorage.removeItem('accessToken'); + localStorage.removeItem('refreshToken'); + throw refreshError; + } finally { + // ์žฌ๋ฐœ๊ธ‰ ์™„๋ฃŒ ํ›„ Promise ์ดˆ๊ธฐํ™” (์„ฑ๊ณต/์‹คํŒจ ๊ด€๊ณ„์—†์ด) + refreshTokenPromise = null; + } + })(); } + + // ์žฌ๋ฐœ๊ธ‰ ์™„๋ฃŒ ๋Œ€๊ธฐ (์ง„ํ–‰ ์ค‘์ด๋ฉด ๊ธฐ์กด Promise ์žฌ์‚ฌ์šฉ) + const newAccessToken = await refreshTokenPromise; + + // ์ƒˆ ํ† ํฐ์œผ๋กœ ์›๋ž˜ ์š”์ฒญ ์žฌ์‹œ๋„ + originalRequest.headers.Authorization = `Bearer ${newAccessToken}`; + return api(originalRequest); } catch (refreshError) { - // refreshToken๋„ ๋งŒ๋ฃŒ๋˜์—ˆ๊ฑฐ๋‚˜ ์žฌ๋ฐœ๊ธ‰ ์‹คํŒจ - console.error('ํ† ํฐ ์žฌ๋ฐœ๊ธ‰ ์‹คํŒจ:', refreshError); - localStorage.removeItem('accessToken'); - localStorage.removeItem('refreshToken'); + // ์žฌ๋ฐœ๊ธ‰ ์‹คํŒจ ์‹œ ์—๋Ÿฌ ๋ฐ˜ํ™˜ return Promise.reject(refreshError); } } diff --git a/src/api/business.ts b/src/api/business.ts index c1dede2..e296315 100644 --- a/src/api/business.ts +++ b/src/api/business.ts @@ -1,5 +1,6 @@ import { CheckListResponse } from '@/types/business/checklist.type'; import api from './api'; +// import type { AxiosError } from 'axios'; import { BusinessPlanCreateResponse, BusinessPlanSubsectionRequest, @@ -10,7 +11,10 @@ import { SubSectionType, AiGradeResponse, BusinessPlanSubsectionsResponse, + PdfGradingRequest, + PdfGradingRequestWithFile, } from '@/types/business/business.type'; +import { uploadImage } from '@/lib/imageUpload'; export async function postBusinessPlan(): Promise { const res = await api.post(`/v1/business-plans`); @@ -27,20 +31,50 @@ export async function postBusinessPlanSubsections( export async function getBusinessPlanSubsection( planId: number, - subSectionType: SubSectionType + subSectionType: SubSectionType, + signal?: AbortSignal ): Promise { - const res = await api.get( - `/v1/business-plans/${planId}/subsections/${subSectionType}` - ); - return res.data as BusinessPlanSubsectionResponse; + try { + const res = await api.get( + `/v1/business-plans/${planId}/subsections/${subSectionType}`, + { signal } + ); + return res.data as BusinessPlanSubsectionResponse; + //404 ์—๋Ÿฌ ์ฒ˜๋ฆฌ(์ž„์‹œ๋กœ 404์—๋Ÿฌ๋ฅผ ๋ฌด์‹œํ•˜๋„๋ก ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.) + } catch (error) { + // const axiosError = error as AxiosError; + // if (axiosError.response?.status === 404) { + // return { + // result: 'SUCCESS', + // data: { + // message: 'NOT_FOUND', + // content: { + // subSectionType, + // checks: [], + // meta: { + // author: '', + // createdAt: new Date().toISOString().split('T')[0], + // }, + // blocks: [], + // }, + // }, + // error: null, + // }; + // } + throw error; + } } -export async function getBusinessPlanSubsections(planId: number): Promise { +export async function getBusinessPlanSubsections( + planId: number +): Promise { const res = await api.get(`/v1/business-plans/${planId}/subsections`); return res.data as BusinessPlanSubsectionsResponse; } -export async function getBusinessPlanTitle(planId: number): Promise { +export async function getBusinessPlanTitle( + planId: number +): Promise { const res = await api.get(`/v1/business-plans/${planId}/titles`); return res.data as BusinessPlanTitleResponse; } @@ -83,3 +117,20 @@ export async function postCheckList(planId: number, body: CheckListResponse) { return res.data; } + +export async function postPdfEvaluation( + params: PdfGradingRequestWithFile +): Promise { + const { title, file } = params; + + const pdfUrl = await uploadImage(file); + + const body: PdfGradingRequest = { + title, + pdfUrl, + }; + + const res = await api.post(`/v1/ai-reports/evaluation/pdf`, body); + + return res.data; +} diff --git a/src/api/expert.ts b/src/api/expert.ts index d846b3b..50f1a28 100644 --- a/src/api/expert.ts +++ b/src/api/expert.ts @@ -5,8 +5,10 @@ import { getExpertReportsResponse, getExpertResponse, getFeedBackExpertResponse, + getUserExpertReportResponse, } from '@/types/expert/expert.type'; import api from './api'; +import { ExpertDetailResponse } from '@/types/expert/expert.detail'; export async function GetExpert(): Promise { const res = await api.get<{ data: getExpertResponse[] }>('/v1/experts'); @@ -67,3 +69,26 @@ export async function GetExpertReport( return res.data.data; } + +export async function GetUserExpertReport( + businessPlanId: number +): Promise { + const res = await api.get('/v1/expert-reports', { + params: { businessPlanId }, + }); + + return res.data; +} + +export async function GetExpertDetail( + expertId: number +): Promise { + const res = await api.get<{ data: ExpertDetailResponse }>( + `/v1/experts/${expertId}`, + { + params: { expertId }, + } + ); + + return res.data.data; +} diff --git a/src/api/mypage.ts b/src/api/mypage.ts index 1644326..5757647 100644 --- a/src/api/mypage.ts +++ b/src/api/mypage.ts @@ -1,4 +1,9 @@ -import { getMemberResponse, GetMyBusinessPlansResponse } from '@/types/mypage/mypage.type'; +import { + getMemberResponse, + GetMyBusinessPlansResponse, + GetOrderProps, + GetOrderResponse, +} from '@/types/mypage/mypage.type'; import api from './api'; export async function getMember(): Promise { @@ -12,9 +17,20 @@ export interface GetMyBusinessPlansParams { size?: number; } -export async function getMyBusinessPlans(params: GetMyBusinessPlansParams): Promise { - const response = await api.get('/v1/business-plans', { - params, - }); +export async function getMyBusinessPlans( + params: GetMyBusinessPlansParams +): Promise { + const response = await api.get( + '/v1/business-plans', + { + params, + } + ); return response.data; } + +export async function getOrders(): Promise { + const res = await api.get('/v1/orders'); + + return res.data.data; +} diff --git a/src/api/payment.ts b/src/api/payment.ts new file mode 100644 index 0000000..63a5e1a --- /dev/null +++ b/src/api/payment.ts @@ -0,0 +1,38 @@ +import { + OrderConfirmRequestPayload, + OrderConfirmResponseDto, + OrderPrepareRequestPayload, + OrderPrepareResponseDto, +} from '@/types/payment/payment.type'; +import api from './api'; + +interface ApiResponse { + result: 'SUCCESS' | 'FAIL'; + data: T; + error: { + code: string; + message: string; + } | null; +} + +export async function postTossPrepare( + payload: OrderPrepareRequestPayload +): Promise { + const res = await api.post>( + '/v1/orders/request', + payload + ); + + return res.data.data; +} + +export async function postTossConfirm( + payload: OrderConfirmRequestPayload +): Promise { + const res = await api.post>( + '/v1/orders/confirm', + payload + ); + + return res.data.data; +} diff --git a/src/app/_components/common/BusinessHeader.tsx b/src/app/_components/common/BusinessHeader.tsx index be6fbfb..8aef387 100644 --- a/src/app/_components/common/BusinessHeader.tsx +++ b/src/app/_components/common/BusinessHeader.tsx @@ -1,16 +1,27 @@ 'use client'; -import React, { useState, useRef, useEffect, Suspense } from 'react'; +import React, { useState, useRef, useEffect, useCallback, useMemo, Suspense } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; import Back from '@/assets/icons/back_icon.svg'; import Eye from '@/assets/icons/eye.svg'; import Button from './Button'; import CreateModal from '@/app/business/components/CreateModal'; +import LoginModal from './LoginModal'; import Image from 'next/image'; import Download from '@/assets/icons/download.svg'; import { useBusinessStore } from '@/store/business.store'; import { downloadPDF } from '@/lib/pdfDownload'; import { patchBusinessPlanTitle } from '@/api/business'; -import { usePostGrade } from '@/hooks/mutation/usePostGrade'; +import { useAuthStore } from '@/store/auth.store'; +import { + GUEST_DRAFT_KEY, + GUEST_PENDING_ACTION_KEY, + LOGIN_REDIRECT_KEY, +} from '@/lib/business/authKeys'; +import sections from '@/data/sidebar.json'; +import { isSectionCompleted } from '@/util/checkcontent'; +import ToastMessage from './ToastMessage'; + +type PendingAction = 'save' | 'grade'; const BusinessHeaderContent = () => { const router = useRouter(); @@ -25,7 +36,10 @@ const BusinessHeaderContent = () => { title, setTitle, loadTitleFromAPI, + getItemContent, + contents, } = useBusinessStore(); + const { isAuthenticated, checkAuth } = useAuthStore(); // URL์˜ planId ๋˜๋Š” store์˜ planId๋กœ ์ œ๋ชฉ ์กฐํšŒ useEffect(() => { @@ -37,9 +51,28 @@ const BusinessHeaderContent = () => { return; } - loadTitleFromAPI(targetPlanId).catch((error) => { - console.error('์ œ๋ชฉ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ์‹คํŒจ:', error); - }); + // planId๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ๋งŒ ์ œ๋ชฉ ๋กœ๋“œ (์ค‘๋ณต ์š”์ฒญ ๋ฐฉ์ง€) + let isCancelled = false; + loadTitleFromAPI(targetPlanId) + .then((loadedTitle) => { + if (!isCancelled && loadedTitle) { + const currentPlanId = planIdParam + ? parseInt(planIdParam, 10) + : planId; + if (currentPlanId === targetPlanId) { + setTitle(loadedTitle); + } + } + }) + .catch((error) => { + if (!isCancelled) { + console.error('์ œ๋ชฉ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ์‹คํŒจ:', error); + } + }); + + return () => { + isCancelled = true; + }; }, [searchParams, planId, loadTitleFromAPI, setTitle]); // ์ œ๋ชฉ ๋ณ€๊ฒฝ ์‹œ API ์š”์ฒญ (debounce ์ ์šฉ) @@ -67,9 +100,44 @@ const BusinessHeaderContent = () => { const [inputWidth, setInputWidth] = useState(179); const spanRef = useRef(null); const [isModalOpen, setIsModalOpen] = useState(false); + const [openLoginModal, setOpenLoginModal] = useState(false); const isSaving = useBusinessStore((state) => state.isSaving); + const [toastMessage, setToastMessage] = useState(null); + const toastTimerRef = useRef | null>(null); + const [gradingPlanId, setGradingPlanId] = useState(null); + + const allSectionItems = useMemo(() => { + type SidebarItem = { number: string }; + type SidebarSection = { items: SidebarItem[] }; + return (sections as SidebarSection[]).flatMap((sec) => sec.items); + }, []); + + const allSectionsCompleted = useMemo(() => { + if (!getItemContent) return false; + return allSectionItems.every((item) => + isSectionCompleted(getItemContent, item.number) + ); + }, [getItemContent, allSectionItems, contents]); + + const clearToast = useCallback(() => { + if (toastTimerRef.current) { + clearTimeout(toastTimerRef.current); + toastTimerRef.current = null; + } + setToastMessage(null); + }, []); + + const showToast = useCallback((message: string) => { + if (toastTimerRef.current) { + clearTimeout(toastTimerRef.current); + } + setToastMessage(message); + toastTimerRef.current = setTimeout(() => { + setToastMessage(null); + toastTimerRef.current = null; + }, 2000); + }, []); - const { mutate: postGradeMutate, isPending: isGrading } = usePostGrade(); const handleOpenModal = () => setIsModalOpen(true); const handleCloseModal = () => setIsModalOpen(false); @@ -78,7 +146,38 @@ const BusinessHeaderContent = () => { await downloadPDF(title || '์‚ฌ์—…๊ณ„ํš์„œ'); }; - const handleSave = async () => { + const persistGuestDraft = useCallback((action: PendingAction) => { + if (typeof window === 'undefined') return; + try { + const { contents, title: currentTitle } = useBusinessStore.getState(); + localStorage.setItem( + GUEST_DRAFT_KEY, + JSON.stringify({ contents, title: currentTitle }) + ); + localStorage.setItem(GUEST_PENDING_ACTION_KEY, action); + const redirectPath = window.location.pathname + window.location.search; + sessionStorage.setItem( + LOGIN_REDIRECT_KEY, + redirectPath && redirectPath.length > 0 ? redirectPath : '/business' + ); + } catch (error) { + console.error('๋น„ํšŒ์› ์ดˆ์•ˆ ์ €์žฅ ์‹คํŒจ:', error); + } + }, []); + + const requireLoginForAction = useCallback( + (action: PendingAction) => { + persistGuestDraft(action); + setOpenLoginModal(true); + }, + [persistGuestDraft] + ); + + const handleSave = useCallback(async () => { + if (!isAuthenticated) { + requireLoginForAction('save'); + return; + } try { setIsSaving(true); const currentPlanId = planId || (await initializePlan()); @@ -88,24 +187,60 @@ const BusinessHeaderContent = () => { } finally { setIsSaving(false); } - }; + }, [isAuthenticated, requireLoginForAction, setIsSaving, planId, initializePlan, saveAllItems]); - const handleGrade = async () => { + const handleGrade = useCallback(async () => { + if (!allSectionsCompleted) { + showToast('๋ชจ๋“  ํ•ญ๋ชฉ์„ ์ž…๋ ฅํ•œ ํ›„์— ์ฑ„์ ์ด ๊ฐ€๋Šฅํ•ด์š”.'); + return; + } + if (!isAuthenticated) { + requireLoginForAction('grade'); + return; + } try { setIsSaving(true); const id = planId ?? (await initializePlan()); if (id == null) throw new Error('planId ์ƒ์„ฑ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.'); await saveAllItems(id); + setGradingPlanId(id); handleOpenModal(); - postGradeMutate(id, { - onSuccess: () => router.push('/report'), - onError: (e) => console.error('์ฑ„์  ์‹คํŒจ:', e), - }); + } catch (error) { + console.error('์ฑ„์  ์ค€๋น„ ์ค‘ ์˜ค๋ฅ˜:', error); } finally { setIsSaving(false); } - }; + }, [ + isAuthenticated, + requireLoginForAction, + setIsSaving, + planId, + initializePlan, + saveAllItems, + handleOpenModal, + allSectionsCompleted, + showToast, + ]); + + const handleCloseLoginModal = useCallback(() => { + setOpenLoginModal(false); + }, []); + + useEffect(() => { + checkAuth(); + }, [checkAuth]); + + // ํŽ˜์ด์ง€ ์ง„์ž… ์‹œ ํ•ญ์ƒ ์ž‘์„ฑ ๋ชจ๋“œ๋กœ ์ดˆ๊ธฐํ™” + useEffect(() => { + setPreview(false); + }, [setPreview]); + + useEffect(() => { + return () => { + clearToast(); + }; + }, [clearToast]); useEffect(() => { if (spanRef.current) { @@ -119,146 +254,165 @@ const BusinessHeaderContent = () => { } }, [title, focused]); + useEffect(() => { + if (!isAuthenticated) return; + if (typeof window === 'undefined') return; + const pendingAction = localStorage.getItem(GUEST_PENDING_ACTION_KEY) as PendingAction | null; + if (!pendingAction) return; + + localStorage.removeItem(GUEST_PENDING_ACTION_KEY); + handleSave(); + }, [isAuthenticated, handleSave]); + return ( -
-
-
{ - // ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๋ชจ๋“œ์ผ ๋•Œ๋Š” ์ž‘์„ฑ ํ™”๋ฉด์œผ๋กœ ์ „ํ™˜ - if (isPreview) { - setPreview(false); - } else { - router.back(); - } - }} - className="flex cursor-pointer items-center justify-center gap-1 rounded-[8px] px-4 py-[6px] active:bg-gray-200" - > - - - ์ด์ „ ํŽ˜์ด์ง€ - -
+ <> +
+
+
{ + // ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๋ชจ๋“œ์ผ ๋•Œ๋Š” ์ž‘์„ฑ ํ™”๋ฉด์œผ๋กœ ์ „ํ™˜ + if (isPreview) { + setPreview(false); + } else { + router.back(); + } + }} + className="flex cursor-pointer items-center justify-center gap-1 rounded-[8px] px-4 py-[6px] active:bg-gray-200" + > + + + ์ด์ „ ํŽ˜์ด์ง€ + +
-
- {isPreview ? null : ( - <> - - setTitle(e.target.value)} - onFocus={() => setFocused(true)} - onBlur={() => setFocused(false)} - placeholder="์ œ๋ชฉ์„ ์ž…๋ ฅํ•˜์„ธ์š”." - aria-label="๋ฌธ์„œ ์ œ๋ชฉ" - style={{ width: inputWidth }} - className="ds-text hover:border-primary-200 rounded-[8px] bg-white px-3 py-[6px] text-start font-medium overflow-ellipsis transition-[width] duration-200 ease-out placeholder:text-gray-400 hover:border-[1.2px] focus:outline-none" - /> - - )} -
+
+ {isPreview ? null : ( + <> + + setTitle(e.target.value)} + onFocus={() => setFocused(true)} + onBlur={() => setFocused(false)} + placeholder="์ œ๋ชฉ์„ ์ž…๋ ฅํ•˜์„ธ์š”." + aria-label="๋ฌธ์„œ ์ œ๋ชฉ" + style={{ width: inputWidth }} + className="ds-text hover:border-primary-200 rounded-[8px] bg-white px-3 py-[6px] text-start font-medium overflow-ellipsis transition-[width] duration-200 ease-out placeholder:text-gray-400 hover:border-[1.2px] focus:outline-none" + /> + + )} +
-
- {isPreview ? ( - <> - -
- -
-
- ๋ฏธ๋ฆฌ๋ณด๊ธฐ ํ˜ธ๋ฒ„ ๋งํ’์„  - - ๋ฏธ๋ฆฌ๋ณด๊ธฐ - -
-
-
-
-
- +
- + + ) : ( + <> +
+ +
+
+ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ํ˜ธ๋ฒ„ ๋งํ’์„  + + ๋ฏธ๋ฆฌ๋ณด๊ธฐ + +
+
+
+
+
+ +
+ + )} +
+ + {isModalOpen && gradingPlanId && ( + { + router.push(`/loading?planId=${gradingPlanId}`); + }} + /> )} +
- - {isModalOpen && ( - { - if (!isGrading) router.push('/report'); - }} - /> - )} -
-
+
+ {toastMessage ? ( + + ) : null} + ); }; @@ -272,4 +426,4 @@ const BusinessHeader = () => ( ); -export default BusinessHeader; +export default BusinessHeader; \ No newline at end of file diff --git a/src/app/_components/common/LoadingScreen.tsx b/src/app/_components/common/LoadingScreen.tsx new file mode 100644 index 0000000..930b208 --- /dev/null +++ b/src/app/_components/common/LoadingScreen.tsx @@ -0,0 +1,60 @@ +'use client'; + +import LoadingCheck from '@/assets/icons/blue_check.svg'; + +interface LoadingScreenProps { + title: string; + subtitles: string[]; + buttonTextLeft?: string; + buttonTextRight?: string; + onClickLeft?: () => void; + onClickRight?: () => void; +} + +const LoadingScreen = ({ + title, + subtitles, + buttonTextLeft, + buttonTextRight, + onClickLeft, + onClickRight, +}: LoadingScreenProps) => { + return ( +
+
+ +
+ {title} +
+
+ {subtitles.map((text, idx) => ( +

+ {text} +

+ ))} +
+ +
+ {buttonTextLeft && ( + + )} + {buttonTextRight && ( + + )} +
+
+
+ ); +}; + +export default LoadingScreen; diff --git a/src/app/_components/common/ToastMessage.tsx b/src/app/_components/common/ToastMessage.tsx new file mode 100644 index 0000000..ed3aed7 --- /dev/null +++ b/src/app/_components/common/ToastMessage.tsx @@ -0,0 +1,75 @@ +'use client'; +import { useLayoutEffect, useState } from 'react'; +import WarningIcon from '@/assets/icons/warning.svg'; +import ToastCloseIcon from '@/assets/icons/toast_close.svg'; + +type ToastMessageProps = { + message: string; + onClose: () => void; + anchorSelector?: string; + verticalOffset?: number; + horizontalOffset?: number; +}; + +const ToastMessage = ({ + message, + onClose, + anchorSelector = '[data-toast-anchor]', + verticalOffset = 74, + horizontalOffset = 22, +}: ToastMessageProps) => { + const [position, setPosition] = useState<{ left: number; top: number } | null>( + null + ); + + useLayoutEffect(() => { + if (typeof window === 'undefined') return; + + const updatePosition = () => { + const anchor = document.querySelector(anchorSelector) as HTMLElement | null; + if (!anchor) { + setPosition(null); + return; + } + const rect = anchor.getBoundingClientRect(); + const left = Math.max(16, rect.left + horizontalOffset); + const top = Math.min( + window.innerHeight - 80, + rect.bottom - verticalOffset + ); + setPosition({ left, top }); + }; + + updatePosition(); + window.addEventListener('resize', updatePosition); + window.addEventListener('scroll', updatePosition, true); + return () => { + window.removeEventListener('resize', updatePosition); + window.removeEventListener('scroll', updatePosition, true); + }; + }, [anchorSelector, verticalOffset, horizontalOffset, message]); + + const baseClass = + 'fixed z-[150] flex w-[748px] max-w-[90vw] items-center gap-2 rounded-[12px] bg-gray-900 px-[12px] py-[10px] text-center'; + + const inlineStyle = position + ? { left: position.left, top: position.top } + : { left: '50%', bottom: '100px', transform: 'translateX(-50%)' }; + + return ( +
+ +

{message}

+ +
+ ); +}; + +export default ToastMessage; \ No newline at end of file diff --git a/src/app/_components/common/UploadReportModal.tsx b/src/app/_components/common/UploadReportModal.tsx index b546623..8303d4f 100644 --- a/src/app/_components/common/UploadReportModal.tsx +++ b/src/app/_components/common/UploadReportModal.tsx @@ -1,168 +1,237 @@ 'use client'; import React, { useEffect, useRef, useState } from 'react'; -import PdfIcon from "@/assets/icons/pdf_icon.svg"; -import CloseIcon from "@/assets/icons/close.svg"; -import WarningIcon from "@/assets/icons/warning.svg"; +import PdfIcon from '@/assets/icons/pdf_icon.svg'; +import CloseIcon from '@/assets/icons/close.svg'; +import WarningIcon from '@/assets/icons/warning.svg'; import { useRouter } from 'next/navigation'; +import { usePdfGrade } from '@/hooks/mutation/usePdfGrade'; type UploadReportModalProps = { - open: boolean; - onClose: () => void; + open: boolean; + onClose: () => void; }; -const UploadReportModal: React.FC = ({ open, onClose }) => { - const router = useRouter(); - const fileInputRef = useRef(null); - const [dragActive, setDragActive] = useState(false); - const [selectedFile, setSelectedFile] = useState(null); - const [validationError, setValidationError] = useState(null); - - const triggerFileDialog = () => fileInputRef.current?.click(); - - const validateAndSetFile = (file: File) => { - setValidationError(null); - - if (file.type !== 'application/pdf') { - setValidationError('PDF ํ˜•์‹์˜ ํŒŒ์ผ๋งŒ ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ์–ด์š”.'); - return; - } - if (file.size > 20 * 1024 * 1024) { - setValidationError('ํŒŒ์ผ ์šฉ๋Ÿ‰์ด ๋„ˆ๋ฌด ์ปค์„œ ์—…๋กœ๋“œํ•  ์ˆ˜ ์—†์–ด์š”. (์ตœ๋Œ€ 20MB)'); - return; - } - setSelectedFile(file); - }; +const UploadReportModal: React.FC = ({ + open, + onClose, +}) => { + const router = useRouter(); + const fileInputRef = useRef(null); + const [dragActive, setDragActive] = useState(false); + const [selectedFile, setSelectedFile] = useState(null); + const [validationError, setValidationError] = useState(null); - const onInputChange: React.ChangeEventHandler = (e) => { - const file = e.target.files?.[0]; - if (file) validateAndSetFile(file); - // allow selecting same file again - if (fileInputRef.current) fileInputRef.current.value = ''; - }; + const { mutateAsync: gradePdf, isPending } = usePdfGrade(); - const onDragOver: React.DragEventHandler = (e) => { - e.preventDefault(); - e.stopPropagation(); - setDragActive(true); - }; - const onDragLeave: React.DragEventHandler = (e) => { - e.preventDefault(); - e.stopPropagation(); - setDragActive(false); - }; - const onDrop: React.DragEventHandler = (e) => { - e.preventDefault(); - e.stopPropagation(); - setDragActive(false); - const file = e.dataTransfer.files?.[0]; - if (file) validateAndSetFile(file); - }; + const triggerFileDialog = () => fileInputRef.current?.click(); + + const validateAndSetFile = (file: File) => { + setValidationError(null); + + if (file.type !== 'application/pdf') { + setValidationError('PDF ํ˜•์‹์˜ ํŒŒ์ผ๋งŒ ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ์–ด์š”.'); + return; + } + if (file.size > 20 * 1024 * 1024) { + setValidationError( + 'ํŒŒ์ผ ์šฉ๋Ÿ‰์ด ๋„ˆ๋ฌด ์ปค์„œ ์—…๋กœ๋“œํ•  ์ˆ˜ ์—†์–ด์š”. (์ตœ๋Œ€ 20MB)' + ); + return; + } + setSelectedFile(file); + }; + + const onInputChange: React.ChangeEventHandler = (e) => { + const file = e.target.files?.[0]; + if (file) validateAndSetFile(file); + if (fileInputRef.current) fileInputRef.current.value = ''; + }; - const formatFileSize = (bytes: number) => { - const mb = bytes / (1024 * 1024); - return `${mb % 1 === 0 ? mb.toFixed(0) : mb.toFixed(1)}MB`; + const onDragOver: React.DragEventHandler = (e) => { + e.preventDefault(); + e.stopPropagation(); + setDragActive(true); + }; + + const onDragLeave: React.DragEventHandler = (e) => { + e.preventDefault(); + e.stopPropagation(); + setDragActive(false); + }; + + const onDrop: React.DragEventHandler = (e) => { + e.preventDefault(); + e.stopPropagation(); + setDragActive(false); + const file = e.dataTransfer.files?.[0]; + if (file) validateAndSetFile(file); + }; + + const formatFileSize = (bytes: number) => { + const mb = bytes / (1024 * 1024); + return `${mb % 1 === 0 ? mb.toFixed(0) : mb.toFixed(1)}MB`; + }; + + useEffect(() => { + if (!open) return; + const onKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Escape') onClose(); }; - useEffect(() => { - if (!open) return; - const onKeyDown = (e: KeyboardEvent) => { - if (e.key === 'Escape') onClose(); - }; - window.addEventListener('keydown', onKeyDown); - return () => window.removeEventListener('keydown', onKeyDown); - }, [open, onClose]); - - if (!open) return null; - - return ( -
+ window.addEventListener('keydown', onKeyDown); + return () => window.removeEventListener('keydown', onKeyDown); + }, [open, onClose]); + + if (!open) return null; + + const resetState = () => { + setSelectedFile(null); + setValidationError(null); + }; + + const handleUpload = async () => { + if (!selectedFile || isPending) return; + + try { + setValidationError(null); + + const title = selectedFile.name.replace(/\.pdf$/i, ''); + + const result = gradePdf({ + title, + file: selectedFile, + }); + + onClose(); + router.push('/report/loading'); + + await result; + + resetState(); + router.push('/report'); + } catch (e) { + console.error(e); + setValidationError( + '์—…๋กœ๋“œ ์ค‘ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์–ด์š”. ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.' + ); + } + }; + + const disabled = !selectedFile || !!validationError || isPending; + + return ( +
+
diff --git a/src/app/_components/landing/LandingBlackSection.tsx b/src/app/_components/landing/LandingBlackSection.tsx index c6d3398..5ac32db 100644 --- a/src/app/_components/landing/LandingBlackSection.tsx +++ b/src/app/_components/landing/LandingBlackSection.tsx @@ -13,7 +13,7 @@ const LandingBlackSection = () => {
- ์–ด์ฉŒ๊ตฌ์–ด์ฉŒ๊ตฌ์–ด์ฉŒ๊ตฌ์–ด์ฉŒ๊พธ์–ด + ์ •๋Ÿ‰ยท์ •์„ฑ ํ‰๊ฐ€ ์š”์†Œ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•œ ์‹ค์ „ํ˜• ์ง„๋‹จ ๊ฐ€์ด๋“œ
{ alt="ํ™ˆํ™”๋ฉด ์ด๋ฏธ์ง€" width={912} height={593} - className="mt-[60px] w-[90vw] max-w-[912px]" + className="mt-[60px] h-auto w-full max-w-[912px]" priority + unoptimized={true} /> @@ -36,16 +37,18 @@ const LandingBlackSection = () => {
- ์–ด์ฉŒ๊ตฌ์–ด์ฉŒ๊ตฌ์–ด์ฉŒ๊ตฌ์–ด์ฉŒ๊พธ์–ด + AI ์ง„๋‹จ ๊ธฐ๋ฐ˜์œผ๋กœ ๋‚ด ์‚ฌ์—…๊ณ„ํš์„œ์˜ ๊ฐ•ยท์•ฝ์ ์„ ์ •ํ™•ํ•˜๊ฒŒ ๋ถ„์„
ํ”ผ๋“œ๋ฐฑ ์ด๋ฏธ์ง€ @@ -58,7 +61,7 @@ const LandingBlackSection = () => {
- ์–ด์ฉŒ๊ตฌ์–ด์ฉŒ๊ตฌ์–ด์ฉŒ๊ตฌ์–ด์ฉŒ๊พธ์–ด + ๊ฒ€์ฆ๋œ ์ „๋ฌธ๊ฐ€์—๊ฒŒ ์ง์ ‘ ํ”ผ๋“œ๋ฐฑ์„ ๋ฐ›์•„ ๊ฐœ์„  ๋ฐฉํ–ฅ์„ ๋ช…ํ™•ํžˆ ์ œ์‹œ
@@ -67,24 +70,30 @@ const LandingBlackSection = () => { alt="๋žœ๋”ฉ ์ „๋ฌธ๊ฐ€ ์ด๋ฏธ์ง€" width={1176} height={200} - className="max-w-[1176px]" + className="h-auto max-w-[1176px] object-contain" + style={{ height: 'auto' }} priority + unoptimized={true} /> ๋žœ๋”ฉ ์ „๋ฌธ๊ฐ€ ์ด๋ฏธ์ง€ ๋žœ๋”ฉ ์ „๋ฌธ๊ฐ€ ์ด๋ฏธ์ง€
diff --git a/src/app/_components/landing/LandingChecklist.tsx b/src/app/_components/landing/LandingChecklist.tsx index ae355d3..d4e1490 100644 --- a/src/app/_components/landing/LandingChecklist.tsx +++ b/src/app/_components/landing/LandingChecklist.tsx @@ -3,7 +3,7 @@ import React from 'react'; const LandingChecklist = () => { return ( -
+
ํ•ญ๋ชฉ๋ณ„ ์ฒดํฌ๋ฆฌ์ŠคํŠธ{' '}
@@ -19,6 +19,7 @@ const LandingChecklist = () => { height={480} className="h-[480px] w-[368px]" priority + unoptimized={true} /> { height={480} className="h-[480px] w-[368px]" priority + unoptimized={true} /> { height={480} className="h-[480px] w-[368px]" priority + unoptimized={true} />
diff --git a/src/app/_components/landing/LandingRelation.tsx b/src/app/_components/landing/LandingRelation.tsx index ab7445b..b431e85 100644 --- a/src/app/_components/landing/LandingRelation.tsx +++ b/src/app/_components/landing/LandingRelation.tsx @@ -48,7 +48,7 @@ const LandingRelation = () => { priority /> -
+
๊ด€๋ จ ๊ธฐ๊ด€
@@ -64,13 +64,13 @@ const LandingRelation = () => { aria-label={`${logo.alt} ๊ณต์‹ ์‚ฌ์ดํŠธ๋กœ ์ด๋™ (์ƒˆ ์ฐฝ)`} className="block" > -
+
{logo.alt}
diff --git a/src/app/business/components/CheckList.tsx b/src/app/business/components/CheckList.tsx index 487ea73..def8966 100644 --- a/src/app/business/components/CheckList.tsx +++ b/src/app/business/components/CheckList.tsx @@ -1,5 +1,5 @@ 'use client'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useCallback } from 'react'; import Button from '@/app/_components/common/Button'; import Check from '@/assets/icons/white_check.svg'; import { useBusinessStore } from '@/store/business.store'; @@ -7,51 +7,88 @@ import sections from '@/data/sidebar.json'; import { CheckListResponse, Section } from '@/types/business/checklist.type'; import { usePostCheckList } from '@/hooks/mutation/usePostChecklist'; import { getSubSectionTypeFromNumber } from '@/lib/business/mappers/getSubsection'; +import { buildSubsectionRequest } from '@/lib/business/requestBuilder'; +import { useAuthStore } from '@/store/auth.store'; +import { + GUEST_DRAFT_KEY, + GUEST_PENDING_ACTION_KEY, + LOGIN_REDIRECT_KEY, +} from '@/lib/business/authKeys'; const CheckList = () => { const selected = useBusinessStore((s) => s.selectedItem); const planId = useBusinessStore((s) => s.planId); const saveAllItems = useBusinessStore((s) => s.saveAllItems); + const getItemContent = useBusinessStore((s) => s.getItemContent); + const updateItemContent = useBusinessStore((s) => s.updateItemContent); + const { isAuthenticated } = useAuthStore(); const { mutate: checkListConfirm, isPending } = usePostCheckList(); const [items, setItems] = useState< Array<{ title: string; content: string; - userChecked: boolean; - Checked: boolean; + checked: boolean; }> >([]); useEffect(() => { const data = sections as Section[]; + for (const section of data) { const found = section.items.find((it) => it.number === selected.number); if (found?.checklist) { + const itemContent = getItemContent(selected.number); + const savedChecks = itemContent.checks || []; + setItems( - found.checklist.map((e) => ({ + found.checklist.map((e, index) => ({ title: e.title, content: e.content, - userChecked: false, - Checked: false, + checked: savedChecks[index] || false, })) ); return; } } + setItems([]); - }, [selected.number]); + }, [selected.number, getItemContent]); + + const persistGuestDraft = useCallback(() => { + if (typeof window === 'undefined') return; + try { + const { contents, title } = useBusinessStore.getState(); + localStorage.setItem( + GUEST_DRAFT_KEY, + JSON.stringify({ contents, title }) + ); + localStorage.setItem(GUEST_PENDING_ACTION_KEY, 'save'); + const redirectPath = window.location.pathname + window.location.search; + sessionStorage.setItem( + LOGIN_REDIRECT_KEY, + redirectPath && redirectPath.length > 0 ? redirectPath : '/business' + ); + } catch (error) { + console.error('๋น„ํšŒ์› ์ดˆ์•ˆ ์ €์žฅ ์‹คํŒจ:', error); + } + }, []); - const toggleCheck = (idx: number) => { - setItems((prev) => - prev.map((item, i) => - i === idx ? { ...item, userChecked: !item.userChecked } : item - ) - ); - }; + const openLoginModal = useCallback(() => { + if (typeof window === 'undefined') return; + const win = window as Window & { openBusinessLoginModal?: () => void }; + win.openBusinessLoginModal?.(); + }, []); const handleCheck = async () => { - if (!planId || items.length === 0) return; + if (items.length === 0) return; + if (!isAuthenticated) { + persistGuestDraft(); + openLoginModal(); + return; + } + + if (!planId) return; await saveAllItems(planId); @@ -61,58 +98,63 @@ const CheckList = () => { return; } + const itemContent = getItemContent(selected.number); + const subsectionRequest = buildSubsectionRequest( + selected.number, + selected.title, + itemContent + ); + + const currentChecks = items.map((item) => item.checked); + const body: CheckListResponse = { subSectionType, - checks: items.map(() => false), + checks: currentChecks, meta: { author: '์ดํ˜ธ๊ทผ', createdAt: new Date().toISOString().slice(0, 10), }, - blocks: items.map((item) => ({ - meta: { title: item.title }, - content: [{ type: 'text', value: item.content }], - })), + blocks: subsectionRequest.blocks, }; checkListConfirm( { planId, body }, { onSuccess: (res) => { + const serverChecks = res.data || currentChecks; + setItems((prev) => prev.map((item, i) => ({ ...item, - Checked: res.checks?.[i] || false, + checked: serverChecks[i] || false, })) ); + + updateItemContent(selected.number, { + checks: serverChecks, + }); }, } ); }; return ( -
-
+
+
์ฒดํฌ๋ฆฌ์ŠคํŠธ
-
+
{items.map((item, i) => { - const isChecked = item.userChecked || item.Checked; - const isUserCheck = item.userChecked; + const isChecked = item.checked; return (
-
toggleCheck(i)} - > +
{isChecked ? ( -
+
) : ( @@ -130,7 +172,7 @@ const CheckList = () => {
{i < items.length - 1 && ( -
+
)}
); @@ -138,7 +180,7 @@ const CheckList = () => { diff --git a/src/app/business/components/editor/WriteFormToolbar.tsx b/src/app/business/components/editor/WriteFormToolbar.tsx index cac5230..c4d9191 100644 --- a/src/app/business/components/editor/WriteFormToolbar.tsx +++ b/src/app/business/components/editor/WriteFormToolbar.tsx @@ -1,4 +1,4 @@ -import { Editor } from '@tiptap/core'; +import { Editor, JSONContent } from '@tiptap/core'; import { useState, useRef } from 'react'; import ToolButton from './ToolButton'; import BoldIcon from '@/assets/icons/write-icons/bold.svg'; @@ -13,10 +13,18 @@ import Heading3Icon from '@/assets/icons/write-icons/heading3.svg'; import GrammerIcon from '@/assets/icons/write-icons/grammer.svg'; import GrammerActiveIcon from '@/assets/icons/write-icons/grammer-active.svg'; import TableGridSelector from './TableGridSelector'; +import { useAuthStore } from '@/store/auth.store'; +import { uploadImage } from '@/lib/imageUpload'; +import { getImageDimensions, clampImageDimensions } from '@/lib/getImageDimensions'; +import { getSelectionAvailableWidth } from '@/lib/business/editor/getSelectionAvailableWidth'; +import { ImageCommandAttributes } from '@/types/business/business.type'; interface WriteFormToolbarProps { activeEditor: Editor | null; - onImageClick: () => void; + editorItemName?: Editor | null; + editorOneLineIntro?: Editor | null; + defaultEditor: Editor | null; + onActiveEditorChange: (editor: Editor | null) => void; onSpellCheckClick: () => void; grammarActive: boolean; spellChecking: boolean; @@ -26,20 +34,178 @@ interface WriteFormToolbarProps { const WriteFormToolbar = ({ activeEditor, - onImageClick, + editorItemName, + editorOneLineIntro, + defaultEditor, + onActiveEditorChange, onSpellCheckClick, grammarActive, spellChecking, isSaving, lastSavedTime, }: WriteFormToolbarProps) => { + // ์•„์ดํ…œ๋ช… ๋˜๋Š” ํ•œ์ค„์†Œ๊ฐœ ์—๋””ํ„ฐ์ธ์ง€ ํ™•์ธ + const isSimpleEditor = activeEditor === editorItemName || activeEditor === editorOneLineIntro; + const { isAuthenticated } = useAuthStore(); const [showTableGrid, setShowTableGrid] = useState(false); + const spellButtonDisabled = spellChecking || !isAuthenticated; const tableButtonRef = useRef(null); + const fileInputRef = useRef(null); + + // ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ + const handleImageUpload = async (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + if (!file || !activeEditor) return; + + if (!file.type.startsWith('image/')) { + alert('์ด๋ฏธ์ง€ ํŒŒ์ผ๋งŒ ์—…๋กœ๋“œ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.'); + return; + } + + try { + const imageUrl = await uploadImage(file); + if (!imageUrl || !activeEditor) return; + + const { width, height } = await getImageDimensions(imageUrl); + const selectionWidth = getSelectionAvailableWidth(activeEditor); + const editorDom = activeEditor.view.dom as HTMLElement | null; + const maxWidth = selectionWidth ?? (editorDom ? editorDom.clientWidth - 48 : undefined); + const { width: clampedWidth, height: clampedHeight } = clampImageDimensions( + width, + height, + maxWidth + ); + + activeEditor.chain().focus().setImage({ + src: imageUrl, + width: clampedWidth ?? undefined, + height: clampedHeight ?? undefined, + } as ImageCommandAttributes).run(); + } catch (error) { + console.error('์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ์‹คํŒจ:', error); + alert('์ด๋ฏธ์ง€ ์—…๋กœ๋“œ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.'); + } finally { + if (fileInputRef.current) fileInputRef.current.value = ''; + } + }; + + const handleImageButtonClick = () => { + if (!activeEditor) { + if (defaultEditor && !defaultEditor.isDestroyed) { + defaultEditor.commands.focus(); + onActiveEditorChange(defaultEditor); + } + } + fileInputRef.current?.click(); + }; const handleTableClick = () => { setShowTableGrid(!showTableGrid); }; + const handleHeading = (level: 1 | 2 | 3) => { + if (!activeEditor) return; + const { state } = activeEditor; + const { from, to, empty } = state.selection; + + // ํ…์ŠคํŠธ๊ฐ€ ์„ ํƒ๋˜์–ด ์žˆ์œผ๋ฉด ์„ ํƒ ๋ถ€๋ถ„๋งŒ ํ—ค๋”ฉ์œผ๋กœ ๋ณ€ํ™˜ + if (!empty && from !== to) { + const $from = state.selection.$from; + + // ์„ ํƒํ•œ ํ…์ŠคํŠธ ์ถ”์ถœ + const selectedText = state.doc.textBetween(from, to); + if (!selectedText.trim()) return; + + // ๋ฌธ๋‹จ ์œ„์น˜ ์ฐพ๊ธฐ + let paragraphStart = from; + let paragraphBeforePos = -1; + + for (let d = $from.depth; d > 0; d--) { + const node = $from.node(d); + if (node.type.name === 'paragraph') { + paragraphBeforePos = $from.before(d); + paragraphStart = $from.start(d); + break; + } + } + + if (paragraphBeforePos === -1) { + activeEditor.chain().focus().toggleHeading({ level }).run(); + return; + } + + // ์„ ํƒํ•œ ๋ฒ”์œ„์˜ ์ฝ˜ํ…์ธ  ์ถ”์ถœ (๋งˆํฌ ํฌํ•จ) + const selectedSlice = state.doc.slice(from, to); + const selectedContentJSON = selectedSlice.content.toJSON(); + + // ๋ฌธ๋‹จ์˜ ๋ ์œ„์น˜ ์ฐพ๊ธฐ + const $to = state.selection.$to; + let paragraphEnd = to; + for (let d = $to.depth; d > 0; d--) { + const node = $to.node(d); + if (node.type.name === 'paragraph') { + paragraphEnd = $to.start(d) + node.nodeSize - 2; + break; + } + } + + // ์•ž๋’ค ์ฝ˜ํ…์ธ  ์ถ”์ถœ (๋งˆํฌ ํฌํ•จ) + const beforeSlice = state.doc.slice(paragraphStart, from); + const afterSlice = state.doc.slice(to, paragraphEnd); + const beforeContentJSON = beforeSlice.content.toJSON(); + const afterContentJSON = afterSlice.content.toJSON(); + + // ์•ž๋’ค ํ…์ŠคํŠธ ํ™•์ธ + const beforeText = state.doc.textBetween(paragraphStart, from); + const afterText = state.doc.textBetween(to, paragraphEnd); + + // ์ƒˆ ์ฝ˜ํ…์ธ  ๊ตฌ์„ฑ + const newContent: JSONContent[] = []; + + // ์•ž๋ถ€๋ถ„ ๋ฌธ๋‹จ (ํ…์ŠคํŠธ๊ฐ€ ์žˆ์„ ๋•Œ๋งŒ) + if (beforeText.trim()) { + newContent.push({ + type: 'paragraph', + content: Array.isArray(beforeContentJSON) && beforeContentJSON.length > 0 + ? beforeContentJSON + : [{ type: 'text', text: beforeText }], + }); + } + + // ํ—ค๋”ฉ + newContent.push({ + type: 'heading', + attrs: { level }, + content: Array.isArray(selectedContentJSON) && selectedContentJSON.length > 0 + ? selectedContentJSON + : [{ type: 'text', text: selectedText }], + }); + + // ๋’ท๋ถ€๋ถ„ ๋ฌธ๋‹จ (ํ…์ŠคํŠธ๊ฐ€ ์žˆ์„ ๋•Œ๋งŒ) + if (afterText.trim()) { + newContent.push({ + type: 'paragraph', + content: Array.isArray(afterContentJSON) && afterContentJSON.length > 0 + ? afterContentJSON + : [{ type: 'text', text: afterText }], + }); + } + + // ์ „์ฒด ๋ฌธ๋‹จ ์‚ญ์ œ ํ›„ ์ƒˆ ์ฝ˜ํ…์ธ ๋ฅผ ํ•œ ๋ฒˆ์— ์‚ฝ์ž… + if (newContent.length > 0) { + activeEditor + .chain() + .focus() + .deleteRange({ from: paragraphStart, to: paragraphEnd }) + .insertContentAt(paragraphBeforePos, newContent) + .run(); + } + } else { + // ํ…์ŠคํŠธ๊ฐ€ ์„ ํƒ๋˜์ง€ ์•Š์•˜์œผ๋ฉด ๊ธฐ๋ณธ ๋™์ž‘ + activeEditor.chain().focus().toggleHeading({ level }).run(); + } + }; + const handleTableSelect = (rows: number, cols: number) => { if (!activeEditor) return; const { state } = activeEditor; @@ -139,17 +305,20 @@ const WriteFormToolbar = ({ } active={!!activeEditor?.isActive('heading', { level: 1 })} - onClick={() => activeEditor?.chain().focus().toggleHeading({ level: 1 }).run()} + onClick={() => handleHeading(1)} + disabled={isSimpleEditor} /> } active={!!activeEditor?.isActive('heading', { level: 2 })} - onClick={() => activeEditor?.chain().focus().toggleHeading({ level: 2 }).run()} + onClick={() => handleHeading(2)} + disabled={isSimpleEditor} /> } active={!!activeEditor?.isActive('heading', { level: 3 })} - onClick={() => activeEditor?.chain().focus().toggleHeading({ level: 3 }).run()} + onClick={() => handleHeading(3)} + disabled={isSimpleEditor} />
@@ -157,8 +326,9 @@ const WriteFormToolbar = ({ ref={tableButtonRef} label={} onClick={handleTableClick} + disabled={isSimpleEditor} /> - {showTableGrid && ( + {showTableGrid && !isSimpleEditor && ( setShowTableGrid(false)} @@ -166,13 +336,25 @@ const WriteFormToolbar = ({ /> )}
- } onClick={onImageClick} /> + } + onClick={handleImageButtonClick} + disabled={isSimpleEditor || !isAuthenticated} + /> +