Skip to content

Conversation

@changuii
Copy link
Contributor

@changuii changuii commented Dec 10, 2025

DEV -> PROD 반영

Summary by CodeRabbit

  • New Features

    • Global mobile detection and responsive sidebar with a mobile menu toggle and auto-close after navigation.
    • New place action modal for mobile with image and action buttons (manage image, edit coordinates, edit, delete).
    • Modals: Escape-to-close, optional backdrop-click closing, and mobile-optimized layouts.
  • Behavior

    • Desktop keeps hover/action UI; mobile switches to tap-to-open workflows, scroll/resize adjustments for maps and lists.
  • Documentation

    • Added issue and pull-request templates.

✏️ Tip: You can customize this high-level summary in your review settings.

@changuii changuii self-assigned this Dec 10, 2025
@changuii changuii added the Feat label Dec 10, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 10, 2025

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Adds a site-wide mobile detection hook and makes UI mobile-aware: sidebar auto-collapses on small screens with a fixed open button, modals gain ESC/backdrop/mobile-layout options, new PlaceActionModal fetches coordinates, and PlacePage/cards open the action modal on mobile taps.

Changes

Cohort / File(s) Summary
Mobile detection hook
src/components/layout/UseIsMobile.jsx
New hook tracking (max-width: 768px) media query, updates on change, returns boolean isMobile.
App mobile integration
src/App.jsx
Use useIsMobile; initialize sidebarOpen from isMobile; container made relative; show fixed mobile "메뉴 열기" toggle when sidebar closed on mobile.
Sidebar responsive updates
src/components/layout/Sidebar.jsx
Use useIsMobile; compute `isExpanded = open
Modal behavior & mobile layout
src/components/common/Modal.jsx
Add props closeOnBackdropClick (default false) and enableMobileLayout (default false); add Escape-key close handler; backdrop clicks conditional on closeOnBackdropClick; apply mobile-specific layout when enabled and device is mobile.
New place actions modal
src/components/modals/PlaceActionModal.jsx
New default-export component rendering place image and action buttons; loads/fetches coordinates from /places/geographies if missing; supports ESC/backdrop close; invokes callbacks then calls onClose.
PlacePage & card mobile flow
src/pages/PlacePage.jsx
Use useIsMobile; add onMobileClick prop to MainPlaceCard/OtherPlaceCard; add mobileActionModalOpen, selectedPlaceForMobile, handleMobileCardClick; open PlaceActionModal on mobile taps and hide hover action UI on mobile.
Geography page mobile adjustments
src/pages/PlaceGeographyPage.jsx
Use useIsMobile; add mobile-specific containers/scroll-locking, map/list rearrangement, map resize effect on layout change; pass enableMobileLayout={true} to coordinate Modal.
Place edit modals mobile tweaks
src/components/modals/...
src/components/modals/OtherPlaceEditModal.jsx, src/components/modals/PlaceEditModal.jsx, src/components/modals/PlaceImagesModal.jsx
Import and use useIsMobile; pass enableMobileLayout to Modal; adjust padding, header sizes, button-group layout and wrappers for mobile scrolling/spacing while preserving behavior.
Repository templates
.github/ISSUE_TEMPLATE/issue_template.md, .github/PULL_REQUEST_TEMPLATE.md
Add issue and PR templates (Korean headings/fields).

Sequence Diagram(s)

sequenceDiagram
  participant User as Mobile User
  participant Card as PlaceCard (Main/Other)
  participant Page as PlacePage
  participant Modal as PlaceActionModal
  participant API as /places/geographies

  User->>Card: tap card (mobile)
  Card->>Page: onMobileClick(place)
  Page->>Modal: open(place, isOpen=true)
  alt coordinates missing
    Modal->>API: GET /places/geographies?placeId=...
    API-->>Modal: coordinates (or error)
  end
  Modal->>User: render image + action buttons
  User->>Modal: click action (e.g., "좌표 수정")
  Modal->>Page: invoke callback (onCoordinateEdit/onEdit/onDelete/onImageManage)
  Modal->>Page: call onClose (close modal)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • DEV -> PROD #13 — Modifies the same mobile/hooks/sidebar/modal/place-page code paths (useIsMobile, Sidebar/App mobile behavior, Modal signature changes, PlaceActionModal and PlacePage mobile wiring).

Poem

🐇 I hopped across the responsive plain,
Menus folded small, then opened again,
A modal fetched a mapline bright,
Buttons to tap by soft moonlight,
Tap, close — the layout feels just right.

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'DEV -> PROD' is generic and vague, failing to describe the actual technical changes being merged (mobile responsiveness, new hooks, modal updates, etc.). Replace with a descriptive title summarizing the main feature, e.g., 'Add mobile responsiveness and responsive UI components' or similar.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f38bccc and 50830fa.

📒 Files selected for processing (2)
  • .github/ISSUE_TEMPLATE/issue_template.md
  • .github/PULL_REQUEST_TEMPLATE.md

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.

Copy link

@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: 0

🧹 Nitpick comments (4)
src/components/layout/UseIsMobile.jsx (1)

3-15: SSR safety concern: Direct window access during initial render.

The hook accesses window.matchMedia synchronously during state initialization (line 4), which will throw a ReferenceError if this code ever runs in a server-side rendering context or during tests without a DOM. Consider initializing to a safe default and syncing in a useEffect:

 export function useIsMobile() {
-  const [isMobile, setIsMobile] = useState(window.matchMedia("(max-width: 768px)").matches);
+  const [isMobile, setIsMobile] = useState(false);

   useEffect(() => {
     const mediaQuery = window.matchMedia("(max-width: 768px)");
+    setIsMobile(mediaQuery.matches);

     const handler = (e) => setIsMobile(e.matches);
     mediaQuery.addEventListener("change", handler);

     return () => mediaQuery.removeEventListener("change", handler);
   }, []);

   return isMobile;
 }

Also, the file naming convention for hooks is typically lowercase (e.g., useIsMobile.js). Consider renaming for consistency with React conventions.

src/App.jsx (2)

53-58: Potential UI flash on mobile due to initial state mismatch.

The sidebarOpen state initializes to true (line 53), then the useEffect sets it to false on mobile (line 57). This causes a brief flash where the sidebar appears open before closing on mobile devices.

Consider initializing sidebarOpen based on viewport width directly, or use a loading state to prevent the flash:

-    const [sidebarOpen, setSidebarOpen] = useState(true);
+    const [sidebarOpen, setSidebarOpen] = useState(() => 
+        typeof window !== 'undefined' ? !window.matchMedia("(max-width: 768px)").matches : true
+    );
     const isMobile = useIsMobile();

-    useEffect(() => {
-        setSidebarOpen(!isMobile); // 모바일에서는 기본 닫힘
-    }, [isMobile]);

Alternatively, keep the effect but add a brief loading state to prevent the flash.


121-129: Redundant md:hidden class.

The button rendering is already gated by isMobile && !sidebarOpen (line 121), so the md:hidden class on line 123 is redundant. If isMobile is false, the button won't render regardless of the CSS class.

                         <button
-                                className="fixed bottom-6 right-6 z-40 md:hidden bg-white border border-gray-200 shadow-sm px-3 py-2 rounded-full text-gray-700 flex items-center gap-2"
+                                className="fixed bottom-6 right-6 z-40 bg-white border border-gray-200 shadow-sm px-3 py-2 rounded-full text-gray-700 flex items-center gap-2"
                                 onClick={() => setSidebarOpen(true)}

Otherwise, this mobile toggle implementation looks good with proper accessibility (aria-label).

src/components/layout/Sidebar.jsx (1)

110-112: Missing backdrop overlay for mobile sidebar.

The mobile sidebar uses fixed inset-0 z-50 positioning but only occupies w-72 of the screen, leaving the rest of the viewport unhandled. Users have no way to dismiss the sidebar by tapping outside it — a standard mobile UX pattern.

Consider adding a backdrop overlay:

+    const backdropClasses = isMobile && open
+        ? 'fixed inset-0 z-40 bg-black/30'
+        : 'hidden';
+
     return (
+        <>
+            {isMobile && open && (
+                <div 
+                    className={backdropClasses}
+                    onClick={() => setOpen(false)}
+                    aria-hidden="true"
+                />
+            )}
             <aside
                 className={sidebarClasses}
                 style={{ minHeight: '100vh' }}
             >

This provides visual context and allows tap-to-dismiss.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 16f19ac and 82864f0.

📒 Files selected for processing (3)
  • src/App.jsx (4 hunks)
  • src/components/layout/Sidebar.jsx (5 hunks)
  • src/components/layout/UseIsMobile.jsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
src/components/layout/UseIsMobile.jsx (2)
src/App.jsx (1)
  • isMobile (54-54)
src/components/layout/Sidebar.jsx (1)
  • isMobile (8-8)
src/components/layout/Sidebar.jsx (4)
src/hooks/usePage.js (2)
  • usePage (4-4)
  • usePage (4-4)
src/pages/PlaceGeographyPage.jsx (1)
  • usePage (19-19)
src/App.jsx (3)
  • isMobile (54-54)
  • page (50-50)
  • sidebarOpen (53-53)
src/components/layout/UseIsMobile.jsx (2)
  • isMobile (4-4)
  • useIsMobile (3-16)
src/App.jsx (2)
src/components/layout/Sidebar.jsx (1)
  • isMobile (8-8)
src/components/layout/UseIsMobile.jsx (2)
  • isMobile (4-4)
  • useIsMobile (3-16)
🔇 Additional comments (3)
src/components/layout/Sidebar.jsx (3)

8-12: Good mobile UX: Auto-close sidebar on navigation.

The handleNav function properly closes the sidebar after navigation on mobile devices, which is the expected behavior for mobile drawer patterns.


26-45: NavLink rendering logic looks correct.

The isExpanded = open || isMobile logic correctly shows labels when either the sidebar is open (desktop) or on mobile (where the sidebar is a full drawer). The handleNav integration properly handles mobile dismissal.


50-50: SubMenu expansion logic is consistent.

The isExpanded = sidebarOpen || isMobile mirrors the NavLink logic, ensuring consistent behavior across navigation elements.

Copy link

@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: 0

🧹 Nitpick comments (6)
src/components/modals/PlaceActionModal.jsx (3)

9-21: Duplicate ESC key handler — already provided by the Modal component.

The Modal component (lines 18-32 in Modal.jsx) already implements ESC key handling. This effect is redundant and can be removed.

🔎 Proposed fix
-    // ESC 키로 닫기
-    useEffect(() => {
-        const handleKeyPress = (e) => {
-            if (e.key === 'Escape') {
-                onClose();
-            }
-        };
-
-        document.addEventListener('keydown', handleKeyPress);
-        return () => {
-            document.removeEventListener('keydown', handleKeyPress);
-        };
-    }, [onClose]);
-

6-7: Unused state: coordinates and loadingCoordinates are never used.

These values are fetched but never displayed or passed anywhere. If this is preparation for future use, consider removing until needed. If it's meant to display current coordinates, the UI is missing.

Also applies to: 23-66


107-136: Consider adding defensive null checks for consistency.

onImageManage has a null check (line 95), but onCoordinateEdit, onEdit, and onDelete do not. While these are always provided in PlacePage.jsx, adding guards would make the component more defensive and reusable.

🔎 Proposed fix for onCoordinateEdit
-                    <button
+                    {onCoordinateEdit && (
+                        <button
                         onClick={() => {
                             onCoordinateEdit(place);
                             onClose();
                         }}
                         className="w-full bg-indigo-50 hover:bg-indigo-100 text-indigo-700 font-semibold py-4 px-4 rounded-lg transition-all duration-200 flex items-center justify-center gap-2"
                     >
                         <i className="fas fa-map-marker-alt"></i>
                         좌표 수정
                     </button>
+                    )}
src/pages/PlacePage.jsx (3)

17-17: Multiple useIsMobile() calls create redundant media query listeners.

useIsMobile() is called in both MainPlaceCard and OtherPlaceCard, plus PlacePage. Each call creates a separate matchMedia listener. Consider passing isMobile as a prop from PlacePage to avoid redundant listeners.

🔎 Proposed approach
-const MainPlaceCard = ({ place, onEdit, onDelete, onImageManage, onCoordinateEdit, showToast, onMobileClick }) => {
+const MainPlaceCard = ({ place, onEdit, onDelete, onImageManage, onCoordinateEdit, showToast, onMobileClick, isMobile }) => {
     const [isHovered, setIsHovered] = useState(false);
     const [imageLoading, setImageLoading] = useState(true);
-    const isMobile = useIsMobile();

Then pass isMobile={isMobile} from PlacePage where the hook is already called (line 299).

Also applies to: 186-186


32-40: Duplicate handleCardClick implementations in both card components.

The logic in MainPlaceCard (lines 32-40) and OtherPlaceCard (lines 205-213) is identical. Consider extracting to a shared helper or using a higher-order component.

🔎 Proposed helper
// At the top of the file or in a utils file
const createMobileCardClickHandler = (isMobile, onMobileClick, place) => (e) => {
    if (isMobile && onMobileClick && !e.target.closest('button')) {
        onMobileClick(place);
    }
};

Also applies to: 205-213


883-895: LGTM! Mobile modal integration is correct.

The modal is properly controlled by state and cleanup resets both mobileActionModalOpen and selectedPlaceForMobile.

Minor: The onImageManage conditional (line 888) duplicates the isMainPlace logic from line 815. Consider extracting this category check to a helper function for consistency.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 82864f0 and 021c8eb.

📒 Files selected for processing (3)
  • src/components/common/Modal.jsx (3 hunks)
  • src/components/modals/PlaceActionModal.jsx (1 hunks)
  • src/pages/PlacePage.jsx (13 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/components/modals/PlaceActionModal.jsx (2)
src/utils/api.js (1)
  • api (5-10)
src/components/common/Modal.jsx (1)
  • Modal (3-77)
src/pages/PlacePage.jsx (1)
src/components/layout/UseIsMobile.jsx (1)
  • useIsMobile (3-16)
🔇 Additional comments (3)
src/components/common/Modal.jsx (2)

18-32: LGTM! ESC key handler is well-implemented.

The effect properly guards against !isOpen, includes cleanup, and has correct dependencies.


41-46: LGTM! Backdrop click handler is correctly implemented.

The contains check ensures clicks on modal content don't trigger close, and the closeOnBackdropClick guard provides opt-in behavior.

src/pages/PlacePage.jsx (1)

297-299: LGTM!

State management for the mobile modal flow is clean and straightforward.

Also applies to: 506-510

Copy link

@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: 0

🧹 Nitpick comments (4)
src/pages/PlaceGeographyPage.jsx (4)

327-335: Consider clearing the timeout on cleanup.

If the component unmounts within 100ms, the timeout callback could attempt to access a stale ref. While unlikely to cause visible issues, it's good practice to clear pending timeouts.

🔎 Proposed fix
   useEffect(() => {
+    let timeoutId;
     if (mapInstanceRef.current && window.naver) {
-      setTimeout(() => {
+      timeoutId = setTimeout(() => {
         window.naver.maps.Event.trigger(mapInstanceRef.current, 'resize');
       }, 100);
     }
+    return () => {
+      if (timeoutId) clearTimeout(timeoutId);
+    };
   }, [isMobile]);

337-357: Direct DOM manipulation of parent element creates tight coupling.

Querying and modifying the <main> element from a child page component introduces implicit dependencies on the layout structure. If the layout changes or the selector fails, this code silently does nothing.

Consider alternatives:

  1. Pass a callback/context from the layout to allow pages to request scroll-lock
  2. Use a CSS class on body or the layout root that child components can toggle
  3. Use a dedicated modal/overlay library that handles body scroll-lock

The current implementation works but is fragile and harder to maintain.


404-412: Redundant ternary check on line 411.

In the else branch of style={isMobile ? {...} : {...}}, isMobile is guaranteed to be false, so height: isMobile ? '100%' : 'calc(100% - 64px)' will always resolve to 'calc(100% - 64px)'.

🔎 Proposed fix
-        style={isMobile ? { 
-          flex: 1, 
-          overflow: 'hidden',
-          paddingTop: '8px',
-          paddingBottom: '8px'
-        } : { height: isMobile ? '100%' : 'calc(100% - 64px)' }}
+        style={isMobile ? { 
+          flex: 1, 
+          overflow: 'hidden',
+          paddingTop: '8px',
+          paddingBottom: '8px'
+        } : { height: 'calc(100% - 64px)' }}

388-402: Consider extracting mobile styles for maintainability.

The inline style objects with conditional mobile/desktop logic are becoming complex. Consider extracting these into named style objects or CSS classes to improve readability and maintainability.

// Example: extract at component top
const mobileContainerStyle = {
  height: '100vh',
  boxSizing: 'border-box',
  overflow: 'hidden',
  position: 'fixed',
  top: 0,
  left: 0,
  right: 0,
  bottom: 0,
  width: '100%'
};

const desktopContainerStyle = {
  height: '100%',
  boxSizing: 'border-box'
};
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 021c8eb and f20e4a9.

📒 Files selected for processing (1)
  • src/pages/PlaceGeographyPage.jsx (7 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/pages/PlaceGeographyPage.jsx (1)
src/components/layout/UseIsMobile.jsx (1)
  • useIsMobile (3-16)
🔇 Additional comments (4)
src/pages/PlaceGeographyPage.jsx (4)

9-9: LGTM!

The useIsMobile hook is imported correctly and used at the component's top level, following React's rules of hooks.

Also applies to: 22-22


299-311: LGTM!

Clean separation of desktop resize logic with proper early return for mobile. The dependency array correctly includes isMobile.


414-465: LGTM!

The mobile layout with 40%/55% height split for map/list containers is reasonable, leaving room for gaps and the title. The conditional overflow handling is appropriate for mobile scroll containment.


553-565: Button click propagates to parent, triggering map pan/zoom.

Clicking the "좌표 수정/설정" button will also invoke the parent div's onClick handler via event propagation, causing handlePlaceClick(booth) to run. This centers the map on the marker and zooms in, in addition to opening the modal.

If this is intentional, consider adding a brief comment. If not, add e.stopPropagation():

🔎 Proposed fix if propagation should be stopped
                       <button
                         className={`font-bold py-2 px-4 rounded-lg text-sm ${
                           hasCoordinates 
                             ? 'bg-blue-200 hover:bg-blue-300 text-blue-800' 
                             : 'bg-red-200 hover:bg-red-300 text-red-800'
                         }`}
-                        onClick={() => {
+                        onClick={(e) => {
+                          e.stopPropagation();
                           setSelectedPlace(booth);
                           setModalOpen(true);
                         }}

Copy link

@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: 1

Caution

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

⚠️ Outside diff range comments (3)
src/components/modals/PlaceEditModal.jsx (1)

142-155: Remove duplicate ESC key handler.

The Modal component now handles ESC key presses internally (as shown in src/components/common/Modal.jsx lines 29-43). This local ESC handler duplicates that functionality and should be removed to avoid redundant event listeners.

🔎 Proposed fix
-  // ESC 키 이벤트 리스너
-  useEffect(() => {
-    const handleEscKey = (event) => {
-      if (event.key === 'Escape') {
-        onClose();
-      }
-    };
-
-    document.addEventListener('keydown', handleEscKey);
-
-    return () => {
-      document.removeEventListener('keydown', handleEscKey);
-    };
-  }, [onClose]);
-
src/components/modals/PlaceImagesModal.jsx (1)

17-35: Remove duplicate ESC key handler.

The Modal component now provides built-in ESC key handling (src/components/common/Modal.jsx lines 29-43). This local handler creates redundant event listeners and should be removed.

🔎 Proposed fix
-    // ESC 키 이벤트 리스너
-    useEffect(() => {
-        const handleEscKey = (event) => {
-            if (event.key === 'Escape') {
-                if (selectedImage) {
-                    handleCloseDetail();
-                } else if (isReorderMode || isDeleteMode) {
-                    handleCancelMode();
-                } else {
-                    onClose();
-                }
-            }
-        };
-
-        document.addEventListener('keydown', handleEscKey);
-        return () => {
-            document.removeEventListener('keydown', handleEscKey);
-        };
-    }, [selectedImage, isReorderMode, isDeleteMode, onClose]);
-

Note: If you need custom ESC behavior (e.g., closing detail view or canceling modes first), consider moving this logic into a ref-based onClose handler passed to Modal instead of adding a separate listener.

src/components/modals/OtherPlaceEditModal.jsx (1)

43-55: Remove duplicate ESC key handler.

The Modal component now handles ESC key presses internally (src/components/common/Modal.jsx lines 29-43). Remove this duplicate handler to avoid redundant event listeners.

🔎 Proposed fix
-    // ESC 키 이벤트 리스너
-    useEffect(() => {
-        const handleEscKey = (event) => {
-            if (event.key === 'Escape') {
-                onClose();
-            }
-        };
-
-        document.addEventListener('keydown', handleEscKey);
-        return () => {
-            document.removeEventListener('keydown', handleEscKey);
-        };
-    }, [onClose]);
-
🧹 Nitpick comments (1)
src/pages/PlaceGeographyPage.jsx (1)

337-357: Consider risks of direct DOM manipulation on main element.

This effect directly modifies the main element's overflow and height styles on mobile. While the cleanup function restores original values, this approach could conflict if:

  • Other code also modifies these styles
  • The component unmounts unexpectedly
  • Multiple instances run simultaneously (unlikely here)

Consider alternative approaches like:

  1. Using a portal for the mobile layout
  2. Applying mobile-specific classes to main via a context/prop
  3. Documenting this behavior for maintainability

The current implementation works correctly with proper cleanup, but the above approaches would be more maintainable long-term.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f20e4a9 and f38bccc.

📒 Files selected for processing (7)
  • src/components/common/Modal.jsx (2 hunks)
  • src/components/modals/OtherPlaceEditModal.jsx (5 hunks)
  • src/components/modals/PlaceActionModal.jsx (1 hunks)
  • src/components/modals/PlaceEditModal.jsx (3 hunks)
  • src/components/modals/PlaceImagesModal.jsx (4 hunks)
  • src/pages/PlaceGeographyPage.jsx (8 hunks)
  • src/pages/PlacePage.jsx (14 hunks)
🧰 Additional context used
🧬 Code graph analysis (7)
src/components/modals/OtherPlaceEditModal.jsx (3)
src/components/common/Modal.jsx (1)
  • isMobile (18-18)
src/components/modals/PlaceImagesModal.jsx (1)
  • isMobile (15-15)
src/components/layout/UseIsMobile.jsx (1)
  • useIsMobile (3-16)
src/components/modals/PlaceActionModal.jsx (3)
src/utils/api.js (1)
  • api (5-10)
src/pages/PlacePage.jsx (1)
  • mainImage (29-29)
src/components/common/Modal.jsx (1)
  • Modal (4-95)
src/components/common/Modal.jsx (1)
src/components/layout/UseIsMobile.jsx (1)
  • useIsMobile (3-16)
src/pages/PlacePage.jsx (3)
src/components/common/Modal.jsx (2)
  • isMobile (18-18)
  • Modal (4-95)
src/components/modals/PlaceImagesModal.jsx (1)
  • isMobile (15-15)
src/components/layout/UseIsMobile.jsx (1)
  • useIsMobile (3-16)
src/components/modals/PlaceImagesModal.jsx (2)
src/components/common/Modal.jsx (2)
  • isMobile (18-18)
  • Modal (4-95)
src/components/layout/UseIsMobile.jsx (1)
  • useIsMobile (3-16)
src/components/modals/PlaceEditModal.jsx (2)
src/components/common/Modal.jsx (2)
  • isMobile (18-18)
  • Modal (4-95)
src/components/layout/UseIsMobile.jsx (1)
  • useIsMobile (3-16)
src/pages/PlaceGeographyPage.jsx (2)
src/components/common/Modal.jsx (2)
  • isMobile (18-18)
  • Modal (4-95)
src/components/layout/UseIsMobile.jsx (1)
  • useIsMobile (3-16)
🔇 Additional comments (9)
src/components/modals/PlaceEditModal.jsx (1)

4-4: LGTM! Mobile-aware modal integration looks good.

The useIsMobile hook integration and enableMobileLayout usage correctly adapt the modal for mobile devices. The conditional wrapper with adjusted padding and typography provides appropriate mobile UX.

Also applies to: 21-21, 274-281

src/components/modals/PlaceImagesModal.jsx (1)

5-5: LGTM! Mobile layout adaptations are well-implemented.

The mobile-aware changes appropriately adjust modal sizing, button layouts (horizontal → vertical stacking), and padding for mobile devices. The responsive button group provides better UX on smaller screens.

Also applies to: 15-15, 343-352, 493-506

src/components/modals/OtherPlaceEditModal.jsx (1)

4-4: LGTM! Mobile layout integration is clean.

The useIsMobile hook integration and conditional layout adjustments provide appropriate mobile responsiveness. Button stacking on mobile improves usability.

Also applies to: 15-15, 119-121, 163-183

src/components/common/Modal.jsx (1)

2-2: LGTM! Modal enhancements improve mobile UX and accessibility.

The additions are well-implemented:

  • ESC key support improves keyboard accessibility
  • enableMobileLayout and closeOnBackdropClick props provide flexible mobile behavior
  • Mobile-specific sizing and padding classes adapt appropriately
  • Backdrop click handling respects the conditional prop

These changes centralize modal behavior and eliminate the need for individual components to implement ESC handlers.

Also applies to: 4-13, 18-18, 29-43, 52-66, 73-73, 77-77

src/components/modals/PlaceActionModal.jsx (2)

23-66: LGTM! Coordinate fetching with proper fallbacks and error handling.

The logic correctly:

  • Checks place.markerCoordinate first
  • Falls back to place.coordinate
  • Finally fetches from API if needed
  • Handles errors gracefully with console logging
  • Ensures setLoadingCoordinates(false) is always called in the finally block

70-146: LGTM! Modal integration and action buttons are well-structured.

The component properly:

  • Uses enableMobileLayout and closeOnBackdropClick for mobile UX
  • Displays the main image with a fallback placeholder
  • Provides clear action buttons (image management, coordinate edit, edit, delete)
  • Closes the modal after each action
src/pages/PlaceGeographyPage.jsx (1)

9-9: LGTM! Mobile layout adaptations are comprehensive and well-executed.

The mobile-specific changes properly:

  • Adjust map and list container heights (40%/55% split)
  • Lock main element scrolling on mobile
  • Trigger map resize when layout changes
  • Apply appropriate mobile styles and overflow handling
  • Enable mobile layout for coordinate modal

The implementation maintains desktop functionality while providing a mobile-optimized experience.

Also applies to: 22-22, 299-335, 389-466, 592-597

src/pages/PlacePage.jsx (2)

9-10: LGTM! Mobile interaction pattern is well-designed and implemented.

The changes effectively:

  • Introduce mobile-specific card click behavior via onMobileClick
  • Gate desktop hover actions with !isMobile checks to avoid conflicts
  • Open PlaceActionModal on mobile card taps (excluding button clicks)
  • Pass consistent props to MainPlaceCard and OtherPlaceCard
  • Manage mobile modal state cleanly with proper cleanup

The desktop and mobile interaction patterns are cleanly separated, providing optimal UX for each context.

Also applies to: 14-48, 76-126, 297-299, 507-510, 826-827, 836-837, 888-900


865-887: LGTM! Coordinate modal properly uses mobile layout.

The modal correctly applies enableMobileLayout={true} for responsive behavior on mobile devices.

Comment on lines +9 to +21
// ESC 키로 닫기
useEffect(() => {
const handleKeyPress = (e) => {
if (e.key === 'Escape') {
onClose();
}
};

document.addEventListener('keydown', handleKeyPress);
return () => {
document.removeEventListener('keydown', handleKeyPress);
};
}, [onClose]);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove duplicate ESC key handler.

The Modal component now handles ESC key presses internally (src/components/common/Modal.jsx lines 29-43). This handler is redundant since the Modal already calls onClose on ESC.

🔎 Proposed fix
-    // ESC 키로 닫기
-    useEffect(() => {
-        const handleKeyPress = (e) => {
-            if (e.key === 'Escape') {
-                onClose();
-            }
-        };
-
-        document.addEventListener('keydown', handleKeyPress);
-        return () => {
-            document.removeEventListener('keydown', handleKeyPress);
-        };
-    }, [onClose]);
-
📝 Committable suggestion

‼️ 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.

Suggested change
// ESC 키로 닫기
useEffect(() => {
const handleKeyPress = (e) => {
if (e.key === 'Escape') {
onClose();
}
};
document.addEventListener('keydown', handleKeyPress);
return () => {
document.removeEventListener('keydown', handleKeyPress);
};
}, [onClose]);
🤖 Prompt for AI Agents
In src/components/modals/PlaceActionModal.jsx around lines 9 to 21, there is a
redundant useEffect that listens for the Escape key and calls onClose; since the
Modal component now handles ESC internally, remove this entire useEffect block
(the handler, addEventListener, and cleanup removeEventListener) so the
component no longer registers its own keydown listener and relies on the Modal's
onClose behavior.

@changuii changuii merged commit a715f73 into prod Dec 24, 2025
1 of 2 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Dec 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants