Repository: BusHanyang/hybus-genesis
Pull request #97: Add RouteMap
Author: login: kcyoow
Labels: name: feature
description: Feature development
Created: 2025-03-08T18:48:58Z | Merged: 2025-03-09T08:42:39Z

## Describe your Pull Request

*Please describe the behavior or changes of your code specifically.*


## Additional content

*Any additional comments?* 😁

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Introduced animated transitions for route cards and shuttle displays, including an interactive arrow indicator and a responsive route map.

- **Enhancements**
  - Improved shuttle scheduling with real-time updates and refined visual cues.
  - Expanded language support with additional translations.
  - Updated the visual theme with a new highlight color and smoother transition effects.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/.pnp.cjs b/.pnp.cjs
index 9ab181b3..818dff4b 100755
--- a/.pnp.cjs
+++ b/.pnp.cjs
@@ -27,10 +27,12 @@ const RAW_RUNTIME_STATE =
         "packageLocation": "./",\
         "packageDependencies": [\
           ["@builder.io/partytown", "npm:0.10.2"],\
+          ["@reduxjs/toolkit", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:2.5.0"],\
           ["@tanstack/react-query", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:5.32.1"],\
           ["@types/node", "npm:20.11.19"],\
           ["@types/react", "npm:18.3.1"],\
           ["@types/react-dom", "npm:18.3.0"],\
+          ["@types/react-transition-group", "npm:4.4.11"],\
           ["@types/styled-components", "npm:5.1.34"],\
           ["@typescript-eslint/eslint-plugin", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:7.8.0"],\
           ["@typescript-eslint/parser", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:7.8.0"],\
@@ -57,12 +59,15 @@ const RAW_RUNTIME_STATE =
           ["react-i18next", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:14.0.5"],\
           ["react-icons", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:5.0.1"],\
           ["react-is", "npm:18.2.0"],\
+          ["react-redux", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:9.2.0"],\
           ["react-router-dom", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:6.23.0"],\
           ["react-simple-pull-to-refresh", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:1.3.3"],\
           ["react-snowfall", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:2.2.0"],\
           ["react-spinners", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:0.13.8"],\
           ["react-tiny-fab", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:4.0.4"],\
+          ["react-transition-group", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:4.4.5"],\
           ["recoil", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:0.7.7"],\
+          ["redux", "npm:5.0.1"],\
           ["sass", "npm:1.76.0"],\
           ["styled-components", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:6.1.9"],\
           ["styled-reset", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:4.5.2"],\
@@ -2727,6 +2732,14 @@ const RAW_RUNTIME_STATE =
           ["regenerator-runtime", "npm:0.14.1"]\
         ],\
         "linkType": "HARD"\
+      }],\
+      ["npm:7.26.0", {\
+        "packageLocation": "./.yarn/cache/@babel-runtime-npm-7.26.0-9afa3c4ef6-9f4ea1c1d5.zip/node_modules/@babel/runtime/",\
+        "packageDependencies": [\
+          ["@babel/runtime", "npm:7.26.0"],\
+          ["regenerator-runtime", "npm:0.14.1"]\
+        ],\
+        "linkType": "HARD"\
       }]\
     ]],\
     ["@babel/template", [\
@@ -3350,6 +3363,36 @@ const RAW_RUNTIME_STATE =
         "linkType": "HARD"\
       }]\
     ]],\
+    ["@reduxjs/toolkit", [\
+      ["npm:2.5.0", {\
+        "packageLocation": "./.yarn/cache/@reduxjs-toolkit-npm-2.5.0-9bdc99574d-b24ee7e89b.zip/node_modules/@reduxjs/toolkit/",\
+        "packageDependencies": [\
+          ["@reduxjs/toolkit", "npm:2.5.0"]\
+        ],\
+        "linkType": "SOFT"\
+      }],\
+      ["virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:2.5.0", {\
+        "packageLocation": "./.yarn/__virtual__/@reduxjs-toolkit-virtual-e9425ef4b8/0/cache/@reduxjs-toolkit-npm-2.5.0-9bdc99574d-b24ee7e89b.zip/node_modules/@reduxjs/toolkit/",\
+        "packageDependencies": [\
+          ["@reduxjs/toolkit", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:2.5.0"],\
+          ["@types/react", "npm:18.3.1"],\
+          ["@types/react-redux", null],\
+          ["immer", "npm:10.1.1"],\
+          ["react", "npm:18.3.1"],\
+          ["react-redux", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:9.2.0"],\
+          ["redux", "npm:5.0.1"],\
+          ["redux-thunk", "virtual:e9425ef4b85baed0716dc6d7e0b695ce42b8d27bf080c466a53bb65e8c519f735db6f29c965815973ae41f018b8f0567b883db4516a04fb0fb312905576e46ca#npm:3.1.0"],\
+          ["reselect", "npm:5.1.1"]\
+        ],\
+        "packagePeers": [\
+          "@types/react-redux",\
+          "@types/react",\
+          "react-redux",\
+          "react"\
+        ],\
+        "linkType": "HARD"\
+      }]\
+    ]],\
     ["@remix-run/router", [\
       ["npm:1.16.0", {\
         "packageLocation": "./.yarn/cache/@remix-run-router-npm-1.16.0-a6b9bac833-51f5805ec1.zip/node_modules/@remix-run/router/",\
@@ -4111,6 +4154,16 @@ const RAW_RUNTIME_STATE =
         "linkType": "HARD"\
       }]\
     ]],\
+    ["@types/react-transition-group", [\
+      ["npm:4.4.11", {\
+        "packageLocation": "./.yarn/cache/@types-react-transition-group-npm-4.4.11-dda183055d-a7f4de6e5f.zip/node_modules/@types/react-transition-group/",\
+        "packageDependencies": [\
+          ["@types/react-transition-group", "npm:4.4.11"],\
+          ["@types/react", "npm:18.3.1"]\
+        ],\
+        "linkType": "HARD"\
+      }]\
+    ]],\
     ["@types/resolve", [\
       ["npm:1.20.2", {\
         "packageLocation": "./.yarn/cache/@types-resolve-npm-1.20.2-5fccb2ad46-1bff0d3875.zip/node_modules/@types/resolve/",\
@@ -4159,6 +4212,15 @@ const RAW_RUNTIME_STATE =
         "linkType": "HARD"\
       }]\
     ]],\
+    ["@types/use-sync-external-store", [\
+      ["npm:0.0.6", {\
+        "packageLocation": "./.yarn/cache/@types-use-sync-external-store-npm-0.0.6-9e5c635381-a95ce33066.zip/node_modules/@types/use-sync-external-store/",\
+        "packageDependencies": [\
+          ["@types/use-sync-external-store", "npm:0.0.6"]\
+        ],\
+        "linkType": "HARD"\
+      }]\
+    ]],\
     ["@typescript-eslint/eslint-plugin", [\
       ["npm:7.8.0", {\
         "packageLocation": "./.yarn/cache/@typescript-eslint-eslint-plugin-npm-7.8.0-4ae7fca53c-0dc5f0933e.zip/node_modules/@typescript-eslint/eslint-plugin/",\
@@ -5773,6 +5835,17 @@ const RAW_RUNTIME_STATE =
         "linkType": "HARD"\
       }]\
     ]],\
