diff --git a/.github/workflows/frontend-ci.yml b/.github/workflows/frontend-ci.yml new file mode 100644 index 0000000..0c91180 --- /dev/null +++ b/.github/workflows/frontend-ci.yml @@ -0,0 +1,38 @@ +name: Frontend CI (Vite Build) + +on: + push: + branches: [ "main" ] # 귣ġ ̸ master ʿ + pull_request: + branches: [ "main" ] + +jobs: + build-check: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [20.x] # Vite 6.x ֽ Node (18, 20+) + + steps: + # 1. ڵ + - name: Checkout code + uses: actions/checkout@v4 + + # 2. Node.js + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + + # 3. ġ + # package-lock.json ִٸ npm ci մϴ. + # lock ٸ npm install ؼ ϼ. + - name: Install dependencies + run: npm ci + + # 4. Ȯ (ٽ ܰ) + # Vite 尡 ϸ ⼭ , PR ɴϴ. + - name: Build Project + run: npm run build \ No newline at end of file diff --git a/.screen-graph.json b/.screen-graph.json new file mode 100644 index 0000000..24122fa --- /dev/null +++ b/.screen-graph.json @@ -0,0 +1,1267 @@ +{ + "nodes": [ + { + "id": "src/screens/Desktop/Desktop.jsx", + "label": "Desktop", + "routes": [ + "/*", + "/desktop" + ], + "dataModelId": "45:2", + "isRoot": true + }, + { + "id": "src/screens/CommunityPage/CommunityPage.jsx", + "label": "CommunityPage", + "routes": [ + "/communitypage" + ], + "dataModelId": "1:6" + }, + { + "id": "src/screens/MyPage/MyPage.jsx", + "label": "MyPage", + "routes": [ + "/mypage" + ] + }, + { + "id": "src/screens/RecipePage/RecipePage.jsx", + "label": "RecipePage", + "routes": [ + "/recipepage/:id" + ] + }, + { + "id": "src/screens/CommunityContent/CommunityContent.jsx", + "label": "CommunityContent", + "routes": [ + "/communitycontentpage/:id" + ], + "dataModelId": "107:399" + }, + { + "id": "src/screens/AddIngredientPage/AddIngredientPage.jsx", + "label": "AddIngredientPage", + "routes": [ + "/addingredient" + ] + }, + { + "id": "src/screens/CreatePostPage/CreatePostPage.jsx", + "label": "CreatePostPage", + "routes": [ + "/createpost" + ] + }, + { + "id": "src/screens/MenuRecommendationPage/MenuRecommendationPage.jsx", + "label": "MenuRecommendationPage", + "routes": [ + "/menurecommendation" + ] + } + ], + "edges": [ + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:74:14-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 홈\r\n ", + "line": 74, + "endLine": 80, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:81:14-to-src\\screens\\MenuRecommendationPage\\MenuRecommendationPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MenuRecommendationPage\\MenuRecommendationPage.jsx", + "data": { + "viaRoute": "/menurecommendation", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 메뉴 추천\r\n ", + "line": 81, + "endLine": 87, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:88:14-to-src\\screens\\CommunityPage\\CommunityPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\CommunityPage\\CommunityPage.jsx", + "data": { + "viaRoute": "/communitypage", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 재료 나눔 게시판\r\n ", + "line": 88, + "endLine": 94, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:95:14-to-src\\screens\\MyPage\\MyPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MyPage\\MyPage.jsx", + "data": { + "viaRoute": "/mypage", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 마이페이지\r\n ", + "line": 95, + "endLine": 101, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:104:10-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "\r\n \r\n ", + "line": 104, + "endLine": 110, + "column": 10, + "endColumn": 17, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:113:8-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "\r\n

\r\n 알고리즘 \r\n 셰프\r\n

\r\n ", + "line": 113, + "endLine": 118, + "column": 8, + "endColumn": 15, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:121:10-to-src\\screens\\MyPage\\MyPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MyPage\\MyPage.jsx", + "data": { + "viaRoute": "/mypage", + "trigger": { + "element": "\r\n
마이페이지
\r\n ", + "line": 121, + "endLine": 123, + "column": 10, + "endColumn": 17, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\PreferencesPopup\\PreferencesPopup.jsx:87:8-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "navigate(\"/desktop\")", + "line": 87, + "endLine": 87, + "column": 8, + "endColumn": 28, + "sourceFile": "src\\components\\PreferencesPopup\\PreferencesPopup.jsx" + } + } + }, + { + "id": "src\\screens\\Desktop\\sections\\IngredientInventory\\IngredientInventory.jsx:134:8-to-src\\screens\\AddIngredientPage\\AddIngredientPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\AddIngredientPage\\AddIngredientPage.jsx", + "data": { + "viaRoute": "/addingredient", + "trigger": { + "element": "\r\n
\r\n
\r\n
추가하기
\r\n
\r\n\r\n
\r\n \r\n
\r\n
\r\n ", + "line": 134, + "endLine": 148, + "column": 8, + "endColumn": 15, + "sourceFile": "src\\screens\\Desktop\\sections\\IngredientInventory\\IngredientInventory.jsx" + } + } + }, + { + "id": "src\\screens\\Desktop\\sections\\MenuInventory\\MenuInventory.jsx:57:8-to-src\\screens\\MenuRecommendationPage\\MenuRecommendationPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MenuRecommendationPage\\MenuRecommendationPage.jsx", + "data": { + "viaRoute": "/menurecommendation", + "trigger": { + "element": "\r\n \r\n ", + "line": 57, + "endLine": 59, + "column": 8, + "endColumn": 15, + "sourceFile": "src\\screens\\Desktop\\sections\\MenuInventory\\MenuInventory.jsx" + } + } + }, + { + "id": "src\\screens\\Desktop\\sections\\MenuInventory\\MenuInventory.jsx:60:8-to-src\\screens\\MenuRecommendationPage\\MenuRecommendationPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MenuRecommendationPage\\MenuRecommendationPage.jsx", + "data": { + "viaRoute": "/menurecommendation", + "trigger": { + "element": "\r\n 추천받기 →\r\n ", + "line": 60, + "endLine": 62, + "column": 8, + "endColumn": 15, + "sourceFile": "src\\screens\\Desktop\\sections\\MenuInventory\\MenuInventory.jsx" + } + } + }, + { + "id": "src\\screens\\Desktop\\sections\\Community\\Community.jsx:18:6-to-src\\screens\\CommunityPage\\CommunityPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\CommunityPage\\CommunityPage.jsx", + "data": { + "viaRoute": "/communitypage", + "trigger": { + "element": "\r\n
재료 나눔 게시판
\r\n ", + "line": 18, + "endLine": 20, + "column": 6, + "endColumn": 13, + "sourceFile": "src\\screens\\Desktop\\sections\\Community\\Community.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:74:14-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 홈\r\n ", + "line": 74, + "endLine": 80, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:81:14-to-src\\screens\\MenuRecommendationPage\\MenuRecommendationPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MenuRecommendationPage\\MenuRecommendationPage.jsx", + "data": { + "viaRoute": "/menurecommendation", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 메뉴 추천\r\n ", + "line": 81, + "endLine": 87, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:88:14-to-src\\screens\\CommunityPage\\CommunityPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\CommunityPage\\CommunityPage.jsx", + "data": { + "viaRoute": "/communitypage", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 재료 나눔 게시판\r\n ", + "line": 88, + "endLine": 94, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:95:14-to-src\\screens\\MyPage\\MyPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MyPage\\MyPage.jsx", + "data": { + "viaRoute": "/mypage", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 마이페이지\r\n ", + "line": 95, + "endLine": 101, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:104:10-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "\r\n \r\n ", + "line": 104, + "endLine": 110, + "column": 10, + "endColumn": 17, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:113:8-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "\r\n

\r\n 알고리즘 \r\n 셰프\r\n

\r\n ", + "line": 113, + "endLine": 118, + "column": 8, + "endColumn": 15, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:121:10-to-src\\screens\\MyPage\\MyPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MyPage\\MyPage.jsx", + "data": { + "viaRoute": "/mypage", + "trigger": { + "element": "\r\n
마이페이지
\r\n ", + "line": 121, + "endLine": 123, + "column": 10, + "endColumn": 17, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\PreferencesPopup\\PreferencesPopup.jsx:87:8-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "navigate(\"/desktop\")", + "line": 87, + "endLine": 87, + "column": 8, + "endColumn": 28, + "sourceFile": "src\\components\\PreferencesPopup\\PreferencesPopup.jsx" + } + } + }, + { + "id": "src\\screens\\CommunityPage\\sections\\Communitypage\\Communitypage.jsx:54:4-to-src\\screens\\CreatePostPage\\CreatePostPage.jsx", + "source": "src\\screens\\CommunityPage\\CommunityPage.jsx", + "target": "src\\screens\\CreatePostPage\\CreatePostPage.jsx", + "data": { + "viaRoute": "/createpost", + "trigger": { + "element": "navigate(\"/createpost\")", + "line": 54, + "endLine": 54, + "column": 4, + "endColumn": 27, + "sourceFile": "src\\screens\\CommunityPage\\sections\\Communitypage\\Communitypage.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:74:14-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 홈\r\n ", + "line": 74, + "endLine": 80, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:81:14-to-src\\screens\\MenuRecommendationPage\\MenuRecommendationPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MenuRecommendationPage\\MenuRecommendationPage.jsx", + "data": { + "viaRoute": "/menurecommendation", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 메뉴 추천\r\n ", + "line": 81, + "endLine": 87, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:88:14-to-src\\screens\\CommunityPage\\CommunityPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\CommunityPage\\CommunityPage.jsx", + "data": { + "viaRoute": "/communitypage", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 재료 나눔 게시판\r\n ", + "line": 88, + "endLine": 94, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:95:14-to-src\\screens\\MyPage\\MyPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MyPage\\MyPage.jsx", + "data": { + "viaRoute": "/mypage", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 마이페이지\r\n ", + "line": 95, + "endLine": 101, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:104:10-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "\r\n \r\n ", + "line": 104, + "endLine": 110, + "column": 10, + "endColumn": 17, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:113:8-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "\r\n

\r\n 알고리즘 \r\n 셰프\r\n

\r\n ", + "line": 113, + "endLine": 118, + "column": 8, + "endColumn": 15, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:121:10-to-src\\screens\\MyPage\\MyPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MyPage\\MyPage.jsx", + "data": { + "viaRoute": "/mypage", + "trigger": { + "element": "\r\n
마이페이지
\r\n ", + "line": 121, + "endLine": 123, + "column": 10, + "endColumn": 17, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\PreferencesPopup\\PreferencesPopup.jsx:87:8-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "navigate(\"/desktop\")", + "line": 87, + "endLine": 87, + "column": 8, + "endColumn": 28, + "sourceFile": "src\\components\\PreferencesPopup\\PreferencesPopup.jsx" + } + } + }, + { + "id": "src\\components\\PreferencesPopup\\PreferencesPopup.jsx:87:8-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "navigate(\"/desktop\")", + "line": 87, + "endLine": 87, + "column": 8, + "endColumn": 28, + "sourceFile": "src\\components\\PreferencesPopup\\PreferencesPopup.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:74:14-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 홈\r\n ", + "line": 74, + "endLine": 80, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:81:14-to-src\\screens\\MenuRecommendationPage\\MenuRecommendationPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MenuRecommendationPage\\MenuRecommendationPage.jsx", + "data": { + "viaRoute": "/menurecommendation", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 메뉴 추천\r\n ", + "line": 81, + "endLine": 87, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:88:14-to-src\\screens\\CommunityPage\\CommunityPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\CommunityPage\\CommunityPage.jsx", + "data": { + "viaRoute": "/communitypage", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 재료 나눔 게시판\r\n ", + "line": 88, + "endLine": 94, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:95:14-to-src\\screens\\MyPage\\MyPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MyPage\\MyPage.jsx", + "data": { + "viaRoute": "/mypage", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 마이페이지\r\n ", + "line": 95, + "endLine": 101, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:104:10-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "\r\n \r\n ", + "line": 104, + "endLine": 110, + "column": 10, + "endColumn": 17, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:113:8-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "\r\n

\r\n 알고리즘 \r\n 셰프\r\n

\r\n ", + "line": 113, + "endLine": 118, + "column": 8, + "endColumn": 15, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:121:10-to-src\\screens\\MyPage\\MyPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MyPage\\MyPage.jsx", + "data": { + "viaRoute": "/mypage", + "trigger": { + "element": "\r\n
마이페이지
\r\n ", + "line": 121, + "endLine": 123, + "column": 10, + "endColumn": 17, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\PreferencesPopup\\PreferencesPopup.jsx:87:8-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "navigate(\"/desktop\")", + "line": 87, + "endLine": 87, + "column": 8, + "endColumn": 28, + "sourceFile": "src\\components\\PreferencesPopup\\PreferencesPopup.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:74:14-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 홈\r\n ", + "line": 74, + "endLine": 80, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:81:14-to-src\\screens\\MenuRecommendationPage\\MenuRecommendationPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MenuRecommendationPage\\MenuRecommendationPage.jsx", + "data": { + "viaRoute": "/menurecommendation", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 메뉴 추천\r\n ", + "line": 81, + "endLine": 87, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:88:14-to-src\\screens\\CommunityPage\\CommunityPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\CommunityPage\\CommunityPage.jsx", + "data": { + "viaRoute": "/communitypage", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 재료 나눔 게시판\r\n ", + "line": 88, + "endLine": 94, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:95:14-to-src\\screens\\MyPage\\MyPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MyPage\\MyPage.jsx", + "data": { + "viaRoute": "/mypage", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 마이페이지\r\n ", + "line": 95, + "endLine": 101, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:104:10-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "\r\n \r\n ", + "line": 104, + "endLine": 110, + "column": 10, + "endColumn": 17, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:113:8-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "\r\n

\r\n 알고리즘 \r\n 셰프\r\n

\r\n ", + "line": 113, + "endLine": 118, + "column": 8, + "endColumn": 15, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:121:10-to-src\\screens\\MyPage\\MyPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MyPage\\MyPage.jsx", + "data": { + "viaRoute": "/mypage", + "trigger": { + "element": "\r\n
마이페이지
\r\n ", + "line": 121, + "endLine": 123, + "column": 10, + "endColumn": 17, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\PreferencesPopup\\PreferencesPopup.jsx:87:8-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "navigate(\"/desktop\")", + "line": 87, + "endLine": 87, + "column": 8, + "endColumn": 28, + "sourceFile": "src\\components\\PreferencesPopup\\PreferencesPopup.jsx" + } + } + }, + { + "id": "src\\screens\\AddIngredientPage\\AddIngredientPage.jsx:170:8-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\AddIngredientPage\\AddIngredientPage.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "navigate(\"/desktop\")", + "line": 170, + "endLine": 170, + "column": 8, + "endColumn": 28, + "sourceFile": "src\\screens\\AddIngredientPage\\AddIngredientPage.jsx" + } + } + }, + { + "id": "src\\screens\\AddIngredientPage\\AddIngredientPage.jsx:271:12-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\AddIngredientPage\\AddIngredientPage.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "\r\n 취소\r\n ", + "line": 271, + "endLine": 273, + "column": 12, + "endColumn": 19, + "sourceFile": "src\\screens\\AddIngredientPage\\AddIngredientPage.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:74:14-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 홈\r\n ", + "line": 74, + "endLine": 80, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:81:14-to-src\\screens\\MenuRecommendationPage\\MenuRecommendationPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MenuRecommendationPage\\MenuRecommendationPage.jsx", + "data": { + "viaRoute": "/menurecommendation", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 메뉴 추천\r\n ", + "line": 81, + "endLine": 87, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:88:14-to-src\\screens\\CommunityPage\\CommunityPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\CommunityPage\\CommunityPage.jsx", + "data": { + "viaRoute": "/communitypage", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 재료 나눔 게시판\r\n ", + "line": 88, + "endLine": 94, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:95:14-to-src\\screens\\MyPage\\MyPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MyPage\\MyPage.jsx", + "data": { + "viaRoute": "/mypage", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 마이페이지\r\n ", + "line": 95, + "endLine": 101, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:104:10-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "\r\n \r\n ", + "line": 104, + "endLine": 110, + "column": 10, + "endColumn": 17, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:113:8-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "\r\n

\r\n 알고리즘 \r\n 셰프\r\n

\r\n ", + "line": 113, + "endLine": 118, + "column": 8, + "endColumn": 15, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:121:10-to-src\\screens\\MyPage\\MyPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MyPage\\MyPage.jsx", + "data": { + "viaRoute": "/mypage", + "trigger": { + "element": "\r\n
마이페이지
\r\n ", + "line": 121, + "endLine": 123, + "column": 10, + "endColumn": 17, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\PreferencesPopup\\PreferencesPopup.jsx:87:8-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "navigate(\"/desktop\")", + "line": 87, + "endLine": 87, + "column": 8, + "endColumn": 28, + "sourceFile": "src\\components\\PreferencesPopup\\PreferencesPopup.jsx" + } + } + }, + { + "id": "src\\screens\\CreatePostPage\\CreatePostPage.jsx:16:6-to-src\\screens\\CommunityPage\\CommunityPage.jsx", + "source": "src\\screens\\CreatePostPage\\CreatePostPage.jsx", + "target": "src\\screens\\CommunityPage\\CommunityPage.jsx", + "data": { + "viaRoute": "/communitypage", + "trigger": { + "element": "navigate(\"/communitypage\")", + "line": 16, + "endLine": 16, + "column": 6, + "endColumn": 32, + "sourceFile": "src\\screens\\CreatePostPage\\CreatePostPage.jsx" + } + } + }, + { + "id": "src\\screens\\CreatePostPage\\CreatePostPage.jsx:49:4-to-src\\screens\\CommunityPage\\CommunityPage.jsx", + "source": "src\\screens\\CreatePostPage\\CreatePostPage.jsx", + "target": "src\\screens\\CommunityPage\\CommunityPage.jsx", + "data": { + "viaRoute": "/communitypage", + "trigger": { + "element": "navigate(\"/communitypage\")", + "line": 49, + "endLine": 49, + "column": 4, + "endColumn": 30, + "sourceFile": "src\\screens\\CreatePostPage\\CreatePostPage.jsx" + } + } + }, + { + "id": "src\\screens\\CreatePostPage\\CreatePostPage.jsx:88:12-to-src\\screens\\CommunityPage\\CommunityPage.jsx", + "source": "src\\screens\\CreatePostPage\\CreatePostPage.jsx", + "target": "src\\screens\\CommunityPage\\CommunityPage.jsx", + "data": { + "viaRoute": "/communitypage", + "trigger": { + "element": "\r\n 취소\r\n ", + "line": 88, + "endLine": 90, + "column": 12, + "endColumn": 19, + "sourceFile": "src\\screens\\CreatePostPage\\CreatePostPage.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:74:14-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 홈\r\n ", + "line": 74, + "endLine": 80, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:81:14-to-src\\screens\\MenuRecommendationPage\\MenuRecommendationPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MenuRecommendationPage\\MenuRecommendationPage.jsx", + "data": { + "viaRoute": "/menurecommendation", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 메뉴 추천\r\n ", + "line": 81, + "endLine": 87, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:88:14-to-src\\screens\\CommunityPage\\CommunityPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\CommunityPage\\CommunityPage.jsx", + "data": { + "viaRoute": "/communitypage", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 재료 나눔 게시판\r\n ", + "line": 88, + "endLine": 94, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:95:14-to-src\\screens\\MyPage\\MyPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MyPage\\MyPage.jsx", + "data": { + "viaRoute": "/mypage", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 마이페이지\r\n ", + "line": 95, + "endLine": 101, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:104:10-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "\r\n \r\n ", + "line": 104, + "endLine": 110, + "column": 10, + "endColumn": 17, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:113:8-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "\r\n

\r\n 알고리즘 \r\n 셰프\r\n

\r\n ", + "line": 113, + "endLine": 118, + "column": 8, + "endColumn": 15, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:121:10-to-src\\screens\\MyPage\\MyPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MyPage\\MyPage.jsx", + "data": { + "viaRoute": "/mypage", + "trigger": { + "element": "\r\n
마이페이지
\r\n ", + "line": 121, + "endLine": 123, + "column": 10, + "endColumn": 17, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\PreferencesPopup\\PreferencesPopup.jsx:87:8-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "navigate(\"/desktop\")", + "line": 87, + "endLine": 87, + "column": 8, + "endColumn": 28, + "sourceFile": "src\\components\\PreferencesPopup\\PreferencesPopup.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:74:14-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 홈\r\n ", + "line": 74, + "endLine": 80, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:81:14-to-src\\screens\\MenuRecommendationPage\\MenuRecommendationPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MenuRecommendationPage\\MenuRecommendationPage.jsx", + "data": { + "viaRoute": "/menurecommendation", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 메뉴 추천\r\n ", + "line": 81, + "endLine": 87, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:88:14-to-src\\screens\\CommunityPage\\CommunityPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\CommunityPage\\CommunityPage.jsx", + "data": { + "viaRoute": "/communitypage", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 재료 나눔 게시판\r\n ", + "line": 88, + "endLine": 94, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:95:14-to-src\\screens\\MyPage\\MyPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MyPage\\MyPage.jsx", + "data": { + "viaRoute": "/mypage", + "trigger": { + "element": " setShowMenu(false)}\r\n >\r\n 마이페이지\r\n ", + "line": 95, + "endLine": 101, + "column": 14, + "endColumn": 21, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:104:10-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "\r\n \r\n ", + "line": 104, + "endLine": 110, + "column": 10, + "endColumn": 17, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:113:8-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "\r\n

\r\n 알고리즘 \r\n 셰프\r\n

\r\n ", + "line": 113, + "endLine": 118, + "column": 8, + "endColumn": 15, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:121:10-to-src\\screens\\MyPage\\MyPage.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\MyPage\\MyPage.jsx", + "data": { + "viaRoute": "/mypage", + "trigger": { + "element": "\r\n
마이페이지
\r\n ", + "line": 121, + "endLine": 123, + "column": 10, + "endColumn": 17, + "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx" + } + } + }, + { + "id": "src\\components\\PreferencesPopup\\PreferencesPopup.jsx:87:8-to-src\\screens\\Desktop\\Desktop.jsx", + "source": "src\\screens\\Desktop\\Desktop.jsx", + "target": "src\\screens\\Desktop\\Desktop.jsx", + "data": { + "viaRoute": "/desktop", + "trigger": { + "element": "navigate(\"/desktop\")", + "line": 87, + "endLine": 87, + "column": 8, + "endColumn": 28, + "sourceFile": "src\\components\\PreferencesPopup\\PreferencesPopup.jsx" + } + } + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index f665eb6..6486779 100644 --- a/README.md +++ b/README.md @@ -1 +1,124 @@ -AlgorithmChef_Frontend +# 알고리즘 셰프 - Algorithm Chef + +환영합니다! 이 프로젝트는 Anima에 의해 자동으로 생성된 React 애플리케이션입니다. +"알고리즘 셰프"는 사용자의 냉장고 속 식재료와 개인 성향을 기반으로 맞춤형 레시피를 추천하고, 식재료 나눔 커뮤니티를 제공하는 서비스입니다. + +## 시작하기 + +> **전제 조건:** +> 다음 단계를 수행하려면 시스템에 [NodeJS](https://nodejs.org/en/)가 설치되어 있어야 합니다. + +프로젝트를 시작하려면 먼저 다음 명령어로 의존성을 설치해야 합니다: + +```bash +npm install +``` + +그 다음, 다음 명령어로 개발 버전을 실행할 수 있습니다: + +```bash +npm run dev +``` + +몇 초 후, 프로젝트는 [http://localhost:5173/](http://localhost:5173/) 주소에서 접근할 수 있습니다. + +결과에 만족하면, 다음 명령어로 릴리스용 프로젝트를 빌드할 수 있습니다: + +```bash +npm run build +``` + +## 주요 기능 + +* **반응형 디자인**: 모든 페이지는 다양한 화면 크기(데스크톱, 태블릿, 모바일)에 최적화되어 있습니다. +* **통합 헤더**: 모든 페이지에 일관된 탐색 및 인증 기능을 제공하는 단일 헤더 컴포넌트가 적용되었습니다. +* **메뉴 내비게이션**: 헤더 좌측의 햄버거 메뉴를 통해 주요 페이지(홈, 메뉴 추천, 재료 나눔 게시판, 마이페이지)로 쉽게 이동할 수 있습니다. +* **사용자 인증**: + * 로그인 및 회원가입 팝업 시스템. + * 성별, 생년월일 입력 및 성향(건강 목표, 알레르기, 선호/비선호 재료, 선호 요리, 매운맛 선호도, 알림 설정) 선택 기능. + * 로그인 시 사용자 이름 표시 및 로그아웃 버튼으로 전환. + * 아이디/비밀번호 유효성 검사 및 중복 아이디 확인 (현재 `localStorage` 기반 목업). +* **나의 냉장고**: + * 식재료 목록을 카테고리별로 필터링하고 검색할 수 있습니다. + * 새로운 식재료를 추가하거나 영수증을 등록할 수 있는 기능 (팝업 및 전용 페이지). +* **메뉴 추천**: + * Gemini API와 연동될 검색창 (현재 목업). + * 식재료 기반 및 성향 기반 자동 메뉴 추천 기능. + * 레시피 카드를 최대 12개까지 표시하며, 좌우 스크롤 버튼으로 페이지 이동 가능. + * 각 레시피 카드를 클릭하면 상세 레시피 페이지로 이동합니다. +* **재료 나눔 게시판**: + * 게시글 목록을 페이지네이션(최대 5페이지)으로 탐색할 수 있습니다. + * 로그인한 사용자만 새 글을 작성할 수 있습니다. + * 각 게시글은 고유한 댓글 기능을 가지며, 댓글은 `localStorage`에 저장됩니다. + * 게시글 상세 페이지에서 현재 게시글의 앞뒤 2개씩, 총 4개의 관련 게시글을 표시합니다. +* **마이페이지**: + * 로그인 상태에 따라 사용자 성향 정보를 표시하거나 로그인 요청 메시지를 출력합니다. + * 사용자의 건강 목표, 알레르기, 선호/비선호 재료, 선호 요리, 매운맛 선호도, 알림 설정을 확인할 수 있습니다. + +## 백엔드 연동 가이드 + +이 프로젝트는 프론트엔드 기능 구현에 중점을 두었으며, 모든 데이터는 현재 `localStorage`를 사용하여 목업(mockup)으로 처리됩니다. 실제 백엔드와 연동하려면 다음 지침을 따르세요: + +1. **API 클라이언트 설치**: `axios`와 같은 HTTP 클라이언트 라이브러리를 설치합니다. + ```bash + npm install axios + ``` + +2. **API 엔드포인트 정의**: 백엔드에서 제공하는 API 엔드포인트를 정의합니다. 예: + * `GET /api/users/{username}/ingredients` (사용자 냉장고 재료) + * `GET /api/ingredients/all` (모든 식재료 DB) + * `POST /api/ingredients` (식재료 추가) + * `POST /api/login` (로그인) + * `POST /api/signup` (회원가입) + * `POST /api/users/{username}/preferences` (사용자 성향 저장) + * `GET /api/users/{username}/preferences` (사용자 성향 조회) + * `GET /api/posts` (모든 게시글) + * `POST /api/posts` (새 게시글 작성) + * `GET /api/posts/{id}/comments` (게시글 댓글 조회) + * `POST /api/posts/{id}/comments` (댓글 작성) + * `GET /api/recommend/ingredients` (식재료 기반 레시피 추천) + * `GET /api/recommend/tendencies` (성향 기반 레시피 추천) + * `POST /api/gemini-search` (Gemini API 검색) + +3. **목업 데이터 교체**: 프로젝트 코드 내의 `// TODO: Backend Integration: Replace with API call` 주석을 찾아 `localStorage`를 사용하는 목업 로직을 실제 `axios` 호출로 교체합니다. + + **예시 (로그인):** + ```javascript + // src/components/LoginPopup/LoginPopup.jsx + const handleLogin = async (e) => { + e.preventDefault(); + try { + const response = await axios.post('/api/login', { username, password }); + login(response.data.user); // Assuming backend returns user data + onClose(); + } catch (error) { + alert("아이디 또는 비밀번호가 일치하지 않습니다."); + console.error("Login error:", error); + } + }; + ``` + + **예시 (게시글 목록):** + ```javascript + // src/screens/CommunityPage/sections/Communitypage/Communitypage.jsx + useEffect(() => { + const fetchPosts = async () => { + try { + const response = await axios.get('/api/posts'); + setAllPosts(response.data); + } catch (error) { + console.error("Failed to fetch posts:", error); + setAllPosts(DEFAULT_POSTS); // Fallback to default posts + } + }; + fetchPosts(); + }, []); + ``` + +4. **환경 변수 설정**: API의 기본 URL 등은 `.env` 파일을 사용하여 관리하는 것이 좋습니다. + ``` + VITE_API_BASE_URL=http://localhost:8080/api + ``` + 그리고 코드에서는 `import.meta.env.VITE_API_BASE_URL`과 같이 접근합니다. + +이 가이드를 통해 "알고리즘 셰프" 프론트엔드를 실제 백엔드 서비스와 성공적으로 연동할 수 있을 것입니다. diff --git a/eslint.config.js b/eslint.config.js deleted file mode 100644 index cee1e2c..0000000 --- a/eslint.config.js +++ /dev/null @@ -1,29 +0,0 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import { defineConfig, globalIgnores } from 'eslint/config' - -export default defineConfig([ - globalIgnores(['dist']), - { - files: ['**/*.{js,jsx}'], - extends: [ - js.configs.recommended, - reactHooks.configs['recommended-latest'], - reactRefresh.configs.vite, - ], - languageOptions: { - ecmaVersion: 2020, - globals: globals.browser, - parserOptions: { - ecmaVersion: 'latest', - ecmaFeatures: { jsx: true }, - sourceType: 'module', - }, - }, - rules: { - 'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }], - }, - }, -]) diff --git a/index.html b/index.html index d7c6abc..ad21cc5 100644 --- a/index.html +++ b/index.html @@ -1,13 +1,36 @@ - + - - - - algorithmcheffront + + 알고리즘 셰프 - Algorithm Chef + + + -
- +
+ diff --git a/package-lock.json b/package-lock.json index 8a0e906..fbb37b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,27 +1,530 @@ { - "name": "algorithmcheffront", - "version": "0.0.0", + "name": "anima-project", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "algorithmcheffront", - "version": "0.0.0", + "name": "anima-project", + "version": "1.0.0", "dependencies": { - "react": "^19.1.1", - "react-dom": "^19.1.1" + "axios": "^1.13.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-icons": "^5.5.0", + "react-router-dom": "^6.8.1" }, "devDependencies": { - "@eslint/js": "^9.36.0", - "@types/react": "^19.1.16", - "@types/react-dom": "^19.1.9", - "@vitejs/plugin-react": "^5.0.4", - "babel-plugin-react-compiler": "^19.1.0-rc.3", - "eslint": "^9.36.0", - "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-react-refresh": "^0.4.22", - "globals": "^16.4.0", - "vite": "^7.1.7" + "@animaapp/vite-plugin-screen-graph": "^0.1.5", + "@vitejs/plugin-react": "4.3.4", + "esbuild": "0.24.0", + "globals": "15.12.0", + "vite": "6.0.4" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/@animaapp/vite-plugin-screen-graph/-/vite-plugin-screen-graph-0.1.11.tgz", + "integrity": "sha512-fTq3NEQaJMqVxY87iSbB18FDmotkXmfP+80Ch9v8MuwI4SB58KvGqrpLt1TuyM3kTLpTvZWSkkDMTOved8m0cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.24.1", + "@babel/traverse": "^7.24.1", + "@babel/types": "^7.27.0", + "estree-walker": "^3.0.3", + "fast-glob": "^3.2.12", + "vite": "^5.0.0" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/@animaapp/vite-plugin-screen-graph/node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } } }, "node_modules/@babel/code-frame": { @@ -308,9 +811,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", "cpu": [ "ppc64" ], @@ -325,9 +828,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", "cpu": [ "arm" ], @@ -342,9 +845,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", "cpu": [ "arm64" ], @@ -359,9 +862,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", "cpu": [ "x64" ], @@ -376,9 +879,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", "cpu": [ "arm64" ], @@ -393,9 +896,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", "cpu": [ "x64" ], @@ -410,9 +913,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", "cpu": [ "arm64" ], @@ -427,9 +930,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", "cpu": [ "x64" ], @@ -444,9 +947,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", "cpu": [ "arm" ], @@ -461,9 +964,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", "cpu": [ "arm64" ], @@ -478,9 +981,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", "cpu": [ "ia32" ], @@ -495,9 +998,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", "cpu": [ "loong64" ], @@ -512,9 +1015,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", "cpu": [ "mips64el" ], @@ -529,9 +1032,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", "cpu": [ "ppc64" ], @@ -546,9 +1049,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", "cpu": [ "riscv64" ], @@ -563,9 +1066,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", "cpu": [ "s390x" ], @@ -580,9 +1083,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", "cpu": [ "x64" ], @@ -596,27 +1099,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", "cpu": [ "x64" ], @@ -631,9 +1117,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", "cpu": [ "arm64" ], @@ -648,9 +1134,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", "cpu": [ "x64" ], @@ -664,27 +1150,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", "cpu": [ "x64" ], @@ -699,9 +1168,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", "cpu": [ "arm64" ], @@ -716,9 +1185,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", "cpu": [ "ia32" ], @@ -733,9 +1202,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", "cpu": [ "x64" ], @@ -749,215 +1218,6 @@ "node": ">=18" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", - "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "9.39.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", - "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.4.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -1008,17 +1268,57 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.43", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.43.tgz", - "integrity": "sha512-5Uxg7fQUCmfhax7FJke2+8B6cqgeUJUD9o2uXIKXhD+mG0mL6NObmVoi9wXEU1tY89mZKgAYA6fTbftx3q2ZPQ==", + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@remix-run/router": { + "version": "1.23.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.1.tgz", + "integrity": "sha512-vDbaOzF7yT2Qs4vO6XV1MHcJv+3dgR1sT+l3B8xxOVhUC336prMvqrvsLL/9Dnw2xr6Qhz4J0dmS0llNAbnUmQ==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.1.tgz", - "integrity": "sha512-bxZtughE4VNVJlL1RdoSE545kc4JxL7op57KKoi59/gwuU5rV6jLWFXXc8jwgFoT6vtj+ZjO+Z2C5nrY0Cl6wA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", "cpu": [ "arm" ], @@ -1030,9 +1330,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.1.tgz", - "integrity": "sha512-44a1hreb02cAAfAKmZfXVercPFaDjqXCK+iKeVOlJ9ltvnO6QqsBHgKVPTu+MJHSLLeMEUbeG2qiDYgbFPU48g==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", "cpu": [ "arm64" ], @@ -1044,9 +1344,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.1.tgz", - "integrity": "sha512-usmzIgD0rf1syoOZ2WZvy8YpXK5G1V3btm3QZddoGSa6mOgfXWkkv+642bfUUldomgrbiLQGrPryb7DXLovPWQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", "cpu": [ "arm64" ], @@ -1058,9 +1358,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.1.tgz", - "integrity": "sha512-is3r/k4vig2Gt8mKtTlzzyaSQ+hd87kDxiN3uDSDwggJLUV56Umli6OoL+/YZa/KvtdrdyNfMKHzL/P4siOOmg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", "cpu": [ "x64" ], @@ -1072,9 +1372,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.1.tgz", - "integrity": "sha512-QJ1ksgp/bDJkZB4daldVmHaEQkG4r8PUXitCOC2WRmRaSaHx5RwPoI3DHVfXKwDkB+Sk6auFI/+JHacTekPRSw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", "cpu": [ "arm64" ], @@ -1086,9 +1386,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.1.tgz", - "integrity": "sha512-J6ma5xgAzvqsnU6a0+jgGX/gvoGokqpkx6zY4cWizRrm0ffhHDpJKQgC8dtDb3+MqfZDIqs64REbfHDMzxLMqQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", "cpu": [ "x64" ], @@ -1100,9 +1400,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.1.tgz", - "integrity": "sha512-JzWRR41o2U3/KMNKRuZNsDUAcAVUYhsPuMlx5RUldw0E4lvSIXFUwejtYz1HJXohUmqs/M6BBJAUBzKXZVddbg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", "cpu": [ "arm" ], @@ -1114,9 +1414,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.1.tgz", - "integrity": "sha512-L8kRIrnfMrEoHLHtHn+4uYA52fiLDEDyezgxZtGUTiII/yb04Krq+vk3P2Try+Vya9LeCE9ZHU8CXD6J9EhzHQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", "cpu": [ "arm" ], @@ -1128,9 +1428,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.1.tgz", - "integrity": "sha512-ysAc0MFRV+WtQ8li8hi3EoFi7us6d1UzaS/+Dp7FYZfg3NdDljGMoVyiIp6Ucz7uhlYDBZ/zt6XI0YEZbUO11Q==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", "cpu": [ "arm64" ], @@ -1142,9 +1442,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.1.tgz", - "integrity": "sha512-UV6l9MJpDbDZZ/fJvqNcvO1PcivGEf1AvKuTcHoLjVZVFeAMygnamCTDikCVMRnA+qJe+B3pSbgX2+lBMqgBhA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", "cpu": [ "arm64" ], @@ -1156,9 +1456,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.1.tgz", - "integrity": "sha512-UDUtelEprkA85g95Q+nj3Xf0M4hHa4DiJ+3P3h4BuGliY4NReYYqwlc0Y8ICLjN4+uIgCEvaygYlpf0hUj90Yg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", "cpu": [ "loong64" ], @@ -1170,9 +1470,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.1.tgz", - "integrity": "sha512-vrRn+BYhEtNOte/zbc2wAUQReJXxEx2URfTol6OEfY2zFEUK92pkFBSXRylDM7aHi+YqEPJt9/ABYzmcrS4SgQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", "cpu": [ "ppc64" ], @@ -1184,9 +1484,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.1.tgz", - "integrity": "sha512-gto/1CxHyi4A7YqZZNznQYrVlPSaodOBPKM+6xcDSCMVZN/Fzb4K+AIkNz/1yAYz9h3Ng+e2fY9H6bgawVq17w==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", "cpu": [ "riscv64" ], @@ -1198,9 +1498,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.1.tgz", - "integrity": "sha512-KZ6Vx7jAw3aLNjFR8eYVcQVdFa/cvBzDNRFM3z7XhNNunWjA03eUrEwJYPk0G8V7Gs08IThFKcAPS4WY/ybIrQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", "cpu": [ "riscv64" ], @@ -1212,9 +1512,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.1.tgz", - "integrity": "sha512-HvEixy2s/rWNgpwyKpXJcHmE7om1M89hxBTBi9Fs6zVuLU4gOrEMQNbNsN/tBVIMbLyysz/iwNiGtMOpLAOlvA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", "cpu": [ "s390x" ], @@ -1226,9 +1526,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.1.tgz", - "integrity": "sha512-E/n8x2MSjAQgjj9IixO4UeEUeqXLtiA7pyoXCFYLuXpBA/t2hnbIdxHfA7kK9BFsYAoNU4st1rHYdldl8dTqGA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", "cpu": [ "x64" ], @@ -1240,9 +1540,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.1.tgz", - "integrity": "sha512-IhJ087PbLOQXCN6Ui/3FUkI9pWNZe/Z7rEIVOzMsOs1/HSAECCvSZ7PkIbkNqL/AZn6WbZvnoVZw/qwqYMo4/w==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", "cpu": [ "x64" ], @@ -1254,9 +1554,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.1.tgz", - "integrity": "sha512-0++oPNgLJHBblreu0SFM7b3mAsBJBTY0Ksrmu9N6ZVrPiTkRgda52mWR7TKhHAsUb9noCjFvAw9l6ZO1yzaVbA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", "cpu": [ "arm64" ], @@ -1268,9 +1568,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.1.tgz", - "integrity": "sha512-VJXivz61c5uVdbmitLkDlbcTk9Or43YC2QVLRkqp86QoeFSqI81bNgjhttqhKNMKnQMWnecOCm7lZz4s+WLGpQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", "cpu": [ "arm64" ], @@ -1282,9 +1582,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.1.tgz", - "integrity": "sha512-NmZPVTUOitCXUH6erJDzTQ/jotYw4CnkMDjCYRxNHVD9bNyfrGoIse684F9okwzKCV4AIHRbUkeTBc9F2OOH5Q==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", "cpu": [ "ia32" ], @@ -1296,9 +1596,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.1.tgz", - "integrity": "sha512-2SNj7COIdAf6yliSpLdLG8BEsp5lgzRehgfkP0Av8zKfQFKku6JcvbobvHASPJu4f3BFxej5g+HuQPvqPhHvpQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", "cpu": [ "x64" ], @@ -1310,9 +1610,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.1.tgz", - "integrity": "sha512-rLarc1Ofcs3DHtgSzFO31pZsCh8g05R2azN1q3fF+H423Co87My0R+tazOEvYVKXSLh8C4LerMK41/K7wlklcg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", "cpu": [ "x64" ], @@ -1340,196 +1640,105 @@ "node_modules/@types/babel__generator": { "version": "7.27.0", "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "19.2.2", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz", - "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.2.2", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.2.tgz", - "integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.2.0" - } - }, - "node_modules/@vitejs/plugin-react": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.0.tgz", - "integrity": "sha512-4LuWrg7EKWgQaMJfnN+wcmbAW+VSsCmqGohftWjuct47bv8uE4n/nPpq4XjJPsxgq00GGG5J8dvBczp8uxScew==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.28.4", - "@babel/plugin-transform-react-jsx-self": "^7.27.1", - "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-beta.43", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.18.0" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" - } - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", "dev": true, "license": "MIT", - "peer": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" + "dependencies": { + "@babel/types": "^7.0.0" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", "dev": true, "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "@babel/types": "^7.28.2" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", + "integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "@babel/core": "^7.26.0", + "@babel/plugin-transform-react-jsx-self": "^7.25.9", + "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.14.2" }, "engines": { - "node": ">=8" + "node": "^14.18.0 || >=16.0.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" }, - "node_modules/babel-plugin-react-compiler": { - "version": "19.1.0-rc.3", - "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-19.1.0-rc.3.tgz", - "integrity": "sha512-mjRn69WuTz4adL0bXGx8Rsyk1086zFJeKmes6aK0xPuK3aaXmDJdLHqwKKMrpm6KAI1MCoUK72d2VeqQbu8YIA==", - "dev": true, + "node_modules/axios": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.0" + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, "node_modules/baseline-browser-mapping": { - "version": "2.8.25", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.25.tgz", - "integrity": "sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA==", + "version": "2.8.30", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.30.tgz", + "integrity": "sha512-aTUKW4ptQhS64+v2d6IkPzymEzzhw+G0bA1g3uBRV3+ntkH+svttKseW5IOR4Ed6NUVKqnY7qT3dKvzQ7io4AA==", "dev": true, "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.js" } }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" } }, "node_modules/browserslist": { - "version": "4.27.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz", - "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==", + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz", + "integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==", "dev": true, "funding": [ { @@ -1548,10 +1757,10 @@ "license": "MIT", "peer": true, "dependencies": { - "baseline-browser-mapping": "^2.8.19", - "caniuse-lite": "^1.0.30001751", - "electron-to-chromium": "^1.5.238", - "node-releases": "^2.0.26", + "baseline-browser-mapping": "^2.8.25", + "caniuse-lite": "^1.0.30001754", + "electron-to-chromium": "^1.5.249", + "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": { @@ -1561,20 +1770,23 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, "engines": { - "node": ">=6" + "node": ">= 0.4" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001754", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001754.tgz", - "integrity": "sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==", + "version": "1.0.30001756", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001756.tgz", + "integrity": "sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A==", "dev": true, "funding": [ { @@ -1592,50 +1804,18 @@ ], "license": "CC-BY-4.0" }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "delayed-stream": "~1.0.0" }, "engines": { - "node": ">=7.0.0" + "node": ">= 0.8" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -1643,28 +1823,6 @@ "dev": true, "license": "MIT" }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true, - "license": "MIT" - }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -1683,353 +1841,217 @@ } } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.249", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.249.tgz", - "integrity": "sha512-5vcfL3BBe++qZ5kuFhD/p8WOM1N9m3nwvJPULJx+4xf2usSlZFJ0qoNYO2fOX4hi3ocuDcmDobtA+5SFr4OmBg==", - "dev": true, - "license": "ISC" + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } }, - "node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", - "dev": true, - "hasInstallScript": true, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" }, "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.12", - "@esbuild/android-arm": "0.25.12", - "@esbuild/android-arm64": "0.25.12", - "@esbuild/android-x64": "0.25.12", - "@esbuild/darwin-arm64": "0.25.12", - "@esbuild/darwin-x64": "0.25.12", - "@esbuild/freebsd-arm64": "0.25.12", - "@esbuild/freebsd-x64": "0.25.12", - "@esbuild/linux-arm": "0.25.12", - "@esbuild/linux-arm64": "0.25.12", - "@esbuild/linux-ia32": "0.25.12", - "@esbuild/linux-loong64": "0.25.12", - "@esbuild/linux-mips64el": "0.25.12", - "@esbuild/linux-ppc64": "0.25.12", - "@esbuild/linux-riscv64": "0.25.12", - "@esbuild/linux-s390x": "0.25.12", - "@esbuild/linux-x64": "0.25.12", - "@esbuild/netbsd-arm64": "0.25.12", - "@esbuild/netbsd-x64": "0.25.12", - "@esbuild/openbsd-arm64": "0.25.12", - "@esbuild/openbsd-x64": "0.25.12", - "@esbuild/openharmony-arm64": "0.25.12", - "@esbuild/sunos-x64": "0.25.12", - "@esbuild/win32-arm64": "0.25.12", - "@esbuild/win32-ia32": "0.25.12", - "@esbuild/win32-x64": "0.25.12" + "node": ">= 0.4" } }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "node_modules/electron-to-chromium": { + "version": "1.5.259", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.259.tgz", + "integrity": "sha512-I+oLXgpEJzD6Cwuwt1gYjxsDmu/S/Kd41mmLA3O+/uH2pFRO/DvOjUyGozL8j3KeLV6WyZ7ssPwELMsXCcsJAQ==", "dev": true, + "license": "ISC" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", "engines": { - "node": ">=6" + "node": ">= 0.4" } }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.4" } }, - "node_modules/eslint": { - "version": "9.39.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", - "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", - "dev": true, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "license": "MIT", - "peer": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@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/plugin-kit": "^0.4.1", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" + "es-errors": "^1.3.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } + "node": ">= 0.4" } }, - "node_modules/eslint-plugin-react-hooks": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", - "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", - "dev": true, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "license": "MIT", - "engines": { - "node": ">=10" + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + "engines": { + "node": ">= 0.4" } }, - "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.24.tgz", - "integrity": "sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==", + "node_modules/esbuild": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", "dev": true, + "hasInstallScript": true, "license": "MIT", - "peerDependencies": { - "eslint": ">=8.40" - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" }, - "funding": { - "url": "https://opencollective.com/eslint" + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" } }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=6" } }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "@types/estree": "^1.0.0" } }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "estraverse": "^5.1.0" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" }, "engines": { - "node": ">=0.10" + "node": ">=8.6.0" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dev": true, - "license": "BSD-2-Clause", + "license": "ISC", "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "reusify": "^1.0.4" } }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "license": "MIT", "dependencies": { - "flat-cache": "^4.0.0" + "to-regex-range": "^5.0.1" }, "engines": { - "node": ">=16.0.0" + "node": ">=8" } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, "engines": { - "node": ">=10" + "node": ">=4.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "debug": { + "optional": true + } } }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "license": "MIT", "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" }, "engines": { - "node": ">=16" + "node": ">= 6" } }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2045,6 +2067,15 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -2055,23 +2086,60 @@ "node": ">=6.9.0" } }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "license": "ISC", "dependencies": { - "is-glob": "^4.0.3" + "is-glob": "^4.0.1" }, "engines": { - "node": ">=10.13.0" + "node": ">= 6" } }, "node_modules/globals": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", - "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", + "version": "15.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.12.0.tgz", + "integrity": "sha512-1+gLErljJFhbOVyaetcwJiJ4+eLe45S2E7P5UiZ9xGfeq3ATQf5DOv9G7MH3gGbKQLkzmNh2DxfZwLdw+j6oTQ==", "dev": true, "license": "MIT", "engines": { @@ -2081,51 +2149,55 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", "engines": { - "node": ">= 4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "license": "MIT", "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "has-symbols": "^1.0.3" }, "engines": { - "node": ">=6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, "engines": { - "node": ">=0.8.19" + "node": ">= 0.4" } }, "node_modules/is-extglob": { @@ -2151,33 +2223,22 @@ "node": ">=0.10.0" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "license": "ISC" + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, "license": "MIT" }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -2191,27 +2252,6 @@ "node": ">=6" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -2225,74 +2265,80 @@ "node": ">=6" } }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "license": "MIT", "dependencies": { - "json-buffer": "3.0.1" + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" + "yallist": "^3.0.2" } }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.4" } }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 8" + } }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "yallist": "^3.0.2" + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" } }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "mime-db": "1.52.0" }, "engines": { - "node": "*" + "node": ">= 0.6" } }, "node_modules/ms": { @@ -2321,13 +2367,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, "node_modules/node-releases": { "version": "2.0.27", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", @@ -2335,89 +2374,6 @@ "dev": true, "license": "MIT" }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -2426,14 +2382,13 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", - "peer": true, "engines": { - "node": ">=12" + "node": ">=8.6" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -2468,72 +2423,126 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, "node_modules/react": { - "version": "19.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", - "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", - "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", + "peer": true, "dependencies": { - "scheduler": "^0.27.0" + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" }, "peerDependencies": { - "react": "^19.2.0" + "react": "^18.3.1" + } + }, + "node_modules/react-icons": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", + "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", + "license": "MIT", + "peerDependencies": { + "react": "*" } }, "node_modules/react-refresh": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", - "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "node_modules/react-router": { + "version": "6.30.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.2.tgz", + "integrity": "sha512-H2Bm38Zu1bm8KUE5NVWRMzuIyAV8p/JrOaBJAwVmp37AXG72+CZJlEBw6pdn9i5TBgLMhNDgijS4ZlblpHyWTA==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.30.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.2.tgz", + "integrity": "sha512-l2OwHn3UUnEVUqc6/1VMmR1cvZryZ3j3NzapC2eUXO1dB0sYp5mvwdjiXhpUbRb21eFow3qSxpP8Yv6oAU824Q==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.1", + "react-router": "6.30.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "iojs": ">=1.0.0", + "node": ">=0.10.0" } }, "node_modules/rollup": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.1.tgz", - "integrity": "sha512-n2I0V0lN3E9cxxMqBCT3opWOiQBzRN7UG60z/WDKqdX2zHUS/39lezBcsckZFsV6fUTSnfqI7kHf60jDAPGKug==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", "dev": true, "license": "MIT", "dependencies": { @@ -2547,36 +2556,63 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.53.1", - "@rollup/rollup-android-arm64": "4.53.1", - "@rollup/rollup-darwin-arm64": "4.53.1", - "@rollup/rollup-darwin-x64": "4.53.1", - "@rollup/rollup-freebsd-arm64": "4.53.1", - "@rollup/rollup-freebsd-x64": "4.53.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.53.1", - "@rollup/rollup-linux-arm-musleabihf": "4.53.1", - "@rollup/rollup-linux-arm64-gnu": "4.53.1", - "@rollup/rollup-linux-arm64-musl": "4.53.1", - "@rollup/rollup-linux-loong64-gnu": "4.53.1", - "@rollup/rollup-linux-ppc64-gnu": "4.53.1", - "@rollup/rollup-linux-riscv64-gnu": "4.53.1", - "@rollup/rollup-linux-riscv64-musl": "4.53.1", - "@rollup/rollup-linux-s390x-gnu": "4.53.1", - "@rollup/rollup-linux-x64-gnu": "4.53.1", - "@rollup/rollup-linux-x64-musl": "4.53.1", - "@rollup/rollup-openharmony-arm64": "4.53.1", - "@rollup/rollup-win32-arm64-msvc": "4.53.1", - "@rollup/rollup-win32-ia32-msvc": "4.53.1", - "@rollup/rollup-win32-x64-gnu": "4.53.1", - "@rollup/rollup-win32-x64-msvc": "4.53.1", + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT" + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } }, "node_modules/semver": { "version": "6.3.1", @@ -2588,29 +2624,6 @@ "semver": "bin/semver.js" } }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -2621,60 +2634,17 @@ "node": ">=0.10.0" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1" + "is-number": "^7.0.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=8.0" } }, "node_modules/update-browserslist-db": { @@ -2708,36 +2678,23 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, "node_modules/vite": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz", - "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.4.tgz", + "integrity": "sha512-zwlH6ar+6o6b4Wp+ydhtIKLrGM/LoqZzcdVmkGAFun0KHTzIzjh+h0kungEx7KJg/PYnC80I4TII9WkjciSR6Q==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.5.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", - "tinyglobby": "^0.2.15" + "esbuild": "^0.24.0", + "postcss": "^8.4.49", + "rollup": "^4.23.0" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -2746,14 +2703,14 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", - "less": "^4.0.0", + "less": "*", "lightningcss": "^1.21.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" @@ -2794,51 +2751,12 @@ } } }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, "license": "ISC" - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } } } } diff --git a/package.json b/package.json index 8c3bc9d..1b8178f 100644 --- a/package.json +++ b/package.json @@ -1,28 +1,25 @@ { - "name": "algorithmcheffront", - "private": true, - "version": "0.0.0", + "name": "anima-project", + "version": "1.0.0", + "description": "A React project automatically generated by Anima, the design-to-code platform", + "source": "./index.html", "type": "module", "scripts": { "dev": "vite", - "build": "vite build", - "lint": "eslint .", - "preview": "vite preview" + "build": "vite build" }, "dependencies": { - "react": "^19.1.1", - "react-dom": "^19.1.1" + "axios": "^1.13.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-icons": "^5.5.0", + "react-router-dom": "^6.8.1" }, "devDependencies": { - "@eslint/js": "^9.36.0", - "@types/react": "^19.1.16", - "@types/react-dom": "^19.1.9", - "@vitejs/plugin-react": "^5.0.4", - "babel-plugin-react-compiler": "^19.1.0-rc.3", - "eslint": "^9.36.0", - "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-react-refresh": "^0.4.22", - "globals": "^16.4.0", - "vite": "^7.1.7" + "@animaapp/vite-plugin-screen-graph": "^0.1.5", + "@vitejs/plugin-react": "4.3.4", + "esbuild": "0.24.0", + "globals": "15.12.0", + "vite": "6.0.4" } } diff --git a/public/vite.svg b/public/vite.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/App.css b/src/App.css deleted file mode 100644 index b9d355d..0000000 --- a/src/App.css +++ /dev/null @@ -1,42 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/src/App.jsx b/src/App.jsx index f67355a..018704f 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,35 +1,69 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import viteLogo from '/vite.svg' -import './App.css' +import React from "react"; +import { RouterProvider, createBrowserRouter } from "react-router-dom"; +import { AuthProvider } from "./contexts/AuthContext"; +import { ScrollToTop } from "./components/ScrollToTop"; -function App() { - const [count, setCount] = useState(0) +// ▼▼▼ [수정 1] 껍데기(CommunityContent) 지우고, 알맹이(Communitycontentpage)를 import 하세요! +// 경로가 './screens/Communitycontentpage/Communitycontentpage' 인지 +// './screens/sections/Communitycontentpage' 인지 본인 폴더 구조에 맞춰 정확히 적어주세요. +import { Communitycontentpage } from "./screens/CommunityContent/sections/Communitycontentpage"; +// ▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲ - return ( - <> - -

Vite + React

-
- -

- Edit src/App.jsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

- - ) -} +import { CommunityPage } from "./screens/CommunityPage"; +import { Desktop } from "./screens/Desktop"; +import { MyPage } from "./screens/MyPage"; +import { RecipePage } from "./screens/RecipePage"; +import { AddIngredientPage } from "./screens/AddIngredientPage"; +import { CreatePostPage } from "./screens/CreatePostPage"; +import { MenuRecommendationPage } from "./screens/MenuRecommendationPage"; + +const router = createBrowserRouter([ + { + path: "/*", + element: , + }, + { + path: "/desktop", + element: , + }, + { + path: "/communitypage", + element: , + }, + { + path: "/mypage", + element: , + }, + { + path: "/recipepage/:id", + element: , + }, + { + // ▼▼▼ [수정 2] 여기서 CommunityContent 대신 Communitycontentpage를 바로 연결! + path: "/communitycontentpage/:id", + element: , + // ▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲ + }, + { + path: "/addingredient", + element: , + }, + { + path: "/createpost", + element: , + }, + { + path: "/menurecommendation", + element: , + }, +]); -export default App +export const App = () => { + return ( + + + + + + ); +}; diff --git a/src/api/Community/community.js b/src/api/Community/community.js new file mode 100644 index 0000000..8c17770 --- /dev/null +++ b/src/api/Community/community.js @@ -0,0 +1,150 @@ +import axios from "axios"; +const BACKEND_DOMAIN = "http://localhost:8080/board"; + +export const getPostList = async (page, size, filter) => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.get(`${BACKEND_DOMAIN}/posts`, { + headers: { + Authorization: `Bearer ${token}`, + }, + params: { + page: page, + size, + filter: filter || null, + }, + }); + return response.data; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; + +export const createPost = async (post) => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.post(`${BACKEND_DOMAIN}/post`, post, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + return response.data; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; + +export const getPostDetail = async (postId, page, size) => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.get(`${BACKEND_DOMAIN}/post/${postId}`, { + headers: { + Authorization: `Bearer ${token}`, + }, + params: { + page, + size, + }, + }); + return response.data; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; + +export const createComment = async (postId, comment) => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.post( + `${BACKEND_DOMAIN}/post/${postId}/comment`, + comment, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + return response.data; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; + +export const getReplies = async (commentId, page) => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.get( + `${BACKEND_DOMAIN}/comments/${commentId}/replies`, + { + headers: { + Authorization: `Bearer ${token}`, + }, + params: { + page, + }, + } + ); + return response.data; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; + +export const updatePost = async (postId, updatedPost) => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.put( + `${BACKEND_DOMAIN}/post/${postId}`, + updatedPost, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + return response.data; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; + +export const deletePost = async (postId) => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.delete(`${BACKEND_DOMAIN}/post/${postId}`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; diff --git a/src/api/Community/recipeReview.js b/src/api/Community/recipeReview.js new file mode 100644 index 0000000..fcb8ac9 --- /dev/null +++ b/src/api/Community/recipeReview.js @@ -0,0 +1,20 @@ +import axios from "axios"; +const BACKEND_DOMAIN = "http://localhost:8080/recipe/review"; + +export const makeReview = async (review) => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.post(`${BACKEND_DOMAIN}`, review, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + return response.data; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; diff --git a/src/api/Recipe/geminiRecipeApi.js b/src/api/Recipe/geminiRecipeApi.js new file mode 100644 index 0000000..2cdc152 --- /dev/null +++ b/src/api/Recipe/geminiRecipeApi.js @@ -0,0 +1,60 @@ +import axios from "axios"; +const BACKEND_DOMAIN = "http://localhost:8080/recipes/recommend"; + +export const recommendPrefer = async (prefer) => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.post(`${BACKEND_DOMAIN}/prefer`, prefer, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + return response.data; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; + +export const recommendCondition = async (condition) => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.post( + `${BACKEND_DOMAIN}/condition`, + condition, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + return response.data; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; + +export const recommendExpir = async (expire) => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.post(`${BACKEND_DOMAIN}/expir`, expire, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + return response.data; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; diff --git a/src/api/Recipe/recipeApi.js b/src/api/Recipe/recipeApi.js new file mode 100644 index 0000000..b3f20ff --- /dev/null +++ b/src/api/Recipe/recipeApi.js @@ -0,0 +1,45 @@ +import axios from "axios"; +const BACKEND_DOMAIN = "http://localhost:8080/api/recipes"; + +export const getRandomRecipe = async (ingredientName) => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.get(`${BACKEND_DOMAIN}/search`, { + headers: { + Authorization: `Bearer ${token}`, + }, + params: { + ingredient: ingredientName, + }, + }); + return response.data; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; + +export const getRandomRecipeMultiple = async (ingredients) => { + const token = localStorage.getItem("accessToken"); + console.log("헤더 값 확인:", `Bearer ${token}`); + try { + const response = await axios.get(`${BACKEND_DOMAIN}/search-multi`, { + headers: { + Authorization: `Bearer ${token}`, + }, + params: { + ingredients: ingredients.join(","), + }, + }); + return response.data; + } catch (error) { + if (error.response) { + console.log("에러 상태 코드:", error.response.status); + console.log("에러 데이터:", error.response.data); + } + throw error; + } +}; diff --git a/src/api/Stt/sttApi.js b/src/api/Stt/sttApi.js new file mode 100644 index 0000000..5646568 --- /dev/null +++ b/src/api/Stt/sttApi.js @@ -0,0 +1,45 @@ +import axios from "axios"; +const BACKEND_DOMAIN = "http://localhost:8080/api"; + +/** + * * @param {Blob} audioBlob - Voice.js에서 녹음된 오디오 Blob 객체 + * @returns {Promise} - 추천된 레시피 리스트 (List) + */ +export const recommendByVoice = async (audioBlob) => { + const token = localStorage.getItem("accessToken"); + const userId = localStorage.getItem("userId"); + + if (!userId) { + console.error("User ID not found. Please login."); + throw new Error("로그인이 필요한 서비스입니다."); + } + + const formData = new FormData(); + + formData.append("audio", audioBlob, "voice.webm"); + + formData.append("userId", userId); + + try { + const response = await axios.post(`${BACKEND_DOMAIN}/stt`, formData, { + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "multipart/form-data", + }, + }); + + console.log("음성 검색 성공, 레시피 개수:", response.data.length); + return response.data; + } catch (error) { + if (error.response) { + console.error( + "음성 검색 요청 실패:", + error.response.status, + error.response.data + ); + } else { + console.error("서버 연결 실패:", error.message); + } + throw error; + } +}; diff --git a/src/api/authApi.js b/src/api/authApi.js new file mode 100644 index 0000000..0140a7d --- /dev/null +++ b/src/api/authApi.js @@ -0,0 +1,46 @@ +import axios from "axios"; +const BACKEND_DOMAIN = "http://localhost:8080/auth"; + +export const loginAPi = async (userId, password) => { + const response = await axios.post(`${BACKEND_DOMAIN}/login`, { + userId, + password, + }); + return response.data; +}; + +export const signUpApi = async (newUser) => { + const response = await axios.post(`${BACKEND_DOMAIN}/signUp`, newUser); + return response.data; +}; + +export const findPasswordApi = async (userId, email) => { + const response = await axios.patch(`${BACKEND_DOMAIN}/findPassword`, { + userId, + email, + }); + return response.data; +}; + +export const findUserIdApi = async (email, birthDate) => { + const response = await axios.post(`${BACKEND_DOMAIN}/findUserId`, { + email, + birthDate, + }); + return response.data; +}; + +export const updateTempPasswordApi = async (password) => { + const token = localStorage.getItem("accessToken"); + + const response = await axios.patch( + `${BACKEND_DOMAIN}/update-tempPassword`, + { password }, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + return response.data; +}; diff --git a/src/api/fridgeApi.js b/src/api/fridgeApi.js new file mode 100644 index 0000000..8b18349 --- /dev/null +++ b/src/api/fridgeApi.js @@ -0,0 +1,124 @@ +import axios from "axios"; +const BACKEND_DOMAIN = "http://localhost:8080/fridge"; +export const registerIngredientManual = async (ingredients) => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.post( + `${BACKEND_DOMAIN}/ingredient/register/manual`, + { ingredients }, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + return response.data; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; + +export const updateIngredientInfo = async (ingredient) => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.patch( + `${BACKEND_DOMAIN}/ingredient/update`, + ingredient, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + return response.data; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; + +export const deleteIngredientInfo = async (id) => { + const token = localStorage.getItem("accessToken"); + try { + await axios.delete(`${BACKEND_DOMAIN}/ingredient/${id}`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + return true; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; + +export const updateIngredients = async (ingredients) => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.patch( + `${BACKEND_DOMAIN}/ingredients/register/update`, + ingredients, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + return response.data; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; + +export const getFridgeIngredients = async () => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.get(`${BACKEND_DOMAIN}/ingredients`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + return response.data; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; + +export const filterFridgeIngredientsByCategory = async (category) => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.get(`${BACKEND_DOMAIN}/ingredients`, { + headers: { + Authorization: `Bearer ${token}`, + }, + params: { + category: category, + }, + }); + return response.data; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; diff --git a/src/api/ingredientsApi.js b/src/api/ingredientsApi.js new file mode 100644 index 0000000..988b4af --- /dev/null +++ b/src/api/ingredientsApi.js @@ -0,0 +1,70 @@ +import axios from "axios"; +const BACKEND_DOMAIN = "http://localhost:8080/ingredients"; + +export const getIngredientsInfo = async (page, size) => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.get(BACKEND_DOMAIN, { + headers: { + Authorization: `Bearer ${token}`, + }, + params: { + page: page, + size: size, + }, + }); + return response.data; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; + +export const filterIngredientsByCategory = async (page, size, category) => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.get(BACKEND_DOMAIN, { + headers: { + Authorization: `Bearer ${token}`, + }, + params: { + page: page, + size: size, + category: category, + }, + }); + return response.data; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; + +export const filterIngredientsByName = async (page, size, ingredientName) => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.get(BACKEND_DOMAIN, { + headers: { + Authorization: `Bearer ${token}`, + }, + params: { + page: page, + size: size, + name: ingredientName, + }, + }); + return response.data; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; diff --git a/src/api/preferenceApi.js b/src/api/preferenceApi.js new file mode 100644 index 0000000..feae0cf --- /dev/null +++ b/src/api/preferenceApi.js @@ -0,0 +1,38 @@ +import axios from "axios"; +const BACKEND_DOMAIN = "http://localhost:8080"; + +export const getAllergyInfo = async () => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.get(`${BACKEND_DOMAIN}/allergies`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + return response.data; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; + +export const getHealthGoalInfo = async () => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.get(`${BACKEND_DOMAIN}/healthGoals`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + return response.data; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; diff --git a/src/api/userInfo.js b/src/api/userInfo.js new file mode 100644 index 0000000..2c6dc25 --- /dev/null +++ b/src/api/userInfo.js @@ -0,0 +1,56 @@ +import axios from "axios"; +const BACKEND_DOMAIN = "http://localhost:8080/user"; + +export const makeSurveyApi = async (survey) => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.post(`${BACKEND_DOMAIN}/survey`, survey, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + return response.data; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; + +export const getUserInfo = async () => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.get(`${BACKEND_DOMAIN}/mypage`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + return response.data; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; + +export const updateUserTendency = async (newInfo) => { + const token = localStorage.getItem("accessToken"); + try { + const response = await axios.patch(`${BACKEND_DOMAIN}/survey`, newInfo, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + return response.data; + } catch (error) { + if (error.response) { + console.log(error.response.status); + console.log(error.response.data); + } + throw error; + } +}; diff --git a/src/assets/react.svg b/src/assets/react.svg deleted file mode 100644 index 6c87de9..0000000 --- a/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/components/AddIngredientPopup/AddIngredientPopup.jsx b/src/components/AddIngredientPopup/AddIngredientPopup.jsx new file mode 100644 index 0000000..6fb24e2 --- /dev/null +++ b/src/components/AddIngredientPopup/AddIngredientPopup.jsx @@ -0,0 +1,133 @@ +import React, { useState, useEffect } from "react"; +import "./style.css"; + +const DEFAULT_IMAGE_URL = + "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"; + +export const AddIngredientPopup = ({ onClose, onAdd, items }) => { + const [editingItems, setEditingItems] = useState([]); + + useEffect(() => { + const today = new Date().toISOString().split("T")[0]; + + if (items && items.length > 0) { + const initializedItems = items.map((item) => ({ + ...item, + purchaseDate: item.purchaseDate + ? item.purchaseDate.split("T")[0] + : today, + expiredDate: item.expiredDate ? item.expiredDate.split("T")[0] : "", + image: item.image || DEFAULT_IMAGE_URL, + })); + setEditingItems(initializedItems); + } + }, [items]); + + const handleItemChange = (id, field, value) => { + setEditingItems((prev) => + prev.map((item) => (item.id === id ? { ...item, [field]: value } : item)) + ); + }; + + const handleDeleteItem = (id) => { + setEditingItems((prev) => prev.filter((item) => item.id !== id)); + }; + + const handleFinalSubmit = () => { + const isValid = editingItems.every( + (item) => item.name && item.purchaseDate && item.expiredDate + ); + + if (!isValid) { + alert("모든 재료의 이름, 구매일자, 유통기한을 입력해주세요."); + return; + } + + if (editingItems.length === 0) { + alert("등록할 재료가 없습니다."); + return; + } + + onAdd(editingItems); + }; + + return ( +
+
e.stopPropagation()} + > + + +
+

재료 확인 및 수정

+

+ 자동 계산된 유통기한을 확인하고 필요하면 수정해주세요. +

+
+ +
+
+ 재료명 + 구매일자 + 유통기한 + +
+ +
+ {editingItems.map((item) => ( +
+ + handleItemChange(item.id, "name", e.target.value) + } + placeholder="재료명" + /> + + + handleItemChange(item.id, "purchaseDate", e.target.value) + } + /> + + + handleItemChange(item.id, "expiredDate", e.target.value) + } + /> + + +
+ ))} +
+ + +
+
+
+ ); +}; + +export default AddIngredientPopup; diff --git a/src/components/AddIngredientPopup/ChangeIngrdientPopup.jsx b/src/components/AddIngredientPopup/ChangeIngrdientPopup.jsx new file mode 100644 index 0000000..555a207 --- /dev/null +++ b/src/components/AddIngredientPopup/ChangeIngrdientPopup.jsx @@ -0,0 +1,130 @@ +import React, { useState } from "react"; +import "./style.css"; +import { + updateIngredientInfo, + deleteIngredientInfo, +} from "../../api/fridgeApi"; + +function ChangeIngredientPopup({ ingredient, onClose, onUpdate }) { + if (!ingredient) return null; + + const { id, name, purchaseDate, expiredDate } = ingredient; + + const formattedPurchaseDate = purchaseDate ? purchaseDate.split("T")[0] : ""; + const formattedExpiredDate = expiredDate ? expiredDate.split("T")[0] : ""; + + const [newPurchaseDate, setNewPurchaseDate] = useState(formattedPurchaseDate); + const [newExpiredDate, setNewExpiredDate] = useState(formattedExpiredDate); + const handleUpdate = async () => { + if (!newPurchaseDate || !newExpiredDate) { + alert("구매일자와 유통기한을 모두 입력해주세요."); + return; + } + + try { + const updateData = { + ingredientId: id, + purchasedDate: `${newPurchaseDate}T00:00:00`, + expiredDate: `${newExpiredDate}T00:00:00`, + }; + + const response = await updateIngredientInfo(updateData); + + if (response) { + console.log("수정 응답:", response); + + const msg = response.message || "수정되었습니다"; + alert(`${name}의 정보가 ${msg}.`); + + if (onUpdate) onUpdate(); + onClose(); + } + } catch (error) { + console.error("수정 실패:", error); + const errorMsg = error.response?.data || "정보 수정에 실패했습니다."; + alert(errorMsg); + } + }; + + const handleDelete = async () => { + if (!window.confirm(`정말로 ${name}을(를) 삭제하시겠습니까?`)) { + return; + } + + try { + const result = await deleteIngredientInfo(id); + + if (result) { + console.log("삭제 요청 ID:", id); + alert(`${name}이(가) 삭제되었습니다.`); + + if (onUpdate) onUpdate(); + onClose(); + } + } catch (error) { + console.error("삭제 실패:", error); + alert("삭제에 실패했습니다. 잠시 후 다시 시도해주세요."); + } + }; + + return ( +
+
e.stopPropagation()} + > + + +
+

재료 정보 수정

+

{name}

+
+ +
+
+ + +
+ +
+ + setNewPurchaseDate(e.target.value)} + /> +
+ +
+ + setNewExpiredDate(e.target.value)} + /> +
+
+ +
+ + +
+
+
+ ); +} + +export default ChangeIngredientPopup; diff --git a/src/components/AddIngredientPopup/ReceiptPopup.jsx b/src/components/AddIngredientPopup/ReceiptPopup.jsx new file mode 100644 index 0000000..249ca7a --- /dev/null +++ b/src/components/AddIngredientPopup/ReceiptPopup.jsx @@ -0,0 +1,50 @@ +import { useState } from "react"; +import "./style.css"; +import AddIngredientPopup from "./AddIngredientPopup"; +function ReceiptPopup({ onClose }) { + const [showAddPopup, setShowAddPopup] = useState(false); + if (showAddPopup) { + return ( + setShowAddPopup(false)} + onAdd={() => console.log("추가할 예정")} + items={() => console.log("ocr api 돌려서 나온 결과")} + /> + ); + } + return ( +
+
e.stopPropagation()} + > + +
+

영수증 등록

+
+
+
+ + +

선명한 영수증 사진을 올려주세요.

+
+ +
+
+
+ ); +} + +export default ReceiptPopup; diff --git a/src/components/AddIngredientPopup/index.js b/src/components/AddIngredientPopup/index.js new file mode 100644 index 0000000..f656586 --- /dev/null +++ b/src/components/AddIngredientPopup/index.js @@ -0,0 +1 @@ +export { AddIngredientPopup } from "./AddIngredientPopup"; diff --git a/src/components/AddIngredientPopup/style.css b/src/components/AddIngredientPopup/style.css new file mode 100644 index 0000000..00bbed4 --- /dev/null +++ b/src/components/AddIngredientPopup/style.css @@ -0,0 +1,334 @@ +.add-ingredient-popup-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + animation: fadeIn 0.2s ease-in-out; +} + +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +/* 재료 추가 팝업 (넓은 버전) */ +.add-ingredient-popup-container { + background-color: #ffffff; + border-radius: 20px; + padding: 40px; + width: 700px; /* 넉넉한 너비 */ + max-width: 95%; + position: relative; + box-shadow: 0px 10px 40px rgba(0, 0, 0, 0.2); + animation: slideUp 0.3s ease-out; + max-height: 90vh; + overflow-y: auto; + box-sizing: border-box; + display: flex; + flex-direction: column; +} + +@keyframes slideUp { + from { + transform: translateY(20px); + opacity: 0; + } + to { + transform: translateY(0); + opacity: 1; + } +} + +.add-ingredient-popup-close { + position: absolute; + top: 15px; + right: 15px; + background: none; + border: none; + font-size: 28px; + color: #666; + cursor: pointer; + z-index: 10; +} + +.add-ingredient-popup-header { + margin-bottom: 25px; + text-align: center; +} + +.add-ingredient-popup-title { + font-family: "Inter", Helvetica, sans-serif; + font-size: 24px; + font-weight: 700; + color: #000; + margin: 0; +} + +.add-ingredient-popup-form { + display: flex; + flex-direction: column; + gap: 20px; +} + +.add-ingredient-popup-input-group { + display: flex; + flex-direction: column; + gap: 8px; + width: 100%; +} + +.add-ingredient-popup-label { + font-family: "Inter", Helvetica, sans-serif; + font-size: 14px; + font-weight: 600; + color: #000; +} + +.add-ingredient-popup-input { + width: 100%; + padding: 12px 16px; + border: 1px solid #e0e0e0; + border-radius: 12px; + font-family: "Inter", Helvetica, sans-serif; + font-size: 16px; + background-color: #fff; + box-sizing: border-box; +} + +.add-ingredient-popup-input:focus { + outline: none; + border-color: #f6910b; +} + +input[type="file"]::file-selector-button { + margin-right: 15px; + border: none; + background: #f0f0f0; + padding: 8px 12px; + border-radius: 8px; + color: #555; + cursor: pointer; + font-family: "Inter", sans-serif; + font-weight: 600; + transition: background-color 0.2s; +} + +input[type="file"]::file-selector-button:hover { + background: #e0e0e0; +} + +.helper-text { + font-size: 13px; + color: #888; + margin-top: 2px; +} + +.add-ingredient-popup-submit-btn { + padding: 14px; + background-color: #f6910b; + color: #fff; + border: none; + border-radius: 12px; + font-size: 16px; + font-weight: 700; + cursor: pointer; + width: 100%; + margin-top: 10px; + transition: background-color 0.2s; +} + +.add-ingredient-popup-submit-btn:hover { + background-color: #e58209; +} + +.processed-list-container { + display: flex; + flex-direction: column; + gap: 15px; + width: 100%; +} + +.processed-list-header { + display: flex; + justify-content: space-between; + padding: 0 10px; + margin-bottom: 5px; + font-size: 13px; + font-weight: 600; + color: #666; + gap: 10px; +} + +.processed-list-body { + display: flex; + flex-direction: column; + gap: 12px; + max-height: 400px; + overflow-y: auto; + padding-right: 5px; +} + +.processed-item-row { + display: flex; + gap: 12px; + align-items: center; +} + +.processed-item-row .item-input { + padding: 10px; + border: 1px solid #e0e0e0; + border-radius: 8px; + font-size: 14px; + font-family: "Inter", Helvetica, sans-serif; + box-sizing: border-box; +} + +.item-input:focus { + outline: none; + border-color: #f6910b; +} + +.name-input { + flex: 1.5; + min-width: 120px; +} + +.date-input { + flex: 1.2; + min-width: 135px; + text-align: center; + font-size: 13px; +} + +.item-delete-btn { + width: 32px; + height: 32px; + border-radius: 50%; + border: none; + background-color: #ffeded; + color: #ff4d4d; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + font-size: 18px; + flex-shrink: 0; + transition: background-color 0.2s; +} + +.item-delete-btn:hover { + background-color: #ffcccc; +} + +.add-row-btn { + background: none; + border: 1px dashed #ccc; + border-radius: 8px; + padding: 12px; + color: #666; + font-size: 14px; + cursor: pointer; + margin-top: 5px; + width: 100%; + transition: all 0.2s; +} + +.add-row-btn:hover { + border-color: #f6910b; + color: #f6910b; + background-color: #fff8f0; +} + +.change-popup-container { + width: 450px; + padding: 40px; + border-radius: 20px; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); +} + +.popup-subtitle { + margin-top: 5px; + font-size: 16px; + color: #666; + font-weight: 500; +} + +.change-popup-body { + display: flex; + flex-direction: column; + gap: 25px; + margin-bottom: 35px; + margin-top: 10px; +} + +.input-group { + display: flex; + flex-direction: column; + gap: 8px; +} + +.input-label { + font-size: 15px; + font-weight: 600; + color: #333; + margin-left: 4px; +} + +.disabled-input { + background-color: #f9f9f9; + color: #666; + cursor: not-allowed; + border-color: #eee; +} + +.change-popup-footer { + display: flex; + gap: 15px; +} + +.btn-update, +.btn-delete { + flex: 1; + padding: 16px; + border-radius: 12px; + font-size: 16px; + font-weight: 700; + cursor: pointer; + border: none; + transition: all 0.2s ease; + display: flex; + align-items: center; + justify-content: center; +} + +.btn-update { + background-color: #f6910b; + color: white; + box-shadow: 0 4px 12px rgba(246, 145, 11, 0.2); +} + +.btn-update:hover { + background-color: #e58209; + transform: translateY(-2px); +} + +.btn-delete { + background-color: #fff0f0; + color: #ff4d4d; + border: 1px solid #ffcccc; +} + +.btn-delete:hover { + background-color: #ffe5e5; + border-color: #ffb3b3; +} diff --git a/src/components/CommunitypageWrapper/CommunitypageWrapper.jsx b/src/components/CommunitypageWrapper/CommunitypageWrapper.jsx new file mode 100644 index 0000000..28ac451 --- /dev/null +++ b/src/components/CommunitypageWrapper/CommunitypageWrapper.jsx @@ -0,0 +1,45 @@ +/* +We're constantly improving the code you see. +Please share your feedback here: https://form.asana.com/?k=uvp-HPgd3_hyoXRBw1IcNg&d=1152665201300829 +*/ + +import React from "react"; +import { Link } from "react-router-dom"; +import "./style.css"; + +export const CommunitypageWrapper = ({ + className, + postId, + title, + author, + date, + category, + content, +}) => { + return ( + +
+
{category}
+
+ +
+
|
+ +
{title}
+
+
+
작성자 :
+ +
{author}
+ +
작성일 :
+ +
{date}
+
+ + ); +}; diff --git a/src/components/CommunitypageWrapper/index.js b/src/components/CommunitypageWrapper/index.js new file mode 100644 index 0000000..47ea4ad --- /dev/null +++ b/src/components/CommunitypageWrapper/index.js @@ -0,0 +1 @@ +export { CommunitypageWrapper } from "./CommunitypageWrapper"; diff --git a/src/components/CommunitypageWrapper/style.css b/src/components/CommunitypageWrapper/style.css new file mode 100644 index 0000000..2b8c5c0 --- /dev/null +++ b/src/components/CommunitypageWrapper/style.css @@ -0,0 +1,124 @@ +.communitypage-wrapper { + align-items: center; + border: 1px solid; + border-color: #0000001a; + border-radius: 16px; + display: flex; + gap: 10px; + padding: 2px 10px; + position: relative; + width: 100%; + text-decoration: none; +} + +.communitypage-wrapper .communitypage-6 { + align-items: center; + align-self: stretch; + display: flex; + gap: 10px; + justify-content: center; + padding: 0px 5px; + position: relative; + width: 60px; +} + +.communitypage-wrapper .communitypage-7 { + align-items: center; + align-self: stretch; + color: #000000bf; + display: flex; + flex: 1; + font-family: "Inter", Helvetica; + font-size: 20px; + font-weight: 700; + justify-content: center; + letter-spacing: -0.40px; + line-height: 24.0px; + margin-top: -1.00px; + position: relative; + text-align: center; + white-space: nowrap; +} + +.communitypage-wrapper .communitypage-8 { + align-items: center; + align-self: stretch; + display: flex; + flex: 1; + flex-grow: 1; + gap: 10px; + padding: 1px 0px; + position: relative; +} + +.communitypage-wrapper .communitypage-9 { + align-items: center; + color: #00000066; + display: flex; + font-family: "Inter", Helvetica; + font-size: 20px; + font-weight: 500; + justify-content: center; + letter-spacing: -0.40px; + line-height: 24.0px; + position: relative; + white-space: nowrap; + width: fit-content; +} + +.communitypage-wrapper .communitypage-10 { + align-items: center; + color: #000000; + display: flex; + font-family: "Inter", Helvetica; + font-size: 16px; + font-weight: 500; + justify-content: center; + letter-spacing: -0.32px; + line-height: 19.2px; + position: relative; + white-space: nowrap; + width: fit-content; +} + +.communitypage-wrapper .communitypage-11 { + align-items: center; + align-self: stretch; + display: flex; + flex: 1; + flex-grow: 1; + gap: 10px; + justify-content: flex-end; + padding: 3px 0px; + position: relative; +} + +.communitypage-wrapper .communitypage-12 { + align-items: center; + color: #00000066; + display: flex; + font-family: "Inter", Helvetica; + font-size: 18px; + font-weight: 500; + justify-content: center; + letter-spacing: -0.36px; + line-height: 21.6px; + margin-top: -1.00px; + position: relative; + white-space: nowrap; + width: fit-content; +} + +.communitypage-wrapper .communitypage-13 { + align-items: center; + color: #000000; + display: flex; + flex: 1; + font-family: "Inter", Helvetica; + font-size: 16px; + font-weight: 500; + justify-content: center; + letter-spacing: -0.32px; + line-height: 19.2px; + position: relative; +} diff --git a/src/components/DivWrapper/DivWrapper.jsx b/src/components/DivWrapper/DivWrapper.jsx new file mode 100644 index 0000000..d93923c --- /dev/null +++ b/src/components/DivWrapper/DivWrapper.jsx @@ -0,0 +1,108 @@ +/* +We're constantly improving the code you see. +Please share your feedback here: https://form.asana.com/?k=uvp-HPgd3_hyoXRBw1IcNg&d=1152665201300829 +*/ + +import React, { useState } from "react"; +import { Link } from "react-router-dom"; +import { useAuth } from "../../contexts/AuthContext"; +import { LoginPopup } from "../LoginPopup"; +import { SignupPopup } from "../SignupPopup"; +import { PreferencesPopup } from "../PreferencesPopup"; +import "./style.css"; + +export const DivWrapper = ({ className }) => { + const { user, isAuthenticated, logout } = useAuth(); + const [showLoginPopup, setShowLoginPopup] = useState(false); + const [showSignupPopup, setShowSignupPopup] = useState(false); + const [showPreferencesPopup, setShowPreferencesPopup] = useState(false); + const [userData, setUserData] = useState(null); + + const handleSwitchToSignup = () => { + setShowLoginPopup(false); + setShowSignupPopup(true); + }; + + const handleSwitchToLogin = () => { + setShowSignupPopup(false); + setShowPreferencesPopup(false); + setShowLoginPopup(true); + }; + + const handleSwitchToPreferences = (data) => { + setUserData(data); + setShowSignupPopup(false); + setShowPreferencesPopup(true); + }; + + const handleCloseAll = () => { + setShowLoginPopup(false); + setShowSignupPopup(false); + setShowPreferencesPopup(false); + setUserData(null); + }; + + return ( + <> +
+
+ + None frame + +
+ + +

+ 알고리즘 + + 셰프 +

+ + +
+ +
마이페이지
+ + {isAuthenticated ? ( +
+ {user?.username} + +
+ ) : ( + + )} +
+
+ + {showLoginPopup && ( + + )} + + {showSignupPopup && ( + + )} + + {showPreferencesPopup && ( + + )} + + ); +}; diff --git a/src/components/DivWrapper/index.js b/src/components/DivWrapper/index.js new file mode 100644 index 0000000..b316d4a --- /dev/null +++ b/src/components/DivWrapper/index.js @@ -0,0 +1 @@ +export { DivWrapper } from "./DivWrapper"; diff --git a/src/components/DivWrapper/style.css b/src/components/DivWrapper/style.css new file mode 100644 index 0000000..4e022cf --- /dev/null +++ b/src/components/DivWrapper/style.css @@ -0,0 +1,216 @@ +.div-wrapper { + align-items: center; + display: flex; + justify-content: space-between; + gap: 10px; + height: 120px; + padding: 10px; + position: relative; + width: 100%; + max-width: 1280px; +} + +.div-wrapper .header-left-section { + display: flex; + align-items: center; + flex: 1; + justify-content: flex-start; +} + +.div-wrapper .none-frame-2 { + height: 80px; + width: auto; + position: relative; +} + +.div-wrapper .algorithm-label-2-link { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + position: absolute; + left: 50%; + transform: translateX(-50%); +} + +.div-wrapper .algorithm-label-2 { + align-items: center; + color: transparent; + display: flex; + font-family: "Inter", Helvetica; + font-size: 48px; + font-weight: 700; + justify-content: center; + letter-spacing: -0.96px; + line-height: 1; + margin: 0; + position: relative; + text-align: center; + width: 100%; +} + +.div-wrapper .text-wrapper-9 { + color: #000000; + letter-spacing: -0.46px; +} + +.div-wrapper .text-wrapper-10 { + color: #f6910b; + letter-spacing: -0.46px; +} + +.div-wrapper .header-right-section { + display: flex; + align-items: center; + flex: 1; + gap: 10px; + justify-content: flex-end; + padding: 10px; +} + +.div-wrapper .mypage-button-2 { + background-image: url(https://c.animaapp.com/sjWITF5i/img/loginvector-3.svg); + background-size: 100% 100%; + border: none; + border-radius: 30px; + cursor: pointer; + height: 36px; + overflow: hidden; + position: relative; + width: 120px; + transition: transform 0.2s; + text-decoration: none; + display: flex; + align-items: center; + justify-content: center; +} + +.div-wrapper .mypage-button-2:hover { + transform: scale(1.05); +} + +.div-wrapper .mypage-text-2 { + color: #ffffff; + font-family: "Inter", Helvetica; + font-size: 16px; + font-weight: 600; + letter-spacing: -0.32px; + line-height: 19.2px; + text-align: center; +} + +.div-wrapper .login-button-2 { + background-image: url(https://c.animaapp.com/sjWITF5i/img/loginvector-3.svg); + background-size: 100% 100%; + border: none; + border-radius: 30px; + cursor: pointer; + height: 36px; + overflow: hidden; + position: relative; + width: 120px; + transition: transform 0.2s; +} + +.div-wrapper .login-button-2:hover { + transform: scale(1.05); +} + +.div-wrapper .login-text-3 { + align-items: center; + color: #ffffff; + display: flex; + font-family: "Inter", Helvetica; + font-size: 20px; + font-weight: 600; + height: 24px; + justify-content: center; + left: calc(50.00% - 33px); + letter-spacing: -0.40px; + line-height: 24.0px; + position: absolute; + text-align: center; + top: calc(50.00% - 12px); + width: 66px; +} + +.div-wrapper .user-info-wrapper { + display: flex; + align-items: center; + gap: 10px; +} + +.div-wrapper .user-name-display { + color: #000000; + font-family: "Inter", Helvetica; + font-size: 16px; + font-weight: 600; + padding: 8px 16px; + background-color: #f6910b1a; + border-radius: 20px; +} + +.div-wrapper .logout-button-2 { + background-image: url(https://c.animaapp.com/sjWITF5i/img/loginvector-3.svg); + background-size: 100% 100%; + border: none; + border-radius: 30px; + cursor: pointer; + height: 36px; + overflow: hidden; + position: relative; + width: 100px; + transition: transform 0.2s; + color: #ffffff; + font-family: "Inter", Helvetica; + font-size: 16px; + font-weight: 600; +} + +.div-wrapper .logout-button-2:hover { + transform: scale(1.05); +} + +@media (max-width: 768px) { + .div-wrapper { + padding: 10px 5px; + height: auto; + min-height: 80px; + } + + .div-wrapper .none-frame-2 { + height: 50px; + } + + .div-wrapper .algorithm-label-2 { + font-size: 24px; + } + + .div-wrapper .mypage-button-2, + .div-wrapper .login-button-2, + .div-wrapper .logout-button-2 { + width: 90px; + height: 32px; + } + + .div-wrapper .mypage-text-2, + .div-wrapper .login-text-3 { + font-size: 12px; + } + + .div-wrapper .user-name-display { + font-size: 12px; + padding: 4px 10px; + } +} + +@media (max-width: 480px) { + .div-wrapper .algorithm-label-2 { + font-size: 20px; + } + + .div-wrapper .header-right-section { + gap: 5px; + padding: 5px; + } +} diff --git a/src/components/IngredientComponent/IngredientComponent.jsx b/src/components/IngredientComponent/IngredientComponent.jsx new file mode 100644 index 0000000..102a14a --- /dev/null +++ b/src/components/IngredientComponent/IngredientComponent.jsx @@ -0,0 +1,50 @@ +import React, { useState } from "react"; +import "./style.css"; +import ChangeIngredientPopup from "../AddIngredientPopup/ChangeIngrdientPopup"; + +export const IngredientComponent = ({ + className, + ingredientImage, + ingredient, + onUpdate, +}) => { + const { name, dday } = ingredient; + + const [showPopup, setShowPopup] = useState(false); + + return ( + <> +
setShowPopup(true)} + style={{ cursor: "pointer" }} + > +
+
+ Ingredient image + +
+
{name}
+
+
+ +
+
소비기한 : {dday}일
+
+
+
+ + {showPopup && ( + setShowPopup(false)} + onUpdate={onUpdate} + /> + )} + + ); +}; diff --git a/src/components/IngredientComponent/index.js b/src/components/IngredientComponent/index.js new file mode 100644 index 0000000..b5ee1c9 --- /dev/null +++ b/src/components/IngredientComponent/index.js @@ -0,0 +1 @@ +export { IngredientComponent } from "./IngredientComponent"; diff --git a/src/components/IngredientComponent/style.css b/src/components/IngredientComponent/style.css new file mode 100644 index 0000000..c5cb482 --- /dev/null +++ b/src/components/IngredientComponent/style.css @@ -0,0 +1,107 @@ +.ingredient-component { + align-items: flex-start; + background-color: #ffffff; + border: 1px solid; + border-color: #0000001a; + border-radius: 16px; + box-shadow: 0px 12px 32px #0000000a, 0px 4px 8px #00000005; + display: flex; + flex-direction: column; + gap: 24px; + height: 120px; + justify-content: space-around; + max-height: 120px; + min-width: 140px; + overflow: hidden; + padding: 10px; + position: relative; + flex-shrink: 0; +} + +@media (max-width: 768px) { + .ingredient-component { + min-width: 120px; + } +} + +.ingredient-component .ingredient-container { + align-items: flex-start; + align-self: stretch; + display: flex; + flex: 1; + flex-direction: column; + flex-grow: 1; + justify-content: space-between; + position: relative; + width: 100%; +} + +.ingredient-component .ingredient-header { + align-items: center; + align-self: stretch; + display: flex; + flex: 0 0 auto; + gap: 10px; + position: relative; + width: 100%; +} + +.ingredient-component .ingredient-image { + aspect-ratio: 1; + height: 48px; + object-fit: cover; + position: relative; + width: 48px; +} + +.ingredient-component .ingredient-name { + align-items: center; + align-self: stretch; + display: flex; + flex: 1; + flex-grow: 1; + gap: 10px; + padding: 2px; + position: relative; +} + +.ingredient-component .ingredient-name-text { + align-items: center; + color: #000000; + display: flex; + font-family: "Inter", Helvetica; + font-size: 20px; + font-weight: 500; + justify-content: center; + letter-spacing: -0.10px; + line-height: 29.0px; + position: relative; + white-space: nowrap; + width: fit-content; +} + +.ingredient-component .ingredient { + align-items: center; + align-self: stretch; + display: flex; + flex: 1; + flex-grow: 1; + gap: 10px; + justify-content: center; + padding: 2px; + position: relative; + width: 100%; +} + +.ingredient-component .ingredient-2 { + align-self: stretch; + color: #000000; + flex: 1; + font-family: "Inter", Helvetica; + font-size: 12px; + font-weight: 500; + letter-spacing: -0.06px; + line-height: 17.4px; + margin-top: -1.00px; + position: relative; +} diff --git a/src/components/IngredientInventoryWrapper/IngredientInventoryWrapper.jsx b/src/components/IngredientInventoryWrapper/IngredientInventoryWrapper.jsx new file mode 100644 index 0000000..4162c4e --- /dev/null +++ b/src/components/IngredientInventoryWrapper/IngredientInventoryWrapper.jsx @@ -0,0 +1,19 @@ +/* +We're constantly improving the code you see. +Please share your feedback here: https://form.asana.com/?k=uvp-HPgd3_hyoXRBw1IcNg&d=1152665201300829 +*/ + +import React from "react"; +import "./style.css"; + +export const IngredientInventoryWrapper = ({ className }) => { + return ( +
+
나의 냉장고
+ +

+ 당신의 냉장고 속 재료는 무엇인가요? +

+
+ ); +}; diff --git a/src/components/IngredientInventoryWrapper/index.js b/src/components/IngredientInventoryWrapper/index.js new file mode 100644 index 0000000..9c45c63 --- /dev/null +++ b/src/components/IngredientInventoryWrapper/index.js @@ -0,0 +1 @@ +export { IngredientInventoryWrapper } from "./IngredientInventoryWrapper"; diff --git a/src/components/IngredientInventoryWrapper/style.css b/src/components/IngredientInventoryWrapper/style.css new file mode 100644 index 0000000..f07ded5 --- /dev/null +++ b/src/components/IngredientInventoryWrapper/style.css @@ -0,0 +1,56 @@ +.ingredient-inventory-wrapper { + align-items: flex-start; + border-bottom-style: solid; + border-bottom-width: 1px; + border-color: #000000; + display: flex; + flex-direction: column; + gap: 10px; + left: 64px; + padding: 5px 0px; + position: relative; + top: 25px; +} + +.ingredient-inventory-wrapper .ingredient-inventory-2 { + align-items: center; + align-self: stretch; + color: #000000; + display: flex; + font-family: "Inter", Helvetica; + font-size: 48px; + font-weight: 700; + justify-content: flex-start; + letter-spacing: -0.96px; + line-height: 57.6px; + margin-top: -1.00px; + position: relative; +} + +@media (max-width: 768px) { + .ingredient-inventory-wrapper .ingredient-inventory-2 { + font-size: 32px; + line-height: 38.4px; + } +} + +.ingredient-inventory-wrapper .ingredient-inventory-3 { + align-items: center; + align-self: stretch; + color: #0000008c; + display: flex; + font-family: "Inter", Helvetica; + font-size: 20px; + font-weight: 500; + justify-content: flex-start; + letter-spacing: -0.10px; + line-height: 29.0px; + position: relative; +} + +@media (max-width: 768px) { + .ingredient-inventory-wrapper .ingredient-inventory-3 { + font-size: 16px; + line-height: 23.2px; + } +} diff --git a/src/components/LoginPopup/ChangePassword/ChangePassword.jsx b/src/components/LoginPopup/ChangePassword/ChangePassword.jsx new file mode 100644 index 0000000..2d45a78 --- /dev/null +++ b/src/components/LoginPopup/ChangePassword/ChangePassword.jsx @@ -0,0 +1,114 @@ +import { useState } from "react"; +import "../style.css"; +import { isPasswordValid } from "../../util/validation"; +import { updateTempPasswordApi } from "../../../api/authApi"; + +function ChangePassword({ onSuccess }) { + const [password, setPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + + const [errors, setErrors] = useState({ + password: "", + confirmPassword: "", + }); + + async function handleChangePassword(e) { + e.preventDefault(); + + let newErrors = {}; + + if (!isPasswordValid(password)) { + newErrors.password = + ", Ư ּ 10 ~ ִ 20ڷ Էּ."; + } + + if (password !== confirmPassword) { + newErrors.confirmPassword = "йȣ ġ ʽϴ."; + } + + setErrors(newErrors); + if (Object.keys(newErrors).length > 0) { + return; + } + + try { + const result = await updateTempPasswordApi(password); + + if (result) { + alert(result.message || "йȣ Ǿϴ."); + + if (onSuccess) { + onSuccess(); + } + } + } catch (error) { + console.error("йȣ :", error); + alert("йȣ 濡 ߽ϴ. ٽ õּ."); + } + } + + const handleInputChange = (setter, field, value) => { + setter(value); + if (errors[field]) { + setErrors({ ...errors, [field]: "" }); + } + }; + + return ( +
+
+ + + handleInputChange(setPassword, "password", e.target.value) + } + required + name="password" + placeholder=", Ư 10~20" + /> + {errors.password && ( + {errors.password} + )} +
+ +
+ + + handleInputChange( + setConfirmPassword, + "confirmPassword", + e.target.value + ) + } + required + name="confirmPassword" + placeholder="йȣ Ȯ" + /> + {errors.confirmPassword && ( + {errors.confirmPassword} + )} +
+ + +
+ ); +} + +export default ChangePassword; diff --git a/src/components/LoginPopup/FindId/FindId.jsx b/src/components/LoginPopup/FindId/FindId.jsx new file mode 100644 index 0000000..b145c32 --- /dev/null +++ b/src/components/LoginPopup/FindId/FindId.jsx @@ -0,0 +1,187 @@ +import "../style.css"; +import { useState } from "react"; +import { isNotEmpty, isEmail } from "../../util/validation"; +import { findUserIdApi } from "../../../api/authApi"; +import { FaCheckCircle } from "react-icons/fa"; + +function FindId({ onClose, onSwitchToLogin }) { + const [email, setEmail] = useState(""); + const [birthDate, setBirthDate] = useState(""); + const [foundId, setFoundId] = useState(null); + + const [errors, setErrors] = useState({ + email: "", + birthDate: "", + }); + + const handleSubmit = async (event) => { + event.preventDefault(); + + let newErrors = {}; + + if (!isNotEmpty(email)) { + newErrors.email = "̸ Էּ."; + } else if (!isEmail(email)) { + newErrors.email = "ùٸ ̸ ƴմϴ."; + } + + if (!isNotEmpty(birthDate)) { + newErrors.birthDate = " ּ."; + } + + setErrors(newErrors); + if (Object.keys(newErrors).length > 0) { + return; + } + const formattedBirthDate = `${birthDate}T00:00:00`; + + try { + const result = await findUserIdApi(email, formattedBirthDate); + if (result && result.userId) { + setFoundId(result.userId); + } else { + alert("ġϴ ϴ."); + } + } catch (error) { + alert("ã : ߻߽ϴ."); + } + }; + + const handleInputChange = (setter, field, value) => { + setter(value); + if (errors[field]) { + setErrors({ ...errors, [field]: "" }); + } + }; + + if (foundId) { + return ( +
+
e.stopPropagation()} + > + + +
+

̵ ã

+
+ +
+
+ +
+ +

+ ȸ ̵ ãҽϴ! +

+ +
+

+ {foundId} +

+
+ + +
+
+
+ ); + } + + return ( +
+
e.stopPropagation()} + > + +
+

̵ ã

+
+ +
+
+ + + handleInputChange(setEmail, "email", e.target.value) + } + name="email" + /> + {errors.email && ( + {errors.email} + )} +
+ +
+ + + handleInputChange(setBirthDate, "birthDate", e.target.value) + } + /> + {errors.birthDate && ( + {errors.birthDate} + )} +
+ + +
+ +
+ + α ư + +
+
+
+ ); +} + +export default FindId; diff --git a/src/components/LoginPopup/FindPassword/FindPassword.jsx b/src/components/LoginPopup/FindPassword/FindPassword.jsx new file mode 100644 index 0000000..303c37e --- /dev/null +++ b/src/components/LoginPopup/FindPassword/FindPassword.jsx @@ -0,0 +1,199 @@ +import "../style.css"; +import { useState } from "react"; +import { isNotEmpty, isEmail } from "../../util/validation"; +import { findPasswordApi } from "../../../api/authApi"; +import { FaCheckCircle } from "react-icons/fa"; + +function FindPassword({ onClose, onSwitchToLogin }) { + const [userId, setUserId] = useState(""); + const [email, setEmail] = useState(""); + const [resultMessage, setResultMessage] = useState(null); + + const [errors, setErrors] = useState({ + userId: "", + email: "", + }); + + const handleSubmit = async (e) => { + e.preventDefault(); + + let newErrors = {}; + + if (!isNotEmpty(userId)) { + newErrors.userId = "아이디를 입력해주세요."; + } + + if (!isNotEmpty(email)) { + newErrors.email = "이메일을 입력해주세요."; + } else if (!isEmail(email)) { + newErrors.email = "올바른 이메일 형식이 아닙니다."; + } + + setErrors(newErrors); + if (Object.keys(newErrors).length > 0) { + return; + } + + try { + const result = await findPasswordApi(userId, email); + + if (result) { + const message = + result.tempPassword || + result.message || + "이메일로 임시 비밀번호가 발송되었습니다."; + setResultMessage(message); + } else { + alert("일치하는 회원 정보가 없습니다."); + } + } catch (error) { + console.error(error); + if (error.response && error.response.data) { + alert(error.response.data); + } else { + alert("비밀번호 찾기 실패: 정보를 다시 확인해주세요."); + } + } + }; + + const handleInputChange = (setter, field, value) => { + setter(value); + if (errors[field]) { + setErrors({ ...errors, [field]: "" }); + } + }; + + if (resultMessage) { + return ( +
+
e.stopPropagation()} + > + +
+

비밀번호 찾기 성공

+
+
+
+ +
+

+ 임시 비밀번호가 발급되었습니다. 이메일을 확인해주세요 +

+
+

+ {resultMessage} +

+
+
+ 로그인 후 반드시 비밀번호를 변경해주세요. +
+ +
+
+
+ ); + } + + return ( +
+
e.stopPropagation()} + > + +
+

비밀번호 찾기

+
+ +
+
+ + + handleInputChange(setUserId, "userId", e.target.value) + } + /> + {errors.userId && ( + {errors.userId} + )} +
+ +
+ + + handleInputChange(setEmail, "email", e.target.value) + } + /> + {errors.email && ( + {errors.email} + )} +
+ + +
+ +
+ + 로그인으로 돌아가기 + +
+
+
+ ); +} + +export default FindPassword; diff --git a/src/components/LoginPopup/LoginPopup.jsx b/src/components/LoginPopup/LoginPopup.jsx new file mode 100644 index 0000000..28c86b4 --- /dev/null +++ b/src/components/LoginPopup/LoginPopup.jsx @@ -0,0 +1,131 @@ +import React, { useState } from "react"; +import { useAuth } from "../../contexts/AuthContext"; +import "./style.css"; +import ChangePassword from "./ChangePassword/ChangePassword"; + +export const LoginPopup = ({ + onClose, + onSwitchToSignup, + onSwitchToFindId, + onSwitchToFindPassword, +}) => { + const [id, setId] = useState(""); + const [password, setPassword] = useState(""); + const [viewMode, setViewMode] = useState("login"); + const { login } = useAuth(); + + const handleLogin = async (e) => { + e.preventDefault(); + const result = await login(id, password); + if (result.status === "SUCCESS") { + console.log("???? ????"); + localStorage.setItem("userId", id); + onClose(); + } else if (result.status === "FORCE_PASSWORD_CHANGE") { + console.log("?????? ?????????."); + setViewMode("changepw"); + } else { + alert("???? ????: ????? ?????? ??????????."); + } + }; + + if (viewMode === "changepw") { + return ( +
+
e.stopPropagation()} + > + +
+

?????? ????

+
+ setViewMode("login")} /> +
+
+ ); + } + + return ( +
+
e.stopPropagation()} + > + + +
+

?????

+
+ +
+
+ {" "} + {/* Changed label */} + setId(e.target.value)} // Changed onChange to setId + placeholder="??????????? ???????????????" // Changed placeholder + required + /> +
+ +
+ + setPassword(e.target.value)} + placeholder="?????????? ???????????????" + required + /> +
+ + + +
+ + ??????? ??????????????? + + +
+
+ ???? ??? + + +
+
+
+
+ ); +}; diff --git a/src/components/LoginPopup/index.js b/src/components/LoginPopup/index.js new file mode 100644 index 0000000..369fc41 --- /dev/null +++ b/src/components/LoginPopup/index.js @@ -0,0 +1 @@ +export { LoginPopup } from "./LoginPopup"; diff --git a/src/components/LoginPopup/style.css b/src/components/LoginPopup/style.css new file mode 100644 index 0000000..516c188 --- /dev/null +++ b/src/components/LoginPopup/style.css @@ -0,0 +1,175 @@ +.login-popup-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + animation: fadeIn 0.2s ease-in-out; +} + +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +.login-popup-container { + background-color: #ffffff; + border-radius: 20px; + padding: 40px; + width: 90%; + max-width: 400px; + position: relative; + box-shadow: 0px 10px 40px rgba(0, 0, 0, 0.2); + animation: slideUp 0.3s ease-out; +} + +@keyframes slideUp { + from { + transform: translateY(20px); + opacity: 0; + } + to { + transform: translateY(0); + opacity: 1; + } +} + +.login-popup-close { + position: absolute; + top: 15px; + right: 15px; + background: none; + border: none; + font-size: 32px; + color: #666666; + cursor: pointer; + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + transition: color 0.2s; +} + +.login-popup-close:hover { + color: #000000; +} + +.login-popup-header { + margin-bottom: 30px; + text-align: center; +} + +.login-popup-title { + font-family: "Inter", Helvetica; + font-size: 28px; + font-weight: 700; + color: #000000; + margin: 0; +} + +.login-popup-form { + display: flex; + flex-direction: column; + gap: 20px; +} + +.login-popup-input-group { + display: flex; + flex-direction: column; + gap: 8px; +} + +.login-popup-label { + font-family: "Inter", Helvetica; + font-size: 14px; + font-weight: 600; + color: #000000; +} + +.login-popup-input { + padding: 12px 16px; + border: 1px solid #e0e0e0; + border-radius: 12px; + font-family: "Inter", Helvetica; + font-size: 16px; + transition: border-color 0.2s; +} + +.login-popup-input:focus { + outline: none; + border-color: #f6910b; +} + +.login-popup-input::placeholder { + color: #999999; +} + +.login-popup-submit-btn { + padding: 14px; + background-color: #f6910b; + color: #ffffff; + border: none; + border-radius: 12px; + font-family: "Inter", Helvetica; + font-size: 16px; + font-weight: 700; + cursor: pointer; + transition: background-color 0.2s; + margin-top: 10px; +} + +.login-popup-submit-btn:hover { + background-color: #e58209; +} + +.login-popup-footer { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + margin-top: 10px; +} + +.login-popup-footer-text { + font-family: "Inter", Helvetica; + font-size: 14px; + color: #666666; +} + +.login-popup-signup-btn { + background: none; + border: none; + color: #f6910b; + font-family: "Inter", Helvetica; + font-size: 14px; + font-weight: 700; + cursor: pointer; + text-decoration: underline; +} + +.login-popup-signup-btn:hover { + color: #e58209; +} + +.error-message { + color: #ff4d4f; + font-size: 12px; + margin-top: 5px; + display: block; + text-align: left; +} + +.input-error { + border: 1px solid #ff4d4f !important; + background-color: #fff1f0; +} diff --git a/src/components/MenuInventoryWrapper/MenuInventoryWrapper.jsx b/src/components/MenuInventoryWrapper/MenuInventoryWrapper.jsx new file mode 100644 index 0000000..9a9a275 --- /dev/null +++ b/src/components/MenuInventoryWrapper/MenuInventoryWrapper.jsx @@ -0,0 +1,19 @@ +/* +We're constantly improving the code you see. +Please share your feedback here: https://form.asana.com/?k=uvp-HPgd3_hyoXRBw1IcNg&d=1152665201300829 +*/ + +import React from "react"; +import "./style.css"; + +export const MenuInventoryWrapper = ({ className }) => { + return ( +
+
메뉴 추천
+ +

+ 당신의 성향과 냉장고를 기반으로 메뉴를 추천해드립니다. +

+
+ ); +}; diff --git a/src/components/MenuInventoryWrapper/index.js b/src/components/MenuInventoryWrapper/index.js new file mode 100644 index 0000000..0064260 --- /dev/null +++ b/src/components/MenuInventoryWrapper/index.js @@ -0,0 +1 @@ +export { MenuInventoryWrapper } from "./MenuInventoryWrapper"; diff --git a/src/components/MenuInventoryWrapper/style.css b/src/components/MenuInventoryWrapper/style.css new file mode 100644 index 0000000..0ca8d78 --- /dev/null +++ b/src/components/MenuInventoryWrapper/style.css @@ -0,0 +1,55 @@ +.menu-inventory-wrapper { + align-items: flex-start; + border-bottom-style: solid; + border-bottom-width: 1px; + border-color: #000000; + display: flex; + flex-direction: column; + gap: 10px; + left: 64px; + padding: 5px 0px; + position: relative; + top: 25px; + width: 1152px; +} + +.menu-inventory-wrapper .menu-inventory-label { + align-items: center; + align-self: stretch; + color: #000000; + display: flex; + font-family: "Inter", Helvetica; + font-size: 48px; + font-weight: 700; + justify-content: flex-start; + letter-spacing: -0.96px; + line-height: 57.6px; + margin-top: -1.00px; + position: relative; +} + +.menu-inventory-wrapper .menu-inventory-2 { + align-items: center; + align-self: stretch; + color: #0000008c; + display: flex; + font-family: "Inter", Helvetica; + font-size: 20px; + font-weight: 500; + justify-content: flex-start; + letter-spacing: -0.10px; + line-height: 29.0px; + position: relative; +} + +@media (max-width: 768px) { + .menu-inventory-wrapper .menu-inventory-label { + font-size: 32px; + line-height: 38.4px; + } + + .menu-inventory-wrapper .menu-inventory-2 { + font-size: 16px; + line-height: 23.2px; + } +} diff --git a/src/components/MypageTendency/MypageTendency.jsx b/src/components/MypageTendency/MypageTendency.jsx new file mode 100644 index 0000000..8f03892 --- /dev/null +++ b/src/components/MypageTendency/MypageTendency.jsx @@ -0,0 +1,19 @@ +/* +We're constantly improving the code you see. +Please share your feedback here: https://form.asana.com/?k=uvp-HPgd3_hyoXRBw1IcNg&d=1152665201300829 +*/ + +import React from "react"; +import "./style.css"; + +export const MypageTendency = ({ className, text = "매콤칼칼" }) => { + return ( +
+
+
+
{text}
+
+
+
+ ); +}; diff --git a/src/components/MypageTendency/index.js b/src/components/MypageTendency/index.js new file mode 100644 index 0000000..ccc46e0 --- /dev/null +++ b/src/components/MypageTendency/index.js @@ -0,0 +1 @@ +export { MypageTendency } from "./MypageTendency"; diff --git a/src/components/MypageTendency/style.css b/src/components/MypageTendency/style.css new file mode 100644 index 0000000..079a288 --- /dev/null +++ b/src/components/MypageTendency/style.css @@ -0,0 +1,55 @@ +.mypage-tendency { + align-items: center; + background-color: #f6910b; + border: 1px solid; + border-color: #0000001a; + border-radius: 16px; + box-shadow: 0px 6px 12px #00000008, 0px 4px 8px #00000005; + display: inline-flex; + flex-direction: column; + min-height: 40px; + justify-content: center; + overflow: hidden; + position: relative; + padding: 8px 16px; +} + +.mypage-tendency .tendency-name-wrapper { + align-items: flex-start; + align-self: stretch; + display: flex; + flex: 1; + flex-direction: column; + flex-grow: 1; + justify-content: center; + padding: 2px 10px; + position: relative; + width: 100%; +} + +.mypage-tendency .tendency-name-text-wrapper { + align-items: flex-start; + align-self: stretch; + display: flex; + flex: 0 0 auto; + flex-direction: column; + position: relative; + width: 100%; +} + +.mypage-tendency .tendency-name-text-2 { + align-items: center; + align-self: stretch; + color: #ffffff; + display: flex; + font-family: "Inter", Helvetica; + font-size: 16px; + font-weight: 700; + justify-content: center; + letter-spacing: -0.08px; + line-height: 22.4px; + margin: 0; + position: relative; + text-align: center; + white-space: nowrap; +} diff --git a/src/components/PageButton/PageButton.jsx b/src/components/PageButton/PageButton.jsx new file mode 100644 index 0000000..65c6620 --- /dev/null +++ b/src/components/PageButton/PageButton.jsx @@ -0,0 +1,18 @@ +/* +We're constantly improving the code you see. +Please share your feedback here: https://form.asana.com/?k=uvp-HPgd3_hyoXRBw1IcNg&d=1152665201300829 +*/ + +import React from "react"; +import "./style.css"; + +export const PageButton = ({ className, text = "1", isActive = false, onClick }) => { + return ( + + ); +}; diff --git a/src/components/PageButton/index.js b/src/components/PageButton/index.js new file mode 100644 index 0000000..b4b7bf5 --- /dev/null +++ b/src/components/PageButton/index.js @@ -0,0 +1 @@ +export { PageButton } from "./PageButton"; diff --git a/src/components/PageButton/style.css b/src/components/PageButton/style.css new file mode 100644 index 0000000..37f0754 --- /dev/null +++ b/src/components/PageButton/style.css @@ -0,0 +1,46 @@ +.page-button { + align-items: center; + background: none; + border: none; + border-bottom-style: solid; + border-bottom-width: 1px; + border-color: #00000033; + cursor: pointer; + display: flex; + flex-direction: column; + height: 20px; + justify-content: center; + position: relative; + width: 20px; + transition: border-color 0.2s; +} + +.page-button:hover { + border-color: #000000cc; +} + +.page-button.active { + border-color: #f6910b; +} + +.page-button .element { + align-items: center; + align-self: stretch; + color: #000000; + display: flex; + font-family: "Inter", Helvetica; + font-size: 15px; + font-weight: 600; + height: 17px; + justify-content: center; + letter-spacing: -0.30px; + line-height: 18.0px; + position: relative; + text-align: center; + white-space: nowrap; + transition: color 0.2s; +} + +.page-button.active .element { + color: #f6910b; +} diff --git a/src/components/PreferencesPopup/PreferencesPopup.jsx b/src/components/PreferencesPopup/PreferencesPopup.jsx new file mode 100644 index 0000000..8713860 --- /dev/null +++ b/src/components/PreferencesPopup/PreferencesPopup.jsx @@ -0,0 +1,261 @@ +import React, { useState, useEffect } from "react"; +import "./style.css"; +import { getAllergyInfo, getHealthGoalInfo } from "../../api/preferenceApi"; +import { makeSurveyApi } from "../../api/userInfo"; +import { useNavigate } from "react-router-dom"; + +const SPICE_LEVELS = ["안매움", "보통", "매움"]; +const CUISINE_TYPES = ["한식", "중식", "일식", "양식", "기타"]; + +export const PreferencesPopup = ({ onClose }) => { + const [healthGoalIds, setHealthGoalIds] = useState([]); + const [allergyIds, setAllergyIds] = useState([]); + const [dislikedIngredients, setDislikedIngredients] = useState(""); + const [likedIngredients, setLikedIngredients] = useState(""); + const [preferredCuisineList, setPreferredCuisineList] = useState([]); + const [spiceLevel, setSpiceLevel] = useState(""); + const [allowPushConsumption, setAllowPushConsumption] = useState(true); + const [allowPushComment, setAllowPushComment] = useState(true); + const [allowPushNudge, setAllowPushNudge] = useState(true); + + const [serverHealthGoals, setServerHealthGoals] = useState([]); + const [serverAllergies, setServerAllergies] = useState([]); + const navigate = useNavigate(); + + useEffect(() => { + const fetchData = async () => { + try { + const [healthData, allergyData] = await Promise.all([ + getHealthGoalInfo(), + getAllergyInfo(), + ]); + + setServerHealthGoals(healthData || []); + setServerAllergies(allergyData || []); + } catch (error) { + console.error("데이터 로딩 실패:", error); + alert("목록을 불러오는 데 실패했습니다."); + } + }; + + fetchData(); + }, []); + + const toggleHealthGoal = (id) => { + setHealthGoalIds((prev) => + prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id] + ); + }; + + const toggleAllergy = (id) => { + setAllergyIds((prev) => + prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id] + ); + }; + + const toggleCuisine = (cuisine) => { + setPreferredCuisineList((prev) => + prev.includes(cuisine) + ? prev.filter((item) => item !== cuisine) + : [...prev, cuisine] + ); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + + const surveyData = { + healthGoalIds, + allergyIds, + dislikedIngredients, + likedIngredients, + preferredCuisine: preferredCuisineList.join(","), + spiceLevel, + allowPushConsumption, + allowPushComment, + allowPushNudge, + }; + + console.log("Sending survey data:", surveyData); + + try { + const result = await makeSurveyApi(surveyData); + + if (result) { + alert("성향 설정이 완료되었습니다! 가입을 환영합니다."); + onClose(); + navigate("/desktop"); + } + } catch (error) { + console.error("설문 전송 실패:", error); + alert("저장에 실패했습니다. 다시 시도해주세요."); + } + }; + + return ( +
+
e.stopPropagation()} + > + + +
+

성향 설정

+

+ 맞춤 추천을 위한 정보를 입력해주세요 +

+
+ +
+
+ +
+ {serverHealthGoals.length > 0 ? ( + serverHealthGoals.map((goal) => ( + + )) + ) : ( +

+ 목록을 불러오는 중... +

+ )} +
+
+ +
+ +
+ {serverAllergies.length > 0 ? ( + serverAllergies.map((allergy) => ( + + )) + ) : ( +

+ 목록을 불러오는 중... +

+ )} +
+
+ +
+ + setDislikedIngredients(e.target.value)} + placeholder="예: 오이, 가지" + /> +
+ +
+ + setLikedIngredients(e.target.value)} + placeholder="예: 돼지고기, 감자" + /> +
+ +
+ +
+ {CUISINE_TYPES.map((cuisine) => ( + + ))} +
+
+ +
+ + +
+ +
+ +
+ + + +
+
+ + +
+
+
+ ); +}; diff --git a/src/components/PreferencesPopup/UpdatePreference.jsx b/src/components/PreferencesPopup/UpdatePreference.jsx new file mode 100644 index 0000000..a22195f --- /dev/null +++ b/src/components/PreferencesPopup/UpdatePreference.jsx @@ -0,0 +1,270 @@ +import React, { useState, useEffect } from "react"; +import "./style.css"; +import { getUserInfo, updateUserTendency } from "../../api/userInfo"; +import { getAllergyInfo, getHealthGoalInfo } from "../../api/preferenceApi"; + +const SPICE_LEVELS = ["안매움", "보통", "매움"]; +const CUISINE_TYPES = ["한식", "중식", "일식", "양식", "기타"]; + +function UpdatePreference({ onClose }) { + const [healthGoalIds, setHealthGoalIds] = useState([]); + const [allergyIds, setAllergyIds] = useState([]); + + const [dislikedIngredients, setDislikedIngredients] = useState(""); + const [likedIngredients, setLikedIngredients] = useState(""); + + const [preferredCuisineList, setPreferredCuisineList] = useState([]); + const [spiceLevel, setSpiceLevel] = useState(""); + + const [allowPushConsumption, setAllowPushConsumption] = useState(false); + const [allowPushComment, setAllowPushComment] = useState(false); + const [allowPushNudge, setAllowPushNudge] = useState(false); + + const [serverHealthGoals, setServerHealthGoals] = useState([]); + const [serverAllergies, setServerAllergies] = useState([]); + + useEffect(() => { + const fetchData = async () => { + try { + const [healthData, allergyData, myData] = await Promise.all([ + getHealthGoalInfo(), + getAllergyInfo(), + getUserInfo(), + ]); + + const healthList = healthData || []; + const allergyList = allergyData || []; + + setServerHealthGoals(healthList); + setServerAllergies(allergyList); + + if (myData) { + const myGoalIds = (myData.goals || []) + .map((name) => healthList.find((h) => h.name === name)?.id) + .filter((id) => id !== undefined); + setHealthGoalIds(myGoalIds); + + const myAllergyIds = (myData.allergyList || []) + .map((name) => allergyList.find((a) => a.name === name)?.id) + .filter((id) => id !== undefined); + setAllergyIds(myAllergyIds); + + setDislikedIngredients(myData.dislikedIngredients || ""); + + setLikedIngredients(myData.likedIngredients || ""); + + setSpiceLevel(myData.spiceLevel || ""); + + if (myData.preferredCuisine) { + setPreferredCuisineList(myData.preferredCuisine.split(",")); + } + + setAllowPushConsumption(myData.allowPushConsumption); + setAllowPushComment(myData.allowPushComment); + setAllowPushNudge(myData.allowPushNudge); + } + } catch (error) { + console.error("데이터 로딩 실패:", error); + alert("정보를 불러오는 데 실패했습니다."); + onClose(); + } + }; + + fetchData(); + }, [onClose]); + + const toggleHealthGoal = (id) => { + setHealthGoalIds((prev) => + prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id] + ); + }; + + const toggleAllergy = (id) => { + setAllergyIds((prev) => + prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id] + ); + }; + + const toggleCuisine = (cuisine) => { + setPreferredCuisineList((prev) => + prev.includes(cuisine) + ? prev.filter((item) => item !== cuisine) + : [...prev, cuisine] + ); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + + const updatedData = { + healthGoalIds, + allergyIds, + dislikedIngredients, + likedIngredients, + preferredCuisine: preferredCuisineList.join(","), + spiceLevel, + allowPushConsumption, + allowPushComment, + allowPushNudge, + }; + + try { + const result = await updateUserTendency(updatedData); + if (result) { + alert("성향 정보가 수정되었습니다."); + onClose(); + window.location.reload(); + } + } catch (error) { + console.error("수정 실패:", error); + alert("정보 수정에 실패했습니다."); + } + }; + + return ( +
+
e.stopPropagation()} + > + +
+

성향 수정

+

+ 변경할 내용을 선택해주세요 +

+
+ +
+
+ +
+ {serverHealthGoals.map((goal) => ( + + ))} +
+
+ +
+ +
+ {serverAllergies.map((allergy) => ( + + ))} +
+
+ +
+ + setDislikedIngredients(e.target.value)} + /> +
+ +
+ + setLikedIngredients(e.target.value)} + /> +
+ +
+ +
+ {CUISINE_TYPES.map((cuisine) => ( + + ))} +
+
+ +
+ + +
+ +
+ +
+ + + +
+
+ + +
+
+
+ ); +} + +export default UpdatePreference; diff --git a/src/components/PreferencesPopup/index.js b/src/components/PreferencesPopup/index.js new file mode 100644 index 0000000..d571f4a --- /dev/null +++ b/src/components/PreferencesPopup/index.js @@ -0,0 +1 @@ +export { PreferencesPopup } from "./PreferencesPopup"; diff --git a/src/components/PreferencesPopup/style.css b/src/components/PreferencesPopup/style.css new file mode 100644 index 0000000..f313960 --- /dev/null +++ b/src/components/PreferencesPopup/style.css @@ -0,0 +1,193 @@ +.preferences-popup-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + animation: fadeIn 0.2s ease-in-out; +} + +.preferences-popup-container { + background-color: #ffffff; + border-radius: 20px; + padding: 40px; + width: 90%; + max-width: 600px; + max-height: 90vh; + overflow-y: auto; + position: relative; + box-shadow: 0px 10px 40px rgba(0, 0, 0, 0.2); + animation: slideUp 0.3s ease-out; +} + +.preferences-popup-close { + position: absolute; + top: 15px; + right: 15px; + background: none; + border: none; + font-size: 32px; + color: #666666; + cursor: pointer; + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + transition: color 0.2s; + z-index: 1; +} + +.preferences-popup-close:hover { + color: #000000; +} + +.preferences-popup-header { + margin-bottom: 30px; + text-align: center; +} + +.preferences-popup-title { + font-family: "Inter", Helvetica; + font-size: 28px; + font-weight: 700; + color: #000000; + margin: 0 0 10px 0; +} + +.preferences-popup-subtitle { + font-family: "Inter", Helvetica; + font-size: 16px; + font-weight: 500; + color: #666666; + margin: 0; +} + +.preferences-popup-form { + display: flex; + flex-direction: column; + gap: 25px; +} + +.preferences-popup-section { + display: flex; + flex-direction: column; + gap: 12px; +} + +.preferences-popup-section-label { + font-family: "Inter", Helvetica; + font-size: 16px; + font-weight: 700; + color: #000000; +} + +.preferences-popup-chips { + display: flex; + flex-wrap: wrap; + gap: 10px; +} + +.preferences-chip { + padding: 8px 16px; + background-color: #e0e0e0; + border: 2px solid transparent; + border-radius: 20px; + color: #666666; + cursor: pointer; + font-family: "Inter", Helvetica; + font-size: 14px; + font-weight: 600; + transition: all 0.2s; +} + +.preferences-chip:hover { + background-color: #d0d0d0; +} + +.preferences-chip.active { + background-color: #f6910b; + border-color: #f6910b; + color: #ffffff; +} + +.preferences-popup-input-group { + display: flex; + flex-direction: column; + gap: 8px; +} + +.preferences-popup-label { + font-family: "Inter", Helvetica; + font-size: 14px; + font-weight: 600; + color: #000000; +} + +.preferences-popup-input, +.preferences-popup-select { + padding: 12px 16px; + border: 1px solid #e0e0e0; + border-radius: 12px; + font-family: "Inter", Helvetica; + font-size: 16px; + transition: border-color 0.2s; + background-color: #ffffff; +} + +.preferences-popup-input:focus, +.preferences-popup-select:focus { + outline: none; + border-color: #f6910b; +} + +.preferences-popup-input::placeholder { + color: #999999; +} + +.preferences-popup-checkboxes { + display: flex; + flex-direction: column; + gap: 12px; +} + +.preferences-checkbox-label { + display: flex; + align-items: center; + gap: 10px; + cursor: pointer; + font-family: "Inter", Helvetica; + font-size: 14px; + font-weight: 500; + color: #000000; +} + +.preferences-checkbox-label input[type="checkbox"] { + width: 20px; + height: 20px; + cursor: pointer; + accent-color: #f6910b; +} + +.preferences-popup-submit-btn { + padding: 14px; + background-color: #f6910b; + color: #ffffff; + border: none; + border-radius: 12px; + font-family: "Inter", Helvetica; + font-size: 16px; + font-weight: 700; + cursor: pointer; + transition: background-color 0.2s; + margin-top: 10px; +} + +.preferences-popup-submit-btn:hover { + background-color: #e58209; +} diff --git a/src/components/Recipepage/Recipepage.jsx b/src/components/Recipepage/Recipepage.jsx new file mode 100644 index 0000000..df0a519 --- /dev/null +++ b/src/components/Recipepage/Recipepage.jsx @@ -0,0 +1,15 @@ +/* +We're constantly improving the code you see. +Please share your feedback here: https://form.asana.com/?k=uvp-HPgd3_hyoXRBw1IcNg&d=1152665201300829 +*/ + +import React from "react"; +import "./style.css"; + +export const Recipepage = ({ className, text = "닭고기" }) => { + return ( +
+
{text}
+
+ ); +}; diff --git a/src/components/Recipepage/index.js b/src/components/Recipepage/index.js new file mode 100644 index 0000000..8a0ee11 --- /dev/null +++ b/src/components/Recipepage/index.js @@ -0,0 +1 @@ +export { Recipepage } from "./Recipepage"; diff --git a/src/components/Recipepage/style.css b/src/components/Recipepage/style.css new file mode 100644 index 0000000..0ffc920 --- /dev/null +++ b/src/components/Recipepage/style.css @@ -0,0 +1,31 @@ +.recipepage { + align-items: center; + border: 1px solid; + border-color: #0000001a; + border-radius: 16px; + display: inline-flex; + flex-direction: column; + gap: 10px; + justify-content: center; + left: 10px; + padding: 5px 15px; + position: relative; + top: 10px; +} + +.recipepage .text-wrapper-11 { + align-items: center; + color: #000000cc; + display: flex; + font-family: "Inter", Helvetica; + font-size: 24px; + font-weight: 500; + justify-content: center; + letter-spacing: -0.12px; + line-height: 33.6px; + margin-top: -1.00px; + position: relative; + text-align: center; + white-space: nowrap; + width: fit-content; +} diff --git a/src/components/ScrollToTop/ScrollToTop.jsx b/src/components/ScrollToTop/ScrollToTop.jsx new file mode 100644 index 0000000..276b090 --- /dev/null +++ b/src/components/ScrollToTop/ScrollToTop.jsx @@ -0,0 +1,25 @@ +import { useLayoutEffect } from 'react'; +import { useLocation } from 'react-router-dom'; + +export const ScrollToTop = () => { + const { pathname } = useLocation(); + + useLayoutEffect(() => { + const scrollToTop = () => { + // Attempt to scroll immediately with instant behavior + window.scrollTo({ top: 0, behavior: 'instant' }); + document.documentElement.scrollTo({ top: 0, behavior: 'instant' }); + document.body.scrollTo({ top: 0, behavior: 'instant' }); + }; + + scrollToTop(); // Run once immediately + + // Add a small timeout as a fallback for stubborn cases + // This ensures it runs after any potential immediate DOM updates + const timer = setTimeout(scrollToTop, 0); + + return () => clearTimeout(timer); // Clean up the timer + }, [pathname]); + + return null; +}; diff --git a/src/components/ScrollToTop/index.js b/src/components/ScrollToTop/index.js new file mode 100644 index 0000000..04f3393 --- /dev/null +++ b/src/components/ScrollToTop/index.js @@ -0,0 +1 @@ +export { ScrollToTop } from "./ScrollToTop"; diff --git a/src/components/SignupPopup/SignupPopup.jsx b/src/components/SignupPopup/SignupPopup.jsx new file mode 100644 index 0000000..de088c7 --- /dev/null +++ b/src/components/SignupPopup/SignupPopup.jsx @@ -0,0 +1,245 @@ +import React, { useState } from "react"; +import "./style.css"; +import { signUpApi } from "../../api/authApi"; +import { isEmail, isNotEmpty, isPasswordValid } from "../util/validation"; + +export const SignupPopup = ({ + onClose, + onSwitchToLogin, + onSwitchToPreferences, +}) => { + const [userId, setUserId] = useState(""); + const [password, setPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + const [email, setEmail] = useState(""); + const [gender, setGender] = useState(""); + const [birthDate, setBirthDate] = useState(""); + + const [errors, setErrors] = useState({ + userId: "", + password: "", + confirmPassword: "", + email: "", + gender: "", + birthDate: "", + }); + + const handleSignup = async (e) => { + e.preventDefault(); + + let newErrors = {}; + + if (!isNotEmpty(userId)) { + newErrors.userId = "아이디를 입력해주세요."; + } + + if (!isNotEmpty(email)) { + newErrors.email = "이메일을 입력해주세요."; + } else if (!isEmail(email)) { + newErrors.email = "올바른 이메일 형식이 아닙니다."; + } + + if (!isNotEmpty(password)) { + newErrors.password = "비밀번호를 입력해주세요."; + } else if (!isPasswordValid(password)) { + newErrors.password = "영문자, 특수문자 포함 10~20자로 작성해주세요."; + } + + if (password !== confirmPassword) { + newErrors.confirmPassword = "비밀번호가 일치하지 않습니다."; + } + + if (!isNotEmpty(gender)) { + newErrors.gender = "성별을 선택해주세요."; + } + + if (!isNotEmpty(birthDate)) { + newErrors.birthDate = "생년월일을 입력해주세요."; + } + + setErrors(newErrors); + if (Object.keys(newErrors).length > 0) { + return; + } + + const userData = { + userId, + password, + email, + gender, + birthDate: `${birthDate}T00:00:00`, + }; + console.log("Signup attempt:", userData); + + try { + const result = await signUpApi(userData); + + if (result) { + console.log("생성된 아이디 : " + result.userId); + onSwitchToPreferences(userData); + } else { + alert("회원가입 실패: 다시 시도해주세요."); + } + } catch (error) { + console.error("회원가입 에러:", error); + alert("서버 오류가 발생했습니다."); + } + }; + + const handleInputChange = (setter, field, value) => { + setter(value); + if (errors[field]) { + setErrors({ ...errors, [field]: "" }); + } + }; + + return ( +
+
e.stopPropagation()} + > + + +
+

회원가입

+
+ +
+
+ + + handleInputChange(setUserId, "userId", e.target.value) + } + placeholder="아이디를 입력하세요" + /> + {errors.userId && ( + {errors.userId} + )} +
+ +
+ + + handleInputChange(setEmail, "email", e.target.value) + } + placeholder="이메일을 입력하세요" + /> + {errors.email && ( + {errors.email} + )} +
+ +
+ + + handleInputChange(setPassword, "password", e.target.value) + } + placeholder="비밀번호를 입력하세요" + /> + {errors.password && ( + {errors.password} + )} +
+ +
+ + + handleInputChange( + setConfirmPassword, + "confirmPassword", + e.target.value + ) + } + placeholder="비밀번호를 다시 입력하세요" + /> + {errors.confirmPassword && ( + {errors.confirmPassword} + )} +
+ +
+ + + {errors.gender && ( + {errors.gender} + )} +
+ +
+ + + handleInputChange(setBirthDate, "birthDate", e.target.value) + } + /> + {errors.birthDate && ( + {errors.birthDate} + )} +
+ + + +
+ + 이미 계정이 있으신가요? + + +
+
+
+
+ ); +}; diff --git a/src/components/SignupPopup/index.js b/src/components/SignupPopup/index.js new file mode 100644 index 0000000..de7e73a --- /dev/null +++ b/src/components/SignupPopup/index.js @@ -0,0 +1 @@ +export { SignupPopup } from "./SignupPopup"; diff --git a/src/components/SignupPopup/style.css b/src/components/SignupPopup/style.css new file mode 100644 index 0000000..92d1b00 --- /dev/null +++ b/src/components/SignupPopup/style.css @@ -0,0 +1,167 @@ +.signup-popup-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + animation: fadeIn 0.2s ease-in-out; +} + +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +.signup-popup-container { + background-color: #ffffff; + border-radius: 20px; + padding: 40px; + width: 90%; + max-width: 400px; + position: relative; + box-shadow: 0px 10px 40px rgba(0, 0, 0, 0.2); + animation: slideUp 0.3s ease-out; + max-height: 90vh; + overflow-y: auto; +} + +@keyframes slideUp { + from { + transform: translateY(20px); + opacity: 0; + } + to { + transform: translateY(0); + opacity: 1; + } +} + +.signup-popup-close { + position: absolute; + top: 15px; + right: 15px; + background: none; + border: none; + font-size: 32px; + color: #666666; + cursor: pointer; + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + transition: color 0.2s; +} + +.signup-popup-close:hover { + color: #000000; +} + +.signup-popup-header { + margin-bottom: 30px; + text-align: center; +} + +.signup-popup-title { + font-family: "Inter", Helvetica; + font-size: 28px; + font-weight: 700; + color: #000000; + margin: 0; +} + +.signup-popup-form { + display: flex; + flex-direction: column; + gap: 20px; +} + +.signup-popup-input-group { + display: flex; + flex-direction: column; + gap: 8px; +} + +.signup-popup-label { + font-family: "Inter", Helvetica; + font-size: 14px; + font-weight: 600; + color: #000000; +} + +.signup-popup-input, +.signup-popup-select { + padding: 12px 16px; + border: 1px solid #e0e0e0; + border-radius: 12px; + font-family: "Inter", Helvetica; + font-size: 16px; + transition: border-color 0.2s; + background-color: #ffffff; +} + +.signup-popup-input:focus, +.signup-popup-select:focus { + outline: none; + border-color: #f6910b; +} + +.signup-popup-input::placeholder { + color: #999999; +} + +.signup-popup-submit-btn { + padding: 14px; + background-color: #f6910b; + color: #ffffff; + border: none; + border-radius: 12px; + font-family: "Inter", Helvetica; + font-size: 16px; + font-weight: 700; + cursor: pointer; + transition: background-color 0.2s; + margin-top: 10px; +} + +.signup-popup-submit-btn:hover { + background-color: #e58209; +} + +.signup-popup-footer { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + margin-top: 10px; +} + +.signup-popup-footer-text { + font-family: "Inter", Helvetica; + font-size: 14px; + color: #666666; +} + +.signup-popup-login-btn { + background: none; + border: none; + color: #f6910b; + font-family: "Inter", Helvetica; + font-size: 14px; + font-weight: 700; + cursor: pointer; + text-decoration: underline; +} + +.signup-popup-login-btn:hover { + color: #e58209; +} diff --git a/src/components/Tendency/Tendency.jsx b/src/components/Tendency/Tendency.jsx new file mode 100644 index 0000000..951a804 --- /dev/null +++ b/src/components/Tendency/Tendency.jsx @@ -0,0 +1,22 @@ +/* +We're constantly improving the code you see. +Please share your feedback here: https://form.asana.com/?k=uvp-HPgd3_hyoXRBw1IcNg&d=1152665201300829 +*/ + +import React from "react"; +import "./style.css"; + +export const Tendency = ({ tendencyContainerClassName, text = "매콤칼칼", isActive = false, onClick }) => { + return ( + + ); +}; diff --git a/src/components/Tendency/index.js b/src/components/Tendency/index.js new file mode 100644 index 0000000..4ba92b2 --- /dev/null +++ b/src/components/Tendency/index.js @@ -0,0 +1 @@ +export { Tendency } from "./Tendency"; diff --git a/src/components/Tendency/style.css b/src/components/Tendency/style.css new file mode 100644 index 0000000..b02e42c --- /dev/null +++ b/src/components/Tendency/style.css @@ -0,0 +1,70 @@ +.tendency { + align-items: center; + background-color: #e0e0e0; + border: 1px solid; + border-color: #0000001a; + border-radius: 16px; + box-shadow: 0px 6px 12px #00000008, 0px 4px 8px #00000005; + cursor: pointer; + display: inline-flex; + justify-content: center; + overflow: hidden; + padding: 6px 16px; + position: relative; + transition: all 0.2s ease; + min-width: fit-content; +} + +.tendency:hover { + transform: translateY(-2px); + box-shadow: 0px 8px 16px #00000012, 0px 6px 10px #00000008; +} + +.tendency.tendency-active { + background-color: #f6910b; +} + +.tendency .tendency-container { + align-items: center; + display: inline-flex; + flex-direction: column; + justify-content: center; + position: relative; +} + +.tendency .tendency-name { + align-items: center; + display: inline-flex; + flex-direction: column; + justify-content: center; + position: relative; +} + +.tendency .tendency-name-text { + align-items: center; + color: #666666; + display: flex; + font-family: "Inter", Helvetica; + font-size: 12px; + font-weight: 700; + justify-content: center; + letter-spacing: -0.06px; + line-height: 16.8px; + text-align: center; + white-space: nowrap; + transition: color 0.2s ease; +} + +.tendency.tendency-active .tendency-name-text { + color: #ffffff; +} + +@media (max-width: 768px) { + .tendency { + padding: 5px 12px; + } + + .tendency .tendency-name-text { + font-size: 11px; + } +} diff --git a/src/components/UnifiedHeader/UnifiedHeader.jsx b/src/components/UnifiedHeader/UnifiedHeader.jsx new file mode 100644 index 0000000..2abd040 --- /dev/null +++ b/src/components/UnifiedHeader/UnifiedHeader.jsx @@ -0,0 +1,178 @@ +import React, { useState } from "react"; +import { Link } from "react-router-dom"; +import { useAuth } from "../../contexts/AuthContext"; +import { LoginPopup } from "../LoginPopup"; +import { SignupPopup } from "../SignupPopup"; +import { PreferencesPopup } from "../PreferencesPopup"; +import FindId from "../LoginPopup/FindId/FindId"; +import FindPassword from "../LoginPopup/FindPassword/FindPassword"; +import "./style.css"; + +export const UnifiedHeader = () => { + const { user, isAuthenticated, logout } = useAuth(); + + const [showLoginPopup, setShowLoginPopup] = useState(false); + const [showSignupPopup, setShowSignupPopup] = useState(false); + const [showPreferencesPopup, setShowPreferencesPopup] = useState(false); + const [showFindIdPopup, setShowFindIdPopup] = useState(false); + const [showFindPasswordPopup, setShowFindPasswordPopup] = useState(false); + + const [showMenu, setShowMenu] = useState(false); + const [userData, setUserData] = useState(null); + const userId = localStorage.getItem("userId"); + + const handleSwitchToSignup = () => { + setShowLoginPopup(false); + setShowSignupPopup(true); + }; + + const handleSwitchToLogin = () => { + setShowSignupPopup(false); + setShowPreferencesPopup(false); + setShowFindIdPopup(false); + setShowFindPasswordPopup(false); + setShowLoginPopup(true); + }; + + const handleSwitchToFindId = () => { + setShowLoginPopup(false); + setShowFindIdPopup(true); + }; + + const handleSwitchToFindPassword = () => { + setShowLoginPopup(false); + setShowFindPasswordPopup(true); + }; + + const handleSwitchToPreferences = (data) => { + setUserData(data); + setShowSignupPopup(false); + setShowPreferencesPopup(true); + }; + + const handleCloseAll = () => { + setShowLoginPopup(false); + setShowSignupPopup(false); + setShowPreferencesPopup(false); + setShowFindIdPopup(false); + setShowFindPasswordPopup(false); + setUserData(null); + }; + + return ( + <> +
+
+ + {showMenu && ( +
+ setShowMenu(false)} + > + 홈 + + setShowMenu(false)} + > + 메뉴 추천 + + setShowMenu(false)} + > + 재료 나눔 게시판 + + setShowMenu(false)} + > + 마이페이지 + +
+ )} + + Logo + +
+ + +

+ 알고리즘 + 셰프 +

+ + +
+ +
마이페이지
+ + {isAuthenticated ? ( +
+ {userId}님 + +
+ ) : ( + + )} +
+
+ + {showLoginPopup && ( + + )} + + {showSignupPopup && ( + + )} + + {showPreferencesPopup && ( + + )} + + {showFindIdPopup && ( + + )} + + {showFindPasswordPopup && ( + + )} + + ); +}; diff --git a/src/components/UnifiedHeader/index.js b/src/components/UnifiedHeader/index.js new file mode 100644 index 0000000..cab05e2 --- /dev/null +++ b/src/components/UnifiedHeader/index.js @@ -0,0 +1 @@ +export { UnifiedHeader } from "./UnifiedHeader"; diff --git a/src/components/UnifiedHeader/style.css b/src/components/UnifiedHeader/style.css new file mode 100644 index 0000000..af3725a --- /dev/null +++ b/src/components/UnifiedHeader/style.css @@ -0,0 +1,268 @@ +.unified-header { + align-items: center; + background-color: transparent; + display: flex; + justify-content: space-between; + gap: 10px; + height: 120px; + padding: 10px 20px; + position: relative; + width: 100%; + max-width: 1280px; + margin: 0 auto; +} + +.unified-header-left { + display: flex; + align-items: center; + gap: 15px; + flex: 1; + justify-content: flex-start; + position: relative; +} + +.menu-toggle-btn { + background: none; + border: none; + font-size: 28px; + color: #000000; + cursor: pointer; + padding: 5px 10px; + transition: color 0.2s; +} + +.menu-toggle-btn:hover { + color: #f6910b; +} + +.menu-dropdown { + position: absolute; + top: 100%; + left: 0; + margin-top: 10px; + background-color: #ffffff; + border: 1px solid #e0e0e0; + border-radius: 12px; + box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1); + overflow: hidden; + z-index: 100; + min-width: 200px; +} + +.menu-item { + display: block; + padding: 15px 20px; + color: #000000; + font-family: "Inter", Helvetica; + font-size: 16px; + font-weight: 500; + text-decoration: none; + transition: background-color 0.2s; + border-bottom: 1px solid #f0f0f0; +} + +.menu-item:last-child { + border-bottom: none; +} + +.menu-item:hover { + background-color: #f6910b1a; + color: #f6910b; +} + +.unified-header-logo { + height: 80px; + width: auto; + display: block; +} + +.unified-header-center { + position: absolute; + left: 50%; + transform: translateX(-50%); + display: flex; + align-items: center; + height: 100%; + text-decoration: none; +} + +.unified-header-title { + align-items: center; + color: transparent; + display: flex; + font-family: "Inter", Helvetica; + font-size: 48px; + font-weight: 700; + justify-content: center; + letter-spacing: -0.96px; + line-height: 1; + margin: 0; + text-align: center; +} + +.title-text-black { + color: #000000; + letter-spacing: -0.46px; +} + +.title-text-orange { + color: #f6910b; + letter-spacing: -0.46px; +} + +.unified-header-right { + display: flex; + align-items: center; + flex: 1; + gap: 10px; + justify-content: flex-end; +} + +.unified-mypage-btn { + background-image: url(https://c.animaapp.com/sjWITF5i/img/loginvector-3.svg); + background-size: 100% 100%; + border: none; + border-radius: 30px; + cursor: pointer; + height: 36px; + overflow: hidden; + position: relative; + width: 120px; + transition: transform 0.2s; + text-decoration: none; + display: flex; + align-items: center; + justify-content: center; +} + +.unified-mypage-btn:hover { + transform: scale(1.05); +} + +.unified-mypage-text { + color: #ffffff; + font-family: "Inter", Helvetica; + font-size: 16px; + font-weight: 600; + letter-spacing: -0.32px; + line-height: 19.2px; + text-align: center; +} + +.unified-login-btn { + background-image: url(https://c.animaapp.com/sjWITF5i/img/loginvector-3.svg); + background-size: 100% 100%; + border: none; + border-radius: 30px; + cursor: pointer; + height: 36px; + overflow: hidden; + position: relative; + width: 120px; + transition: transform 0.2s; +} + +.unified-login-btn:hover { + transform: scale(1.05); +} + +.unified-login-text { + align-items: center; + color: #ffffff; + display: flex; + font-family: "Inter", Helvetica; + font-size: 20px; + font-weight: 600; + height: 24px; + justify-content: center; + left: calc(50.00% - 33px); + letter-spacing: -0.40px; + line-height: 24.0px; + position: absolute; + text-align: center; + top: calc(50.00% - 12px); + width: 66px; +} + +.unified-user-info { + display: flex; + align-items: center; + gap: 10px; +} + +.unified-username { + color: #000000; + font-family: "Inter", Helvetica; + font-size: 16px; + font-weight: 600; + padding: 8px 16px; + background-color: #f6910b1a; + border-radius: 20px; +} + +.unified-logout-btn { + background-image: url(https://c.animaapp.com/sjWITF5i/img/loginvector-3.svg); + background-size: 100% 100%; + border: none; + border-radius: 30px; + cursor: pointer; + height: 36px; + padding: 0 20px; + position: relative; + transition: transform 0.2s; + color: #ffffff; + font-family: "Inter", Helvetica; + font-size: 16px; + font-weight: 600; +} + +.unified-logout-btn:hover { + transform: scale(1.05); +} + +@media (max-width: 768px) { + .unified-header { + padding: 10px 10px; + height: auto; + min-height: 80px; + } + + .unified-header-logo { + height: 50px; + } + + .unified-header-title { + font-size: 24px; + } + + .unified-mypage-btn, + .unified-login-btn, + .unified-logout-btn { + width: 90px; + height: 32px; + } + + .unified-mypage-text, + .unified-login-text { + font-size: 12px; + } + + .unified-username { + font-size: 12px; + padding: 4px 10px; + } + + .menu-toggle-btn { + font-size: 24px; + } +} + +@media (max-width: 480px) { + .unified-header-title { + font-size: 20px; + } + + .unified-header-right { + gap: 5px; + } +} diff --git a/src/components/UsedIngredient/UsedIngredient.jsx b/src/components/UsedIngredient/UsedIngredient.jsx new file mode 100644 index 0000000..24ef221 --- /dev/null +++ b/src/components/UsedIngredient/UsedIngredient.jsx @@ -0,0 +1,15 @@ +/* +We're constantly improving the code you see. +Please share your feedback here: https://form.asana.com/?k=uvp-HPgd3_hyoXRBw1IcNg&d=1152665201300829 +*/ + +import React from "react"; +import "./style.css"; + +export const UsedIngredient = ({ className, text = "닭고기" }) => { + return ( +
+
{text}
+
+ ); +}; diff --git a/src/components/UsedIngredient/index.js b/src/components/UsedIngredient/index.js new file mode 100644 index 0000000..fc3b90e --- /dev/null +++ b/src/components/UsedIngredient/index.js @@ -0,0 +1 @@ +export { UsedIngredient } from "./UsedIngredient"; diff --git a/src/components/UsedIngredient/style.css b/src/components/UsedIngredient/style.css new file mode 100644 index 0000000..0ce632e --- /dev/null +++ b/src/components/UsedIngredient/style.css @@ -0,0 +1,32 @@ +.used-ingredient { + align-items: center; + border: 1px solid; + border-color: #0000001a; + border-radius: 16px; + display: inline-flex; + flex-direction: column; + gap: 10px; + height: 23px; + justify-content: center; + left: 4px; + padding: 3px 10px; + position: relative; + top: 2px; +} + +.used-ingredient .use-ingredient-name { + align-items: center; + color: #000000cc; + display: flex; + font-family: "Inter", Helvetica; + font-size: 12px; + font-weight: 500; + justify-content: center; + letter-spacing: -0.06px; + line-height: 16.8px; + margin-top: -1.00px; + position: relative; + text-align: center; + white-space: nowrap; + width: fit-content; +} diff --git a/src/components/util/validation.js b/src/components/util/validation.js new file mode 100644 index 0000000..6439022 --- /dev/null +++ b/src/components/util/validation.js @@ -0,0 +1,34 @@ +export function isEmail(value) { + return value.includes('@'); +} + +export function isNotEmpty(value) { + return value.trim() !== ''; +} + +export function hasMinLength(value, minLength) { + return value.length >= minLength; +} + +export function isEqualToOtherValue(value, otherValue) { + return value === otherValue; +} +export function isIdValid(value) { + if (!value || typeof value !== 'string') { + return false; + } + + const regExp = /^[a-zA-Z0-9]+$/; + + return regExp.test(value); +} + +export function isPasswordValid(value) { + if (!value || typeof value !== 'string') { + return false; + } + + const regExp = /^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*?_])[a-zA-Z0-9!@#$%^&*?_]{8,20}$/; + + return regExp.test(value); +} \ No newline at end of file diff --git a/src/contexts/AuthContext.js b/src/contexts/AuthContext.js new file mode 100644 index 0000000..328bb52 --- /dev/null +++ b/src/contexts/AuthContext.js @@ -0,0 +1 @@ +export { AuthProvider, useAuth } from "./AuthContext.jsx"; diff --git a/src/contexts/AuthContext.jsx b/src/contexts/AuthContext.jsx new file mode 100644 index 0000000..2e90c55 --- /dev/null +++ b/src/contexts/AuthContext.jsx @@ -0,0 +1,53 @@ +import React, { createContext, useContext, useState, useEffect } from "react"; +import { loginAPi } from "../api/authApi"; + +const AuthContext = createContext(); + +export const useAuth = () => { + const context = useContext(AuthContext); + if (!context) { + throw new Error("useAuth must be used within an AuthProvider"); + } + return context; +}; + +export const AuthProvider = ({ children }) => { + const [user, setUser] = useState(null); + const [isAuthenticated, setIsAuthenticated] = useState(false); + + useEffect(() => { + const token = localStorage.getItem("accessToken"); + if (token) { + setIsAuthenticated(true); + } + }, []); + + const login = async (userId, password) => { + try { + const data = await loginAPi(userId, password); + if (data.accessToken) { + localStorage.setItem("accessToken", data.accessToken); + } + + if (data.status === "SUCCESS") { + setIsAuthenticated(true); + } + return data; + } catch (error) { + console.error("α :", error); + return { status: "FAIL", message: "α ߻" }; + } + }; + + const logout = () => { + setUser(null); + setIsAuthenticated(false); + localStorage.removeItem("currentUser"); + }; + + return ( + + {children} + + ); +}; diff --git a/src/index.css b/src/index.css deleted file mode 100644 index 08a3ac9..0000000 --- a/src/index.css +++ /dev/null @@ -1,68 +0,0 @@ -:root { - font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} - -body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } -} diff --git a/src/index.jsx b/src/index.jsx new file mode 100644 index 0000000..f55d523 --- /dev/null +++ b/src/index.jsx @@ -0,0 +1,9 @@ +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import { App } from "./App"; + +createRoot(document.getElementById("app")).render( + + + , +); diff --git a/src/main.jsx b/src/main.jsx deleted file mode 100644 index b9a1a6d..0000000 --- a/src/main.jsx +++ /dev/null @@ -1,10 +0,0 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import './index.css' -import App from './App.jsx' - -createRoot(document.getElementById('root')).render( - - - , -) diff --git a/src/screens/AddIngredientPage/AddIngredientPage.jsx b/src/screens/AddIngredientPage/AddIngredientPage.jsx new file mode 100644 index 0000000..b500e2a --- /dev/null +++ b/src/screens/AddIngredientPage/AddIngredientPage.jsx @@ -0,0 +1,286 @@ +import React, { useState, useRef, useEffect } from "react"; +import { Link, useNavigate } from "react-router-dom"; +import { UnifiedHeader } from "../../components/UnifiedHeader"; +import { BiSolidRightArrow, BiSolidLeftArrow } from "react-icons/bi"; +import "./style.css"; +import { useAuth } from "../../contexts/AuthContext"; +import { registerIngredientManual } from "../../api/fridgeApi"; +import { updateIngredients } from "../../api/fridgeApi"; +import { + filterIngredientsByName, + getIngredientsInfo, + filterIngredientsByCategory, +} from "../../api/ingredientsApi"; + +import { AddIngredientPopup } from "../../components/AddIngredientPopup/AddIngredientPopup"; + +const CATEGORIES = [ + "전체", + "육류", + "가공육", + "난류", + "수산물", + "가공수산물", + "건어물", + "젓갈", + "채소", + "버섯", + "유제품", + "두부류", + "콩가공품", + "콩", + "김치류", + "절임류", + "빵류", + "기타", + "가공식품", + "과일", + "조미료", + "향신료", + "곡류", + "견과", +]; +const DEFAULT_IMAGE_URL = + "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"; + +export const AddIngredientPage = () => { + const navigate = useNavigate(); + const scrollRef = useRef(null); + + const [selectedCategory, setSelectedCategory] = useState("전체"); + const [searchQuery, setSearchQuery] = useState(""); + const [selectedIngredients, setSelectedIngredients] = useState([]); + const [previewIngredients, setPreviewIngredients] = useState([]); + const [page, setPage] = useState(0); + const itemsPerPage = 7; + const [ingredients, setIngredients] = useState([]); + const { isAuthenticated } = useAuth(); + const [currentView, setCurrentView] = useState(false); + + useEffect(() => { + setPage(0); + }, [searchQuery, selectedCategory]); + + useEffect(() => { + if (!isAuthenticated) { + setIngredients([]); + return; + } + + const fetchData = async () => { + let ingredientData = []; + try { + if (searchQuery) { + ingredientData = await filterIngredientsByName( + page, + itemsPerPage, + searchQuery + ); + } else if (selectedCategory && selectedCategory !== "전체") { + ingredientData = await filterIngredientsByCategory( + page, + itemsPerPage, + selectedCategory + ); + } else { + ingredientData = await getIngredientsInfo(page, itemsPerPage); + } + + if (ingredientData.content) { + setIngredients(ingredientData.content); + } else if (Array.isArray(ingredientData)) { + setIngredients(ingredientData); + } else { + setIngredients([]); + } + } catch (error) { + console.error("재료 데이터 로딩 실패"); + } + }; + fetchData(); + }, [isAuthenticated, searchQuery, selectedCategory, page]); + + function handlePrev() { + setPage((prev) => Math.max(prev - 1, 0)); + } + function handleNext() { + if (ingredients.length === itemsPerPage) setPage((prev) => prev + 1); + } + + const toggleIngredient = (ingredient) => { + setSelectedIngredients((prev) => { + const exists = prev.find((item) => item.id === ingredient.id); + if (exists) { + return prev.filter((item) => item.id !== ingredient.id); + } else { + const today = new Date().toISOString().split("T")[0]; + return [ + ...prev, + { + ...ingredient, + purchaseDate: today, + image: DEFAULT_IMAGE_URL, + }, + ]; + } + }); + }; + + const handleAddIngredients = async () => { + if (selectedIngredients.length === 0) { + alert("추가할 식재료를 선택해주세요."); + return; + } + + try { + const requestData = selectedIngredients.map((item) => ({ + name: item.name, + purchaseDate: `${item.purchaseDate}T00:00:00`, + })); + + const response = await registerIngredientManual(requestData); + + if (response && response.ingredients) { + setPreviewIngredients(response.ingredients); + setCurrentView("확인"); + } + } catch (error) { + console.error("1차 등록 실패:", error); + alert("재료 정보를 불러오는 데 실패했습니다."); + } + }; + + const handleFinalRegister = async (updatedItems) => { + try { + const requestData = { + ingredients: updatedItems.map((item) => ({ + ingredientId: item.id, + purchaseDate: `${item.purchaseDate}T00:00:00`, + expiredDate: item.expiredDate ? `${item.expiredDate}T00:00:00` : null, + })), + }; + + const result = await updateIngredients(requestData); + + if (result) { + alert("재료가 성공적으로 저장되었습니다!"); + setSelectedIngredients([]); + setPreviewIngredients([]); + setCurrentView(false); + navigate("/desktop"); + } + } catch (error) { + console.error("최종 저장 실패:", error); + alert("재료 수정에 실패했습니다."); + } + }; + + if (currentView === "확인") { + return ( + setCurrentView(false)} + /> + ); + } + + return ( +
+ +
+
+

식재료 추가

+

+ 냉장고에 추가할 식재료를 선택하세요 +

+
+ +
+ setSearchQuery(e.target.value)} + /> +
+ +
+ {CATEGORIES.map((category) => ( + + ))} +
+ + {/* 가로 스크롤 그리드 */} +
+ + +
+ {ingredients.map((ingredient) => { + const isSelected = selectedIngredients.find( + (item) => item.id === ingredient.id + ); + return ( + + ); + })} +
+ + +
+ +
+
+ 선택된 식재료: {selectedIngredients.length}개 +
+
+ + 취소 + + +
+
+
+
+ ); +}; diff --git a/src/screens/AddIngredientPage/index.js b/src/screens/AddIngredientPage/index.js new file mode 100644 index 0000000..13def78 --- /dev/null +++ b/src/screens/AddIngredientPage/index.js @@ -0,0 +1 @@ +export { AddIngredientPage } from "./AddIngredientPage"; diff --git a/src/screens/AddIngredientPage/style.css b/src/screens/AddIngredientPage/style.css new file mode 100644 index 0000000..bcadd52 --- /dev/null +++ b/src/screens/AddIngredientPage/style.css @@ -0,0 +1,358 @@ +.add-ingredient-page { + align-items: center; + background-color: #ffffff; + display: flex; + flex-direction: column; + min-height: 100vh; + position: relative; + width: 100%; +} + +.add-ingredient-page .add-ingredient-header { + align-self: stretch !important; + width: 100% !important; + max-width: 1280px !important; + margin: 0 auto !important; +} + +.add-ingredient-content { + align-items: flex-start; + display: flex; + flex: 1; + flex-direction: column; + gap: 20px; + padding: 20px 64px; + position: relative; + width: 100%; + max-width: 1280px; + margin: 0 auto; +} + +.add-ingredient-title-section { + align-items: flex-start; + align-self: stretch; + border-bottom: 1px solid #000000; + display: flex; + flex-direction: column; + gap: 10px; + padding-bottom: 20px; + width: 100%; +} + +.add-ingredient-title { + color: #000000; + font-family: "Inter", Helvetica; + font-size: 48px; + font-weight: 700; + letter-spacing: -0.96px; + line-height: 57.6px; + margin: 0; +} + +.add-ingredient-subtitle { + color: #0000008c; + font-family: "Inter", Helvetica; + font-size: 20px; + font-weight: 500; + letter-spacing: -0.1px; + line-height: 29px; + margin: 0; +} + +.add-ingredient-search-section { + align-self: stretch; + display: flex; + gap: 10px; + width: 100%; +} + +.add-ingredient-search-input { + flex: 1; + padding: 12px 20px; + border: 1px solid #e0e0e0; + border-radius: 12px; + font-family: "Inter", Helvetica; + font-size: 16px; + transition: border-color 0.2s; +} + +.add-ingredient-search-input:focus { + outline: none; + border-color: #f6910b; +} + +.add-ingredient-category-section { + align-self: stretch; + display: flex; + gap: 10px; + flex-wrap: wrap; +} + +.add-ingredient-category-btn { + padding: 8px 20px; + background-color: #e0e0e0; + border: none; + border-radius: 12px; + color: #666666; + cursor: pointer; + font-family: "Inter", Helvetica; + font-size: 16px; + font-weight: 600; + transition: all 0.2s; +} + +.add-ingredient-category-btn:hover { + background-color: #d0d0d0; +} + +.add-ingredient-category-btn.active { + background-color: #f6910b; + color: #ffffff; +} + +/* --- [가로 스크롤 핵심 스타일] --- */ + +.add-ingredient-grid-wrapper { + position: relative; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + padding: 20px 0; +} + +/* Flexbox를 사용하여 가로 배치를 강제합니다 */ +.add-ingredient-grid { + align-self: stretch; + display: flex; + flex-direction: row; /* 가로 방향 명시 */ + flex-wrap: nowrap; /* 줄바꿈 금지 (핵심) */ + overflow-x: auto; /* 가로 스크롤 허용 */ + gap: 20px; + padding: 10px 5px; + width: 100%; + scroll-behavior: smooth; + + /* 스크롤바 숨기기 (옵션) */ + -ms-overflow-style: none; /* IE, Edge */ + scrollbar-width: none; /* Firefox */ +} + +.add-ingredient-grid::-webkit-scrollbar { + display: none; /* Chrome, Safari */ +} + +/* 화살표 버튼 */ +.scroll-arrow-btn { + position: absolute; + top: 50%; + transform: translateY(-50%); + width: 44px; + height: 44px; + border-radius: 50%; + background-color: #ffffff; + border: 1px solid #e0e0e0; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + z-index: 10; + color: #f6910b; + font-size: 20px; + transition: all 0.2s ease; +} + +.scroll-arrow-btn:hover { + background-color: #f6910b; + color: white; + border-color: #f6910b; + transform: scale(1.1) translateY(-45%); +} + +.scroll-arrow-btn.left { + left: -20px; +} + +.scroll-arrow-btn.right { + right: -20px; +} + +/* 재료 카드 */ +.add-ingredient-card { + align-items: center; + background-color: #ffffff; + border: 2px solid #e0e0e0; + border-radius: 16px; + cursor: pointer; + display: flex; + flex-direction: column; + gap: 10px; + padding: 15px; + position: relative; + transition: all 0.2s; + + /* ★ 카드가 찌그러지지 않도록 크기 고정 */ + min-width: 160px; /* 최소 너비 확보 */ + width: 160px; + height: 200px; + flex-shrink: 0; /* 공간이 좁아도 줄어들지 않음 (핵심) */ +} + +.add-ingredient-card:hover { + border-color: #f6910b; + transform: translateY(-4px); + box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1); +} + +.add-ingredient-card.selected { + border-color: #f6910b; + background-color: #fff8f0; +} + +.add-ingredient-card-image { + width: 90px; + height: 90px; + object-fit: contain; + margin-bottom: 5px; +} + +.add-ingredient-card-name { + color: #000000; + font-family: "Inter", Helvetica; + font-size: 16px; + font-weight: 600; + text-align: center; + width: 100%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.add-ingredient-card-category { + color: #666666; + font-family: "Inter", Helvetica; + font-size: 13px; + font-weight: 500; + text-align: center; +} + +.add-ingredient-card-check { + position: absolute; + top: 10px; + right: 10px; + width: 24px; + height: 24px; + background-color: #f6910b; + border-radius: 50%; + color: #ffffff; + display: flex; + align-items: center; + justify-content: center; + font-size: 14px; + font-weight: 700; +} + +.add-ingredient-footer { + align-items: center; + align-self: stretch; + border-top: 1px solid #e0e0e0; + display: flex; + justify-content: space-between; + padding: 20px 0; + width: 100%; + margin-top: auto; +} + +.add-ingredient-selected-count { + color: #000000; + font-family: "Inter", Helvetica; + font-size: 18px; + font-weight: 600; +} + +.add-ingredient-footer-buttons { + display: flex; + gap: 15px; +} + +.add-ingredient-cancel-btn { + padding: 12px 30px; + background-color: #e0e0e0; + border: none; + border-radius: 12px; + color: #666666; + cursor: pointer; + font-family: "Inter", Helvetica; + font-size: 16px; + font-weight: 600; + text-decoration: none; + transition: background-color 0.2s; + display: flex; + align-items: center; + justify-content: center; +} + +.add-ingredient-cancel-btn:hover { + background-color: #d0d0d0; +} + +.add-ingredient-submit-btn { + padding: 12px 30px; + background-color: #f6910b; + border: none; + border-radius: 12px; + color: #ffffff; + cursor: pointer; + font-family: "Inter", Helvetica; + font-size: 16px; + font-weight: 700; + transition: background-color 0.2s; +} + +.add-ingredient-submit-btn:hover:not(:disabled) { + background-color: #e58209; +} + +.add-ingredient-submit-btn:disabled { + background-color: #cccccc; + cursor: not-allowed; +} + +/* 반응형 스타일 */ +@media (max-width: 768px) { + .add-ingredient-content { + padding: 20px 20px; + } + + .add-ingredient-title { + font-size: 32px; + line-height: 38.4px; + } + + .add-ingredient-subtitle { + font-size: 16px; + line-height: 23.2px; + } + + .scroll-arrow-btn.left { + left: 0; + } + .scroll-arrow-btn.right { + right: 0; + } + + .add-ingredient-footer { + flex-direction: column; + gap: 15px; + } + + .add-ingredient-footer-buttons { + width: 100%; + } + + .add-ingredient-cancel-btn, + .add-ingredient-submit-btn { + flex: 1; + } +} diff --git a/src/screens/CommunityContent/CommunityContent.jsx b/src/screens/CommunityContent/CommunityContent.jsx new file mode 100644 index 0000000..d3c060f --- /dev/null +++ b/src/screens/CommunityContent/CommunityContent.jsx @@ -0,0 +1,13 @@ +import React from "react"; +import { UnifiedHeader } from "../../components/UnifiedHeader"; +import { Communitycontentpage } from "./sections/Communitycontentpage"; +import "./style.css"; + +export const CommunityContent = () => { + return ( +
+ + +
+ ); +}; diff --git a/src/screens/CommunityContent/index.js b/src/screens/CommunityContent/index.js new file mode 100644 index 0000000..9ea88ad --- /dev/null +++ b/src/screens/CommunityContent/index.js @@ -0,0 +1 @@ +export { CommunityContent } from "./CommunityContent"; diff --git a/src/screens/CommunityContent/sections/Communitycontentpage/Communitycontentpage.jsx b/src/screens/CommunityContent/sections/Communitycontentpage/Communitycontentpage.jsx new file mode 100644 index 0000000..27274e1 --- /dev/null +++ b/src/screens/CommunityContent/sections/Communitycontentpage/Communitycontentpage.jsx @@ -0,0 +1,464 @@ +import React, { useState, useEffect } from "react"; +import { useNavigate, useParams, useLocation } from "react-router-dom"; +import { useAuth } from "../../../../contexts/AuthContext"; +import "./style.css"; +import { HiArrowNarrowLeft } from "react-icons/hi"; +import UpdatePost from "./UpdatePost/UpdatePost"; +import { + getPostDetail, + deletePost, + updatePost, + createComment, + getReplies, +} from "../../../../api/Community/community"; + +export const Communitycontentpage = () => { + const navigate = useNavigate(); + const location = useLocation(); + const { id } = useParams(); + const { isAuthenticated } = useAuth(); + + const [currentPost, setCurrentPost] = useState(location.state?.post || null); + const [loading, setLoading] = useState(!location.state?.post); + const [showUpdatePage, setShowUpdatePage] = useState(false); + + const [comments, setComments] = useState([]); + const [newComment, setNewComment] = useState(""); + const [replyingTo, setReplyingTo] = useState(null); + const [repliesState, setRepliesState] = useState({}); + + const formatDate = (dateString) => { + if (!dateString) return ""; + return String(dateString).substring(0, 10); + }; + + useEffect(() => { + //게시글의 댓글 가져오는 함수 + const fetchPostData = async () => { + try { + if (!currentPost) setLoading(true); + + const response = await getPostDetail(parseInt(id)); + setCurrentPost(response); + + if (response.comments) { + setComments(response.comments); + } + } catch (error) { + console.error("게시글 로드 실패:", error); + if (!currentPost) { + alert("게시글을 불러오는 데 실패했습니다."); + navigate("/communitypage"); + } + } finally { + setLoading(false); + } + }; + + if (id) { + fetchPostData(); + } + }, [id, navigate]); + + const handleToggleReplies = async (commentId) => { + const currentState = repliesState[commentId] || { + list: [], + page: 0, + expanded: false, + }; + + if (currentState.expanded) { + setRepliesState((prev) => ({ + ...prev, + [commentId]: { ...currentState, expanded: false }, + })); + return; + } + if (currentState.list.length > 0) { + setRepliesState((prev) => ({ + ...prev, + [commentId]: { ...currentState, expanded: true }, + })); + return; + } + loadReplies(commentId, 0); + }; + + const loadReplies = async (commentId, page) => { + try { + const response = await getReplies(commentId, page); + + let newReplies = []; + if (Array.isArray(response)) { + newReplies = response; + } else if (response && Array.isArray(response.replies)) { + newReplies = response.replies; + } else if (response && Array.isArray(response.content)) { + newReplies = response.content; + } else { + newReplies = []; + } + + setRepliesState((prev) => { + const prevState = prev[commentId] || { list: [] }; + return { + ...prev, + [commentId]: { + list: page === 0 ? newReplies : [...prevState.list, ...newReplies], + page: page, + expanded: true, + hasMore: newReplies.length >= 20, + }, + }; + }); + } catch (error) { + console.error("대댓글 로드 실패:", error); + } + }; + + const handleDelete = async () => { + if (!isAuthenticated) return alert("로그인이 필요합니다."); + if (window.confirm("정말 삭제하시겠습니까?")) { + try { + await deletePost(parseInt(id)); + alert("삭제되었습니다."); + navigate("/communitypage"); + } catch (error) { + alert("삭제 실패"); + } + } + }; + + const handlePostUpdate = async (updatedData) => { + try { + const response = await updatePost(parseInt(id), updatedData); + setCurrentPost((prev) => ({ + ...prev, + ...updatedData, + ...(response && typeof response === "object" ? response : {}), + })); + alert("게시글이 수정되었습니다."); + setShowUpdatePage(false); + } catch (error) { + alert("수정 실패"); + } + }; + + const handleCommentSubmit = async (e) => { + e.preventDefault(); + if (!isAuthenticated) return alert("로그인이 필요합니다."); + if (!newComment.trim()) return; + + const commentRequest = { + parentCommentId: replyingTo ? replyingTo.commentId : null, + content: newComment, + }; + + try { + await createComment(parseInt(id), commentRequest); + + const updatedPost = await getPostDetail(parseInt(id)); + if (updatedPost.comments) { + setComments(updatedPost.comments); + } + + if (replyingTo) { + loadReplies(replyingTo.commentId, 0); + } + + setNewComment(""); + setReplyingTo(null); + } catch (error) { + console.error("댓글 작성 실패:", error); + alert("댓글 작성 중 오류가 발생했습니다."); + } + }; + + const renderCommentItem = (comment) => { + const repliesInfo = repliesState[comment.commentId]; + const isExpanded = repliesInfo?.expanded; + + const rawList = repliesInfo?.list; + const repliesList = Array.isArray(rawList) ? rawList : []; + + return ( +
+
+
+
+ {comment.userId || comment.author} +
+
+ {formatDate(comment.createdAt)} +
+
+
+ {comment.content} +
+ +
+ {isAuthenticated && ( + { + setReplyingTo(comment); + document.querySelector(".comment-input")?.focus(); + }} + style={{ cursor: "pointer", color: "#666" }} + > + 답글 달기 + + )} + + {comment.replyCount > 0 && ( + handleToggleReplies(comment.commentId)} + style={{ + cursor: "pointer", + color: "#f6910b", + fontWeight: "bold", + }} + > + {isExpanded + ? "답글 숨기기" + : `답글 ${comment.replyCount}개 보기`} + + )} +
+
+ + {isExpanded && ( +
+ {repliesList.map((reply) => ( +
+
+ ↳ {reply.userId} +
+
+ {reply.content} +
+
+ {formatDate(reply.createdAt)} +
+
+ ))} + + {repliesInfo?.hasMore && ( + + )} +
+ )} +
+ ); + }; + + if (loading) return
로딩 중...
; + if (!currentPost) + return
게시글 없음
; + + if (showUpdatePage) { + return ( + setShowUpdatePage(false)} + onUpdate={handlePostUpdate} + /> + ); + } + + return ( +
+
+
+
navigate("/communitypage")} + style={{ + cursor: "pointer", + display: "flex", + flexDirection: "column", + alignItems: "center", + marginRight: "5px", + padding: "5px", + minWidth: "60px", + }} + > + + 뒤로 + + +
+
+ icon +
{currentPost.category}
+
+
+ +
+
{currentPost.title}
+ {isAuthenticated && ( +
+ + +
+ )} +
+ +
+
작성자
+
+ {currentPost.author || currentPost.userId} +
+
작성일
+
+ {formatDate(currentPost.createdAt)} +
+
+ +
+

{currentPost.content}

+
+ + {/* 댓글 섹션 */} +
+
+

댓글

+
+ +
+ {comments && comments.length > 0 ? ( + comments.map((comment) => renderCommentItem(comment)) + ) : ( +
+ 아직 댓글이 없습니다. +
+ )} +
+ +
+ {replyingTo && ( +
+ @{replyingTo.userId} 님에게 답글 작성 중... + +
+ )} + +
+ + + + +
+ +
+
+
+ ); +}; + +export default RecipeReview; diff --git a/src/screens/RecipePage/RecipeReview/style.css b/src/screens/RecipePage/RecipeReview/style.css new file mode 100644 index 0000000..c8365b8 --- /dev/null +++ b/src/screens/RecipePage/RecipeReview/style.css @@ -0,0 +1,165 @@ +.review-popup-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + animation: fadeIn 0.2s ease-in-out; + } + + @keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } + } + + .review-popup-container { + background-color: #ffffff; + border-radius: 20px; + padding: 40px; + width: 90%; + max-width: 500px; + position: relative; + box-shadow: 0px 10px 40px rgba(0, 0, 0, 0.2); + animation: slideUp 0.3s ease-out; + max-height: 90vh; + overflow-y: auto; + } + + @keyframes slideUp { + from { + transform: translateY(20px); + opacity: 0; + } + to { + transform: translateY(0); + opacity: 1; + } + } + + .review-popup-close { + position: absolute; + top: 15px; + right: 15px; + background: none; + border: none; + font-size: 32px; + color: #666666; + cursor: pointer; + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + transition: color 0.2s; + } + + .review-popup-close:hover { + color: #000000; + } + + .review-popup-header { + margin-bottom: 30px; + text-align: center; + } + + .review-popup-title { + font-family: "Inter", Helvetica, sans-serif; + font-size: 24px; + font-weight: 700; + color: #000000; + margin: 0; + } + + .review-popup-form { + display: flex; + flex-direction: column; + gap: 20px; + } + + .review-popup-input-group { + display: flex; + flex-direction: column; + gap: 8px; + } + + .review-popup-label { + font-family: "Inter", Helvetica, sans-serif; + font-size: 14px; + font-weight: 600; + color: #000000; + } + + .review-popup-input, + .review-popup-textarea { + padding: 12px 16px; + border: 1px solid #e0e0e0; + border-radius: 12px; + font-family: "Inter", Helvetica, sans-serif; + font-size: 16px; + transition: border-color 0.2s; + background-color: #fff; + width: 100%; + box-sizing: border-box; + } + + .review-popup-textarea { + min-height: 120px; + resize: vertical; + } + + .review-popup-input:focus, + .review-popup-textarea:focus { + outline: none; + border-color: #f6910b; + } + + .review-popup-input::placeholder, + .review-popup-textarea::placeholder { + color: #999999; + } + + .review-popup-submit-btn { + padding: 14px; + background-color: #f6910b; + color: #ffffff; + border: none; + border-radius: 12px; + font-family: "Inter", Helvetica, sans-serif; + font-size: 16px; + font-weight: 700; + cursor: pointer; + transition: background-color 0.2s; + margin-top: 10px; + } + + .review-popup-submit-btn:hover { + background-color: #e58209; + } + + .review-popup-footer { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + margin-top: 10px; + } + + .review-popup-cancel-btn { + background: none; + border: none; + color: #666666; + font-family: "Inter", Helvetica, sans-serif; + font-size: 14px; + font-weight: 600; + cursor: pointer; + text-decoration: underline; + } + + .review-popup-cancel-btn:hover { + color: #333333; + } \ No newline at end of file diff --git a/src/screens/RecipePage/index.js b/src/screens/RecipePage/index.js new file mode 100644 index 0000000..7c5922e --- /dev/null +++ b/src/screens/RecipePage/index.js @@ -0,0 +1 @@ +export { RecipePage } from "./RecipePage"; diff --git a/src/screens/RecipePage/style.css b/src/screens/RecipePage/style.css new file mode 100644 index 0000000..cdaeaf6 --- /dev/null +++ b/src/screens/RecipePage/style.css @@ -0,0 +1,363 @@ +/* style.css - 이미지 제거 버전 (최종 수정) */ + +/* 전체 페이지 컨테이너 */ +.recipe-page { + background-color: #ffffff; + min-height: 100vh; + width: 100%; + display: flex; + flex-direction: column; + font-family: "Pretendard", -apple-system, BlinkMacSystemFont, "Segoe UI", + Roboto, sans-serif; +} + +/* 콘텐츠 중앙 정렬 래퍼 */ +.recipe-content-wrapper { + width: 100%; + max-width: 800px; /* 본문 너비 적절히 제한 */ + margin: 0 auto; + padding: 40px 20px; + display: flex; + flex-direction: column; + gap: 30px; +} + +/* 1. 요리 제목 영역 */ +.recipe-header-section { + text-align: center; + border-bottom: 2px solid #f6910b; /* 주황색 포인트 라인 */ + padding-bottom: 20px; + margin-bottom: 10px; +} + +.recipe-title { + color: #333; + font-size: 32px; + font-weight: 800; + margin-bottom: 15px; + word-break: keep-all; +} + +/* 2. 재료 태그 영역 */ +.ingredients-section { + display: flex; + flex-wrap: wrap; /* 줄바꿈 허용 (중요) */ + justify-content: center; + gap: 8px; /* 태그 사이 간격 */ + margin-bottom: 10px; +} + +.ingredient-badge { + background-color: #fff3e0; /* 연한 주황 배경 */ + color: #f6910b; /* 진한 주황 글씨 */ + padding: 8px 16px; + border-radius: 20px; + font-size: 15px; + font-weight: 600; + border: 1px solid #ffe0b2; + white-space: nowrap; /* 글자 줄바꿈 방지 */ +} + +/* 3. 레시피 설명 박스 (이미지 없이 꽉 채움) */ +.description-section { + background-color: #f9f9f9; + border-radius: 12px; + padding: 30px; + border-left: 5px solid #f6910b; /* 왼쪽 강조선 */ + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); +} + +.section-label { + font-size: 20px; + font-weight: 700; + color: #333; + margin-bottom: 15px; + display: block; +} + +.description-text { + font-size: 16px; + line-height: 1.7; /* 줄간격 넉넉하게 */ + color: #444; + word-break: keep-all; +} + +/* 4. 조리 순서 영역 */ +.steps-section { + display: flex; + flex-direction: column; + gap: 15px; +} + +.step-card { + display: flex; + align-items: flex-start; + gap: 15px; + background-color: #ffffff; + padding: 20px; + border-radius: 12px; + border: 1px solid #eeeeee; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.03); + transition: transform 0.2s, border-color 0.2s; +} + +.step-card:hover { + transform: translateY(-2px); + border-color: #f6910b; +} + +.step-number { + display: flex; + align-items: center; + justify-content: center; + background-color: #f6910b; + color: #ffffff; + border-radius: 50%; + min-width: 28px; + height: 28px; + font-size: 14px; + font-weight: 700; + margin-top: 2px; /* 텍스트와 높이 맞춤 */ +} + +.step-content { + flex: 1; + font-size: 16px; + font-weight: 500; + line-height: 1.6; + color: #333; + word-break: keep-all; +} + +/* 5. 리뷰 버튼 */ +.button-section { + text-align: center; + margin-top: 20px; +} + +.review-btn { + background-color: #f6910b; + color: white; + border: none; + padding: 15px 50px; + font-size: 18px; + font-weight: 700; + border-radius: 30px; + cursor: pointer; + box-shadow: 0 4px 10px rgba(246, 145, 11, 0.3); + transition: all 0.2s; +} + +.review-btn:hover { + background-color: #e08200; + transform: translateY(-2px); +} + +/* 모바일 대응 */ +@media (max-width: 768px) { + .recipe-content-wrapper { + padding: 20px; + } + .recipe-title { + font-size: 26px; + } + .description-section { + padding: 20px; + } +} +/* style.css - 뒤로가기 버튼 추가됨 */ + +.recipe-page { + background-color: #ffffff; + min-height: 100vh; + width: 100%; + display: flex; + flex-direction: column; + font-family: "Pretendard", -apple-system, BlinkMacSystemFont, "Segoe UI", + Roboto, sans-serif; +} + +.recipe-content-wrapper { + width: 100%; + max-width: 800px; + margin: 0 auto; + padding: 20px 20px 60px; /* 상단 패딩 줄임 */ + display: flex; + flex-direction: column; + gap: 30px; +} + +/* --- [추가] 뒤로 가기 버튼 스타일 --- */ +.back-button-area { + width: 100%; + display: flex; + justify-content: flex-start; + margin-bottom: 10px; +} + +.back-link-btn { + background: none; + border: none; + font-size: 16px; + color: #666; + cursor: pointer; + padding: 8px 0; + display: flex; + align-items: center; + font-weight: 600; + transition: color 0.2s; +} + +.back-link-btn:hover { + color: #f6910b; /* 호버 시 포인트 컬러 */ +} + +/* 에러 화면용 뒤로가기 버튼 */ +.error-back-btn { + padding: 10px 20px; + background-color: #333; + color: white; + border: none; + border-radius: 8px; + cursor: pointer; + margin-top: 15px; +} + +.recipe-header-section { + text-align: center; + border-bottom: 2px solid #f6910b; + padding-bottom: 20px; + margin-bottom: 10px; +} + +.recipe-title { + color: #333; + font-size: 32px; + font-weight: 800; + margin-bottom: 15px; + word-break: keep-all; +} + +.ingredients-section { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 8px; + margin-bottom: 10px; +} + +.ingredient-badge { + background-color: #fff3e0; + color: #f6910b; + padding: 8px 16px; + border-radius: 20px; + font-size: 15px; + font-weight: 600; + border: 1px solid #ffe0b2; + white-space: nowrap; +} + +.description-section { + background-color: #f9f9f9; + border-radius: 12px; + padding: 30px; + border-left: 5px solid #f6910b; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); +} + +.section-label { + font-size: 20px; + font-weight: 700; + color: #333; + margin-bottom: 15px; + display: block; +} + +.description-text { + font-size: 16px; + line-height: 1.7; + color: #444; + word-break: keep-all; +} + +.steps-section { + display: flex; + flex-direction: column; + gap: 15px; +} + +.step-card { + display: flex; + align-items: flex-start; + gap: 15px; + background-color: #ffffff; + padding: 20px; + border-radius: 12px; + border: 1px solid #eeeeee; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.03); + transition: transform 0.2s, border-color 0.2s; +} + +.step-card:hover { + transform: translateY(-2px); + border-color: #f6910b; +} + +.step-number { + display: flex; + align-items: center; + justify-content: center; + background-color: #f6910b; + color: #ffffff; + border-radius: 50%; + min-width: 28px; + height: 28px; + font-size: 14px; + font-weight: 700; + margin-top: 2px; +} + +.step-content { + flex: 1; + font-size: 16px; + font-weight: 500; + line-height: 1.6; + color: #333; + word-break: keep-all; +} + +.button-section { + text-align: center; + margin-top: 20px; +} + +.review-btn { + background-color: #f6910b; + color: white; + border: none; + padding: 15px 50px; + font-size: 18px; + font-weight: 700; + border-radius: 30px; + cursor: pointer; + box-shadow: 0 4px 10px rgba(246, 145, 11, 0.3); + transition: all 0.2s; +} + +.review-btn:hover { + background-color: #e08200; + transform: translateY(-2px); +} + +/* 모바일 대응 */ +@media (max-width: 768px) { + .recipe-content-wrapper { + padding: 20px; + } + .recipe-title { + font-size: 26px; + } + .description-section { + padding: 20px; + } +} diff --git a/static/_redirects b/static/_redirects new file mode 100644 index 0000000..7797f7c --- /dev/null +++ b/static/_redirects @@ -0,0 +1 @@ +/* /index.html 200 diff --git a/vite.config.js b/vite.config.js index 4efe189..154431f 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,13 +1,11 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; + +import { screenGraphPlugin } from "@animaapp/vite-plugin-screen-graph"; // https://vite.dev/config/ -export default defineConfig({ - plugins: [ - react({ - babel: { - plugins: [['babel-plugin-react-compiler']], - }, - }), - ], -}) +export default defineConfig(({ mode }) => ({ + plugins: [react(), mode === "development" && screenGraphPlugin()], + publicDir: "./static", + base: "./", +}));