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..14f3d5c
--- /dev/null
+++ b/.screen-graph.json
@@ -0,0 +1,1141 @@
+{
+ "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"
+ ],
+ "dataModelId": "168:346"
+ },
+ {
+ "id": "src/screens/RecipePage/RecipePage.jsx",
+ "label": "RecipePage",
+ "routes": [
+ "/recipepage/:id"
+ ],
+ "dataModelId": "199:457"
+ },
+ {
+ "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:53: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)}>\n 홈\n ",
+ "line": 53,
+ "endLine": 55,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:56: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)}>\n 메뉴 추천\n ",
+ "line": 56,
+ "endLine": 58,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:59: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)}>\n 재료 나눔 게시판\n ",
+ "line": 59,
+ "endLine": 61,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:62: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)}>\n 마이페이지\n ",
+ "line": 62,
+ "endLine": 64,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:67:10-to-src\\screens\\Desktop\\Desktop.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\Desktop\\Desktop.jsx",
+ "data": {
+ "viaRoute": "/desktop",
+ "trigger": {
+ "element": "\n
\n ",
+ "line": 67,
+ "endLine": 73,
+ "column": 10,
+ "endColumn": 17,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:76:8-to-src\\screens\\Desktop\\Desktop.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\Desktop\\Desktop.jsx",
+ "data": {
+ "viaRoute": "/desktop",
+ "trigger": {
+ "element": "\n
\n 알고리즘 \n 셰프\n
\n ",
+ "line": 76,
+ "endLine": 81,
+ "column": 8,
+ "endColumn": 15,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:84:10-to-src\\screens\\MyPage\\MyPage.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\MyPage\\MyPage.jsx",
+ "data": {
+ "viaRoute": "/mypage",
+ "trigger": {
+ "element": "\n 마이페이지
\n ",
+ "line": 84,
+ "endLine": 86,
+ "column": 10,
+ "endColumn": 17,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\screens\\Desktop\\sections\\IngredientInventory\\IngredientInventory.jsx:74:8-to-src\\screens\\AddIngredientPage\\AddIngredientPage.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\AddIngredientPage\\AddIngredientPage.jsx",
+ "data": {
+ "viaRoute": "/addingredient",
+ "trigger": {
+ "element": "\n \n
\n\n
\n

\n
\n
\n ",
+ "line": 74,
+ "endLine": 88,
+ "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": "\n \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": "\n 더보기 →\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": "\n 재료 나눔 게시판
\n ",
+ "line": 18,
+ "endLine": 20,
+ "column": 6,
+ "endColumn": 13,
+ "sourceFile": "src\\screens\\Desktop\\sections\\Community\\Community.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:53: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)}>\n 홈\n ",
+ "line": 53,
+ "endLine": 55,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:56: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)}>\n 메뉴 추천\n ",
+ "line": 56,
+ "endLine": 58,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:59: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)}>\n 재료 나눔 게시판\n ",
+ "line": 59,
+ "endLine": 61,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:62: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)}>\n 마이페이지\n ",
+ "line": 62,
+ "endLine": 64,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:67:10-to-src\\screens\\Desktop\\Desktop.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\Desktop\\Desktop.jsx",
+ "data": {
+ "viaRoute": "/desktop",
+ "trigger": {
+ "element": "\n
\n ",
+ "line": 67,
+ "endLine": 73,
+ "column": 10,
+ "endColumn": 17,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:76:8-to-src\\screens\\Desktop\\Desktop.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\Desktop\\Desktop.jsx",
+ "data": {
+ "viaRoute": "/desktop",
+ "trigger": {
+ "element": "\n \n 알고리즘 \n 셰프\n
\n ",
+ "line": 76,
+ "endLine": 81,
+ "column": 8,
+ "endColumn": 15,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:84:10-to-src\\screens\\MyPage\\MyPage.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\MyPage\\MyPage.jsx",
+ "data": {
+ "viaRoute": "/mypage",
+ "trigger": {
+ "element": "\n 마이페이지
\n ",
+ "line": 84,
+ "endLine": 86,
+ "column": 10,
+ "endColumn": 17,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.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:53: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)}>\n 홈\n ",
+ "line": 53,
+ "endLine": 55,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:56: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)}>\n 메뉴 추천\n ",
+ "line": 56,
+ "endLine": 58,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:59: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)}>\n 재료 나눔 게시판\n ",
+ "line": 59,
+ "endLine": 61,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:62: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)}>\n 마이페이지\n ",
+ "line": 62,
+ "endLine": 64,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:67:10-to-src\\screens\\Desktop\\Desktop.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\Desktop\\Desktop.jsx",
+ "data": {
+ "viaRoute": "/desktop",
+ "trigger": {
+ "element": "\n
\n ",
+ "line": 67,
+ "endLine": 73,
+ "column": 10,
+ "endColumn": 17,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:76:8-to-src\\screens\\Desktop\\Desktop.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\Desktop\\Desktop.jsx",
+ "data": {
+ "viaRoute": "/desktop",
+ "trigger": {
+ "element": "\n \n 알고리즘 \n 셰프\n
\n ",
+ "line": 76,
+ "endLine": 81,
+ "column": 8,
+ "endColumn": 15,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:84:10-to-src\\screens\\MyPage\\MyPage.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\MyPage\\MyPage.jsx",
+ "data": {
+ "viaRoute": "/mypage",
+ "trigger": {
+ "element": "\n 마이페이지
\n ",
+ "line": 84,
+ "endLine": 86,
+ "column": 10,
+ "endColumn": 17,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:53: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)}>\n 홈\n ",
+ "line": 53,
+ "endLine": 55,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:56: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)}>\n 메뉴 추천\n ",
+ "line": 56,
+ "endLine": 58,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:59: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)}>\n 재료 나눔 게시판\n ",
+ "line": 59,
+ "endLine": 61,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:62: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)}>\n 마이페이지\n ",
+ "line": 62,
+ "endLine": 64,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:67:10-to-src\\screens\\Desktop\\Desktop.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\Desktop\\Desktop.jsx",
+ "data": {
+ "viaRoute": "/desktop",
+ "trigger": {
+ "element": "\n
\n ",
+ "line": 67,
+ "endLine": 73,
+ "column": 10,
+ "endColumn": 17,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:76:8-to-src\\screens\\Desktop\\Desktop.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\Desktop\\Desktop.jsx",
+ "data": {
+ "viaRoute": "/desktop",
+ "trigger": {
+ "element": "\n \n 알고리즘 \n 셰프\n
\n ",
+ "line": 76,
+ "endLine": 81,
+ "column": 8,
+ "endColumn": 15,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:84:10-to-src\\screens\\MyPage\\MyPage.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\MyPage\\MyPage.jsx",
+ "data": {
+ "viaRoute": "/mypage",
+ "trigger": {
+ "element": "\n 마이페이지
\n ",
+ "line": 84,
+ "endLine": 86,
+ "column": 10,
+ "endColumn": 17,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:53: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)}>\n 홈\n ",
+ "line": 53,
+ "endLine": 55,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:56: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)}>\n 메뉴 추천\n ",
+ "line": 56,
+ "endLine": 58,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:59: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)}>\n 재료 나눔 게시판\n ",
+ "line": 59,
+ "endLine": 61,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:62: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)}>\n 마이페이지\n ",
+ "line": 62,
+ "endLine": 64,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:67:10-to-src\\screens\\Desktop\\Desktop.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\Desktop\\Desktop.jsx",
+ "data": {
+ "viaRoute": "/desktop",
+ "trigger": {
+ "element": "\n
\n ",
+ "line": 67,
+ "endLine": 73,
+ "column": 10,
+ "endColumn": 17,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:76:8-to-src\\screens\\Desktop\\Desktop.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\Desktop\\Desktop.jsx",
+ "data": {
+ "viaRoute": "/desktop",
+ "trigger": {
+ "element": "\n \n 알고리즘 \n 셰프\n
\n ",
+ "line": 76,
+ "endLine": 81,
+ "column": 8,
+ "endColumn": 15,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:84:10-to-src\\screens\\MyPage\\MyPage.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\MyPage\\MyPage.jsx",
+ "data": {
+ "viaRoute": "/mypage",
+ "trigger": {
+ "element": "\n 마이페이지
\n ",
+ "line": 84,
+ "endLine": 86,
+ "column": 10,
+ "endColumn": 17,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\screens\\CommunityContent\\sections\\Communitycontentpage\\Communitycontentpage.jsx:87:6-to-src\\screens\\CommunityPage\\CommunityPage.jsx",
+ "source": "src\\screens\\CommunityContent\\CommunityContent.jsx",
+ "target": "src\\screens\\CommunityPage\\CommunityPage.jsx",
+ "data": {
+ "viaRoute": "/communitypage",
+ "trigger": {
+ "element": "\n 재료 나눔 게시판
\n ",
+ "line": 87,
+ "endLine": 89,
+ "column": 6,
+ "endColumn": 13,
+ "sourceFile": "src\\screens\\CommunityContent\\sections\\Communitycontentpage\\Communitycontentpage.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\screens\\AddIngredientPage\\AddIngredientPage.jsx:54:4-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": 54,
+ "endLine": 54,
+ "column": 4,
+ "endColumn": 24,
+ "sourceFile": "src\\screens\\AddIngredientPage\\AddIngredientPage.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\screens\\AddIngredientPage\\AddIngredientPage.jsx:118:12-to-src\\screens\\Desktop\\Desktop.jsx",
+ "source": "src\\screens\\AddIngredientPage\\AddIngredientPage.jsx",
+ "target": "src\\screens\\Desktop\\Desktop.jsx",
+ "data": {
+ "viaRoute": "/desktop",
+ "trigger": {
+ "element": "\n 취소\n ",
+ "line": 118,
+ "endLine": 120,
+ "column": 12,
+ "endColumn": 19,
+ "sourceFile": "src\\screens\\AddIngredientPage\\AddIngredientPage.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:53: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)}>\n 홈\n ",
+ "line": 53,
+ "endLine": 55,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:56: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)}>\n 메뉴 추천\n ",
+ "line": 56,
+ "endLine": 58,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:59: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)}>\n 재료 나눔 게시판\n ",
+ "line": 59,
+ "endLine": 61,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:62: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)}>\n 마이페이지\n ",
+ "line": 62,
+ "endLine": 64,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:67:10-to-src\\screens\\Desktop\\Desktop.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\Desktop\\Desktop.jsx",
+ "data": {
+ "viaRoute": "/desktop",
+ "trigger": {
+ "element": "\n
\n ",
+ "line": 67,
+ "endLine": 73,
+ "column": 10,
+ "endColumn": 17,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:76:8-to-src\\screens\\Desktop\\Desktop.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\Desktop\\Desktop.jsx",
+ "data": {
+ "viaRoute": "/desktop",
+ "trigger": {
+ "element": "\n \n 알고리즘 \n 셰프\n
\n ",
+ "line": 76,
+ "endLine": 81,
+ "column": 8,
+ "endColumn": 15,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:84:10-to-src\\screens\\MyPage\\MyPage.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\MyPage\\MyPage.jsx",
+ "data": {
+ "viaRoute": "/mypage",
+ "trigger": {
+ "element": "\n 마이페이지
\n ",
+ "line": 84,
+ "endLine": 86,
+ "column": 10,
+ "endColumn": 17,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.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": "\n 취소\n ",
+ "line": 88,
+ "endLine": 90,
+ "column": 12,
+ "endColumn": 19,
+ "sourceFile": "src\\screens\\CreatePostPage\\CreatePostPage.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:53: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)}>\n 홈\n ",
+ "line": 53,
+ "endLine": 55,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:56: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)}>\n 메뉴 추천\n ",
+ "line": 56,
+ "endLine": 58,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:59: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)}>\n 재료 나눔 게시판\n ",
+ "line": 59,
+ "endLine": 61,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:62: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)}>\n 마이페이지\n ",
+ "line": 62,
+ "endLine": 64,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:67:10-to-src\\screens\\Desktop\\Desktop.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\Desktop\\Desktop.jsx",
+ "data": {
+ "viaRoute": "/desktop",
+ "trigger": {
+ "element": "\n
\n ",
+ "line": 67,
+ "endLine": 73,
+ "column": 10,
+ "endColumn": 17,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:76:8-to-src\\screens\\Desktop\\Desktop.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\Desktop\\Desktop.jsx",
+ "data": {
+ "viaRoute": "/desktop",
+ "trigger": {
+ "element": "\n \n 알고리즘 \n 셰프\n
\n ",
+ "line": 76,
+ "endLine": 81,
+ "column": 8,
+ "endColumn": 15,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:84:10-to-src\\screens\\MyPage\\MyPage.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\MyPage\\MyPage.jsx",
+ "data": {
+ "viaRoute": "/mypage",
+ "trigger": {
+ "element": "\n 마이페이지
\n ",
+ "line": 84,
+ "endLine": 86,
+ "column": 10,
+ "endColumn": 17,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:53: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)}>\n 홈\n ",
+ "line": 53,
+ "endLine": 55,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:56: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)}>\n 메뉴 추천\n ",
+ "line": 56,
+ "endLine": 58,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:59: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)}>\n 재료 나눔 게시판\n ",
+ "line": 59,
+ "endLine": 61,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:62: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)}>\n 마이페이지\n ",
+ "line": 62,
+ "endLine": 64,
+ "column": 14,
+ "endColumn": 21,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:67:10-to-src\\screens\\Desktop\\Desktop.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\Desktop\\Desktop.jsx",
+ "data": {
+ "viaRoute": "/desktop",
+ "trigger": {
+ "element": "\n
\n ",
+ "line": 67,
+ "endLine": 73,
+ "column": 10,
+ "endColumn": 17,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:76:8-to-src\\screens\\Desktop\\Desktop.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\Desktop\\Desktop.jsx",
+ "data": {
+ "viaRoute": "/desktop",
+ "trigger": {
+ "element": "\n \n 알고리즘 \n 셰프\n
\n ",
+ "line": 76,
+ "endLine": 81,
+ "column": 8,
+ "endColumn": 15,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx"
+ }
+ }
+ },
+ {
+ "id": "src\\components\\UnifiedHeader\\UnifiedHeader.jsx:84:10-to-src\\screens\\MyPage\\MyPage.jsx",
+ "source": "src\\screens\\Desktop\\Desktop.jsx",
+ "target": "src\\screens\\MyPage\\MyPage.jsx",
+ "data": {
+ "viaRoute": "/mypage",
+ "trigger": {
+ "element": "\n 마이페이지
\n ",
+ "line": 84,
+ "endLine": 86,
+ "column": 10,
+ "endColumn": 17,
+ "sourceFile": "src\\components\\UnifiedHeader\\UnifiedHeader.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..6ba856d 100644
--- a/index.html
+++ b/index.html
@@ -1,13 +1,35 @@
-
+
-
-
-
- algorithmcheffront
+
+ 알고리즘 셰프 - Algorithm Chef
+
+
+
-
-
+
+
diff --git a/package-lock.json b/package-lock.json
index 8a0e906..10817b7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,27 +1,528 @@
{
- "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"
+ "react": "^18.2.0",
+ "react-dom": "^18.2.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 +809,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 +826,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 +843,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 +860,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 +877,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 +894,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 +911,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 +928,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 +945,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 +962,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 +979,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 +996,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 +1013,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 +1030,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 +1047,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 +1064,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 +1081,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 +1097,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 +1115,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 +1132,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 +1148,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 +1166,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 +1183,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 +1200,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 +1216,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 +1266,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==",
- "dev": true,
- "license": "MIT"
+ "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",
+ "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 +1328,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 +1342,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 +1356,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 +1370,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 +1384,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 +1398,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 +1412,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 +1426,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 +1440,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 +1454,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 +1468,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 +1482,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 +1496,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 +1510,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 +1524,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 +1538,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 +1552,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 +1566,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 +1580,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 +1594,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 +1608,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"
],
@@ -1375,161 +1673,53 @@
"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==",
+ "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": {
- "@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",
+ "@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.18.0"
+ "react-refresh": "^0.14.2"
},
"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==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "bin": {
- "acorn": "bin/acorn"
+ "node": "^14.18.0 || >=16.0.0"
},
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/acorn-jsx": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
- "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
- "dev": true,
- "license": "MIT",
"peerDependencies": {
- "acorn": "^6.0.0 || ^7.0.0 || ^8.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==",
- "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"
- }
- },
- "node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "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/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,
- "license": "MIT",
- "dependencies": {
- "@babel/types": "^7.26.0"
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.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 +1738,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 +1751,10 @@
"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,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"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 +1772,6 @@
],
"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,
- "license": "MIT",
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true,
- "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 +1779,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,24 +1797,17 @@
}
}
},
- "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==",
+ "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/esbuild": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
- "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+ "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",
@@ -1711,32 +1818,30 @@
"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"
+ "@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/escalade": {
@@ -1749,287 +1854,56 @@
"node": ">=6"
}
},
- "node_modules/escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "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,
- "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"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://eslint.org/donate"
- },
- "peerDependencies": {
- "jiti": "*"
- },
- "peerDependenciesMeta": {
- "jiti": {
- "optional": true
- }
- }
- },
- "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,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "peerDependencies": {
- "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
- }
- },
- "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/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": "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"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "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==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/espree": {
- "version": "10.4.0",
- "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
- "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
- "dev": true,
- "license": "BSD-2-Clause",
- "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"
- }
- },
- "node_modules/esquery": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
- "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "estraverse": "^5.1.0"
- },
- "engines": {
- "node": ">=0.10"
- }
- },
- "node_modules/esrecurse": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
- "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
- "dev": true,
- "license": "BSD-2-Clause",
"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
- }
+ "@types/estree": "^1.0.0"
}
},
- "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/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": "MIT",
"dependencies": {
- "flat-cache": "^4.0.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": ">=16.0.0"
+ "node": ">=8.6.0"
}
},
- "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==",
+ "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": "MIT",
+ "license": "ISC",
"dependencies": {
- "locate-path": "^6.0.0",
- "path-exists": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "reusify": "^1.0.4"
}
},
- "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==",
+ "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": {
- "flatted": "^3.2.9",
- "keyv": "^4.5.4"
+ "to-regex-range": "^5.0.1"
},
"engines": {
- "node": ">=16"
+ "node": ">=8"
}
},
- "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",
@@ -2047,85 +1921,38 @@
},
"node_modules/gensync": {
"version": "1.0.0-beta.2",
- "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
- "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/glob-parent": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
- "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "is-glob": "^4.0.3"
- },
- "engines": {
- "node": ">=10.13.0"
- }
- },
- "node_modules/globals": {
- "version": "16.5.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
- "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "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,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/ignore": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
- "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">= 4"
+ "node": ">=6.9.0"
}
},
- "node_modules/import-fresh": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
- "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
- "license": "MIT",
+ "license": "ISC",
"dependencies": {
- "parent-module": "^1.0.0",
- "resolve-from": "^4.0.0"
+ "is-glob": "^4.0.1"
},
"engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "node": ">= 6"
}
},
- "node_modules/imurmurhash": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
- "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "node_modules/globals": {
+ "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": {
- "node": ">=0.8.19"
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-extglob": {
@@ -2151,33 +1978,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 +2007,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,53 +2020,18 @@
"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,
- "license": "MIT",
- "dependencies": {
- "json-buffer": "3.0.1"
- }
- },
- "node_modules/levn": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
- "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "prelude-ls": "^1.2.1",
- "type-check": "~0.4.0"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "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/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": {
- "p-locate": "^5.0.0"
- },
- "engines": {
- "node": ">=10"
+ "js-tokens": "^3.0.0 || ^4.0.0"
},
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "bin": {
+ "loose-envify": "cli.js"
}
},
- "node_modules/lodash.merge": {
- "version": "4.6.2",
- "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
- "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -2282,17 +2042,28 @@
"yallist": "^3.0.2"
}
},
- "node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "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": "ISC",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "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": "MIT",
"dependencies": {
- "brace-expansion": "^1.1.7"
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
},
"engines": {
- "node": "*"
+ "node": ">=8.6"
}
},
"node_modules/ms": {
@@ -2321,13 +2092,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 +2099,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 +2107,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 +2148,111 @@
"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/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-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 +2266,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 +2334,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 +2344,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 +2388,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 +2413,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 +2461,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..eb97940 100644
--- a/package.json
+++ b/package.json
@@ -1,28 +1,23 @@
{
- "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"
+ "react": "^18.2.0",
+ "react-dom": "^18.2.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..5c31980 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,35 +1,58 @@
-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 { CommunityContent } from "./screens/CommunityContent";
+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";
-function App() {
- const [count, setCount] = useState(0)
+const router = createBrowserRouter([
+ {
+ path: "/*",
+ element: ,
+ },
+ {
+ path: "/desktop",
+ element: ,
+ },
+ {
+ path: "/communitypage",
+ element: ,
+ },
+ {
+ path: "/mypage",
+ element: ,
+ },
+ {
+ path: "/recipepage/:id",
+ element: ,
+ },
+ {
+ path: "/communitycontentpage/:id",
+ element: ,
+ },
+ {
+ path: "/addingredient",
+ element: ,
+ },
+ {
+ path: "/createpost",
+ element: ,
+ },
+ {
+ path: "/menurecommendation",
+ element: ,
+ },
+]);
+export const App = () => {
return (
- <>
-
- Vite + React
-
-
-
- Edit src/App.jsx and save to test HMR
-
-
-
- Click on the Vite and React logos to learn more
-
- >
- )
-}
-
-export default App
+
+
+
+ );
+};
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..63381f8
--- /dev/null
+++ b/src/components/AddIngredientPopup/AddIngredientPopup.jsx
@@ -0,0 +1,89 @@
+import React, { useState } from "react";
+import "./style.css";
+
+const CATEGORIES = ["육류", "채소류", "가공류", "유제품", "기타"];
+
+export const AddIngredientPopup = ({ onClose, onAdd }) => {
+ const [name, setName] = useState("");
+ const [category, setCategory] = useState("채소류");
+ const [expiryDays, setExpiryDays] = useState("");
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+
+ if (!name || !expiryDays) {
+ alert("모든 필드를 입력해주세요.");
+ return;
+ }
+
+ const newIngredient = {
+ id: Date.now(),
+ name,
+ category,
+ expiryDays: parseInt(expiryDays),
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ };
+
+ // TODO: Send to backend
+ console.log("Adding ingredient via receipt:", newIngredient);
+ onAdd(newIngredient);
+ onClose();
+ };
+
+ return (
+
+
e.stopPropagation()}>
+
+
+
+
식재료 추가
+
+
+
+
+
+ );
+};
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..4463c52
--- /dev/null
+++ b/src/components/AddIngredientPopup/style.css
@@ -0,0 +1,136 @@
+.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: 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;
+ }
+}
+
+.add-ingredient-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;
+}
+
+.add-ingredient-popup-close:hover {
+ color: #000000;
+}
+
+.add-ingredient-popup-header {
+ margin-bottom: 30px;
+ text-align: center;
+}
+
+.add-ingredient-popup-title {
+ font-family: "Inter", Helvetica;
+ font-size: 28px;
+ font-weight: 700;
+ color: #000000;
+ 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;
+}
+
+.add-ingredient-popup-label {
+ font-family: "Inter", Helvetica;
+ font-size: 14px;
+ font-weight: 600;
+ color: #000000;
+}
+
+.add-ingredient-popup-input,
+.add-ingredient-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;
+}
+
+.add-ingredient-popup-input:focus,
+.add-ingredient-popup-select:focus {
+ outline: none;
+ border-color: #f6910b;
+}
+
+.add-ingredient-popup-input::placeholder {
+ color: #999999;
+}
+
+.add-ingredient-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;
+}
+
+.add-ingredient-popup-submit-btn:hover {
+ background-color: #e58209;
+}
diff --git a/src/components/CommunitypageWrapper/CommunitypageWrapper.jsx b/src/components/CommunitypageWrapper/CommunitypageWrapper.jsx
new file mode 100644
index 0000000..8b47a5f
--- /dev/null
+++ b/src/components/CommunitypageWrapper/CommunitypageWrapper.jsx
@@ -0,0 +1,46 @@
+/*
+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 = "돼지고기 100g 나눔합니다.",
+ author = "test3User",
+ date = "2025-11-03",
+ category = "나눔",
+ content = ""
+}) => {
+ return (
+
+
+
+
+
+
+
작성자 :
+
+
{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 (
+ <>
+
+
+
+

+
+
+
+
+
+ 알고리즘
+
+ 셰프
+
+
+
+
+
+
마이페이지
+
+ {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..21f08e8
--- /dev/null
+++ b/src/components/IngredientComponent/IngredientComponent.jsx
@@ -0,0 +1,36 @@
+/*
+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 IngredientComponent = ({
+ className,
+ ingredientImage = "https://c.animaapp.com/sjWITF5i/img/ingredientimage-1.png",
+ text = "양파",
+ expiryDays = 0,
+}) => {
+ return (
+
+
+
+

+
+
+
+
+
+
+
+ );
+};
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/LoginPopup.jsx b/src/components/LoginPopup/LoginPopup.jsx
new file mode 100644
index 0000000..8d4cf23
--- /dev/null
+++ b/src/components/LoginPopup/LoginPopup.jsx
@@ -0,0 +1,91 @@
+import React, { useState } from "react";
+import { useAuth } from "../../contexts/AuthContext";
+import "./style.css";
+
+export const LoginPopup = ({ onClose, onSwitchToSignup }) => {
+ const [username, setUsername] = useState("");
+ const [password, setPassword] = useState("");
+ const { login } = useAuth();
+
+ const handleLogin = async (e) => {
+ e.preventDefault();
+
+ // TODO: Backend Integration: Replace with actual backend API call for login
+ // Example: axios.post('/api/login', { username, password })
+ // .then(response => {
+ // login(response.data.user);
+ // onClose();
+ // })
+ // .catch(error => {
+ // alert("아이디 또는 비밀번호가 일치하지 않습니다.");
+ // });
+
+ // Simulating backend validation with localStorage
+ const mockUsers = JSON.parse(localStorage.getItem("registeredUsers") || "[]");
+ const foundUser = mockUsers.find(
+ (u) => u.username === username && u.password === password
+ );
+
+ if (foundUser) {
+ const userData = { username: foundUser.username };
+ login(userData);
+ console.log("Login successful:", userData);
+ onClose();
+ } else {
+ alert("아이디 또는 비밀번호가 일치하지 않습니다.");
+ }
+ };
+
+ return (
+
+
e.stopPropagation()}>
+
+
+
+
로그인
+
+
+
+
+
+ );
+};
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..4d5cd73
--- /dev/null
+++ b/src/components/LoginPopup/style.css
@@ -0,0 +1,162 @@
+.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;
+}
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 (
+
+ );
+};
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..924c41f
--- /dev/null
+++ b/src/components/PreferencesPopup/PreferencesPopup.jsx
@@ -0,0 +1,217 @@
+import React, { useState } from "react";
+import "./style.css";
+
+const HEALTH_GOALS = [
+ { id: 1, name: "체중 감량" },
+ { id: 2, name: "근육 증가" },
+ { id: 3, name: "건강 유지" },
+ { id: 4, name: "면역력 강화" },
+ { id: 5, name: "소화 개선" },
+];
+
+const ALLERGIES = [
+ { id: 1, name: "우유" },
+ { id: 2, name: "계란" },
+ { id: 3, name: "땅콩" },
+ { id: 4, name: "견과류" },
+ { id: 5, name: "갑각류" },
+ { id: 6, name: "밀" },
+ { id: 7, name: "대두" },
+];
+
+const SPICE_LEVELS = ["안매움", "약간매움", "매움", "아주매움"];
+const CUISINE_TYPES = ["한식", "중식", "일식", "양식", "기타"];
+
+export const PreferencesPopup = ({ onClose, userData }) => {
+ const [healthGoalIds, setHealthGoalIds] = useState([]);
+ const [allergyIds, setAllergyIds] = useState([]);
+ const [dislikedIngredients, setDislikedIngredients] = useState("");
+ const [preferredIngredients, setPreferredIngredients] = useState("");
+ const [preferredCuisine, setPreferredCuisine] = useState("");
+ const [spiceLevel, setSpiceLevel] = useState("");
+ const [allowPushConsumption, setAllowPushConsumption] = useState(true);
+ const [allowPushComment, setAllowPushComment] = useState(true);
+ const [allowPushNudge, setAllowPushNudge] = useState(true);
+
+ 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 handleSubmit = (e) => {
+ e.preventDefault();
+
+ const preferences = {
+ ...userData,
+ healthGoalIds,
+ allergyIds,
+ dislikedIngredients,
+ preferredIngredients,
+ preferredCuisine,
+ spiceLevel,
+ allowPushConsumption,
+ allowPushComment,
+ allowPushNudge,
+ };
+
+ // TODO: Backend Integration: Replace with actual API call to save user preferences
+ // Example: axios.post(`/api/users/${userData.username}/preferences`, preferences)
+ // .then(() => {
+ // alert("회원가입이 완료되었습니다!");
+ // onClose();
+ // })
+ // .catch(error => {
+ // alert("성향 저장 실패: " + error.response.data.message);
+ // });
+
+ // Simulating saving preferences to localStorage
+ console.log("Complete signup with preferences:", preferences);
+ localStorage.setItem("userPreferences", JSON.stringify(preferences)); // Store preferences
+ alert("회원가입이 완료되었습니다!");
+ onClose();
+ };
+
+ return (
+
+
e.stopPropagation()}>
+
+
+
+
성향 설정
+
맞춤 추천을 위한 정보를 입력해주세요
+
+
+
+
+
+ );
+};
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 (
+
+ );
+};
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/SignupPopup/SignupPopup.jsx b/src/components/SignupPopup/SignupPopup.jsx
new file mode 100644
index 0000000..6e93d5e
--- /dev/null
+++ b/src/components/SignupPopup/SignupPopup.jsx
@@ -0,0 +1,150 @@
+import React, { useState } from "react";
+import "./style.css";
+
+export const SignupPopup = ({ onClose, onSwitchToLogin, onSwitchToPreferences }) => {
+ const [username, setUsername] = useState("");
+ const [password, setPassword] = useState("");
+ const [confirmPassword, setConfirmPassword] = useState("");
+ const [email, setEmail] = useState("");
+ const [gender, setGender] = useState("");
+ const [birthDate, setBirthDate] = useState("");
+
+ const handleSignup = (e) => {
+ e.preventDefault();
+
+ if (password !== confirmPassword) {
+ alert("비밀번호가 일치하지 않습니다.");
+ return;
+ }
+
+ // TODO: Backend Integration: Replace with actual backend API call for signup
+ // Example: axios.post('/api/signup', userData)
+ // .then(response => {
+ // onSwitchToPreferences(response.data.user); // Pass user data from backend
+ // })
+ // .catch(error => {
+ // alert("회원가입 실패: " + error.response.data.message);
+ // });
+
+ // Simulating backend validation and storage with localStorage
+ const mockUsers = JSON.parse(localStorage.getItem("registeredUsers") || "[]");
+
+ // Check if username already exists
+ if (mockUsers.find((u) => u.username === username)) {
+ alert("이미 존재하는 아이디입니다.");
+ return;
+ }
+
+ const userData = { username, email, password, gender, birthDate };
+ mockUsers.push(userData);
+ localStorage.setItem("registeredUsers", JSON.stringify(mockUsers));
+
+ console.log("Signup attempt:", userData);
+
+ // Move to preferences popup
+ onSwitchToPreferences(userData);
+ };
+
+ return (
+
+
e.stopPropagation()}>
+
+
+
+
회원가입
+
+
+
+
+
+ );
+};
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..c3173d2
--- /dev/null
+++ b/src/components/UnifiedHeader/UnifiedHeader.jsx
@@ -0,0 +1,125 @@
+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 UnifiedHeader = () => {
+ const { user, isAuthenticated, logout } = useAuth();
+ const [showLoginPopup, setShowLoginPopup] = useState(false);
+ const [showSignupPopup, setShowSignupPopup] = useState(false);
+ const [showPreferencesPopup, setShowPreferencesPopup] = useState(false);
+ const [showMenu, setShowMenu] = 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 (
+ <>
+
+
+ {showLoginPopup && (
+
+ )}
+
+ {showSignupPopup && (
+
+ )}
+
+ {showPreferencesPopup && (
+
+ )}
+ >
+ );
+};
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 (
+
+ );
+};
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/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..930af19
--- /dev/null
+++ b/src/contexts/AuthContext.jsx
@@ -0,0 +1,44 @@
+import React, { createContext, useContext, useState, useEffect } from "react";
+
+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(() => {
+ // Check if user is logged in from localStorage
+ const storedUser = localStorage.getItem("currentUser");
+ if (storedUser) {
+ const userData = JSON.parse(storedUser);
+ setUser(userData);
+ setIsAuthenticated(true);
+ }
+ }, []);
+
+ const login = (userData) => {
+ setUser(userData);
+ setIsAuthenticated(true);
+ localStorage.setItem("currentUser", JSON.stringify(userData));
+ };
+
+ 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..19f69ce
--- /dev/null
+++ b/src/screens/AddIngredientPage/AddIngredientPage.jsx
@@ -0,0 +1,133 @@
+import React, { useState } from "react";
+import { Link, useNavigate } from "react-router-dom";
+import { UnifiedHeader } from "../../components/UnifiedHeader";
+import "./style.css";
+
+const CATEGORIES = ["전체", "육류", "채소류", "가공류", "유제품", "기타"];
+
+const MOCK_INGREDIENT_DATABASE = [
+ // TODO: Backend Integration: Replace with API call to fetch all available ingredients from DB
+ { id: 1, name: "양파", category: "채소류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-1.png" },
+ { id: 2, name: "닭고기", category: "육류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 3, name: "돼지고기", category: "육류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 4, name: "소고기", category: "육류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 5, name: "고추장", category: "가공류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 6, name: "고춧가루", category: "가공류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 7, name: "된장", category: "가공류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 8, name: "간장", category: "가공류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 9, name: "양배추", category: "채소류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 10, name: "당근", category: "채소류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 11, name: "감자", category: "채소류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 12, name: "마늘", category: "채소류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 13, name: "우유", category: "유제품", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 14, name: "치즈", category: "유제품", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 15, name: "요거트", category: "유제품", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 16, name: "계란", category: "기타", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+];
+
+export const AddIngredientPage = () => {
+ const navigate = useNavigate();
+ const [selectedCategory, setSelectedCategory] = useState("전체");
+ const [searchQuery, setSearchQuery] = useState("");
+ const [selectedIngredients, setSelectedIngredients] = useState([]);
+
+ const filteredIngredients = MOCK_INGREDIENT_DATABASE.filter((ingredient) => {
+ const matchesCategory = selectedCategory === "전체" || ingredient.category === selectedCategory;
+ const matchesSearch = ingredient.name.toLowerCase().includes(searchQuery.toLowerCase());
+ return matchesCategory && matchesSearch;
+ });
+
+ 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 {
+ return [...prev, { ...ingredient, expiryDays: 7 }];
+ }
+ });
+ };
+
+ const handleAddIngredients = () => {
+ // TODO: Send selected ingredients to backend
+ console.log("Adding ingredients:", selectedIngredients);
+ navigate("/desktop");
+ };
+
+ return (
+
+
+
+
+
+
식재료 추가
+
냉장고에 추가할 식재료를 선택하세요
+
+
+
+ setSearchQuery(e.target.value)}
+ />
+
+
+
+ {CATEGORIES.map((category) => (
+
+ ))}
+
+
+
+ {filteredIngredients.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..baad900
--- /dev/null
+++ b/src/screens/AddIngredientPage/style.css
@@ -0,0 +1,284 @@
+.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.10px;
+ line-height: 29.0px;
+ 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 {
+ align-self: stretch;
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
+ gap: 20px;
+ padding: 20px 0;
+ width: 100%;
+}
+
+.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;
+}
+
+.add-ingredient-card:hover {
+ border-color: #f6910b;
+ transform: translateY(-2px);
+ box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1);
+}
+
+.add-ingredient-card.selected {
+ border-color: #f6910b;
+ background-color: #f6910b1a;
+}
+
+.add-ingredient-card-image {
+ width: 80px;
+ height: 80px;
+ object-fit: cover;
+ border-radius: 8px;
+}
+
+.add-ingredient-card-name {
+ color: #000000;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 600;
+ text-align: center;
+}
+
+.add-ingredient-card-category {
+ color: #666666;
+ font-family: "Inter", Helvetica;
+ font-size: 12px;
+ 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: 16px;
+ 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%;
+}
+
+.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;
+ }
+
+ .add-ingredient-grid {
+ grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
+ gap: 15px;
+ }
+
+ .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..c36c180
--- /dev/null
+++ b/src/screens/CommunityContent/sections/Communitycontentpage/Communitycontentpage.jsx
@@ -0,0 +1,178 @@
+import React, { useState } from "react";
+import { Link, useLocation, useParams } from "react-router-dom";
+import { useAuth } from "../../../../contexts/AuthContext";
+import { CommunitypageWrapper } from "../../../../components/CommunitypageWrapper";
+import { PageButton } from "../../../../components/PageButton";
+import "./style.css";
+
+const ALL_POSTS = [
+ // TODO: Backend Integration: Replace with API call to fetch all community posts
+ { id: 1, title: "돼지고기 100g 나눔합니다.", author: "test3User", date: "2025-11-03", category: "나눔", content: "돼지고기를 너무 많이 샀네요 남는 돼지고기 나눔해요" },
+ { id: 2, title: "양파 2개 나눔해요", author: "user123", date: "2025-11-02", category: "나눔", content: "양파 2개 필요하신 분 가져가세요" },
+ { id: 3, title: "닭고기 500g 나눔", author: "foodlover", date: "2025-11-01", category: "나눔", content: "신선한 닭고기 나눔합니다" },
+ { id: 4, title: "고추장 새것 나눔합니다", author: "chef99", date: "2025-10-31", category: "나눔", content: "개봉 안한 고추장 드립니다" },
+ { id: 5, title: "양배추 한통 가져가세요", author: "veggie_fan", date: "2025-10-30", category: "나눔", content: "양배추 한통 나눔해요" },
+ { id: 6, title: "감자 5개 나눔", author: "potato_lover", date: "2025-10-29", category: "나눔", content: "감자 5개 드립니다" },
+ { id: 7, title: "당근 한봉지 드립니다", author: "healthy_eater", date: "2025-10-28", category: "나눔", content: "당근 한봉지 나눔합니다" },
+ { id: 8, title: "우유 1L 나눔해요", author: "milk_fan", date: "2025-10-27", category: "나눔", content: "우유 1L 필요하신 분" },
+ { id: 9, title: "계란 10개 나눔", author: "egg_master", date: "2025-10-26", category: "나눔", content: "계란 10개 드립니다" },
+ { id: 10, title: "토마토 나눔합니다", author: "tomato_grower", date: "2025-10-25", category: "나눔", content: "토마토 나눔해요" },
+];
+
+export const Communitycontentpage = () => {
+ const location = useLocation();
+ const { id } = useParams();
+ const { user, isAuthenticated } = useAuth();
+ const [newComment, setNewComment] = useState("");
+
+ // Load comments from localStorage for this specific post
+ const [comments, setComments] = useState(() => {
+ const storedComments = localStorage.getItem(`comments_${id}`);
+ return storedComments ? JSON.parse(storedComments) : [];
+ });
+
+ const post = location.state?.post || {
+ // TODO: Backend Integration: Fetch post details by ID if not available in state
+ id: id,
+ title: "돼지고기 100g 나눔합니다.",
+ author: "test3User",
+ date: "2025-11-03",
+ category: "나눔",
+ content: "돼지고기를 너무 많이 샀네요 남는 돼지고기 나눔해요"
+ };
+
+ // Get related posts (2 before and 2 after current post)
+ const currentIndex = ALL_POSTS.findIndex(p => p.id === parseInt(id));
+ const relatedPosts = [];
+
+ // Get 2 posts before
+ for (let i = Math.max(0, currentIndex - 2); i < currentIndex; i++) {
+ if (ALL_POSTS[i]) relatedPosts.push(ALL_POSTS[i]);
+ }
+
+ // Get 2 posts after
+ for (let i = currentIndex + 1; i <= Math.min(ALL_POSTS.length - 1, currentIndex + 2); i++) {
+ if (ALL_POSTS[i]) relatedPosts.push(ALL_POSTS[i]);
+ }
+
+ const handleCommentSubmit = (e) => {
+ e.preventDefault();
+ if (!isAuthenticated) {
+ alert("로그인이 필요합니다.");
+ return;
+ }
+ if (!newComment.trim()) return;
+
+ const comment = {
+ id: Date.now(),
+ author: user.username,
+ content: newComment,
+ date: new Date().toLocaleString('ko-KR')
+ };
+
+ const updatedComments = [...comments, comment];
+ setComments(updatedComments);
+
+ // TODO: Backend Integration: Replace with API call to add a new comment
+ // Example: axios.post(`/api/posts/${id}/comments`, comment)
+
+ // Save comments to localStorage for this specific post (mock DB)
+ localStorage.setItem(`comments_${id}`, JSON.stringify(updatedComments));
+ setNewComment("");
+ };
+
+
+ return (
+
+
+
재료 나눔 게시판
+
+
+
+
+

+
+
{post.category}
+
+
+
+
+
+
작성자
+
+
{post.author}
+
+
작성일
+
+
{post.date}
+
+
+
+
+
+
+
댓글 ({comments.length})
+
+
+
+ {comments.map((comment) => (
+
+
{comment.author}
+
{comment.content}
+
{comment.date}
+
+ ))}
+
+
+
+
+
+
+ {relatedPosts.length > 0 && (
+
+
+
다른 게시글
+
+ {relatedPosts.map((relatedPost, index) => (
+
+ ))}
+
+ )}
+
+ );
+};
diff --git a/src/screens/CommunityContent/sections/Communitycontentpage/index.js b/src/screens/CommunityContent/sections/Communitycontentpage/index.js
new file mode 100644
index 0000000..1002677
--- /dev/null
+++ b/src/screens/CommunityContent/sections/Communitycontentpage/index.js
@@ -0,0 +1 @@
+export { Communitycontentpage } from "./Communitycontentpage";
diff --git a/src/screens/CommunityContent/sections/Communitycontentpage/style.css b/src/screens/CommunityContent/sections/Communitycontentpage/style.css
new file mode 100644
index 0000000..8c59938
--- /dev/null
+++ b/src/screens/CommunityContent/sections/Communitycontentpage/style.css
@@ -0,0 +1,336 @@
+.communitycontentpage {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ flex-grow: 1;
+ gap: 10px;
+ padding: 10px 64px;
+ position: relative;
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+.communitycontentpage .communitycontentpage-wrapper {
+ align-items: flex-start;
+ align-self: stretch;
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ border-color: #000000;
+ display: flex;
+ flex: 0 0 auto;
+ flex-direction: column;
+ gap: 10px;
+ padding: 10px;
+ position: relative;
+ width: 100%;
+}
+
+.communitycontentpage .text-wrapper-5 {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 48px;
+ font-weight: 700;
+ justify-content: center;
+ letter-spacing: -0.96px;
+ line-height: 57.6px;
+ margin-top: -1.00px;
+ position: relative;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+.communitycontentpage .div-3 {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ flex-grow: 1;
+ gap: 10px;
+ padding: 10px;
+ position: relative;
+ width: 100%;
+}
+
+.communitycontentpage .content-category {
+ align-items: flex-start;
+ background-color: #0000001a;
+ border-radius: 16px;
+ display: inline-flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ padding: 0px 20px;
+ position: relative;
+}
+
+.communitycontentpage .category-icon {
+ aspect-ratio: 1;
+ height: 54px;
+ position: relative;
+ width: 54px;
+}
+
+.communitycontentpage .content-categorytext {
+ align-items: center;
+ color: #00000099;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 48px;
+ font-weight: 700;
+ justify-content: center;
+ letter-spacing: -0.96px;
+ line-height: 57.6px;
+ margin-top: -1.00px;
+ position: relative;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+.communitycontentpage .content-header {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ padding: 0px 20px;
+ position: relative;
+ width: 100%;
+}
+
+.communitycontentpage .content-headertext {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 36px;
+ font-weight: 700;
+ justify-content: center;
+ letter-spacing: -0.72px;
+ line-height: 43.2px;
+ margin-top: -1.00px;
+ position: relative;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+.communitycontentpage .navbar {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ padding: 0px 25px;
+ position: relative;
+ width: 100%;
+}
+
+.communitycontentpage .text-wrapper-6 {
+ align-items: center;
+ color: #00000066;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 12px;
+ font-weight: 500;
+ justify-content: center;
+ letter-spacing: -0.24px;
+ line-height: 14.4px;
+ margin-top: -1.00px;
+ position: relative;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+.communitycontentpage .content {
+ align-items: flex-start;
+ align-self: stretch;
+ background-color: #f9f9f9;
+ border-radius: 12px;
+ display: flex;
+ flex: 1;
+ flex-grow: 1;
+ gap: 10px;
+ min-height: 200px;
+ padding: 20px;
+ position: relative;
+ width: 100%;
+}
+
+.communitycontentpage .content-text {
+ align-self: stretch;
+ color: #000000;
+ flex: 1;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 500;
+ letter-spacing: -0.08px;
+ line-height: 24px;
+ margin: 0;
+ position: relative;
+ white-space: pre-wrap;
+ word-break: break-word;
+}
+
+.communitycontentpage .comments-section {
+ align-self: stretch;
+ background-color: #f5f5f5;
+ border-radius: 15px;
+ display: flex;
+ flex-direction: column;
+ gap: 15px;
+ padding: 20px;
+ width: 100%;
+}
+
+.communitycontentpage .comments-header {
+ border-bottom: 1px solid #e0e0e0;
+ padding-bottom: 10px;
+}
+
+.communitycontentpage .comments-title {
+ font-family: "Inter", Helvetica;
+ font-size: 20px;
+ font-weight: 700;
+ color: #000000;
+ margin: 0;
+}
+
+.communitycontentpage .comments-list {
+ display: flex;
+ flex-direction: column;
+ gap: 15px;
+}
+
+.communitycontentpage .comment-item {
+ background-color: #ffffff;
+ border-radius: 10px;
+ padding: 15px;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.communitycontentpage .comment-author {
+ font-family: "Inter", Helvetica;
+ font-size: 14px;
+ font-weight: 700;
+ color: #f6910b;
+}
+
+.communitycontentpage .comment-content {
+ font-family: "Inter", Helvetica;
+ font-size: 14px;
+ font-weight: 500;
+ color: #000000;
+ line-height: 1.5;
+}
+
+.communitycontentpage .comment-date {
+ font-family: "Inter", Helvetica;
+ font-size: 12px;
+ font-weight: 500;
+ color: #999999;
+}
+
+.communitycontentpage .comment-form {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ margin-top: 10px;
+}
+
+.communitycontentpage .comment-input {
+ padding: 12px;
+ border: 1px solid #e0e0e0;
+ border-radius: 10px;
+ font-family: "Inter", Helvetica;
+ font-size: 14px;
+ resize: vertical;
+ transition: border-color 0.2s;
+}
+
+.communitycontentpage .comment-input:focus {
+ outline: none;
+ border-color: #f6910b;
+}
+
+.communitycontentpage .comment-input:disabled {
+ background-color: #f5f5f5;
+ cursor: not-allowed;
+}
+
+.communitycontentpage .comment-submit-btn {
+ align-self: flex-end;
+ padding: 10px 20px;
+ background-color: #f6910b;
+ border: none;
+ border-radius: 10px;
+ color: #ffffff;
+ cursor: pointer;
+ font-family: "Inter", Helvetica;
+ font-size: 14px;
+ font-weight: 700;
+ transition: background-color 0.2s;
+}
+
+.communitycontentpage .comment-submit-btn:hover:not(:disabled) {
+ background-color: #e58209;
+}
+
+.communitycontentpage .comment-submit-btn:disabled {
+ background-color: #cccccc;
+ cursor: not-allowed;
+}
+
+.communitycontentpage .div-4 {
+ align-items: center;
+ align-self: stretch;
+ border-color: #000000;
+ border-top-style: solid;
+ border-top-width: 1px;
+ display: flex;
+ flex: 0 0 auto;
+ flex-direction: column;
+ gap: 10px;
+ padding: 20px 10px;
+ position: relative;
+ width: 100%;
+}
+
+.communitycontentpage .related-posts-header {
+ align-self: stretch;
+ padding: 10px 0;
+ border-bottom: 1px solid #e0e0e0;
+ margin-bottom: 10px;
+}
+
+.communitycontentpage .related-posts-title {
+ font-family: "Inter", Helvetica;
+ font-size: 20px;
+ font-weight: 700;
+ color: #000000;
+ margin: 0;
+ text-align: center;
+}
+
+.communitycontentpage .design-component-instance-node {
+ flex: 0 0 auto !important;
+ left: unset !important;
+ margin-left: -1.00px !important;
+ margin-right: -1.00px !important;
+ margin-top: -1.00px !important;
+ top: unset !important;
+}
+
+.communitycontentpage .communitypage-2 {
+ flex: 0 0 auto !important;
+ left: unset !important;
+ margin-left: -1.00px !important;
+ margin-right: -1.00px !important;
+ top: unset !important;
+}
+
diff --git a/src/screens/CommunityContent/sections/HeaderWrapper/HeaderWrapper.jsx b/src/screens/CommunityContent/sections/HeaderWrapper/HeaderWrapper.jsx
new file mode 100644
index 0000000..d30f954
--- /dev/null
+++ b/src/screens/CommunityContent/sections/HeaderWrapper/HeaderWrapper.jsx
@@ -0,0 +1,62 @@
+import React, { useState } from "react";
+import { Link } from "react-router-dom";
+import { LoginPopup } from "../../../../components/LoginPopup";
+import { SignupPopup } from "../../../../components/SignupPopup";
+import "./style.css";
+
+export const HeaderWrapper = () => {
+ const [showLoginPopup, setShowLoginPopup] = useState(false);
+ const [showSignupPopup, setShowSignupPopup] = useState(false);
+
+ const handleSwitchToSignup = () => {
+ setShowLoginPopup(false);
+ setShowSignupPopup(true);
+ };
+
+ const handleSwitchToLogin = () => {
+ setShowSignupPopup(false);
+ setShowLoginPopup(true);
+ };
+
+ return (
+
+
+
+

+
+
+
+
+
+ 알고리즘
+
+ 셰프
+
+
+
+
+
+
+
+ {showLoginPopup && (
+ setShowLoginPopup(false)}
+ onSwitchToSignup={handleSwitchToSignup}
+ />
+ )}
+
+ {showSignupPopup && (
+ setShowSignupPopup(false)}
+ onSwitchToLogin={handleSwitchToLogin}
+ />
+ )}
+
+ );
+};
diff --git a/src/screens/CommunityContent/sections/HeaderWrapper/index.js b/src/screens/CommunityContent/sections/HeaderWrapper/index.js
new file mode 100644
index 0000000..2f50715
--- /dev/null
+++ b/src/screens/CommunityContent/sections/HeaderWrapper/index.js
@@ -0,0 +1 @@
+export { HeaderWrapper } from "./HeaderWrapper";
diff --git a/src/screens/CommunityContent/sections/HeaderWrapper/style.css b/src/screens/CommunityContent/sections/HeaderWrapper/style.css
new file mode 100644
index 0000000..749abf8
--- /dev/null
+++ b/src/screens/CommunityContent/sections/HeaderWrapper/style.css
@@ -0,0 +1,105 @@
+.header-wrapper {
+ align-items: center;
+ align-self: stretch;
+ background-color: transparent;
+ display: flex;
+ gap: 10px;
+ height: 120px;
+ padding: 10px;
+ position: relative;
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+.header-wrapper .icon-image-wrapper {
+ align-items: center;
+ display: flex;
+ flex: 1;
+ flex-grow: 1;
+ gap: 10px;
+ justify-content: flex-end;
+ overflow: hidden;
+ position: relative;
+ height: 100%;
+}
+
+.header-wrapper .icon-image-2 {
+ height: 80px;
+ width: auto;
+ display: block;
+ position: relative;
+}
+
+.header-wrapper .p {
+ 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;
+ position: relative;
+ text-align: center;
+ width: 271px;
+ height: 100%;
+}
+
+.header-wrapper .text-wrapper-3 {
+ color: #000000;
+ letter-spacing: -0.46px;
+}
+
+.header-wrapper .text-wrapper-4 {
+ color: #f6910b;
+ letter-spacing: -0.46px;
+}
+
+.header-wrapper .login-button-wrapper {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 1;
+ flex-grow: 1;
+ gap: 10px;
+ justify-content: flex-end;
+ padding: 10px;
+ position: relative;
+}
+
+.header-wrapper .login-text-wrapper {
+ 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;
+}
+
+.header-wrapper .login-text-wrapper:hover {
+ transform: scale(1.05);
+}
+
+.header-wrapper .login-text-2 {
+ 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;
+}
diff --git a/src/screens/CommunityContent/style.css b/src/screens/CommunityContent/style.css
new file mode 100644
index 0000000..ca2c8b3
--- /dev/null
+++ b/src/screens/CommunityContent/style.css
@@ -0,0 +1,9 @@
+.community-content {
+ align-items: center;
+ background-color: #ffffff;
+ display: flex;
+ flex-direction: column;
+ min-height: 1080px;
+ position: relative;
+ width: 100%;
+}
diff --git a/src/screens/CommunityPage/CommunityPage.jsx b/src/screens/CommunityPage/CommunityPage.jsx
new file mode 100644
index 0000000..fc190e3
--- /dev/null
+++ b/src/screens/CommunityPage/CommunityPage.jsx
@@ -0,0 +1,13 @@
+import React from "react";
+import { UnifiedHeader } from "../../components/UnifiedHeader";
+import { Communitypage } from "./sections/Communitypage";
+import "./style.css";
+
+export const CommunityPage = () => {
+ return (
+
+
+
+
+ );
+};
diff --git a/src/screens/CommunityPage/index.js b/src/screens/CommunityPage/index.js
new file mode 100644
index 0000000..b50944e
--- /dev/null
+++ b/src/screens/CommunityPage/index.js
@@ -0,0 +1 @@
+export { CommunityPage } from "./CommunityPage";
diff --git a/src/screens/CommunityPage/sections/Communitypage/Communitypage.jsx b/src/screens/CommunityPage/sections/Communitypage/Communitypage.jsx
new file mode 100644
index 0000000..3d44cf5
--- /dev/null
+++ b/src/screens/CommunityPage/sections/Communitypage/Communitypage.jsx
@@ -0,0 +1,130 @@
+import React, { useState, useEffect } from "react";
+import { Link, useNavigate } from "react-router-dom";
+import { useAuth } from "../../../../contexts/AuthContext";
+import { CommunitypageWrapper } from "../../../../components/CommunitypageWrapper";
+import { PageButton } from "../../../../components/PageButton";
+import "./style.css";
+
+const DEFAULT_POSTS = [
+ // TODO: Backend Integration: These would typically be fetched from the backend
+ { id: 1, title: "돼지고기 100g 나눔합니다.", author: "test3User", date: "2025-11-03", category: "나눔", content: "돼지고기를 너무 많이 샀네요 남는 돼지고기 나눔해요" },
+ { id: 2, title: "양파 2개 나눔해요", author: "user123", date: "2025-11-02", category: "나눔", content: "양파 2개 필요하신 분 가져가세요" },
+ { id: 3, title: "닭고기 500g 나눔", author: "foodlover", date: "2025-11-01", category: "나눔", content: "신선한 닭고기 나눔합니다" },
+ { id: 4, title: "고추장 새것 나눔합니다", author: "chef99", date: "2025-10-31", category: "나눔", content: "개봉 안한 고추장 드립니다" },
+ { id: 5, title: "양배추 한통 가져가세요", author: "veggie_fan", date: "2025-10-30", category: "나눔", content: "양배추 한통 나눔해요" },
+ { id: 6, title: "감자 5개 나눔", author: "potato_lover", date: "2025-10-29", category: "나눔", content: "감자 5개 드립니다" },
+ { id: 7, title: "당근 한봉지 드립니다", author: "healthy_eater", date: "2025-10-28", category: "나눔", content: "당근 한봉지 나눔합니다" },
+ { id: 8, title: "우유 1L 나눔해요", author: "milk_fan", date: "2025-10-27", category: "나눔", content: "우유 1L 필요하신 분" },
+ { id: 9, title: "계란 10개 나눔", author: "egg_master", date: "2025-10-26", category: "나눔", content: "계란 10개 드립니다" },
+ { id: 10, title: "토마토 나눔합니다", author: "tomato_grower", date: "2025-10-25", category: "나눔", content: "토마토 나눔해요" },
+ { id: 11, title: "버섯 한팩 드려요", author: "mushroom_fan", date: "2025-10-24", category: "나눔", content: "버섯 한팩 가져가세요" },
+ { id: 12, title: "파프리카 나눔", author: "veggie_lover", date: "2025-10-23", category: "나눔", content: "파프리카 나눔합니다" },
+ { id: 13, title: "두부 2모 나눔해요", author: "tofu_master", date: "2025-10-22", category: "나눔", content: "두부 2모 드립니다" },
+ { id: 14, title: "김치 나눔합니다", author: "kimchi_maker", date: "2025-10-21", category: "나눔", content: "김치 나눔해요" },
+ { id: 15, title: "된장 새것 드립니다", author: "ferment_pro", date: "2025-10-20", category: "나눔", content: "된장 새것 가져가세요" },
+ { id: 16, title: "참기름 나눔", author: "oil_collector", date: "2025-10-19", category: "나눔", content: "참기름 나눔합니다" },
+ { id: 17, title: "생선 나눔", author: "fish_lover", date: "2025-10-18", category: "나눔", content: "생선 나눔합니다" },
+ { id: 18, title: "쌀 나눔", author: "rice_farmer", date: "2025-10-17", category: "나눔", content: "쌀 나눔해요" },
+ { id: 19, title: "과일 나눔", author: "fruit_fan", date: "2025-10-16", category: "나눔", content: "과일 나눔합니다" },
+ { id: 20, title: "야채 나눔", author: "veggie_master", date: "2025-10-15", category: "나눔", content: "야채 나눔해요" },
+];
+
+const POSTS_PER_PAGE = 10;
+
+export const Communitypage = () => {
+ const navigate = useNavigate();
+ const { isAuthenticated } = useAuth();
+ const [currentPage, setCurrentPage] = useState(1);
+ const [allPosts, setAllPosts] = useState([]);
+
+ useEffect(() => {
+ // TODO: Backend Integration: Replace with API call to fetch all community posts
+ // Example: axios.get('/api/posts').then(response => setAllPosts(response.data));
+
+ const storedPosts = JSON.parse(localStorage.getItem("communityPosts") || "[]");
+ const combined = [...storedPosts, ...DEFAULT_POSTS];
+ setAllPosts(combined);
+ }, []);
+
+ const handleCreatePost = () => {
+ if (!isAuthenticated) {
+ alert("로그인이 필요합니다.");
+ return;
+ }
+ navigate("/createpost");
+ };
+
+ const totalPages = Math.min(Math.ceil(allPosts.length / POSTS_PER_PAGE), 5);
+ const startIndex = (currentPage - 1) * POSTS_PER_PAGE;
+ const endIndex = startIndex + POSTS_PER_PAGE;
+ const currentPosts = allPosts.slice(startIndex, endIndex);
+
+ const handlePageChange = (page) => {
+ if (page >= 1 && page <= totalPages) {
+ setCurrentPage(page);
+ }
+ };
+ return (
+
+
+
+
+ {currentPosts.map((post, index) => (
+
+ ))}
+
+
+
+
+
+
+ {Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => (
+
handlePageChange(page)}
+ />
+ ))}
+
+
+
+
+ );
+};
diff --git a/src/screens/CommunityPage/sections/Communitypage/index.js b/src/screens/CommunityPage/sections/Communitypage/index.js
new file mode 100644
index 0000000..35d0d64
--- /dev/null
+++ b/src/screens/CommunityPage/sections/Communitypage/index.js
@@ -0,0 +1 @@
+export { Communitypage } from "./Communitypage";
diff --git a/src/screens/CommunityPage/sections/Communitypage/style.css b/src/screens/CommunityPage/sections/Communitypage/style.css
new file mode 100644
index 0000000..3e5f9ef
--- /dev/null
+++ b/src/screens/CommunityPage/sections/Communitypage/style.css
@@ -0,0 +1,194 @@
+.communitypage {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ flex-grow: 1;
+ gap: 10px;
+ padding: 10px 64px;
+ position: relative;
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+.communitypage .communitypage-header {
+ align-items: flex-start;
+ align-self: stretch;
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ border-color: #000000;
+ display: flex;
+ flex: 0 0 auto;
+ flex-direction: column;
+ gap: 10px;
+ padding: 10px;
+ position: relative;
+ width: 100%;
+}
+
+.communitypage .div {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 48px;
+ font-weight: 700;
+ justify-content: center;
+ letter-spacing: -0.96px;
+ line-height: 57.6px;
+ margin-top: -1.00px;
+ position: relative;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+.communitypage .div-2 {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ flex-grow: 1;
+ gap: 10px;
+ max-height: 800px;
+ padding: 10px;
+ position: relative;
+ width: 100%;
+}
+
+.communitypage .communitypage-postheader {
+ align-self: stretch !important;
+ flex: 0 0 auto !important;
+ left: unset !important;
+ margin-left: -1.00px !important;
+ margin-right: -1.00px !important;
+ margin-top: -1.00px !important;
+ top: unset !important;
+ width: 100% !important;
+}
+
+.communitypage .communitypage-instance {
+ flex: 0 0 auto !important;
+ left: unset !important;
+ margin-left: -1.00px !important;
+ margin-right: -1.00px !important;
+ top: unset !important;
+}
+
+.communitypage .communitypage-postheader-instance {
+ flex: 0 0 auto !important;
+ left: unset !important;
+ margin-left: -1.00px !important;
+ margin-right: -1.00px !important;
+ overflow: hidden !important;
+ top: unset !important;
+}
+
+.communitypage .communitypage-wrapper-2 {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ justify-content: flex-end;
+ padding: 10px 0;
+ position: relative;
+ width: 100%;
+}
+
+.communitypage .communitypage-wrapper-3 {
+ align-items: center;
+ background-color: #f5900b;
+ border: none;
+ border-radius: 16px;
+ cursor: pointer;
+ display: inline-flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ justify-content: center;
+ overflow: hidden;
+ padding: 15px 20px;
+ position: relative;
+ text-decoration: none;
+ transition: background-color 0.2s, transform 0.2s;
+}
+
+.communitypage .communitypage-wrapper-3:hover {
+ background-color: #e58209;
+ transform: translateY(-2px);
+}
+
+.communitypage .text-wrapper-2 {
+ align-items: center;
+ color: #ffffff;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 700;
+ justify-content: center;
+ letter-spacing: -0.32px;
+ line-height: 19.2px;
+ margin-top: -1.00px;
+ position: relative;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+.communitypage .communitypage-nav {
+ align-items: center;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ justify-content: center;
+ padding: 20px 10px;
+ position: relative;
+ width: 317px;
+}
+
+.communitypage .page-nav-arrow {
+ background: none;
+ border: none;
+ cursor: pointer;
+ padding: 5px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: opacity 0.2s;
+}
+
+.communitypage .page-nav-arrow:disabled {
+ opacity: 0.3;
+ cursor: not-allowed;
+}
+
+.communitypage .page-nav-arrow:hover:not(:disabled) {
+ opacity: 0.7;
+}
+
+.communitypage .img {
+ height: 20px;
+ position: relative;
+ width: 20px;
+}
+
+.communitypage .page-button-container {
+ left: unset !important;
+ top: unset !important;
+}
+
+@media (max-width: 768px) {
+ .communitypage {
+ padding: 10px 20px;
+ }
+
+ .communitypage .div {
+ font-size: 32px;
+ line-height: 38.4px;
+ }
+
+ .communitypage .text-wrapper-2 {
+ font-size: 14px;
+ }
+}
diff --git a/src/screens/CommunityPage/sections/Header/Header.jsx b/src/screens/CommunityPage/sections/Header/Header.jsx
new file mode 100644
index 0000000..0129aa7
--- /dev/null
+++ b/src/screens/CommunityPage/sections/Header/Header.jsx
@@ -0,0 +1,101 @@
+import React, { useState } from "react";
+import { Link } from "react-router-dom";
+import { useAuth } from "../../../../contexts/AuthContext";
+import { LoginPopup } from "../../../../components/LoginPopup";
+import { SignupPopup } from "../../../../components/SignupPopup";
+import { PreferencesPopup } from "../../../../components/PreferencesPopup";
+import "./style.css";
+
+export const Header = () => {
+ 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 (
+
+ );
+};
diff --git a/src/screens/CommunityPage/sections/Header/index.js b/src/screens/CommunityPage/sections/Header/index.js
new file mode 100644
index 0000000..c940126
--- /dev/null
+++ b/src/screens/CommunityPage/sections/Header/index.js
@@ -0,0 +1 @@
+export { Header } from "./Header";
diff --git a/src/screens/CommunityPage/sections/Header/style.css b/src/screens/CommunityPage/sections/Header/style.css
new file mode 100644
index 0000000..6a13ba6
--- /dev/null
+++ b/src/screens/CommunityPage/sections/Header/style.css
@@ -0,0 +1,217 @@
+.header {
+ align-items: center;
+ background-color: transparent;
+ display: flex;
+ justify-content: space-between;
+ gap: 10px;
+ height: 120px;
+ padding: 10px;
+ position: relative;
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+.header .header-left-section {
+ display: flex;
+ align-items: center;
+ flex: 1;
+ justify-content: flex-start;
+}
+
+.header .icon-image {
+ height: 80px;
+ width: auto;
+ display: block;
+ position: relative;
+}
+
+.header .algorithm-label-link {
+ position: absolute;
+ left: 50%;
+ transform: translateX(-50%);
+ display: flex;
+ align-items: center;
+ height: 100%;
+}
+
+.header .algorithm-label {
+ 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;
+ position: relative;
+ text-align: center;
+ margin: 0;
+}
+
+.header .text-wrapper {
+ color: #000000;
+ letter-spacing: -0.46px;
+}
+
+.header .span {
+ color: #f6910b;
+ letter-spacing: -0.46px;
+}
+
+.header .header-right-section {
+ display: flex;
+ align-items: center;
+ flex: 1;
+ gap: 10px;
+ justify-content: flex-end;
+ padding: 10px;
+}
+
+.header .mypage-button {
+ 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;
+}
+
+.header .mypage-button:hover {
+ transform: scale(1.05);
+}
+
+.header .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;
+}
+
+.header .login-button {
+ 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;
+}
+
+.header .login-button:hover {
+ transform: scale(1.05);
+}
+
+.header .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;
+}
+
+.header .user-info-wrapper {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.header .user-name-display {
+ color: #000000;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 600;
+ padding: 8px 16px;
+ background-color: #f6910b1a;
+ border-radius: 20px;
+}
+
+.header .logout-button {
+ 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;
+}
+
+.header .logout-button:hover {
+ transform: scale(1.05);
+}
+
+@media (max-width: 768px) {
+ .header {
+ padding: 10px 5px;
+ height: auto;
+ min-height: 80px;
+ }
+
+ .header .icon-image {
+ height: 50px;
+ }
+
+ .header .algorithm-label {
+ font-size: 24px;
+ }
+
+ .header .mypage-button,
+ .header .login-button,
+ .header .logout-button {
+ width: 90px;
+ height: 32px;
+ }
+
+ .header .mypage-text,
+ .header .login-text {
+ font-size: 12px;
+ }
+
+ .header .user-name-display {
+ font-size: 12px;
+ padding: 4px 10px;
+ }
+}
+
+@media (max-width: 480px) {
+ .header .algorithm-label {
+ font-size: 20px;
+ }
+
+ .header .header-right-section {
+ gap: 5px;
+ padding: 5px;
+ }
+}
diff --git a/src/screens/CommunityPage/style.css b/src/screens/CommunityPage/style.css
new file mode 100644
index 0000000..20ece7e
--- /dev/null
+++ b/src/screens/CommunityPage/style.css
@@ -0,0 +1,23 @@
+.community-page {
+ align-items: center;
+ background-color: #ffffff;
+ display: flex;
+ flex-direction: column;
+ min-height: 1080px;
+ position: relative;
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+@media (max-width: 1280px) {
+ .community-page {
+ width: 100%;
+ }
+}
+
+@media (max-width: 768px) {
+ .community-page {
+ min-height: auto;
+ }
+}
diff --git a/src/screens/CreatePostPage/CreatePostPage.jsx b/src/screens/CreatePostPage/CreatePostPage.jsx
new file mode 100644
index 0000000..e92ec12
--- /dev/null
+++ b/src/screens/CreatePostPage/CreatePostPage.jsx
@@ -0,0 +1,99 @@
+import React, { useState, useEffect } from "react";
+import { Link, useNavigate } from "react-router-dom";
+import { useAuth } from "../../contexts/AuthContext";
+import { UnifiedHeader } from "../../components/UnifiedHeader";
+import "./style.css";
+
+export const CreatePostPage = () => {
+ const navigate = useNavigate();
+ const { user, isAuthenticated } = useAuth();
+ const [title, setTitle] = useState("");
+ const [content, setContent] = useState("");
+
+ useEffect(() => {
+ if (!isAuthenticated) {
+ alert("로그인이 필요합니다.");
+ navigate("/communitypage");
+ }
+ }, [isAuthenticated, navigate]);
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+
+ if (!isAuthenticated) {
+ alert("로그인이 필요합니다.");
+ return;
+ }
+
+ // TODO: Backend Integration: Replace with API call to create a new post
+ // Example: axios.post('/api/posts', newPost)
+
+ // Get existing posts (mock DB)
+ const existingPosts = JSON.parse(localStorage.getItem("communityPosts") || "[]");
+
+ // Create new post
+ const newPost = {
+ id: Date.now(), // Unique ID for the post
+ title,
+ content,
+ author: user.username,
+ date: new Date().toISOString().split('T')[0], // Current date
+ category: "나눔" // Default category
+ };
+
+ // Add to beginning of array (최상단)
+ const updatedPosts = [newPost, ...existingPosts];
+ localStorage.setItem("communityPosts", JSON.stringify(updatedPosts));
+
+ console.log("Creating post:", newPost);
+ navigate("/communitypage");
+ };
+
+ return (
+
+
+
+
+
+
새 글 작성
+
재료 나눔 게시판에 글을 작성하세요
+
+
+
+
+
+ );
+};
diff --git a/src/screens/CreatePostPage/index.js b/src/screens/CreatePostPage/index.js
new file mode 100644
index 0000000..b99e657
--- /dev/null
+++ b/src/screens/CreatePostPage/index.js
@@ -0,0 +1 @@
+export { CreatePostPage } from "./CreatePostPage";
diff --git a/src/screens/CreatePostPage/style.css b/src/screens/CreatePostPage/style.css
new file mode 100644
index 0000000..88bf770
--- /dev/null
+++ b/src/screens/CreatePostPage/style.css
@@ -0,0 +1,180 @@
+.create-post-page {
+ align-items: center;
+ background-color: #ffffff;
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ position: relative;
+ width: 100%;
+}
+
+.create-post-page .create-post-header {
+ align-self: stretch !important;
+ width: 100% !important;
+ max-width: 1280px !important;
+ margin: 0 auto !important;
+}
+
+.create-post-content {
+ align-items: flex-start;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ gap: 30px;
+ padding: 20px 64px;
+ position: relative;
+ width: 100%;
+ max-width: 900px;
+ margin: 0 auto;
+}
+
+.create-post-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%;
+}
+
+.create-post-title {
+ color: #000000;
+ font-family: "Inter", Helvetica;
+ font-size: 48px;
+ font-weight: 700;
+ letter-spacing: -0.96px;
+ line-height: 57.6px;
+ margin: 0;
+}
+
+.create-post-subtitle {
+ color: #0000008c;
+ font-family: "Inter", Helvetica;
+ font-size: 20px;
+ font-weight: 500;
+ letter-spacing: -0.10px;
+ line-height: 29.0px;
+ margin: 0;
+}
+
+.create-post-form {
+ align-self: stretch;
+ display: flex;
+ flex-direction: column;
+ gap: 25px;
+ width: 100%;
+}
+
+.create-post-input-group {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.create-post-label {
+ color: #000000;
+ font-family: "Inter", Helvetica;
+ font-size: 18px;
+ font-weight: 600;
+}
+
+.create-post-input {
+ padding: 12px 20px;
+ border: 1px solid #e0e0e0;
+ border-radius: 12px;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ transition: border-color 0.2s;
+}
+
+.create-post-input:focus {
+ outline: none;
+ border-color: #f6910b;
+}
+
+.create-post-textarea {
+ padding: 12px 20px;
+ border: 1px solid #e0e0e0;
+ border-radius: 12px;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ resize: vertical;
+ transition: border-color 0.2s;
+}
+
+.create-post-textarea:focus {
+ outline: none;
+ border-color: #f6910b;
+}
+
+.create-post-footer {
+ display: flex;
+ gap: 15px;
+ justify-content: flex-end;
+ padding-top: 20px;
+}
+
+.create-post-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;
+}
+
+.create-post-cancel-btn:hover {
+ background-color: #d0d0d0;
+}
+
+.create-post-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;
+}
+
+.create-post-submit-btn:hover {
+ background-color: #e58209;
+}
+
+@media (max-width: 768px) {
+ .create-post-content {
+ padding: 20px 20px;
+ }
+
+ .create-post-title {
+ font-size: 32px;
+ line-height: 38.4px;
+ }
+
+ .create-post-subtitle {
+ font-size: 16px;
+ line-height: 23.2px;
+ }
+
+ .create-post-footer {
+ flex-direction: column;
+ }
+
+ .create-post-cancel-btn,
+ .create-post-submit-btn {
+ width: 100%;
+ }
+}
diff --git a/src/screens/Desktop/Desktop.jsx b/src/screens/Desktop/Desktop.jsx
new file mode 100644
index 0000000..0df0f3b
--- /dev/null
+++ b/src/screens/Desktop/Desktop.jsx
@@ -0,0 +1,23 @@
+import React from "react";
+import { UnifiedHeader } from "../../components/UnifiedHeader";
+import { Community } from "./sections/Community";
+import { Footer } from "./sections/Footer";
+import { IngredientInventory } from "./sections/IngredientInventory";
+import { MenuInventory } from "./sections/MenuInventory";
+import "./style.css";
+
+export const Desktop = () => {
+ return (
+
+ );
+};
diff --git a/src/screens/Desktop/index.js b/src/screens/Desktop/index.js
new file mode 100644
index 0000000..4d75c1e
--- /dev/null
+++ b/src/screens/Desktop/index.js
@@ -0,0 +1 @@
+export { Desktop } from "./Desktop";
diff --git a/src/screens/Desktop/sections/Community/Community.jsx b/src/screens/Desktop/sections/Community/Community.jsx
new file mode 100644
index 0000000..49a6158
--- /dev/null
+++ b/src/screens/Desktop/sections/Community/Community.jsx
@@ -0,0 +1,38 @@
+import React from "react";
+import { Link } from "react-router-dom";
+import { CommunitypageWrapper } from "../../../../components/CommunitypageWrapper";
+import "./style.css";
+
+const MOCK_POSTS = [
+ // TODO: Backend Integration: Replace with API call to fetch recent community posts
+ { id: 1, title: "돼지고기 100g 나눔합니다.", author: "test3User", date: "2025-11-03", category: "나눔", content: "돼지고기를 너무 많이 샀네요 남는 돼지고기 나눔해요" },
+ { id: 2, title: "양파 2개 나눔해요", author: "user123", date: "2025-11-02", category: "나눔", content: "양파 2개 필요하신 분 가져가세요" },
+ { id: 3, title: "닭고기 500g 나눔", author: "foodlover", date: "2025-11-01", category: "나눔", content: "신선한 닭고기 나눔합니다" },
+ { id: 4, title: "고추장 새것 나눔합니다", author: "chef99", date: "2025-10-31", category: "나눔", content: "개봉 안한 고추장 드립니다" },
+ { id: 5, title: "양배추 한통 가져가세요", author: "veggie_fan", date: "2025-10-30", category: "나눔", content: "양배추 한통 나눔해요" },
+];
+
+export const Community = () => {
+ return (
+
+
+
재료 나눔 게시판
+
+
+
+ {MOCK_POSTS.map((post, index) => (
+
+ ))}
+
+
+ );
+};
diff --git a/src/screens/Desktop/sections/Community/index.js b/src/screens/Desktop/sections/Community/index.js
new file mode 100644
index 0000000..95a905a
--- /dev/null
+++ b/src/screens/Desktop/sections/Community/index.js
@@ -0,0 +1 @@
+export { Community } from "./Community";
diff --git a/src/screens/Desktop/sections/Community/style.css b/src/screens/Desktop/sections/Community/style.css
new file mode 100644
index 0000000..2e024bd
--- /dev/null
+++ b/src/screens/Desktop/sections/Community/style.css
@@ -0,0 +1,83 @@
+.community {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ flex-direction: column;
+ gap: 10px;
+ padding: 10px 64px;
+ position: relative;
+ width: 100%;
+}
+
+.community .community-header {
+ align-self: stretch;
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ border-color: #000000b2;
+ display: flex;
+ flex: 0 0 auto;
+ padding: 10px 0;
+ position: relative;
+ width: 100%;
+}
+
+.community .community-header-2 {
+ align-items: center;
+ 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;
+ white-space: nowrap;
+}
+
+@media (max-width: 768px) {
+ .community .community-header-2 {
+ font-size: 32px;
+ line-height: 38.4px;
+ }
+}
+
+.community .community-contents {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ flex-direction: column;
+ gap: 10px;
+ max-height: 300px;
+ overflow: hidden;
+ padding: 10px 20px;
+ position: relative;
+ width: 100%;
+}
+
+.community .communitypage-3 {
+ flex: 0 0 auto !important;
+ left: unset !important;
+ margin-left: -1.00px !important;
+ margin-right: -21.00px !important;
+ margin-top: -1.00px !important;
+ top: unset !important;
+}
+
+.community .communitypage-4 {
+ flex: 0 0 auto !important;
+ left: unset !important;
+ margin-left: -1.00px !important;
+ margin-right: -21.00px !important;
+ top: unset !important;
+}
+
+.community .communitypage-5 {
+ flex: 0 0 auto !important;
+ left: unset !important;
+ margin-bottom: -1.00px !important;
+ margin-left: -1.00px !important;
+ margin-right: -21.00px !important;
+ top: unset !important;
+}
diff --git a/src/screens/Desktop/sections/Footer/Footer.jsx b/src/screens/Desktop/sections/Footer/Footer.jsx
new file mode 100644
index 0000000..c6a7cd0
--- /dev/null
+++ b/src/screens/Desktop/sections/Footer/Footer.jsx
@@ -0,0 +1,42 @@
+import React from "react";
+import "./style.css";
+
+export const Footer = () => {
+ return (
+
+ );
+};
diff --git a/src/screens/Desktop/sections/Footer/index.js b/src/screens/Desktop/sections/Footer/index.js
new file mode 100644
index 0000000..e5ea0e5
--- /dev/null
+++ b/src/screens/Desktop/sections/Footer/index.js
@@ -0,0 +1 @@
+export { Footer } from "./Footer";
diff --git a/src/screens/Desktop/sections/Footer/style.css b/src/screens/Desktop/sections/Footer/style.css
new file mode 100644
index 0000000..9ff750f
--- /dev/null
+++ b/src/screens/Desktop/sections/Footer/style.css
@@ -0,0 +1,156 @@
+.footer {
+ align-items: center;
+ align-self: stretch;
+ background-color: transparent;
+ border-radius: 20px;
+ display: flex;
+ gap: 96px;
+ min-height: 266px;
+ justify-content: space-around;
+ padding: 20px 64px;
+ position: relative;
+ width: 100%;
+ margin-top: 40px;
+}
+
+.footer .footer-container {
+ align-items: flex-start;
+ align-self: stretch;
+ border-color: #000000b2;
+ border-top-style: solid;
+ border-top-width: 1px;
+ display: flex;
+ flex: 1;
+ flex-grow: 1;
+ gap: 120px;
+ padding: 20px 0px;
+ position: relative;
+}
+
+.footer .footer-left-content {
+ align-items: flex-start;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ flex-grow: 1;
+ gap: 10px;
+ position: relative;
+}
+
+.footer .company-container {
+ align-items: flex-start;
+ display: inline-flex;
+ flex: 0 0 auto;
+ gap: 8px;
+ position: relative;
+}
+
+.footer .company-text {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 24px;
+ font-weight: 600;
+ justify-content: center;
+ letter-spacing: -0.48px;
+ line-height: 34.8px;
+ margin-top: -1.00px;
+ position: relative;
+ text-align: center;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+.footer .developer-container {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ justify-content: center;
+ padding: 0px 10px;
+ position: relative;
+ width: 100%;
+}
+
+.footer .developer-text {
+ align-items: flex-start;
+ color: #0000008c;
+ display: flex;
+ flex: 1;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 500;
+ justify-content: flex-start;
+ letter-spacing: -0.08px;
+ line-height: 23.2px;
+ margin-top: -1.00px;
+ position: relative;
+ text-align: left;
+}
+
+@media (max-width: 768px) {
+ .footer .developer-text {
+ font-size: 14px;
+ line-height: 20.3px;
+ }
+}
+
+.footer .footer-right-content {
+ align-items: flex-start;
+ display: inline-flex;
+ flex: 0 0 auto;
+ gap: 40px;
+ position: relative;
+}
+
+.footer .support-container {
+ align-items: flex-end;
+ align-self: stretch;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ justify-content: center;
+ position: relative;
+ width: 130px;
+}
+
+.footer .support-header {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 4px;
+ padding: 0px 0px 16px;
+ position: relative;
+ width: 100%;
+}
+
+.footer .support-text {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 600;
+ justify-content: center;
+ letter-spacing: -0.08px;
+ line-height: 23.2px;
+ margin-top: -1.00px;
+ position: relative;
+ width: 63px;
+}
+
+.footer .text-wrapper-8 {
+ align-items: center;
+ align-self: stretch;
+ color: #0000008c;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 500;
+ justify-content: center;
+ letter-spacing: -0.08px;
+ line-height: 23.2px;
+ position: relative;
+}
diff --git a/src/screens/Desktop/sections/HeaderInstanceWrapper/HeaderInstanceWrapper.jsx b/src/screens/Desktop/sections/HeaderInstanceWrapper/HeaderInstanceWrapper.jsx
new file mode 100644
index 0000000..737b9bb
--- /dev/null
+++ b/src/screens/Desktop/sections/HeaderInstanceWrapper/HeaderInstanceWrapper.jsx
@@ -0,0 +1,7 @@
+import React from "react";
+import { DivWrapper } from "../../../../components/DivWrapper";
+import "./style.css";
+
+export const HeaderInstanceWrapper = () => {
+ return ;
+};
diff --git a/src/screens/Desktop/sections/HeaderInstanceWrapper/index.js b/src/screens/Desktop/sections/HeaderInstanceWrapper/index.js
new file mode 100644
index 0000000..05c0f61
--- /dev/null
+++ b/src/screens/Desktop/sections/HeaderInstanceWrapper/index.js
@@ -0,0 +1 @@
+export { HeaderInstanceWrapper } from "./HeaderInstanceWrapper";
diff --git a/src/screens/Desktop/sections/HeaderInstanceWrapper/style.css b/src/screens/Desktop/sections/HeaderInstanceWrapper/style.css
new file mode 100644
index 0000000..43b0cd3
--- /dev/null
+++ b/src/screens/Desktop/sections/HeaderInstanceWrapper/style.css
@@ -0,0 +1,7 @@
+.header-instance {
+ align-self: stretch !important;
+ background-color: transparent !important;
+ width: 100% !important;
+ max-width: 1280px !important;
+ margin: 0 auto !important;
+}
diff --git a/src/screens/Desktop/sections/IngredientInventory/IngredientInventory.jsx b/src/screens/Desktop/sections/IngredientInventory/IngredientInventory.jsx
new file mode 100644
index 0000000..b15217e
--- /dev/null
+++ b/src/screens/Desktop/sections/IngredientInventory/IngredientInventory.jsx
@@ -0,0 +1,110 @@
+import React, { useState } from "react";
+import { Link } from "react-router-dom";
+import { IngredientComponent } from "../../../../components/IngredientComponent";
+import { IngredientInventoryWrapper } from "../../../../components/IngredientInventoryWrapper";
+import { AddIngredientPopup } from "../../../../components/AddIngredientPopup";
+import "./style.css";
+
+const CATEGORIES = ["전체", "육류", "채소류", "가공류", "유제품", "기타"];
+
+const INITIAL_INGREDIENTS = [
+ // TODO: Backend Integration: Replace with API call to fetch user's ingredients
+ { id: 1, name: "양파", category: "채소류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-1.png", expiryDays: 5 },
+ { id: 2, name: "닭고기", category: "육류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png", expiryDays: 3 },
+ { id: 3, name: "고추장", category: "가공류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png", expiryDays: 30 },
+ { id: 4, name: "고춧가루", category: "가공류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png", expiryDays: 60 },
+ { id: 5, name: "양배추", category: "채소류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png", expiryDays: 7 },
+ { id: 6, name: "스테비아", category: "가공류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png", expiryDays: 90 },
+ { id: 7, name: "마늘", category: "채소류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png", expiryDays: 14 },
+];
+
+export const IngredientInventory = () => {
+ const [selectedCategory, setSelectedCategory] = useState("전체");
+ const [showCategoryMenu, setShowCategoryMenu] = useState(false);
+ const [showAddPopup, setShowAddPopup] = useState(false);
+ const [ingredients, setIngredients] = useState(INITIAL_INGREDIENTS);
+
+ const handleAddIngredient = (newIngredient) => {
+ setIngredients([...ingredients, newIngredient]);
+ };
+
+ const filteredIngredients = selectedCategory === "전체"
+ ? ingredients
+ : ingredients.filter(item => item.category === selectedCategory);
+
+ return (
+
+
+
+
+ {showCategoryMenu && (
+
+ {CATEGORIES.map((category) => (
+
+ ))}
+
+ )}
+
+
+
+ {filteredIngredients.map((ingredient) => (
+
+ ))}
+
+
+
+
+
+

+
+
+
+
+
+
+
+ {showAddPopup && (
+
setShowAddPopup(false)}
+ onAdd={handleAddIngredient}
+ />
+ )}
+
+ );
+};
diff --git a/src/screens/Desktop/sections/IngredientInventory/index.js b/src/screens/Desktop/sections/IngredientInventory/index.js
new file mode 100644
index 0000000..3bd227e
--- /dev/null
+++ b/src/screens/Desktop/sections/IngredientInventory/index.js
@@ -0,0 +1 @@
+export { IngredientInventory } from "./IngredientInventory";
diff --git a/src/screens/Desktop/sections/IngredientInventory/style.css b/src/screens/Desktop/sections/IngredientInventory/style.css
new file mode 100644
index 0000000..87c83a9
--- /dev/null
+++ b/src/screens/Desktop/sections/IngredientInventory/style.css
@@ -0,0 +1,262 @@
+.ingredient-inventory {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ flex-direction: column;
+ gap: 10px;
+ padding: 25px 64px;
+ position: relative;
+ width: 100%;
+}
+
+@media (max-width: 768px) {
+ .ingredient-inventory {
+ padding: 20px 20px;
+ }
+}
+
+.ingredient-inventory .ingredient-inventory-header {
+ align-self: stretch !important;
+ flex: 0 0 auto !important;
+ left: unset !important;
+ top: unset !important;
+ width: 100% !important;
+}
+
+.ingredient-inventory .ingredient-category-wrapper {
+ position: relative;
+ display: inline-flex;
+ flex: 0 0 auto;
+}
+
+.ingredient-inventory .ingredient-category-button {
+ align-items: center;
+ background-color: #f6910b;
+ border: none;
+ border-radius: 16px;
+ cursor: pointer;
+ display: flex;
+ gap: 8px;
+ justify-content: center;
+ overflow: hidden;
+ padding: 8px 25px;
+ position: relative;
+ transition: background-color 0.2s;
+}
+
+.ingredient-inventory .ingredient-category-button:hover {
+ background-color: #e58209;
+}
+
+.ingredient-inventory .ingredient-category-text {
+ align-items: center;
+ color: #ffffff;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 18px;
+ font-weight: 500;
+ justify-content: center;
+ letter-spacing: -0.09px;
+ line-height: 26.1px;
+ text-align: center;
+ white-space: nowrap;
+}
+
+.ingredient-inventory .ingredient-category-arrow {
+ color: #ffffff;
+ font-size: 12px;
+}
+
+.ingredient-inventory .ingredient-category-menu {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ margin-top: 5px;
+ background-color: #ffffff;
+ border: 1px solid #0000001a;
+ border-radius: 12px;
+ box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1);
+ overflow: hidden;
+ z-index: 10;
+ min-width: 120px;
+}
+
+.ingredient-inventory .ingredient-category-menu-item {
+ width: 100%;
+ padding: 10px 20px;
+ background: none;
+ border: none;
+ text-align: left;
+ cursor: pointer;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 500;
+ color: #000000;
+ transition: background-color 0.2s;
+}
+
+.ingredient-inventory .ingredient-category-menu-item:hover {
+ background-color: #f5f5f5;
+}
+
+.ingredient-inventory .ingredient-category-menu-item.active {
+ background-color: #f6910b1a;
+ color: #f6910b;
+ font-weight: 600;
+}
+
+.ingredient-inventory .ingrdient-invnetory {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ gap: 32px;
+ height: 120px;
+ overflow: hidden;
+ overflow-x: scroll;
+ padding: 0px 15px;
+ position: relative;
+ width: 100%;
+}
+
+.ingredient-inventory .ingrdient-invnetory::-webkit-scrollbar {
+ display: none;
+ width: 0;
+}
+
+.ingredient-inventory .ingredient-component-instance {
+ left: unset !important;
+}
+
+.ingredient-inventory .add-new-ingredient {
+ align-items: center;
+ background-color: #ffffff;
+ border: 1px solid;
+ border-color: #0000001a;
+ border-radius: 16px;
+ box-shadow: 0px 12px 32px #0000000a, 0px 4px 8px #00000005;
+ cursor: pointer;
+ display: flex;
+ flex-direction: column;
+ gap: 15px;
+ height: 120px;
+ justify-content: center;
+ max-height: 120px;
+ min-width: 140px;
+ overflow: hidden;
+ padding: 20px 10px;
+ position: relative;
+ flex-shrink: 0;
+ transition: transform 0.2s, box-shadow 0.2s;
+ text-decoration: none;
+}
+
+.ingredient-inventory .add-new-ingredient:hover {
+ transform: translateY(-2px);
+ box-shadow: 0px 16px 40px #0000001a, 0px 6px 12px #0000000a;
+}
+
+.ingredient-inventory .receipt-register-btn {
+ align-items: center;
+ background-color: #f6910b;
+ border: none;
+ border-radius: 16px;
+ box-shadow: 0px 12px 32px #0000000a, 0px 4px 8px #00000005;
+ cursor: pointer;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ height: 120px;
+ justify-content: center;
+ min-width: 140px;
+ overflow: hidden;
+ padding: 20px 10px;
+ position: relative;
+ flex-shrink: 0;
+ transition: transform 0.2s, box-shadow 0.2s;
+}
+
+.ingredient-inventory .receipt-register-btn:hover {
+ transform: translateY(-2px);
+ box-shadow: 0px 16px 40px #0000001a, 0px 6px 12px #0000000a;
+ background-color: #e58209;
+}
+
+.ingredient-inventory .receipt-register-container {
+ align-items: center;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.ingredient-inventory .receipt-register-text {
+ color: #ffffff;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 700;
+ text-align: center;
+ white-space: nowrap;
+}
+
+.ingredient-inventory .receipt-register-icon {
+ width: 32px;
+ height: 32px;
+ filter: brightness(0) invert(1);
+}
+
+.ingredient-inventory .add-new-container {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ flex-grow: 1;
+ gap: 8px;
+ position: relative;
+ width: 100%;
+}
+
+.ingredient-inventory .add-new-header {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ justify-content: center;
+ position: relative;
+ width: 100%;
+}
+
+.ingredient-inventory .add-new-header-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;
+ margin-top: -1.00px;
+ position: relative;
+ text-align: center;
+ width: 74px;
+}
+
+.ingredient-inventory .add-new-section {
+ 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-inventory .add-new-icon {
+ align-self: stretch;
+ aspect-ratio: 1;
+ position: relative;
+}
diff --git a/src/screens/Desktop/sections/MenuInventory/MenuInventory.jsx b/src/screens/Desktop/sections/MenuInventory/MenuInventory.jsx
new file mode 100644
index 0000000..32785cb
--- /dev/null
+++ b/src/screens/Desktop/sections/MenuInventory/MenuInventory.jsx
@@ -0,0 +1,115 @@
+import React, { useState, useEffect } from "react";
+import { Link, useNavigate } from "react-router-dom";
+import { MenuInventoryWrapper } from "../../../../components/MenuInventoryWrapper";
+import { Tendency } from "../../../../components/Tendency";
+import { UsedIngredient } from "../../../../components/UsedIngredient";
+import "./style.css";
+
+const MOCK_RECIPES = [
+ {
+ id: 1,
+ name: "저당 닭갈비",
+ ingredients: ["닭고기", "양파", "고추장", "고춧가루", "양배추", "스테비아", "마늘"],
+ steps: ["닭고기 손질 및 양념 재우기", "야채 손질", "볶기"],
+ tendencies: ["매콤칼칼", "스트레스 해소", "저당"]
+ },
+ {
+ id: 2,
+ name: "저당 닭볶음탕",
+ ingredients: ["닭고기", "감자", "당근", "양파", "고추장"],
+ steps: ["닭고기 손질 및 초벌", "양념장 만들기", "끓이기", "채소 넣고 마무리"],
+ tendencies: ["매콤칼칼", "저당"]
+ }
+];
+
+const AVAILABLE_TENDENCIES = ["매콤칼칼", "스트레스 해소", "저당", "담백", "건강식"];
+
+export const MenuInventory = () => {
+ const navigate = useNavigate();
+ const [selectedTendencies, setSelectedTendencies] = useState([]);
+ const [filteredRecipes, setFilteredRecipes] = useState([]);
+
+ // TODO: Backend Integration: Fetch user's ingredients and preferences for initial recommendation
+ useEffect(() => {
+ // Simulate fetching initial ingredient-based recommendations
+ // Replace with actual API call to get recommended recipes
+ setFilteredRecipes(MOCK_RECIPES.slice(0, 2)); // Show a couple of default recipes
+ }, []);
+
+ const toggleTendency = (tendency) => {
+ setSelectedTendencies(prev => {
+ const newTendencies = prev.includes(tendency)
+ ? prev.filter(t => t !== tendency)
+ : [...prev, tendency];
+
+ // TODO: Backend Integration: Fetch recipes based on newTendencies
+ const updatedRecipes = MOCK_RECIPES.filter(recipe =>
+ newTendencies.length === 0 || newTendencies.some(t => recipe.tendencies.includes(t))
+ );
+ setFilteredRecipes(updatedRecipes.slice(0, 2)); // Limit to 2 for main screen
+ return newTendencies;
+ });
+ };
+
+ return (
+
+
+
+
+
+
+ 더보기 →
+
+
+
+ {AVAILABLE_TENDENCIES.map((tendency) => (
+ toggleTendency(tendency)}
+ />
+ ))}
+
+
+
+ {filteredRecipes.map((recipe) => (
+
+
+
+
+
+ {recipe.ingredients.map((ingredient, index) => (
+
+ ))}
+
+
+
+
+ {recipe.steps.join('\n')}
+
+
+

+
+
+
+ ))}
+
+
+ );
+};
diff --git a/src/screens/Desktop/sections/MenuInventory/index.js b/src/screens/Desktop/sections/MenuInventory/index.js
new file mode 100644
index 0000000..be34bd4
--- /dev/null
+++ b/src/screens/Desktop/sections/MenuInventory/index.js
@@ -0,0 +1 @@
+export { MenuInventory } from "./MenuInventory";
diff --git a/src/screens/Desktop/sections/MenuInventory/style.css b/src/screens/Desktop/sections/MenuInventory/style.css
new file mode 100644
index 0000000..ef0c8fa
--- /dev/null
+++ b/src/screens/Desktop/sections/MenuInventory/style.css
@@ -0,0 +1,245 @@
+.menu-inventory {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ flex-direction: column;
+ gap: 10px;
+ justify-content: center;
+ padding: 25px 64px;
+ position: relative;
+ width: 100%;
+}
+
+.menu-inventory .menu-inventory-header-wrapper {
+ align-self: stretch;
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-end;
+ width: 100%;
+}
+
+.menu-inventory .menu-inventory-header-link {
+ text-decoration: none;
+ color: inherit;
+}
+
+.menu-inventory .design-component-instance-node-2 {
+ flex: 0 0 auto !important;
+ left: unset !important;
+ top: unset !important;
+}
+
+.menu-inventory .menu-more-btn {
+ padding: 8px 20px;
+ background-color: #f6910b;
+ border-radius: 12px;
+ color: #ffffff;
+ cursor: pointer;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 600;
+ text-decoration: none;
+ transition: all 0.2s;
+ display: flex;
+ align-items: center;
+ gap: 5px;
+}
+
+.menu-inventory .menu-more-btn:hover {
+ background-color: #e58209;
+ transform: translateX(3px);
+}
+
+.menu-inventory .tendency-inventory {
+ align-items: center;
+ display: inline-flex;
+ flex: 0 0 auto;
+ gap: 8px;
+ position: relative;
+}
+
+.menu-inventory .tendency-instance {
+ margin-left: -14.50px !important;
+}
+
+.menu-inventory .tendency-2 {
+ margin-left: unset !important;
+}
+
+.menu-inventory .div-6 {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 32px;
+ overflow-x: scroll;
+ position: relative;
+ width: 100%;
+}
+
+.menu-inventory .div-6::-webkit-scrollbar {
+ display: none;
+ width: 0;
+}
+
+.menu-inventory .recipe {
+ align-items: flex-start;
+ background-color: #ffffff;
+ border: 1px solid;
+ border-color: #0000001a;
+ border-radius: 16px;
+ box-shadow: 0px 6px 12px #00000008, 0px 4px 8px #00000005;
+ display: flex;
+ flex-direction: column;
+ height: 140px;
+ justify-content: center;
+ max-width: 240px;
+ overflow: hidden;
+ padding: 8px 10px;
+ position: relative;
+ width: 240px;
+}
+
+.menu-inventory .recipe-text {
+ align-items: flex-end;
+ align-self: stretch;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ flex-grow: 1;
+ gap: 2px;
+ padding: 3px;
+ position: relative;
+ width: 100%;
+}
+
+.menu-inventory .recipe-name {
+ align-items: center;
+ align-self: stretch;
+ border-radius: 15px;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ justify-content: center;
+ overflow: hidden;
+ padding: 0px 5px;
+ position: relative;
+ width: 100%;
+}
+
+.menu-inventory .recipe-name-text {
+ align-items: center;
+ color: #000000e6;
+ display: flex;
+ flex: 1;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 500;
+ justify-content: center;
+ letter-spacing: -0.08px;
+ line-height: 22.4px;
+ margin-top: -1.00px;
+ position: relative;
+}
+
+.menu-inventory .use-ingredient {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 2px;
+ overflow: hidden;
+ overflow-x: scroll;
+ padding: 2px 4px;
+ position: relative;
+ width: 100%;
+}
+
+.menu-inventory .use-ingredient::-webkit-scrollbar {
+ display: none;
+ width: 0;
+}
+
+.menu-inventory .used-ingredient-instance {
+ align-self: stretch !important;
+ flex: 0 0 auto !important;
+ height: unset !important;
+ left: unset !important;
+ top: unset !important;
+}
+
+.menu-inventory .recipe-container {
+ align-items: center;
+ align-self: stretch;
+ background-color: #0000001a;
+ border-radius: 16px;
+ display: flex;
+ flex: 1;
+ flex-grow: 1;
+ gap: 5px;
+ justify-content: center;
+ padding: 5px 10px;
+ position: relative;
+ width: 100%;
+}
+
+.menu-inventory .recipe-text-2 {
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 4;
+ align-items: center;
+ color: #0000008c;
+ display: -webkit-box;
+ flex: 1;
+ font-family: "Inter", Helvetica;
+ font-size: 11px;
+ font-weight: 500;
+ justify-content: center;
+ letter-spacing: -0.06px;
+ line-height: 15.4px;
+ margin-bottom: -1.50px;
+ margin-top: -3.50px;
+ overflow: hidden;
+ position: relative;
+ text-overflow: ellipsis;
+}
+
+.menu-inventory .recipe-extension {
+ height: 24px;
+ position: relative;
+ width: 24px;
+}
+
+.menu-inventory .recipe-container-2 {
+ align-items: center;
+ align-self: stretch;
+ background-color: #0000001a;
+ border-radius: 16px;
+ display: flex;
+ flex: 1;
+ flex-grow: 1;
+ gap: 5px;
+ justify-content: center;
+ padding: 3px 10px;
+ position: relative;
+ width: 100%;
+}
+
+.menu-inventory .text-wrapper-7 {
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 4;
+ align-items: center;
+ color: #0000008c;
+ display: -webkit-box;
+ flex: 1;
+ font-family: "Inter", Helvetica;
+ font-size: 11px;
+ font-weight: 500;
+ justify-content: center;
+ letter-spacing: -0.06px;
+ line-height: 15.4px;
+ margin-top: -1.50px;
+ overflow: hidden;
+ position: relative;
+ text-overflow: ellipsis;
+}
diff --git a/src/screens/Desktop/style.css b/src/screens/Desktop/style.css
new file mode 100644
index 0000000..e790d15
--- /dev/null
+++ b/src/screens/Desktop/style.css
@@ -0,0 +1,59 @@
+.desktop {
+ align-items: center;
+ background-color: #ffffff;
+ display: flex;
+ flex-direction: column;
+ min-height: 1080px;
+ position: relative;
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+@media (max-width: 1280px) {
+ .desktop {
+ width: 100%;
+ }
+}
+
+@media (max-width: 768px) {
+ .desktop {
+ min-height: auto;
+ }
+}
+
+.desktop .site-header-name {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 48px;
+ justify-content: center;
+ padding: 20px 0px;
+ position: relative;
+ width: 100%;
+}
+
+.desktop .choose-your-receipt {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 36px;
+ font-weight: 700;
+ justify-content: center;
+ letter-spacing: -0.72px;
+ line-height: 43.2px;
+ margin: 0;
+ position: relative;
+ text-align: center;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+@media (max-width: 768px) {
+ .desktop .choose-your-receipt {
+ font-size: 24px;
+ line-height: 28.8px;
+ }
+}
diff --git a/src/screens/MenuRecommendationPage/MenuRecommendationPage.jsx b/src/screens/MenuRecommendationPage/MenuRecommendationPage.jsx
new file mode 100644
index 0000000..f8f657f
--- /dev/null
+++ b/src/screens/MenuRecommendationPage/MenuRecommendationPage.jsx
@@ -0,0 +1,305 @@
+import React, { useState, useRef } from "react";
+import { Link } from "react-router-dom";
+import { UnifiedHeader } from "../../components/UnifiedHeader";
+import "./style.css";
+
+const MOCK_RECIPES = [
+ // TODO: Backend Integration: Replace with API call to fetch all available recipes
+ {
+ id: 1,
+ name: "저당 닭갈비",
+ ingredients: ["닭고기", "양파", "고추장", "고춧가루", "양배추", "스테비아", "마늘"],
+ steps: ["닭고기 손질 및 양념 재우기", "야채 손질", "볶기"],
+ tendencies: ["매콤칼칼", "스트레스 해소", "저당"],
+ description: "건강한 저당 닭갈비 레시피",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 2,
+ name: "저당 닭볶음탕",
+ ingredients: ["닭고기", "감자", "당근", "양파", "고추장"],
+ steps: ["닭고기 손질 및 초벌", "양념장 만들기", "끓이기", "채소 넣고 마무리"],
+ tendencies: ["매콤칼칼", "저당"],
+ description: "매콤한 닭볶음탕",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 3,
+ name: "양파 볶음",
+ ingredients: ["양파", "간장", "참기름"],
+ steps: ["양파 썰기", "볶기", "간장으로 간하기"],
+ tendencies: ["담백", "건강식"],
+ description: "간단한 양파 볶음",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-1.png"
+ },
+ {
+ id: 4,
+ name: "된장찌개",
+ ingredients: ["된장", "두부", "양파", "감자", "호박"],
+ steps: ["재료 손질", "육수 내기", "된장 풀기", "끓이기"],
+ tendencies: ["담백", "건강식"],
+ description: "구수한 된장찌개",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 5,
+ name: "김치찌개",
+ ingredients: ["김치", "돼지고기", "두부", "양파"],
+ steps: ["돼지고기 볶기", "김치 넣고 볶기", "물 넣고 끓이기"],
+ tendencies: ["매콤칼칼", "스트레스 해소"],
+ description: "얼큰한 김치찌개",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 6,
+ name: "불고기",
+ ingredients: ["소고기", "양파", "당근", "간장", "설탕"],
+ steps: ["고기 재우기", "야채 썰기", "볶기"],
+ tendencies: ["담백"],
+ description: "달콤한 불고기",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 7,
+ name: "제육볶음",
+ ingredients: ["돼지고기", "양파", "고추장", "고춧가루"],
+ steps: ["고기 양념하기", "볶기"],
+ tendencies: ["매콤칼칼", "스트레스 해소"],
+ description: "매콤한 제육볶음",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 8,
+ name: "계란찜",
+ ingredients: ["계란", "물", "소금"],
+ steps: ["계란 풀기", "물 섞기", "찌기"],
+ tendencies: ["담백", "건강식"],
+ description: "부드러운 계란찜",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 9,
+ name: "비빔밥",
+ ingredients: ["밥", "시금치", "당근", "고사리", "고추장"],
+ steps: ["나물 볶기", "밥 위에 올리기", "비비기"],
+ tendencies: ["건강식"],
+ description: "영양 만점 비빔밥",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 10,
+ name: "된장국",
+ ingredients: ["된장", "두부", "파", "감자"],
+ steps: ["육수 내기", "된장 풀기", "재료 넣고 끓이기"],
+ tendencies: ["담백", "건강식"],
+ description: "구수한 된장국",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 11,
+ name: "삼겹살 구이",
+ ingredients: ["삼겹살", "소금", "후추"],
+ steps: ["삼겹살 굽기", "간하기"],
+ tendencies: ["스트레스 해소"],
+ description: "고소한 삼겹살",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 12,
+ name: "잡채",
+ ingredients: ["당면", "시금치", "당근", "양파", "간장"],
+ steps: ["당면 삶기", "야채 볶기", "섞기"],
+ tendencies: ["담백"],
+ description: "맛있는 잡채",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 13,
+ name: "순두부찌개",
+ ingredients: ["순두부", "고추장", "계란", "파"],
+ steps: ["육수 내기", "순두부 넣기", "끓이기"],
+ tendencies: ["매콤칼칼", "건강식"],
+ description: "얼큰한 순두부찌개",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 14,
+ name: "감자조림",
+ ingredients: ["감자", "간장", "설탕", "물엿"],
+ steps: ["감자 썰기", "조리기", "졸이기"],
+ tendencies: ["담백"],
+ description: "달콤한 감자조림",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 15,
+ name: "떡볶이",
+ ingredients: ["떡", "고추장", "어묵", "파"],
+ steps: ["육수 내기", "고추장 풀기", "떡 넣고 끓이기"],
+ tendencies: ["매콤칼칼", "스트레스 해소"],
+ description: "매콤달콤 떡볶이",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+];
+
+const RECIPES_PER_PAGE = 12;
+
+export const MenuRecommendationPage = () => {
+ const [searchQuery, setSearchQuery] = useState("");
+ const [recipes, setRecipes] = useState([]);
+ const [currentPage, setCurrentPage] = useState(0);
+ const [recommendationType, setRecommendationType] = useState(null);
+ const scrollContainerRef = useRef(null);
+
+ const handleIngredientBasedRecommendation = async () => {
+ // TODO: Backend Integration: Replace with API call to get ingredient-based recommendations
+ // Example: const response = await axios.get('/api/recommend/ingredients');
+ // setRecipes(response.data);
+ setRecommendationType("ingredient");
+ setRecipes(MOCK_RECIPES); // Using mock data for now
+ setCurrentPage(0);
+ };
+
+ const handleTendencyBasedRecommendation = async () => {
+ // TODO: Backend Integration: Replace with API call to get tendency-based recommendations
+ // Example: const response = await axios.get('/api/recommend/tendencies');
+ // setRecipes(response.data);
+ setRecommendationType("tendency");
+ setRecipes(MOCK_RECIPES); // Using mock data for now
+ setCurrentPage(0);
+ };
+
+ const handleGeminiSearch = async () => {
+ if (!searchQuery.trim()) return;
+
+ // TODO: Backend Integration: Integrate with Gemini API via your backend
+ // Example: const response = await axios.post('/api/gemini-search', { query: searchQuery });
+ // setRecipes(response.data);
+ console.log("Searching with Gemini:", searchQuery);
+ alert("Gemini API 연동 예정입니다.");
+
+ // Mock search results
+ const filtered = MOCK_RECIPES.filter(recipe =>
+ recipe.name.includes(searchQuery) ||
+ recipe.ingredients.some(ing => ing.includes(searchQuery))
+ );
+ setRecipes(filtered.length > 0 ? filtered : MOCK_RECIPES);
+ setRecommendationType("search");
+ setCurrentPage(0);
+ };
+
+ const totalPages = Math.ceil(recipes.length / RECIPES_PER_PAGE);
+ const displayedRecipes = recipes.slice(
+ currentPage * RECIPES_PER_PAGE,
+ (currentPage + 1) * RECIPES_PER_PAGE
+ );
+
+ const handleScroll = (direction) => {
+ if (direction === "left" && currentPage > 0) {
+ setCurrentPage(currentPage - 1);
+ } else if (direction === "right" && currentPage < totalPages - 1) {
+ setCurrentPage(currentPage + 1);
+ }
+ };
+
+ return (
+
+
+
+
+
+
메뉴 추천
+
AI가 추천하는 맞춤 레시피를 찾아보세요
+
+
+
+ setSearchQuery(e.target.value)}
+ onKeyPress={(e) => e.key === 'Enter' && handleGeminiSearch()}
+ />
+
+
+
+
+
+
+
+
+ {recipes.length > 0 && (
+
+
+
+
+ {currentPage + 1} / {totalPages}
+
+
+
+
+
+ {displayedRecipes.map((recipe) => (
+
+

+
+
{recipe.name}
+
{recipe.description}
+
+ {recipe.tendencies.slice(0, 2).map((tendency, index) => (
+
+ {tendency}
+
+ ))}
+
+
+
+ ))}
+
+
+ )}
+
+ {recipes.length === 0 && recommendationType && (
+
+
추천할 레시피가 없습니다.
+
다른 조건으로 검색해보세요.
+
+ )}
+
+
+ );
+};
diff --git a/src/screens/MenuRecommendationPage/index.js b/src/screens/MenuRecommendationPage/index.js
new file mode 100644
index 0000000..13935ed
--- /dev/null
+++ b/src/screens/MenuRecommendationPage/index.js
@@ -0,0 +1 @@
+export { MenuRecommendationPage } from "./MenuRecommendationPage";
diff --git a/src/screens/MenuRecommendationPage/style.css b/src/screens/MenuRecommendationPage/style.css
new file mode 100644
index 0000000..6fec0be
--- /dev/null
+++ b/src/screens/MenuRecommendationPage/style.css
@@ -0,0 +1,289 @@
+.menu-recommendation-page {
+ align-items: center;
+ background-color: #ffffff;
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ position: relative;
+ width: 100%;
+}
+
+.menu-recommendation-content {
+ align-items: flex-start;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ gap: 30px;
+ padding: 20px 64px;
+ position: relative;
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+.menu-recommendation-header {
+ align-items: flex-start;
+ align-self: stretch;
+ border-bottom: 1px solid #000000;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ padding-bottom: 20px;
+ width: 100%;
+}
+
+.menu-recommendation-title {
+ color: #000000;
+ font-family: "Inter", Helvetica;
+ font-size: 48px;
+ font-weight: 700;
+ letter-spacing: -0.96px;
+ line-height: 57.6px;
+ margin: 0;
+}
+
+.menu-recommendation-subtitle {
+ color: #0000008c;
+ font-family: "Inter", Helvetica;
+ font-size: 20px;
+ font-weight: 500;
+ letter-spacing: -0.10px;
+ line-height: 29.0px;
+ margin: 0;
+}
+
+.menu-search-section {
+ align-self: stretch;
+ display: flex;
+ gap: 10px;
+ width: 100%;
+}
+
+.menu-search-input {
+ flex: 1;
+ padding: 14px 20px;
+ border: 2px solid #e0e0e0;
+ border-radius: 12px;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ transition: border-color 0.2s;
+}
+
+.menu-search-input:focus {
+ outline: none;
+ border-color: #f6910b;
+}
+
+.menu-search-btn {
+ padding: 14px 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;
+}
+
+.menu-search-btn:hover {
+ background-color: #e58209;
+}
+
+.menu-recommendation-buttons {
+ align-self: stretch;
+ display: flex;
+ gap: 15px;
+ justify-content: center;
+}
+
+.menu-recommendation-btn {
+ padding: 14px 30px;
+ background-color: #e0e0e0;
+ border: 2px solid transparent;
+ border-radius: 12px;
+ color: #666666;
+ cursor: pointer;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 700;
+ transition: all 0.2s;
+}
+
+.menu-recommendation-btn:hover {
+ background-color: #d0d0d0;
+}
+
+.menu-recommendation-btn.active {
+ background-color: #f6910b;
+ border-color: #f6910b;
+ color: #ffffff;
+}
+
+.menu-recipes-section {
+ align-self: stretch;
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ width: 100%;
+}
+
+.menu-recipes-navigation {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 20px;
+}
+
+.menu-scroll-btn {
+ width: 40px;
+ height: 40px;
+ background-color: #f6910b;
+ border: none;
+ border-radius: 50%;
+ color: #ffffff;
+ cursor: pointer;
+ font-size: 20px;
+ font-weight: 700;
+ transition: all 0.2s;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.menu-scroll-btn:hover:not(:disabled) {
+ background-color: #e58209;
+ transform: scale(1.1);
+}
+
+.menu-scroll-btn:disabled {
+ background-color: #cccccc;
+ cursor: not-allowed;
+ opacity: 0.5;
+}
+
+.menu-page-indicator {
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 600;
+ color: #000000;
+}
+
+.menu-recipes-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
+ gap: 20px;
+ width: 100%;
+ transition: transform 0.3s ease-in-out;
+}
+
+.menu-recipe-card {
+ background-color: #ffffff;
+ border: 1px solid #e0e0e0;
+ border-radius: 16px;
+ overflow: hidden;
+ cursor: pointer;
+ transition: all 0.2s;
+ text-decoration: none;
+ display: flex;
+ flex-direction: column;
+ box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.05);
+}
+
+.menu-recipe-card:hover {
+ transform: translateY(-4px);
+ box-shadow: 0px 8px 16px rgba(0, 0, 0, 0.1);
+ border-color: #f6910b;
+}
+
+.menu-recipe-card-image {
+ width: 100%;
+ height: 180px;
+ object-fit: cover;
+}
+
+.menu-recipe-card-content {
+ padding: 15px;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.menu-recipe-card-title {
+ font-family: "Inter", Helvetica;
+ font-size: 18px;
+ font-weight: 700;
+ color: #000000;
+ margin: 0;
+}
+
+.menu-recipe-card-description {
+ font-family: "Inter", Helvetica;
+ font-size: 14px;
+ font-weight: 500;
+ color: #666666;
+ margin: 0;
+ line-height: 1.5;
+}
+
+.menu-recipe-card-tags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+}
+
+.menu-recipe-card-tag {
+ padding: 4px 10px;
+ background-color: #f6910b1a;
+ border-radius: 12px;
+ color: #f6910b;
+ font-family: "Inter", Helvetica;
+ font-size: 12px;
+ font-weight: 600;
+}
+
+.menu-empty-state {
+ align-self: stretch;
+ text-align: center;
+ padding: 60px 40px;
+ background-color: #f5f5f5;
+ border-radius: 20px;
+}
+
+.menu-empty-state p {
+ font-family: "Inter", Helvetica;
+ font-size: 18px;
+ font-weight: 500;
+ color: #666666;
+ margin: 10px 0;
+}
+
+@media (max-width: 768px) {
+ .menu-recommendation-content {
+ padding: 20px 20px;
+ }
+
+ .menu-recommendation-title {
+ font-size: 32px;
+ line-height: 38.4px;
+ }
+
+ .menu-recommendation-subtitle {
+ font-size: 16px;
+ line-height: 23.2px;
+ }
+
+ .menu-recipes-grid {
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+ gap: 15px;
+ }
+
+ .menu-recommendation-buttons {
+ flex-direction: column;
+ }
+
+ .menu-recommendation-btn {
+ width: 100%;
+ }
+}
diff --git a/src/screens/MyPage/MyPage.jsx b/src/screens/MyPage/MyPage.jsx
new file mode 100644
index 0000000..2fa8455
--- /dev/null
+++ b/src/screens/MyPage/MyPage.jsx
@@ -0,0 +1,224 @@
+import React, { useState, useEffect } from "react";
+import { useNavigate, useLocation } from "react-router-dom";
+import { useAuth } from "../../contexts/AuthContext";
+import { UnifiedHeader } from "../../components/UnifiedHeader";
+import { LoginPopup } from "../../components/LoginPopup";
+import { SignupPopup } from "../../components/SignupPopup";
+import { PreferencesPopup } from "../../components/PreferencesPopup";
+import { MypageTendency } from "../../components/MypageTendency";
+import "./style.css";
+
+const HEALTH_GOALS = [
+ { id: 1, name: "체중 감량" },
+ { id: 2, name: "근육 증가" },
+ { id: 3, name: "건강 유지" },
+ { id: 4, name: "면역력 강화" },
+ { id: 5, name: "소화 개선" },
+];
+
+const ALLERGIES = [
+ { id: 1, name: "우유" },
+ { id: 2, name: "계란" },
+ { id: 3, name: "땅콩" },
+ { id: 4, name: "견과류" },
+ { id: 5, name: "갑각류" },
+ { id: 6, name: "밀" },
+ { id: 7, name: "대두" },
+];
+
+export const MyPage = () => {
+ const { user, isAuthenticated, logout } = useAuth();
+ const navigate = useNavigate();
+ const location = useLocation();
+ const [preferences, setPreferences] = useState(null);
+ const [showLoginPopup, setShowLoginPopup] = useState(false);
+ const [showSignupPopup, setShowSignupPopup] = useState(false);
+ const [showPreferencesPopup, setShowPreferencesPopup] = useState(false);
+ const [userData, setUserData] = useState(null);
+
+ useEffect(() => {
+ if (isAuthenticated && user) {
+ // TODO: Backend Integration: Fetch user preferences from backend using user.username
+ // Example: axios.get(`/api/users/${user.username}/preferences`).then(response => setPreferences(response.data));
+ const stored = localStorage.getItem("userPreferences");
+ if (stored) {
+ setPreferences(JSON.parse(stored));
+ }
+ }
+ }, [isAuthenticated, user]);
+
+ useEffect(() => {
+ // Check if we should show login popup from navigation state
+ if (location.state?.showLogin) {
+ setShowLoginPopup(true);
+ // Clear the state
+ navigate(location.pathname, { replace: true, state: {} });
+ }
+ }, [location, navigate]);
+
+ const handleLoginClick = () => {
+ setShowLoginPopup(true);
+ };
+
+ 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);
+ };
+
+ const getHealthGoalNames = () => {
+ if (!preferences?.healthGoalIds) return [];
+ return preferences.healthGoalIds.map(
+ (id) => HEALTH_GOALS.find((goal) => goal.id === id)?.name || ""
+ );
+ };
+
+ const getAllergyNames = () => {
+ if (!preferences?.allergyIds) return [];
+ return preferences.allergyIds.map(
+ (id) => ALLERGIES.find((allergy) => allergy.id === id)?.name || ""
+ );
+ };
+
+ return (
+
+
+
+
+
+
+ {!isAuthenticated ? (
+
+
로그인이 필요합니다
+
마이페이지를 이용하시려면 로그인해주세요.
+
+
+ ) : preferences ? (
+ <>
+
+
+
+ {getHealthGoalNames().map((goal, index) => (
+
+ ))}
+
+
+
+
+
+
+ {getAllergyNames().length > 0 ? (
+ getAllergyNames().map((allergy, index) => (
+
+ ))
+ ) : (
+
없음
+ )}
+
+
+
+
+
+ 싫어하는 재료:
+ {preferences.dislikedIngredients || "없음"}
+
+
+ 선호하는 재료:
+ {preferences.preferredIngredients || "없음"}
+
+
+ 선호 요리:
+ {preferences.preferredCuisine}
+
+
+ 매운맛 선호도:
+ {preferences.spiceLevel}
+
+
+
+
+
+
+
+ 소비 알림
+
+ {preferences.allowPushConsumption ? "ON" : "OFF"}
+
+
+
+ 댓글 알림
+
+ {preferences.allowPushComment ? "ON" : "OFF"}
+
+
+
+ 넛지 알림
+
+ {preferences.allowPushNudge ? "ON" : "OFF"}
+
+
+
+
+ >
+ ) : (
+
+
저장된 성향 정보가 없습니다.
+
회원가입을 통해 성향을 설정해주세요.
+
+ )}
+
+
+ {showLoginPopup && (
+
+ )}
+
+ {showSignupPopup && (
+
+ )}
+
+ {showPreferencesPopup && (
+
+ )}
+
+ );
+};
diff --git a/src/screens/MyPage/index.js b/src/screens/MyPage/index.js
new file mode 100644
index 0000000..b2f5896
--- /dev/null
+++ b/src/screens/MyPage/index.js
@@ -0,0 +1 @@
+export { MyPage } from "./MyPage";
diff --git a/src/screens/MyPage/style.css b/src/screens/MyPage/style.css
new file mode 100644
index 0000000..4524930
--- /dev/null
+++ b/src/screens/MyPage/style.css
@@ -0,0 +1,420 @@
+.my-page {
+ align-items: center;
+ background-color: #ffffff;
+ display: flex;
+ flex-direction: column;
+ min-height: 1080px;
+ position: relative;
+ width: 100%;
+}
+
+.my-page .mypage-header {
+ align-items: center;
+ display: flex;
+ gap: 10px;
+ height: 120px;
+ padding: 10px;
+ position: relative;
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+.my-page .none-frame-3 {
+ flex: 1;
+ flex-grow: 1;
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ height: 100%;
+ position: relative;
+}
+
+.my-page .algorithm-label-3 {
+ 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: 271px;
+ height: 100%;
+}
+
+.my-page .text-wrapper-12 {
+ color: #000000;
+ letter-spacing: -0.46px;
+}
+
+.my-page .text-wrapper-13 {
+ color: #f6910b;
+ letter-spacing: -0.46px;
+}
+
+.my-page .user-name-container-wrapper {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 1;
+ flex-grow: 1;
+ gap: 10px;
+ justify-content: flex-end;
+ padding: 10px;
+ position: relative;
+}
+
+.my-page .user-name-container {
+ align-items: flex-start;
+ background-color: #0000001a;
+ border-radius: 16px;
+ display: inline-flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ padding: 6px 20px;
+ position: relative;
+}
+
+.my-page .user-name-text {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 12px;
+ font-weight: 700;
+ justify-content: center;
+ letter-spacing: -0.24px;
+ line-height: 14.4px;
+ margin-top: -1.00px;
+ position: relative;
+ text-align: center;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+.my-page .mypage-nav {
+ align-items: center;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 48px;
+ justify-content: center;
+ padding: 5px 64px;
+ position: relative;
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+.my-page .mypage-textcontainer {
+ align-items: center;
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ border-color: #000000;
+ display: flex;
+ flex: 1;
+ flex-grow: 1;
+ gap: 10px;
+ justify-content: center;
+ padding: 20px 64px;
+ position: relative;
+}
+
+.my-page .mypage-text {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ flex: 1;
+ font-family: "Inter", Helvetica;
+ font-size: 36px;
+ font-weight: 700;
+ justify-content: center;
+ letter-spacing: -0.72px;
+ line-height: 43.2px;
+ margin-top: -1.00px;
+ position: relative;
+ text-align: center;
+}
+
+.my-page .mypage-section {
+ align-items: center;
+ display: flex;
+ flex: 0 0 auto;
+ flex-direction: column;
+ gap: 30px;
+ padding: 0px 64px;
+ position: relative;
+ width: 100%;
+ max-width: 900px;
+ margin: 0 auto;
+}
+
+.my-page .mypage {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ min-height: auto;
+ padding: 10px 20px;
+ position: relative;
+ width: 100%;
+}
+
+.my-page .mypage-wrapper {
+ align-items: flex-start;
+ align-self: stretch;
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ border-color: #000000;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ justify-content: flex-start;
+ padding: 10px;
+ position: relative;
+ width: 100%;
+}
+
+.my-page .mypage-2 {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 36px;
+ font-weight: 700;
+ justify-content: flex-start;
+ letter-spacing: -0.72px;
+ line-height: 43.2px;
+ margin: 0;
+ position: relative;
+ text-align: left;
+ white-space: nowrap;
+}
+
+.my-page .mypage-3 {
+ background-color: #0000000d;
+ border-radius: 15px;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ justify-content: center;
+ min-height: auto;
+ overflow: visible;
+ padding: 20px;
+ position: relative;
+ width: 100%;
+}
+
+
+.my-page .mypage-info-section {
+ align-self: stretch;
+ background-color: #0000000d;
+ border-radius: 15px;
+ display: flex;
+ flex-direction: column;
+ gap: 15px;
+ padding: 20px;
+ width: 100%;
+}
+
+.my-page .mypage-info-item {
+ display: flex;
+ gap: 10px;
+ align-items: center;
+}
+
+.my-page .mypage-info-label {
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 700;
+ color: #000000;
+ min-width: 150px;
+}
+
+.my-page .mypage-info-value {
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 500;
+ color: #666666;
+}
+
+.my-page .mypage-notification-section {
+ align-self: stretch;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ width: 100%;
+}
+
+.my-page .mypage-notification-list {
+ background-color: #0000000d;
+ border-radius: 15px;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ padding: 20px;
+}
+
+.my-page .mypage-notification-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 500;
+ color: #000000;
+}
+
+.my-page .mypage-notification-item .active {
+ color: #f6910b;
+ font-weight: 700;
+}
+
+.my-page .mypage-notification-item .inactive {
+ color: #999999;
+ font-weight: 500;
+}
+
+.my-page .mypage-empty {
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 500;
+ color: #666666;
+ padding: 20px;
+ text-align: center;
+}
+
+.my-page .mypage-empty-state {
+ text-align: center;
+ padding: 40px;
+ color: #666666;
+ font-family: "Inter", Helvetica;
+ font-size: 18px;
+ font-weight: 500;
+}
+
+.my-page .mypage-empty-state p {
+ margin: 10px 0;
+}
+
+.my-page .mypage-login-required {
+ text-align: center;
+ padding: 60px 40px;
+ background-color: #f5f5f5;
+ border-radius: 20px;
+ width: 100%;
+}
+
+.my-page .mypage-login-title {
+ font-family: "Inter", Helvetica;
+ font-size: 32px;
+ font-weight: 700;
+ color: #000000;
+ margin: 0 0 20px 0;
+}
+
+.my-page .mypage-login-text {
+ font-family: "Inter", Helvetica;
+ font-size: 18px;
+ font-weight: 500;
+ color: #666666;
+ margin: 0 0 30px 0;
+}
+
+.my-page .mypage-login-btn {
+ padding: 14px 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;
+}
+
+.my-page .mypage-login-btn:hover {
+ background-color: #e58209;
+}
+
+.my-page .user-info-wrapper {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.my-page .user-name-display {
+ color: #000000;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 600;
+ padding: 8px 16px;
+ background-color: #f6910b1a;
+ border-radius: 20px;
+}
+
+.my-page .logout-button {
+ 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: 14px;
+ font-weight: 600;
+}
+
+.my-page .logout-button:hover {
+ transform: scale(1.05);
+}
+
+.my-page .login-button {
+ 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: 14px;
+ font-weight: 600;
+}
+
+.my-page .login-button:hover {
+ transform: scale(1.05);
+}
+
+@media (max-width: 768px) {
+ .my-page .mypage-section {
+ padding: 0px 20px;
+ }
+
+ .my-page .mypage-2 {
+ font-size: 28px;
+ line-height: 33.6px;
+ }
+
+ .my-page .mypage-text {
+ font-size: 28px;
+ line-height: 33.6px;
+ }
+
+ .my-page .algorithm-label-3 {
+ font-size: 32px;
+ }
+}
diff --git a/src/screens/RecipePage/RecipePage.jsx b/src/screens/RecipePage/RecipePage.jsx
new file mode 100644
index 0000000..2304d31
--- /dev/null
+++ b/src/screens/RecipePage/RecipePage.jsx
@@ -0,0 +1,77 @@
+import React from "react";
+import { useLocation, useParams } from "react-router-dom";
+import { UnifiedHeader } from "../../components/UnifiedHeader";
+import { Recipepage } from "../../components/Recipepage";
+import "./style.css";
+
+export const RecipePage = () => {
+ const location = useLocation();
+ const { id } = useParams();
+ const recipe = location.state?.recipe || {
+ id: id,
+ name: "레시피 이름",
+ ingredients: ["닭고기", "양파", "고추장"],
+ steps: ["재료 준비", "조리 시작", "완성"],
+ description: "레시피 설명이 여기에 표시됩니다.",
+ tendencies: ["매콤칼칼"]
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ {recipe.ingredients.map((ingredient, index) => (
+
+ ))}
+
+
+
+
+
+
+
+
+
+
{recipe.description || "레시피 설명이 여기에 표시됩니다."}
+
+
+
+
+
+
+
+
+ {recipe.steps.map((step, index) => (
+
+ {index + 1}.
+ {step}
+
+ ))}
+
+
+
+
+
+ );
+};
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..56cc608
--- /dev/null
+++ b/src/screens/RecipePage/style.css
@@ -0,0 +1,412 @@
+.recipe-page {
+ align-items: center;
+ background-color: #ffffff;
+ display: flex;
+ flex-direction: column;
+ min-height: 1080px;
+ position: relative;
+ width: 100%;
+}
+
+.recipe-page .recipepage-header {
+ align-items: center;
+ display: flex;
+ gap: 10px;
+ height: 120px;
+ padding: 10px;
+ position: relative;
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+.recipe-page .none-frame-4 {
+ flex: 1;
+ flex-grow: 1;
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ height: 100%;
+ position: relative;
+}
+
+.recipe-page .algorithm-label-4 {
+ 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: 271px;
+ height: 100%;
+}
+
+.recipe-page .text-wrapper-14 {
+ color: #000000;
+ letter-spacing: -0.46px;
+}
+
+.recipe-page .text-wrapper-15 {
+ color: #f6910b;
+ letter-spacing: -0.46px;
+}
+
+.recipe-page .user-menu-frame-3 {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 1;
+ flex-grow: 1;
+ gap: 10px;
+ justify-content: flex-end;
+ padding: 10px;
+ position: relative;
+}
+
+.recipe-page .user-name-text-wrapper {
+ align-items: flex-start;
+ background-color: #0000001a;
+ border-radius: 16px;
+ display: inline-flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ padding: 6px 20px;
+ position: relative;
+}
+
+.recipe-page .user-name-text-2 {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 12px;
+ font-weight: 700;
+ justify-content: center;
+ letter-spacing: -0.24px;
+ line-height: 14.4px;
+ margin-top: -1.00px;
+ position: relative;
+ text-align: center;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+.recipe-page .recipepage-nav {
+ align-items: center;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 48px;
+ justify-content: center;
+ padding: 5px 64px;
+ position: relative;
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+.recipe-page .recipepage-text-wrapper {
+ align-items: center;
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ border-color: #000000;
+ display: flex;
+ flex: 1;
+ flex-grow: 1;
+ gap: 10px;
+ justify-content: center;
+ padding: 20px 64px;
+ position: relative;
+}
+
+.recipe-page .recipepage-text {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ flex: 1;
+ font-family: "Inter", Helvetica;
+ font-size: 36px;
+ font-weight: 700;
+ justify-content: center;
+ letter-spacing: -0.72px;
+ line-height: 43.2px;
+ margin-top: -1.00px;
+ position: relative;
+ text-align: center;
+}
+
+.recipe-page .recipepage-section {
+ align-items: center;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ flex-grow: 1;
+ gap: 30px;
+ padding: 0px 64px 20px;
+ position: relative;
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+.recipe-page .recipepage-content {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ flex-grow: 1;
+ gap: 10px;
+ padding: 10px 130px;
+ position: relative;
+ width: 100%;
+}
+
+.recipe-page .recipepage-wrapper {
+ align-items: flex-start;
+ align-self: stretch;
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ border-color: #000000;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ padding: 10px;
+ position: relative;
+ width: 100%;
+}
+
+.recipe-page .recipepage-2 {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 48px;
+ font-weight: 700;
+ justify-content: center;
+ letter-spacing: -0.96px;
+ line-height: 57.6px;
+ margin-top: -1.00px;
+ position: relative;
+ text-align: center;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+.recipe-page .recipepage-3 {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ overflow: hidden;
+ overflow-x: scroll;
+ padding: 10px;
+ position: relative;
+ width: 100%;
+}
+
+.recipe-page .recipepage-3::-webkit-scrollbar {
+ display: none;
+ width: 0;
+}
+
+.recipe-page .recipepage-usedingredient {
+ flex: 0 0 auto !important;
+ left: unset !important;
+ top: unset !important;
+}
+
+.recipe-page .recipepage-4 {
+ align-items: flex-start;
+ align-self: stretch;
+ background-color: #0000000d;
+ border-radius: 15px;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 15px;
+ overflow: hidden;
+ padding: 15px;
+ position: relative;
+ width: 100%;
+}
+
+.recipe-page .recipepage-menuimage {
+ background-color: #ffffff;
+ border-radius: 16px;
+ height: 80px;
+ position: relative;
+ width: 80px;
+}
+
+.recipe-page .recipepage-5 {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ flex-grow: 1;
+ gap: 5px;
+ padding: 0px 5px;
+ position: relative;
+}
+
+.recipe-page .recipepage-6 {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ flex-direction: column;
+ gap: 10px;
+ position: relative;
+ width: 100%;
+}
+
+.recipe-page .recipepage-7 {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 24px;
+ font-weight: 700;
+ justify-content: center;
+ letter-spacing: -0.48px;
+ line-height: 28.8px;
+ margin-top: -1.00px;
+ position: relative;
+ text-align: center;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+.recipe-page .recipepage-8 {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ padding: 0px 10px;
+ position: relative;
+ width: 100%;
+}
+
+.recipe-page .recipepage-9 {
+ 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;
+ margin-top: -1.00px;
+ position: relative;
+}
+
+.recipe-page .recipepage-10 {
+ align-items: flex-start;
+ align-self: stretch;
+ background-color: #0000000d;
+ border-radius: 15px;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ flex-grow: 1;
+ gap: 15px;
+ overflow: hidden;
+ padding: 10px;
+ position: relative;
+ width: 100%;
+}
+
+.recipe-page .recipepage-11 {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ flex-direction: column;
+ gap: 10px;
+ padding: 0px 10px;
+ position: relative;
+ width: 100%;
+}
+
+.recipe-page .recipepage-12 {
+ align-items: center;
+ align-self: stretch;
+ color: #000000;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 24px;
+ font-weight: 700;
+ height: 29px;
+ justify-content: center;
+ letter-spacing: -0.48px;
+ line-height: 28.8px;
+ margin-top: -1.00px;
+ position: relative;
+ white-space: nowrap;
+}
+
+.recipe-page .recipepage-steps {
+ align-self: stretch;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ padding: 10px;
+ width: 100%;
+}
+
+.recipe-page .recipepage-step {
+ display: flex;
+ gap: 10px;
+ align-items: flex-start;
+}
+
+.recipe-page .recipepage-step-number {
+ color: #f6910b;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 700;
+ min-width: 30px;
+}
+
+.recipe-page .recipepage-step-text {
+ color: #000000;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 500;
+ line-height: 22.4px;
+ flex: 1;
+}
+
+@media (max-width: 768px) {
+ .recipe-page .recipepage-section {
+ padding: 0px 20px 20px;
+ }
+
+ .recipe-page .recipepage-content {
+ padding: 10px 20px;
+ }
+
+ .recipe-page .recipepage-2 {
+ font-size: 32px;
+ line-height: 38.4px;
+ }
+
+ .recipe-page .recipepage-text {
+ font-size: 28px;
+ line-height: 33.6px;
+ }
+
+ .recipe-page .algorithm-label-4 {
+ font-size: 32px;
+ }
+}
diff --git a/static/.gitignore b/static/.gitignore
new file mode 100644
index 0000000..a547bf3
--- /dev/null
+++ b/static/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/static/.screen-graph.json b/static/.screen-graph.json
new file mode 100644
index 0000000..333d5f2
--- /dev/null
+++ b/static/.screen-graph.json
@@ -0,0 +1 @@
+{"nodes":[{"id":"src/screens/Desktop/Desktop.jsx","label":"Desktop","routes":["/*","/desktop"],"type":"screen","dataModelId":"45:2","isRoot":true},{"id":"src/screens/CommunityPage/CommunityPage.jsx","label":"CommunityPage","routes":["/communitypage"],"type":"screen","dataModelId":"1:6"},{"id":"src/screens/MyPage/MyPage.jsx","label":"MyPage","routes":["/mypage"],"type":"screen","dataModelId":"168:346"},{"id":"src/screens/RecipePage/RecipePage.jsx","label":"RecipePage","routes":["/recipepage/:id"],"type":"screen","dataModelId":"199:457"},{"id":"src/screens/CommunityContent/CommunityContent.jsx","label":"CommunityContent","routes":["/communitycontentpage/:id"],"type":"screen","dataModelId":"107:399"},{"id":"src/screens/AddIngredientPage/AddIngredientPage.jsx","label":"AddIngredientPage","routes":["/addingredient"],"type":"screen"},{"id":"src/screens/CreatePostPage/CreatePostPage.jsx","label":"CreatePostPage","routes":["/createpost"],"type":"screen"},{"id":"src/screens/MenuRecommendationPage/MenuRecommendationPage.jsx","label":"MenuRecommendationPage","routes":["/menurecommendation"],"type":"screen"},{"id":"src/screens/CommunityPage/sections/Header/Header.jsx","label":"Header","routes":[],"type":"component"},{"id":"src/screens/CommunityPage/sections/Communitypage/Communitypage.jsx","label":"Communitypage","routes":[],"type":"component"},{"id":"src/screens/CommunityContent/sections/HeaderWrapper/HeaderWrapper.jsx","label":"HeaderWrapper","routes":[],"type":"component"},{"id":"src/screens/CommunityContent/sections/Communitycontentpage/Communitycontentpage.jsx","label":"Communitycontentpage","routes":[],"type":"component"},{"id":"src/screens/Desktop/sections/IngredientInventory/IngredientInventory.jsx","label":"IngredientInventory","routes":[],"type":"component"},{"id":"src/screens/Desktop/sections/MenuInventory/MenuInventory.jsx","label":"MenuInventory","routes":[],"type":"component"},{"id":"src/screens/Desktop/sections/Community/Community.jsx","label":"Community","routes":[],"type":"component"},{"id":"src/components/DivWrapper/DivWrapper.jsx","label":"DivWrapper","routes":[],"type":"component"},{"id":"src/components/UnifiedHeader/UnifiedHeader.jsx","label":"UnifiedHeader","routes":[],"type":"component"}],"edges":[{"id":"src/screens/AddIngredientPage/AddIngredientPage.jsx:69:4-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":69,"endLine":69,"column":4,"endColumn":24,"sourceFile":"src/screens/AddIngredientPage/AddIngredientPage.jsx"}}},{"id":"src/screens/AddIngredientPage/AddIngredientPage.jsx:133:12-to-src/screens/Desktop/Desktop.jsx","source":"src/screens/AddIngredientPage/AddIngredientPage.jsx","target":"src/screens/Desktop/Desktop.jsx","data":{"viaRoute":"/desktop","trigger":{"element":"\n 취소\n ","line":133,"endLine":135,"column":12,"endColumn":19,"sourceFile":"src/screens/AddIngredientPage/AddIngredientPage.jsx"}}},{"id":"src/components/UnifiedHeader/UnifiedHeader.jsx:53:14-to-src/screens/Desktop/Desktop.jsx","source":"src/screens/AddIngredientPage/AddIngredientPage.jsx","target":"src/screens/Desktop/Desktop.jsx","data":{"viaRoute":"/desktop","trigger":{"element":" setShowMenu(false)}>\n 홈\n ","line":53,"endLine":55,"column":14,"endColumn":21,"sourceFile":"src/components/UnifiedHeader/UnifiedHeader.jsx"}}},{"id":"src/components/UnifiedHeader/UnifiedHeader.jsx:56:14-to-src/screens/MenuRecommendationPage/MenuRecommendationPage.jsx","source":"src/screens/AddIngredientPage/AddIngredientPage.jsx","target":"src/screens/MenuRecommendationPage/MenuRecommendationPage.jsx","data":{"viaRoute":"/menurecommendation","trigger":{"element":" setShowMenu(false)}>\n 메뉴 추천\n ","line":56,"endLine":58,"column":14,"endColumn":21,"sourceFile":"src/components/UnifiedHeader/UnifiedHeader.jsx"}}},{"id":"src/components/UnifiedHeader/UnifiedHeader.jsx:59:14-to-src/screens/CommunityPage/CommunityPage.jsx","source":"src/screens/AddIngredientPage/AddIngredientPage.jsx","target":"src/screens/CommunityPage/CommunityPage.jsx","data":{"viaRoute":"/communitypage","trigger":{"element":" setShowMenu(false)}>\n 재료 나눔 게시판\n ","line":59,"endLine":61,"column":14,"endColumn":21,"sourceFile":"src/components/UnifiedHeader/UnifiedHeader.jsx"}}},{"id":"src/components/UnifiedHeader/UnifiedHeader.jsx:62:14-to-src/screens/MyPage/MyPage.jsx","source":"src/screens/AddIngredientPage/AddIngredientPage.jsx","target":"src/screens/MyPage/MyPage.jsx","data":{"viaRoute":"/mypage","trigger":{"element":" setShowMenu(false)}>\n 마이페이지\n ","line":62,"endLine":64,"column":14,"endColumn":21,"sourceFile":"src/components/UnifiedHeader/UnifiedHeader.jsx"}}},{"id":"src/components/UnifiedHeader/UnifiedHeader.jsx:67:10-to-src/screens/Desktop/Desktop.jsx","source":"src/screens/AddIngredientPage/AddIngredientPage.jsx","target":"src/screens/Desktop/Desktop.jsx","data":{"viaRoute":"/desktop","trigger":{"element":"\n
\n ","line":67,"endLine":73,"column":10,"endColumn":17,"sourceFile":"src/components/UnifiedHeader/UnifiedHeader.jsx"}}},{"id":"src/components/UnifiedHeader/UnifiedHeader.jsx:76:8-to-src/screens/Desktop/Desktop.jsx","source":"src/screens/AddIngredientPage/AddIngredientPage.jsx","target":"src/screens/Desktop/Desktop.jsx","data":{"viaRoute":"/desktop","trigger":{"element":"\n \n 알고리즘 \n 셰프\n
\n ","line":76,"endLine":81,"column":8,"endColumn":15,"sourceFile":"src/components/UnifiedHeader/UnifiedHeader.jsx"}}},{"id":"src/components/UnifiedHeader/UnifiedHeader.jsx:84:10-to-src/screens/MyPage/MyPage.jsx","source":"src/screens/AddIngredientPage/AddIngredientPage.jsx","target":"src/screens/MyPage/MyPage.jsx","data":{"viaRoute":"/mypage","trigger":{"element":"\n 마이페이지
\n ","line":84,"endLine":86,"column":10,"endColumn":17,"sourceFile":"src/components/UnifiedHeader/UnifiedHeader.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":"\n 취소\n ","line":88,"endLine":90,"column":12,"endColumn":19,"sourceFile":"src/screens/CreatePostPage/CreatePostPage.jsx"}}},{"id":"src/components/UnifiedHeader/UnifiedHeader.jsx:53:14-to-src/screens/Desktop/Desktop.jsx","source":"src/screens/CreatePostPage/CreatePostPage.jsx","target":"src/screens/Desktop/Desktop.jsx","data":{"viaRoute":"/desktop","trigger":{"element":" setShowMenu(false)}>\n 홈\n ","line":53,"endLine":55,"column":14,"endColumn":21,"sourceFile":"src/components/UnifiedHeader/UnifiedHeader.jsx"}}},{"id":"src/components/UnifiedHeader/UnifiedHeader.jsx:56:14-to-src/screens/MenuRecommendationPage/MenuRecommendationPage.jsx","source":"src/screens/CreatePostPage/CreatePostPage.jsx","target":"src/screens/MenuRecommendationPage/MenuRecommendationPage.jsx","data":{"viaRoute":"/menurecommendation","trigger":{"element":" setShowMenu(false)}>\n 메뉴 추천\n ","line":56,"endLine":58,"column":14,"endColumn":21,"sourceFile":"src/components/UnifiedHeader/UnifiedHeader.jsx"}}},{"id":"src/components/UnifiedHeader/UnifiedHeader.jsx:59:14-to-src/screens/CommunityPage/CommunityPage.jsx","source":"src/screens/CreatePostPage/CreatePostPage.jsx","target":"src/screens/CommunityPage/CommunityPage.jsx","data":{"viaRoute":"/communitypage","trigger":{"element":" setShowMenu(false)}>\n 재료 나눔 게시판\n ","line":59,"endLine":61,"column":14,"endColumn":21,"sourceFile":"src/components/UnifiedHeader/UnifiedHeader.jsx"}}},{"id":"src/components/UnifiedHeader/UnifiedHeader.jsx:62:14-to-src/screens/MyPage/MyPage.jsx","source":"src/screens/CreatePostPage/CreatePostPage.jsx","target":"src/screens/MyPage/MyPage.jsx","data":{"viaRoute":"/mypage","trigger":{"element":" setShowMenu(false)}>\n 마이페이지\n ","line":62,"endLine":64,"column":14,"endColumn":21,"sourceFile":"src/components/UnifiedHeader/UnifiedHeader.jsx"}}},{"id":"src/components/UnifiedHeader/UnifiedHeader.jsx:67:10-to-src/screens/Desktop/Desktop.jsx","source":"src/screens/CreatePostPage/CreatePostPage.jsx","target":"src/screens/Desktop/Desktop.jsx","data":{"viaRoute":"/desktop","trigger":{"element":"\n
\n ","line":67,"endLine":73,"column":10,"endColumn":17,"sourceFile":"src/components/UnifiedHeader/UnifiedHeader.jsx"}}},{"id":"src/components/UnifiedHeader/UnifiedHeader.jsx:76:8-to-src/screens/Desktop/Desktop.jsx","source":"src/screens/CreatePostPage/CreatePostPage.jsx","target":"src/screens/Desktop/Desktop.jsx","data":{"viaRoute":"/desktop","trigger":{"element":"\n \n 알고리즘 \n 셰프\n
\n ","line":76,"endLine":81,"column":8,"endColumn":15,"sourceFile":"src/components/UnifiedHeader/UnifiedHeader.jsx"}}},{"id":"src/components/UnifiedHeader/UnifiedHeader.jsx:84:10-to-src/screens/MyPage/MyPage.jsx","source":"src/screens/CreatePostPage/CreatePostPage.jsx","target":"src/screens/MyPage/MyPage.jsx","data":{"viaRoute":"/mypage","trigger":{"element":"\n 마이페이지
\n ","line":84,"endLine":86,"column":10,"endColumn":17,"sourceFile":"src/components/UnifiedHeader/UnifiedHeader.jsx"}}},{"id":"src/screens/CommunityPage/sections/Header/Header.jsx:43:8-to-src/screens/Desktop/Desktop.jsx","source":"src/screens/CommunityPage/sections/Header/Header.jsx","target":"src/screens/Desktop/Desktop.jsx","data":{"viaRoute":"/desktop","trigger":{"element":"\n
\n ","line":43,"endLine":49,"column":8,"endColumn":15,"sourceFile":"src/screens/CommunityPage/sections/Header/Header.jsx"}}},{"id":"src/screens/CommunityPage/sections/Header/Header.jsx:52:6-to-src/screens/Desktop/Desktop.jsx","source":"src/screens/CommunityPage/sections/Header/Header.jsx","target":"src/screens/Desktop/Desktop.jsx","data":{"viaRoute":"/desktop","trigger":{"element":"\n \n 알고리즘 \n\n 셰프\n
\n ","line":52,"endLine":58,"column":6,"endColumn":13,"sourceFile":"src/screens/CommunityPage/sections/Header/Header.jsx"}}},{"id":"src/screens/CommunityPage/sections/Header/Header.jsx:61:8-to-src/screens/MyPage/MyPage.jsx","source":"src/screens/CommunityPage/sections/Header/Header.jsx","target":"src/screens/MyPage/MyPage.jsx","data":{"viaRoute":"/mypage","trigger":{"element":"\n 마이페이지
\n ","line":61,"endLine":63,"column":8,"endColumn":15,"sourceFile":"src/screens/CommunityPage/sections/Header/Header.jsx"}}},{"id":"src/screens/CommunityPage/sections/Communitypage/Communitypage.jsx:54:4-to-src/screens/CreatePostPage/CreatePostPage.jsx","source":"src/screens/CommunityPage/sections/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/screens/CommunityContent/sections/HeaderWrapper/HeaderWrapper.jsx:24:8-to-src/screens/Desktop/Desktop.jsx","source":"src/screens/CommunityContent/sections/HeaderWrapper/HeaderWrapper.jsx","target":"src/screens/Desktop/Desktop.jsx","data":{"viaRoute":"/desktop","trigger":{"element":"\n
\n ","line":24,"endLine":30,"column":8,"endColumn":15,"sourceFile":"src/screens/CommunityContent/sections/HeaderWrapper/HeaderWrapper.jsx"}}},{"id":"src/screens/CommunityContent/sections/HeaderWrapper/HeaderWrapper.jsx:33:6-to-src/screens/Desktop/Desktop.jsx","source":"src/screens/CommunityContent/sections/HeaderWrapper/HeaderWrapper.jsx","target":"src/screens/Desktop/Desktop.jsx","data":{"viaRoute":"/desktop","trigger":{"element":"\n \n 알고리즘 \n\n 셰프\n
\n ","line":33,"endLine":39,"column":6,"endColumn":13,"sourceFile":"src/screens/CommunityContent/sections/HeaderWrapper/HeaderWrapper.jsx"}}},{"id":"src/screens/CommunityContent/sections/Communitycontentpage/Communitycontentpage.jsx:87:6-to-src/screens/CommunityPage/CommunityPage.jsx","source":"src/screens/CommunityContent/sections/Communitycontentpage/Communitycontentpage.jsx","target":"src/screens/CommunityPage/CommunityPage.jsx","data":{"viaRoute":"/communitypage","trigger":{"element":"\n 재료 나눔 게시판
\n ","line":87,"endLine":89,"column":6,"endColumn":13,"sourceFile":"src/screens/CommunityContent/sections/Communitycontentpage/Communitycontentpage.jsx"}}},{"id":"src/screens/Desktop/sections/IngredientInventory/IngredientInventory.jsx:58:8-to-src/screens/AddIngredientPage/AddIngredientPage.jsx","source":"src/screens/Desktop/sections/IngredientInventory/IngredientInventory.jsx","target":"src/screens/AddIngredientPage/AddIngredientPage.jsx","data":{"viaRoute":"/addingredient","trigger":{"element":"\n \n
\n\n
\n

\n
\n
\n ","line":58,"endLine":75,"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/sections/MenuInventory/MenuInventory.jsx","target":"src/screens/MenuRecommendationPage/MenuRecommendationPage.jsx","data":{"viaRoute":"/menurecommendation","trigger":{"element":"\n \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/sections/MenuInventory/MenuInventory.jsx","target":"src/screens/MenuRecommendationPage/MenuRecommendationPage.jsx","data":{"viaRoute":"/menurecommendation","trigger":{"element":"\n 더보기 →\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/sections/Community/Community.jsx","target":"src/screens/CommunityPage/CommunityPage.jsx","data":{"viaRoute":"/communitypage","trigger":{"element":"\n 재료 나눔 게시판
\n ","line":18,"endLine":20,"column":6,"endColumn":13,"sourceFile":"src/screens/Desktop/sections/Community/Community.jsx"}}},{"id":"src/components/DivWrapper/DivWrapper.jsx:49:10-to-src/screens/Desktop/Desktop.jsx","source":"src/components/DivWrapper/DivWrapper.jsx","target":"src/screens/Desktop/Desktop.jsx","data":{"viaRoute":"/desktop","trigger":{"element":"\n
\n ","line":49,"endLine":55,"column":10,"endColumn":17,"sourceFile":"src/components/DivWrapper/DivWrapper.jsx"}}},{"id":"src/components/DivWrapper/DivWrapper.jsx:58:8-to-src/screens/Desktop/Desktop.jsx","source":"src/components/DivWrapper/DivWrapper.jsx","target":"src/screens/Desktop/Desktop.jsx","data":{"viaRoute":"/desktop","trigger":{"element":"\n \n 알고리즘 \n\n 셰프\n
\n ","line":58,"endLine":64,"column":8,"endColumn":15,"sourceFile":"src/components/DivWrapper/DivWrapper.jsx"}}},{"id":"src/components/DivWrapper/DivWrapper.jsx:67:10-to-src/screens/MyPage/MyPage.jsx","source":"src/components/DivWrapper/DivWrapper.jsx","target":"src/screens/MyPage/MyPage.jsx","data":{"viaRoute":"/mypage","trigger":{"element":"\n 마이페이지
\n ","line":67,"endLine":69,"column":10,"endColumn":17,"sourceFile":"src/components/DivWrapper/DivWrapper.jsx"}}},{"id":"src/components/UnifiedHeader/UnifiedHeader.jsx:53:14-to-src/screens/Desktop/Desktop.jsx","source":"src/components/UnifiedHeader/UnifiedHeader.jsx","target":"src/screens/Desktop/Desktop.jsx","data":{"viaRoute":"/desktop","trigger":{"element":" setShowMenu(false)}>\n 홈\n ","line":53,"endLine":55,"column":14,"endColumn":21,"sourceFile":"src/components/UnifiedHeader/UnifiedHeader.jsx"}}},{"id":"src/components/UnifiedHeader/UnifiedHeader.jsx:56:14-to-src/screens/MenuRecommendationPage/MenuRecommendationPage.jsx","source":"src/components/UnifiedHeader/UnifiedHeader.jsx","target":"src/screens/MenuRecommendationPage/MenuRecommendationPage.jsx","data":{"viaRoute":"/menurecommendation","trigger":{"element":" setShowMenu(false)}>\n 메뉴 추천\n ","line":56,"endLine":58,"column":14,"endColumn":21,"sourceFile":"src/components/UnifiedHeader/UnifiedHeader.jsx"}}},{"id":"src/components/UnifiedHeader/UnifiedHeader.jsx:59:14-to-src/screens/CommunityPage/CommunityPage.jsx","source":"src/components/UnifiedHeader/UnifiedHeader.jsx","target":"src/screens/CommunityPage/CommunityPage.jsx","data":{"viaRoute":"/communitypage","trigger":{"element":" setShowMenu(false)}>\n 재료 나눔 게시판\n ","line":59,"endLine":61,"column":14,"endColumn":21,"sourceFile":"src/components/UnifiedHeader/UnifiedHeader.jsx"}}},{"id":"src/components/UnifiedHeader/UnifiedHeader.jsx:62:14-to-src/screens/MyPage/MyPage.jsx","source":"src/components/UnifiedHeader/UnifiedHeader.jsx","target":"src/screens/MyPage/MyPage.jsx","data":{"viaRoute":"/mypage","trigger":{"element":" setShowMenu(false)}>\n 마이페이지\n ","line":62,"endLine":64,"column":14,"endColumn":21,"sourceFile":"src/components/UnifiedHeader/UnifiedHeader.jsx"}}},{"id":"src/components/UnifiedHeader/UnifiedHeader.jsx:67:10-to-src/screens/Desktop/Desktop.jsx","source":"src/components/UnifiedHeader/UnifiedHeader.jsx","target":"src/screens/Desktop/Desktop.jsx","data":{"viaRoute":"/desktop","trigger":{"element":"\n
\n ","line":67,"endLine":73,"column":10,"endColumn":17,"sourceFile":"src/components/UnifiedHeader/UnifiedHeader.jsx"}}},{"id":"src/components/UnifiedHeader/UnifiedHeader.jsx:76:8-to-src/screens/Desktop/Desktop.jsx","source":"src/components/UnifiedHeader/UnifiedHeader.jsx","target":"src/screens/Desktop/Desktop.jsx","data":{"viaRoute":"/desktop","trigger":{"element":"\n \n 알고리즘 \n 셰프\n
\n ","line":76,"endLine":81,"column":8,"endColumn":15,"sourceFile":"src/components/UnifiedHeader/UnifiedHeader.jsx"}}},{"id":"src/components/UnifiedHeader/UnifiedHeader.jsx:84:10-to-src/screens/MyPage/MyPage.jsx","source":"src/components/UnifiedHeader/UnifiedHeader.jsx","target":"src/screens/MyPage/MyPage.jsx","data":{"viaRoute":"/mypage","trigger":{"element":"\n 마이페이지
\n ","line":84,"endLine":86,"column":10,"endColumn":17,"sourceFile":"src/components/UnifiedHeader/UnifiedHeader.jsx"}}}]}
\ No newline at end of file
diff --git a/static/README.md b/static/README.md
new file mode 100644
index 0000000..6486779
--- /dev/null
+++ b/static/README.md
@@ -0,0 +1,124 @@
+# 알고리즘 셰프 - 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/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/static/index.html b/static/index.html
new file mode 100644
index 0000000..ad21cc5
--- /dev/null
+++ b/static/index.html
@@ -0,0 +1,36 @@
+
+
+
+
+ 알고리즘 셰프 - Algorithm Chef
+
+
+
+
+
+
+
+
+
diff --git a/static/package.json b/static/package.json
new file mode 100644
index 0000000..eb97940
--- /dev/null
+++ b/static/package.json
@@ -0,0 +1,23 @@
+{
+ "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"
+ },
+ "dependencies": {
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-router-dom": "^6.8.1"
+ },
+ "devDependencies": {
+ "@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/static/src/App.jsx b/static/src/App.jsx
new file mode 100644
index 0000000..ece6972
--- /dev/null
+++ b/static/src/App.jsx
@@ -0,0 +1,61 @@
+import React from "react";
+import { RouterProvider, createBrowserRouter } from "react-router-dom";
+import { AuthProvider } from "./contexts/AuthContext";
+import { ScrollToTop } from "./components/ScrollToTop"; // Import ScrollToTop
+import { CommunityContent } from "./screens/CommunityContent";
+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: ,
+ },
+ {
+ path: "/communitycontentpage/:id",
+ element: ,
+ },
+ {
+ path: "/addingredient",
+ element: ,
+ },
+ {
+ path: "/createpost",
+ element: ,
+ },
+ {
+ path: "/menurecommendation",
+ element: ,
+ },
+]);
+
+export const App = () => {
+ return (
+
+
+ {/* Render ScrollToTop inside RouterProvider */}
+
+
+ );
+};
diff --git a/static/src/components/AddIngredientPopup/AddIngredientPopup.jsx b/static/src/components/AddIngredientPopup/AddIngredientPopup.jsx
new file mode 100644
index 0000000..63381f8
--- /dev/null
+++ b/static/src/components/AddIngredientPopup/AddIngredientPopup.jsx
@@ -0,0 +1,89 @@
+import React, { useState } from "react";
+import "./style.css";
+
+const CATEGORIES = ["육류", "채소류", "가공류", "유제품", "기타"];
+
+export const AddIngredientPopup = ({ onClose, onAdd }) => {
+ const [name, setName] = useState("");
+ const [category, setCategory] = useState("채소류");
+ const [expiryDays, setExpiryDays] = useState("");
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+
+ if (!name || !expiryDays) {
+ alert("모든 필드를 입력해주세요.");
+ return;
+ }
+
+ const newIngredient = {
+ id: Date.now(),
+ name,
+ category,
+ expiryDays: parseInt(expiryDays),
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ };
+
+ // TODO: Send to backend
+ console.log("Adding ingredient via receipt:", newIngredient);
+ onAdd(newIngredient);
+ onClose();
+ };
+
+ return (
+
+
e.stopPropagation()}>
+
+
+
+
식재료 추가
+
+
+
+
+
+ );
+};
diff --git a/static/src/components/AddIngredientPopup/index.js b/static/src/components/AddIngredientPopup/index.js
new file mode 100644
index 0000000..f656586
--- /dev/null
+++ b/static/src/components/AddIngredientPopup/index.js
@@ -0,0 +1 @@
+export { AddIngredientPopup } from "./AddIngredientPopup";
diff --git a/static/src/components/AddIngredientPopup/style.css b/static/src/components/AddIngredientPopup/style.css
new file mode 100644
index 0000000..4463c52
--- /dev/null
+++ b/static/src/components/AddIngredientPopup/style.css
@@ -0,0 +1,136 @@
+.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: 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;
+ }
+}
+
+.add-ingredient-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;
+}
+
+.add-ingredient-popup-close:hover {
+ color: #000000;
+}
+
+.add-ingredient-popup-header {
+ margin-bottom: 30px;
+ text-align: center;
+}
+
+.add-ingredient-popup-title {
+ font-family: "Inter", Helvetica;
+ font-size: 28px;
+ font-weight: 700;
+ color: #000000;
+ 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;
+}
+
+.add-ingredient-popup-label {
+ font-family: "Inter", Helvetica;
+ font-size: 14px;
+ font-weight: 600;
+ color: #000000;
+}
+
+.add-ingredient-popup-input,
+.add-ingredient-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;
+}
+
+.add-ingredient-popup-input:focus,
+.add-ingredient-popup-select:focus {
+ outline: none;
+ border-color: #f6910b;
+}
+
+.add-ingredient-popup-input::placeholder {
+ color: #999999;
+}
+
+.add-ingredient-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;
+}
+
+.add-ingredient-popup-submit-btn:hover {
+ background-color: #e58209;
+}
diff --git a/static/src/components/CommunitypageWrapper/CommunitypageWrapper.jsx b/static/src/components/CommunitypageWrapper/CommunitypageWrapper.jsx
new file mode 100644
index 0000000..8b47a5f
--- /dev/null
+++ b/static/src/components/CommunitypageWrapper/CommunitypageWrapper.jsx
@@ -0,0 +1,46 @@
+/*
+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 = "돼지고기 100g 나눔합니다.",
+ author = "test3User",
+ date = "2025-11-03",
+ category = "나눔",
+ content = ""
+}) => {
+ return (
+
+
+
+
+
+
+
작성자 :
+
+
{author}
+
+
작성일 :
+
+
{date}
+
+
+ );
+};
diff --git a/static/src/components/CommunitypageWrapper/index.js b/static/src/components/CommunitypageWrapper/index.js
new file mode 100644
index 0000000..47ea4ad
--- /dev/null
+++ b/static/src/components/CommunitypageWrapper/index.js
@@ -0,0 +1 @@
+export { CommunitypageWrapper } from "./CommunitypageWrapper";
diff --git a/static/src/components/CommunitypageWrapper/style.css b/static/src/components/CommunitypageWrapper/style.css
new file mode 100644
index 0000000..2b8c5c0
--- /dev/null
+++ b/static/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/static/src/components/DivWrapper/DivWrapper.jsx b/static/src/components/DivWrapper/DivWrapper.jsx
new file mode 100644
index 0000000..d93923c
--- /dev/null
+++ b/static/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 (
+ <>
+
+
+
+

+
+
+
+
+
+ 알고리즘
+
+ 셰프
+
+
+
+
+
+
마이페이지
+
+ {isAuthenticated ? (
+
+ {user?.username}
+
+
+ ) : (
+
+ )}
+
+
+
+ {showLoginPopup && (
+
+ )}
+
+ {showSignupPopup && (
+
+ )}
+
+ {showPreferencesPopup && (
+
+ )}
+ >
+ );
+};
diff --git a/static/src/components/DivWrapper/index.js b/static/src/components/DivWrapper/index.js
new file mode 100644
index 0000000..b316d4a
--- /dev/null
+++ b/static/src/components/DivWrapper/index.js
@@ -0,0 +1 @@
+export { DivWrapper } from "./DivWrapper";
diff --git a/static/src/components/DivWrapper/style.css b/static/src/components/DivWrapper/style.css
new file mode 100644
index 0000000..4e022cf
--- /dev/null
+++ b/static/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/static/src/components/IngredientComponent/IngredientComponent.jsx b/static/src/components/IngredientComponent/IngredientComponent.jsx
new file mode 100644
index 0000000..21f08e8
--- /dev/null
+++ b/static/src/components/IngredientComponent/IngredientComponent.jsx
@@ -0,0 +1,36 @@
+/*
+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 IngredientComponent = ({
+ className,
+ ingredientImage = "https://c.animaapp.com/sjWITF5i/img/ingredientimage-1.png",
+ text = "양파",
+ expiryDays = 0,
+}) => {
+ return (
+
+
+
+

+
+
+
+
+
+
+
+ );
+};
diff --git a/static/src/components/IngredientComponent/index.js b/static/src/components/IngredientComponent/index.js
new file mode 100644
index 0000000..b5ee1c9
--- /dev/null
+++ b/static/src/components/IngredientComponent/index.js
@@ -0,0 +1 @@
+export { IngredientComponent } from "./IngredientComponent";
diff --git a/static/src/components/IngredientComponent/style.css b/static/src/components/IngredientComponent/style.css
new file mode 100644
index 0000000..c5cb482
--- /dev/null
+++ b/static/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/static/src/components/IngredientInventoryWrapper/IngredientInventoryWrapper.jsx b/static/src/components/IngredientInventoryWrapper/IngredientInventoryWrapper.jsx
new file mode 100644
index 0000000..4162c4e
--- /dev/null
+++ b/static/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/static/src/components/IngredientInventoryWrapper/index.js b/static/src/components/IngredientInventoryWrapper/index.js
new file mode 100644
index 0000000..9c45c63
--- /dev/null
+++ b/static/src/components/IngredientInventoryWrapper/index.js
@@ -0,0 +1 @@
+export { IngredientInventoryWrapper } from "./IngredientInventoryWrapper";
diff --git a/static/src/components/IngredientInventoryWrapper/style.css b/static/src/components/IngredientInventoryWrapper/style.css
new file mode 100644
index 0000000..f07ded5
--- /dev/null
+++ b/static/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/static/src/components/LoginPopup/LoginPopup.jsx b/static/src/components/LoginPopup/LoginPopup.jsx
new file mode 100644
index 0000000..9e29ed3
--- /dev/null
+++ b/static/src/components/LoginPopup/LoginPopup.jsx
@@ -0,0 +1,91 @@
+import React, { useState } from "react";
+import { useAuth } from "../../contexts/AuthContext";
+import "./style.css";
+
+export const LoginPopup = ({ onClose, onSwitchToSignup }) => {
+ const [email, setEmail] = useState(""); // Changed to email
+ const [password, setPassword] = useState("");
+ const { login } = useAuth();
+
+ const handleLogin = async (e) => {
+ e.preventDefault();
+
+ // TODO: Backend Integration: Replace with actual backend API call for login
+ // Example: axios.post('/api/login', { email, password })
+ // .then(response => {
+ // login(response.data.user); // Assuming backend returns user data including username
+ // onClose();
+ // })
+ // .catch(error => {
+ // alert("이메일 또는 비밀번호가 일치하지 않습니다.");
+ // });
+
+ // Simulating backend validation with localStorage
+ const mockUsers = JSON.parse(localStorage.getItem("registeredUsers") || "[]");
+ const foundUser = mockUsers.find(
+ (u) => u.email === email && u.password === password // Validate by email
+ );
+
+ if (foundUser) {
+ const userData = { username: foundUser.username, email: foundUser.email }; // Store username for display
+ login(userData);
+ console.log("Login successful:", userData);
+ onClose();
+ } else {
+ alert("이메일 또는 비밀번호가 일치하지 않습니다.");
+ }
+ };
+
+ return (
+
+
e.stopPropagation()}>
+
+
+
+
로그인
+
+
+
+
+
+ );
+};
diff --git a/static/src/components/LoginPopup/index.js b/static/src/components/LoginPopup/index.js
new file mode 100644
index 0000000..369fc41
--- /dev/null
+++ b/static/src/components/LoginPopup/index.js
@@ -0,0 +1 @@
+export { LoginPopup } from "./LoginPopup";
diff --git a/static/src/components/LoginPopup/style.css b/static/src/components/LoginPopup/style.css
new file mode 100644
index 0000000..4d5cd73
--- /dev/null
+++ b/static/src/components/LoginPopup/style.css
@@ -0,0 +1,162 @@
+.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;
+}
diff --git a/static/src/components/MenuInventoryWrapper/MenuInventoryWrapper.jsx b/static/src/components/MenuInventoryWrapper/MenuInventoryWrapper.jsx
new file mode 100644
index 0000000..9a9a275
--- /dev/null
+++ b/static/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/static/src/components/MenuInventoryWrapper/index.js b/static/src/components/MenuInventoryWrapper/index.js
new file mode 100644
index 0000000..0064260
--- /dev/null
+++ b/static/src/components/MenuInventoryWrapper/index.js
@@ -0,0 +1 @@
+export { MenuInventoryWrapper } from "./MenuInventoryWrapper";
diff --git a/static/src/components/MenuInventoryWrapper/style.css b/static/src/components/MenuInventoryWrapper/style.css
new file mode 100644
index 0000000..0ca8d78
--- /dev/null
+++ b/static/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/static/src/components/MypageTendency/MypageTendency.jsx b/static/src/components/MypageTendency/MypageTendency.jsx
new file mode 100644
index 0000000..8f03892
--- /dev/null
+++ b/static/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 (
+
+ );
+};
diff --git a/static/src/components/MypageTendency/index.js b/static/src/components/MypageTendency/index.js
new file mode 100644
index 0000000..ccc46e0
--- /dev/null
+++ b/static/src/components/MypageTendency/index.js
@@ -0,0 +1 @@
+export { MypageTendency } from "./MypageTendency";
diff --git a/static/src/components/MypageTendency/style.css b/static/src/components/MypageTendency/style.css
new file mode 100644
index 0000000..079a288
--- /dev/null
+++ b/static/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/static/src/components/PageButton/PageButton.jsx b/static/src/components/PageButton/PageButton.jsx
new file mode 100644
index 0000000..65c6620
--- /dev/null
+++ b/static/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/static/src/components/PageButton/index.js b/static/src/components/PageButton/index.js
new file mode 100644
index 0000000..b4b7bf5
--- /dev/null
+++ b/static/src/components/PageButton/index.js
@@ -0,0 +1 @@
+export { PageButton } from "./PageButton";
diff --git a/static/src/components/PageButton/style.css b/static/src/components/PageButton/style.css
new file mode 100644
index 0000000..37f0754
--- /dev/null
+++ b/static/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/static/src/components/PreferencesPopup/PreferencesPopup.jsx b/static/src/components/PreferencesPopup/PreferencesPopup.jsx
new file mode 100644
index 0000000..924c41f
--- /dev/null
+++ b/static/src/components/PreferencesPopup/PreferencesPopup.jsx
@@ -0,0 +1,217 @@
+import React, { useState } from "react";
+import "./style.css";
+
+const HEALTH_GOALS = [
+ { id: 1, name: "체중 감량" },
+ { id: 2, name: "근육 증가" },
+ { id: 3, name: "건강 유지" },
+ { id: 4, name: "면역력 강화" },
+ { id: 5, name: "소화 개선" },
+];
+
+const ALLERGIES = [
+ { id: 1, name: "우유" },
+ { id: 2, name: "계란" },
+ { id: 3, name: "땅콩" },
+ { id: 4, name: "견과류" },
+ { id: 5, name: "갑각류" },
+ { id: 6, name: "밀" },
+ { id: 7, name: "대두" },
+];
+
+const SPICE_LEVELS = ["안매움", "약간매움", "매움", "아주매움"];
+const CUISINE_TYPES = ["한식", "중식", "일식", "양식", "기타"];
+
+export const PreferencesPopup = ({ onClose, userData }) => {
+ const [healthGoalIds, setHealthGoalIds] = useState([]);
+ const [allergyIds, setAllergyIds] = useState([]);
+ const [dislikedIngredients, setDislikedIngredients] = useState("");
+ const [preferredIngredients, setPreferredIngredients] = useState("");
+ const [preferredCuisine, setPreferredCuisine] = useState("");
+ const [spiceLevel, setSpiceLevel] = useState("");
+ const [allowPushConsumption, setAllowPushConsumption] = useState(true);
+ const [allowPushComment, setAllowPushComment] = useState(true);
+ const [allowPushNudge, setAllowPushNudge] = useState(true);
+
+ 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 handleSubmit = (e) => {
+ e.preventDefault();
+
+ const preferences = {
+ ...userData,
+ healthGoalIds,
+ allergyIds,
+ dislikedIngredients,
+ preferredIngredients,
+ preferredCuisine,
+ spiceLevel,
+ allowPushConsumption,
+ allowPushComment,
+ allowPushNudge,
+ };
+
+ // TODO: Backend Integration: Replace with actual API call to save user preferences
+ // Example: axios.post(`/api/users/${userData.username}/preferences`, preferences)
+ // .then(() => {
+ // alert("회원가입이 완료되었습니다!");
+ // onClose();
+ // })
+ // .catch(error => {
+ // alert("성향 저장 실패: " + error.response.data.message);
+ // });
+
+ // Simulating saving preferences to localStorage
+ console.log("Complete signup with preferences:", preferences);
+ localStorage.setItem("userPreferences", JSON.stringify(preferences)); // Store preferences
+ alert("회원가입이 완료되었습니다!");
+ onClose();
+ };
+
+ return (
+
+
e.stopPropagation()}>
+
+
+
+
성향 설정
+
맞춤 추천을 위한 정보를 입력해주세요
+
+
+
+
+
+ );
+};
diff --git a/static/src/components/PreferencesPopup/index.js b/static/src/components/PreferencesPopup/index.js
new file mode 100644
index 0000000..d571f4a
--- /dev/null
+++ b/static/src/components/PreferencesPopup/index.js
@@ -0,0 +1 @@
+export { PreferencesPopup } from "./PreferencesPopup";
diff --git a/static/src/components/PreferencesPopup/style.css b/static/src/components/PreferencesPopup/style.css
new file mode 100644
index 0000000..f313960
--- /dev/null
+++ b/static/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/static/src/components/Recipepage/Recipepage.jsx b/static/src/components/Recipepage/Recipepage.jsx
new file mode 100644
index 0000000..df0a519
--- /dev/null
+++ b/static/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 (
+
+ );
+};
diff --git a/static/src/components/Recipepage/index.js b/static/src/components/Recipepage/index.js
new file mode 100644
index 0000000..8a0ee11
--- /dev/null
+++ b/static/src/components/Recipepage/index.js
@@ -0,0 +1 @@
+export { Recipepage } from "./Recipepage";
diff --git a/static/src/components/Recipepage/style.css b/static/src/components/Recipepage/style.css
new file mode 100644
index 0000000..0ffc920
--- /dev/null
+++ b/static/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/static/src/components/ScrollToTop/ScrollToTop.jsx b/static/src/components/ScrollToTop/ScrollToTop.jsx
new file mode 100644
index 0000000..276b090
--- /dev/null
+++ b/static/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/static/src/components/ScrollToTop/index.js b/static/src/components/ScrollToTop/index.js
new file mode 100644
index 0000000..04f3393
--- /dev/null
+++ b/static/src/components/ScrollToTop/index.js
@@ -0,0 +1 @@
+export { ScrollToTop } from "./ScrollToTop";
diff --git a/static/src/components/SignupPopup/SignupPopup.jsx b/static/src/components/SignupPopup/SignupPopup.jsx
new file mode 100644
index 0000000..11b872a
--- /dev/null
+++ b/static/src/components/SignupPopup/SignupPopup.jsx
@@ -0,0 +1,150 @@
+import React, { useState } from "react";
+import "./style.css";
+
+export const SignupPopup = ({ onClose, onSwitchToLogin, onSwitchToPreferences }) => {
+ const [username, setUsername] = useState("");
+ const [password, setPassword] = useState("");
+ const [confirmPassword, setConfirmPassword] = useState("");
+ const [email, setEmail] = useState("");
+ const [gender, setGender] = useState("");
+ const [birthDate, setBirthDate] = useState("");
+
+ const handleSignup = (e) => {
+ e.preventDefault();
+
+ if (password !== confirmPassword) {
+ alert("비밀번호가 일치하지 않습니다.");
+ return;
+ }
+
+ // TODO: Backend Integration: Replace with actual backend API call for signup
+ // Example: axios.post('/api/signup', userData)
+ // .then(response => {
+ // onSwitchToPreferences(response.data.user); // Pass user data from backend
+ // })
+ // .catch(error => {
+ // alert("회원가입 실패: " + error.response.data.message);
+ // });
+
+ // Simulating backend validation and storage with localStorage
+ const mockUsers = JSON.parse(localStorage.getItem("registeredUsers") || "[]");
+
+ // Check if email already exists
+ if (mockUsers.find((u) => u.email === email)) { // Check for duplicate email
+ alert("이미 존재하는 이메일입니다.");
+ return;
+ }
+
+ const userData = { username, email, password, gender, birthDate };
+ mockUsers.push(userData);
+ localStorage.setItem("registeredUsers", JSON.stringify(mockUsers));
+
+ console.log("Signup attempt:", userData);
+
+ // Move to preferences popup
+ onSwitchToPreferences(userData);
+ };
+
+ return (
+
+ );
+};
diff --git a/static/src/components/SignupPopup/index.js b/static/src/components/SignupPopup/index.js
new file mode 100644
index 0000000..de7e73a
--- /dev/null
+++ b/static/src/components/SignupPopup/index.js
@@ -0,0 +1 @@
+export { SignupPopup } from "./SignupPopup";
diff --git a/static/src/components/SignupPopup/style.css b/static/src/components/SignupPopup/style.css
new file mode 100644
index 0000000..92d1b00
--- /dev/null
+++ b/static/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/static/src/components/Tendency/Tendency.jsx b/static/src/components/Tendency/Tendency.jsx
new file mode 100644
index 0000000..951a804
--- /dev/null
+++ b/static/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/static/src/components/Tendency/index.js b/static/src/components/Tendency/index.js
new file mode 100644
index 0000000..4ba92b2
--- /dev/null
+++ b/static/src/components/Tendency/index.js
@@ -0,0 +1 @@
+export { Tendency } from "./Tendency";
diff --git a/static/src/components/Tendency/style.css b/static/src/components/Tendency/style.css
new file mode 100644
index 0000000..b02e42c
--- /dev/null
+++ b/static/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/static/src/components/UnifiedHeader/UnifiedHeader.jsx b/static/src/components/UnifiedHeader/UnifiedHeader.jsx
new file mode 100644
index 0000000..f0fadc6
--- /dev/null
+++ b/static/src/components/UnifiedHeader/UnifiedHeader.jsx
@@ -0,0 +1,125 @@
+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 UnifiedHeader = () => {
+ const { user, isAuthenticated, logout } = useAuth();
+ const [showLoginPopup, setShowLoginPopup] = useState(false);
+ const [showSignupPopup, setShowSignupPopup] = useState(false);
+ const [showPreferencesPopup, setShowPreferencesPopup] = useState(false);
+ const [showMenu, setShowMenu] = 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 (
+ <>
+
+
+ {showLoginPopup && (
+
+ )}
+
+ {showSignupPopup && (
+
+ )}
+
+ {showPreferencesPopup && (
+
+ )}
+ >
+ );
+};
diff --git a/static/src/components/UnifiedHeader/index.js b/static/src/components/UnifiedHeader/index.js
new file mode 100644
index 0000000..cab05e2
--- /dev/null
+++ b/static/src/components/UnifiedHeader/index.js
@@ -0,0 +1 @@
+export { UnifiedHeader } from "./UnifiedHeader";
diff --git a/static/src/components/UnifiedHeader/style.css b/static/src/components/UnifiedHeader/style.css
new file mode 100644
index 0000000..af3725a
--- /dev/null
+++ b/static/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/static/src/components/UsedIngredient/UsedIngredient.jsx b/static/src/components/UsedIngredient/UsedIngredient.jsx
new file mode 100644
index 0000000..24ef221
--- /dev/null
+++ b/static/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 (
+
+ );
+};
diff --git a/static/src/components/UsedIngredient/index.js b/static/src/components/UsedIngredient/index.js
new file mode 100644
index 0000000..fc3b90e
--- /dev/null
+++ b/static/src/components/UsedIngredient/index.js
@@ -0,0 +1 @@
+export { UsedIngredient } from "./UsedIngredient";
diff --git a/static/src/components/UsedIngredient/style.css b/static/src/components/UsedIngredient/style.css
new file mode 100644
index 0000000..0ce632e
--- /dev/null
+++ b/static/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/static/src/contexts/AuthContext.js b/static/src/contexts/AuthContext.js
new file mode 100644
index 0000000..328bb52
--- /dev/null
+++ b/static/src/contexts/AuthContext.js
@@ -0,0 +1 @@
+export { AuthProvider, useAuth } from "./AuthContext.jsx";
diff --git a/static/src/contexts/AuthContext.jsx b/static/src/contexts/AuthContext.jsx
new file mode 100644
index 0000000..930af19
--- /dev/null
+++ b/static/src/contexts/AuthContext.jsx
@@ -0,0 +1,44 @@
+import React, { createContext, useContext, useState, useEffect } from "react";
+
+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(() => {
+ // Check if user is logged in from localStorage
+ const storedUser = localStorage.getItem("currentUser");
+ if (storedUser) {
+ const userData = JSON.parse(storedUser);
+ setUser(userData);
+ setIsAuthenticated(true);
+ }
+ }, []);
+
+ const login = (userData) => {
+ setUser(userData);
+ setIsAuthenticated(true);
+ localStorage.setItem("currentUser", JSON.stringify(userData));
+ };
+
+ const logout = () => {
+ setUser(null);
+ setIsAuthenticated(false);
+ localStorage.removeItem("currentUser");
+ };
+
+ return (
+
+ {children}
+
+ );
+};
diff --git a/static/src/index.jsx b/static/src/index.jsx
new file mode 100644
index 0000000..f55d523
--- /dev/null
+++ b/static/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/static/src/screens/AddIngredientPage/AddIngredientPage.jsx b/static/src/screens/AddIngredientPage/AddIngredientPage.jsx
new file mode 100644
index 0000000..6d907fe
--- /dev/null
+++ b/static/src/screens/AddIngredientPage/AddIngredientPage.jsx
@@ -0,0 +1,148 @@
+import React, { useState, useEffect } from "react";
+import { Link, useNavigate } from "react-router-dom"; // Removed useLocation
+import { UnifiedHeader } from "../../components/UnifiedHeader";
+import "./style.css";
+
+const CATEGORIES = ["전체", "육류", "채소류", "가공류", "유제품", "기타"];
+
+const MOCK_INGREDIENT_DATABASE = [
+ // TODO: Backend Integration: Replace with API call to fetch all available ingredients from DB
+ { id: 1, name: "양파", category: "채소류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-1.png" },
+ { id: 2, name: "닭고기", category: "육류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 3, name: "돼지고기", category: "육류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 4, name: "소고기", category: "육류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 5, name: "고추장", category: "가공류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 6, name: "고춧가루", category: "가공류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 7, name: "된장", category: "가공류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 8, name: "간장", category: "가공류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 9, name: "양배추", category: "채소류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 10, name: "당근", category: "채소류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 11, name: "감자", category: "채소류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 12, name: "마늘", category: "채소류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 13, name: "우유", category: "유제품", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 14, name: "치즈", category: "유제품", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 15, name: "요거트", category: "유제품", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+ { id: 16, name: "계란", category: "기타", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png" },
+];
+
+export const AddIngredientPage = () => {
+ const navigate = useNavigate();
+ // Removed useLocation and onAddMultipleIngredients here
+ // const location = useLocation();
+ // const onAddMultipleIngredients = location.state?.onAddMultipleIngredients;
+
+ const [selectedCategory, setSelectedCategory] = useState("전체");
+ const [searchQuery, setSearchQuery] = useState("");
+ const [selectedIngredients, setSelectedIngredients] = useState([]);
+
+ const filteredIngredients = MOCK_INGREDIENT_DATABASE.filter((ingredient) => {
+ const matchesCategory = selectedCategory === "전체" || ingredient.category === selectedCategory;
+ const matchesSearch = ingredient.name.toLowerCase().includes(searchQuery.toLowerCase());
+ return matchesCategory && matchesSearch;
+ });
+
+ 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 {
+ return [...prev, { ...ingredient, expiryDays: 7 }]; // Default expiry days
+ }
+ });
+ };
+
+ const handleAddIngredients = () => {
+ if (selectedIngredients.length === 0) {
+ alert("추가할 식재료를 선택해주세요.");
+ return;
+ }
+
+ // Directly update localStorage for userIngredients
+ const currentIngredients = JSON.parse(localStorage.getItem("userIngredients") || "[]");
+ const updatedIngredients = [...currentIngredients, ...selectedIngredients];
+ localStorage.setItem("userIngredients", JSON.stringify(updatedIngredients));
+
+ // TODO: Backend Integration: Send selectedIngredients to backend
+ console.log("Adding ingredients to mock DB:", selectedIngredients);
+
+ navigate("/desktop"); // Navigate back to desktop
+ };
+
+ return (
+
+
+
+
+
+
식재료 추가
+
냉장고에 추가할 식재료를 선택하세요
+
+
+
+ setSearchQuery(e.target.value)}
+ />
+
+
+
+ {CATEGORIES.map((category) => (
+
+ ))}
+
+
+
+ {filteredIngredients.map((ingredient) => {
+ const isSelected = selectedIngredients.find((item) => item.id === ingredient.id);
+ return (
+
+ );
+ })}
+
+
+
+
+ 선택된 식재료: {selectedIngredients.length}개
+
+
+
+ 취소
+
+
+
+
+
+
+ );
+};
diff --git a/static/src/screens/AddIngredientPage/index.js b/static/src/screens/AddIngredientPage/index.js
new file mode 100644
index 0000000..13def78
--- /dev/null
+++ b/static/src/screens/AddIngredientPage/index.js
@@ -0,0 +1 @@
+export { AddIngredientPage } from "./AddIngredientPage";
diff --git a/static/src/screens/AddIngredientPage/style.css b/static/src/screens/AddIngredientPage/style.css
new file mode 100644
index 0000000..baad900
--- /dev/null
+++ b/static/src/screens/AddIngredientPage/style.css
@@ -0,0 +1,284 @@
+.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.10px;
+ line-height: 29.0px;
+ 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 {
+ align-self: stretch;
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
+ gap: 20px;
+ padding: 20px 0;
+ width: 100%;
+}
+
+.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;
+}
+
+.add-ingredient-card:hover {
+ border-color: #f6910b;
+ transform: translateY(-2px);
+ box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1);
+}
+
+.add-ingredient-card.selected {
+ border-color: #f6910b;
+ background-color: #f6910b1a;
+}
+
+.add-ingredient-card-image {
+ width: 80px;
+ height: 80px;
+ object-fit: cover;
+ border-radius: 8px;
+}
+
+.add-ingredient-card-name {
+ color: #000000;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 600;
+ text-align: center;
+}
+
+.add-ingredient-card-category {
+ color: #666666;
+ font-family: "Inter", Helvetica;
+ font-size: 12px;
+ 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: 16px;
+ 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%;
+}
+
+.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;
+ }
+
+ .add-ingredient-grid {
+ grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
+ gap: 15px;
+ }
+
+ .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/static/src/screens/CommunityContent/CommunityContent.jsx b/static/src/screens/CommunityContent/CommunityContent.jsx
new file mode 100644
index 0000000..d3c060f
--- /dev/null
+++ b/static/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/static/src/screens/CommunityContent/index.js b/static/src/screens/CommunityContent/index.js
new file mode 100644
index 0000000..9ea88ad
--- /dev/null
+++ b/static/src/screens/CommunityContent/index.js
@@ -0,0 +1 @@
+export { CommunityContent } from "./CommunityContent";
diff --git a/static/src/screens/CommunityContent/sections/Communitycontentpage/Communitycontentpage.jsx b/static/src/screens/CommunityContent/sections/Communitycontentpage/Communitycontentpage.jsx
new file mode 100644
index 0000000..c36c180
--- /dev/null
+++ b/static/src/screens/CommunityContent/sections/Communitycontentpage/Communitycontentpage.jsx
@@ -0,0 +1,178 @@
+import React, { useState } from "react";
+import { Link, useLocation, useParams } from "react-router-dom";
+import { useAuth } from "../../../../contexts/AuthContext";
+import { CommunitypageWrapper } from "../../../../components/CommunitypageWrapper";
+import { PageButton } from "../../../../components/PageButton";
+import "./style.css";
+
+const ALL_POSTS = [
+ // TODO: Backend Integration: Replace with API call to fetch all community posts
+ { id: 1, title: "돼지고기 100g 나눔합니다.", author: "test3User", date: "2025-11-03", category: "나눔", content: "돼지고기를 너무 많이 샀네요 남는 돼지고기 나눔해요" },
+ { id: 2, title: "양파 2개 나눔해요", author: "user123", date: "2025-11-02", category: "나눔", content: "양파 2개 필요하신 분 가져가세요" },
+ { id: 3, title: "닭고기 500g 나눔", author: "foodlover", date: "2025-11-01", category: "나눔", content: "신선한 닭고기 나눔합니다" },
+ { id: 4, title: "고추장 새것 나눔합니다", author: "chef99", date: "2025-10-31", category: "나눔", content: "개봉 안한 고추장 드립니다" },
+ { id: 5, title: "양배추 한통 가져가세요", author: "veggie_fan", date: "2025-10-30", category: "나눔", content: "양배추 한통 나눔해요" },
+ { id: 6, title: "감자 5개 나눔", author: "potato_lover", date: "2025-10-29", category: "나눔", content: "감자 5개 드립니다" },
+ { id: 7, title: "당근 한봉지 드립니다", author: "healthy_eater", date: "2025-10-28", category: "나눔", content: "당근 한봉지 나눔합니다" },
+ { id: 8, title: "우유 1L 나눔해요", author: "milk_fan", date: "2025-10-27", category: "나눔", content: "우유 1L 필요하신 분" },
+ { id: 9, title: "계란 10개 나눔", author: "egg_master", date: "2025-10-26", category: "나눔", content: "계란 10개 드립니다" },
+ { id: 10, title: "토마토 나눔합니다", author: "tomato_grower", date: "2025-10-25", category: "나눔", content: "토마토 나눔해요" },
+];
+
+export const Communitycontentpage = () => {
+ const location = useLocation();
+ const { id } = useParams();
+ const { user, isAuthenticated } = useAuth();
+ const [newComment, setNewComment] = useState("");
+
+ // Load comments from localStorage for this specific post
+ const [comments, setComments] = useState(() => {
+ const storedComments = localStorage.getItem(`comments_${id}`);
+ return storedComments ? JSON.parse(storedComments) : [];
+ });
+
+ const post = location.state?.post || {
+ // TODO: Backend Integration: Fetch post details by ID if not available in state
+ id: id,
+ title: "돼지고기 100g 나눔합니다.",
+ author: "test3User",
+ date: "2025-11-03",
+ category: "나눔",
+ content: "돼지고기를 너무 많이 샀네요 남는 돼지고기 나눔해요"
+ };
+
+ // Get related posts (2 before and 2 after current post)
+ const currentIndex = ALL_POSTS.findIndex(p => p.id === parseInt(id));
+ const relatedPosts = [];
+
+ // Get 2 posts before
+ for (let i = Math.max(0, currentIndex - 2); i < currentIndex; i++) {
+ if (ALL_POSTS[i]) relatedPosts.push(ALL_POSTS[i]);
+ }
+
+ // Get 2 posts after
+ for (let i = currentIndex + 1; i <= Math.min(ALL_POSTS.length - 1, currentIndex + 2); i++) {
+ if (ALL_POSTS[i]) relatedPosts.push(ALL_POSTS[i]);
+ }
+
+ const handleCommentSubmit = (e) => {
+ e.preventDefault();
+ if (!isAuthenticated) {
+ alert("로그인이 필요합니다.");
+ return;
+ }
+ if (!newComment.trim()) return;
+
+ const comment = {
+ id: Date.now(),
+ author: user.username,
+ content: newComment,
+ date: new Date().toLocaleString('ko-KR')
+ };
+
+ const updatedComments = [...comments, comment];
+ setComments(updatedComments);
+
+ // TODO: Backend Integration: Replace with API call to add a new comment
+ // Example: axios.post(`/api/posts/${id}/comments`, comment)
+
+ // Save comments to localStorage for this specific post (mock DB)
+ localStorage.setItem(`comments_${id}`, JSON.stringify(updatedComments));
+ setNewComment("");
+ };
+
+
+ return (
+
+
+
재료 나눔 게시판
+
+
+
+
+

+
+
{post.category}
+
+
+
+
+
+
작성자
+
+
{post.author}
+
+
작성일
+
+
{post.date}
+
+
+
+
+
+
+
댓글 ({comments.length})
+
+
+
+ {comments.map((comment) => (
+
+
{comment.author}
+
{comment.content}
+
{comment.date}
+
+ ))}
+
+
+
+
+
+
+ {relatedPosts.length > 0 && (
+
+
+
다른 게시글
+
+ {relatedPosts.map((relatedPost, index) => (
+
+ ))}
+
+ )}
+
+ );
+};
diff --git a/static/src/screens/CommunityContent/sections/Communitycontentpage/index.js b/static/src/screens/CommunityContent/sections/Communitycontentpage/index.js
new file mode 100644
index 0000000..1002677
--- /dev/null
+++ b/static/src/screens/CommunityContent/sections/Communitycontentpage/index.js
@@ -0,0 +1 @@
+export { Communitycontentpage } from "./Communitycontentpage";
diff --git a/static/src/screens/CommunityContent/sections/Communitycontentpage/style.css b/static/src/screens/CommunityContent/sections/Communitycontentpage/style.css
new file mode 100644
index 0000000..8c59938
--- /dev/null
+++ b/static/src/screens/CommunityContent/sections/Communitycontentpage/style.css
@@ -0,0 +1,336 @@
+.communitycontentpage {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ flex-grow: 1;
+ gap: 10px;
+ padding: 10px 64px;
+ position: relative;
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+.communitycontentpage .communitycontentpage-wrapper {
+ align-items: flex-start;
+ align-self: stretch;
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ border-color: #000000;
+ display: flex;
+ flex: 0 0 auto;
+ flex-direction: column;
+ gap: 10px;
+ padding: 10px;
+ position: relative;
+ width: 100%;
+}
+
+.communitycontentpage .text-wrapper-5 {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 48px;
+ font-weight: 700;
+ justify-content: center;
+ letter-spacing: -0.96px;
+ line-height: 57.6px;
+ margin-top: -1.00px;
+ position: relative;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+.communitycontentpage .div-3 {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ flex-grow: 1;
+ gap: 10px;
+ padding: 10px;
+ position: relative;
+ width: 100%;
+}
+
+.communitycontentpage .content-category {
+ align-items: flex-start;
+ background-color: #0000001a;
+ border-radius: 16px;
+ display: inline-flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ padding: 0px 20px;
+ position: relative;
+}
+
+.communitycontentpage .category-icon {
+ aspect-ratio: 1;
+ height: 54px;
+ position: relative;
+ width: 54px;
+}
+
+.communitycontentpage .content-categorytext {
+ align-items: center;
+ color: #00000099;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 48px;
+ font-weight: 700;
+ justify-content: center;
+ letter-spacing: -0.96px;
+ line-height: 57.6px;
+ margin-top: -1.00px;
+ position: relative;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+.communitycontentpage .content-header {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ padding: 0px 20px;
+ position: relative;
+ width: 100%;
+}
+
+.communitycontentpage .content-headertext {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 36px;
+ font-weight: 700;
+ justify-content: center;
+ letter-spacing: -0.72px;
+ line-height: 43.2px;
+ margin-top: -1.00px;
+ position: relative;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+.communitycontentpage .navbar {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ padding: 0px 25px;
+ position: relative;
+ width: 100%;
+}
+
+.communitycontentpage .text-wrapper-6 {
+ align-items: center;
+ color: #00000066;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 12px;
+ font-weight: 500;
+ justify-content: center;
+ letter-spacing: -0.24px;
+ line-height: 14.4px;
+ margin-top: -1.00px;
+ position: relative;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+.communitycontentpage .content {
+ align-items: flex-start;
+ align-self: stretch;
+ background-color: #f9f9f9;
+ border-radius: 12px;
+ display: flex;
+ flex: 1;
+ flex-grow: 1;
+ gap: 10px;
+ min-height: 200px;
+ padding: 20px;
+ position: relative;
+ width: 100%;
+}
+
+.communitycontentpage .content-text {
+ align-self: stretch;
+ color: #000000;
+ flex: 1;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 500;
+ letter-spacing: -0.08px;
+ line-height: 24px;
+ margin: 0;
+ position: relative;
+ white-space: pre-wrap;
+ word-break: break-word;
+}
+
+.communitycontentpage .comments-section {
+ align-self: stretch;
+ background-color: #f5f5f5;
+ border-radius: 15px;
+ display: flex;
+ flex-direction: column;
+ gap: 15px;
+ padding: 20px;
+ width: 100%;
+}
+
+.communitycontentpage .comments-header {
+ border-bottom: 1px solid #e0e0e0;
+ padding-bottom: 10px;
+}
+
+.communitycontentpage .comments-title {
+ font-family: "Inter", Helvetica;
+ font-size: 20px;
+ font-weight: 700;
+ color: #000000;
+ margin: 0;
+}
+
+.communitycontentpage .comments-list {
+ display: flex;
+ flex-direction: column;
+ gap: 15px;
+}
+
+.communitycontentpage .comment-item {
+ background-color: #ffffff;
+ border-radius: 10px;
+ padding: 15px;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.communitycontentpage .comment-author {
+ font-family: "Inter", Helvetica;
+ font-size: 14px;
+ font-weight: 700;
+ color: #f6910b;
+}
+
+.communitycontentpage .comment-content {
+ font-family: "Inter", Helvetica;
+ font-size: 14px;
+ font-weight: 500;
+ color: #000000;
+ line-height: 1.5;
+}
+
+.communitycontentpage .comment-date {
+ font-family: "Inter", Helvetica;
+ font-size: 12px;
+ font-weight: 500;
+ color: #999999;
+}
+
+.communitycontentpage .comment-form {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ margin-top: 10px;
+}
+
+.communitycontentpage .comment-input {
+ padding: 12px;
+ border: 1px solid #e0e0e0;
+ border-radius: 10px;
+ font-family: "Inter", Helvetica;
+ font-size: 14px;
+ resize: vertical;
+ transition: border-color 0.2s;
+}
+
+.communitycontentpage .comment-input:focus {
+ outline: none;
+ border-color: #f6910b;
+}
+
+.communitycontentpage .comment-input:disabled {
+ background-color: #f5f5f5;
+ cursor: not-allowed;
+}
+
+.communitycontentpage .comment-submit-btn {
+ align-self: flex-end;
+ padding: 10px 20px;
+ background-color: #f6910b;
+ border: none;
+ border-radius: 10px;
+ color: #ffffff;
+ cursor: pointer;
+ font-family: "Inter", Helvetica;
+ font-size: 14px;
+ font-weight: 700;
+ transition: background-color 0.2s;
+}
+
+.communitycontentpage .comment-submit-btn:hover:not(:disabled) {
+ background-color: #e58209;
+}
+
+.communitycontentpage .comment-submit-btn:disabled {
+ background-color: #cccccc;
+ cursor: not-allowed;
+}
+
+.communitycontentpage .div-4 {
+ align-items: center;
+ align-self: stretch;
+ border-color: #000000;
+ border-top-style: solid;
+ border-top-width: 1px;
+ display: flex;
+ flex: 0 0 auto;
+ flex-direction: column;
+ gap: 10px;
+ padding: 20px 10px;
+ position: relative;
+ width: 100%;
+}
+
+.communitycontentpage .related-posts-header {
+ align-self: stretch;
+ padding: 10px 0;
+ border-bottom: 1px solid #e0e0e0;
+ margin-bottom: 10px;
+}
+
+.communitycontentpage .related-posts-title {
+ font-family: "Inter", Helvetica;
+ font-size: 20px;
+ font-weight: 700;
+ color: #000000;
+ margin: 0;
+ text-align: center;
+}
+
+.communitycontentpage .design-component-instance-node {
+ flex: 0 0 auto !important;
+ left: unset !important;
+ margin-left: -1.00px !important;
+ margin-right: -1.00px !important;
+ margin-top: -1.00px !important;
+ top: unset !important;
+}
+
+.communitycontentpage .communitypage-2 {
+ flex: 0 0 auto !important;
+ left: unset !important;
+ margin-left: -1.00px !important;
+ margin-right: -1.00px !important;
+ top: unset !important;
+}
+
diff --git a/static/src/screens/CommunityContent/sections/HeaderWrapper/HeaderWrapper.jsx b/static/src/screens/CommunityContent/sections/HeaderWrapper/HeaderWrapper.jsx
new file mode 100644
index 0000000..d30f954
--- /dev/null
+++ b/static/src/screens/CommunityContent/sections/HeaderWrapper/HeaderWrapper.jsx
@@ -0,0 +1,62 @@
+import React, { useState } from "react";
+import { Link } from "react-router-dom";
+import { LoginPopup } from "../../../../components/LoginPopup";
+import { SignupPopup } from "../../../../components/SignupPopup";
+import "./style.css";
+
+export const HeaderWrapper = () => {
+ const [showLoginPopup, setShowLoginPopup] = useState(false);
+ const [showSignupPopup, setShowSignupPopup] = useState(false);
+
+ const handleSwitchToSignup = () => {
+ setShowLoginPopup(false);
+ setShowSignupPopup(true);
+ };
+
+ const handleSwitchToLogin = () => {
+ setShowSignupPopup(false);
+ setShowLoginPopup(true);
+ };
+
+ return (
+
+
+
+

+
+
+
+
+
+ 알고리즘
+
+ 셰프
+
+
+
+
+
+
+
+ {showLoginPopup && (
+ setShowLoginPopup(false)}
+ onSwitchToSignup={handleSwitchToSignup}
+ />
+ )}
+
+ {showSignupPopup && (
+ setShowSignupPopup(false)}
+ onSwitchToLogin={handleSwitchToLogin}
+ />
+ )}
+
+ );
+};
diff --git a/static/src/screens/CommunityContent/sections/HeaderWrapper/index.js b/static/src/screens/CommunityContent/sections/HeaderWrapper/index.js
new file mode 100644
index 0000000..2f50715
--- /dev/null
+++ b/static/src/screens/CommunityContent/sections/HeaderWrapper/index.js
@@ -0,0 +1 @@
+export { HeaderWrapper } from "./HeaderWrapper";
diff --git a/static/src/screens/CommunityContent/sections/HeaderWrapper/style.css b/static/src/screens/CommunityContent/sections/HeaderWrapper/style.css
new file mode 100644
index 0000000..749abf8
--- /dev/null
+++ b/static/src/screens/CommunityContent/sections/HeaderWrapper/style.css
@@ -0,0 +1,105 @@
+.header-wrapper {
+ align-items: center;
+ align-self: stretch;
+ background-color: transparent;
+ display: flex;
+ gap: 10px;
+ height: 120px;
+ padding: 10px;
+ position: relative;
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+.header-wrapper .icon-image-wrapper {
+ align-items: center;
+ display: flex;
+ flex: 1;
+ flex-grow: 1;
+ gap: 10px;
+ justify-content: flex-end;
+ overflow: hidden;
+ position: relative;
+ height: 100%;
+}
+
+.header-wrapper .icon-image-2 {
+ height: 80px;
+ width: auto;
+ display: block;
+ position: relative;
+}
+
+.header-wrapper .p {
+ 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;
+ position: relative;
+ text-align: center;
+ width: 271px;
+ height: 100%;
+}
+
+.header-wrapper .text-wrapper-3 {
+ color: #000000;
+ letter-spacing: -0.46px;
+}
+
+.header-wrapper .text-wrapper-4 {
+ color: #f6910b;
+ letter-spacing: -0.46px;
+}
+
+.header-wrapper .login-button-wrapper {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 1;
+ flex-grow: 1;
+ gap: 10px;
+ justify-content: flex-end;
+ padding: 10px;
+ position: relative;
+}
+
+.header-wrapper .login-text-wrapper {
+ 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;
+}
+
+.header-wrapper .login-text-wrapper:hover {
+ transform: scale(1.05);
+}
+
+.header-wrapper .login-text-2 {
+ 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;
+}
diff --git a/static/src/screens/CommunityContent/style.css b/static/src/screens/CommunityContent/style.css
new file mode 100644
index 0000000..ca2c8b3
--- /dev/null
+++ b/static/src/screens/CommunityContent/style.css
@@ -0,0 +1,9 @@
+.community-content {
+ align-items: center;
+ background-color: #ffffff;
+ display: flex;
+ flex-direction: column;
+ min-height: 1080px;
+ position: relative;
+ width: 100%;
+}
diff --git a/static/src/screens/CommunityPage/CommunityPage.jsx b/static/src/screens/CommunityPage/CommunityPage.jsx
new file mode 100644
index 0000000..fc190e3
--- /dev/null
+++ b/static/src/screens/CommunityPage/CommunityPage.jsx
@@ -0,0 +1,13 @@
+import React from "react";
+import { UnifiedHeader } from "../../components/UnifiedHeader";
+import { Communitypage } from "./sections/Communitypage";
+import "./style.css";
+
+export const CommunityPage = () => {
+ return (
+
+
+
+
+ );
+};
diff --git a/static/src/screens/CommunityPage/index.js b/static/src/screens/CommunityPage/index.js
new file mode 100644
index 0000000..b50944e
--- /dev/null
+++ b/static/src/screens/CommunityPage/index.js
@@ -0,0 +1 @@
+export { CommunityPage } from "./CommunityPage";
diff --git a/static/src/screens/CommunityPage/sections/Communitypage/Communitypage.jsx b/static/src/screens/CommunityPage/sections/Communitypage/Communitypage.jsx
new file mode 100644
index 0000000..3d44cf5
--- /dev/null
+++ b/static/src/screens/CommunityPage/sections/Communitypage/Communitypage.jsx
@@ -0,0 +1,130 @@
+import React, { useState, useEffect } from "react";
+import { Link, useNavigate } from "react-router-dom";
+import { useAuth } from "../../../../contexts/AuthContext";
+import { CommunitypageWrapper } from "../../../../components/CommunitypageWrapper";
+import { PageButton } from "../../../../components/PageButton";
+import "./style.css";
+
+const DEFAULT_POSTS = [
+ // TODO: Backend Integration: These would typically be fetched from the backend
+ { id: 1, title: "돼지고기 100g 나눔합니다.", author: "test3User", date: "2025-11-03", category: "나눔", content: "돼지고기를 너무 많이 샀네요 남는 돼지고기 나눔해요" },
+ { id: 2, title: "양파 2개 나눔해요", author: "user123", date: "2025-11-02", category: "나눔", content: "양파 2개 필요하신 분 가져가세요" },
+ { id: 3, title: "닭고기 500g 나눔", author: "foodlover", date: "2025-11-01", category: "나눔", content: "신선한 닭고기 나눔합니다" },
+ { id: 4, title: "고추장 새것 나눔합니다", author: "chef99", date: "2025-10-31", category: "나눔", content: "개봉 안한 고추장 드립니다" },
+ { id: 5, title: "양배추 한통 가져가세요", author: "veggie_fan", date: "2025-10-30", category: "나눔", content: "양배추 한통 나눔해요" },
+ { id: 6, title: "감자 5개 나눔", author: "potato_lover", date: "2025-10-29", category: "나눔", content: "감자 5개 드립니다" },
+ { id: 7, title: "당근 한봉지 드립니다", author: "healthy_eater", date: "2025-10-28", category: "나눔", content: "당근 한봉지 나눔합니다" },
+ { id: 8, title: "우유 1L 나눔해요", author: "milk_fan", date: "2025-10-27", category: "나눔", content: "우유 1L 필요하신 분" },
+ { id: 9, title: "계란 10개 나눔", author: "egg_master", date: "2025-10-26", category: "나눔", content: "계란 10개 드립니다" },
+ { id: 10, title: "토마토 나눔합니다", author: "tomato_grower", date: "2025-10-25", category: "나눔", content: "토마토 나눔해요" },
+ { id: 11, title: "버섯 한팩 드려요", author: "mushroom_fan", date: "2025-10-24", category: "나눔", content: "버섯 한팩 가져가세요" },
+ { id: 12, title: "파프리카 나눔", author: "veggie_lover", date: "2025-10-23", category: "나눔", content: "파프리카 나눔합니다" },
+ { id: 13, title: "두부 2모 나눔해요", author: "tofu_master", date: "2025-10-22", category: "나눔", content: "두부 2모 드립니다" },
+ { id: 14, title: "김치 나눔합니다", author: "kimchi_maker", date: "2025-10-21", category: "나눔", content: "김치 나눔해요" },
+ { id: 15, title: "된장 새것 드립니다", author: "ferment_pro", date: "2025-10-20", category: "나눔", content: "된장 새것 가져가세요" },
+ { id: 16, title: "참기름 나눔", author: "oil_collector", date: "2025-10-19", category: "나눔", content: "참기름 나눔합니다" },
+ { id: 17, title: "생선 나눔", author: "fish_lover", date: "2025-10-18", category: "나눔", content: "생선 나눔합니다" },
+ { id: 18, title: "쌀 나눔", author: "rice_farmer", date: "2025-10-17", category: "나눔", content: "쌀 나눔해요" },
+ { id: 19, title: "과일 나눔", author: "fruit_fan", date: "2025-10-16", category: "나눔", content: "과일 나눔합니다" },
+ { id: 20, title: "야채 나눔", author: "veggie_master", date: "2025-10-15", category: "나눔", content: "야채 나눔해요" },
+];
+
+const POSTS_PER_PAGE = 10;
+
+export const Communitypage = () => {
+ const navigate = useNavigate();
+ const { isAuthenticated } = useAuth();
+ const [currentPage, setCurrentPage] = useState(1);
+ const [allPosts, setAllPosts] = useState([]);
+
+ useEffect(() => {
+ // TODO: Backend Integration: Replace with API call to fetch all community posts
+ // Example: axios.get('/api/posts').then(response => setAllPosts(response.data));
+
+ const storedPosts = JSON.parse(localStorage.getItem("communityPosts") || "[]");
+ const combined = [...storedPosts, ...DEFAULT_POSTS];
+ setAllPosts(combined);
+ }, []);
+
+ const handleCreatePost = () => {
+ if (!isAuthenticated) {
+ alert("로그인이 필요합니다.");
+ return;
+ }
+ navigate("/createpost");
+ };
+
+ const totalPages = Math.min(Math.ceil(allPosts.length / POSTS_PER_PAGE), 5);
+ const startIndex = (currentPage - 1) * POSTS_PER_PAGE;
+ const endIndex = startIndex + POSTS_PER_PAGE;
+ const currentPosts = allPosts.slice(startIndex, endIndex);
+
+ const handlePageChange = (page) => {
+ if (page >= 1 && page <= totalPages) {
+ setCurrentPage(page);
+ }
+ };
+ return (
+
+
+
+
+ {currentPosts.map((post, index) => (
+
+ ))}
+
+
+
+
+
+
+ {Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => (
+
handlePageChange(page)}
+ />
+ ))}
+
+
+
+
+ );
+};
diff --git a/static/src/screens/CommunityPage/sections/Communitypage/index.js b/static/src/screens/CommunityPage/sections/Communitypage/index.js
new file mode 100644
index 0000000..35d0d64
--- /dev/null
+++ b/static/src/screens/CommunityPage/sections/Communitypage/index.js
@@ -0,0 +1 @@
+export { Communitypage } from "./Communitypage";
diff --git a/static/src/screens/CommunityPage/sections/Communitypage/style.css b/static/src/screens/CommunityPage/sections/Communitypage/style.css
new file mode 100644
index 0000000..3e5f9ef
--- /dev/null
+++ b/static/src/screens/CommunityPage/sections/Communitypage/style.css
@@ -0,0 +1,194 @@
+.communitypage {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ flex-grow: 1;
+ gap: 10px;
+ padding: 10px 64px;
+ position: relative;
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+.communitypage .communitypage-header {
+ align-items: flex-start;
+ align-self: stretch;
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ border-color: #000000;
+ display: flex;
+ flex: 0 0 auto;
+ flex-direction: column;
+ gap: 10px;
+ padding: 10px;
+ position: relative;
+ width: 100%;
+}
+
+.communitypage .div {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 48px;
+ font-weight: 700;
+ justify-content: center;
+ letter-spacing: -0.96px;
+ line-height: 57.6px;
+ margin-top: -1.00px;
+ position: relative;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+.communitypage .div-2 {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ flex-grow: 1;
+ gap: 10px;
+ max-height: 800px;
+ padding: 10px;
+ position: relative;
+ width: 100%;
+}
+
+.communitypage .communitypage-postheader {
+ align-self: stretch !important;
+ flex: 0 0 auto !important;
+ left: unset !important;
+ margin-left: -1.00px !important;
+ margin-right: -1.00px !important;
+ margin-top: -1.00px !important;
+ top: unset !important;
+ width: 100% !important;
+}
+
+.communitypage .communitypage-instance {
+ flex: 0 0 auto !important;
+ left: unset !important;
+ margin-left: -1.00px !important;
+ margin-right: -1.00px !important;
+ top: unset !important;
+}
+
+.communitypage .communitypage-postheader-instance {
+ flex: 0 0 auto !important;
+ left: unset !important;
+ margin-left: -1.00px !important;
+ margin-right: -1.00px !important;
+ overflow: hidden !important;
+ top: unset !important;
+}
+
+.communitypage .communitypage-wrapper-2 {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ justify-content: flex-end;
+ padding: 10px 0;
+ position: relative;
+ width: 100%;
+}
+
+.communitypage .communitypage-wrapper-3 {
+ align-items: center;
+ background-color: #f5900b;
+ border: none;
+ border-radius: 16px;
+ cursor: pointer;
+ display: inline-flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ justify-content: center;
+ overflow: hidden;
+ padding: 15px 20px;
+ position: relative;
+ text-decoration: none;
+ transition: background-color 0.2s, transform 0.2s;
+}
+
+.communitypage .communitypage-wrapper-3:hover {
+ background-color: #e58209;
+ transform: translateY(-2px);
+}
+
+.communitypage .text-wrapper-2 {
+ align-items: center;
+ color: #ffffff;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 700;
+ justify-content: center;
+ letter-spacing: -0.32px;
+ line-height: 19.2px;
+ margin-top: -1.00px;
+ position: relative;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+.communitypage .communitypage-nav {
+ align-items: center;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ justify-content: center;
+ padding: 20px 10px;
+ position: relative;
+ width: 317px;
+}
+
+.communitypage .page-nav-arrow {
+ background: none;
+ border: none;
+ cursor: pointer;
+ padding: 5px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: opacity 0.2s;
+}
+
+.communitypage .page-nav-arrow:disabled {
+ opacity: 0.3;
+ cursor: not-allowed;
+}
+
+.communitypage .page-nav-arrow:hover:not(:disabled) {
+ opacity: 0.7;
+}
+
+.communitypage .img {
+ height: 20px;
+ position: relative;
+ width: 20px;
+}
+
+.communitypage .page-button-container {
+ left: unset !important;
+ top: unset !important;
+}
+
+@media (max-width: 768px) {
+ .communitypage {
+ padding: 10px 20px;
+ }
+
+ .communitypage .div {
+ font-size: 32px;
+ line-height: 38.4px;
+ }
+
+ .communitypage .text-wrapper-2 {
+ font-size: 14px;
+ }
+}
diff --git a/static/src/screens/CommunityPage/sections/Header/Header.jsx b/static/src/screens/CommunityPage/sections/Header/Header.jsx
new file mode 100644
index 0000000..0129aa7
--- /dev/null
+++ b/static/src/screens/CommunityPage/sections/Header/Header.jsx
@@ -0,0 +1,101 @@
+import React, { useState } from "react";
+import { Link } from "react-router-dom";
+import { useAuth } from "../../../../contexts/AuthContext";
+import { LoginPopup } from "../../../../components/LoginPopup";
+import { SignupPopup } from "../../../../components/SignupPopup";
+import { PreferencesPopup } from "../../../../components/PreferencesPopup";
+import "./style.css";
+
+export const Header = () => {
+ 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 (
+
+ );
+};
diff --git a/static/src/screens/CommunityPage/sections/Header/index.js b/static/src/screens/CommunityPage/sections/Header/index.js
new file mode 100644
index 0000000..c940126
--- /dev/null
+++ b/static/src/screens/CommunityPage/sections/Header/index.js
@@ -0,0 +1 @@
+export { Header } from "./Header";
diff --git a/static/src/screens/CommunityPage/sections/Header/style.css b/static/src/screens/CommunityPage/sections/Header/style.css
new file mode 100644
index 0000000..6a13ba6
--- /dev/null
+++ b/static/src/screens/CommunityPage/sections/Header/style.css
@@ -0,0 +1,217 @@
+.header {
+ align-items: center;
+ background-color: transparent;
+ display: flex;
+ justify-content: space-between;
+ gap: 10px;
+ height: 120px;
+ padding: 10px;
+ position: relative;
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+.header .header-left-section {
+ display: flex;
+ align-items: center;
+ flex: 1;
+ justify-content: flex-start;
+}
+
+.header .icon-image {
+ height: 80px;
+ width: auto;
+ display: block;
+ position: relative;
+}
+
+.header .algorithm-label-link {
+ position: absolute;
+ left: 50%;
+ transform: translateX(-50%);
+ display: flex;
+ align-items: center;
+ height: 100%;
+}
+
+.header .algorithm-label {
+ 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;
+ position: relative;
+ text-align: center;
+ margin: 0;
+}
+
+.header .text-wrapper {
+ color: #000000;
+ letter-spacing: -0.46px;
+}
+
+.header .span {
+ color: #f6910b;
+ letter-spacing: -0.46px;
+}
+
+.header .header-right-section {
+ display: flex;
+ align-items: center;
+ flex: 1;
+ gap: 10px;
+ justify-content: flex-end;
+ padding: 10px;
+}
+
+.header .mypage-button {
+ 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;
+}
+
+.header .mypage-button:hover {
+ transform: scale(1.05);
+}
+
+.header .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;
+}
+
+.header .login-button {
+ 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;
+}
+
+.header .login-button:hover {
+ transform: scale(1.05);
+}
+
+.header .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;
+}
+
+.header .user-info-wrapper {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.header .user-name-display {
+ color: #000000;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 600;
+ padding: 8px 16px;
+ background-color: #f6910b1a;
+ border-radius: 20px;
+}
+
+.header .logout-button {
+ 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;
+}
+
+.header .logout-button:hover {
+ transform: scale(1.05);
+}
+
+@media (max-width: 768px) {
+ .header {
+ padding: 10px 5px;
+ height: auto;
+ min-height: 80px;
+ }
+
+ .header .icon-image {
+ height: 50px;
+ }
+
+ .header .algorithm-label {
+ font-size: 24px;
+ }
+
+ .header .mypage-button,
+ .header .login-button,
+ .header .logout-button {
+ width: 90px;
+ height: 32px;
+ }
+
+ .header .mypage-text,
+ .header .login-text {
+ font-size: 12px;
+ }
+
+ .header .user-name-display {
+ font-size: 12px;
+ padding: 4px 10px;
+ }
+}
+
+@media (max-width: 480px) {
+ .header .algorithm-label {
+ font-size: 20px;
+ }
+
+ .header .header-right-section {
+ gap: 5px;
+ padding: 5px;
+ }
+}
diff --git a/static/src/screens/CommunityPage/style.css b/static/src/screens/CommunityPage/style.css
new file mode 100644
index 0000000..20ece7e
--- /dev/null
+++ b/static/src/screens/CommunityPage/style.css
@@ -0,0 +1,23 @@
+.community-page {
+ align-items: center;
+ background-color: #ffffff;
+ display: flex;
+ flex-direction: column;
+ min-height: 1080px;
+ position: relative;
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+@media (max-width: 1280px) {
+ .community-page {
+ width: 100%;
+ }
+}
+
+@media (max-width: 768px) {
+ .community-page {
+ min-height: auto;
+ }
+}
diff --git a/static/src/screens/CreatePostPage/CreatePostPage.jsx b/static/src/screens/CreatePostPage/CreatePostPage.jsx
new file mode 100644
index 0000000..e92ec12
--- /dev/null
+++ b/static/src/screens/CreatePostPage/CreatePostPage.jsx
@@ -0,0 +1,99 @@
+import React, { useState, useEffect } from "react";
+import { Link, useNavigate } from "react-router-dom";
+import { useAuth } from "../../contexts/AuthContext";
+import { UnifiedHeader } from "../../components/UnifiedHeader";
+import "./style.css";
+
+export const CreatePostPage = () => {
+ const navigate = useNavigate();
+ const { user, isAuthenticated } = useAuth();
+ const [title, setTitle] = useState("");
+ const [content, setContent] = useState("");
+
+ useEffect(() => {
+ if (!isAuthenticated) {
+ alert("로그인이 필요합니다.");
+ navigate("/communitypage");
+ }
+ }, [isAuthenticated, navigate]);
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+
+ if (!isAuthenticated) {
+ alert("로그인이 필요합니다.");
+ return;
+ }
+
+ // TODO: Backend Integration: Replace with API call to create a new post
+ // Example: axios.post('/api/posts', newPost)
+
+ // Get existing posts (mock DB)
+ const existingPosts = JSON.parse(localStorage.getItem("communityPosts") || "[]");
+
+ // Create new post
+ const newPost = {
+ id: Date.now(), // Unique ID for the post
+ title,
+ content,
+ author: user.username,
+ date: new Date().toISOString().split('T')[0], // Current date
+ category: "나눔" // Default category
+ };
+
+ // Add to beginning of array (최상단)
+ const updatedPosts = [newPost, ...existingPosts];
+ localStorage.setItem("communityPosts", JSON.stringify(updatedPosts));
+
+ console.log("Creating post:", newPost);
+ navigate("/communitypage");
+ };
+
+ return (
+
+
+
+
+
+
새 글 작성
+
재료 나눔 게시판에 글을 작성하세요
+
+
+
+
+
+ );
+};
diff --git a/static/src/screens/CreatePostPage/index.js b/static/src/screens/CreatePostPage/index.js
new file mode 100644
index 0000000..b99e657
--- /dev/null
+++ b/static/src/screens/CreatePostPage/index.js
@@ -0,0 +1 @@
+export { CreatePostPage } from "./CreatePostPage";
diff --git a/static/src/screens/CreatePostPage/style.css b/static/src/screens/CreatePostPage/style.css
new file mode 100644
index 0000000..88bf770
--- /dev/null
+++ b/static/src/screens/CreatePostPage/style.css
@@ -0,0 +1,180 @@
+.create-post-page {
+ align-items: center;
+ background-color: #ffffff;
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ position: relative;
+ width: 100%;
+}
+
+.create-post-page .create-post-header {
+ align-self: stretch !important;
+ width: 100% !important;
+ max-width: 1280px !important;
+ margin: 0 auto !important;
+}
+
+.create-post-content {
+ align-items: flex-start;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ gap: 30px;
+ padding: 20px 64px;
+ position: relative;
+ width: 100%;
+ max-width: 900px;
+ margin: 0 auto;
+}
+
+.create-post-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%;
+}
+
+.create-post-title {
+ color: #000000;
+ font-family: "Inter", Helvetica;
+ font-size: 48px;
+ font-weight: 700;
+ letter-spacing: -0.96px;
+ line-height: 57.6px;
+ margin: 0;
+}
+
+.create-post-subtitle {
+ color: #0000008c;
+ font-family: "Inter", Helvetica;
+ font-size: 20px;
+ font-weight: 500;
+ letter-spacing: -0.10px;
+ line-height: 29.0px;
+ margin: 0;
+}
+
+.create-post-form {
+ align-self: stretch;
+ display: flex;
+ flex-direction: column;
+ gap: 25px;
+ width: 100%;
+}
+
+.create-post-input-group {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.create-post-label {
+ color: #000000;
+ font-family: "Inter", Helvetica;
+ font-size: 18px;
+ font-weight: 600;
+}
+
+.create-post-input {
+ padding: 12px 20px;
+ border: 1px solid #e0e0e0;
+ border-radius: 12px;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ transition: border-color 0.2s;
+}
+
+.create-post-input:focus {
+ outline: none;
+ border-color: #f6910b;
+}
+
+.create-post-textarea {
+ padding: 12px 20px;
+ border: 1px solid #e0e0e0;
+ border-radius: 12px;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ resize: vertical;
+ transition: border-color 0.2s;
+}
+
+.create-post-textarea:focus {
+ outline: none;
+ border-color: #f6910b;
+}
+
+.create-post-footer {
+ display: flex;
+ gap: 15px;
+ justify-content: flex-end;
+ padding-top: 20px;
+}
+
+.create-post-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;
+}
+
+.create-post-cancel-btn:hover {
+ background-color: #d0d0d0;
+}
+
+.create-post-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;
+}
+
+.create-post-submit-btn:hover {
+ background-color: #e58209;
+}
+
+@media (max-width: 768px) {
+ .create-post-content {
+ padding: 20px 20px;
+ }
+
+ .create-post-title {
+ font-size: 32px;
+ line-height: 38.4px;
+ }
+
+ .create-post-subtitle {
+ font-size: 16px;
+ line-height: 23.2px;
+ }
+
+ .create-post-footer {
+ flex-direction: column;
+ }
+
+ .create-post-cancel-btn,
+ .create-post-submit-btn {
+ width: 100%;
+ }
+}
diff --git a/static/src/screens/Desktop/Desktop.jsx b/static/src/screens/Desktop/Desktop.jsx
new file mode 100644
index 0000000..599f6c4
--- /dev/null
+++ b/static/src/screens/Desktop/Desktop.jsx
@@ -0,0 +1,73 @@
+import React, { useState, useEffect } from "react";
+import { useLocation } from "react-router-dom"; // Import useLocation
+import { UnifiedHeader } from "../../components/UnifiedHeader";
+import { Community } from "./sections/Community";
+import { Footer } from "./sections/Footer";
+import { IngredientInventory } from "./sections/IngredientInventory";
+import { MenuInventory } from "./sections/MenuInventory";
+import "./style.css";
+
+const INITIAL_INGREDIENTS = [
+ { id: 1, name: "양파", category: "채소류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-1.png", expiryDays: 5 },
+ { id: 2, name: "닭고기", category: "육류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png", expiryDays: 3 },
+ { id: 3, name: "고추장", category: "가공류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png", expiryDays: 30 },
+ { id: 4, name: "고춧가루", category: "가공류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png", expiryDays: 60 },
+ { id: 5, name: "양배추", category: "채소류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png", expiryDays: 7 },
+ { id: 6, name: "스테비아", category: "가공류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png", expiryDays: 90 },
+ { id: 7, name: "마늘", category: "채소류", image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png", expiryDays: 14 },
+];
+
+export const Desktop = () => {
+ const [ingredients, setIngredients] = useState([]);
+ const location = useLocation(); // Initialize useLocation
+
+ useEffect(() => {
+ // This effect will run on mount and when location changes,
+ // ensuring fresh data when returning from AddIngredientPage
+ const storedIngredients = JSON.parse(localStorage.getItem("userIngredients") || "[]");
+ if (storedIngredients.length > 0) {
+ setIngredients(storedIngredients);
+ } else {
+ setIngredients(INITIAL_INGREDIENTS);
+ localStorage.setItem("userIngredients", JSON.stringify(INITIAL_INGREDIENTS));
+ }
+ }, [location.pathname]); // Re-run when pathname changes
+
+ const handleAddIngredient = (newIngredient) => {
+ setIngredients((prevIngredients) => {
+ const updatedIngredients = [...prevIngredients, newIngredient];
+ localStorage.setItem("userIngredients", JSON.stringify(updatedIngredients));
+ // TODO: Backend Integration: Send newIngredient to backend
+ console.log("Added ingredient to mock DB:", newIngredient);
+ return updatedIngredients;
+ });
+ };
+
+ const handleAddMultipleIngredients = (newIngredients) => {
+ setIngredients((prevIngredients) => {
+ const updatedIngredients = [...prevIngredients, ...newIngredients];
+ localStorage.setItem("userIngredients", JSON.stringify(updatedIngredients));
+ // TODO: Backend Integration: Send newIngredients to backend
+ console.log("Added multiple ingredients to mock DB:", newIngredients);
+ return updatedIngredients;
+ });
+ };
+
+ return (
+
+ );
+};
diff --git a/static/src/screens/Desktop/index.js b/static/src/screens/Desktop/index.js
new file mode 100644
index 0000000..4d75c1e
--- /dev/null
+++ b/static/src/screens/Desktop/index.js
@@ -0,0 +1 @@
+export { Desktop } from "./Desktop";
diff --git a/static/src/screens/Desktop/sections/Community/Community.jsx b/static/src/screens/Desktop/sections/Community/Community.jsx
new file mode 100644
index 0000000..49a6158
--- /dev/null
+++ b/static/src/screens/Desktop/sections/Community/Community.jsx
@@ -0,0 +1,38 @@
+import React from "react";
+import { Link } from "react-router-dom";
+import { CommunitypageWrapper } from "../../../../components/CommunitypageWrapper";
+import "./style.css";
+
+const MOCK_POSTS = [
+ // TODO: Backend Integration: Replace with API call to fetch recent community posts
+ { id: 1, title: "돼지고기 100g 나눔합니다.", author: "test3User", date: "2025-11-03", category: "나눔", content: "돼지고기를 너무 많이 샀네요 남는 돼지고기 나눔해요" },
+ { id: 2, title: "양파 2개 나눔해요", author: "user123", date: "2025-11-02", category: "나눔", content: "양파 2개 필요하신 분 가져가세요" },
+ { id: 3, title: "닭고기 500g 나눔", author: "foodlover", date: "2025-11-01", category: "나눔", content: "신선한 닭고기 나눔합니다" },
+ { id: 4, title: "고추장 새것 나눔합니다", author: "chef99", date: "2025-10-31", category: "나눔", content: "개봉 안한 고추장 드립니다" },
+ { id: 5, title: "양배추 한통 가져가세요", author: "veggie_fan", date: "2025-10-30", category: "나눔", content: "양배추 한통 나눔해요" },
+];
+
+export const Community = () => {
+ return (
+
+
+
재료 나눔 게시판
+
+
+
+ {MOCK_POSTS.map((post, index) => (
+
+ ))}
+
+
+ );
+};
diff --git a/static/src/screens/Desktop/sections/Community/index.js b/static/src/screens/Desktop/sections/Community/index.js
new file mode 100644
index 0000000..95a905a
--- /dev/null
+++ b/static/src/screens/Desktop/sections/Community/index.js
@@ -0,0 +1 @@
+export { Community } from "./Community";
diff --git a/static/src/screens/Desktop/sections/Community/style.css b/static/src/screens/Desktop/sections/Community/style.css
new file mode 100644
index 0000000..2e024bd
--- /dev/null
+++ b/static/src/screens/Desktop/sections/Community/style.css
@@ -0,0 +1,83 @@
+.community {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ flex-direction: column;
+ gap: 10px;
+ padding: 10px 64px;
+ position: relative;
+ width: 100%;
+}
+
+.community .community-header {
+ align-self: stretch;
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ border-color: #000000b2;
+ display: flex;
+ flex: 0 0 auto;
+ padding: 10px 0;
+ position: relative;
+ width: 100%;
+}
+
+.community .community-header-2 {
+ align-items: center;
+ 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;
+ white-space: nowrap;
+}
+
+@media (max-width: 768px) {
+ .community .community-header-2 {
+ font-size: 32px;
+ line-height: 38.4px;
+ }
+}
+
+.community .community-contents {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ flex-direction: column;
+ gap: 10px;
+ max-height: 300px;
+ overflow: hidden;
+ padding: 10px 20px;
+ position: relative;
+ width: 100%;
+}
+
+.community .communitypage-3 {
+ flex: 0 0 auto !important;
+ left: unset !important;
+ margin-left: -1.00px !important;
+ margin-right: -21.00px !important;
+ margin-top: -1.00px !important;
+ top: unset !important;
+}
+
+.community .communitypage-4 {
+ flex: 0 0 auto !important;
+ left: unset !important;
+ margin-left: -1.00px !important;
+ margin-right: -21.00px !important;
+ top: unset !important;
+}
+
+.community .communitypage-5 {
+ flex: 0 0 auto !important;
+ left: unset !important;
+ margin-bottom: -1.00px !important;
+ margin-left: -1.00px !important;
+ margin-right: -21.00px !important;
+ top: unset !important;
+}
diff --git a/static/src/screens/Desktop/sections/Footer/Footer.jsx b/static/src/screens/Desktop/sections/Footer/Footer.jsx
new file mode 100644
index 0000000..c6a7cd0
--- /dev/null
+++ b/static/src/screens/Desktop/sections/Footer/Footer.jsx
@@ -0,0 +1,42 @@
+import React from "react";
+import "./style.css";
+
+export const Footer = () => {
+ return (
+
+ );
+};
diff --git a/static/src/screens/Desktop/sections/Footer/index.js b/static/src/screens/Desktop/sections/Footer/index.js
new file mode 100644
index 0000000..e5ea0e5
--- /dev/null
+++ b/static/src/screens/Desktop/sections/Footer/index.js
@@ -0,0 +1 @@
+export { Footer } from "./Footer";
diff --git a/static/src/screens/Desktop/sections/Footer/style.css b/static/src/screens/Desktop/sections/Footer/style.css
new file mode 100644
index 0000000..9ff750f
--- /dev/null
+++ b/static/src/screens/Desktop/sections/Footer/style.css
@@ -0,0 +1,156 @@
+.footer {
+ align-items: center;
+ align-self: stretch;
+ background-color: transparent;
+ border-radius: 20px;
+ display: flex;
+ gap: 96px;
+ min-height: 266px;
+ justify-content: space-around;
+ padding: 20px 64px;
+ position: relative;
+ width: 100%;
+ margin-top: 40px;
+}
+
+.footer .footer-container {
+ align-items: flex-start;
+ align-self: stretch;
+ border-color: #000000b2;
+ border-top-style: solid;
+ border-top-width: 1px;
+ display: flex;
+ flex: 1;
+ flex-grow: 1;
+ gap: 120px;
+ padding: 20px 0px;
+ position: relative;
+}
+
+.footer .footer-left-content {
+ align-items: flex-start;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ flex-grow: 1;
+ gap: 10px;
+ position: relative;
+}
+
+.footer .company-container {
+ align-items: flex-start;
+ display: inline-flex;
+ flex: 0 0 auto;
+ gap: 8px;
+ position: relative;
+}
+
+.footer .company-text {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 24px;
+ font-weight: 600;
+ justify-content: center;
+ letter-spacing: -0.48px;
+ line-height: 34.8px;
+ margin-top: -1.00px;
+ position: relative;
+ text-align: center;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+.footer .developer-container {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ justify-content: center;
+ padding: 0px 10px;
+ position: relative;
+ width: 100%;
+}
+
+.footer .developer-text {
+ align-items: flex-start;
+ color: #0000008c;
+ display: flex;
+ flex: 1;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 500;
+ justify-content: flex-start;
+ letter-spacing: -0.08px;
+ line-height: 23.2px;
+ margin-top: -1.00px;
+ position: relative;
+ text-align: left;
+}
+
+@media (max-width: 768px) {
+ .footer .developer-text {
+ font-size: 14px;
+ line-height: 20.3px;
+ }
+}
+
+.footer .footer-right-content {
+ align-items: flex-start;
+ display: inline-flex;
+ flex: 0 0 auto;
+ gap: 40px;
+ position: relative;
+}
+
+.footer .support-container {
+ align-items: flex-end;
+ align-self: stretch;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ justify-content: center;
+ position: relative;
+ width: 130px;
+}
+
+.footer .support-header {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 4px;
+ padding: 0px 0px 16px;
+ position: relative;
+ width: 100%;
+}
+
+.footer .support-text {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 600;
+ justify-content: center;
+ letter-spacing: -0.08px;
+ line-height: 23.2px;
+ margin-top: -1.00px;
+ position: relative;
+ width: 63px;
+}
+
+.footer .text-wrapper-8 {
+ align-items: center;
+ align-self: stretch;
+ color: #0000008c;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 500;
+ justify-content: center;
+ letter-spacing: -0.08px;
+ line-height: 23.2px;
+ position: relative;
+}
diff --git a/static/src/screens/Desktop/sections/HeaderInstanceWrapper/HeaderInstanceWrapper.jsx b/static/src/screens/Desktop/sections/HeaderInstanceWrapper/HeaderInstanceWrapper.jsx
new file mode 100644
index 0000000..737b9bb
--- /dev/null
+++ b/static/src/screens/Desktop/sections/HeaderInstanceWrapper/HeaderInstanceWrapper.jsx
@@ -0,0 +1,7 @@
+import React from "react";
+import { DivWrapper } from "../../../../components/DivWrapper";
+import "./style.css";
+
+export const HeaderInstanceWrapper = () => {
+ return ;
+};
diff --git a/static/src/screens/Desktop/sections/HeaderInstanceWrapper/index.js b/static/src/screens/Desktop/sections/HeaderInstanceWrapper/index.js
new file mode 100644
index 0000000..05c0f61
--- /dev/null
+++ b/static/src/screens/Desktop/sections/HeaderInstanceWrapper/index.js
@@ -0,0 +1 @@
+export { HeaderInstanceWrapper } from "./HeaderInstanceWrapper";
diff --git a/static/src/screens/Desktop/sections/HeaderInstanceWrapper/style.css b/static/src/screens/Desktop/sections/HeaderInstanceWrapper/style.css
new file mode 100644
index 0000000..43b0cd3
--- /dev/null
+++ b/static/src/screens/Desktop/sections/HeaderInstanceWrapper/style.css
@@ -0,0 +1,7 @@
+.header-instance {
+ align-self: stretch !important;
+ background-color: transparent !important;
+ width: 100% !important;
+ max-width: 1280px !important;
+ margin: 0 auto !important;
+}
diff --git a/static/src/screens/Desktop/sections/IngredientInventory/IngredientInventory.jsx b/static/src/screens/Desktop/sections/IngredientInventory/IngredientInventory.jsx
new file mode 100644
index 0000000..313ccd9
--- /dev/null
+++ b/static/src/screens/Desktop/sections/IngredientInventory/IngredientInventory.jsx
@@ -0,0 +1,97 @@
+import React, { useState } from "react";
+import { Link } from "react-router-dom";
+import { IngredientComponent } from "../../../../components/IngredientComponent";
+import { IngredientInventoryWrapper } from "../../../../components/IngredientInventoryWrapper";
+import { AddIngredientPopup } from "../../../../components/AddIngredientPopup";
+import "./style.css";
+
+const CATEGORIES = ["전체", "육류", "채소류", "가공류", "유제품", "기타"];
+
+export const IngredientInventory = ({ ingredients, onAddIngredient }) => { // Removed onAddMultipleIngredients from props
+ const [selectedCategory, setSelectedCategory] = useState("전체");
+ const [showCategoryMenu, setShowCategoryMenu] = useState(false);
+ const [showAddPopup, setShowAddPopup] = useState(false);
+
+ const filteredIngredients = selectedCategory === "전체"
+ ? ingredients
+ : ingredients.filter(item => item.category === selectedCategory);
+
+ return (
+
+
+
+
+ {showCategoryMenu && (
+
+ {CATEGORIES.map((category) => (
+
+ ))}
+
+ )}
+
+
+
+ {filteredIngredients.map((ingredient) => (
+
+ ))}
+
+
+
+
+
+

+
+
+
+
+
+
+
+ {showAddPopup && (
+
setShowAddPopup(false)}
+ onAdd={onAddIngredient} // Use the prop function
+ />
+ )}
+
+ );
+};
diff --git a/static/src/screens/Desktop/sections/IngredientInventory/index.js b/static/src/screens/Desktop/sections/IngredientInventory/index.js
new file mode 100644
index 0000000..3bd227e
--- /dev/null
+++ b/static/src/screens/Desktop/sections/IngredientInventory/index.js
@@ -0,0 +1 @@
+export { IngredientInventory } from "./IngredientInventory";
diff --git a/static/src/screens/Desktop/sections/IngredientInventory/style.css b/static/src/screens/Desktop/sections/IngredientInventory/style.css
new file mode 100644
index 0000000..87c83a9
--- /dev/null
+++ b/static/src/screens/Desktop/sections/IngredientInventory/style.css
@@ -0,0 +1,262 @@
+.ingredient-inventory {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ flex-direction: column;
+ gap: 10px;
+ padding: 25px 64px;
+ position: relative;
+ width: 100%;
+}
+
+@media (max-width: 768px) {
+ .ingredient-inventory {
+ padding: 20px 20px;
+ }
+}
+
+.ingredient-inventory .ingredient-inventory-header {
+ align-self: stretch !important;
+ flex: 0 0 auto !important;
+ left: unset !important;
+ top: unset !important;
+ width: 100% !important;
+}
+
+.ingredient-inventory .ingredient-category-wrapper {
+ position: relative;
+ display: inline-flex;
+ flex: 0 0 auto;
+}
+
+.ingredient-inventory .ingredient-category-button {
+ align-items: center;
+ background-color: #f6910b;
+ border: none;
+ border-radius: 16px;
+ cursor: pointer;
+ display: flex;
+ gap: 8px;
+ justify-content: center;
+ overflow: hidden;
+ padding: 8px 25px;
+ position: relative;
+ transition: background-color 0.2s;
+}
+
+.ingredient-inventory .ingredient-category-button:hover {
+ background-color: #e58209;
+}
+
+.ingredient-inventory .ingredient-category-text {
+ align-items: center;
+ color: #ffffff;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 18px;
+ font-weight: 500;
+ justify-content: center;
+ letter-spacing: -0.09px;
+ line-height: 26.1px;
+ text-align: center;
+ white-space: nowrap;
+}
+
+.ingredient-inventory .ingredient-category-arrow {
+ color: #ffffff;
+ font-size: 12px;
+}
+
+.ingredient-inventory .ingredient-category-menu {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ margin-top: 5px;
+ background-color: #ffffff;
+ border: 1px solid #0000001a;
+ border-radius: 12px;
+ box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1);
+ overflow: hidden;
+ z-index: 10;
+ min-width: 120px;
+}
+
+.ingredient-inventory .ingredient-category-menu-item {
+ width: 100%;
+ padding: 10px 20px;
+ background: none;
+ border: none;
+ text-align: left;
+ cursor: pointer;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 500;
+ color: #000000;
+ transition: background-color 0.2s;
+}
+
+.ingredient-inventory .ingredient-category-menu-item:hover {
+ background-color: #f5f5f5;
+}
+
+.ingredient-inventory .ingredient-category-menu-item.active {
+ background-color: #f6910b1a;
+ color: #f6910b;
+ font-weight: 600;
+}
+
+.ingredient-inventory .ingrdient-invnetory {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ gap: 32px;
+ height: 120px;
+ overflow: hidden;
+ overflow-x: scroll;
+ padding: 0px 15px;
+ position: relative;
+ width: 100%;
+}
+
+.ingredient-inventory .ingrdient-invnetory::-webkit-scrollbar {
+ display: none;
+ width: 0;
+}
+
+.ingredient-inventory .ingredient-component-instance {
+ left: unset !important;
+}
+
+.ingredient-inventory .add-new-ingredient {
+ align-items: center;
+ background-color: #ffffff;
+ border: 1px solid;
+ border-color: #0000001a;
+ border-radius: 16px;
+ box-shadow: 0px 12px 32px #0000000a, 0px 4px 8px #00000005;
+ cursor: pointer;
+ display: flex;
+ flex-direction: column;
+ gap: 15px;
+ height: 120px;
+ justify-content: center;
+ max-height: 120px;
+ min-width: 140px;
+ overflow: hidden;
+ padding: 20px 10px;
+ position: relative;
+ flex-shrink: 0;
+ transition: transform 0.2s, box-shadow 0.2s;
+ text-decoration: none;
+}
+
+.ingredient-inventory .add-new-ingredient:hover {
+ transform: translateY(-2px);
+ box-shadow: 0px 16px 40px #0000001a, 0px 6px 12px #0000000a;
+}
+
+.ingredient-inventory .receipt-register-btn {
+ align-items: center;
+ background-color: #f6910b;
+ border: none;
+ border-radius: 16px;
+ box-shadow: 0px 12px 32px #0000000a, 0px 4px 8px #00000005;
+ cursor: pointer;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ height: 120px;
+ justify-content: center;
+ min-width: 140px;
+ overflow: hidden;
+ padding: 20px 10px;
+ position: relative;
+ flex-shrink: 0;
+ transition: transform 0.2s, box-shadow 0.2s;
+}
+
+.ingredient-inventory .receipt-register-btn:hover {
+ transform: translateY(-2px);
+ box-shadow: 0px 16px 40px #0000001a, 0px 6px 12px #0000000a;
+ background-color: #e58209;
+}
+
+.ingredient-inventory .receipt-register-container {
+ align-items: center;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.ingredient-inventory .receipt-register-text {
+ color: #ffffff;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 700;
+ text-align: center;
+ white-space: nowrap;
+}
+
+.ingredient-inventory .receipt-register-icon {
+ width: 32px;
+ height: 32px;
+ filter: brightness(0) invert(1);
+}
+
+.ingredient-inventory .add-new-container {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ flex-grow: 1;
+ gap: 8px;
+ position: relative;
+ width: 100%;
+}
+
+.ingredient-inventory .add-new-header {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ justify-content: center;
+ position: relative;
+ width: 100%;
+}
+
+.ingredient-inventory .add-new-header-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;
+ margin-top: -1.00px;
+ position: relative;
+ text-align: center;
+ width: 74px;
+}
+
+.ingredient-inventory .add-new-section {
+ 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-inventory .add-new-icon {
+ align-self: stretch;
+ aspect-ratio: 1;
+ position: relative;
+}
diff --git a/static/src/screens/Desktop/sections/MenuInventory/MenuInventory.jsx b/static/src/screens/Desktop/sections/MenuInventory/MenuInventory.jsx
new file mode 100644
index 0000000..32785cb
--- /dev/null
+++ b/static/src/screens/Desktop/sections/MenuInventory/MenuInventory.jsx
@@ -0,0 +1,115 @@
+import React, { useState, useEffect } from "react";
+import { Link, useNavigate } from "react-router-dom";
+import { MenuInventoryWrapper } from "../../../../components/MenuInventoryWrapper";
+import { Tendency } from "../../../../components/Tendency";
+import { UsedIngredient } from "../../../../components/UsedIngredient";
+import "./style.css";
+
+const MOCK_RECIPES = [
+ {
+ id: 1,
+ name: "저당 닭갈비",
+ ingredients: ["닭고기", "양파", "고추장", "고춧가루", "양배추", "스테비아", "마늘"],
+ steps: ["닭고기 손질 및 양념 재우기", "야채 손질", "볶기"],
+ tendencies: ["매콤칼칼", "스트레스 해소", "저당"]
+ },
+ {
+ id: 2,
+ name: "저당 닭볶음탕",
+ ingredients: ["닭고기", "감자", "당근", "양파", "고추장"],
+ steps: ["닭고기 손질 및 초벌", "양념장 만들기", "끓이기", "채소 넣고 마무리"],
+ tendencies: ["매콤칼칼", "저당"]
+ }
+];
+
+const AVAILABLE_TENDENCIES = ["매콤칼칼", "스트레스 해소", "저당", "담백", "건강식"];
+
+export const MenuInventory = () => {
+ const navigate = useNavigate();
+ const [selectedTendencies, setSelectedTendencies] = useState([]);
+ const [filteredRecipes, setFilteredRecipes] = useState([]);
+
+ // TODO: Backend Integration: Fetch user's ingredients and preferences for initial recommendation
+ useEffect(() => {
+ // Simulate fetching initial ingredient-based recommendations
+ // Replace with actual API call to get recommended recipes
+ setFilteredRecipes(MOCK_RECIPES.slice(0, 2)); // Show a couple of default recipes
+ }, []);
+
+ const toggleTendency = (tendency) => {
+ setSelectedTendencies(prev => {
+ const newTendencies = prev.includes(tendency)
+ ? prev.filter(t => t !== tendency)
+ : [...prev, tendency];
+
+ // TODO: Backend Integration: Fetch recipes based on newTendencies
+ const updatedRecipes = MOCK_RECIPES.filter(recipe =>
+ newTendencies.length === 0 || newTendencies.some(t => recipe.tendencies.includes(t))
+ );
+ setFilteredRecipes(updatedRecipes.slice(0, 2)); // Limit to 2 for main screen
+ return newTendencies;
+ });
+ };
+
+ return (
+
+
+
+
+
+
+ 더보기 →
+
+
+
+ {AVAILABLE_TENDENCIES.map((tendency) => (
+ toggleTendency(tendency)}
+ />
+ ))}
+
+
+
+ {filteredRecipes.map((recipe) => (
+
+
+
+
+
+ {recipe.ingredients.map((ingredient, index) => (
+
+ ))}
+
+
+
+
+ {recipe.steps.join('\n')}
+
+
+

+
+
+
+ ))}
+
+
+ );
+};
diff --git a/static/src/screens/Desktop/sections/MenuInventory/index.js b/static/src/screens/Desktop/sections/MenuInventory/index.js
new file mode 100644
index 0000000..be34bd4
--- /dev/null
+++ b/static/src/screens/Desktop/sections/MenuInventory/index.js
@@ -0,0 +1 @@
+export { MenuInventory } from "./MenuInventory";
diff --git a/static/src/screens/Desktop/sections/MenuInventory/style.css b/static/src/screens/Desktop/sections/MenuInventory/style.css
new file mode 100644
index 0000000..ef0c8fa
--- /dev/null
+++ b/static/src/screens/Desktop/sections/MenuInventory/style.css
@@ -0,0 +1,245 @@
+.menu-inventory {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ flex-direction: column;
+ gap: 10px;
+ justify-content: center;
+ padding: 25px 64px;
+ position: relative;
+ width: 100%;
+}
+
+.menu-inventory .menu-inventory-header-wrapper {
+ align-self: stretch;
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-end;
+ width: 100%;
+}
+
+.menu-inventory .menu-inventory-header-link {
+ text-decoration: none;
+ color: inherit;
+}
+
+.menu-inventory .design-component-instance-node-2 {
+ flex: 0 0 auto !important;
+ left: unset !important;
+ top: unset !important;
+}
+
+.menu-inventory .menu-more-btn {
+ padding: 8px 20px;
+ background-color: #f6910b;
+ border-radius: 12px;
+ color: #ffffff;
+ cursor: pointer;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 600;
+ text-decoration: none;
+ transition: all 0.2s;
+ display: flex;
+ align-items: center;
+ gap: 5px;
+}
+
+.menu-inventory .menu-more-btn:hover {
+ background-color: #e58209;
+ transform: translateX(3px);
+}
+
+.menu-inventory .tendency-inventory {
+ align-items: center;
+ display: inline-flex;
+ flex: 0 0 auto;
+ gap: 8px;
+ position: relative;
+}
+
+.menu-inventory .tendency-instance {
+ margin-left: -14.50px !important;
+}
+
+.menu-inventory .tendency-2 {
+ margin-left: unset !important;
+}
+
+.menu-inventory .div-6 {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 32px;
+ overflow-x: scroll;
+ position: relative;
+ width: 100%;
+}
+
+.menu-inventory .div-6::-webkit-scrollbar {
+ display: none;
+ width: 0;
+}
+
+.menu-inventory .recipe {
+ align-items: flex-start;
+ background-color: #ffffff;
+ border: 1px solid;
+ border-color: #0000001a;
+ border-radius: 16px;
+ box-shadow: 0px 6px 12px #00000008, 0px 4px 8px #00000005;
+ display: flex;
+ flex-direction: column;
+ height: 140px;
+ justify-content: center;
+ max-width: 240px;
+ overflow: hidden;
+ padding: 8px 10px;
+ position: relative;
+ width: 240px;
+}
+
+.menu-inventory .recipe-text {
+ align-items: flex-end;
+ align-self: stretch;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ flex-grow: 1;
+ gap: 2px;
+ padding: 3px;
+ position: relative;
+ width: 100%;
+}
+
+.menu-inventory .recipe-name {
+ align-items: center;
+ align-self: stretch;
+ border-radius: 15px;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ justify-content: center;
+ overflow: hidden;
+ padding: 0px 5px;
+ position: relative;
+ width: 100%;
+}
+
+.menu-inventory .recipe-name-text {
+ align-items: center;
+ color: #000000e6;
+ display: flex;
+ flex: 1;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 500;
+ justify-content: center;
+ letter-spacing: -0.08px;
+ line-height: 22.4px;
+ margin-top: -1.00px;
+ position: relative;
+}
+
+.menu-inventory .use-ingredient {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 2px;
+ overflow: hidden;
+ overflow-x: scroll;
+ padding: 2px 4px;
+ position: relative;
+ width: 100%;
+}
+
+.menu-inventory .use-ingredient::-webkit-scrollbar {
+ display: none;
+ width: 0;
+}
+
+.menu-inventory .used-ingredient-instance {
+ align-self: stretch !important;
+ flex: 0 0 auto !important;
+ height: unset !important;
+ left: unset !important;
+ top: unset !important;
+}
+
+.menu-inventory .recipe-container {
+ align-items: center;
+ align-self: stretch;
+ background-color: #0000001a;
+ border-radius: 16px;
+ display: flex;
+ flex: 1;
+ flex-grow: 1;
+ gap: 5px;
+ justify-content: center;
+ padding: 5px 10px;
+ position: relative;
+ width: 100%;
+}
+
+.menu-inventory .recipe-text-2 {
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 4;
+ align-items: center;
+ color: #0000008c;
+ display: -webkit-box;
+ flex: 1;
+ font-family: "Inter", Helvetica;
+ font-size: 11px;
+ font-weight: 500;
+ justify-content: center;
+ letter-spacing: -0.06px;
+ line-height: 15.4px;
+ margin-bottom: -1.50px;
+ margin-top: -3.50px;
+ overflow: hidden;
+ position: relative;
+ text-overflow: ellipsis;
+}
+
+.menu-inventory .recipe-extension {
+ height: 24px;
+ position: relative;
+ width: 24px;
+}
+
+.menu-inventory .recipe-container-2 {
+ align-items: center;
+ align-self: stretch;
+ background-color: #0000001a;
+ border-radius: 16px;
+ display: flex;
+ flex: 1;
+ flex-grow: 1;
+ gap: 5px;
+ justify-content: center;
+ padding: 3px 10px;
+ position: relative;
+ width: 100%;
+}
+
+.menu-inventory .text-wrapper-7 {
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 4;
+ align-items: center;
+ color: #0000008c;
+ display: -webkit-box;
+ flex: 1;
+ font-family: "Inter", Helvetica;
+ font-size: 11px;
+ font-weight: 500;
+ justify-content: center;
+ letter-spacing: -0.06px;
+ line-height: 15.4px;
+ margin-top: -1.50px;
+ overflow: hidden;
+ position: relative;
+ text-overflow: ellipsis;
+}
diff --git a/static/src/screens/Desktop/style.css b/static/src/screens/Desktop/style.css
new file mode 100644
index 0000000..e790d15
--- /dev/null
+++ b/static/src/screens/Desktop/style.css
@@ -0,0 +1,59 @@
+.desktop {
+ align-items: center;
+ background-color: #ffffff;
+ display: flex;
+ flex-direction: column;
+ min-height: 1080px;
+ position: relative;
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+@media (max-width: 1280px) {
+ .desktop {
+ width: 100%;
+ }
+}
+
+@media (max-width: 768px) {
+ .desktop {
+ min-height: auto;
+ }
+}
+
+.desktop .site-header-name {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 48px;
+ justify-content: center;
+ padding: 20px 0px;
+ position: relative;
+ width: 100%;
+}
+
+.desktop .choose-your-receipt {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 36px;
+ font-weight: 700;
+ justify-content: center;
+ letter-spacing: -0.72px;
+ line-height: 43.2px;
+ margin: 0;
+ position: relative;
+ text-align: center;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+@media (max-width: 768px) {
+ .desktop .choose-your-receipt {
+ font-size: 24px;
+ line-height: 28.8px;
+ }
+}
diff --git a/static/src/screens/MenuRecommendationPage/MenuRecommendationPage.jsx b/static/src/screens/MenuRecommendationPage/MenuRecommendationPage.jsx
new file mode 100644
index 0000000..714280e
--- /dev/null
+++ b/static/src/screens/MenuRecommendationPage/MenuRecommendationPage.jsx
@@ -0,0 +1,335 @@
+import React, { useState, useRef } from "react";
+import { Link } from "react-router-dom";
+import { UnifiedHeader } from "../../components/UnifiedHeader";
+import "./style.css";
+
+// Placeholder for WebSpeech API integration
+const startVoiceRecognition = () => {
+ alert("음성 인식 기능은 WebSpeech API와 연동 예정입니다.");
+ // const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
+ // if (!SpeechRecognition) {
+ // alert("이 브라우저는 음성 인식을 지원하지 않습니다.");
+ // return;
+ // }
+ // const recognition = new SpeechRecognition();
+ // recognition.lang = 'ko-KR';
+ // recognition.interimResults = false;
+ // recognition.maxAlternatives = 1;
+
+ // recognition.start();
+
+ // recognition.onresult = (event) => {
+ // const speechResult = event.results[0][0].transcript;
+ // console.log('Speech Result:', speechResult);
+ // // setSearchQuery(speechResult); // Update search query with speech result
+ // };
+
+ // recognition.onerror = (event) => {
+ // console.error('Speech recognition error:', event.error);
+ // alert('음성 인식 중 오류가 발생했습니다: ' + event.error);
+ // };
+};
+
+const MOCK_RECIPES = [
+ // TODO: Backend Integration: Replace with API call to fetch all available recipes
+ {
+ id: 1,
+ name: "저당 닭갈비",
+ ingredients: ["닭고기", "양파", "고추장", "고춧가루", "양배추", "스테비아", "마늘"],
+ steps: ["닭고기 손질 및 양념 재우기", "야채 손질", "볶기"],
+ tendencies: ["매콤칼칼", "스트레스 해소", "저당"],
+ description: "건강한 저당 닭갈비 레시피",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 2,
+ name: "저당 닭볶음탕",
+ ingredients: ["닭고기", "감자", "당근", "양파", "고추장"],
+ steps: ["닭고기 손질 및 초벌", "양념장 만들기", "끓이기", "채소 넣고 마무리"],
+ tendencies: ["매콤칼칼", "저당"],
+ description: "매콤한 닭볶음탕",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 3,
+ name: "양파 볶음",
+ ingredients: ["양파", "간장", "참기름"],
+ steps: ["양파 썰기", "볶기", "간장으로 간하기"],
+ tendencies: ["담백", "건강식"],
+ description: "간단한 양파 볶음",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-1.png"
+ },
+ {
+ id: 4,
+ name: "된장찌개",
+ ingredients: ["된장", "두부", "양파", "감자", "호박"],
+ steps: ["재료 손질", "육수 내기", "된장 풀기", "끓이기"],
+ tendencies: ["담백", "건강식"],
+ description: "구수한 된장찌개",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 5,
+ name: "김치찌개",
+ ingredients: ["김치", "돼지고기", "두부", "양파"],
+ steps: ["돼지고기 볶기", "김치 넣고 볶기", "물 넣고 끓이기"],
+ tendencies: ["매콤칼칼", "스트레스 해소"],
+ description: "얼큰한 김치찌개",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 6,
+ name: "불고기",
+ ingredients: ["소고기", "양파", "당근", "간장", "설탕"],
+ steps: ["고기 재우기", "야채 썰기", "볶기"],
+ tendencies: ["담백"],
+ description: "달콤한 불고기",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 7,
+ name: "제육볶음",
+ ingredients: ["돼지고기", "양파", "고추장", "고춧가루"],
+ steps: ["고기 양념하기", "볶기"],
+ tendencies: ["매콤칼칼", "스트레스 해소"],
+ description: "매콤한 제육볶음",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 8,
+ name: "계란찜",
+ ingredients: ["계란", "물", "소금"],
+ steps: ["계란 풀기", "물 섞기", "찌기"],
+ tendencies: ["담백", "건강식"],
+ description: "부드러운 계란찜",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 9,
+ name: "비빔밥",
+ ingredients: ["밥", "시금치", "당근", "고사리", "고추장"],
+ steps: ["나물 볶기", "밥 위에 올리기", "비비기"],
+ tendencies: ["건강식"],
+ description: "영양 만점 비빔밥",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 10,
+ name: "된장국",
+ ingredients: ["된장", "두부", "파", "감자"],
+ steps: ["육수 내기", "된장 풀기", "재료 넣고 끓이기"],
+ tendencies: ["담백", "건강식"],
+ description: "구수한 된장국",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 11,
+ name: "삼겹살 구이",
+ ingredients: ["삼겹살", "소금", "후추"],
+ steps: ["삼겹살 굽기", "간하기"],
+ tendencies: ["스트레스 해소"],
+ description: "고소한 삼겹살",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 12,
+ name: "잡채",
+ ingredients: ["당면", "시금치", "당근", "양파", "간장"],
+ steps: ["당면 삶기", "야채 볶기", "섞기"],
+ tendencies: ["담백"],
+ description: "맛있는 잡채",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 13,
+ name: "순두부찌개",
+ ingredients: ["순두부", "고추장", "계란", "파"],
+ steps: ["육수 내기", "순두부 넣기", "끓이기"],
+ tendencies: ["매콤칼칼", "건강식"],
+ description: "얼큰한 순두부찌개",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 14,
+ name: "감자조림",
+ ingredients: ["감자", "간장", "설탕", "물엿"],
+ steps: ["감자 썰기", "조리기", "졸이기"],
+ tendencies: ["담백"],
+ description: "달콤한 감자조림",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+ {
+ id: 15,
+ name: "떡볶이",
+ ingredients: ["떡", "고추장", "어묵", "파"],
+ steps: ["육수 내기", "고추장 풀기", "떡 넣고 끓이기"],
+ tendencies: ["매콤칼칼", "스트레스 해소"],
+ description: "매콤달콤 떡볶이",
+ image: "https://c.animaapp.com/sjWITF5i/img/ingredientimage-7@2x.png"
+ },
+];
+
+const RECIPES_PER_PAGE = 12;
+
+export const MenuRecommendationPage = () => {
+ const [searchQuery, setSearchQuery] = useState("");
+ const [recipes, setRecipes] = useState([]);
+ const [currentPage, setCurrentPage] = useState(0);
+ const [recommendationType, setRecommendationType] = useState(null);
+ const scrollContainerRef = useRef(null);
+
+ const handleIngredientBasedRecommendation = async () => {
+ // TODO: Backend Integration: Replace with API call to get ingredient-based recommendations
+ // Example: const response = await axios.get('/api/recommend/ingredients');
+ // setRecipes(response.data);
+ setRecommendationType("ingredient");
+ setRecipes(MOCK_RECIPES); // Using mock data for now
+ setCurrentPage(0);
+ };
+
+ const handleTendencyBasedRecommendation = async () => {
+ // TODO: Backend Integration: Replace with API call to get tendency-based recommendations
+ // Example: const response = await axios.get('/api/recommend/tendencies');
+ // setRecipes(response.data);
+ setRecommendationType("tendency");
+ setRecipes(MOCK_RECIPES); // Using mock data for now
+ setCurrentPage(0);
+ };
+
+ const handleGeminiSearch = async () => {
+ if (!searchQuery.trim()) return;
+
+ // TODO: Backend Integration: Integrate with Gemini API via your backend
+ // Example: const response = await axios.post('/api/gemini-search', { query: searchQuery });
+ // setRecipes(response.data);
+ console.log("Searching with Gemini:", searchQuery);
+ alert("Gemini API 연동 예정입니다.");
+
+ // Mock search results
+ const filtered = MOCK_RECIPES.filter(recipe =>
+ recipe.name.includes(searchQuery) ||
+ recipe.ingredients.some(ing => ing.includes(searchQuery))
+ );
+ setRecipes(filtered.length > 0 ? filtered : MOCK_RECIPES);
+ setRecommendationType("search");
+ setCurrentPage(0);
+ };
+
+ const totalPages = Math.ceil(recipes.length / RECIPES_PER_PAGE);
+ const displayedRecipes = recipes.slice(
+ currentPage * RECIPES_PER_PAGE,
+ (currentPage + 1) * RECIPES_PER_PAGE
+ );
+
+ const handleScroll = (direction) => {
+ if (direction === "left" && currentPage > 0) {
+ setCurrentPage(currentPage - 1);
+ } else if (direction === "right" && currentPage < totalPages - 1) {
+ setCurrentPage(currentPage + 1);
+ }
+ };
+
+ return (
+
+
+
+
+
+
메뉴 추천
+
AI가 추천하는 맞춤 레시피를 찾아보세요
+
+
+
+ setSearchQuery(e.target.value)}
+ onKeyPress={(e) => e.key === 'Enter' && handleGeminiSearch()}
+ />
+
+
+
+
+
+
+
+
+
+ {recipes.length > 0 && (
+
+
+
+
+ {currentPage + 1} / {totalPages}
+
+
+
+
+
+ {displayedRecipes.map((recipe) => (
+
+

+
+
{recipe.name}
+
{recipe.description}
+
+ {recipe.tendencies.slice(0, 2).map((tendency, index) => (
+
+ {tendency}
+
+ ))}
+
+
+
+ ))}
+
+
+ )}
+
+ {recipes.length === 0 && recommendationType && (
+
+
추천할 레시피가 없습니다.
+
다른 조건으로 검색해보세요.
+
+ )}
+
+
+ );
+};
diff --git a/static/src/screens/MenuRecommendationPage/index.js b/static/src/screens/MenuRecommendationPage/index.js
new file mode 100644
index 0000000..13935ed
--- /dev/null
+++ b/static/src/screens/MenuRecommendationPage/index.js
@@ -0,0 +1 @@
+export { MenuRecommendationPage } from "./MenuRecommendationPage";
diff --git a/static/src/screens/MenuRecommendationPage/style.css b/static/src/screens/MenuRecommendationPage/style.css
new file mode 100644
index 0000000..d7168f1
--- /dev/null
+++ b/static/src/screens/MenuRecommendationPage/style.css
@@ -0,0 +1,308 @@
+.menu-recommendation-page {
+ align-items: center;
+ background-color: #ffffff;
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ position: relative;
+ width: 100%;
+}
+
+.menu-recommendation-content {
+ align-items: flex-start;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ gap: 30px;
+ padding: 20px 64px;
+ position: relative;
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+.menu-recommendation-header {
+ align-items: flex-start;
+ align-self: stretch;
+ border-bottom: 1px solid #000000;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ padding-bottom: 20px;
+ width: 100%;
+}
+
+.menu-recommendation-title {
+ color: #000000;
+ font-family: "Inter", Helvetica;
+ font-size: 48px;
+ font-weight: 700;
+ letter-spacing: -0.96px;
+ line-height: 57.6px;
+ margin: 0;
+}
+
+.menu-recommendation-subtitle {
+ color: #0000008c;
+ font-family: "Inter", Helvetica;
+ font-size: 20px;
+ font-weight: 500;
+ letter-spacing: -0.10px;
+ line-height: 29.0px;
+ margin: 0;
+}
+
+.menu-search-section {
+ align-self: stretch;
+ display: flex;
+ gap: 10px;
+ width: 100%;
+}
+
+.menu-search-input {
+ flex: 1;
+ padding: 14px 20px;
+ border: 2px solid #e0e0e0;
+ border-radius: 12px;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ transition: border-color 0.2s;
+}
+
+.menu-search-input:focus {
+ outline: none;
+ border-color: #f6910b;
+}
+
+.menu-search-btn {
+ padding: 14px 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;
+}
+
+.menu-search-btn:hover {
+ background-color: #e58209;
+}
+
+.menu-voice-btn {
+ width: 50px;
+ height: 50px;
+ background-color: #f6910b;
+ border: none;
+ border-radius: 12px;
+ color: #ffffff;
+ cursor: pointer;
+ font-size: 24px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: background-color 0.2s;
+}
+
+.menu-voice-btn:hover {
+ background-color: #e58209;
+}
+
+.menu-recommendation-buttons {
+ align-self: stretch;
+ display: flex;
+ gap: 15px;
+ justify-content: center;
+}
+
+.menu-recommendation-btn {
+ padding: 14px 30px;
+ background-color: #e0e0e0;
+ border: 2px solid transparent;
+ border-radius: 12px;
+ color: #666666;
+ cursor: pointer;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 700;
+ transition: all 0.2s;
+}
+
+.menu-recommendation-btn:hover {
+ background-color: #d0d0d0;
+}
+
+.menu-recommendation-btn.active {
+ background-color: #f6910b;
+ border-color: #f6910b;
+ color: #ffffff;
+}
+
+.menu-recipes-section {
+ align-self: stretch;
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ width: 100%;
+}
+
+.menu-recipes-navigation {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 20px;
+}
+
+.menu-scroll-btn {
+ width: 40px;
+ height: 40px;
+ background-color: #f6910b;
+ border: none;
+ border-radius: 50%;
+ color: #ffffff;
+ cursor: pointer;
+ font-size: 20px;
+ font-weight: 700;
+ transition: all 0.2s;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.menu-scroll-btn:hover:not(:disabled) {
+ background-color: #e58209;
+ transform: scale(1.1);
+}
+
+.menu-scroll-btn:disabled {
+ background-color: #cccccc;
+ cursor: not-allowed;
+ opacity: 0.5;
+}
+
+.menu-page-indicator {
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 600;
+ color: #000000;
+}
+
+.menu-recipes-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
+ gap: 20px;
+ width: 100%;
+ transition: transform 0.3s ease-in-out;
+}
+
+.menu-recipe-card {
+ background-color: #ffffff;
+ border: 1px solid #e0e0e0;
+ border-radius: 16px;
+ overflow: hidden;
+ cursor: pointer;
+ transition: all 0.2s;
+ text-decoration: none;
+ display: flex;
+ flex-direction: column;
+ box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.05);
+}
+
+.menu-recipe-card:hover {
+ transform: translateY(-4px);
+ box-shadow: 0px 8px 16px rgba(0, 0, 0, 0.1);
+ border-color: #f6910b;
+}
+
+.menu-recipe-card-image {
+ width: 100%;
+ height: 180px;
+ object-fit: cover;
+}
+
+.menu-recipe-card-content {
+ padding: 15px;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.menu-recipe-card-title {
+ font-family: "Inter", Helvetica;
+ font-size: 18px;
+ font-weight: 700;
+ color: #000000;
+ margin: 0;
+}
+
+.menu-recipe-card-description {
+ font-family: "Inter", Helvetica;
+ font-size: 14px;
+ font-weight: 500;
+ color: #666666;
+ margin: 0;
+ line-height: 1.5;
+}
+
+.menu-recipe-card-tags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+}
+
+.menu-recipe-card-tag {
+ padding: 4px 10px;
+ background-color: #f6910b1a;
+ border-radius: 12px;
+ color: #f6910b;
+ font-family: "Inter", Helvetica;
+ font-size: 12px;
+ font-weight: 600;
+}
+
+.menu-empty-state {
+ align-self: stretch;
+ text-align: center;
+ padding: 60px 40px;
+ background-color: #f5f5f5;
+ border-radius: 20px;
+}
+
+.menu-empty-state p {
+ font-family: "Inter", Helvetica;
+ font-size: 18px;
+ font-weight: 500;
+ color: #666666;
+ margin: 10px 0;
+}
+
+@media (max-width: 768px) {
+ .menu-recommendation-content {
+ padding: 20px 20px;
+ }
+
+ .menu-recommendation-title {
+ font-size: 32px;
+ line-height: 38.4px;
+ }
+
+ .menu-recommendation-subtitle {
+ font-size: 16px;
+ line-height: 23.2px;
+ }
+
+ .menu-recipes-grid {
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+ gap: 15px;
+ }
+
+ .menu-recommendation-buttons {
+ flex-direction: column;
+ }
+
+ .menu-recommendation-btn {
+ width: 100%;
+ }
+}
diff --git a/static/src/screens/MyPage/MyPage.jsx b/static/src/screens/MyPage/MyPage.jsx
new file mode 100644
index 0000000..2fa8455
--- /dev/null
+++ b/static/src/screens/MyPage/MyPage.jsx
@@ -0,0 +1,224 @@
+import React, { useState, useEffect } from "react";
+import { useNavigate, useLocation } from "react-router-dom";
+import { useAuth } from "../../contexts/AuthContext";
+import { UnifiedHeader } from "../../components/UnifiedHeader";
+import { LoginPopup } from "../../components/LoginPopup";
+import { SignupPopup } from "../../components/SignupPopup";
+import { PreferencesPopup } from "../../components/PreferencesPopup";
+import { MypageTendency } from "../../components/MypageTendency";
+import "./style.css";
+
+const HEALTH_GOALS = [
+ { id: 1, name: "체중 감량" },
+ { id: 2, name: "근육 증가" },
+ { id: 3, name: "건강 유지" },
+ { id: 4, name: "면역력 강화" },
+ { id: 5, name: "소화 개선" },
+];
+
+const ALLERGIES = [
+ { id: 1, name: "우유" },
+ { id: 2, name: "계란" },
+ { id: 3, name: "땅콩" },
+ { id: 4, name: "견과류" },
+ { id: 5, name: "갑각류" },
+ { id: 6, name: "밀" },
+ { id: 7, name: "대두" },
+];
+
+export const MyPage = () => {
+ const { user, isAuthenticated, logout } = useAuth();
+ const navigate = useNavigate();
+ const location = useLocation();
+ const [preferences, setPreferences] = useState(null);
+ const [showLoginPopup, setShowLoginPopup] = useState(false);
+ const [showSignupPopup, setShowSignupPopup] = useState(false);
+ const [showPreferencesPopup, setShowPreferencesPopup] = useState(false);
+ const [userData, setUserData] = useState(null);
+
+ useEffect(() => {
+ if (isAuthenticated && user) {
+ // TODO: Backend Integration: Fetch user preferences from backend using user.username
+ // Example: axios.get(`/api/users/${user.username}/preferences`).then(response => setPreferences(response.data));
+ const stored = localStorage.getItem("userPreferences");
+ if (stored) {
+ setPreferences(JSON.parse(stored));
+ }
+ }
+ }, [isAuthenticated, user]);
+
+ useEffect(() => {
+ // Check if we should show login popup from navigation state
+ if (location.state?.showLogin) {
+ setShowLoginPopup(true);
+ // Clear the state
+ navigate(location.pathname, { replace: true, state: {} });
+ }
+ }, [location, navigate]);
+
+ const handleLoginClick = () => {
+ setShowLoginPopup(true);
+ };
+
+ 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);
+ };
+
+ const getHealthGoalNames = () => {
+ if (!preferences?.healthGoalIds) return [];
+ return preferences.healthGoalIds.map(
+ (id) => HEALTH_GOALS.find((goal) => goal.id === id)?.name || ""
+ );
+ };
+
+ const getAllergyNames = () => {
+ if (!preferences?.allergyIds) return [];
+ return preferences.allergyIds.map(
+ (id) => ALLERGIES.find((allergy) => allergy.id === id)?.name || ""
+ );
+ };
+
+ return (
+
+
+
+
+
+
+ {!isAuthenticated ? (
+
+
로그인이 필요합니다
+
마이페이지를 이용하시려면 로그인해주세요.
+
+
+ ) : preferences ? (
+ <>
+
+
+
+ {getHealthGoalNames().map((goal, index) => (
+
+ ))}
+
+
+
+
+
+
+ {getAllergyNames().length > 0 ? (
+ getAllergyNames().map((allergy, index) => (
+
+ ))
+ ) : (
+
없음
+ )}
+
+
+
+
+
+ 싫어하는 재료:
+ {preferences.dislikedIngredients || "없음"}
+
+
+ 선호하는 재료:
+ {preferences.preferredIngredients || "없음"}
+
+
+ 선호 요리:
+ {preferences.preferredCuisine}
+
+
+ 매운맛 선호도:
+ {preferences.spiceLevel}
+
+
+
+
+
+
+
+ 소비 알림
+
+ {preferences.allowPushConsumption ? "ON" : "OFF"}
+
+
+
+ 댓글 알림
+
+ {preferences.allowPushComment ? "ON" : "OFF"}
+
+
+
+ 넛지 알림
+
+ {preferences.allowPushNudge ? "ON" : "OFF"}
+
+
+
+
+ >
+ ) : (
+
+
저장된 성향 정보가 없습니다.
+
회원가입을 통해 성향을 설정해주세요.
+
+ )}
+
+
+ {showLoginPopup && (
+
+ )}
+
+ {showSignupPopup && (
+
+ )}
+
+ {showPreferencesPopup && (
+
+ )}
+
+ );
+};
diff --git a/static/src/screens/MyPage/index.js b/static/src/screens/MyPage/index.js
new file mode 100644
index 0000000..b2f5896
--- /dev/null
+++ b/static/src/screens/MyPage/index.js
@@ -0,0 +1 @@
+export { MyPage } from "./MyPage";
diff --git a/static/src/screens/MyPage/style.css b/static/src/screens/MyPage/style.css
new file mode 100644
index 0000000..4524930
--- /dev/null
+++ b/static/src/screens/MyPage/style.css
@@ -0,0 +1,420 @@
+.my-page {
+ align-items: center;
+ background-color: #ffffff;
+ display: flex;
+ flex-direction: column;
+ min-height: 1080px;
+ position: relative;
+ width: 100%;
+}
+
+.my-page .mypage-header {
+ align-items: center;
+ display: flex;
+ gap: 10px;
+ height: 120px;
+ padding: 10px;
+ position: relative;
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+.my-page .none-frame-3 {
+ flex: 1;
+ flex-grow: 1;
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ height: 100%;
+ position: relative;
+}
+
+.my-page .algorithm-label-3 {
+ 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: 271px;
+ height: 100%;
+}
+
+.my-page .text-wrapper-12 {
+ color: #000000;
+ letter-spacing: -0.46px;
+}
+
+.my-page .text-wrapper-13 {
+ color: #f6910b;
+ letter-spacing: -0.46px;
+}
+
+.my-page .user-name-container-wrapper {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 1;
+ flex-grow: 1;
+ gap: 10px;
+ justify-content: flex-end;
+ padding: 10px;
+ position: relative;
+}
+
+.my-page .user-name-container {
+ align-items: flex-start;
+ background-color: #0000001a;
+ border-radius: 16px;
+ display: inline-flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ padding: 6px 20px;
+ position: relative;
+}
+
+.my-page .user-name-text {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 12px;
+ font-weight: 700;
+ justify-content: center;
+ letter-spacing: -0.24px;
+ line-height: 14.4px;
+ margin-top: -1.00px;
+ position: relative;
+ text-align: center;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+.my-page .mypage-nav {
+ align-items: center;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 48px;
+ justify-content: center;
+ padding: 5px 64px;
+ position: relative;
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+.my-page .mypage-textcontainer {
+ align-items: center;
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ border-color: #000000;
+ display: flex;
+ flex: 1;
+ flex-grow: 1;
+ gap: 10px;
+ justify-content: center;
+ padding: 20px 64px;
+ position: relative;
+}
+
+.my-page .mypage-text {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ flex: 1;
+ font-family: "Inter", Helvetica;
+ font-size: 36px;
+ font-weight: 700;
+ justify-content: center;
+ letter-spacing: -0.72px;
+ line-height: 43.2px;
+ margin-top: -1.00px;
+ position: relative;
+ text-align: center;
+}
+
+.my-page .mypage-section {
+ align-items: center;
+ display: flex;
+ flex: 0 0 auto;
+ flex-direction: column;
+ gap: 30px;
+ padding: 0px 64px;
+ position: relative;
+ width: 100%;
+ max-width: 900px;
+ margin: 0 auto;
+}
+
+.my-page .mypage {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ min-height: auto;
+ padding: 10px 20px;
+ position: relative;
+ width: 100%;
+}
+
+.my-page .mypage-wrapper {
+ align-items: flex-start;
+ align-self: stretch;
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ border-color: #000000;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ justify-content: flex-start;
+ padding: 10px;
+ position: relative;
+ width: 100%;
+}
+
+.my-page .mypage-2 {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 36px;
+ font-weight: 700;
+ justify-content: flex-start;
+ letter-spacing: -0.72px;
+ line-height: 43.2px;
+ margin: 0;
+ position: relative;
+ text-align: left;
+ white-space: nowrap;
+}
+
+.my-page .mypage-3 {
+ background-color: #0000000d;
+ border-radius: 15px;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ justify-content: center;
+ min-height: auto;
+ overflow: visible;
+ padding: 20px;
+ position: relative;
+ width: 100%;
+}
+
+
+.my-page .mypage-info-section {
+ align-self: stretch;
+ background-color: #0000000d;
+ border-radius: 15px;
+ display: flex;
+ flex-direction: column;
+ gap: 15px;
+ padding: 20px;
+ width: 100%;
+}
+
+.my-page .mypage-info-item {
+ display: flex;
+ gap: 10px;
+ align-items: center;
+}
+
+.my-page .mypage-info-label {
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 700;
+ color: #000000;
+ min-width: 150px;
+}
+
+.my-page .mypage-info-value {
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 500;
+ color: #666666;
+}
+
+.my-page .mypage-notification-section {
+ align-self: stretch;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ width: 100%;
+}
+
+.my-page .mypage-notification-list {
+ background-color: #0000000d;
+ border-radius: 15px;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ padding: 20px;
+}
+
+.my-page .mypage-notification-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 500;
+ color: #000000;
+}
+
+.my-page .mypage-notification-item .active {
+ color: #f6910b;
+ font-weight: 700;
+}
+
+.my-page .mypage-notification-item .inactive {
+ color: #999999;
+ font-weight: 500;
+}
+
+.my-page .mypage-empty {
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 500;
+ color: #666666;
+ padding: 20px;
+ text-align: center;
+}
+
+.my-page .mypage-empty-state {
+ text-align: center;
+ padding: 40px;
+ color: #666666;
+ font-family: "Inter", Helvetica;
+ font-size: 18px;
+ font-weight: 500;
+}
+
+.my-page .mypage-empty-state p {
+ margin: 10px 0;
+}
+
+.my-page .mypage-login-required {
+ text-align: center;
+ padding: 60px 40px;
+ background-color: #f5f5f5;
+ border-radius: 20px;
+ width: 100%;
+}
+
+.my-page .mypage-login-title {
+ font-family: "Inter", Helvetica;
+ font-size: 32px;
+ font-weight: 700;
+ color: #000000;
+ margin: 0 0 20px 0;
+}
+
+.my-page .mypage-login-text {
+ font-family: "Inter", Helvetica;
+ font-size: 18px;
+ font-weight: 500;
+ color: #666666;
+ margin: 0 0 30px 0;
+}
+
+.my-page .mypage-login-btn {
+ padding: 14px 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;
+}
+
+.my-page .mypage-login-btn:hover {
+ background-color: #e58209;
+}
+
+.my-page .user-info-wrapper {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.my-page .user-name-display {
+ color: #000000;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 600;
+ padding: 8px 16px;
+ background-color: #f6910b1a;
+ border-radius: 20px;
+}
+
+.my-page .logout-button {
+ 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: 14px;
+ font-weight: 600;
+}
+
+.my-page .logout-button:hover {
+ transform: scale(1.05);
+}
+
+.my-page .login-button {
+ 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: 14px;
+ font-weight: 600;
+}
+
+.my-page .login-button:hover {
+ transform: scale(1.05);
+}
+
+@media (max-width: 768px) {
+ .my-page .mypage-section {
+ padding: 0px 20px;
+ }
+
+ .my-page .mypage-2 {
+ font-size: 28px;
+ line-height: 33.6px;
+ }
+
+ .my-page .mypage-text {
+ font-size: 28px;
+ line-height: 33.6px;
+ }
+
+ .my-page .algorithm-label-3 {
+ font-size: 32px;
+ }
+}
diff --git a/static/src/screens/RecipePage/RecipePage.jsx b/static/src/screens/RecipePage/RecipePage.jsx
new file mode 100644
index 0000000..2304d31
--- /dev/null
+++ b/static/src/screens/RecipePage/RecipePage.jsx
@@ -0,0 +1,77 @@
+import React from "react";
+import { useLocation, useParams } from "react-router-dom";
+import { UnifiedHeader } from "../../components/UnifiedHeader";
+import { Recipepage } from "../../components/Recipepage";
+import "./style.css";
+
+export const RecipePage = () => {
+ const location = useLocation();
+ const { id } = useParams();
+ const recipe = location.state?.recipe || {
+ id: id,
+ name: "레시피 이름",
+ ingredients: ["닭고기", "양파", "고추장"],
+ steps: ["재료 준비", "조리 시작", "완성"],
+ description: "레시피 설명이 여기에 표시됩니다.",
+ tendencies: ["매콤칼칼"]
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ {recipe.ingredients.map((ingredient, index) => (
+
+ ))}
+
+
+
+
+
+
+
+
+
+
{recipe.description || "레시피 설명이 여기에 표시됩니다."}
+
+
+
+
+
+
+
+
+ {recipe.steps.map((step, index) => (
+
+ {index + 1}.
+ {step}
+
+ ))}
+
+
+
+
+
+ );
+};
diff --git a/static/src/screens/RecipePage/index.js b/static/src/screens/RecipePage/index.js
new file mode 100644
index 0000000..7c5922e
--- /dev/null
+++ b/static/src/screens/RecipePage/index.js
@@ -0,0 +1 @@
+export { RecipePage } from "./RecipePage";
diff --git a/static/src/screens/RecipePage/style.css b/static/src/screens/RecipePage/style.css
new file mode 100644
index 0000000..56cc608
--- /dev/null
+++ b/static/src/screens/RecipePage/style.css
@@ -0,0 +1,412 @@
+.recipe-page {
+ align-items: center;
+ background-color: #ffffff;
+ display: flex;
+ flex-direction: column;
+ min-height: 1080px;
+ position: relative;
+ width: 100%;
+}
+
+.recipe-page .recipepage-header {
+ align-items: center;
+ display: flex;
+ gap: 10px;
+ height: 120px;
+ padding: 10px;
+ position: relative;
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+.recipe-page .none-frame-4 {
+ flex: 1;
+ flex-grow: 1;
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ height: 100%;
+ position: relative;
+}
+
+.recipe-page .algorithm-label-4 {
+ 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: 271px;
+ height: 100%;
+}
+
+.recipe-page .text-wrapper-14 {
+ color: #000000;
+ letter-spacing: -0.46px;
+}
+
+.recipe-page .text-wrapper-15 {
+ color: #f6910b;
+ letter-spacing: -0.46px;
+}
+
+.recipe-page .user-menu-frame-3 {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 1;
+ flex-grow: 1;
+ gap: 10px;
+ justify-content: flex-end;
+ padding: 10px;
+ position: relative;
+}
+
+.recipe-page .user-name-text-wrapper {
+ align-items: flex-start;
+ background-color: #0000001a;
+ border-radius: 16px;
+ display: inline-flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ padding: 6px 20px;
+ position: relative;
+}
+
+.recipe-page .user-name-text-2 {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 12px;
+ font-weight: 700;
+ justify-content: center;
+ letter-spacing: -0.24px;
+ line-height: 14.4px;
+ margin-top: -1.00px;
+ position: relative;
+ text-align: center;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+.recipe-page .recipepage-nav {
+ align-items: center;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 48px;
+ justify-content: center;
+ padding: 5px 64px;
+ position: relative;
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+.recipe-page .recipepage-text-wrapper {
+ align-items: center;
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ border-color: #000000;
+ display: flex;
+ flex: 1;
+ flex-grow: 1;
+ gap: 10px;
+ justify-content: center;
+ padding: 20px 64px;
+ position: relative;
+}
+
+.recipe-page .recipepage-text {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ flex: 1;
+ font-family: "Inter", Helvetica;
+ font-size: 36px;
+ font-weight: 700;
+ justify-content: center;
+ letter-spacing: -0.72px;
+ line-height: 43.2px;
+ margin-top: -1.00px;
+ position: relative;
+ text-align: center;
+}
+
+.recipe-page .recipepage-section {
+ align-items: center;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ flex-grow: 1;
+ gap: 30px;
+ padding: 0px 64px 20px;
+ position: relative;
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+}
+
+.recipe-page .recipepage-content {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ flex-grow: 1;
+ gap: 10px;
+ padding: 10px 130px;
+ position: relative;
+ width: 100%;
+}
+
+.recipe-page .recipepage-wrapper {
+ align-items: flex-start;
+ align-self: stretch;
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ border-color: #000000;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ padding: 10px;
+ position: relative;
+ width: 100%;
+}
+
+.recipe-page .recipepage-2 {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 48px;
+ font-weight: 700;
+ justify-content: center;
+ letter-spacing: -0.96px;
+ line-height: 57.6px;
+ margin-top: -1.00px;
+ position: relative;
+ text-align: center;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+.recipe-page .recipepage-3 {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ overflow: hidden;
+ overflow-x: scroll;
+ padding: 10px;
+ position: relative;
+ width: 100%;
+}
+
+.recipe-page .recipepage-3::-webkit-scrollbar {
+ display: none;
+ width: 0;
+}
+
+.recipe-page .recipepage-usedingredient {
+ flex: 0 0 auto !important;
+ left: unset !important;
+ top: unset !important;
+}
+
+.recipe-page .recipepage-4 {
+ align-items: flex-start;
+ align-self: stretch;
+ background-color: #0000000d;
+ border-radius: 15px;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 15px;
+ overflow: hidden;
+ padding: 15px;
+ position: relative;
+ width: 100%;
+}
+
+.recipe-page .recipepage-menuimage {
+ background-color: #ffffff;
+ border-radius: 16px;
+ height: 80px;
+ position: relative;
+ width: 80px;
+}
+
+.recipe-page .recipepage-5 {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ flex-grow: 1;
+ gap: 5px;
+ padding: 0px 5px;
+ position: relative;
+}
+
+.recipe-page .recipepage-6 {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ flex-direction: column;
+ gap: 10px;
+ position: relative;
+ width: 100%;
+}
+
+.recipe-page .recipepage-7 {
+ align-items: center;
+ color: #000000;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 24px;
+ font-weight: 700;
+ justify-content: center;
+ letter-spacing: -0.48px;
+ line-height: 28.8px;
+ margin-top: -1.00px;
+ position: relative;
+ text-align: center;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+.recipe-page .recipepage-8 {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 10px;
+ padding: 0px 10px;
+ position: relative;
+ width: 100%;
+}
+
+.recipe-page .recipepage-9 {
+ 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;
+ margin-top: -1.00px;
+ position: relative;
+}
+
+.recipe-page .recipepage-10 {
+ align-items: flex-start;
+ align-self: stretch;
+ background-color: #0000000d;
+ border-radius: 15px;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ flex-grow: 1;
+ gap: 15px;
+ overflow: hidden;
+ padding: 10px;
+ position: relative;
+ width: 100%;
+}
+
+.recipe-page .recipepage-11 {
+ align-items: flex-start;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ flex-direction: column;
+ gap: 10px;
+ padding: 0px 10px;
+ position: relative;
+ width: 100%;
+}
+
+.recipe-page .recipepage-12 {
+ align-items: center;
+ align-self: stretch;
+ color: #000000;
+ display: flex;
+ font-family: "Inter", Helvetica;
+ font-size: 24px;
+ font-weight: 700;
+ height: 29px;
+ justify-content: center;
+ letter-spacing: -0.48px;
+ line-height: 28.8px;
+ margin-top: -1.00px;
+ position: relative;
+ white-space: nowrap;
+}
+
+.recipe-page .recipepage-steps {
+ align-self: stretch;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ padding: 10px;
+ width: 100%;
+}
+
+.recipe-page .recipepage-step {
+ display: flex;
+ gap: 10px;
+ align-items: flex-start;
+}
+
+.recipe-page .recipepage-step-number {
+ color: #f6910b;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 700;
+ min-width: 30px;
+}
+
+.recipe-page .recipepage-step-text {
+ color: #000000;
+ font-family: "Inter", Helvetica;
+ font-size: 16px;
+ font-weight: 500;
+ line-height: 22.4px;
+ flex: 1;
+}
+
+@media (max-width: 768px) {
+ .recipe-page .recipepage-section {
+ padding: 0px 20px 20px;
+ }
+
+ .recipe-page .recipepage-content {
+ padding: 10px 20px;
+ }
+
+ .recipe-page .recipepage-2 {
+ font-size: 32px;
+ line-height: 38.4px;
+ }
+
+ .recipe-page .recipepage-text {
+ font-size: 28px;
+ line-height: 33.6px;
+ }
+
+ .recipe-page .algorithm-label-4 {
+ font-size: 32px;
+ }
+}
diff --git a/static/static/_redirects b/static/static/_redirects
new file mode 100644
index 0000000..7797f7c
--- /dev/null
+++ b/static/static/_redirects
@@ -0,0 +1 @@
+/* /index.html 200
diff --git a/static/vite.config.js b/static/vite.config.js
new file mode 100644
index 0000000..154431f
--- /dev/null
+++ b/static/vite.config.js
@@ -0,0 +1,11 @@
+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(({ mode }) => ({
+ plugins: [react(), mode === "development" && screenGraphPlugin()],
+ publicDir: "./static",
+ base: "./",
+}));
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: "./",
+}));