+    ["dom-helpers", [\
+      ["npm:5.2.1", {\
+        "packageLocation": "./.yarn/cache/dom-helpers-npm-5.2.1-b38bb4470b-bed2341adf.zip/node_modules/dom-helpers/",\
+        "packageDependencies": [\
+          ["dom-helpers", "npm:5.2.1"],\
+          ["@babel/runtime", "npm:7.26.0"],\
+          ["csstype", "npm:3.1.3"]\
+        ],\
+        "linkType": "HARD"\
+      }]\
+    ]],\
     ["dot-case", [\
       ["npm:3.0.4", {\
         "packageLocation": "./.yarn/cache/dot-case-npm-3.0.4-09675b5521-a65e351941.zip/node_modules/dot-case/",\
@@ -7194,10 +7267,12 @@ const RAW_RUNTIME_STATE =
         "packageDependencies": [\
           ["hybus-genesis", "workspace:."],\
           ["@builder.io/partytown", "npm:0.10.2"],\
+          ["@reduxjs/toolkit", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:2.5.0"],\
           ["@tanstack/react-query", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:5.32.1"],\
           ["@types/node", "npm:20.11.19"],\
           ["@types/react", "npm:18.3.1"],\
           ["@types/react-dom", "npm:18.3.0"],\
+          ["@types/react-transition-group", "npm:4.4.11"],\
           ["@types/styled-components", "npm:5.1.34"],\
           ["@typescript-eslint/eslint-plugin", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:7.8.0"],\
           ["@typescript-eslint/parser", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:7.8.0"],\
@@ -7224,12 +7299,15 @@ const RAW_RUNTIME_STATE =
           ["react-i18next", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:14.0.5"],\
           ["react-icons", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:5.0.1"],\
           ["react-is", "npm:18.2.0"],\
+          ["react-redux", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:9.2.0"],\
           ["react-router-dom", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:6.23.0"],\
           ["react-simple-pull-to-refresh", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:1.3.3"],\
           ["react-snowfall", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:2.2.0"],\
           ["react-spinners", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:0.13.8"],\
           ["react-tiny-fab", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:4.0.4"],\
+          ["react-transition-group", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:4.4.5"],\
           ["recoil", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:0.7.7"],\
+          ["redux", "npm:5.0.1"],\
           ["sass", "npm:1.76.0"],\
           ["styled-components", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:6.1.9"],\
           ["styled-reset", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:4.5.2"],\
@@ -7318,6 +7396,15 @@ const RAW_RUNTIME_STATE =
         "linkType": "HARD"\
       }]\
     ]],\
+    ["immer", [\
+      ["npm:10.1.1", {\
+        "packageLocation": "./.yarn/cache/immer-npm-10.1.1-973ae10d09-9dacf1e8c2.zip/node_modules/immer/",\
+        "packageDependencies": [\
+          ["immer", "npm:10.1.1"]\
+        ],\
+        "linkType": "HARD"\
+      }]\
+    ]],\
     ["immutable", [\
       ["npm:4.3.4", {\
         "packageLocation": "./.yarn/cache/immutable-npm-4.3.4-2f54cf641b-ea187acc1e.zip/node_modules/immutable/",\
@@ -9350,6 +9437,34 @@ const RAW_RUNTIME_STATE =
         "linkType": "HARD"\
       }]\
     ]],\
+    ["react-redux", [\
+      ["npm:9.2.0", {\
+        "packageLocation": "./.yarn/cache/react-redux-npm-9.2.0-d87bb27c82-b3d2f89f46.zip/node_modules/react-redux/",\
+        "packageDependencies": [\
+          ["react-redux", "npm:9.2.0"]\
+        ],\
+        "linkType": "SOFT"\
+      }],\
+      ["virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:9.2.0", {\
+        "packageLocation": "./.yarn/__virtual__/react-redux-virtual-6c2a35cc57/0/cache/react-redux-npm-9.2.0-d87bb27c82-b3d2f89f46.zip/node_modules/react-redux/",\
+        "packageDependencies": [\
+          ["react-redux", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:9.2.0"],\
+          ["@types/react", "npm:18.3.1"],\
+          ["@types/redux", null],\
+          ["@types/use-sync-external-store", "npm:0.0.6"],\
+          ["react", "npm:18.3.1"],\
+          ["redux", "npm:5.0.1"],\
+          ["use-sync-external-store", "virtual:6c2a35cc5782aa9eeb4d3d81ae3e0d6c79f21470c5ba9e754091b78a190f312b7d86c6e40189789b69df4d2ee549521564a7175a1c36f69b8e31f767b9a6e2c6#npm:1.4.0"]\
+        ],\
+        "packagePeers": [\
+          "@types/react",\
+          "@types/redux",\
+          "react",\
+          "redux"\
+        ],\
+        "linkType": "HARD"\
+      }]\
+    ]],\
     ["react-refresh", [\
       ["npm:0.14.0", {\
         "packageLocation": "./.yarn/cache/react-refresh-npm-0.14.0-78ef5eeb73-75941262ce.zip/node_modules/react-refresh/",\
@@ -9511,6 +9626,36 @@ const RAW_RUNTIME_STATE =
         "linkType": "HARD"\
       }]\
     ]],\
+    ["react-transition-group", [\
+      ["npm:4.4.5", {\
+        "packageLocation": "./.yarn/cache/react-transition-group-npm-4.4.5-98ea4ef96e-ca32d3fd21.zip/node_modules/react-transition-group/",\
+        "packageDependencies": [\
+          ["react-transition-group", "npm:4.4.5"]\
+        ],\
+        "linkType": "SOFT"\
+      }],\
+      ["virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:4.4.5", {\
+        "packageLocation": "./.yarn/__virtual__/react-transition-group-virtual-d8c3884d97/0/cache/react-transition-group-npm-4.4.5-98ea4ef96e-ca32d3fd21.zip/node_modules/react-transition-group/",\
+        "packageDependencies": [\
+          ["react-transition-group", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:4.4.5"],\
+          ["@babel/runtime", "npm:7.26.0"],\
+          ["@types/react", "npm:18.3.1"],\
+          ["@types/react-dom", "npm:18.3.0"],\
+          ["dom-helpers", "npm:5.2.1"],\
+          ["loose-envify", "npm:1.4.0"],\
+          ["prop-types", "npm:15.8.1"],\
+          ["react", "npm:18.3.1"],\
+          ["react-dom", "virtual:a0dcacbc896eccf189caadb21dc337a943d120637d608a889ce480f30d60747b2e369188c5cb540e65c153429bc715bc64aac1ec96b7e01c4ef9b6515ffe1d10#npm:18.3.1"]\
+        ],\
+        "packagePeers": [\
+          "@types/react-dom",\
+          "@types/react",\
+          "react-dom",\
+          "react"\
+        ],\
+        "linkType": "HARD"\
+      }]\
+    ]],\
     ["read-cache", [\
       ["npm:1.0.0", {\
         "packageLocation": "./.yarn/cache/read-cache-npm-1.0.0-00fa89ed05-83a39149d9.zip/node_modules/read-cache/",\
@@ -9588,6 +9733,37 @@ const RAW_RUNTIME_STATE =
         "linkType": "HARD"\
       }]\
     ]],\
+    ["redux", [\
+      ["npm:5.0.1", {\
+        "packageLocation": "./.yarn/cache/redux-npm-5.0.1-f8e6b1cb23-a373f9ed65.zip/node_modules/redux/",\
+        "packageDependencies": [\
+          ["redux", "npm:5.0.1"]\
+        ],\
+        "linkType": "HARD"\
+      }]\
+    ]],\
+    ["redux-thunk", [\
+      ["npm:3.1.0", {\
+        "packageLocation": "./.yarn/cache/redux-thunk-npm-3.1.0-6a8fdd3211-38c563db5f.zip/node_modules/redux-thunk/",\
+        "packageDependencies": [\
+          ["redux-thunk", "npm:3.1.0"]\
+        ],\
+        "linkType": "SOFT"\
+      }],\
+      ["virtual:e9425ef4b85baed0716dc6d7e0b695ce42b8d27bf080c466a53bb65e8c519f735db6f29c965815973ae41f018b8f0567b883db4516a04fb0fb312905576e46ca#npm:3.1.0", {\
+        "packageLocation": "./.yarn/__virtual__/redux-thunk-virtual-d4049ee82f/0/cache/redux-thunk-npm-3.1.0-6a8fdd3211-38c563db5f.zip/node_modules/redux-thunk/",\
+        "packageDependencies": [\
+          ["redux-thunk", "virtual:e9425ef4b85baed0716dc6d7e0b695ce42b8d27bf080c466a53bb65e8c519f735db6f29c965815973ae41f018b8f0567b883db4516a04fb0fb312905576e46ca#npm:3.1.0"],\
+          ["@types/redux", null],\
+          ["redux", "npm:5.0.1"]\
+        ],\
+        "packagePeers": [\
+          "@types/redux",\
+          "redux"\
+        ],\
+        "linkType": "HARD"\
+      }]\
+    ]],\
     ["reflect.getprototypeof", [\
       ["npm:1.0.4", {\
         "packageLocation": "./.yarn/cache/reflect.getprototypeof-npm-1.0.4-fa901640c3-52ff881f62.zip/node_modules/reflect.getprototypeof/",\
@@ -9698,6 +9874,15 @@ const RAW_RUNTIME_STATE =
         "linkType": "HARD"\
       }]\
     ]],\
+    ["reselect", [\
+      ["npm:5.1.1", {\
+        "packageLocation": "./.yarn/cache/reselect-npm-5.1.1-667568f51c-1fdae11a39.zip/node_modules/reselect/",\
+        "packageDependencies": [\
+          ["reselect", "npm:5.1.1"]\
+        ],\
+        "linkType": "HARD"\
+      }]\
+    ]],\
     ["resolve", [\
       ["patch:resolve@npm%3A1.22.8#optional!builtin<compat/resolve>::version=1.22.8&hash=c3c19d", {\
         "packageLocation": "./.yarn/cache/resolve-patch-4254c24959-f345cd37f5.zip/node_modules/resolve/",\
@@ -11149,6 +11334,28 @@ const RAW_RUNTIME_STATE =
         "linkType": "HARD"\
       }]\
     ]],\
+    ["use-sync-external-store", [\
+      ["npm:1.4.0", {\
+        "packageLocation": "./.yarn/cache/use-sync-external-store-npm-1.4.0-176448bea1-08bf581a8a.zip/node_modules/use-sync-external-store/",\
+        "packageDependencies": [\
+          ["use-sync-external-store", "npm:1.4.0"]\
+        ],\
+        "linkType": "SOFT"\
+      }],\
+      ["virtual:6c2a35cc5782aa9eeb4d3d81ae3e0d6c79f21470c5ba9e754091b78a190f312b7d86c6e40189789b69df4d2ee549521564a7175a1c36f69b8e31f767b9a6e2c6#npm:1.4.0", {\
+        "packageLocation": "./.yarn/__virtual__/use-sync-external-store-virtual-1f5aeaed89/0/cache/use-sync-external-store-npm-1.4.0-176448bea1-08bf581a8a.zip/node_modules/use-sync-external-store/",\
+        "packageDependencies": [\
+          ["use-sync-external-store", "virtual:6c2a35cc5782aa9eeb4d3d81ae3e0d6c79f21470c5ba9e754091b78a190f312b7d86c6e40189789b69df4d2ee549521564a7175a1c36f69b8e31f767b9a6e2c6#npm:1.4.0"],\
+          ["@types/react", "npm:18.3.1"],\
+          ["react", "npm:18.3.1"]\
+        ],\
+        "packagePeers": [\
+          "@types/react",\
+          "react"\
+        ],\
+        "linkType": "HARD"\
+      }]\
+    ]],\
     ["util-deprecate", [\
       ["npm:1.0.2", {\
         "packageLocation": "./.yarn/cache/util-deprecate-npm-1.0.2-e3fe1a219c-474acf1146.zip/node_modules/util-deprecate/",\
diff --git a/.yarn/cache/@babel-runtime-npm-7.26.0-9afa3c4ef6-9f4ea1c1d5.zip b/.yarn/cache/@babel-runtime-npm-7.26.0-9afa3c4ef6-9f4ea1c1d5.zip
new file mode 100644
index 00000000..59f3301c
Binary files /dev/null and b/.yarn/cache/@babel-runtime-npm-7.26.0-9afa3c4ef6-9f4ea1c1d5.zip differ
diff --git a/.yarn/cache/@reduxjs-toolkit-npm-2.5.0-9bdc99574d-b24ee7e89b.zip b/.yarn/cache/@reduxjs-toolkit-npm-2.5.0-9bdc99574d-b24ee7e89b.zip
new file mode 100644
index 00000000..f7c76f6a
Binary files /dev/null and b/.yarn/cache/@reduxjs-toolkit-npm-2.5.0-9bdc99574d-b24ee7e89b.zip differ
diff --git a/.yarn/cache/@types-react-transition-group-npm-4.4.11-dda183055d-a7f4de6e5f.zip b/.yarn/cache/@types-react-transition-group-npm-4.4.11-dda183055d-a7f4de6e5f.zip
new file mode 100644
index 00000000..f12bcf24
Binary files /dev/null and b/.yarn/cache/@types-react-transition-group-npm-4.4.11-dda183055d-a7f4de6e5f.zip differ
diff --git a/.yarn/cache/@types-use-sync-external-store-npm-0.0.6-9e5c635381-a95ce33066.zip b/.yarn/cache/@types-use-sync-external-store-npm-0.0.6-9e5c635381-a95ce33066.zip
new file mode 100644
index 00000000..a29bf3d6
Binary files /dev/null and b/.yarn/cache/@types-use-sync-external-store-npm-0.0.6-9e5c635381-a95ce33066.zip differ
diff --git a/.yarn/cache/dom-helpers-npm-5.2.1-b38bb4470b-bed2341adf.zip b/.yarn/cache/dom-helpers-npm-5.2.1-b38bb4470b-bed2341adf.zip
new file mode 100644
index 00000000..409ad179
Binary files /dev/null and b/.yarn/cache/dom-helpers-npm-5.2.1-b38bb4470b-bed2341adf.zip differ
diff --git a/.yarn/cache/immer-npm-10.1.1-973ae10d09-9dacf1e8c2.zip b/.yarn/cache/immer-npm-10.1.1-973ae10d09-9dacf1e8c2.zip
new file mode 100644
index 00000000..7ba8e39b
Binary files /dev/null and b/.yarn/cache/immer-npm-10.1.1-973ae10d09-9dacf1e8c2.zip differ
diff --git a/.yarn/cache/react-redux-npm-9.2.0-d87bb27c82-b3d2f89f46.zip b/.yarn/cache/react-redux-npm-9.2.0-d87bb27c82-b3d2f89f46.zip
new file mode 100644
index 00000000..bb3f3f81
Binary files /dev/null and b/.yarn/cache/react-redux-npm-9.2.0-d87bb27c82-b3d2f89f46.zip differ
diff --git a/.yarn/cache/react-transition-group-npm-4.4.5-98ea4ef96e-ca32d3fd21.zip b/.yarn/cache/react-transition-group-npm-4.4.5-98ea4ef96e-ca32d3fd21.zip
new file mode 100644
index 00000000..2bb121f0
Binary files /dev/null and b/.yarn/cache/react-transition-group-npm-4.4.5-98ea4ef96e-ca32d3fd21.zip differ
diff --git a/.yarn/cache/redux-npm-5.0.1-f8e6b1cb23-a373f9ed65.zip b/.yarn/cache/redux-npm-5.0.1-f8e6b1cb23-a373f9ed65.zip
new file mode 100644
index 00000000..54d851c0
Binary files /dev/null and b/.yarn/cache/redux-npm-5.0.1-f8e6b1cb23-a373f9ed65.zip differ
diff --git a/.yarn/cache/redux-thunk-npm-3.1.0-6a8fdd3211-38c563db5f.zip b/.yarn/cache/redux-thunk-npm-3.1.0-6a8fdd3211-38c563db5f.zip
new file mode 100644
index 00000000..fda88748
Binary files /dev/null and b/.yarn/cache/redux-thunk-npm-3.1.0-6a8fdd3211-38c563db5f.zip differ
diff --git a/.yarn/cache/reselect-npm-5.1.1-667568f51c-1fdae11a39.zip b/.yarn/cache/reselect-npm-5.1.1-667568f51c-1fdae11a39.zip
new file mode 100644
index 00000000..e94331bf
Binary files /dev/null and b/.yarn/cache/reselect-npm-5.1.1-667568f51c-1fdae11a39.zip differ
diff --git a/.yarn/cache/use-sync-external-store-npm-1.4.0-176448bea1-08bf581a8a.zip b/.yarn/cache/use-sync-external-store-npm-1.4.0-176448bea1-08bf581a8a.zip
new file mode 100644
index 00000000..d965899e
Binary files /dev/null and b/.yarn/cache/use-sync-external-store-npm-1.4.0-176448bea1-08bf581a8a.zip differ
diff --git a/package.json b/package.json
index 98bd6e95..b2c6d6c7 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,7 @@
   },
   "dependencies": {
     "@builder.io/partytown": "^0.10.2",
+    "@reduxjs/toolkit": "^2.5.0",
     "@tanstack/react-query": "^5.32.1",
     "@types/node": "^20.11.1",
     "axios": "^1.6.8",
@@ -26,12 +27,15 @@
     "react-i18next": "^14.0.5",
     "react-icons": "^5.0.1",
     "react-is": "^18.2.0",
+    "react-redux": "^9.2.0",
     "react-router-dom": "^6.23.0",
     "react-simple-pull-to-refresh": "^1.3.3",
     "react-snowfall": "^2.2.0",
     "react-spinners": "^0.13.8",
     "react-tiny-fab": "^4.0.4",
+    "react-transition-group": "^4.4.5",
     "recoil": "^0.7.7",
+    "redux": "^5.0.1",
     "sass": "^1.76.0",
     "styled-components": "^6.1.9",
     "styled-reset": "^4.5.2",
@@ -40,6 +44,7 @@
   "devDependencies": {
     "@types/react": "18.3.1",
     "@types/react-dom": "18.3.0",
+    "@types/react-transition-group": "^4",
     "@types/styled-components": "^5.1.34",
     "@typescript-eslint/eslint-plugin": "^7.8.0",
     "@typescript-eslint/parser": "^7.8.0",
diff --git a/src/App.tsx b/src/App.tsx
index 6c223f40..337753eb 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,14 +1,17 @@
-import React, { lazy, Suspense, useEffect, useState } from 'react'
+import React, { lazy, Suspense, useEffect, useState } from 'react' 
 import { useTranslation } from 'react-i18next'
 import { BrowserRouter, Link, Navigate, Route, Routes } from 'react-router-dom'
 import PullToRefresh from 'react-simple-pull-to-refresh'
+import {Transition} from 'react-transition-group'
 import styled, { css } from 'styled-components'
 import { Reset } from 'styled-reset'
 import tw from 'twin.macro'
 
+import Arrow from '/image/expand_less_white_48dp.svg'
 import HelpImg from '/public/image/helpblack.svg?react'
 import { Shuttle } from '@/components'
 import Fabs from '@/components/fab/fab'
+import { RouteMap } from '@/components/routemap/RouteMap'
 import { useDarkMode } from '@/components/useDarkMode'
 import { useDarkmodeContext } from '@/context/ThemeContext'
 import { StopLocation } from '@/data'
@@ -115,14 +118,24 @@ const HelpIcon = styled(HelpImg)`
   ${tw`bottom-3 right-0 absolute h-9 w-9 hsm:h-8 hsm:w-8 cursor-default`} drag-save-n
 `
 
-const RouteIndexCardView = styled(CardView)`
-  ${tw`p-4 h-12 hm:p-2 flex`}
+const RouteIndexCardView = styled(CardView)<{status: string}>`
+  ${tw`relative p-4 h-12 hsm:h-20 hm:p-2 transition-[height] ease-in-out duration-150`}
+  ${(props) => props.status === 'entered' ? tw`h-48 hm:h-48` : tw`h-16 hm:h-20`}
 `
 
 const RouteIndexWrapper = styled.div`
   ${tw`flex flex-wrap place-content-center items-center`}
 `
 
+const RouteIndexContainer = styled.div<{status: string}>`
+  ${tw`absolute top-0 inset-0 flex place-content-center items-center transition ease-in-out duration-300`}
+  ${(props) => props.status === 'exited' ? tw`opacity-100` : tw`opacity-0`}
+  ${(props) => props.status === 'entered' ? tw`hidden`: tw``}
+`
+const RouteToggleImage = styled.img<{status: string}>`
+  ${tw`absolute bottom-0 inset-x-0 rotate-180 m-auto flex invert dark:invert-0 h-6 w-6 opacity-30 transition ease-in-out duration-150`}
+  ${(props) => props.status === 'entered'? tw`rotate-0`:tw`rotate-180`}
+`
 const SegmentedControl = styled.div`
   ${tw`
     relative p-1 w-[16rem] hsm:w-[14rem] text-sm hsm:text-xs items-center grid grid-cols-2 gap-3 rounded-3xl bg-control-main will-change-transform
@@ -183,6 +196,7 @@ function App() {
     //window.localStorage.getItem('xmas_alert') === null,
     window.localStorage.getItem('spring_2025') === null,
   )
+  const [routeCardClick, setRouteCardClick] = useState<boolean>(false)
 
   const handleContextMenu = (e: { preventDefault: () => void }) => {
     e.preventDefault()
@@ -431,26 +445,37 @@ function App() {
                           </SegmentedControl>
                         </SegmentedControlWrapper>
                       </MainCardView>
-
-                      <RouteIndexCardView>
-                        <RouteIndexWrapper>
-                          <CycleCircle theme={theme} />
-                          <RouteText>{t('cycle_index')}</RouteText>
-                        </RouteIndexWrapper>
-                        <RouteIndexWrapper>
-                          <DirectCircle theme={theme} />
-                          <RouteText>{t('direct_index')}</RouteText>
-                        </RouteIndexWrapper>
-                        <RouteIndexWrapper>
-                          <YesulinCircle theme={theme} />
-                          <RouteText>{t('yesulin_index')}</RouteText>
-                        </RouteIndexWrapper>
-                        <RouteIndexWrapper>
-                          <JungangCircle theme={theme} />
-                          <RouteText>{t('jungang_index')}</RouteText>
-                        </RouteIndexWrapper>
-                      </RouteIndexCardView>
-
+                        <Transition
+                          in={routeCardClick}
+                          timeout={150}
+                        >
+                          {(state) => (
+                            <>
+                          <RouteIndexCardView status={state} onClick={() => {setRouteCardClick(!routeCardClick)}}>
+                            <RouteIndexContainer status={state}>
+                              <RouteIndexWrapper>
+                                <CycleCircle theme={theme}/>
+                                <RouteText>{t('cycle_index')}</RouteText>
+                              </RouteIndexWrapper>
+                              <RouteIndexWrapper>
+                                <DirectCircle theme={theme}/>
+                                <RouteText>{t('direct_index')}</RouteText>
+                              </RouteIndexWrapper>
+                              <RouteIndexWrapper>
+                                <YesulinCircle theme={theme}/>
+                                <RouteText>{t('yesulin_index')}</RouteText>
+                              </RouteIndexWrapper>
+                              <RouteIndexWrapper>
+                                <JungangCircle theme={theme}/>
+                                <RouteText>{t('jungang_index')}</RouteText>
+                              </RouteIndexWrapper>
+                            </RouteIndexContainer>
+                          <RouteMap status={state} tab={tab}/>
+                          <RouteToggleImage src={Arrow} status={state}/>
+                          </RouteIndexCardView>
+                            </>
+                          )}
+                        </Transition>
                       <StationButtonWrapper>
                         <Button
                           id="shuttlecoke_o"
diff --git a/src/app/components/fulltime/FullTime.tsx b/src/app/components/fulltime/FullTime.tsx
index d5875346..1130b930 100644
--- a/src/app/components/fulltime/FullTime.tsx
+++ b/src/app/components/fulltime/FullTime.tsx
@@ -100,6 +100,8 @@ const TimeBoxInner = styled.div<{ $maxChips: number }>`
       return tw`h-28 hm:h-24`
     } else if ($maxChips === 3) {
       return tw`h-32 hm:h-28`
+    } else if ($maxChips === 4) {
+      return tw`h-40 hm:h-32`
     }
   }}
 `
@@ -121,10 +123,10 @@ const TimetableContainer = styled.div`
   ${tw`pb-6`}
 `
 
-const YesulinMinuteWrapper = styled.span<{ $itemCount: number }>`
-  ${tw`inline-block text-green-500`}
-  ${({ $itemCount }) => ($itemCount === 0 ? tw`hidden` : undefined)}
-`
+// const YesulinMinuteWrapper = styled.span<{ $itemCount: number }>`
+//   ${tw`inline-block text-green-500`}
+//   ${({ $itemCount }) => ($itemCount === 0 ? tw`hidden` : undefined)}
+// `
 
 const ComboBox = (props: {
   type: string
@@ -175,19 +177,25 @@ const TimeBox = (props: OrganizedTimetables) => {
             <Chip className="bg-chip-purple">{t('cycle_ja')}</Chip>
             <MinuteContainer>{props.jungang.join(' ')}</MinuteContainer>
           </TimeBoxBodyGrid>
+          <TimeBoxBodyGrid $itemCount={props.directY.length}>
+            <Chip className="bg-chip-green">{t('yesul')}</Chip>
+            <MinuteContainer>{props.directY.join(' ')}</MinuteContainer>
+          </TimeBoxBodyGrid>
           <TimeBoxBodyGrid $itemCount={props.direct.length}>
-            <Chip className="bg-chip-blue">{t('direct')}</Chip>
+            <Chip className={props.isShuttleI?"bg-chip-orange":"bg-chip-blue"}>{t('direct')}</Chip>
             <DirectMinuteContainer>
               {props.direct.map((time, idx) => {
-                return (
-                  <React.Fragment key={idx}>
-                    <span>{time} </span>
-                  </React.Fragment>
+                let isExist = false
+                props.directY.map((ytime) => {
+                  time === ytime ? (isExist = true) : null
+                })
+                return(
+                  isExist ? null :
+                <React.Fragment key={idx}>
+                  <span>{time} </span>
+                </React.Fragment>
                 )
               })}
-              <YesulinMinuteWrapper $itemCount={props.directY.length}>
-                {`${props.directY.join(' ')} (${t('to_yesulin')})`}
-              </YesulinMinuteWrapper>
             </DirectMinuteContainer>
           </TimeBoxBodyGrid>
         </TimeBoxBody>
@@ -251,6 +259,7 @@ const FullTime = () => {
         circle: [],
         directY: [],
         jungang: [],
+        isShuttleI: false,
         count: 0,
       }
       schedules.forEach((schedule) => {
@@ -266,6 +275,9 @@ const FullTime = () => {
           single.circle.push(schedule.time.split(':')[1])
         } else if (schedule.type === 'DHJ') {
           single.jungang.push(schedule.time.split(':')[1])
+        } 
+        if (schedule.type === 'R'){
+          single.isShuttleI = true
         }
       })
 
@@ -278,12 +290,15 @@ const FullTime = () => {
       if (single.jungang.length !== 0) {
         single.count++
       }
+      if (single.directY.length !== 0) {
+        single.count++
+      }
 
       filteredByType.push(single)
       return <></>
       // [{ time: '08', direct: ["08:00", "08:10", ...], circle: [], directY: ["08:20", "08:50"] }, { time: '09', direct: [], circle: [], directY: [] }, ...]
     })
-
+  
     const maxCount = filteredByType.reduce(
       (prev, curr) => (prev.count > curr.count ? prev : curr),
       { count: 0 },
@@ -292,20 +307,21 @@ const FullTime = () => {
     if (maxCount > countChip) {
       setCountChip(maxCount)
     }
-
+    
     return (
       <div className="grid grid-flow-row gap-2">
         {filteredByType.map((schedule) => {
           // if schedule.direct.length === 0
           return (
             <React.Fragment key={schedule.time}>
-              {schedule.direct.length + schedule.circle.length === 0 ? null : (
+              {schedule.direct.length + schedule.circle.length === 0 && (schedule.directY.length === 0 && schedule.jungang.length === 0) ? null : (
                 <TimeBox
                   time={schedule.time}
                   direct={schedule.direct}
                   directY={schedule.directY}
                   circle={schedule.circle}
                   jungang={schedule.jungang}
+                  isShuttleI={schedule.isShuttleI}
                   count={countChip}
                 />
               )}
diff --git a/src/app/components/index.ts b/src/app/components/index.ts
index f65b9907..1d85ab9e 100644
--- a/src/app/components/index.ts
+++ b/src/app/components/index.ts
@@ -1,2 +1,2 @@
 export * from './fab/fab'
-export * from './shuttle/Shuttle'
+export * from './shuttle/Shuttle'
\ No newline at end of file
diff --git a/src/app/components/lang/lang.en.json b/src/app/components/lang/lang.en.json
index fd350479..a72b7020 100644
--- a/src/app/components/lang/lang.en.json
+++ b/src/app/components/lang/lang.en.json
@@ -9,6 +9,9 @@
     "cycle": "Cycle",
     "direct": "Direct",    
     "cycle_ja": "Direct",
+    "yesul": "Yesulin",
+    "jung": "Jungang",
+
     "NA": "N/A",
     "left": "Left",
     "departure": "Depart",
diff --git a/src/app/components/lang/lang.ko.json b/src/app/components/lang/lang.ko.json
index 7eae6ffb..ff9d2b0c 100644
--- a/src/app/components/lang/lang.ko.json
+++ b/src/app/components/lang/lang.ko.json
@@ -9,6 +9,9 @@
     "cycle": "순환",
     "direct": "직행",
     "cycle_ja": "중앙역",
+    "yesul": "예술인",
+
+    "jung": "중앙역",
     "NA": "미운행",
     "left": "후 출발",
     "departure": "출발",
diff --git a/src/app/components/routemap/RouteMap.tsx b/src/app/components/routemap/RouteMap.tsx
new file mode 100644
index 00000000..b4f7d32d
--- /dev/null
+++ b/src/app/components/routemap/RouteMap.tsx
@@ -0,0 +1,361 @@
+import React, { useCallback, useEffect,useRef, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useSelector } from 'react-redux';
+import styled from 'styled-components';
+import tw from 'twin.macro';
+
+import { CircleAnimate } from '@/data';
+import { SingleShuttleSchedule } from "@/data";
+
+const Circle = styled.span`
+  ${tw`
+    flex rounded-full inline-block
+    h-3 w-3 rt1:h-2.5 rt1:w-2.5
+    z-10
+  `}
+`
+
+const CycleCircle = styled(Circle)`
+  ${tw`bg-chip-red mx-2`}
+`
+
+const DirectCircle = styled(Circle)`
+  ${tw`bg-chip-blue mx-2`}
+`
+
+const YesulinCircle = styled(Circle)`
+  ${tw`bg-chip-green mx-2`}
+`
+
+const JungangCircle = styled(Circle)`
+  ${tw`bg-chip-purple mx-2`}
+`
+const EndCircle = styled(Circle)`
+    ${tw`bg-chip-orange mx-2`}
+`
+const RouteRowsContainer = styled.div`
+${tw`grid grid-rows-5 gap-2`}
+`
+const RouteColsContainer = styled.div`
+${tw`grid grid-cols-6 place-items-center`}
+`
+const RouteTextContainer =  styled.div<{lang:string}>`
+${tw`whitespace-nowrap text-center`}
+${(props) => props.lang === 'ko' ? tw`text-[15px] hm:text-[13px]` : tw`text-[12px] rt1:text-[9px]`}
+`
+const RouteStations = styled.div`
+${tw`transition duration-150 ease-in-out flex relative`}
+`
+const RouteMethod = styled.div`
+${tw`text-center rounded-full py-1 w-16 hm:w-12 hm:text-xs self-center text-black`}
+`
+const MainContainer = styled.div<{status: string}>`
+${tw`transition duration-150 ease-in-out mx-auto`}
+    ${(props) => props.status === 'entered' || props.status === 'exit' ? tw`opacity-100` : tw`opacity-0`}
+`
+const RouteLine = styled.div`
+    ${tw`absolute transition duration-150 ease-in-out h-[3px] z-1`}
+
+`
+export const RouteMap = (props: {
+    status:string,
+    tab: string
+}) => {
+    interface RootState {
+        actions: SingleShuttleSchedule[]
+    }
+    const timetable = useSelector((state: RootState) => state.actions)
+    const { t, i18n } = useTranslation()
+    // dots
+    const [direct, setDirect] = useState<JSX.Element[]>([])
+    const [cycle, setCycle] = useState<JSX.Element[]>([])
+    const [yesulin, setYesulin] = useState<JSX.Element[]>([])
+    const [jungang, setJungang] = useState<JSX.Element[]>([])
+    // lines
+    const [dirLine, setDirLine] = useState<JSX.Element[]>([])
+    const [cycLine, setCycLine] = useState<JSX.Element[]>([])
+    const [yesLine, setYesLine] = useState<JSX.Element[]>([])
+    const [junLine, setJunLine] = useState<JSX.Element[]>([])
+    // dot refs
+    const refdir = useRef<Array<HTMLDivElement>>([])
+    const refcyc = useRef<Array<HTMLDivElement>>([])
+    const refyes = useRef<Array<HTMLDivElement>>([])
+    const refjun = useRef<Array<HTMLDivElement>>([])
+    // line refs
+    const linedir = useRef<Array<HTMLDivElement>>([])
+    const linecyc = useRef<Array<HTMLDivElement>>([])
+    const lineyes = useRef<Array<HTMLDivElement>>([])
+    const linejun = useRef<Array<HTMLDivElement>>([])
+    // langtest
+    const lang = useRef<Array<HTMLParagraphElement | null>>([])
+    const directInput = useCallback(() => {
+        const arrdir = []
+        const arrcyc = []
+        const arryes = []
+        const arrjun = []
+        for(let i = 0; i < 5; i++){
+            if(i === 4){
+                arrdir.push(<RouteStations key={i} ref={d => d!= null?refdir.current[i] = d:null}><EndCircle/></RouteStations>)
+                continue
+            }
+            arrdir.push(<RouteStations key={i} ref={d => d!= null?refdir.current[i] = d:null}><DirectCircle/></RouteStations>)
+        }
+        for(let i = 0; i < 6; i++){
+            if(i >= 2 && i <= 4){
+                if(i === 2){
+                    arrcyc.push(
+                        <div key={i} className='col-span-2 grid grid-cols-3 w-[75%] place-items-center'>
+                            <RouteStations key={i} ref={d => d!= null?refcyc.current[i] = d:null}><CycleCircle/></RouteStations>
+                            <RouteStations key={i+1} ref={d => d!= null?refcyc.current[i+1] = d:null}><CycleCircle className='grid grid-rows-2 relative'><p key={0} ref={d => lang.current[0] = d} className='absolute text-xs top-[-20px] left-[-14px] text-center w-10 text-chip-green'>{t('yesul')}</p></CycleCircle></RouteStations>
+                            <RouteStations key={i+2} ref={d => d!= null?refcyc.current[i+2] = d:null}><CycleCircle/></RouteStations>
+                        </div>
+                    )
+                    arryes.push(
+                        <div key={i} className='col-span-2 grid grid-cols-3 w-[75%] place-items-center'>
+                            <RouteStations key={i} ref={d => d!= null?refyes.current[i] = d:null} title='skip'><YesulinCircle className='opacity-0'/></RouteStations>
+                            <RouteStations key={i+1} ref={d => d!= null?refyes.current[i+1] = d:null}><YesulinCircle className='grid grid-rows-2 relative'><p key={1} ref={d => lang.current[1] = d} className='absolute text-xs top-[-20px] left-[-14px] text-center w-10 text-chip-green'>{t('yesul')}</p></YesulinCircle></RouteStations>
+                            <RouteStations key={i+2} ref={d => d!= null?refyes.current[i+2] = d:null}><YesulinCircle/></RouteStations>
+                        </div>
+                    )
+                    arrjun.push(
+                        <div key={i} className='col-span-2 grid grid-cols-3 w-[75%] place-items-center'>
+                            <RouteStations key={i} ref={d => d!= null?refjun.current[i] = d:null}><JungangCircle/></RouteStations>
+                            <RouteStations key={i+1} ref={d => d!= null?refjun.current[i+1] = d:null}><JungangCircle className='grid grid-rows-2 relative'><p key={2} ref={d => lang.current[2] = d} className='absolute text-xs top-[-20px] left-[-14px] text-center w-10 text-chip-purple'>{t('jung')}</p></JungangCircle></RouteStations>
+                            <RouteStations key={i+2} ref={d => d!= null?refjun.current[i+2] = d:null}><JungangCircle/></RouteStations>
+                        </div>
+                    )
+                }
+                continue
+            }
+            if(i === 5){
+                arrcyc.push(<RouteStations key={i} ref={d => d!= null?refcyc.current[i] = d:null}><EndCircle/></RouteStations>)
+                arryes.push(<RouteStations key={i} ref={d => d!= null?refyes.current[i] = d:null}><EndCircle/></RouteStations>)
+                arrjun.push(<RouteStations key={i} ref={d => d!= null?refjun.current[i] = d:null}><EndCircle/></RouteStations>)
+                continue
+            }
+            arrcyc.push(<RouteStations key={i} ref={d => d!= null?refcyc.current[i] = d:null}><CycleCircle/></RouteStations>)
+            arryes.push(<RouteStations key={i} ref={d => d!= null?refyes.current[i] = d:null}><YesulinCircle/></RouteStations>)
+            arrjun.push(<RouteStations key={i} ref={d => d!= null?refjun.current[i] = d:null}><JungangCircle/></RouteStations>)
+        }
+        setDirect(arrdir)
+        setCycle(arrcyc)
+        setYesulin(arryes)
+        setJungang(arrjun)
+    },[t])
+        const directLineInput = useCallback(() => {
+            const arrdir = []
+            const arrcyc = []
+            const arryes = []
+            const arrjun = []
+            for(let i = 0; i < 4; i++){
+                if(i === 3){
+                    arrdir.push(
+                        <RouteLine className={'bg-chip-orange'} key={i} ref={d=> d!= null?linedir.current[i] = d:null}></RouteLine>
+                    )
+                    continue
+                }
+                arrdir.push(
+                    <RouteLine className={'bg-chip-blue'} key={i} ref={d=> d!= null?linedir.current[i] = d:null}></RouteLine>
+                )
+            }
+            for(let i = 0; i < 5; i++){
+                if(i === 4){
+                    arrcyc.push(<RouteLine className={'bg-chip-orange'} key={i} ref={d=> d!= null?linecyc.current[i] = d:null}></RouteLine>)
+                    arryes.push(<RouteLine className={'bg-chip-orange'} key={i} ref={d=> d!= null?lineyes.current[i] = d:null}></RouteLine>)
+                    arrjun.push(<RouteLine className={'bg-chip-orange'} key={i} ref={d=> d!= null?linejun.current[i] = d:null}></RouteLine>)
+                    continue
+                }
+                arrcyc.push(
+                    <RouteLine className={'bg-chip-red'} key={i} ref={d=> d!= null?linecyc.current[i] = d:null}></RouteLine>
+                )
+                arryes.push(
+                    <RouteLine className={'bg-chip-green'} key={i} ref={d=> d!= null?lineyes.current[i] = d:null}></RouteLine>
+                )
+                arrjun.push(
+                    <RouteLine className={'bg-chip-purple'} key={i} ref={d=> d!= null?linejun.current[i] = d:null}></RouteLine>
+                )
+            }
+            setDirLine(arrdir)
+            setCycLine(arrcyc)
+            setYesLine(arryes)
+            setJunLine(arrjun)
+        },[])
+        const responsiveLines = (refs:React.MutableRefObject<Array<HTMLDivElement>>, lines:React.MutableRefObject<Array<HTMLDivElement>>) => {
+            if(refs.current.length > 0 && lines.current.length > 0){
+                for(let i = 1; i <= lines.current.length; i++){
+                    const rectA = {"left":refs.current[i-1]?.offsetLeft, "top":refs.current[i-1]?.offsetTop, "width":refs.current[i-1]?.offsetWidth, "height":refs.current[i-1]?.offsetHeight}
+                    const rectB = {"left":refs.current[i]?.offsetLeft, "top":refs.current[i]?.offsetTop, "width":refs.current[i]?.offsetWidth, "height":refs.current[i]?.offsetHeight}
+                    const x1 = (rectA.left ?? 0) + (rectA.width ?? 0) / 2
+                    const y1 = (rectA.top ?? 0) + (rectA.height ?? 0) / 2
+                    const x2 = (rectB.left ?? 0) + (rectB.width ?? 0) / 2
+                    const y2 = (rectB.top ?? 0) + (rectB.height ?? 0) / 2
+                    
+                    const d = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2))
+                    lines.current[i-1].style.width = `${d}px`
+                    lines.current[i-1].style.top = `${(refs.current[i-1]?.offsetTop ?? 0)+4}px`
+                    const dotchild = refs.current[i-1]?.firstChild as HTMLElement
+                    if(dotchild != undefined){
+                        lines.current[i-1].style.left = `${refs.current[i-1]?.offsetLeft+dotchild.offsetLeft+1}px`
+                    }
+                }
+            }
+        }
+        const updateLines = useCallback(() => {
+            responsiveLines(refdir, linedir)
+            responsiveLines(refcyc, linecyc)
+            responsiveLines(refyes, lineyes)
+            responsiveLines(refjun, linejun)
+        },[])
+        const fetchLanguages = useCallback(() => {
+            lang.current.forEach((element:HTMLParagraphElement | null, index:number) => {
+                if(element != undefined){
+                    if(index <= 1){
+                        element.innerText = t('yesul')
+                    } else {
+                        element.innerText = t('jung')
+                    }
+                }
+            });
+        }, [t])
+        const circleAnimation = (props: CircleAnimate) => {
+            for(let i = props.index; i <= props.index+1; i++){
+                if(props.ref.current[i].childNodes.length <= 1){
+                    if(i === 5 || (i === 4 && props.chipColor === 'bg-chip-blue')){
+                        const pingCircle = document.createElement('div')
+                        pingCircle.classList.add('animate-ping', 'absolute', 'rounded-full', 'inline-flex', 'h-3', 'w-3', 'rt1:h-2.5', 'rt1:w-2.5', 'z-11', 'bg-chip-orange', 'mx-2');
+                        if(props.ref.current[i].title === 'skip'){
+                            props.ref.current[i+1]?.append(pingCircle)
+                        } else{
+                            props.ref.current[i]?.append(pingCircle)
+                        }
+                        props.ref.current[i]?.append(pingCircle)
+                        continue
+
+                    }
+                    const pingCircle = document.createElement('div')
+                    pingCircle.classList.add('animate-ping', 'absolute', 'rounded-full', 'inline-flex', 'h-3', 'w-3', 'rt1:h-2.5', 'rt1:w-2.5', 'z-11', props.chipColor, 'mx-2');
+                    if(props.ref.current[i].title === 'skip'){
+                        props.ref.current[i+1]?.append(pingCircle)
+                    } else{
+                        props.ref.current[i]?.append(pingCircle)
+                    }
+                }
+            }
+        }
+        const circleAnimationRemove = (refs: React.MutableRefObject<Array<HTMLDivElement>>) => {
+            for(const refr of refs.current){
+                if(refr?.childNodes.length > 1){
+                    refr?.removeChild(refr?.lastChild as Node)
+                }
+            }
+        //    ref.current[i]?.removeChild(props.ref.current[i]?.lastChild as Node)
+                
+        }
+        const timetableType = (type: string, index:number) => {
+            if(type === 'C'){
+                return {ref: refcyc, index: index, chipColor: 'bg-chip-red'}
+            } else if(type === 'DHJ'){
+                return {ref: refjun, index: index, chipColor: 'bg-chip-purple'}
+            } else if(type === 'DY'){
+                return {ref: refyes, index: index, chipColor: 'bg-chip-green'}
+            }else {
+                return {ref: refdir, index: index, chipColor: 'bg-chip-blue'}
+            }
+        }
+        useEffect(() => {
+            fetchLanguages()
+        },[i18n.language, fetchLanguages])
+        useEffect(() => {
+            directInput()
+            directLineInput()
+            window.addEventListener("resize",updateLines)
+            return () => {
+                window.removeEventListener("resize", updateLines);
+            }
+        }, [updateLines, directInput, directLineInput])
+        useEffect(() => {
+            const circleAnimationRemoveAll = () => {
+                circleAnimationRemove(refdir)
+                circleAnimationRemove(refcyc)
+                circleAnimationRemove(refyes)
+                circleAnimationRemove(refjun)
+            }
+            const updateHighlight = () => {
+                if(linecyc.current.length > 0 && linedir.current.length > 0 && lineyes.current.length > 0 && linejun.current.length > 0){
+                    circleAnimationRemoveAll()
+                    if(timetable.length > 0){
+                        if(props.tab === 'shuttlecoke_o'){
+                            circleAnimation(timetableType(timetable[0].type,1))
+                        } else if(props.tab === 'subway'){
+                            circleAnimation(timetableType(timetable[0].type,2))
+                        } else if(props.tab === 'yesulin'){
+                            circleAnimation(timetableType(timetable[0].type,3))
+                        } else if(props.tab === 'jungang'){
+                            circleAnimation({ref: refjun, index: 3, chipColor: 'bg-chip-purple'})
+                        } else if(props.tab === 'shuttlecoke_i'){
+                            if(timetable[0].type === 'NA'){
+                                return
+                            }
+                            circleAnimation({ref: refdir, index: 3, chipColor: 'bg-chip-blue'})
+                            circleAnimation({ref: refjun, index: 4, chipColor: 'bg-chip-purple'})
+                            circleAnimation({ref: refcyc, index: 4, chipColor: 'bg-chip-red'})
+                            circleAnimation({ref: refyes, index: 4, chipColor: 'bg-chip-green'})
+                        } else {
+                            circleAnimation(timetableType(timetable[0].type,0))
+                        }
+                    }
+                }
+            }
+            updateLines()
+            updateHighlight()
+        },[direct, cycle, yesulin, jungang, dirLine, cycLine, yesLine, junLine, props.tab, timetable, updateLines])
+    return (
+        <MainContainer status={props.status}>
+            <RouteRowsContainer>
+                <RouteColsContainer>
+                    <RouteTextContainer lang={i18n.language} className='col-start-2'>{t('dest_dorm')}</RouteTextContainer>
+                    <RouteTextContainer lang={i18n.language}>{t('dest_shuttle_o')}</RouteTextContainer>
+                    <RouteTextContainer lang={i18n.language}>{t('dest_subway')}</RouteTextContainer>
+                    <RouteTextContainer lang={i18n.language}>{t('dest_shuttle_o')}</RouteTextContainer>
+                    <RouteTextContainer lang={i18n.language}>{t('dest_dorm')}</RouteTextContainer>
+                </RouteColsContainer>
+                <RouteColsContainer className='grid grid-cols-6'>
+                <RouteMethod className='bg-chip-blue'>{t('direct')}</RouteMethod>
+                    {direct.map((item) => {
+                        return item
+                    })}
+                    {dirLine.map((item) => {
+                        return item
+                    })}
+                </RouteColsContainer>
+                <RouteColsContainer>
+                <RouteMethod className='bg-chip-red'>{t('cycle')}</RouteMethod>
+                    {cycle.map((item) => {
+                        return item
+                    })}
+                    {cycLine.map((item) => {
+                        return item
+                    })}
+                </RouteColsContainer>
+                <RouteColsContainer>
+                <RouteMethod className='bg-chip-green'>{t('yesul')}</RouteMethod>
+                    {yesulin.map((item) => {
+                        return item
+                    })}
+                    {yesLine.map((item) => {
+                        return item
+                    })}
+                </RouteColsContainer>
+                <RouteColsContainer>
+                <RouteMethod className='bg-chip-purple'>{t('jung')}</RouteMethod>
+                    {jungang.map((item) => {
+                        return item
+                    })}
+                    {junLine.map((item) => {
+                        return item
+                    })}
+                </RouteColsContainer>
+            </RouteRowsContainer>
+        </MainContainer>
+    )
+}
diff --git a/src/app/components/shuttle/Shuttle.tsx b/src/app/components/shuttle/Shuttle.tsx
index ae9cf528..ecbd5e76 100644
--- a/src/app/components/shuttle/Shuttle.tsx
+++ b/src/app/components/shuttle/Shuttle.tsx
@@ -4,6 +4,7 @@ import customParse from 'dayjs/plugin/customParseFormat'
 import { t } from 'i18next'
 import React, { useEffect, useState } from 'react'
 import { useTranslation } from 'react-i18next'
+import { useDispatch } from 'react-redux'
 import { SyncLoader } from 'react-spinners'
 import styled from 'styled-components'
 import tw from 'twin.macro'
@@ -22,6 +23,7 @@ import {
 import { seasonKeys } from '@/data/shuttle/season'
 import { weekKeys } from '@/data/shuttle/week'
 import { settingAPI, shuttleAPI } from '@/network'
+import { updateActions } from '@/reducer/store'
 
 dayjs.extend(customParse)
 
@@ -114,7 +116,6 @@ const isWeekend = (): boolean => {
 
 const getSeason = (setting: Settings | null): [Season, Week] => {
   const today = dayjs()
-
   if (setting === null) {
     // Error fetching settings
     return [seasonKeys.UNKNOWN, weekKeys.UNKNOWN]
@@ -336,12 +337,16 @@ const ColoredChip = ({ chipType }: ChipType) => {
     return <Chip className="bg-chip-purple">{busTypeToText(chipType)}</Chip>
   } else if (chipType == 'DY') {
     return <Chip className="bg-chip-green">{busTypeToText(chipType)}</Chip>
+  } else if (chipType == 'R' || chipType == 'NA') {
+    return <Chip className="bg-chip-orange">{busTypeToText(chipType)}</Chip>
+
   }
 
   return <Chip className="bg-chip-blue">{busTypeToText(chipType)}</Chip>
 }
 
 export const Shuttle = ({ location }: ShuttleStop) => {
+  const dispatch = useDispatch()
   const setting = useQuery({
     queryKey: ['settings'],
     queryFn: settingAPI,
@@ -415,6 +420,14 @@ export const Shuttle = ({ location }: ShuttleStop) => {
     }
   }, [season, week])
 
+  useEffect(() => {
+    if(timetable.data !== undefined){
+      const filtered = timetable.data.filter((val) => isAfterCurrentTime(val))
+      dispatch(updateActions(filtered[0]))
+    }
+
+  }, [currentTime, dispatch, timetable.data])
+
   const handleActionStart = () => {
     setTouched(true)
   }
@@ -478,7 +491,6 @@ export const Shuttle = ({ location }: ShuttleStop) => {
 
     const filtered = timetable.data.filter((val) => isAfterCurrentTime(val))
     const reverted = filtered.map((val) => convertUnixToTime(val))
-
     if (filtered.length === 0) {
       // Buses are done for today. User should refresh after midnight.
       return (
diff --git a/src/app/data/index.ts b/src/app/data/index.ts
index 77d6b1bc..a8eab789 100644
--- a/src/app/data/index.ts
+++ b/src/app/data/index.ts
@@ -2,6 +2,7 @@ export type { StopLocation } from './common/stopLocation'
 export type { Changelog } from './modal/changelog'
 export type { NoticeInfo } from './notice/noticeInfo'
 export type { NoticeType } from './notice/noticeType'
+export type { CircleAnimate } from './routemap/circleanimate'
 export type { ChipType } from './shuttle/chipType'
 export type { OrganizedTimetables } from './shuttle/organizedTimetables'
 export type { Period } from './shuttle/period'
diff --git a/src/app/data/routemap/circleanimate.ts b/src/app/data/routemap/circleanimate.ts
new file mode 100644
index 00000000..f31c8aba
--- /dev/null
+++ b/src/app/data/routemap/circleanimate.ts
@@ -0,0 +1,7 @@
+import React from 'react';
+
+export type CircleAnimate = {
+    ref: React.MutableRefObject<Array<HTMLDivElement>>,
+    index: number,
+    chipColor: string,
+}
\ No newline at end of file
diff --git a/src/app/data/shuttle/organizedTimetables.ts b/src/app/data/shuttle/organizedTimetables.ts
index e602e62f..257daaa0 100644
--- a/src/app/data/shuttle/organizedTimetables.ts
+++ b/src/app/data/shuttle/organizedTimetables.ts
@@ -3,6 +3,7 @@ export type OrganizedTimetables = {
   direct: Array<string>
   circle: Array<string>
   directY: Array<string>
-  jungang: Array<string>
+  jungang: Array<string>,
+  isShuttleI: boolean,
   count: number
 }
diff --git a/src/app/network/shuttle.ts b/src/app/network/shuttle.ts
index 97c4dc23..37e9be65 100644
--- a/src/app/network/shuttle.ts
+++ b/src/app/network/shuttle.ts
@@ -13,7 +13,7 @@ export const shuttleAPI = async (
   location: StopLocation
 ): Promise<Array<SingleShuttleSchedule>> => {
   return apiHandler<Array<SingleShuttleSchedule>>(
-    `/timetable/${season}/${week}/${location}`
+    `/v2/timetable/${season}/${week}/${location}`
   )
 }
 
diff --git a/src/app/reducer/store.ts b/src/app/reducer/store.ts
new file mode 100644
index 00000000..c35dab43
--- /dev/null
+++ b/src/app/reducer/store.ts
@@ -0,0 +1,31 @@
+import { configureStore, createSlice } from "@reduxjs/toolkit";
+
+import { SingleShuttleSchedule } from "@/data";
+
+interface Action{
+    type: string,
+    payload: SingleShuttleSchedule
+}
+
+const actions = createSlice({
+    name: 'actions',
+    initialState: [] as SingleShuttleSchedule[],
+    reducers: {
+        updateActions: (state, action: Action) => {
+            while(state.length > 0){
+                state.pop()
+            }
+            if(action.payload !== undefined){
+                state.push(action.payload)
+            }
+        }
+    }
+})
+
+export const store = configureStore({
+    reducer: {
+        actions: actions.reducer
+    }
+})
+
+export const { updateActions } = actions.actions
\ No newline at end of file
diff --git a/src/main.tsx b/src/main.tsx
index b7f76c88..0bdc1e4c 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -5,10 +5,12 @@ import { Partytown } from '@builder.io/partytown/react'
 import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
 import React from 'react'
 import ReactDOM from 'react-dom/client'
+import { Provider } from 'react-redux'
 import { Snowfall } from 'react-snowfall'
 import { RecoilRoot } from 'recoil'
 
 import { DarkmodeContextProvider } from '@/context/ThemeContext'
+import { store } from '@/reducer/store'
 
 import App from './App'
 // import { Ptr } from './app/components/ptr/Ptr'
@@ -40,13 +42,15 @@ ReactDOM.createRoot(root).render(
             wind={[-0.5, 0.5]}
             radius={[14.0, 16.0]}
             style={{
-              zIndex: 1,
+              zIndex: 12,
               position: 'fixed',
               height: '100vh',
               pointerEvents: 'none',
             }}
           />
+          <Provider store={store}>
           <App />
+          </Provider>
         </DarkmodeContextProvider>
       </RecoilRoot>
     </QueryClientProvider>
diff --git a/tailwind.config.js b/tailwind.config.js
index bd7327b6..9c4b7323 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -41,6 +41,7 @@ module.exports = {
         'chip-blue': '#7FAAFF',
         'chip-green': '#81c784',
         'chip-purple': '#d0affb',
+        'chip-orange': '#FFAB40',
       },
       boxShadow: { 
         'theme-shadow' : 'var(--color-theme-shadow)',
@@ -139,6 +140,9 @@ module.exports = {
           },
         },
       },
+      transitionProperty:{
+        'height': 'height',
+      },
     },
   },
   darkMode: 'class',
diff --git a/yarn.lock b/yarn.lock
index e255e25b..d1f1b0da 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1614,6 +1614,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.7":
+  version: 7.26.0
+  resolution: "@babel/runtime@npm:7.26.0"
+  dependencies:
+    regenerator-runtime: "npm:^0.14.0"
+  checksum: 10/9f4ea1c1d566c497c052d505587554e782e021e6ccd302c2ad7ae8291c8e16e3f19d4a7726fb64469e057779ea2081c28b7dbefec6d813a22f08a35712c0f699
+  languageName: node
+  linkType: hard
+
 "@babel/template@npm:^7.22.15":
   version: 7.22.15
   resolution: "@babel/template@npm:7.22.15"
@@ -2140,6 +2149,26 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@reduxjs/toolkit@npm:^2.5.0":
+  version: 2.5.0
+  resolution: "@reduxjs/toolkit@npm:2.5.0"
+  dependencies:
+    immer: "npm:^10.0.3"
+    redux: "npm:^5.0.1"
+    redux-thunk: "npm:^3.1.0"
+    reselect: "npm:^5.1.0"
+  peerDependencies:
+    react: ^16.9.0 || ^17.0.0 || ^18 || ^19
+    react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0
+  peerDependenciesMeta:
+    react:
+      optional: true
+    react-redux:
+      optional: true
+  checksum: 10/b24ee7e89bd00c5f3ee5df12cbc1b6395784ff215b9bf75d2c1b211d8494af220057ced666f49c6e815b723252bc3eb704f7be4a3c261ab06ee7b776f4565d4b
+  languageName: node
+  linkType: hard
+
 "@remix-run/router@npm:1.16.0":
   version: 1.16.0
   resolution: "@remix-run/router@npm:1.16.0"
@@ -2629,6 +2658,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@types/react-transition-group@npm:^4":
+  version: 4.4.11
+  resolution: "@types/react-transition-group@npm:4.4.11"
+  dependencies:
+    "@types/react": "npm:*"
+  checksum: 10/a7f4de6e5f57d9fcdea027e22873c633f96a803c96d422db8b99a45c36a9cceb7882d152136bbc31c7158fc1827e37aea5070d369724bb71dd11b5687332bc4d
+  languageName: node
+  linkType: hard
+
 "@types/react@npm:18.3.1":
   version: 18.3.1
   resolution: "@types/react@npm:18.3.1"
@@ -2678,6 +2716,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@types/use-sync-external-store@npm:^0.0.6":
+  version: 0.0.6
+  resolution: "@types/use-sync-external-store@npm:0.0.6"
+  checksum: 10/a95ce330668501ad9b1c5b7f2b14872ad201e552a0e567787b8f1588b22c7040c7c3d80f142cbb9f92d13c4ea41c46af57a20f2af4edf27f224d352abcfe4049
+  languageName: node
+  linkType: hard
+
 "@typescript-eslint/eslint-plugin@npm:^7.8.0":
   version: 7.8.0
   resolution: "@typescript-eslint/eslint-plugin@npm:7.8.0"
@@ -3980,6 +4025,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"dom-helpers@npm:^5.0.1":
+  version: 5.2.1
+  resolution: "dom-helpers@npm:5.2.1"
+  dependencies:
+    "@babel/runtime": "npm:^7.8.7"
+    csstype: "npm:^3.0.2"
+  checksum: 10/bed2341adf8864bf932b3289c24f35fdd99930af77df46688abf2d753ff291df49a15850c874d686d9be6ec4e1c6835673906e64dbd8b2839d227f117a11fd41
+  languageName: node
+  linkType: hard
+
 "dot-case@npm:^3.0.4":
   version: 3.0.4
   resolution: "dot-case@npm:3.0.4"
@@ -5286,10 +5341,12 @@ __metadata:
   resolution: "hybus-genesis@workspace:."
   dependencies:
     "@builder.io/partytown": "npm:^0.10.2"
+    "@reduxjs/toolkit": "npm:^2.5.0"
     "@tanstack/react-query": "npm:^5.32.1"
     "@types/node": "npm:^20.11.1"
     "@types/react": "npm:18.3.1"
     "@types/react-dom": "npm:18.3.0"
+    "@types/react-transition-group": "npm:^4"
     "@types/styled-components": "npm:^5.1.34"
     "@typescript-eslint/eslint-plugin": "npm:^7.8.0"
     "@typescript-eslint/parser": "npm:^7.8.0"
@@ -5316,12 +5373,15 @@ __metadata:
     react-i18next: "npm:^14.0.5"
     react-icons: "npm:^5.0.1"
     react-is: "npm:^18.2.0"
+    react-redux: "npm:^9.2.0"
     react-router-dom: "npm:^6.23.0"
     react-simple-pull-to-refresh: "npm:^1.3.3"
     react-snowfall: "npm:^2.2.0"
     react-spinners: "npm:^0.13.8"
     react-tiny-fab: "npm:^4.0.4"
+    react-transition-group: "npm:^4.4.5"
     recoil: "npm:^0.7.7"
+    redux: "npm:^5.0.1"
     sass: "npm:^1.76.0"
     styled-components: "npm:^6.1.9"
     styled-reset: "npm:^4.5.2"
@@ -5402,6 +5462,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"immer@npm:^10.0.3":
+  version: 10.1.1
+  resolution: "immer@npm:10.1.1"
+  checksum: 10/9dacf1e8c201d69191ccd88dc5d733bafe166cd45a5a360c5d7c88f1de0dff974a94114d72b35f3106adfe587fcfb131c545856184a2247d89d735ad25589863
+  languageName: node
+  linkType: hard
+
 "immutable@npm:^4.0.0":
   version: 4.3.4
   resolution: "immutable@npm:4.3.4"
@@ -6992,7 +7059,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"prop-types@npm:^15.8.1":
+"prop-types@npm:^15.6.2, prop-types@npm:^15.8.1":
   version: 15.8.1
   resolution: "prop-types@npm:15.8.1"
   dependencies:
@@ -7131,6 +7198,25 @@ __metadata:
   languageName: node
   linkType: hard
 
+"react-redux@npm:^9.2.0":
+  version: 9.2.0
+  resolution: "react-redux@npm:9.2.0"
+  dependencies:
+    "@types/use-sync-external-store": "npm:^0.0.6"
+    use-sync-external-store: "npm:^1.4.0"
+  peerDependencies:
+    "@types/react": ^18.2.25 || ^19
+    react: ^18.0 || ^19
+    redux: ^5.0.0
+  peerDependenciesMeta:
+    "@types/react":
+      optional: true
+    redux:
+      optional: true
+  checksum: 10/b3d2f89f469169475ab0a9f8914d54a336ac9bc6a31af6e8dcfe9901e6fe2cfd8c1a3f6ce7a2f7f3e0928a93fbab833b668804155715598b7f2ad89927d3ff50
+  languageName: node
+  linkType: hard
+
 "react-refresh@npm:^0.14.0":
   version: 0.14.0
   resolution: "react-refresh@npm:0.14.0"
@@ -7203,6 +7289,21 @@ __metadata:
   languageName: node
   linkType: hard
 
+"react-transition-group@npm:^4.4.5":
+  version: 4.4.5
+  resolution: "react-transition-group@npm:4.4.5"
+  dependencies:
+    "@babel/runtime": "npm:^7.5.5"
+    dom-helpers: "npm:^5.0.1"
+    loose-envify: "npm:^1.4.0"
+    prop-types: "npm:^15.6.2"
+  peerDependencies:
+    react: ">=16.6.0"
+    react-dom: ">=16.6.0"
+  checksum: 10/ca32d3fd2168c976c5d90a317f25d5f5cd723608b415fb3b9006f9d793c8965c619562d0884503a3e44e4b06efbca4fdd1520f30e58ca3e00a0890e637d55419
+  languageName: node
+  linkType: hard
+
 "react@npm:18.3.1":
   version: 18.3.1
   resolution: "react@npm:18.3.1"
@@ -7272,6 +7373,22 @@ __metadata:
   languageName: node
   linkType: hard
 
+"redux-thunk@npm:^3.1.0":
+  version: 3.1.0
+  resolution: "redux-thunk@npm:3.1.0"
+  peerDependencies:
+    redux: ^5.0.0
+  checksum: 10/38c563db5f0bbec90d2e65cc27f3c870c1b6102e0c071258734fac41cb0e51d31d894125815c2f4133b20aff231f51f028ad99bccc05a7e3249f1a5d5a959ed3
+  languageName: node
+  linkType: hard
+
+"redux@npm:^5.0.1":
+  version: 5.0.1
+  resolution: "redux@npm:5.0.1"
+  checksum: 10/a373f9ed65693ead58bea5ef61c1d6bef39da9f2706db3be6f84815f3a1283230ecd1184efb1b3daa7f807d8211b0181564ca8f336fc6ee0b1e2fa0ba06737c2
+  languageName: node
+  linkType: hard
+
 "reflect.getprototypeof@npm:^1.0.4":
   version: 1.0.4
   resolution: "reflect.getprototypeof@npm:1.0.4"
@@ -7373,6 +7490,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"reselect@npm:^5.1.0":
+  version: 5.1.1
+  resolution: "reselect@npm:5.1.1"
+  checksum: 10/1fdae11a39ed9c8d85a24df19517c8372ee24fefea9cce3fae9eaad8e9cefbba5a3d4940c6fe31296b6addf76e035588c55798f7e6e147e1b7c0855f119e7fa5
+  languageName: node
+  linkType: hard
+
 "resolve-from@npm:^4.0.0":
   version: 4.0.0
   resolution: "resolve-from@npm:4.0.0"
@@ -8722,6 +8846,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"use-sync-external-store@npm:^1.4.0":
+  version: 1.4.0
+  resolution: "use-sync-external-store@npm:1.4.0"
+  peerDependencies:
+    react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+  checksum: 10/08bf581a8a2effaefc355e9d18ed025d436230f4cc973db2f593166df357cf63e47b9097b6e5089b594758bde322e1737754ad64905e030d70f8ff7ee671fd01
+  languageName: node
+  linkType: hard
+
 "util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1":
   version: 1.0.2
   resolution: "util-deprecate@npm:1.0.2"


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 3**

<details>
<summary>🧹 Nitpick comments (10)</summary><blockquote>

<details>
<summary>src/app/reducer/store.ts (1)</summary><blockquote>

`5-8`: **Use PayloadAction for better type safety**

Consider using Redux Toolkit's built-in `PayloadAction` type for better type safety.


```diff
+ import { configureStore, createSlice, PayloadAction } from "@reduxjs/toolkit";

import { SingleShuttleSchedule } from "@/data";

- interface Action{
-     type: string,
-     payload: SingleShuttleSchedule
- }

const actions = createSlice({
    name: 'actions',
    initialState: [] as SingleShuttleSchedule[],
    reducers: {
-       updateActions: (state, action: Action) => {
+       updateActions: (state, action: PayloadAction<SingleShuttleSchedule>) => {
```

</blockquote></details>
<details>
<summary>src/app/components/fulltime/FullTime.tsx (2)</summary><blockquote>

`126-129`: **Remove commented code instead of keeping it**

The `YesulinMinuteWrapper` component has been commented out but not removed. This creates unnecessary clutter in the codebase.


```diff
-// const YesulinMinuteWrapper = styled.span<{ $itemCount: number }>`
-//   ${tw`inline-block text-green-500`}
-//   ${({ $itemCount }) => ($itemCount === 0 ? tw`hidden` : undefined)}
-// `
```

---

`299-299`: **Missing key in iterable**

The return statement in the `timetableFiltered.forEach` callback is missing a key property when returning the empty fragment.


While this code doesn't actually render anything (it returns an empty fragment within a forEach loop whose return value isn't used), it's still flagged by the static analyzer and could be confusing to other developers.

```diff
- return <></>
+ return null
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 299-299: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation. 

(lint/correctness/useJsxKeyInIterable)

</details>

</details>

</blockquote></details>
<details>
<summary>src/App.tsx (4)</summary><blockquote>

`1-5`: **Sort the imports.**

ESLint reports that imports need reordering. Adhere to your project’s style guidelines or run the autofix to ensure a proper import order.


Example sorted diff:
```diff
- import React, { lazy, Suspense, useEffect, useState } from 'react'
- import {Transition} from 'react-transition-group'
- import Arrow from '/image/expand_less_white_48dp.svg'
- import { RouteMap } from '@/components/routemap/RouteMap'
+ import React, { lazy, Suspense, useEffect, useState } from 'react'
+ import { Transition } from 'react-transition-group'
+ import { RouteMap } from '@/components/routemap/RouteMap'
+ import Arrow from '/image/expand_less_white_48dp.svg'
```


Also applies to: 15-16

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 ESLint</summary>

[error] 1-21: Run autofix to sort these imports!

(simple-import-sort/imports)

</details>

</details>

---

`201-201`: **Use clearer boolean naming.**

Renaming `routeCardClick` to something like `isRouteCardOpen` can improve readability.


```diff
-  const [routeCardClick, setRouteCardClick] = useState<boolean>(false)
+  const [isRouteCardOpen, setIsRouteCardOpen] = useState<boolean>(false)
```

---

`123-125`: **Handle additional transition states.**

Only `entered` is accounted for. If you want fine-grained control (e.g., "entering", "exiting"), consider adding handling for these states as well.

---

`450-480`: **Optional: Enhance transitions for intermediate states.**

Your usage of `react-transition-group` is correct. If you want smoother animations, consider unique styling for `"entering"` and `"exiting"` states. Otherwise, this looks fine.

</blockquote></details>
<details>
<summary>src/app/components/routemap/RouteMap.tsx (3)</summary><blockquote>

`7-8`: **Combine imports from the same module.**

You can consolidate imports for `'@/data'` to simplify and reduce repetition.


```diff
-import { CircleAnimate } from '@/data';
-import { SingleShuttleSchedule } from "@/data";
+import { CircleAnimate, SingleShuttleSchedule } from '@/data';
```

---

`91-144`: **Reduce repetitive logic in `directInput`.**

Substantial duplication for different route types. Extract a helper or use a data-driven approach to make code more concise and future-proof.

---

`145-182`: **Avoid duplication in `directLineInput`.**

This function largely repeats the same pattern. Factor out shared logic into a utility function to improve maintainability.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between d9fca8b66fe748a0f99dbb7a900af5073265e72c and 933d808fb4b27309680ad22605fe332b5b7aff70.

</details>

<details>
<summary>⛔ Files ignored due to path filters (13)</summary>

* `.yarn/cache/@babel-runtime-npm-7.26.0-9afa3c4ef6-9f4ea1c1d5.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/@reduxjs-toolkit-npm-2.5.0-9bdc99574d-b24ee7e89b.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/@types-react-transition-group-npm-4.4.11-dda183055d-a7f4de6e5f.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/@types-use-sync-external-store-npm-0.0.6-9e5c635381-a95ce33066.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/dom-helpers-npm-5.2.1-b38bb4470b-bed2341adf.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/immer-npm-10.1.1-973ae10d09-9dacf1e8c2.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/react-redux-npm-9.2.0-d87bb27c82-b3d2f89f46.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/react-transition-group-npm-4.4.5-98ea4ef96e-ca32d3fd21.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/redux-npm-5.0.1-f8e6b1cb23-a373f9ed65.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/redux-thunk-npm-3.1.0-6a8fdd3211-38c563db5f.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/reselect-npm-5.1.1-667568f51c-1fdae11a39.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/use-sync-external-store-npm-1.4.0-176448bea1-08bf581a8a.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `yarn.lock` is excluded by `!**/yarn.lock`, `!**/*.lock`

</details>

<details>
<summary>📒 Files selected for processing (16)</summary>

* `.pnp.cjs` (15 hunks)
* `package.json` (3 hunks)
* `src/App.tsx` (4 hunks)
* `src/app/components/fulltime/FullTime.tsx` (7 hunks)
* `src/app/components/index.ts` (1 hunks)
* `src/app/components/lang/lang.en.json` (1 hunks)
* `src/app/components/lang/lang.ko.json` (1 hunks)
* `src/app/components/routemap/RouteMap.tsx` (1 hunks)
* `src/app/components/shuttle/Shuttle.tsx` (4 hunks)
* `src/app/data/index.ts` (1 hunks)
* `src/app/data/routemap/circleanimate.ts` (1 hunks)
* `src/app/data/shuttle/organizedTimetables.ts` (1 hunks)
* `src/app/network/shuttle.ts` (1 hunks)
* `src/app/reducer/store.ts` (1 hunks)
* `src/main.tsx` (2 hunks)
* `tailwind.config.js` (2 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>src/app/components/fulltime/FullTime.tsx</summary>

[error] 299-299: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation. 

(lint/correctness/useJsxKeyInIterable)

</details>

</details>
<details>
<summary>🪛 ESLint</summary>

<details>
<summary>src/App.tsx</summary>

[error] 1-21: Run autofix to sort these imports!

(simple-import-sort/imports)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (1)</summary>

* GitHub Check: Cloudflare Pages

</details>

<details>
<summary>🔇 Additional comments (43)</summary><blockquote>

<details>
<summary>src/app/data/index.ts (1)</summary>

`5-5`: **New export for RouteMap animation**

The addition of the `CircleAnimate` type export supports the new RouteMap feature by providing animation type definitions. This follows the established pattern in the file of exporting types from their respective modules.

</details>
<details>
<summary>src/app/components/index.ts (1)</summary>

`2-2`: **LGTM on Shuttle component export**

This export is unchanged but is now part of an expanded architecture that integrates with Redux for state management according to the PR summary.

</details>
<details>
<summary>src/app/network/shuttle.ts (1)</summary>

`16-16`: <details>
<summary>✅ Verification successful</summary>

**API endpoint versioning change**

The API endpoint has been updated from `/timetable/${season}/${week}/${location}` to `/v2/timetable/${season}/${week}/${location}`, indicating an API version upgrade. This change likely supports the new RouteMap feature with potentially enhanced data.


---

🏁 Script executed:

```shell
#!/bin/bash
# Check if any other API calls need to be updated to v2
rg -g "*.ts" -g "*.tsx" "apiHandler\(" --no-heading | grep -v "/v2/" | grep "/timetable/"
```

Length of output: 87

---

Let's perform an additional check to ensure that every reference to the `/timetable/` endpoint has been updated to include the `/v2/` prefix. I'll run a new verification shell script that searches for any occurrences of `/timetable/` that do not include `/v2/`.




Please verify the output of this command. If nothing is returned, it confirms that all `/timetable/` endpoints have been updated correctly.

---

🏁 Script executed:

```shell
#!/bin/bash
# This script re-checks all TypeScript source files to verify that every `/timetable/` usage
# already includes the `/v2/` prefix.
rg -g "*.ts" -g "*.tsx" '/timetable/' --no-heading | grep -v "/v2/"
```

Length of output: 65

---



</details>

**API endpoint upgrade verified: All `/timetable/` references have been correctly updated to `/v2/timetable/` throughout the codebase.**  

- The change in `src/app/network/shuttle.ts` at line 16 is confirmed.
- Verification scripts show no lingering calls to the old API endpoint.

</details>
<details>
<summary>src/app/components/lang/lang.ko.json (2)</summary>

`12-13`: **Added localization for "yesul" (artist)**

New localization entry for "yesul" (예술인) supports the RouteMap feature UI. This is consistent with existing entries like "yesulin_btn" at line 41.

---

`14-14`: **Added localization for "jung" (central station)**

New localization entry for "jung" (중앙역) added to support the RouteMap feature UI. Note that there's already a similar entry "cycle_ja" with the same value at line 11, which might cause confusion.

Could the duplicate meaning between "cycle_ja" (line 11) and "jung" (line 14) cause confusion in the UI? Consider documenting the purpose of each key to clarify when each should be used.

</details>
<details>
<summary>src/app/data/shuttle/organizedTimetables.ts (1)</summary>

`6-7`: **Type definition expanded with new boolean property**

The addition of `isShuttleI: boolean` to the `OrganizedTimetables` type extends the shuttle data model with a new state flag. Consider adding a brief comment above this property to document what "isShuttleI" represents for better maintainability.

</details>
<details>
<summary>src/app/data/routemap/circleanimate.ts (1)</summary>

`1-5`: **New type definition looks well-structured**

The `CircleAnimate` type provides a clean interface for managing circle animations with appropriate typing for React refs, indices, and styling properties.

Consider adding a brief JSDoc comment at the top of this file to explain the purpose of this type and how it should be used in animation contexts.

</details>
<details>
<summary>tailwind.config.js (1)</summary>

`44-44`: **Color addition follows existing pattern**

The new 'chip-orange' color follows the naming convention of other chip colors in the configuration.

</details>
<details>
<summary>src/app/components/lang/lang.en.json (1)</summary>

`12-15`: **Addition of new localization keys looks good.**

The new keys "yesul", "jung", and "dorm" have been added with appropriate translations, which appear to be shorter versions of existing entries like "yesulin", "jungang", and "residence". These likely support the new RouteMap component mentioned in the summary.

</details>
<details>
<summary>src/main.tsx (2)</summary>

`8-8`: **Redux imports added correctly.**

The imports for Redux Provider and store are properly placed with other imports, following the existing code style.



Also applies to: 13-13

---

`51-53`: **Redux Provider implementation looks good.**

The App component is correctly wrapped with the Redux Provider, making the store accessible throughout the application.

</details>
<details>
<summary>package.json (1)</summary>

`15-15`: **New package dependencies are properly configured.**

The Redux-related dependencies and react-transition-group have been added with specific versions, maintaining alphabetical ordering. The TypeScript type definitions are correctly added to devDependencies.



Also applies to: 30-30, 36-36, 38-38, 47-47

</details>
<details>
<summary>src/app/components/shuttle/Shuttle.tsx (3)</summary>

`7-7`: **Redux integration added correctly.**

The useDispatch hook and updateActions action creator are properly imported and initialized, enabling the component to dispatch actions to the Redux store.



Also applies to: 26-26, 349-349

---

`340-342`: **Bus type styling enhancement looks good.**

The ColoredChip function now properly handles 'R' and 'NA' bus types with an orange background, improving visual differentiation between bus types.

---

`423-429`: <details>
<summary>❓ Verification inconclusive</summary>

**Redux state integration implemented properly.**

The useEffect hook correctly filters timetable data to find entries after the current time and dispatches the first entry to the Redux store. The dependencies array is properly set to re-run when currentTime, dispatch, or timetable.data changes.


Verify that there's appropriate handling for when no upcoming shuttles are available (filtered array is empty):

---

🏁 Script executed:

```shell
#!/bin/bash
# Search for updateActions dispatch implementation and store definition
rg -A 3 "updateActions" src/
```

Length of output: 826

---



</details>

**Action Required: Verify Handling for No Upcoming Shuttles**

The Redux integration and filtering logic in the useEffect hook are implemented correctly for cases where timetable data contains upcoming shuttles. However, please verify that the scenario where the filtered array is empty (i.e., no upcoming shuttles are available) is appropriately handled. Specifically:

- **File: src/app/components/shuttle/Shuttle.tsx (Lines 423‑429):**  
  Consider adding a guard to check if the filtered array is non-empty before dispatching, to prevent dispatching an undefined value.

- **File: src/app/reducer/store.ts:**  
  Confirm that the `updateActions` reducer can safely manage an undefined payload if no shuttle is found.

If additional handling is needed, please adjust the logic accordingly.

</details>
<details>
<summary>src/app/components/fulltime/FullTime.tsx (9)</summary>

`103-104`: **Height styles added for 4 chips case**

Good addition of the new condition to handle the case when `$maxChips` equals 4, setting appropriate height styles. This supports the new `directY` display added to the component.

---

`180-183`: **Properly added TimeBoxBodyGrid for Yesulin routes**

Good implementation of a dedicated grid for Yesulin direct times. This separates them from other direct routes and makes the UI clearer.

---

`185-185`: **Conditional styling based on shuttle type**

Good use of conditional styling to visually differentiate Shuttle I routes from standard direct routes. This improves the user experience by providing visual cues about the route type.

---

`188-197`: **Implemented duplication prevention logic**

The logic now checks if a time exists in both `props.direct` and `props.directY` arrays, preventing duplicates from being rendered. This is a good improvement to the UI.

---

`262-262`: **Added isShuttleI flag to OrganizedTimetables**

The addition of the `isShuttleI` flag to track Shuttle I routes is a good way to maintain this information throughout the component.

---

`278-281`: **Logic for setting isShuttleI flag**

The code correctly sets the `isShuttleI` flag when the schedule type is 'R'. This is aligned with the new functionality to differentiate Shuttle I routes.

---

`293-295`: **Updated count logic for directY**

The count logic now includes `single.directY`, ensuring that the component correctly accounts for the new type of shuttle route when calculating heights.

---

`317-317`: **Refined TimeBox rendering condition**

The condition now correctly checks if `directY` and `jungang` are also empty before deciding not to render a TimeBox. This maintains consistency with the existing logic while incorporating the new route types.

---

`324-324`: **Passed isShuttleI property to TimeBox**

The `isShuttleI` property is correctly passed to the TimeBox component, enabling the conditional styling seen earlier.

</details>
<details>
<summary>.pnp.cjs (16)</summary>

`27-38`: **Dependency Updates: Redux Toolkit & React Transition Group Types Added**  
New entries for `@reduxjs/toolkit` (version 2.5.0, virtual dependency) and `@types/react-transition-group` (version 4.4.11) have been introduced. Please verify that these dependency specifications match the intended versions in your package manifest and are consistent with your project’s usage.

---

`59-73`: **Dependency Updates: React-Redux & Additional UI Libraries**  
The configuration now adds/updates dependencies for `react-redux` (version 9.2.0, virtual dependency) and `react-transition-group` (updated to version 4.4.5), alongside other UI libraries. Please double-check that these versions and their virtual linking align with your package.json and are compatible with the rest of your stack.

---

`2732-2745`: **Babel Runtime Package Restructuring**  
The hunk adjusts how the `@babel/runtime` package is structured, grouping its dependencies (including `regenerator-runtime`) with a HARD link type. Confirm that this revised structure integrates seamlessly with your build process and does not conflict with existing cache settings.

---

`3363-3396`: **Redux Toolkit Dual-Entry Configuration Added**  
A new configuration block for `@reduxjs/toolkit` is introduced with two entries: one direct npm entry (SOFT link) and one virtual entry carrying additional dependencies (with a HARD link). Verify that the dual-record setup is intentional and consistently referenced with your Redux integration.

---

`4154-4167`: **Types Package for React Transition Group Added**  
A new dependency configuration for `@types/react-transition-group` is provided (version 4.4.11) along with its dependencies, including `@types/react`. Ensure that this entry does not conflict with any previous type definitions and meets your type safety expectations.

---

`4212-4224`: **Addition of @types/use-sync-external-store**  
The configuration now adds the package `@types/use-sync-external-store` (version 0.0.6) with a HARD link. Please confirm that this version is compatible with your usage of the corresponding package and aligns with other type definitions in the project.

---

`5835-5849`: **Dependency Added: dom-helpers**  
A new dependency for `dom-helpers` (version 5.2.1) is being included, together with its sub-dependencies such as `@babel/runtime` and `csstype`. Verify that the chosen versions meet your UI utility requirements and that the HARD link type is correctly applied.

---

`7267-7278`: **Top-Level Dependency Consistency Check**  
Within the overall workspace dependencies for "hybus-genesis", the entries for `@reduxjs/toolkit` and `@types/react-transition-group` appear again. Ensure that these versions are consistent across all sections of your configuration and that no duplicate or conflicting version specifications occur.

---

`7299-7313`: **Verification of Duplicate React-Redux & React-Transition-Group Entries**  
This hunk reiterates the dependency definitions for `react-redux` and `react-transition-group`. Please verify that these repeat entries are intentional for the workspace configuration and that the virtual dependency strings exactly match the previously defined ones to avoid potential conflicts.

---

`7396-7410`: **Addition of Immer Dependency**  
The configuration now includes `immer` (version 10.1.1) with a HARD link. Ensure that this update aligns with your state management requirements and is compatible with the other Redux-related dependencies.

---

`9437-9470`: **Updated React-Redux Configuration with Dual Entries**  
A refined block for `react-redux` (version 9.2.0) is introduced with both a direct npm (SOFT link) and a corresponding virtual dependency entry that also lists associated packages (e.g., `@types/react`, `redux-thunk` integration). Please verify that the distinction between SOFT and HARD link types is as intended and that peer dependencies are properly satisfied.

---

`9626-9659`: **Refined Configuration for React Transition Group**  
The configuration for `react-transition-group` now includes both a direct npm entry (SOFT link) and a more detailed virtual dependency block (HARD link) that lists several sub-dependencies plus package peers. Confirm that this dual-entry approach is deliberate and that all peer dependencies (like types and runtime helpers) are correctly integrated.

---

`9733-9767`: **Redux & Redux-Thunk Configuration Updates**  
New blocks for `redux` (version 5.0.1) and `redux-thunk` (version 3.1.0) are specified. Note that `redux-thunk` is defined with both a direct npm entry and a virtual entry (which includes extra information on peer dependencies). Verify that the link types (HARD for redux and SOFT then HARD for redux-thunk) are intentionally configured and that these entries align with your state management setup.

---

`9874-9886`: **Reselect and Patched Resolve Dependency Added**  
This hunk adds a new configuration for `reselect` (version 5.1.1) and begins a patched dependency definition for `resolve`. Please ensure that the reselect version meets your memoization needs and that the patch identifier for `resolve` is valid and necessary for your build environment.

---

`11334-11359`: **Dual Entry for use-sync-external-store Updated**  
A refined configuration for `use-sync-external-store` (version 1.4.0) is provided with both a direct npm entry (SOFT link) and a corresponding virtual entry (HARD link) along with its package peers. Please confirm that this configuration is consistent with your usage and that the version is aligned with upstream requirements.

---

`11360-11361`: **Util-Deprecate Configuration**  
The block for `util-deprecate` (version 1.0.2) follows immediately after the dual entry for use-sync-external-store. No changes are introduced here, so please verify that this dependency remains correct within the current configuration context.

</details>
<details>
<summary>src/App.tsx (2)</summary>

`132-136`: **Verify reversed transition logic.**

Currently, `exited` applies `opacity-100` whereas `entered` is hidden. Ensure this is the intended behavior.

---

`137-140`: **Arrow rotation looks good.**

The arrow flip logic is clear and consistent with the transition state.

</details>
<details>
<summary>src/app/components/routemap/RouteMap.tsx (1)</summary>

`220-244`: **Check for index overflow on circle animation.**

When `props.index` is near the end, `i + 1` may exceed array bounds, leading to potential runtime errors.


Example modification:
```diff
if (i === props.ref.current.length - 1) {
  // Avoid referencing props.ref.current[i+1]
  continue
}
```

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-08T18:55:37Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>tailwind.config.js (1)</summary><blockquote>

`143-145`: _⚠️ Potential issue_

**Fix the `transitionProperty` configuration format**

The current configuration assigns an array of variants to `'height'`, but Tailwind expects the values to be CSS property strings. To ensure proper functionality, please change the configuration as shown below and handle variants separately in the `variants` section if needed.



```diff
 transitionProperty:{
-  'height': ['responsive', 'hover', 'focus'],
+  'height': 'height',
 },
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (7)</summary><blockquote>

<details>
<summary>src/app/components/fulltime/FullTime.tsx (3)</summary><blockquote>

`188-196`: **Consider using a simple membership check instead of mapping.**
Currently, the code maps over `props.directY` simply to set `isExist`. A more direct approach would be:
```diff
- props.directY.map((ytime) => {
-   time === ytime ? (isExist = true) : null
- })
+ if (props.directY.includes(time)) {
+   isExist = true
+ }
```
This improves readability and slightly reduces overhead.

---

`299-299`: **Remove or annotate the empty fragment to avoid React key warnings.**
Returning `<></>` inside this `.forEach()` may trigger lint errors about missing keys. Since nothing is rendered, consider removing the line entirely:
```diff
- return <></>
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 299-299: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation. 

(lint/correctness/useJsxKeyInIterable)

</details>

</details>

---

`301-301`: **Suspicious no-op return inside a `.forEach()`.**
The `return <></>` doesn’t affect the pushed data and may cause confusion for future maintainers. Consider removing it if not needed.

</blockquote></details>
<details>
<summary>src/App.tsx (1)</summary><blockquote>

`130-134`: **Double-check intermediate states when hiding container.**
Currently, only the `'exited'` and `'entered'` states are handled. You might want to handle `'exiting'` or `'entering'` if you want smoother animations, as React Transition Group can emit those states.

</blockquote></details>
<details>
<summary>.pnp.cjs (3)</summary><blockquote>

`2732-2745`: **Babel Runtime Block Refinement**

A new block explicitly configuring the `@babel/runtime` (v7.26.0) alongside `regenerator-runtime` is now present. This restructuring centralizes Babel runtime dependencies for improved caching and consistency in your Plug'n'Play environment. Please verify that all Babel-related dependencies remain compatible after this change.

---

`9874-9888`: **Reselect and Patched Resolve Dependency Update**

A dedicated block for `reselect` (v5.1.1) is added with a HARD link, ensuring efficient memoized selector creation. Additionally, a patched version of `resolve` is configured. Review the necessity of this patch and ensure that it harmonizes with your module resolution strategy.

---

`11359-11361`: **Util-Deprecate Dependency Verification**

The inclusion of `util-deprecate` (v1.0.2) appears standard. It is advisable to confirm that this package is indeed necessary for your project and that its version does not conflict with any dependent modules.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between d9fca8b66fe748a0f99dbb7a900af5073265e72c and fb2669f9619b432bfe3f477e6c7ebe6529d78447.

</details>

<details>
<summary>⛔ Files ignored due to path filters (13)</summary>

* `.yarn/cache/@babel-runtime-npm-7.26.0-9afa3c4ef6-9f4ea1c1d5.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/@reduxjs-toolkit-npm-2.5.0-9bdc99574d-b24ee7e89b.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/@types-react-transition-group-npm-4.4.11-dda183055d-a7f4de6e5f.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/@types-use-sync-external-store-npm-0.0.6-9e5c635381-a95ce33066.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/dom-helpers-npm-5.2.1-b38bb4470b-bed2341adf.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/immer-npm-10.1.1-973ae10d09-9dacf1e8c2.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/react-redux-npm-9.2.0-d87bb27c82-b3d2f89f46.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/react-transition-group-npm-4.4.5-98ea4ef96e-ca32d3fd21.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/redux-npm-5.0.1-f8e6b1cb23-a373f9ed65.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/redux-thunk-npm-3.1.0-6a8fdd3211-38c563db5f.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/reselect-npm-5.1.1-667568f51c-1fdae11a39.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/use-sync-external-store-npm-1.4.0-176448bea1-08bf581a8a.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `yarn.lock` is excluded by `!**/yarn.lock`, `!**/*.lock`

</details>

<details>
<summary>📒 Files selected for processing (16)</summary>

* `.pnp.cjs` (15 hunks)
* `package.json` (3 hunks)
* `src/App.tsx` (4 hunks)
* `src/app/components/fulltime/FullTime.tsx` (7 hunks)
* `src/app/components/index.ts` (1 hunks)
* `src/app/components/lang/lang.en.json` (1 hunks)
* `src/app/components/lang/lang.ko.json` (1 hunks)
* `src/app/components/routemap/RouteMap.tsx` (1 hunks)
* `src/app/components/shuttle/Shuttle.tsx` (4 hunks)
* `src/app/data/index.ts` (1 hunks)
* `src/app/data/routemap/circleanimate.ts` (1 hunks)
* `src/app/data/shuttle/organizedTimetables.ts` (1 hunks)
* `src/app/network/shuttle.ts` (1 hunks)
* `src/app/reducer/store.ts` (1 hunks)
* `src/main.tsx` (2 hunks)
* `tailwind.config.js` (2 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>src/app/components/fulltime/FullTime.tsx</summary>

[error] 299-299: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation. 

(lint/correctness/useJsxKeyInIterable)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (2)</summary>

* GitHub Check: Generate Lighthouse Report
* GitHub Check: Cloudflare Pages

</details>

<details>
<summary>🔇 Additional comments (46)</summary><blockquote>

<details>
<summary>src/app/components/index.ts (1)</summary>

`2-2`: **LGTM: Export statement for Shuttle component.**

The export statement correctly exposes the Shuttle component, which according to the PR context has been updated to use Redux for state management.

</details>
<details>
<summary>src/app/data/index.ts (1)</summary>

`5-5`: **New type export for animation functionality.**

The addition of the CircleAnimate type export follows the established pattern in this file and supports the new RouteMap feature mentioned in the PR objectives.

</details>
<details>
<summary>src/app/network/shuttle.ts (1)</summary>

`16-16`: <details>
<summary>❓ Verification inconclusive</summary>

**API endpoint version update.**

The API endpoint has been updated to use a versioned path (/v2/). Ensure that this new endpoint is available and compatible with your service implementation.


---

🏁 Script executed:

```shell
#!/bin/bash
# Check for other v2 API endpoint usages to ensure consistency
rg -g "*.ts" -g "*.tsx" "/v2/" --no-filename | grep -v $(echo "/v2/timetable/" | sed 's/\//\\\//g')
```

Length of output: 118

---



</details>

**API v2 Endpoint Verification Needed**

- The updated endpoint in `src/app/network/shuttle.ts` is now defined as:
  
  ```typescript
      `/v2/timetable/${season}/${week}/${location}`
  ```

- A search for other `/v2/` usages (excluding `/v2/timetable/`) did not reveal any conflicting endpoints in our TypeScript files.
- **Action:** Please manually verify that the new `/v2/timetable` endpoint is live and fully compatible with the service implementation, as our automated check returned no additional output.

</details>
<details>
<summary>src/app/data/shuttle/organizedTimetables.ts (1)</summary>

`6-7`: <details>
<summary>✅ Verification successful</summary>

**Type definition expanded with isShuttleI property.**

The OrganizedTimetables type has been extended with a new boolean property `isShuttleI`. This appears to be used for determining shuttle type in rendering logic according to the PR context.


---

🏁 Script executed:

```shell
#!/bin/bash
# Check how the new isShuttleI property is being utilized across the codebase
rg -g "*.ts" -g "*.tsx" "isShuttleI" --no-filename
```

Length of output: 286

---



</details>

**Update verified: `isShuttleI` property integration confirmed.**  

The new `isShuttleI` boolean property in the `OrganizedTimetables` type has been successfully incorporated. Based on our review, it is effectively used in rendering logic—evident in conditional class assignments (e.g., in the `<Chip>` component) and in schedule object manipulations. The codebase usage confirms that the integration works as intended.

</details>
<details>
<summary>src/app/components/lang/lang.ko.json (2)</summary>

`12-13`: **LGTM! Adding translation for "yesul"**

The addition of "yesul": "예술인" is a good enhancement to the Korean localization file, providing translation for the "artist" term which matches with the existing "yesulin" entries.

---

`14-14`: **LGTM! Adding translation for "jung"**

The addition of "jung": "중앙역" provides proper Korean translation for "central station", which is consistent with the existing "jungang" entries.

</details>
<details>
<summary>tailwind.config.js (1)</summary>

`44-44`: **LGTM! New color addition**

The addition of 'chip-orange' follows the naming convention of other chip colors and provides a consistent color palette for the UI.

</details>
<details>
<summary>src/app/components/lang/lang.en.json (1)</summary>

`12-14`: **LGTM! Added translations for route locations**

The additions of "yesul" and "jung" keys provide appropriate English translations that match with the Korean localization file.

</details>
<details>
<summary>src/app/reducer/store.ts (5)</summary>

`1-4`: **Good implementation of Redux store with proper typing.**

The imports and type definition are appropriate for a Redux implementation with TypeScript.

---

`5-8`: **Type definition looks good.**

The Action interface properly defines the structure for Redux actions with appropriate typing for payload.

---

`10-23`: **Inefficient state clearing approach**

The current implementation clears the state array by repeatedly calling `pop()`, which is inefficient and not idiomatic Redux.


Use Redux Toolkit's pattern of returning a new state or leverage Immer's direct state mutation:

```diff
updateActions: (state, action: Action) => {
-   while(state.length > 0){
-       state.pop()
-   }
-   if(action.payload !== undefined){
-       state.push(action.payload)
-   }
+   // Immer lets us "mutate" state directly
+   state.length = 0; // Clear array efficiently
+   if(action.payload !== undefined){
+       state.push(action.payload)
+   }
+   
+   // Or return a new state directly
+   // return action.payload !== undefined ? [action.payload] : [];
}
```

Additionally, if this store is designed to only ever contain a single item, consider using an object instead of an array for the state.

---

`25-29`: **Good Redux store configuration.**

The store is properly configured with the reducer from the actions slice.

---

`31-31`: **Correct action export pattern.**

Properly exports the action creator from the slice for use in components.

</details>
<details>
<summary>src/main.tsx (2)</summary>

`8-8`: **Good Redux integration.**

The necessary Redux imports are correctly specified.



Also applies to: 13-13

---

`51-53`: **Correct Provider implementation.**

The Redux Provider component correctly wraps the App component with the store, making the Redux state available throughout the component tree.

</details>
<details>
<summary>package.json (2)</summary>

`15-15`: **Appropriate dependency versions.**

The added dependencies for Redux and animations use appropriate version ranges.



Also applies to: 30-30, 36-36, 38-38

---

`47-47`: **Type definitions correctly added.**

Type definitions for the React Transition Group are correctly included as a dev dependency.

</details>
<details>
<summary>src/app/components/shuttle/Shuttle.tsx (4)</summary>

`7-7`: **Good Redux hook and action imports.**

The Redux dispatch hook and action creator are properly imported.



Also applies to: 26-26

---

`340-342`: **Enhanced chip rendering implementation.**

The code now handles 'R' and 'NA' chip types with appropriate styling.

---

`349-349`: **Correct Redux hook usage.**

Redux dispatch hook is properly initialized in the component.

---

`423-429`: **Good Redux state update pattern.**

The useEffect is correctly implemented to dispatch the first upcoming shuttle schedule to the Redux store when timetable data changes.


This enables sharing the shuttle schedule data with other components (likely the RouteMap component mentioned in the PR title).

</details>
<details>
<summary>src/app/components/fulltime/FullTime.tsx (7)</summary>

`103-104`: **Height logic for $maxChips=4 looks good.**
No issues identified here, and the responsive styling is consistent with the existing pattern.

---

`180-183`: **Conditional rendering for `directY` is valid.**
This addition properly provides a separate section for Yesulin times, matching the approach taken for other routes.

---

`185-185`: **Introducing `isShuttleI` for chip color is coherent.**
Switching the color to orange when `isShuttleI` is true clearly indicates the R-type shuttle.

---

`262-262`: **New boolean field `isShuttleI` is well-introduced.**
Defaulting to `false` is a clear and safe initialization.

---

`279-280`: **Logic for R-type shuttle is consistent.**
Setting `isShuttleI = true` on `schedule.type === 'R'` aligns with the new orange chip rendering.

---

`293-295`: **Incrementing `count` for `directY` keeps consistency.**
This matches the existing logic for other arrays in the timetable.

---

`317-325`: **Conditional check for empty arrays is correct.**
Preventing a blank `TimeBox` from rendering trims unnecessary UI elements.

</details>
<details>
<summary>src/App.tsx (4)</summary>

`1-1`: **[ESLint] Sort and group the imports to match project conventions.**
This echoes a past review comment regarding import order. Running an auto-fix (e.g., `npm run lint -- --fix`) should address it.



Also applies to: 5-5, 10-10, 14-14

---

`121-123`: **Height transition for `RouteIndexCardView` is clear.**
This approach properly extends `CardView` with a custom `status` prop to animate its height changes.

---

`136-138`: **Rotation on `'entered'` state is valid.**
If you need a transition during `'entering'`, consider mapping that as well. Otherwise, this logic is acceptable as is.

---

`448-478`: **Overall transition block is well-implemented.**
Passing the transition `state` into child elements cleanly decouples styling from business logic.

</details>
<details>
<summary>src/app/components/routemap/RouteMap.tsx (1)</summary>

`53-55`: **Fix the transition state check.**
React Transition Group doesn’t emit a plain `"exit"` state. It provides `"exiting"` or `"exited"` instead. This mismatch can break your opacity logic.

```diff
- ${(props) => props.status === 'entered' || props.status === 'exit' ? tw`opacity-100` : tw`opacity-0`}
+ ${(props) => props.status === 'entered' || props.status === 'exited' ? tw`opacity-100` : tw`opacity-0`}
```

</details>
<details>
<summary>.pnp.cjs (13)</summary>

`27-38`: **Redux Toolkit & React Transition Group Type Dependency Additions**

The new dependency on `@reduxjs/toolkit` (v2.5.0) is added with a virtual resolution, and the type definitions for `@types/react-transition-group` (v4.4.11) are now explicitly declared. These additions support the upcoming Redux integration (likely required by the new RouteMap feature) and ensure proper TypeScript support for animated components. Please verify these versions are consistent with the ones in your package.json.

---

`59-73`: **React-Redux & UI Library Dependency Updates**

The added entry for `react-redux` (v9.2.0) and the update for `react-transition-group` (v4.4.5) ensure that state management and transition animations are well-supported. These changes complement the evolving Redux setup and UI improvements anticipated for RouteMap. Confirm that these updates are fully compatible with your existing component implementations.

---

`3363-3397`: **Redux Toolkit Configuration Update**

The configuration for `@reduxjs/toolkit` now includes both the direct npm reference and its corresponding virtual dependency—with the virtual entry linked as SOFT. This dual setup should offer flexibility in dependency resolution. Ensure that this configuration meshes well with your Redux store setup throughout the application.

---

`4154-4166`: **Type Definitions for React Transition Group**

A dedicated dependency block for `@types/react-transition-group` (v4.4.11) is introduced with its associated dependency on `@types/react`. This improves type safety for components that use transitions. No issues detected; just verify that there’s no conflict with preexisting type definitions.

---

`4212-4224`: **Type Definitions for use-sync-external-store**

The addition of `@types/use-sync-external-store` (v0.0.6) with a HARD link ensures proper TypeScript support for the corresponding package. This update should improve integration with any state synchronization mechanisms. Please confirm that there are no version conflicts with other type definitions.

---

`5835-5848`: **Dom-Helpers Dependency Configuration**

The new block for `dom-helpers` (v5.2.1) is now in place, correctly listing its dependencies (`@babel/runtime` and `csstype`), and using a HARD link. This should enhance DOM utility functions, which can be beneficial for advanced UI interactions in features like RouteMap. Ensure these dependencies integrate smoothly with your styling and component logic.

---

`7267-7278`: **Unified Dependency Declarations in Project Block**

This dependency block recapitulates several critical entries—including `@reduxjs/toolkit` (v2.5.0) and `@types/react-transition-group` (v4.4.11)—ensuring they are also declared within this section. Consistency across different parts of the configuration is key; please double-check that these entries are uniform with previous sections.

---

`7299-7313`: **Extended React & Redux Ecosystem Dependencies**

The updates include explicit references for `react-redux` (v9.2.0), `react-transition-group` (v4.4.5), and `redux` (v5.0.1). These entries support state management and UI transitions, which are critical for the new RouteMap functionality. Make sure that these versions and their virtual references align across the codebase.

---

`7396-7407`: **Introduction of Immer for Immutable State Management**

A new dependency block for `immer` (v10.1.1) is added with a HARD link. This package will enhance immutable update patterns, especially beneficial in Redux workflows. Verify that this version of Immer is compatible with your current state management and middleware implementations.

---

`9437-9470`: **React-Redux Block Refinement**

The `react-redux` dependency block now details two configurations—a primary npm package (linked as SOFT) and an accompanying virtual package (linked as HARD). Note the null entry for `@types/redux`; if this is intentional, please confirm that any consumers of Redux types are handled accordingly. This dual configuration should ensure robust integration with React’s state management.

---

`9626-9657`: **Refined React-Transition-Group Setup**

This update reorganizes the `react-transition-group` dependency into two sections: one using the npm package (with a SOFT link) and another using its virtual counterpart (with a HARD link) along with detailed peer dependencies. Ensure that this dual configuration is required and that it does not introduce any version mismatches.

---

`9733-9769`: **Redux & Redux-Thunk Dependency Enhancements**

The dependency blocks for `redux` (v5.0.1) and `redux-thunk` (v3.1.0) have been refined. Notably, `redux-thunk` now appears with both npm and virtual configurations—using a SOFT link for the direct version and a HARD link for the virtual one. Please verify that the null value for `@types/redux` is intentional and that these changes won’t create runtime or build inconsistencies.

---

`11334-11357`: **Use-Sync-External-Store Dependency Consolidation**

The update for `use-sync-external-store` (v1.4.0) shows a dual configuration: one entry using a SOFT link and another virtual entry with a HARD link, along with peer dependencies on `@types/react` and `react`. This nuanced setup promotes robust external state synchronization; please verify that this dual strategy is warranted by your project's requirements.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-08T19:01:03Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>🧹 Nitpick comments (13)</summary><blockquote>

<details>
<summary>src/app/data/shuttle/organizedTimetables.ts (1)</summary><blockquote>

`6-7`: **New property added with inconsistent comma usage.**

The `isShuttleI` boolean property has been added to the `OrganizedTimetables` type. However, there's an inconsistency in the trailing comma usage across the type definition.

Consider standardizing the trailing comma usage throughout the type definition:

```diff
export type OrganizedTimetables = {
  time: string
  direct: Array<string>
  circle: Array<string>
  directY: Array<string>
-  jungang: Array<string>,
-  isShuttleI: boolean,
+  jungang: Array<string>
+  isShuttleI: boolean
  count: number
}
```

</blockquote></details>
<details>
<summary>src/app/components/lang/lang.ko.json (1)</summary><blockquote>

`12-14`: **Consider standardizing duplicate translation entries**

The newly added keys `"yesul"` and `"jung"` have similar or identical translations to the existing keys `"yesulin"` and `"jungang"` (lines 25-26). This duplication could make maintenance more difficult if translations need to be updated in the future.


Consider one of these approaches:
1. Document the different contexts for these duplicates in a comment
2. Use a reusable translation system that references existing keys
3. If these represent shortened versions, consider naming them to indicate that (e.g., `"yesul_short"`)

</blockquote></details>
<details>
<summary>src/app/components/lang/lang.en.json (1)</summary><blockquote>

`12-14`: **Consider standardizing duplicate translation entries**

The newly added keys `"yesul"` and `"jung"` have similar translations to the existing keys `"yesulin"` and `"jungang"` (lines 25-26). This duplication may cause confusion during maintenance.


Consider one of these approaches:
1. Document the different contexts for these duplicates in a comment
2. Use a reusable translation system that references existing keys
3. If these represent shortened versions, consider naming them to indicate that (e.g., `"yesul_short"`)

</blockquote></details>
<details>
<summary>src/App.tsx (3)</summary><blockquote>

`199-199`: **State added for route card toggle.**

The `routeCardClick` state variable effectively manages the expanded/collapsed state of the route card.


Consider renaming to a more descriptive name like `isRouteCardExpanded` for better code readability.

---

`448-478`: **Well-implemented transition component for animations.**

The `Transition` component is properly used to manage animation states with appropriate timeout. The component structure with render props pattern is well-implemented.


Consider extracting the click handler function for better readability:

```diff
-<RouteIndexCardView status={state} onClick={() => {setRouteCardClick(!routeCardClick)}}>
+<RouteIndexCardView status={state} onClick={toggleRouteCard}>
```

With the following function added to the component:

```javascript
const toggleRouteCard = () => setRouteCardClick(!routeCardClick);
```

---

`454-456`: **Consider accessibility improvements for expandable content.**

The expandable route map functionality might benefit from improved accessibility.

Consider adding ARIA attributes to improve accessibility:

```diff
-<RouteIndexCardView status={state} onClick={() => {setRouteCardClick(!routeCardClick)}}>
+<RouteIndexCardView 
+  status={state} 
+  onClick={() => {setRouteCardClick(!routeCardClick)}}
+  role="button"
+  aria-expanded={routeCardClick}
+  aria-controls="route-map-content"
+>
```

And then add an id to the RouteMap:

```diff
-<RouteMap status={state} tab={tab}/>
+<RouteMap id="route-map-content" status={state} tab={tab}/>
```


Also applies to: 473-475

</blockquote></details>
<details>
<summary>src/app/components/fulltime/FullTime.tsx (4)</summary><blockquote>

`126-129`: **Remove or clarify commented-out code.**
Leaving the code commented out may introduce confusion; either remove it entirely or add a clarifying comment if you plan to reuse it later.

---

`188-197`: **Consider using built-in array methods instead of manual flag checks.**
Rather than mapping to set `isExist`, you could leverage `.some()` or `.includes()` for clearer intent and less side-effect usage.

```diff
- let isExist = false
- props.directY.map((ytime) => {
-   time === ytime ? (isExist = true) : null
- })
- return (
-   isExist ? null :
-   <React.Fragment key={idx}>
-     <span>{time} </span>
-   </React.Fragment>
- )
+ if (!props.directY.includes(time)) {
+   return (
+     <React.Fragment key={idx}>
+       <span>{time} </span>
+     </React.Fragment>
+   )
+ }
+ return null
```

---

`299-301`: **Avoid returning an empty fragment from the forEach callback.**
This line does not render anything and may cause confusion. Consider removing it or explaining its purpose.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 299-299: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation. 

(lint/correctness/useJsxKeyInIterable)

</details>

</details>

---

`316-317`: **Nested conditional for rendering can reduce readability.**
Consider an early `return null` for empty schedules to simplify the JSX at line 317.

```diff
- {schedule.direct.length + schedule.circle.length === 0 && (schedule.directY.length === 0 && schedule.jungang.length === 0) ? null : (
+ {!(schedule.direct.length || schedule.circle.length || schedule.directY.length || schedule.jungang.length) ? null : (
```

</blockquote></details>
<details>
<summary>src/app/components/routemap/RouteMap.tsx (3)</summary><blockquote>

`108-110`: **Ensure unique keys for array elements.**
Reusing `i`, `i+1`, `i+2` in the same loop pass may conflict if indices change, leading to potential key collisions.

---

`220-244`: **DOM manipulation for circle animations could be handled in React.**
Appending elements via `document.createElement` bypasses React's virtual DOM, risking synchronization issues and memory leaks. A React-based or library-managed approach would be cleaner.

---

`92-144`: **Refactor repeated code in `directInput` and `directLineInput`.**
Most lines mirror each other for direct, cycle, yesulin, and jungang. Consolidating them into generic functions can improve maintainability and readability.



Also applies to: 146-182

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between d9fca8b66fe748a0f99dbb7a900af5073265e72c and e6aa3547927996af6cc1bd2aadc22f5d9d5dc8f1.

</details>

<details>
<summary>⛔ Files ignored due to path filters (13)</summary>

* `.yarn/cache/@babel-runtime-npm-7.26.0-9afa3c4ef6-9f4ea1c1d5.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/@reduxjs-toolkit-npm-2.5.0-9bdc99574d-b24ee7e89b.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/@types-react-transition-group-npm-4.4.11-dda183055d-a7f4de6e5f.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/@types-use-sync-external-store-npm-0.0.6-9e5c635381-a95ce33066.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/dom-helpers-npm-5.2.1-b38bb4470b-bed2341adf.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/immer-npm-10.1.1-973ae10d09-9dacf1e8c2.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/react-redux-npm-9.2.0-d87bb27c82-b3d2f89f46.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/react-transition-group-npm-4.4.5-98ea4ef96e-ca32d3fd21.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/redux-npm-5.0.1-f8e6b1cb23-a373f9ed65.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/redux-thunk-npm-3.1.0-6a8fdd3211-38c563db5f.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/reselect-npm-5.1.1-667568f51c-1fdae11a39.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/use-sync-external-store-npm-1.4.0-176448bea1-08bf581a8a.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `yarn.lock` is excluded by `!**/yarn.lock`, `!**/*.lock`

</details>

<details>
<summary>📒 Files selected for processing (16)</summary>

* `.pnp.cjs` (15 hunks)
* `package.json` (3 hunks)
* `src/App.tsx` (4 hunks)
* `src/app/components/fulltime/FullTime.tsx` (7 hunks)
* `src/app/components/index.ts` (1 hunks)
* `src/app/components/lang/lang.en.json` (1 hunks)
* `src/app/components/lang/lang.ko.json` (1 hunks)
* `src/app/components/routemap/RouteMap.tsx` (1 hunks)
* `src/app/components/shuttle/Shuttle.tsx` (4 hunks)
* `src/app/data/index.ts` (1 hunks)
* `src/app/data/routemap/circleanimate.ts` (1 hunks)
* `src/app/data/shuttle/organizedTimetables.ts` (1 hunks)
* `src/app/network/shuttle.ts` (1 hunks)
* `src/app/reducer/store.ts` (1 hunks)
* `src/main.tsx` (2 hunks)
* `tailwind.config.js` (2 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>src/app/components/fulltime/FullTime.tsx</summary>

[error] 299-299: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation. 

(lint/correctness/useJsxKeyInIterable)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (1)</summary>

* GitHub Check: Cloudflare Pages

</details>

<details>
<summary>🔇 Additional comments (49)</summary><blockquote>

<details>
<summary>src/app/data/index.ts (1)</summary>

`5-5`: **Type export added correctly.**

The `CircleAnimate` type is now properly exported from this module, making it accessible to other components. This follows the established pattern of exporting types from this index file.

</details>
<details>
<summary>src/app/components/index.ts (1)</summary>

`2-2`: **Shuttle component export maintained.**

No functional changes here - the export is consistent with the module's existing pattern.

</details>
<details>
<summary>src/app/network/shuttle.ts (1)</summary>

`16-16`: <details>
<summary>✅ Verification successful</summary>

**API endpoint updated to versioned path.**

The API endpoint has been upgraded to use `/v2/`, which follows good API versioning practices. Ensure all consumers of this API are updated to handle any changes in the response structure from this new version.


---

🏁 Script executed:

```shell
#!/bin/bash
# Find any other references to the old API path pattern to ensure all have been updated
rg -l "timetable/[^v]" --type ts
```

Length of output: 61

---



</details>

**API Endpoint Update Confirmed – Note on Grep Behavior**

The API endpoint in `src/app/network/shuttle.ts` is correctly updated to use the `/v2/` versioned path. The grep command flagged this file because the regex pattern (`timetable/[^v]`) matches the character following `timetable/` in the template literal (i.e., the `$` from `${season}`), which is a false positive in this context.

- **File Checked:** `src/app/network/shuttle.ts` correctly uses `/v2/timetable/${season}/${week}/${location}`.
- **Grep Observation:** The regex pattern is not fully accounting for template literal interpolation. Consider refining the pattern if needed to avoid false positives.
- **Action Required:** Ensure all API consumers are aware of potential response structure changes due to the version update.

</details>
<details>
<summary>src/app/data/routemap/circleanimate.ts (1)</summary>

`1-7`: **Type definition looks good**

The `CircleAnimate` type is well-structured with appropriate types for animated circles in a route map. The React import is correctly included for the MutableRefObject type.

</details>
<details>
<summary>tailwind.config.js (2)</summary>

`44-44`: **LGTM: New color added correctly**

The new `chip-orange` color has been added properly following the existing pattern for chip colors.

---

`143-145`: **Correctly implemented transitionProperty**

The height transition property has been correctly configured with the proper syntax, addressing the format concerns mentioned in previous reviews.

</details>
<details>
<summary>src/main.tsx (3)</summary>

`8-8`: **LGTM: Clean import of Redux Provider.**

The import of the Redux Provider from 'react-redux' is properly defined.

---

`13-13`: **LGTM: Correct import of Redux store.**

The store is properly imported from the reducer directory.

---

`51-53`: **LGTM: Proper Redux Provider implementation.**

The Redux Provider is correctly wrapped around the App component, following standard Redux integration patterns.

</details>
<details>
<summary>src/app/reducer/store.ts (5)</summary>

`1-4`: **LGTM: Proper Redux Toolkit imports and data type.**

The imports from Redux Toolkit and the SingleShuttleSchedule type are correctly defined.

---

`5-9`: **LGTM: Good Action interface definition.**

The Action interface properly defines the type structure for Redux actions.

---

`14-21`: **Inefficient state clearing approach.**

The current implementation clears the state array by repeatedly calling `pop()`, which is inefficient and not idiomatic Redux.


Use Redux Toolkit's pattern of returning a new state or leverage Immer's direct state mutation:

```diff
updateActions: (state, action: Action) => {
-   while(state.length > 0){
-       state.pop()
-   }
-   if(action.payload !== undefined){
-       state.push(action.payload)
-   }
+   // Immer lets us "mutate" state directly
+   state.length = 0; // Clear array efficiently
+   if(action.payload !== undefined){
+       state.push(action.payload)
+   }
+   
+   // Or return a new state directly
+   // return action.payload !== undefined ? [action.payload] : [];
}
```

Additionally, if this store is designed to only ever contain a single item, consider using an object instead of an array for the state.

---

`25-29`: **LGTM: Clean store configuration.**

The store is properly configured with the actions reducer.

---

`31-31`: **LGTM: Correct action export.**

The updateActions action creator is correctly exported for use in other components.

</details>
<details>
<summary>package.json (4)</summary>

`14-16`: **LGTM: Proper Redux core dependency.**

The Redux toolkit dependency is correctly added with an appropriate version.

---

`29-31`: **LGTM: Required React-Redux binding.**

The react-redux dependency is correctly added for connecting React components to Redux.

---

`35-38`: **LGTM: Dependencies for transitions and Redux core.**

The react-transition-group and redux dependencies are correctly added with appropriate versions.

---

`46-48`: **LGTM: Type definitions for transition group.**

The TypeScript type definitions for react-transition-group are correctly added as a dev dependency.

</details>
<details>
<summary>src/app/components/shuttle/Shuttle.tsx (4)</summary>

`7-7`: **LGTM: Redux dispatch hook import.**

The useDispatch hook is correctly imported from react-redux.

---

`26-26`: **LGTM: Action creator import.**

The updateActions action creator is correctly imported from the Redux store.

---

`340-342`: **LGTM: Enhanced chip type handling.**

The ColoredChip component now correctly handles 'R' and 'NA' types with an orange background.

---

`349-349`: **LGTM: Redux dispatch setup.**

The useDispatch hook is correctly initialized for use in the component.

</details>
<details>
<summary>src/App.tsx (5)</summary>

`1-1`: **New imports support route map and transitions.**

The additions of `useState`, `Transition`, the Arrow image, and `RouteMap` component are appropriate for implementing the new route map feature with animated transitions.



Also applies to: 5-5, 10-10, 14-14

---

`121-124`: **Good styled component enhancement with transition properties.**

The `RouteIndexCardView` now accepts a `status` prop that controls its height through conditional styling, enabling smooth height transitions based on the component's state.

---

`130-134`: **Well-implemented container with opacity transitions.**

The `RouteIndexContainer` elegantly manages content visibility using opacity transitions based on the transition state, showing content only when collapsed and hiding it when expanded.

---

`135-138`: **Nice touch with the rotating arrow indicator.**

The `RouteToggleImage` component provides a visual affordance by rotating based on the expanded/collapsed state, enhancing user experience with a clear indication of the component's state.

---

`473-473`: <details>
<summary>❓ Verification inconclusive</summary>

**RouteMap integration with status and tab props.**

The `RouteMap` component is appropriately integrated with the transition state and current tab selection.


Verify that the `RouteMap` component is properly handling the status prop for animations:

---

🏁 Script executed:

```shell
#!/bin/bash
# Check how RouteMap implements the status prop
fd "RouteMap" --type file | xargs grep -A 20 "RouteMap"
```

Length of output: 899

---



</details>

**RouteMap integration verified – please double-check animation usage**

- In **src/App.tsx** (line 473), the `<RouteMap status={state} tab={tab}/>` call properly passes the required props.
- The RouteMap component correctly declares its props as `{ status: string, tab: string }`.
- The output of our search confirms that these props are defined. However, in the first 20 lines of the RouteMap implementation output no explicit animation logic based on `status` was shown.
- Please ensure that the remaining implementation (beyond the snippet provided) uses the `status` prop appropriately to trigger or control animation transitions as intended.

</details>
<details>
<summary>.pnp.cjs (15)</summary>

`27-38`: **Dependency Update: Redux Toolkit and React Transition Group Types Addition**  
The new entries for `@reduxjs/toolkit` (v2.5.0) and `@types/react-transition-group` (v4.4.11) have been added here. Please ensure that these versions are consistent with your `package.json` and that any peer or transitive dependencies remain compatible across the project.

---

`59-73`: **Update Dependency Versions for React Ecosystem**  
This block updates several React-related dependencies: `react-redux` is added at v9.2.0, `react-transition-group` at v4.4.5, and `redux` is pinned to v5.0.1. Confirm that these versions harmonize with the overall dependency tree (especially with any type definitions) and that there are no conflicts with existing configurations.

---

`2732-2745`: **New Babel Runtime Configuration Block**  
A new block for `@babel/runtime` v7.26.0 (with its dependency on `regenerator-runtime` v0.14.1) has been introduced. Verify that this version is in line with your Babel configuration and that the caching/linking settings (`linkType: HARD`) meet your project's requirements.

---

`3363-3396`: **New Configuration for @reduxjs/toolkit Resolution**  
A dedicated dependency block for `@reduxjs/toolkit` is added here. It includes both a direct npm resolution and a virtual resolution which also pulls in related dependencies (like `@types/react`, `immer`, and `redux-thunk`). Be sure to double-check that this dual resolution does not cause conflicts and that all peer dependencies are fulfilled correctly.

---

`4154-4167`: **New Block for @types/react-transition-group Dependency**  
A standalone dependency block for `@types/react-transition-group` (v4.4.11) has been added. Please verify that this declaration does not duplicate any previous definitions and that the version chosen is compatible with your installed `react-transition-group` package.

---

`4212-4226`: **Introduce Dependency Block for @types/use-sync-external-store**  
A new dependency block for `@types/use-sync-external-store` (v0.0.6) is introduced to support TypeScript usage patterns for `use-sync-external-store`. Confirm that this version aligns with other type definitions and that the linking type (`HARD`) is appropriate for your project's configuration.

---

`5835-5851`: **New Dependency Block for dom-helpers**  
A new configuration for `dom-helpers` (v5.2.1) is added, also listing its dependencies on `@babel/runtime` and `csstype`. Please ensure that this addition is intentional and that these dependent versions mesh well with those used elsewhere in the codebase.

---

`7267-7278`: **Workspace Dependency Block Consistency**  
Within the workspace dependency block for `hybus-genesis`, key dependencies (including `@reduxjs/toolkit`, `@tanstack/react-query`, and various type packages) are listed. Verify that these entries match what is declared in your other configuration files and that no version conflicts arise between workspace resolutions and external packages.

---

`7299-7313`: **Consistent Dependency Resolution for React Libraries**  
This block reasserts the dependency declarations for several React libraries (e.g. `react-redux`, `react-transition-group`, and `redux`). Make sure these versions are consistent with previously defined blocks to prevent mismatches that could lead to runtime issues.

---

`7396-7410`: **New Dependency Block for immer**  
A dependency block for `immer` (v10.1.1) has been added. Confirm that this update is compatible with your state management setup—especially if it's integrated with Redux or Redux Toolkit—and that it does not inadvertently conflict with other immutability-related libraries.

---

`9437-9470`: **Refined React-Redux Dependency Block**  
A refined configuration for `react-redux` (v9.2.0) is introduced, including both a direct npm entry and a virtual resolution. Check that these settings correctly satisfy both runtime and type requirements and that any peer dependencies (such as `@types/react` and Redux) are properly met.

---

`9626-9661`: **Enhanced React-Transition-Group Dependency Block**  
This update adds a new block for `react-transition-group` (v4.4.5) with both direct and virtual entries. The block also lists several peer dependencies. Ensure that these selections do not conflict with your existing configuration and that the peer versions for React, Babel Runtime, and type definitions are aligned.

---

`9733-9769`: **New Dependency Blocks for Redux and Redux-Thunk**  
New blocks for `redux` (v5.0.1) and `redux-thunk` (v3.1.0) have been added. Verify that these versions integrate seamlessly with your existing Redux Toolkit and middleware configurations. The virtual resolution for `redux-thunk` should also be confirmed to prevent any subtle resolution issues.

---

`9874-9888`: **New Dependency Block for Reselect**  
A new block for `reselect` (v5.1.1) is introduced here. Alongside this, note the `resolve` patch declaration that immediately follows—ensure that this patch is applied as intended for compatibility or bug fixes.

---

`11334-11361`: **New Dependency Block for use-sync-external-store**  
A dedicated block for `use-sync-external-store` (v1.4.0) has been added, including both direct and virtual resolution entries. Please check that the peer dependencies (`@types/react` and `react`) are consistent across all blocks and that the dual resolution does not lead to duplication issues.

</details>
<details>
<summary>src/app/components/fulltime/FullTime.tsx (6)</summary>

`103-104`: **No concerns on height condition for $maxChips=4.**
The logic fits the existing pattern and appears correct.

---

`180-183`: **DirectY block looks good.**
Logic for displaying `directY` times is clear and straightforward.

---

`185-185`: **Conditional chip color approach is fine.**
Using `props.isShuttleI` to toggle the chip color concisely expresses the shuttle condition.

---

`262-262`: **New property `isShuttleI` introduced.**
No issues found. Implementation aligns with the updated schedule type logic.

---

`278-279`: **Correctly handling 'R' schedule type.**
Marking `isShuttleI = true` for type `'R'` is a straightforward solution.

---

`293-295`: **Incrementing `count` for `directY`.**
Unchecked usage looks correct and consistent with other type checks.

</details>
<details>
<summary>src/app/components/routemap/RouteMap.tsx (1)</summary>

`53-54`: **Fix transition state to avoid mismatches**  
React Transition Group does not emit a plain `"exit"` state. Use `"exited"`.

```diff
- ${(props) => props.status === 'entered' || props.status === 'exit' ? tw`opacity-100` : tw`opacity-0`}
+ ${(props) => props.status === 'entered' || props.status === 'exited' ? tw`opacity-100` : tw`opacity-0`}
```

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-08T19:40:11Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 4**

<details>
<summary>♻️ Duplicate comments (2)</summary><blockquote>

<details>
<summary>src/app/reducer/store.ts (1)</summary><blockquote>

`14-21`: _🛠️ Refactor suggestion_

**Inefficient state clearing approach.**

The current implementation clears the state array by repeatedly calling `pop()`, which is inefficient and not idiomatic Redux.



Use Redux Toolkit's pattern of returning a new state or leverage Immer's direct state mutation:

```diff
updateActions: (state, action: Action) => {
-   while(state.length > 0){
-       state.pop()
-   }
-   if(action.payload !== undefined){
-       state.push(action.payload)
-   }
+   // Immer lets us "mutate" state directly
+   state.length = 0; // Clear array efficiently
+   if(action.payload !== undefined){
+       state.push(action.payload)
+   }
+   
+   // Or return a new state directly
+   // return action.payload !== undefined ? [action.payload] : [];
}
```

Additionally, if this store is designed to only ever contain a single item, consider using an object instead of an array for the state.

</blockquote></details>
<details>
<summary>src/app/components/shuttle/Shuttle.tsx (1)</summary><blockquote>

`423-429`: _⚠️ Potential issue_

**Add null check before dispatching filtered data.**

The effect dispatches `filtered[0]` without checking if the filtered array is empty, which could result in dispatching undefined.



Implement a null/empty check:

```diff
useEffect(() => {
  if(timetable.data !== undefined){
    const filtered = timetable.data.filter((val) => isAfterCurrentTime(val))
-   dispatch(updateActions(filtered[0]))
+   if(filtered.length > 0) {
+     dispatch(updateActions(filtered[0]))
+   }
  }
}, [currentTime, dispatch, timetable.data])
```

Also, consider optimizing this effect as it runs every second due to the `currentTime` dependency, which may cause unnecessary re-renders.

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (5)</summary><blockquote>

<details>
<summary>src/app/data/shuttle/organizedTimetables.ts (1)</summary><blockquote>

`6-7`: **Consider using enum instead of boolean flag**

Adding the `isShuttleI: boolean` property to the type suggests a binary state for shuttle classification. For better type safety and extensibility, consider using an enum instead of a boolean flag if there might be more shuttle types in the future.

```diff
+export enum ShuttleType {
+  REGULAR = 'regular',
+  I = 'i'
+}

 export type OrganizedTimetables = {
   time: string
   direct: Array<string>
   circle: Array<string>
   directY: Array<string>
   jungang: Array<string>,
-  isShuttleI: boolean,
+  shuttleType: ShuttleType,
   count: number
 }
```

</blockquote></details>
<details>
<summary>src/main.tsx (1)</summary><blockquote>

`8-8`: **Using multiple state management libraries may cause maintenance issues**

The application is now importing Redux (`react-redux`) while already using Recoil (line 10). Using multiple state management libraries in the same application may lead to:

1. Increased complexity in debugging
2. Inconsistent state management patterns
3. Higher learning curve for new developers

Consider consolidating to a single state management solution for better maintainability.



Also applies to: 13-13

</blockquote></details>
<details>
<summary>src/app/components/fulltime/FullTime.tsx (3)</summary><blockquote>

`126-129`: **Remove or clarify commented-out code.**  
The commented-out `YesulinMinuteWrapper` suggests it's deprecated. If it's truly unnecessary, consider removing it entirely for clarity.

---

`188-194`: **Optimize nested iteration for large timetables.**  
You’re iterating over `props.directY` for each direct time to check membership. Consider using a Set for `props.directY` to reduce the complexity from O(n^2) to O(n).  

```diff
- let isExist = false;
- props.directY.map((ytime) => {
-   time === ytime ? (isExist = true) : null;
- });
+ const resultSet = new Set(props.directY);
+ const isExist = resultSet.has(time);
```

---

`299-299`: **Unused return statement.**  
Returning `<></>` inside the `.forEach` callback doesn’t produce meaningful output. Remove it to streamline the logic.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 299-299: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation. 

(lint/correctness/useJsxKeyInIterable)

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between d9fca8b66fe748a0f99dbb7a900af5073265e72c and 28280f74862e7cfdaa2c44493bad38e3bb1435c8.

</details>

<details>
<summary>⛔ Files ignored due to path filters (13)</summary>

* `.yarn/cache/@babel-runtime-npm-7.26.0-9afa3c4ef6-9f4ea1c1d5.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/@reduxjs-toolkit-npm-2.5.0-9bdc99574d-b24ee7e89b.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/@types-react-transition-group-npm-4.4.11-dda183055d-a7f4de6e5f.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/@types-use-sync-external-store-npm-0.0.6-9e5c635381-a95ce33066.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/dom-helpers-npm-5.2.1-b38bb4470b-bed2341adf.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/immer-npm-10.1.1-973ae10d09-9dacf1e8c2.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/react-redux-npm-9.2.0-d87bb27c82-b3d2f89f46.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/react-transition-group-npm-4.4.5-98ea4ef96e-ca32d3fd21.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/redux-npm-5.0.1-f8e6b1cb23-a373f9ed65.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/redux-thunk-npm-3.1.0-6a8fdd3211-38c563db5f.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/reselect-npm-5.1.1-667568f51c-1fdae11a39.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `.yarn/cache/use-sync-external-store-npm-1.4.0-176448bea1-08bf581a8a.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
* `yarn.lock` is excluded by `!**/yarn.lock`, `!**/*.lock`

</details>

<details>
<summary>📒 Files selected for processing (16)</summary>

* `.pnp.cjs` (15 hunks)
* `package.json` (3 hunks)
* `src/App.tsx` (4 hunks)
* `src/app/components/fulltime/FullTime.tsx` (7 hunks)
* `src/app/components/index.ts` (1 hunks)
* `src/app/components/lang/lang.en.json` (1 hunks)
* `src/app/components/lang/lang.ko.json` (1 hunks)
* `src/app/components/routemap/RouteMap.tsx` (1 hunks)
* `src/app/components/shuttle/Shuttle.tsx` (4 hunks)
* `src/app/data/index.ts` (1 hunks)
* `src/app/data/routemap/circleanimate.ts` (1 hunks)
* `src/app/data/shuttle/organizedTimetables.ts` (1 hunks)
* `src/app/network/shuttle.ts` (1 hunks)
* `src/app/reducer/store.ts` (1 hunks)
* `src/main.tsx` (2 hunks)
* `tailwind.config.js` (2 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>src/app/components/fulltime/FullTime.tsx</summary>

[error] 299-299: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation. 

(lint/correctness/useJsxKeyInIterable)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (1)</summary>

* GitHub Check: Cloudflare Pages

</details>

<details>
<summary>🔇 Additional comments (60)</summary><blockquote>

<details>
<summary>src/app/components/index.ts (1)</summary>

`2-2`: **No functional change detected.**

This export statement appears to have been removed and added back with identical content, resulting in no functional change to the code.

</details>
<details>
<summary>src/app/data/index.ts (1)</summary>

`5-5`: **New type export added for CircleAnimate.**

This addition properly extends the module's exports with the new `CircleAnimate` type from the routemap module, which is likely used for animation effects in the new RouteMap component mentioned in the PR title.

</details>
<details>
<summary>src/app/components/lang/lang.en.json (2)</summary>

`12-14`: **Added new localization keys for Yesulin and Jungang.**

The addition of "yesul" and "jung" translation keys is good, but ensure they're also added to the Korean language file for consistency.


```shell
#!/bin/bash
# Check if the same keys exist in the Korean language file
echo "Checking if 'yesul' and 'jung' keys exist in Korean language file..."
grep -n "\"yesul\":" src/app/components/lang/lang.ko.json
grep -n "\"jung\":" src/app/components/lang/lang.ko.json
```

---

`12-13`: <details>
<summary>❓ Verification inconclusive</summary>

**Check for potential redundancy in translation keys.**

There appear to be both short-form keys ("yesul", "jung") and long-form keys ("yesulin", "jungang") for what seem to be the same locations. Verify that this is intentional and not redundant.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check how these keys are used in the codebase
echo "Checking for usage of the new translation keys..."
rg -n "\"yesul\"" --type tsx src/app/
rg -n "\"yesulin\"" --type tsx src/app/
rg -n "\"jung\"" --type tsx src/app/
rg -n "\"jungang\"" --type tsx src/app/
```

Length of output: 375

---



</details>

Below is a revised review comment incorporating the updated investigation:

---

**Action: Verify potential redundancy in translation keys usage**

The translation file at `src/app/components/lang/lang.en.json` (lines 12–13) defines both short-form keys (`"yesul"`, `"jung"`) and their corresponding long-form values (`"Yesulin"`, `"Jungang"`). Our initial attempt to verify key usage in the codebase using `rg --type tsx` produced file type errors. To get accurate results, please re-run the search with an updated command that targets TSX files by file glob, for example:

```shell
#!/bin/bash
echo "Checking for usage of translation keys in TSX files..."
rg -n '"yesul"' -g '*.tsx' src/app/
rg -n '"yesulin"' -g '*.tsx' src/app/
rg -n '"jung"' -g '*.tsx' src/app/
rg -n '"jungang"' -g '*.tsx' src/app/
```

Once you confirm whether both the short-form and long-form keys are actually referenced, determine if the redundant short forms can be removed or if having both is intentional.

</details>
<details>
<summary>src/app/data/routemap/circleanimate.ts (1)</summary>

`1-7`: **Type definition looks good**

The `CircleAnimate` type is properly defined with the necessary React import and appropriate types for managing circle animations in the route map.

</details>
<details>
<summary>src/main.tsx (2)</summary>

`45-46`: **Increased z-index may cause unintended UI layering issues**

Changing the Snowfall component's z-index from 1 to 12 is a significant increase. This could potentially:

1. Cause the snowfall effect to appear above important UI elements
2. Interfere with modals or popups that might have lower z-indices

Ensure this change doesn't obstruct any interactive elements in the application.

---

`51-53`: **Redux Provider implementation looks good**

The Redux Provider is correctly implemented to wrap the App component, making the store available throughout the component tree.

</details>
<details>
<summary>tailwind.config.js (2)</summary>

`44-44`: **LGTM! Added new color for enhanced UI elements.**

The addition of 'chip-orange' color extends the design system and supports the new bus type styling in the Shuttle component.

---

`143-145`: **Fixed the transitionProperty configuration format.**

The current configuration correctly sets the value as a CSS property string rather than an array of variants. This properly resolves the issue identified in previous reviews.

</details>
<details>
<summary>src/app/reducer/store.ts (2)</summary>

`1-8`: **LGTM! Good setup for Redux integration.**

The imports and interface definition are well-structured for type safety.

---

`25-31`: **LGTM! Store configuration is well-structured.**

The store setup and action exports follow Redux best practices.

</details>
<details>
<summary>package.json (2)</summary>

`47-47`: **LGTM! Added type definitions for improved development experience.**

Adding TypeScript definitions for react-transition-group ensures better type checking and IDE support.

---

`15-15`: <details>
<summary>✅ Verification successful</summary>

**Verify Redux package versions for compatibility.**

The added Redux-related dependencies appear to be the latest major versions, which is good for security and features. However, ensure they're compatible with your React version (18.3.1).




Also applies to: 30-30, 36-38

---

🌐 Web query:

```web
Are Redux v5.0.1, Redux Toolkit v2.5.0, and React Redux v9.2.0 compatible with React 18.3.1?
```

💡 Result:

Yes, Redux v5.0.1, Redux Toolkit v2.5.0, and React Redux v9.2.0 are fully compatible with React 18.3.1. Here's the compatibility breakdown:

### Core Compatibility
1. **React 18.3.1 Support**
   - React Redux v9.x specifically requires React 18+[1][6]
   - Redux Toolkit v2.5.0 works with React 18's concurrent features[2][6]
   - Redux core v5.x has no direct React dependency but aligns with this ecosystem[16]

### Key Version Requirements
| Library          | Required React Version | Compatible with 18.3.1? |
|------------------|-------------------------|-------------------------|
| React Redux 9.2  | React 18+               | Yes ✅                  |
| Redux Toolkit 2.5| React 18+               | Yes ✅                  |
| Redux Core 5.0.1 | N/A (JS library)        | Yes ✅                  |

### Migration Considerations
- **Peer Dependencies**: React Redux 9.2 explicitly lists React 18+ in its peer dependencies[1][3]
- **TypeScript Support**: All versions include updated types for React 18's API surface[1][2]
- **Concurrent Mode**: Full support for React 18's concurrent rendering features[1][4]

### Upgrade Path
```bash
# Recommended installation command
npm install react@18.3.1 react-dom@18.3.1
npm install @reduxjs/toolkit@2.5.0 react-redux@9.2.0 redux@5.0.1
```

The Redux team specifically designed these versions to work together with React 18's feature set, including:
- Concurrent rendering capabilities
- New `useSyncExternalStore` hook implementation
- Updated TypeScript definitions[1][2][6]

For projects using React 18.3.1, this combination represents the officially supported stack from the Redux maintainers[1][2][16].

Citations:

- 1: https://github.com/reduxjs/react-redux/releases
- 2: https://redux-toolkit.js.org/usage/migrating-rtk-2
- 3: https://www.jsdocs.io/package/react-redux
- 4: https://redux.js.org/usage/migrating-to-modern-redux
- 5: https://www.npmjs.com/package/react-redux
- 6: https://www.npmjs.com/package/@reduxjs/toolkit
- 7: https://www.npmjs.com/package/react-redux/v/8.0.0-alpha.1?activeTab=versions
- 8: https://forum.freecodecamp.org/t/i-have-a-problem-with-applying-redux-toolkit-to-my-project/615115
- 9: https://community.developer.atlassian.com/t/versions-missmatch-between-react-and-react-dom-after-switch-to-ui-kit-2-ga/78080
- 10: https://redux-toolkit.js.org
- 11: https://react-redux.js.org
- 12: https://github.com/reduxjs/redux-toolkit/issues/4540
- 13: https://dev.to/ifeanyichima/how-to-downgrade-from-react-18-to-1702-818
- 14: https://github.com/reduxjs/redux-toolkit/issues/4759
- 15: https://endoflife.date/react
- 16: https://redux.js.org/usage/migrations/migrating-rtk-2
- 17: https://github.com/reduxjs/react-redux/blob/master/package.json
- 18: https://react.dev
- 19: https://deps.dev/npm/react-redux/3.0.1/compare
- 20: https://reactrouter.com

---



</details>

**Redux Package Compatibility Confirmed: No Changes Required**

The Redux-related dependencies (Redux v5.0.1, Redux Toolkit v2.5.0, and React Redux v9.2.0) have been verified and are fully compatible with React 18.3.1. No further modifications are needed based on this compatibility check.

- **File:** package.json (Lines 15, 30, 36-38)
- **Conclusion:** The versions used are appropriate and compatible with the current React version.

</details>
<details>
<summary>src/app/components/shuttle/Shuttle.tsx (3)</summary>

`7-7`: **LGTM! Proper imports for Redux integration.**

The Redux imports are correctly set up to enable state management for shuttle schedules.



Also applies to: 26-26

---

`340-342`: **LGTM! Enhanced visual feedback for additional bus types.**

The ColoredChip component now properly handles 'R' and 'NA' bus types with the new orange color scheme.

---

`349-349`: **LGTM! Properly initialized Redux dispatch.**

The useDispatch hook is correctly set up to enable dispatching actions to the Redux store.

</details>
<details>
<summary>src/App.tsx (5)</summary>

`5-5`: **Imports for transitions, icons, and RouteMap look good.**  
No concerns with these new imports. They are consistent with the code usage.  



Also applies to: 10-10, 14-14

---

`121-124`: **Height transition logic is consistent.**  
The usage of the `status` prop to transition height from a minimal to expanded state is well-implemented. This approach is clean and maintainable.

---

`130-134`: **Verify the reversed transition logic.**  
Using `opacity-100` when `status === 'exited'` can invert the typical expectation that “exited” means fully hidden. Ensure this matches your intended UX design.

---

`199-199`: **Local state for toggling the route card is effective.**  
Using a boolean state variable `routeCardClick` is straightforward for controlling this expand/collapse behavior.

---

`448-478`: **Transition usage is logical and well-scoped.**  
Integrating `react-transition-group` to animate the route card is nicely done. The `in={routeCardClick}` prop and `timeout={150}` look consistent for a simple toggle effect.

</details>
<details>
<summary>src/app/components/fulltime/FullTime.tsx (4)</summary>

`104-104`: **Extended height for 4-line timetables.**  
This conditional section optimally scales the timetable layout when `$maxChips` equals 4.

---

`180-183`: **Support for directY times is clearly integrated.**  
The new grid block for `directY` is well-labeled with a green chip. This is a clean separation from other timetable categories.

---

`185-185`: **Dynamic color for direct shuttle.**  
The conditional `isShuttleI ? "bg-chip-orange" : "bg-chip-blue"` is a neat way to highlight special shuttle routes.

---

`279-279`: **Visibility flag for reversed route.**  
Setting `isShuttleI = true` when `schedule.type === 'R'` is consistent with your dynamic color approach for direct shuttles.

</details>
<details>
<summary>src/app/components/routemap/RouteMap.tsx (20)</summary>

`1-4`: **Redux integration for route data.**  
Importing from Redux and local data sources is consistent with your state management approach.

---

`5-9`: **Twin.macro styled usage is consistent.**  
No obvious issues with the styled-components or twin.macro imports.

---

`10-16`: **Base circle component.**  
The `Circle` styled component is well-defined and sets a clear foundation for the other variants.

---

`18-35`: **Specialized circle variants for route types.**  
Using separate styled components for each route color helps keep the design logic straightforward.

---

`36-41`: **Layout containers for routes.**  
These specialized container components form a consistent grid structure.

---

`42-45`: **Internationalization style logic.**  
Switching font size based on the current language is a small but helpful i18n detail.

---

`46-51`: **Core station and method components.**  
Well-organized styled wrappers keep the route info visually consistent.

---

`52-55`: **Use "exited" instead of "exit".**  
React Transition Group doesn’t emit a plain `"exit"` state, so this condition won’t behave as intended.

---

`56-59`: **Route line styling.**  
The transition-based styling for these lines parallels the circle expansions and looks good.

---

`60-83`: **State initialization for route mapping.**  
Maintaining separate arrays for direct, cycle, yesulin, and jungang circles/lines is a clean approach.

---

`84-88`: **Line references for dynamic updates.**  
Storing refs to each line element allows you to size and position them post-render.

---

`183-202`: **Coordinate-based line drawing approach is valid.**  
Computing line width via the distance formula on circle positions is straightforward geometry.

---

`203-208`: **Update lines on resize.**  
Your `updateLines` callback is properly triggered after window resizing. This ensures correct alignment.

---

`210-219`: **Dynamic language updates.**  
Refreshing text content based on the current language is a sensible approach for multi-lingual support.

---

`220-244`: **Ping circle animations.**  
Appending a ping circle for highlight is creative. Ensure you remove them properly to prevent stacking duplicates.

---

`246-253`: **Circle cleanup logic.**  
`circleAnimationRemove` systematically removes ping circles, preventing animation overlap.

---

`254-264`: **Switching route type reference.**  
The `timetableType` helper clarifies which circle refs and colors to highlight.

---

`265-275`: **Language effect hooks.**  
Running `fetchLanguages` upon language changes maintains the correct text labels for route points.

---

`276-311`: **Highlight logic in route transitions.**  
Removing old animations and initializing new ones per `props.tab` is cleanly gathered in a single effect.

---

`312-360`: **Comprehensive route layout.**  
Mapping each route type into its own column with circles and lines is logical and maintainable.

</details>
<details>
<summary>.pnp.cjs (15)</summary>

`27-38`: **Verification: New Dependencies Addition**  
The additions for "@reduxjs/toolkit" (line 30) and "@types/react-transition-group" (line 35) are correctly introduced with the appropriate version strings and virtual identifiers. Please ensure these versions match those declared in your package manifest for consistency.

---

`59-73`: **Addition of UI and Redux-Related Dependencies**  
New entries—including "react-redux" (line 62), "react-transition-group" (line 68), and the redux package update at line 70—are added with pinned versions and virtual identifiers. Verify that these versions and the use of virtual references align with your package.json and overall dependency strategy.

---

`2732-2745`: **New Entry for @babel/runtime Configuration**  
A fresh configuration block for "@babel/runtime" at version 7.26.0 is introduced (lines 2735–2743), including its dependency on "regenerator-runtime". Ensure that this additional entry is consistent with your dependency graph and does not conflict with other Babel runtime configurations.

---

`3363-3398`: **Redux Toolkit Yarn PnP Entry Update**  
The new section for "@reduxjs/toolkit" (lines 3366–3395) features two entries—a direct npm entry (line 3367–3373 with a SOFT link type) and a virtual entry with several peer dependencies (lines 3374–3394). Please verify that distinguishing between the SOFT and HARD link types is intentional and that both entries are required in your build.

---

`4154-4169`: **Addition of Type Definitions**  
The new block for "@types/react-transition-group" (lines 4157–4166) properly includes its own dependency on "@types/react" and is followed by the entry for "@types/resolve". These additions seem accurate; just confirm that they match the declared types in your project’s configuration.

---

`4212-4226`: **TypeScript ESLint and Use-Sync-External-Store Definitions**  
A new entry for "@types/use-sync-external-store" (lines 4215–4223) is added, immediately preceding the configuration for "@typescript-eslint/eslint-plugin". Verify that the version for the ESLint plugin (7.8.0) matches your linting setup and that these type definitions are needed by your project.

---

`5835-5851`: **Configuration for dom-helpers & dot-case**  
The new configuration for "dom-helpers" (lines 5838–5847) correctly references its dependencies (including "@babel/runtime" and "csstype"). The subsequent "dot-case" entry at line 5850 appears standard. Confirm that these entries do not duplicate existing definitions elsewhere.

---

`7267-7278`: **Synchronizing Workspace Dependency Configuration**  
Within the workspace dependency block for "hybus-genesis" (lines 7267–7278), the entry for "@reduxjs/toolkit" (line 7270) and for "@types/react-transition-group" (line 7275) are refreshed. Ensure these mirror the entries defined in other sections, maintaining consistency across the configuration.

---

`7299-7313`: **Replication of UI Dependency Entries**  
This block (lines 7299–7313) reaffirms the UI-related dependencies—such as "react-redux" (line 7302) and "react-transition-group" (line 7308)—with virtual references. Confirm that these duplicated sections are intentional (e.g. for different resolution contexts) and consistent with the rest of the configuration.

---

`7396-7410`: **Addition of Immer Dependency**  
A dedicated entry for "immer" (lines 7399–7407) at version 10.1.1 is added before the "immutable" block. Verify that this update is compatible with your Redux middleware or state management usage.

---

`9437-9470`: **Enhancement of react-redux Entry**  
The configuration for "react-redux" (lines 9440–9467) now includes both a primary (SOFT-linked) and a virtual (HARD-linked) entry with detailed dependency declarations and peer requirements. Check that the dual entry approach is deliberate and aligns with your project’s dependency resolution strategy.

---

`9626-9661`: **React-Transition-Group Configuration Update**  
This hunk (lines 9629–9658) introduces two entries for "react-transition-group": one primary entry with a SOFT link type and one virtual entry with an extensive dependency list and peer configuration. Ensure the additional dependencies (e.g., "@babel/runtime", "loose-envify", "prop-types") are correct and necessary for your runtime behavior.

---

`9733-9769`: **Updated Redux and Redux-Thunk Configuration**  
The new blocks (lines 9733–9765) add or update the configuration for "redux" and "redux-thunk" (with dual entries for thunk). Please verify that the packagePeers for "redux-thunk" (including references to "@types/redux") meet your project’s compatibility needs.

---

`9874-9888`: **New Reselect and Resolve Configuration**  
The addition for "reselect" (lines 9877–9885) is straightforward. The subsequent entry for "resolve" (starting at line 9887) shows a patched version. Please double-check that the applied patch for "resolve" is documented and intentional within your project’s context.

---

`11334-11361`: **Updated Use-Sync-External-Store Configuration**  
The entries for "use-sync-external-store" (lines 11337–11357) now include both a direct npm version and a virtual version with associated peers ("@types/react" and "react"). The transition to a SOFT link type for the direct entry appears deliberate. Verify that these configurations, as well as the subsequent "util-deprecate" entry, are aligned with your runtime expectations.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-09T06:48:32Z

Review comments:
diff_hunk: @@ -0,0 +1,31 @@
+import { configureStore, createSlice } from "@reduxjs/toolkit";
+
+import { SingleShuttleSchedule } from "@/data";
+
+interface Action{
+    type: string,
+    payload: SingleShuttleSchedule
+}
+
+const actions = createSlice({
+    name: 'actions',
+    initialState: [] as SingleShuttleSchedule[],
+    reducers: {
+        updateActions: (state, action: Action) => {
+            while(state.length > 0){
+                state.pop()
+            }
+            if(action.payload !== undefined){
+                state.push(action.payload)
+            }
+        }
path: src/app/reducer/store.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Inefficient state clearing approach**

The current implementation clears the state array by repeatedly calling `pop()`, which is inefficient and not idiomatic Redux.


Use Redux Toolkit's pattern of returning a new state or leverage Immer's direct state mutation:

```diff
updateActions: (state, action: Action) => {
-   while(state.length > 0){
-       state.pop()
-   }
-   if(action.payload !== undefined){
-       state.push(action.payload)
-   }
+   // Immer lets us "mutate" state directly
+   state.length = 0; // Clear array efficiently
+   if(action.payload !== undefined){
+       state.push(action.payload)
+   }
+   
+   // Or return a new state directly
+   // return action.payload !== undefined ? [action.payload] : [];
}
```

Additionally, if this store is designed to only ever contain a single item, consider using an object instead of an array for the state.


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
        updateActions: (state, action: Action) => {
            // Immer lets us "mutate" state directly
            state.length = 0; // Clear array efficiently
            if(action.payload !== undefined){
                state.push(action.payload)
            }
            
            // Or return a new state directly
            // return action.payload !== undefined ? [action.payload] : [];
        }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-08T18:55:34Z
author_association: NONE
diff_hunk: @@ -139,6 +140,9 @@ module.exports = {
           },
         },
       },
+      transitionProperty:{
+        'height': ['responsive', 'hover', 'focus'],
+      },
path: tailwind.config.js
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>❓ Verification inconclusive</summary>

**Verify transitionProperty configuration format**

The `transitionProperty` configuration may not be correctly formatted. In Tailwind, transition properties are typically defined as objects where keys are names and values are CSS property lists, not arrays of variants.


Please verify the correct format according to Tailwind documentation:




Consider adjusting to the standard format if needed:

```diff
transitionProperty:{
-    'height': ['responsive', 'hover', 'focus'],
+    'height': 'height',
},
```

---

🏁 Script executed:

```shell
#!/bin/bash
# Check for transitionProperty examples in Tailwind docs or other files
curl -s https://tailwindcss.com/docs/transition-property | grep -A 10 "module.exports"
```

Length of output: 86

---



</details>

**Fix the `transitionProperty` configuration in tailwind.config.js**

The current configuration assigns an array of variants to `'height'`, but Tailwind expects the values to be CSS property strings. To ensure proper functionality, please change the configuration as shown below and handle variants separately (e.g., in the `variants` section):

- **File:** `tailwind.config.js` (Lines 143-145)
- **Change From:**
  ```js
      transitionProperty:{
        'height': ['responsive', 'hover', 'focus'],
      },
  ```
- **Change To:**
  ```js
      transitionProperty:{
        'height': 'height',
      },
  ```

Review and verify that the intended variants (`responsive`, `hover`, `focus`) are applied via the appropriate Tailwind configuration options.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-08T18:55:34Z
author_association: NONE
diff_hunk: @@ -0,0 +1,361 @@
+import React, { useCallback, useEffect,useRef, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useSelector } from 'react-redux';
+import styled from 'styled-components';
+import tw from 'twin.macro';
+
+import { CircleAnimate } from '@/data';
+import { SingleShuttleSchedule } from "@/data";
+
+const Circle = styled.span`
+  ${tw`
+    flex rounded-full inline-block
+    h-3 w-3 rt1:h-2.5 rt1:w-2.5
+    z-10
+  `}
+`
+
+const CycleCircle = styled(Circle)`
+  ${tw`bg-chip-red mx-2`}
+`
+
+const DirectCircle = styled(Circle)`
+  ${tw`bg-chip-blue mx-2`}
+`
+
+const YesulinCircle = styled(Circle)`
+  ${tw`bg-chip-green mx-2`}
+`
+
+const JungangCircle = styled(Circle)`
+  ${tw`bg-chip-purple mx-2`}
+`
+const EndCircle = styled(Circle)`
+    ${tw`bg-chip-orange mx-2`}
+`
+const RouteRowsContainer = styled.div`
+${tw`grid grid-rows-5 gap-2`}
+`
+const RouteColsContainer = styled.div`
+${tw`grid grid-cols-6 place-items-center`}
+`
+const RouteTextContainer =  styled.div<{lang:string}>`
+${tw`whitespace-nowrap text-center`}
+${(props) => props.lang === 'ko' ? tw`text-[15px] hm:text-[13px]` : tw`text-[12px] rt1:text-[9px]`}
+`
+const RouteStations = styled.div`
+${tw`transition duration-150 ease-in-out flex relative`}
+`
+const RouteMethod = styled.div`
+${tw`text-center rounded-full py-1 w-16 hm:w-12 hm:text-xs self-center text-black`}
+`
+const MainContainer = styled.div<{status: string}>`
+${tw`transition duration-150 ease-in-out mx-auto`}
+    ${(props) => props.status === 'entered' || props.status === 'exit' ? tw`opacity-100` : tw`opacity-0`}
+`
path: src/app/components/routemap/RouteMap.tsx
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix the transition state check.**

React Transition Group doesn’t emit a plain `"exit"` state. It provides `"exiting"` or `"exited"` instead. This mismatch can break your opacity logic.


```diff
- ${(props) => props.status === 'entered' || props.status === 'exit' ? tw`opacity-100` : tw`opacity-0`}
+ ${(props) => props.status === 'entered' || props.status === 'exited' ? tw`opacity-100` : tw`opacity-0`}
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
const MainContainer = styled.div<{status: string}>`
${tw`transition duration-150 ease-in-out mx-auto`}
    ${(props) => props.status === 'entered' || props.status === 'exited' ? tw`opacity-100` : tw`opacity-0`}
`
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-08T18:55:34Z
author_association: NONE
diff_hunk: @@ -0,0 +1,5 @@
+export type CircleAnimate = {
+    ref: React.MutableRefObject<Array<HTMLDivElement>>,
+    index: number,
+    chipColor: string,
+}
path: src/app/data/routemap/circleanimate.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Missing React import for React types**

The `CircleAnimate` type uses `React.MutableRefObject` but there's no import statement for React. This will cause a compilation error if the React namespace isn't available in this file.


Add the following import at the top of the file:

```diff
+import React from 'react';
+
 export type CircleAnimate = {
     ref: React.MutableRefObject<Array<HTMLDivElement>>,
     index: number,
     chipColor: string,
 }
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
import React from 'react';

export type CircleAnimate = {
    ref: React.MutableRefObject<Array<HTMLDivElement>>,
    index: number,
    chipColor: string,
}
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-08T19:01:02Z
author_association: NONE
diff_hunk: @@ -9,6 +9,11 @@
     "cycle": "Cycle",
     "direct": "Direct",    
     "cycle_ja": "Direct",
+    "yesul": "Yesulin",
+    "jung": "Jungang",
+
+    "dorm": "Dormitory",
+
path: src/app/components/lang/lang.en.json
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>❓ Verification inconclusive</summary>

**Ensure consistency across language files**

The "dorm" key has been added to the English file but not to the Korean file. While the Korean file has translations for dormitory under other keys (like "residence"), consider adding a matching "dorm" key to the Korean file for consistency.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if "dorm" exists in the Korean language file
grep -n "\"dorm\":" src/app/components/lang/lang.ko.json
```

Length of output: 56

---



</details>

**Inconsistency in Language Key Definitions Detected**

- The English language file (`src/app/components/lang/lang.en.json`) now includes the `"dorm"` key.
- A grep search in the Korean file (`src/app/components/lang/lang.ko.json`) did not find any existing `"dorm"` key.
- For consistent terminology across translations, please add a corresponding `"dorm"` key (with the appropriate Korean translation) to the Korean file.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-08T19:01:03Z
author_association: NONE
diff_hunk: @@ -415,6 +420,14 @@ export const Shuttle = ({ location }: ShuttleStop) => {
     }
   }, [season, week])
 
+  useEffect(() => {
+    if(timetable.data !== undefined){
+      const filtered = timetable.data.filter((val) => isAfterCurrentTime(val))
+      dispatch(updateActions(filtered[0]))
+    }
+
+  }, [currentTime, dispatch, timetable.data])
path: src/app/components/shuttle/Shuttle.tsx
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Add null check before dispatching filtered data.**

The effect dispatches `filtered[0]` without checking if the filtered array is empty, which could result in dispatching undefined.


Implement a null/empty check:

```diff
useEffect(() => {
  if(timetable.data !== undefined){
    const filtered = timetable.data.filter((val) => isAfterCurrentTime(val))
-   dispatch(updateActions(filtered[0]))
+   if(filtered.length > 0) {
+     dispatch(updateActions(filtered[0]))
+   }
  }
}, [currentTime, dispatch, timetable.data])
```

Also, consider optimizing this effect as it runs every second due to the `currentTime` dependency, which may cause unnecessary re-renders.


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
  useEffect(() => {
    if(timetable.data !== undefined){
      const filtered = timetable.data.filter((val) => isAfterCurrentTime(val))
      if(filtered.length > 0) {
        dispatch(updateActions(filtered[0]))
      }
    }

  }, [currentTime, dispatch, timetable.data])
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-08T19:40:10Z
author_association: NONE
diff_hunk: @@ -13,7 +13,7 @@ export const shuttleAPI = async (
   location: StopLocation
 ): Promise<Array<SingleShuttleSchedule>> => {
   return apiHandler<Array<SingleShuttleSchedule>>(
-    `/timetable/${season}/${week}/${location}`
+    `/v2/timetable/${season}/${week}/${location}`
path: src/app/network/shuttle.ts
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**API endpoint version updated to v2.**

The shuttle API endpoint has been updated to use a versioned path (`/v2/timetable/...`), which is a good practice for API versioning. This change ensures backward compatibility while allowing for API evolution.


---

🏁 Script executed:

```shell
#!/bin/bash
# Check if there are other API calls that might need versioning update for consistency
echo "Checking for other unversioned API endpoints..."
rg -n "apiHandler<.*>\('/" --type ts src/app/
```

Length of output: 399

---



</details>

**API Versioning Consistency – Additional Endpoints Need Updating**

The update to the timetable endpoint using `/v2/timetable/${season}/${week}/${location}` in `src/app/network/shuttle.ts` is a solid improvement for versioning. However, a shell script search revealed that there are additional unversioned endpoints that might need similar treatment for consistency:

- **src/app/network/shuttle.ts (line ~21):** The `/settings/` endpoint remains unversioned.
- **src/app/network/notice.ts (line ~5):** The `/announcements/` endpoint is still unversioned.
- **src/app/network/changelog.ts (line ~5):** The `/changelog/` endpoint is unversioned.

**Recommendation:**  
Please update these endpoints to include versioning (for example, `/v2/settings/`, `/v2/announcements/`, `/v2/changelog/`) if the API evolution plan applies across the board. If certain endpoints are intentionally left unversioned, kindly document the rationale to avoid future confusion.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-09T06:48:31Z
author_association: NONE
diff_hunk: @@ -9,6 +9,9 @@
     "cycle": "순환",
     "direct": "직행",
     "cycle_ja": "중앙역",
+    "yesul": "예술인",
+
+    "jung": "중앙역",
path: src/app/components/lang/lang.ko.json
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Localization entries added, but potential duplication detected**

The newly added entries "yesul" and "jung" provide localization for "예술인" (artist) and "중앙역" (central station). However, the term "jung" (중앙역) appears to duplicate existing entry "jungang" (also 중앙역) found on line 26. 

This could lead to inconsistent usage throughout the application. Consider standardizing on a single term for central station.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-09T06:48:31Z
author_association: NONE
diff_hunk: @@ -0,0 +1,361 @@
+import React, { useCallback, useEffect,useRef, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useSelector } from 'react-redux';
+import styled from 'styled-components';
+import tw from 'twin.macro';
+
+import { CircleAnimate } from '@/data';
+import { SingleShuttleSchedule } from "@/data";
+
+const Circle = styled.span`
+  ${tw`
+    flex rounded-full inline-block
+    h-3 w-3 rt1:h-2.5 rt1:w-2.5
+    z-10
+  `}
+`
+
+const CycleCircle = styled(Circle)`
+  ${tw`bg-chip-red mx-2`}
+`
+
+const DirectCircle = styled(Circle)`
+  ${tw`bg-chip-blue mx-2`}
+`
+
+const YesulinCircle = styled(Circle)`
+  ${tw`bg-chip-green mx-2`}
+`
+
+const JungangCircle = styled(Circle)`
+  ${tw`bg-chip-purple mx-2`}
+`
+const EndCircle = styled(Circle)`
+    ${tw`bg-chip-orange mx-2`}
+`
+const RouteRowsContainer = styled.div`
+${tw`grid grid-rows-5 gap-2`}
+`
+const RouteColsContainer = styled.div`
+${tw`grid grid-cols-6 place-items-center`}
+`
+const RouteTextContainer =  styled.div<{lang:string}>`
+${tw`whitespace-nowrap text-center`}
+${(props) => props.lang === 'ko' ? tw`text-[15px] hm:text-[13px]` : tw`text-[12px] rt1:text-[9px]`}
+`
+const RouteStations = styled.div`
+${tw`transition duration-150 ease-in-out flex relative`}
+`
+const RouteMethod = styled.div`
+${tw`text-center rounded-full py-1 w-16 hm:w-12 hm:text-xs self-center text-black`}
+`
+const MainContainer = styled.div<{status: string}>`
+${tw`transition duration-150 ease-in-out mx-auto`}
+    ${(props) => props.status === 'entered' || props.status === 'exit' ? tw`opacity-100` : tw`opacity-0`}
+`
+const RouteLine = styled.div`
+    ${tw`absolute transition duration-150 ease-in-out h-[3px] z-1`}
+
+`
+export const RouteMap = (props: {
+    status:string,
+    tab: string
+}) => {
+    interface RootState {
+        actions: SingleShuttleSchedule[]
+    }
+    const timetable = useSelector((state: RootState) => state.actions)
+    const { t, i18n } = useTranslation()
+    // dots
+    const [direct, setDirect] = useState<JSX.Element[]>([])
+    const [cycle, setCycle] = useState<JSX.Element[]>([])
+    const [yesulin, setYesulin] = useState<JSX.Element[]>([])
+    const [jungang, setJungang] = useState<JSX.Element[]>([])
+    // lines
+    const [dirLine, setDirLine] = useState<JSX.Element[]>([])
+    const [cycLine, setCycLine] = useState<JSX.Element[]>([])
+    const [yesLine, setYesLine] = useState<JSX.Element[]>([])
+    const [junLine, setJunLine] = useState<JSX.Element[]>([])
+    // dot refs
+    const refdir = useRef<Array<HTMLDivElement>>([])
+    const refcyc = useRef<Array<HTMLDivElement>>([])
+    const refyes = useRef<Array<HTMLDivElement>>([])
+    const refjun = useRef<Array<HTMLDivElement>>([])
+    // line refs
+    const linedir = useRef<Array<HTMLDivElement>>([])
+    const linecyc = useRef<Array<HTMLDivElement>>([])
+    const lineyes = useRef<Array<HTMLDivElement>>([])
+    const linejun = useRef<Array<HTMLDivElement>>([])
+    // langtest
+    const lang = useRef<Array<HTMLParagraphElement | null>>([])
+    const directInput = useCallback(() => {
+        const arrdir = []
+        const arrcyc = []
+        const arryes = []
+        const arrjun = []
+        for(let i = 0; i < 5; i++){
+            if(i === 4){
+                arrdir.push(<RouteStations key={i} ref={d => d!= null?refdir.current[i] = d:null}><EndCircle/></RouteStations>)
+                continue
+            }
+            arrdir.push(<RouteStations key={i} ref={d => d!= null?refdir.current[i] = d:null}><DirectCircle/></RouteStations>)
+        }
+        for(let i = 0; i < 6; i++){
+            if(i >= 2 && i <= 4){
+                if(i === 2){
+                    arrcyc.push(
+                        <div key={i} className='col-span-2 grid grid-cols-3 w-[75%] place-items-center'>
+                            <RouteStations key={i} ref={d => d!= null?refcyc.current[i] = d:null}><CycleCircle/></RouteStations>
+                            <RouteStations key={i+1} ref={d => d!= null?refcyc.current[i+1] = d:null}><CycleCircle className='grid grid-rows-2 relative'><p key={0} ref={d => lang.current[0] = d} className='absolute text-xs top-[-20px] left-[-14px] text-center w-10 text-chip-green'>{t('yesul')}</p></CycleCircle></RouteStations>
+                            <RouteStations key={i+2} ref={d => d!= null?refcyc.current[i+2] = d:null}><CycleCircle/></RouteStations>
+                        </div>
+                    )
+                    arryes.push(
+                        <div key={i} className='col-span-2 grid grid-cols-3 w-[75%] place-items-center'>
+                            <RouteStations key={i} ref={d => d!= null?refyes.current[i] = d:null} title='skip'><YesulinCircle className='opacity-0'/></RouteStations>
+                            <RouteStations key={i+1} ref={d => d!= null?refyes.current[i+1] = d:null}><YesulinCircle className='grid grid-rows-2 relative'><p key={1} ref={d => lang.current[1] = d} className='absolute text-xs top-[-20px] left-[-14px] text-center w-10 text-chip-green'>{t('yesul')}</p></YesulinCircle></RouteStations>
+                            <RouteStations key={i+2} ref={d => d!= null?refyes.current[i+2] = d:null}><YesulinCircle/></RouteStations>
+                        </div>
+                    )
+                    arrjun.push(
+                        <div key={i} className='col-span-2 grid grid-cols-3 w-[75%] place-items-center'>
+                            <RouteStations key={i} ref={d => d!= null?refjun.current[i] = d:null}><JungangCircle/></RouteStations>
+                            <RouteStations key={i+1} ref={d => d!= null?refjun.current[i+1] = d:null}><JungangCircle className='grid grid-rows-2 relative'><p key={2} ref={d => lang.current[2] = d} className='absolute text-xs top-[-20px] left-[-14px] text-center w-10 text-chip-purple'>{t('jung')}</p></JungangCircle></RouteStations>
+                            <RouteStations key={i+2} ref={d => d!= null?refjun.current[i+2] = d:null}><JungangCircle/></RouteStations>
+                        </div>
+                    )
+                }
+                continue
+            }
+            if(i === 5){
+                arrcyc.push(<RouteStations key={i} ref={d => d!= null?refcyc.current[i] = d:null}><EndCircle/></RouteStations>)
+                arryes.push(<RouteStations key={i} ref={d => d!= null?refyes.current[i] = d:null}><EndCircle/></RouteStations>)
+                arrjun.push(<RouteStations key={i} ref={d => d!= null?refjun.current[i] = d:null}><EndCircle/></RouteStations>)
+                continue
+            }
+            arrcyc.push(<RouteStations key={i} ref={d => d!= null?refcyc.current[i] = d:null}><CycleCircle/></RouteStations>)
+            arryes.push(<RouteStations key={i} ref={d => d!= null?refyes.current[i] = d:null}><YesulinCircle/></RouteStations>)
+            arrjun.push(<RouteStations key={i} ref={d => d!= null?refjun.current[i] = d:null}><JungangCircle/></RouteStations>)
+        }
+        setDirect(arrdir)
+        setCycle(arrcyc)
+        setYesulin(arryes)
+        setJungang(arrjun)
+    },[t])
+        const directLineInput = useCallback(() => {
+            const arrdir = []
+            const arrcyc = []
+            const arryes = []
+            const arrjun = []
+            for(let i = 0; i < 4; i++){
+                if(i === 3){
+                    arrdir.push(
+                        <RouteLine className={'bg-chip-orange'} key={i} ref={d=> d!= null?linedir.current[i] = d:null}></RouteLine>
+                    )
+                    continue
+                }
+                arrdir.push(
+                    <RouteLine className={'bg-chip-blue'} key={i} ref={d=> d!= null?linedir.current[i] = d:null}></RouteLine>
+                )
+            }
+            for(let i = 0; i < 5; i++){
+                if(i === 4){
+                    arrcyc.push(<RouteLine className={'bg-chip-orange'} key={i} ref={d=> d!= null?linecyc.current[i] = d:null}></RouteLine>)
+                    arryes.push(<RouteLine className={'bg-chip-orange'} key={i} ref={d=> d!= null?lineyes.current[i] = d:null}></RouteLine>)
+                    arrjun.push(<RouteLine className={'bg-chip-orange'} key={i} ref={d=> d!= null?linejun.current[i] = d:null}></RouteLine>)
+                    continue
+                }
+                arrcyc.push(
+                    <RouteLine className={'bg-chip-red'} key={i} ref={d=> d!= null?linecyc.current[i] = d:null}></RouteLine>
+                )
+                arryes.push(
+                    <RouteLine className={'bg-chip-green'} key={i} ref={d=> d!= null?lineyes.current[i] = d:null}></RouteLine>
+                )
+                arrjun.push(
+                    <RouteLine className={'bg-chip-purple'} key={i} ref={d=> d!= null?linejun.current[i] = d:null}></RouteLine>
+                )
+            }
+            setDirLine(arrdir)
+            setCycLine(arrcyc)
+            setYesLine(arryes)
+            setJunLine(arrjun)
+        },[])
path: src/app/components/routemap/RouteMap.tsx
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Avoid repeated 'key={i}' in line arrays.**  
Use distinct key values for each line element to help React’s reconciliation remain stable.  

```diff
- <RouteLine key={i} ... />
+ <RouteLine key={`dir-${i}`} ... />
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
        const directLineInput = useCallback(() => {
            const arrdir = []
            const arrcyc = []
            const arryes = []
            const arrjun = []
            for(let i = 0; i < 4; i++){
                if(i === 3){
                    arrdir.push(
                        <RouteLine className={'bg-chip-orange'} key={`dir-${i}`} ref={d=> d!= null?linedir.current[i] = d:null}></RouteLine>
                    )
                    continue
                }
                arrdir.push(
                    <RouteLine className={'bg-chip-blue'} key={`dir-${i}`} ref={d=> d!= null?linedir.current[i] = d:null}></RouteLine>
                )
            }
            for(let i = 0; i < 5; i++){
                if(i === 4){
                    arrcyc.push(
                        <RouteLine className={'bg-chip-orange'} key={`cyc-${i}`} ref={d=> d!= null?linecyc.current[i] = d:null}></RouteLine>
                    )
                    arryes.push(
                        <RouteLine className={'bg-chip-orange'} key={`yes-${i}`} ref={d=> d!= null?lineyes.current[i] = d:null}></RouteLine>
                    )
                    arrjun.push(
                        <RouteLine className={'bg-chip-orange'} key={`jun-${i}`} ref={d=> d!= null?linejun.current[i] = d:null}></RouteLine>
                    )
                    continue
                }
                arrcyc.push(
                    <RouteLine className={'bg-chip-red'} key={`cyc-${i}`} ref={d=> d!= null?linecyc.current[i] = d:null}></RouteLine>
                )
                arryes.push(
                    <RouteLine className={'bg-chip-green'} key={`yes-${i}`} ref={d=> d!= null?lineyes.current[i] = d:null}></RouteLine>
                )
                arrjun.push(
                    <RouteLine className={'bg-chip-purple'} key={`jun-${i}`} ref={d=> d!= null?linejun.current[i] = d:null}></RouteLine>
                )
            }
            setDirLine(arrdir)
            setCycLine(arrcyc)
            setYesLine(arryes)
            setJunLine(arrjun)
        },[])
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-09T06:48:31Z
author_association: NONE
diff_hunk: @@ -0,0 +1,361 @@
+import React, { useCallback, useEffect,useRef, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useSelector } from 'react-redux';
+import styled from 'styled-components';
+import tw from 'twin.macro';
+
+import { CircleAnimate } from '@/data';
+import { SingleShuttleSchedule } from "@/data";
+
+const Circle = styled.span`
+  ${tw`
+    flex rounded-full inline-block
+    h-3 w-3 rt1:h-2.5 rt1:w-2.5
+    z-10
+  `}
+`
+
+const CycleCircle = styled(Circle)`
+  ${tw`bg-chip-red mx-2`}
+`
+
+const DirectCircle = styled(Circle)`
+  ${tw`bg-chip-blue mx-2`}
+`
+
+const YesulinCircle = styled(Circle)`
+  ${tw`bg-chip-green mx-2`}
+`
+
+const JungangCircle = styled(Circle)`
+  ${tw`bg-chip-purple mx-2`}
+`
+const EndCircle = styled(Circle)`
+    ${tw`bg-chip-orange mx-2`}
+`
+const RouteRowsContainer = styled.div`
+${tw`grid grid-rows-5 gap-2`}
+`
+const RouteColsContainer = styled.div`
+${tw`grid grid-cols-6 place-items-center`}
+`
+const RouteTextContainer =  styled.div<{lang:string}>`
+${tw`whitespace-nowrap text-center`}
+${(props) => props.lang === 'ko' ? tw`text-[15px] hm:text-[13px]` : tw`text-[12px] rt1:text-[9px]`}
+`
+const RouteStations = styled.div`
+${tw`transition duration-150 ease-in-out flex relative`}
+`
+const RouteMethod = styled.div`
+${tw`text-center rounded-full py-1 w-16 hm:w-12 hm:text-xs self-center text-black`}
+`
+const MainContainer = styled.div<{status: string}>`
+${tw`transition duration-150 ease-in-out mx-auto`}
+    ${(props) => props.status === 'entered' || props.status === 'exit' ? tw`opacity-100` : tw`opacity-0`}
+`
+const RouteLine = styled.div`
+    ${tw`absolute transition duration-150 ease-in-out h-[3px] z-1`}
+
+`
+export const RouteMap = (props: {
+    status:string,
+    tab: string
+}) => {
+    interface RootState {
+        actions: SingleShuttleSchedule[]
+    }
+    const timetable = useSelector((state: RootState) => state.actions)
+    const { t, i18n } = useTranslation()
+    // dots
+    const [direct, setDirect] = useState<JSX.Element[]>([])
+    const [cycle, setCycle] = useState<JSX.Element[]>([])
+    const [yesulin, setYesulin] = useState<JSX.Element[]>([])
+    const [jungang, setJungang] = useState<JSX.Element[]>([])
+    // lines
+    const [dirLine, setDirLine] = useState<JSX.Element[]>([])
+    const [cycLine, setCycLine] = useState<JSX.Element[]>([])
+    const [yesLine, setYesLine] = useState<JSX.Element[]>([])
+    const [junLine, setJunLine] = useState<JSX.Element[]>([])
+    // dot refs
+    const refdir = useRef<Array<HTMLDivElement>>([])
+    const refcyc = useRef<Array<HTMLDivElement>>([])
+    const refyes = useRef<Array<HTMLDivElement>>([])
+    const refjun = useRef<Array<HTMLDivElement>>([])
+    // line refs
+    const linedir = useRef<Array<HTMLDivElement>>([])
+    const linecyc = useRef<Array<HTMLDivElement>>([])
+    const lineyes = useRef<Array<HTMLDivElement>>([])
+    const linejun = useRef<Array<HTMLDivElement>>([])
+    // langtest
+    const lang = useRef<Array<HTMLParagraphElement | null>>([])
+    const directInput = useCallback(() => {
+        const arrdir = []
+        const arrcyc = []
+        const arryes = []
+        const arrjun = []
+        for(let i = 0; i < 5; i++){
+            if(i === 4){
+                arrdir.push(<RouteStations key={i} ref={d => d!= null?refdir.current[i] = d:null}><EndCircle/></RouteStations>)
+                continue
+            }
+            arrdir.push(<RouteStations key={i} ref={d => d!= null?refdir.current[i] = d:null}><DirectCircle/></RouteStations>)
+        }
+        for(let i = 0; i < 6; i++){
+            if(i >= 2 && i <= 4){
+                if(i === 2){
+                    arrcyc.push(
+                        <div key={i} className='col-span-2 grid grid-cols-3 w-[75%] place-items-center'>
+                            <RouteStations key={i} ref={d => d!= null?refcyc.current[i] = d:null}><CycleCircle/></RouteStations>
+                            <RouteStations key={i+1} ref={d => d!= null?refcyc.current[i+1] = d:null}><CycleCircle className='grid grid-rows-2 relative'><p key={0} ref={d => lang.current[0] = d} className='absolute text-xs top-[-20px] left-[-14px] text-center w-10 text-chip-green'>{t('yesul')}</p></CycleCircle></RouteStations>
+                            <RouteStations key={i+2} ref={d => d!= null?refcyc.current[i+2] = d:null}><CycleCircle/></RouteStations>
+                        </div>
+                    )
+                    arryes.push(
+                        <div key={i} className='col-span-2 grid grid-cols-3 w-[75%] place-items-center'>
+                            <RouteStations key={i} ref={d => d!= null?refyes.current[i] = d:null} title='skip'><YesulinCircle className='opacity-0'/></RouteStations>
+                            <RouteStations key={i+1} ref={d => d!= null?refyes.current[i+1] = d:null}><YesulinCircle className='grid grid-rows-2 relative'><p key={1} ref={d => lang.current[1] = d} className='absolute text-xs top-[-20px] left-[-14px] text-center w-10 text-chip-green'>{t('yesul')}</p></YesulinCircle></RouteStations>
+                            <RouteStations key={i+2} ref={d => d!= null?refyes.current[i+2] = d:null}><YesulinCircle/></RouteStations>
+                        </div>
+                    )
+                    arrjun.push(
+                        <div key={i} className='col-span-2 grid grid-cols-3 w-[75%] place-items-center'>
+                            <RouteStations key={i} ref={d => d!= null?refjun.current[i] = d:null}><JungangCircle/></RouteStations>
+                            <RouteStations key={i+1} ref={d => d!= null?refjun.current[i+1] = d:null}><JungangCircle className='grid grid-rows-2 relative'><p key={2} ref={d => lang.current[2] = d} className='absolute text-xs top-[-20px] left-[-14px] text-center w-10 text-chip-purple'>{t('jung')}</p></JungangCircle></RouteStations>
+                            <RouteStations key={i+2} ref={d => d!= null?refjun.current[i+2] = d:null}><JungangCircle/></RouteStations>
+                        </div>
+                    )
+                }
+                continue
+            }
+            if(i === 5){
+                arrcyc.push(<RouteStations key={i} ref={d => d!= null?refcyc.current[i] = d:null}><EndCircle/></RouteStations>)
+                arryes.push(<RouteStations key={i} ref={d => d!= null?refyes.current[i] = d:null}><EndCircle/></RouteStations>)
+                arrjun.push(<RouteStations key={i} ref={d => d!= null?refjun.current[i] = d:null}><EndCircle/></RouteStations>)
+                continue
+            }
+            arrcyc.push(<RouteStations key={i} ref={d => d!= null?refcyc.current[i] = d:null}><CycleCircle/></RouteStations>)
+            arryes.push(<RouteStations key={i} ref={d => d!= null?refyes.current[i] = d:null}><YesulinCircle/></RouteStations>)
+            arrjun.push(<RouteStations key={i} ref={d => d!= null?refjun.current[i] = d:null}><JungangCircle/></RouteStations>)
+        }
+        setDirect(arrdir)
+        setCycle(arrcyc)
+        setYesulin(arryes)
+        setJungang(arrjun)
+    },[t])
path: src/app/components/routemap/RouteMap.tsx
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Ensure unique keys in nested map.**  
In `directInput`, the same index (`i`) is reused among multiple elements. React requires unique keys to track each element.  

```diff
- <RouteStations key={i}>... </RouteStations>
- <RouteStations key={i+1}>... </RouteStations>
- <RouteStations key={i+2}>... </RouteStations>
+ <RouteStations key={`${i}-0`}>... </RouteStations>
+ <RouteStations key={`${i}-1`}>... </RouteStations>
+ <RouteStations key={`${i}-2`}>... </RouteStations>
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    const lang = useRef<Array<HTMLParagraphElement | null>>([])
    const directInput = useCallback(() => {
        const arrdir = []
        const arrcyc = []
        const arryes = []
        const arrjun = []
        for(let i = 0; i < 5; i++){
            if(i === 4){
                arrdir.push(<RouteStations key={i} ref={d => d != null ? refdir.current[i] = d : null}><EndCircle/></RouteStations>)
                continue
            }
            arrdir.push(<RouteStations key={i} ref={d => d != null ? refdir.current[i] = d : null}><DirectCircle/></RouteStations>)
        }
        for(let i = 0; i < 6; i++){
            if(i >= 2 && i <= 4){
                if(i === 2){
                    arrcyc.push(
                        <div key={i} className='col-span-2 grid grid-cols-3 w-[75%] place-items-center'>
                            <RouteStations key={`${i}-0`} ref={d => d != null ? refcyc.current[i] = d : null}><CycleCircle/></RouteStations>
                            <RouteStations key={`${i}-1`} ref={d => d != null ? refcyc.current[i+1] = d : null}>
                                <CycleCircle className='grid grid-rows-2 relative'>
                                    <p key={0} ref={d => lang.current[0] = d} className='absolute text-xs top-[-20px] left-[-14px] text-center w-10 text-chip-green'>
                                        {t('yesul')}
                                    </p>
                                </CycleCircle>
                            </RouteStations>
                            <RouteStations key={`${i}-2`} ref={d => d != null ? refcyc.current[i+2] = d : null}><CycleCircle/></RouteStations>
                        </div>
                    )
                    arryes.push(
                        <div key={i} className='col-span-2 grid grid-cols-3 w-[75%] place-items-center'>
                            <RouteStations key={`${i}-0`} ref={d => d != null ? refyes.current[i] = d : null} title='skip'>
                                <YesulinCircle className='opacity-0'/>
                            </RouteStations>
                            <RouteStations key={`${i}-1`} ref={d => d != null ? refyes.current[i+1] = d : null}>
                                <YesulinCircle className='grid grid-rows-2 relative'>
                                    <p key={1} ref={d => lang.current[1] = d} className='absolute text-xs top-[-20px] left-[-14px] text-center w-10 text-chip-green'>
                                        {t('yesul')}
                                    </p>
                                </YesulinCircle>
                            </RouteStations>
                            <RouteStations key={`${i}-2`} ref={d => d != null ? refyes.current[i+2] = d : null}><YesulinCircle/></RouteStations>
                        </div>
                    )
                    arrjun.push(
                        <div key={i} className='col-span-2 grid grid-cols-3 w-[75%] place-items-center'>
                            <RouteStations key={`${i}-0`} ref={d => d != null ? refjun.current[i] = d : null}><JungangCircle/></RouteStations>
                            <RouteStations key={`${i}-1`} ref={d => d != null ? refjun.current[i+1] = d : null}>
                                <JungangCircle className='grid grid-rows-2 relative'>
                                    <p key={2} ref={d => lang.current[2] = d} className='absolute text-xs top-[-20px] left-[-14px] text-center w-10 text-chip-purple'>
                                        {t('jung')}
                                    </p>
                                </JungangCircle>
                            </RouteStations>
                            <RouteStations key={`${i}-2`} ref={d => d != null ? refjun.current[i+2] = d : null}><JungangCircle/></RouteStations>
                        </div>
                    )
                }
                continue
            }
            if(i === 5){
                arrcyc.push(<RouteStations key={i} ref={d => d != null ? refcyc.current[i] = d : null}><EndCircle/></RouteStations>)
                arryes.push(<RouteStations key={i} ref={d => d != null ? refyes.current[i] = d : null}><EndCircle/></RouteStations>)
                arrjun.push(<RouteStations key={i} ref={d => d != null ? refjun.current[i] = d : null}><EndCircle/></RouteStations>)
                continue
            }
            arrcyc.push(<RouteStations key={i} ref={d => d != null ? refcyc.current[i] = d : null}><CycleCircle/></RouteStations>)
            arryes.push(<RouteStations key={i} ref={d => d != null ? refyes.current[i] = d : null}><YesulinCircle/></RouteStations>)
            arrjun.push(<RouteStations key={i} ref={d => d != null ? refjun.current[i] = d : null}><JungangCircle/></RouteStations>)
        }
        setDirect(arrdir)
        setCycle(arrcyc)
        setYesulin(arryes)
        setJungang(arrjun)
    }, [t])
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-09T06:48:31Z
author_association: NONE

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-03-08T18:49:05Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: review in progress by coderabbit.ai -->

> [!NOTE]
> Currently processing new changes in this PR. This may take a few minutes, please wait...
> 
> <details>
> <summary>📥 Commits</summary>
> 
> Reviewing files that changed from the base of the PR and between d9fca8b66fe748a0f99dbb7a900af5073265e72c and 28280f74862e7cfdaa2c44493bad38e3bb1435c8.
> 
> </details>
> 
> <details>
> <summary>⛔ Files ignored due to path filters (13)</summary>
> 
> * `.yarn/cache/@babel-runtime-npm-7.26.0-9afa3c4ef6-9f4ea1c1d5.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
> * `.yarn/cache/@reduxjs-toolkit-npm-2.5.0-9bdc99574d-b24ee7e89b.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
> * `.yarn/cache/@types-react-transition-group-npm-4.4.11-dda183055d-a7f4de6e5f.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
> * `.yarn/cache/@types-use-sync-external-store-npm-0.0.6-9e5c635381-a95ce33066.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
> * `.yarn/cache/dom-helpers-npm-5.2.1-b38bb4470b-bed2341adf.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
> * `.yarn/cache/immer-npm-10.1.1-973ae10d09-9dacf1e8c2.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
> * `.yarn/cache/react-redux-npm-9.2.0-d87bb27c82-b3d2f89f46.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
> * `.yarn/cache/react-transition-group-npm-4.4.5-98ea4ef96e-ca32d3fd21.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
> * `.yarn/cache/redux-npm-5.0.1-f8e6b1cb23-a373f9ed65.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
> * `.yarn/cache/redux-thunk-npm-3.1.0-6a8fdd3211-38c563db5f.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
> * `.yarn/cache/reselect-npm-5.1.1-667568f51c-1fdae11a39.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
> * `.yarn/cache/use-sync-external-store-npm-1.4.0-176448bea1-08bf581a8a.zip` is excluded by `!**/.yarn/**`, `!**/*.zip`
> * `yarn.lock` is excluded by `!**/yarn.lock`, `!**/*.lock`
> 
> </details>
> 
> <details>
> <summary>📒 Files selected for processing (16)</summary>
> 
> * `.pnp.cjs` (15 hunks)
> * `package.json` (3 hunks)
> * `src/App.tsx` (4 hunks)
> * `src/app/components/fulltime/FullTime.tsx` (7 hunks)
> * `src/app/components/index.ts` (1 hunks)
> * `src/app/components/lang/lang.en.json` (1 hunks)
> * `src/app/components/lang/lang.ko.json` (1 hunks)
> * `src/app/components/routemap/RouteMap.tsx` (1 hunks)
> * `src/app/components/shuttle/Shuttle.tsx` (4 hunks)
> * `src/app/data/index.ts` (1 hunks)
> * `src/app/data/routemap/circleanimate.ts` (1 hunks)
> * `src/app/data/shuttle/organizedTimetables.ts` (1 hunks)
> * `src/app/network/shuttle.ts` (1 hunks)
> * `src/app/reducer/store.ts` (1 hunks)
> * `src/main.tsx` (2 hunks)
> * `tailwind.config.js` (2 hunks)
> 
> </details>
> 
> ```ascii
>  ______________________________________________________________________________________________
> < Rabbits with frickin' laser beams attached to their heads. And they're pointed at your bugs. >
>  ----------------------------------------------------------------------------------------------
>   \
>    \   (\__/)
>        (•ㅅ•)
>        / 　 づ
> ```

<!-- end of auto-generated comment: review in progress by coderabbit.ai -->
<!-- usage_tips_start -->

> [!TIP]
> <details>
> <summary>You can validate your CodeRabbit configuration file in your editor.</summary>
> 
> If your editor has YAML language server, you can enable auto-completion and validation by adding `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json` at the top of your CodeRabbit configuration file.
> 
> </details>

<!-- usage_tips_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=BusHanyang/hybus-genesis&utm_content=97):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: cloudflare-workers-and-pages[bot]
created_at: 2025-03-08T18:55:35Z
body: ## Deploying hybus-genesis with &nbsp;<a href="https://pages.dev"><img alt="Cloudflare Pages" src="https://user-images.githubusercontent.com/23264/106598434-9e719e00-654f-11eb-9e59-6167043cfa01.png" width="16"></a> &nbsp;Cloudflare Pages

<table><tr><td><strong>Latest commit:</strong> </td><td>
<code>28280f7</code>
</td></tr>
<tr><td><strong>Status:</strong></td><td>&nbsp;✅&nbsp; Deploy successful!</td></tr>
<tr><td><strong>Preview URL:</strong></td><td>
<a href='https://9a4a27ba.hybus-genesis.pages.dev'>https://9a4a27ba.hybus-genesis.pages.dev</a>
</td></tr>
<tr><td><strong>Branch Preview URL:</strong></td><td>
<a href='https://dev-route-map-x150.hybus-genesis.pages.dev'>https://dev-route-map-x150.hybus-genesis.pages.dev</a>
</td></tr>
</table>

[View logs](https://dash.cloudflare.com/25e5a82c44ef34fa034e2dee672bd2d6/pages/view/hybus-genesis/9a4a27ba-0b9a-44b6-991b-92fdb429b952)

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-09T06:44:08Z
body: Check out your Lighthouse Report: https://lighthouse.hybus.app/app/projects/bushanyang-production/dashboard
<!-- thollander/actions-comment-pull-request "lighthouse" -->
author_association: NONE