Skip to content

Feature/side bar&chart#24

Merged
JAEJUN090210 merged 4 commits intomainfrom
feature/sideBar&chart
Mar 11, 2026
Merged

Feature/side bar&chart#24
JAEJUN090210 merged 4 commits intomainfrom
feature/sideBar&chart

Conversation

@JAEJUN090210
Copy link
Copy Markdown
Member

@JAEJUN090210 JAEJUN090210 commented Mar 7, 2026

Summary by CodeRabbit

  • New Features
    • Added device detail page with interactive charts (auto-scrolls into view)
    • Added persistent sidebar navigation with section management and add-section control
    • Cards now open a detail view on click and support inline device name editing
    • Added bulk delete mode with per-item selection controls
    • Sidebar header/logo and improved two-column layout for main content

Introduces a comprehensive sidebar with a header, navigation items, and an application logo.
Integrates the sidebar into the Home page, updating the overall layout to accommodate it.
Includes styled components for consistent theming and logic for managing active navigation states.
- Implement a new Detail page featuring a line chart using Chart.js to visualize device data.
- Refactor the sidebar to support dynamic section addition and improved active state tracking.
- Update the device card component to navigate to the detail view upon selection.
- Install chart.js and react-chartjs-2 dependencies.
@JAEJUN090210 JAEJUN090210 requested a review from yunho09 March 7, 2026 11:26
@JAEJUN090210 JAEJUN090210 self-assigned this Mar 7, 2026
@JAEJUN090210 JAEJUN090210 added the enhancement New feature or request label Mar 7, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 7, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Adds Chart.js deps, a Sidebar navigation system with dynamic sections, a Detail page rendering device charts, routes to support detail views, and DeviceCardItem updates for navigation, in-place renaming, and delete/select interactions.

Changes

Cohort / File(s) Summary
Dependencies
package.json
Adds chart.js (^4.5.1) and react-chartjs-2 (^5.3.1) runtime dependencies for chart rendering.
App Routing
src/App.tsx
Imports Detail and restructures routes: Home moved to /section-1, adds /detail/:id, and a catch-all route.
Device Card
src/components/DeviceCardItem.tsx
Implements navigation to detail on card click, inline name editing with auto-focus and Enter-to-save, stopPropagation for delete/menu controls, and always renders DeviceDetail with explicit props.
Sidebar Core
src/components/sidebar/sidebar.tsx, src/components/sidebar/sidebar-style.ts, src/components/sidebar/index.ts
New Sidebar component and styles: manages navItems state (initial Section 1), path-based navigation, dynamic "add section" behavior, active-item highlighting, and a barrel export.
Sidebar Header & Item
src/components/sidebar/header/sidebar-header.tsx, src/components/sidebar/header/sidebar-header-style.ts, src/components/sidebar/item/sidebar-item.tsx, src/components/sidebar/item/sidebar-item-style.ts
Adds a SidebarHeader with centered logo and styled components for sidebar items; introduces memoized SidebarItem component with active state and accessible button labeling.
Pages
src/pages/detail.tsx, src/pages/home/home.tsx
Adds Detail page rendering a responsive Chart.js line chart (uses route param id) and refactors Home to include persistent Sidebar, MainContent wrapper, and expanded DeviceCardItem props to support delete/select flows.

Sequence Diagram

sequenceDiagram
    actor User
    participant Router as App Router
    participant Sidebar
    participant Home
    participant Card as DeviceCardItem
    participant Detail
    participant Chart

    User->>Router: Open app
    Router->>Home: Render /section-1
    Home->>Sidebar: Render Sidebar (nav items)
    Sidebar->>Router: Navigate on item click

    User->>Card: Click device card
    Card->>Router: navigate("/detail/:id")
    Router->>Detail: Render Detail page with id
    Detail->>Chart: Register Chart.js & render chart
    Chart-->>Detail: Chart drawn
    Detail-->>User: Show device chart

    User->>Card: Click edit (menu)
    Card->>Card: Show edit input (auto-focus)
    User->>Card: Type name + Enter
    Card->>Card: updateDeviceName(id, newName)
    Card-->>User: Name updated
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through code with nimble paws,
I planted charts and built new laws.
A sidebar path, a detail view—
Devices gossip, data true.
Hooray, I nibble bugs anew! 🎨📊

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Feature/side bar&chart' partially references real changes in the PR (sidebar and chart components), but lacks specificity and uses informal phrasing with ampersand notation instead of descriptive language about what was actually implemented. Revise the title to be more descriptive and specific, such as 'Add sidebar navigation and device detail chart view' to clearly communicate the main changes to reviewers.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/sideBar&chart

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Explicitly import and apply the ChartOptions type to the chart configuration object to ensure better type checking and IDE support.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/DeviceCardItem.tsx (1)

43-50: ⚠️ Potential issue | 🟡 Minor

Persist the normalized name, not the raw input.

Line 46 validates newName.trim(), but Line 50 saves the untrimmed value. That still allows whitespace-only names or accidental leading/trailing spaces through to updateDeviceName.

🧹 Suggested fix
   const enterHandle = (e: React.KeyboardEvent<HTMLInputElement>) => {
     if (e.key === "Enter") {
-      const newName = (e.target as HTMLInputElement).value;
-      if (newName.trim().length > 8) {
+      const newName = e.currentTarget.value.trim();
+      if (newName.length === 0 || newName.length > 8) {
         alert("별명은 최대 8자까지 가능합니다.");
         return;
       }
       updateDeviceName(id, newName);
       setIsEditing(false);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/DeviceCardItem.tsx` around lines 43 - 50, The handler
enterHandle currently validates newName.trim() but calls updateDeviceName with
the raw newName, allowing leading/trailing or whitespace-only names to be
persisted; change the implementation to derive a trimmedName = newName.trim(),
validate trimmedName (check non-empty and <= 8 chars) and then call
updateDeviceName(id, trimmedName) (and use trimmedName for any other logic) so
only the normalized name is saved.
🧹 Nitpick comments (5)
src/components/sidebar/header/sidebar-header.tsx (1)

2-24: Reuse the shared header style instead of redefining it here.

Lines 13-24 duplicate SideBarHeader even though src/components/sidebar/header/sidebar-header-style.ts already exports one. The two definitions already diverge, so changes to the shared style file will not affect the rendered header.

♻️ Suggested cleanup
 import { memo } from "react";
-import styled from "@emotion/styled";
 import LogoIcon from "../../../assets/logo.svg";
+import { SideBarHeader } from "./sidebar-header-style";
@@
 function SidebarHeaderComponent() {
   return (
     <SideBarHeader>
       <img src={LogoIcon} alt="logo" />
     </SideBarHeader>
   );
 }
-
-const SideBarHeader = styled.header`
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  align-self: stretch;
-  width: 100%;
-  background-color: `#222222`;
-  padding-top: 95px;
-  padding-bottom: 40px;
-  box-sizing: border-box;
-  cursor: default;
-`;
 
 export const SidebarHeader = memo(SidebarHeaderComponent);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/sidebar/header/sidebar-header.tsx` around lines 2 - 24, The
local styled component SideBarHeader duplicates the shared header style; remove
the local SideBarHeader declaration and instead import the shared styled header
exported by the header style module, then update SidebarHeaderComponent to use
that imported styled component (ensure the import name matches the exported
symbol from the shared style module and remove the duplicate styled definition).
src/components/sidebar/sidebar.tsx (2)

21-28: Unused props in interface.

onSearch, onTeamCreated, and onTeamUpdated are defined in SidebarProps but never used. If these are planned for future use, consider removing them until needed to keep the interface clean.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/sidebar/sidebar.tsx` around lines 21 - 28, The SidebarProps
interface currently declares onSearch, onTeamCreated, and onTeamUpdated but
Sidebar only uses onNavItemClick; remove the unused props from the SidebarProps
declaration (or alternatively add them to Sidebar’s parameter destructuring and
use them if they are intentionally required) so the interface matches actual
usage—update SidebarProps to only include onNavItemClick (and any props actually
consumed) and adjust the Sidebar function signature accordingly to keep types
accurate and eliminate unused prop declarations.

58-66: Consider using a stable ID generator.

Using prev.length + 1 for the section index works now but could produce duplicate IDs if section deletion is added later. A simple counter ref or Date.now() would be more robust.

💡 Alternative approach using a counter
+import { useCallback, useState, useRef } from "react";
-import { useCallback, useState } from "react";

 function Sidebar({ onNavItemClick = () => {} }: SidebarProps) {
   const navigate = useNavigate();
   const location = useLocation();
+  const sectionCounter = useRef(1);

   const [navItems, setNavItems] = useState<SidebarNavItem[]>([
     { id: "section-1", label: "Section 1", path: "/section-1" },
   ]);

   // ...

   const handleAddItem = useCallback(() => {
     setNavItems((prev) => {
-      const nextIndex = prev.length + 1;
+      sectionCounter.current += 1;
+      const nextIndex = sectionCounter.current;
       const newId = `section-${nextIndex}`;
       const newLabel = `Section ${nextIndex}`;
       const newPath = `/${newId}`;
       return [...prev, { id: newId, label: newLabel, path: newPath }];
     });
   }, []);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/sidebar/sidebar.tsx` around lines 58 - 66, The handleAddItem
callback currently builds new IDs from prev.length which can collide if items
are removed; change it to use a stable unique ID source (e.g., a useRef counter
or timestamp/random suffix) instead of prev.length: create a
module/component-level ref like nextIdRef = useRef(1) and on each add use and
then increment nextIdRef.current (or use Date.now()+Math.random() / a UUID lib)
to synthesize newId (and derive newLabel/newPath from that stable value), then
push the new item via setNavItems as before in handleAddItem.
src/pages/home/home.tsx (1)

62-73: Repeated .find() calls can be optimized.

warningDevices.find((device) => device.warning) is called three times within the same render. Extract it to a variable for clarity and minor performance improvement.

♻️ Proposed refactor
+      {(() => {
+        const warningDevice = warningDevices.find((device) => device.warning);
+        return warningDevice ? (
+          <WarningModal
+            deviceName={warningDevice.name}
+            deviceTemp={warningDevice.temperature}
+            checkWarning={() => checkWarning(warningDevice.id)}
+          />
+        ) : null;
+      })()}
-      {warningDevices.some((device) => device.warning) && (
-        <WarningModal
-          deviceName={warningDevices.find((device) => device.warning)?.name}
-          deviceTemp={
-            warningDevices.find((device) => device.warning)?.temperature
-          }
-          checkWarning={() =>
-            checkWarning(
-              warningDevices.find((device) => device.warning)?.id || "",
-            )
-          }
-        />
-      )}

Or extract before JSX:

const activeWarningDevice = warningDevices.find((device) => device.warning);

// In JSX:
{activeWarningDevice && (
  <WarningModal
    deviceName={activeWarningDevice.name}
    deviceTemp={activeWarningDevice.temperature}
    checkWarning={() => checkWarning(activeWarningDevice.id)}
  />
)}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/home/home.tsx` around lines 62 - 73, Extract the repeated
warningDevices.find((device) => device.warning) call into a local variable
(e.g., activeWarningDevice) before the JSX render, then use that variable in the
conditional and props passed to WarningModal (deviceName, deviceTemp, and the
checkWarning callback that calls checkWarning(activeWarningDevice.id)). Update
the JSX conditional from warningDevices.some(...) to use the truthiness of
activeWarningDevice to avoid multiple finds.
src/pages/detail.tsx (1)

118-122: Auto-scroll won't trigger when chart data updates.

The useEffect runs only on mount due to the empty dependency array. If data changes dynamically in the future, the scroll position won't update. Consider adding data.labels.length or chartWidth as a dependency if the data becomes dynamic.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/detail.tsx` around lines 118 - 122, The current useEffect that
auto-scrolls the chart only runs on mount because it has an empty dependency
array; update it to run when chart data or dimensions change by adding the
relevant dependencies (e.g., data.labels.length and/or chartWidth) so it
re-executes on updates, and keep the existing guard that checks
chartBodyRef.current before setting scrollLeft; modify the dependency array in
the useEffect containing chartBodyRef.scroll logic (the useEffect that
references chartBodyRef) to include those dynamic values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/DeviceCardItem.tsx`:
- Around line 63-68: The card's onClick handler (CardContainer ->
navigate(`/detail/${id}`) when !isDeleteMode) is causing clicks inside the
rename input to navigate away; modify the rename input element (the interactive
input rendered in DeviceCardItem around lines 97-103) to prevent bubbling by
adding event handlers that stop propagation (e.g., onMouseDown and onClick call
event.stopPropagation()), or alternatively update the CardContainer click logic
to also check a renaming flag (isRenaming) before navigating; reference
CardContainer, navigate, id, isDeleteMode and the rename input element when
making the change.

In `@src/components/sidebar/item/sidebar-item-style.ts`:
- Around line 15-22: The SideBarMenuItemProps defines an isActive prop but
SideBarMenuItem (styled.div) doesn't use it; either remove isActive from the
SideBarMenuItemProps interface or update the SideBarMenuItem styled component to
consume isActive and apply conditional styles (e.g., different background,
color, or font-weight) based on props; locate SideBarMenuItem and
SideBarMenuItemProps in sidebar-item-style.ts and either delete isActive from
the props interface or reference props.isActive inside the styled template
(using the existing ITEM_PADDING constant) to adjust styling when active.

In `@src/components/sidebar/sidebar.tsx`:
- Around line 74-79: The plus icon image is not keyboard accessible because
<img> with onClick cannot receive focus; wrap the Plus image in a semantic,
focusable element (e.g., a <button>) or replace the <img> with a styled <button>
that contains the image, add proper aria-label (e.g., aria-label="Add item"),
preserve the onClick handler handleAddItem and any styling
(width/height/cursor), and ensure keyboard activation (Enter/Space) works and
the button has type="button" to avoid form submission.

In `@src/pages/detail.tsx`:
- Around line 52-77: The Chart.js v4 option drawBorder on GridLineOptions must
be removed; update the chart options in the scales object (the x and y config in
this file) by removing grid.drawBorder and, if you intend to hide the axis
border, add/adjust scales.x.border and/or scales.y.border properties instead
(e.g. set border.display = false or configure border.color/width) so the code
around scales.x.grid and scales.y.grid no longer references drawBorder.

In `@src/pages/home/home.tsx`:
- Around line 101-107: The comment above the MainContent styled component is
inaccurate: it claims the sidebar width is 280px while the code uses
margin-left: 432px; update the comment to state the actual sidebar width (432px)
or otherwise make the comment consistent with the MainContent definition
(referencing MainContent and its margin-left value) so the comment accurately
describes why the margin exists.

---

Outside diff comments:
In `@src/components/DeviceCardItem.tsx`:
- Around line 43-50: The handler enterHandle currently validates newName.trim()
but calls updateDeviceName with the raw newName, allowing leading/trailing or
whitespace-only names to be persisted; change the implementation to derive a
trimmedName = newName.trim(), validate trimmedName (check non-empty and <= 8
chars) and then call updateDeviceName(id, trimmedName) (and use trimmedName for
any other logic) so only the normalized name is saved.

---

Nitpick comments:
In `@src/components/sidebar/header/sidebar-header.tsx`:
- Around line 2-24: The local styled component SideBarHeader duplicates the
shared header style; remove the local SideBarHeader declaration and instead
import the shared styled header exported by the header style module, then update
SidebarHeaderComponent to use that imported styled component (ensure the import
name matches the exported symbol from the shared style module and remove the
duplicate styled definition).

In `@src/components/sidebar/sidebar.tsx`:
- Around line 21-28: The SidebarProps interface currently declares onSearch,
onTeamCreated, and onTeamUpdated but Sidebar only uses onNavItemClick; remove
the unused props from the SidebarProps declaration (or alternatively add them to
Sidebar’s parameter destructuring and use them if they are intentionally
required) so the interface matches actual usage—update SidebarProps to only
include onNavItemClick (and any props actually consumed) and adjust the Sidebar
function signature accordingly to keep types accurate and eliminate unused prop
declarations.
- Around line 58-66: The handleAddItem callback currently builds new IDs from
prev.length which can collide if items are removed; change it to use a stable
unique ID source (e.g., a useRef counter or timestamp/random suffix) instead of
prev.length: create a module/component-level ref like nextIdRef = useRef(1) and
on each add use and then increment nextIdRef.current (or use
Date.now()+Math.random() / a UUID lib) to synthesize newId (and derive
newLabel/newPath from that stable value), then push the new item via setNavItems
as before in handleAddItem.

In `@src/pages/detail.tsx`:
- Around line 118-122: The current useEffect that auto-scrolls the chart only
runs on mount because it has an empty dependency array; update it to run when
chart data or dimensions change by adding the relevant dependencies (e.g.,
data.labels.length and/or chartWidth) so it re-executes on updates, and keep the
existing guard that checks chartBodyRef.current before setting scrollLeft;
modify the dependency array in the useEffect containing chartBodyRef.scroll
logic (the useEffect that references chartBodyRef) to include those dynamic
values.

In `@src/pages/home/home.tsx`:
- Around line 62-73: Extract the repeated warningDevices.find((device) =>
device.warning) call into a local variable (e.g., activeWarningDevice) before
the JSX render, then use that variable in the conditional and props passed to
WarningModal (deviceName, deviceTemp, and the checkWarning callback that calls
checkWarning(activeWarningDevice.id)). Update the JSX conditional from
warningDevices.some(...) to use the truthiness of activeWarningDevice to avoid
multiple finds.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8b47fb80-e503-4bc0-b386-7dbce026a191

📥 Commits

Reviewing files that changed from the base of the PR and between b60acb6 and 1dd3129.

⛔ Files ignored due to path filters (2)
  • bun.lock is excluded by !**/*.lock
  • src/assets/logo.svg is excluded by !**/*.svg
📒 Files selected for processing (12)
  • package.json
  • src/App.tsx
  • src/components/DeviceCardItem.tsx
  • src/components/sidebar/header/sidebar-header-style.ts
  • src/components/sidebar/header/sidebar-header.tsx
  • src/components/sidebar/index.ts
  • src/components/sidebar/item/sidebar-item-style.ts
  • src/components/sidebar/item/sidebar-item.tsx
  • src/components/sidebar/sidebar-style.ts
  • src/components/sidebar/sidebar.tsx
  • src/pages/detail.tsx
  • src/pages/home/home.tsx

Clean up the chart configuration by removing the `drawBorder` property from the axis grid settings, as it is no longer a valid property in the current version of the chart library.
@JAEJUN090210 JAEJUN090210 merged commit 586d76e into main Mar 11, 2026
2 of 3 checks passed
@JAEJUN090210 JAEJUN090210 deleted the feature/sideBar&chart branch March 11, 2026 09:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant