-
Notifications
You must be signed in to change notification settings - Fork 3
Develop #416
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Develop #416
Conversation
fix: 부스 관리 페이지 반응형 롤백 & 배너이미지 반응형 반영
fix: 배너이미지 반응형(모바일)
feat: 메뉴 섹션 스와이프 삭제 기능 추가
feat: 메뉴 섹션 스와이프 삭제 기능 추가
fix: 토스트 알림 디자인 & 삭제 구현
refactor: add user login page image
|
Caution Review failedThe pull request is closed. WalkthroughAdds swipe/dismiss interactions and compact toast UI for new orders; introduces a reusable SwipeableRow for swipe-to-delete menu items with confirm flow; applies max-width layout constraints across AdminBooth pages; updates CloseButton API and toast/global CSS; integrates Sentry into nowait-user and adds many runtime package mappings (.pnp.cjs). Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant U as User
participant T as NewOrderToast
participant S as SwipeToDismiss
participant R as Router
rect rgb(230,245,255)
note right of T: toast renders compact, summary computed
U->>T: Tap toast
T->>R: navigate("/admin/orders/{storeId}")
end
rect rgb(255,240,230)
U->>S: Swipe right on toast
S-->>T: threshold exceeded → dismiss (animate out)
U->>T: Click CloseButton
T-->>T: explicit dismiss
end
sequenceDiagram
autonumber
participant U as User
participant SW as SwipeableRow
participant MR as MenuSection
participant M as ConfirmModal
participant API as deleteMenu()
participant ST as LocalState
U->>SW: Swipe left on row
SW-->>SW: reveal "삭제" (open)
U->>SW: Tap "삭제"
SW->>MR: onDeleteClick()
MR->>M: open confirm modal
alt Confirmed
M->>API: deleteMenu(menuId)
API-->>MR: success
MR->>ST: remove item from menus (local state)
else Cancelled
M-->>MR: close modal, no change
end
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Suggested reviewers
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. ⛔ Files ignored due to path filters (51)
📒 Files selected for processing (5)
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (8)
apps/nowait-admin/src/pages/AdminBooth/components/NoticeEditor.tsx (1)
114-121: Fix event-listener leak on the editor "update" handlerYou add a new listener on every mount but don’t remove it, causing duplicate updates and memory growth.
useEffect(() => { if (!editor) return; - editor.on("update", () => { - setNotice(editor.getHTML()); - }); + const handler = () => setNotice(editor.getHTML()); + editor.on("update", handler); + return () => editor.off("update", handler); }, [editor, setNotice]);apps/nowait-admin/src/pages/AdminBooth/components/BoothSection.tsx (1)
103-123: Avoid object URL leaks in Preview propsURL.createObjectURL is called during render; repeated renders will leak URLs. Generate URLs in a memo/effect and revoke them on cleanup (or move this responsibility into PreviewModal).
Example inside PreviewModal (preferred centralization):
// derive URL for a possibly-File image const useObjectUrl = (fileOrUrl?: File | string | null) => { const [url, setUrl] = useState<string | null>(null); useEffect(() => { if (!fileOrUrl) return setUrl(null); if (fileOrUrl instanceof File) { const u = URL.createObjectURL(fileOrUrl); setUrl(u); return () => URL.revokeObjectURL(u); } setUrl(fileOrUrl); }, [fileOrUrl]); return url; };Then pass raw File or URL from BoothSection; let PreviewModal resolve and clean up.
apps/nowait-admin/src/pages/AdminBooth/components/AccountPage.tsx (3)
230-247: Unblock: setSaving(false) is never reset on API errorsBoth create and update error paths leave
savingstucktrue, disabling the Save button until reload.Apply:
createPayment(payload, { onSuccess: () => { console.log("결제 정보가 생성되었습니다."); setSaving(false); }, - onError: () => console.log("결제 정보 생성 실패"), + onError: () => { + console.log("결제 정보 생성 실패"); + setSaving(false); + }, });updatePayment(payload, { onSuccess: () => { console.log("결제 정보가 수정되었습니다.", payload); refetch(); setSaving(false); }, - onError: () => console.log("결제 정보 수정 실패"), + onError: () => { + console.log("결제 정보 수정 실패"); + setSaving(false); + }, });
270-282: Guard against undefined/empty accountNumber to avoid runtime crash
res.accountNumber.split(" ")will throw ifaccountNumberisundefinedor"". Also, use robust splitting.- const accountInfo = res.accountNumber.split(" "); + const rawAccount = (res.accountNumber ?? "").trim(); + const accountInfo = rawAccount.length ? rawAccount.split(/\s+/) : [];
451-456: Use text input for account numbers (leading zeros, long values, no spinners)
type="number"drops leading zeros and allows scientific notation. Use text + numeric keyboard.- <input - type="number" + <input + type="text" + inputMode="numeric" + pattern="[0-9\- ]*" placeholder="계좌번호" value={accountNumber} onChange={(e) => setAccountNumber(e.target.value)} className="w-1/2 border border-[#dddddd] bg-black-5 text-14-regular text-black-90 rounded-xl p-4" />apps/nowait-admin/src/pages/AdminBooth/components/MenuSection.tsx (3)
294-301: react-beautiful-dnd integration is broken by duplicate ref/props.provided.innerRef and draggableProps must be attached to the same DOM node. Currently you attach them to both rowContent’s inner div and to SwipeableRow (via ref/contentProps), which can break drag and cause warnings.
Apply:
- const rowContent = ( - <div - className="flex justify-between items-center py-4" - ref={provided.innerRef} - {...provided.draggableProps} - style={lockedStyle} - > + const rowContent = ( + <div className="flex justify-between items-center py-4"> ... </div> ); return ( <SwipeableRow - ref={provided.innerRef} + ref={provided.innerRef} disabled={editMode} onDelete={() => { // 즉시 삭제 setSelectedMenu(menu); deleteMenu(menu.id, { onSuccess: () => { setMenus((prev) => prev.filter((m) => m.id !== menu.id) ); }, }); }} contentProps={{ - ...provided.draggableProps, - style: lockedStyle, + ...provided.draggableProps, + style: lockedStyle, + className: "flex justify-between items-center py-4", }} > {rowContent} </SwipeableRow> );Also applies to: 340-361
254-258: Fix Tailwind class typo and add alt for icon.“itens-center” should be “items-center”. Also add alt for the decorative icon to satisfy a11y.
- <button - className="flex itens-center text-14-semibold px-[10px] py-[7.5px] bg-black-5 text-black-70 rounded-[8px]" + <button + className="flex items-center text-14-semibold px-[10px] py-[7.5px] bg-black-5 text-black-70 rounded-[8px]" onClick={() => setIsAddModalOpen(true)} > - 메뉴 추가 <img src={addIcon} /> + 메뉴 추가 <img src={addIcon} alt="" aria-hidden="true" /> </button>
173-196: Sold-out toggle rolls UI state the wrong way on error.You only flip state on success; on error you also flip, leaving the UI incorrect. Either do optimistic update with rollback, or keep current pattern and don’t touch state on error.
Minimal fix:
onError: () => { - // 3) 실패 시 롤백 - setMenus((prev) => { - const next = [...prev]; - next[index] = { ...next[index], soldOut: !next[index].soldOut }; - return next; - }); + console.log("품절 토글 실패"); },If you prefer optimistic UX, flip before calling soldOut and keep the rollback block (then remove the onSuccess flip).
🧹 Nitpick comments (24)
apps/nowait-admin/src/pages/AdminBooth/components/OperatingTimeSelector.tsx (1)
64-64: Center the width-capped container for consistent layoutAdd w-full and mx-auto so the section stays centered while capped at 614px (mirrors other sections).
- <div className="mb-[50px] max-w-[614px]"> + <div className="mb-[50px] w-full max-w-[614px] mx-auto">apps/nowait-admin/src/pages/AdminBooth/components/NoticeEditor.tsx (2)
131-131: Center the editor card like other capped sectionsAdd mx-auto so the 614px-capped card is horizontally centered.
- <div className="w-full bg-white border border-[#DDDDDD] rounded-xl max-w-[614px]"> + <div className="w-full bg-white border border-[#DDDDDD] rounded-xl max-w-[614px] mx-auto">
46-53: Remove debug log from toolbar effectconsole.log(editorChanged) is noisy in production; the effect already re-renders via the transaction listener.
useEffect(() => { if (!editor) return; - console.log(editorChanged); const update = () => setEditorChanged((prev) => prev + 1); editor.on("transaction", update); return () => editor.off("transaction", update); }, [editor]);apps/nowait-admin/src/pages/AdminBooth/components/BoothSection.tsx (3)
76-76: Center all 614px-capped sections for visual consistencyAdd w-full and mx-auto to each capped wrapper so they’re centered across viewports.
- <div className="flex flex-col items-center pb-[50px] max-w-[614px]"> + <div className="flex flex-col items-center pb-[50px] w-full max-w-[614px] mx-auto">- <div className="flex flex-col mb-[50px] relative max-w-[614px]"> + <div className="flex flex-col mb-[50px] relative w-full max-w-[614px] mx-auto">- <div className="flex flex-col mb-[50px] max-w-[614px]"> + <div className="flex flex-col mb-[50px] w-full max-w-[614px] mx-auto">Also applies to: 174-174, 204-204
223-236: Constrain uploads to images at the input levelAdd accept to prevent non-image files from selection.
- <input - type="file" + <input + type="file" + accept="image/*" className="hidden"
76-76: Reduce repetition of the magic width "614px"Consider a shared token (Tailwind theme: extend maxWidth.booth = "614px" → use max-w-booth) to avoid hardcoding across components.
apps/nowait-user/src/pages/login/LoginPage.tsx (1)
87-89: A11y: mark decorative SVG as hidden or provide a titleIf purely decorative, hide it from AT. If meaningful, provide an accessible name.
- <LoginImage_4 /> + <LoginImage_4 aria-hidden="true" focusable="false" />Alternatively:
<LoginImage_4 role="img" aria-label="축제 맵 일러스트" />apps/nowait-admin/src/global.css (2)
74-80: Tame backdrop blur cost and ensure visible frosted lookBlur(200px) is costly on mobile GPUs and the frosted effect needs a semi-transparent background to be noticeable. Reduce blur and add a translucent background; include a fallback for browsers without backdrop-filter.
.toast-card { - box-shadow: 0px 4px 25px rgba(0, 0, 0, 0.1); - backdrop-filter: blur(200px); - -webkit-backdrop-filter: blur(200px); - border-radius: 10px; + box-shadow: 0px 4px 25px rgba(0, 0, 0, 0.1); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + background: rgba(255, 255, 255, 0.75); + border-radius: 10px; }Optional fallback:
@supports not (backdrop-filter: blur(1px)) { .toast-card { background: rgba(255, 255, 255, 0.95); } }
90-113: Align text utility colors with theme tokensHardcoded colors (#fff, #666666, #222222) can drift from the design system. Prefer CSS variables or Tailwind theme colors (e.g., var(--black-70)) for consistency and theming.
apps/nowait-admin/src/pages/AdminBooth/components/AccountPage.tsx (5)
342-342: Fix non-existent utility and ensure inner content can shrink
w-overflow-scrollisn't a standard Tailwind class. Use overflow utilities andmin-w-0to prevent flex children from overflowing.- <div className={`flex flex-col w-[79%] w-overflow-scroll`}> + <div className="flex flex-col min-w-0 w-[79%] overflow-x-auto">
180-183: Save disabled state should reflect error presence (and avoid unused dep)Currently
hasErroris in deps but not in the expression.- const saveDisabled = useMemo(() => { - return saving || (sameUrls && sameAccount) || !paymentFilled; - }, [saving, sameUrls, sameAccount, paymentFilled, hasError]); + const saveDisabled = useMemo(() => { + return saving || (sameUrls && sameAccount) || !paymentFilled || hasError; + }, [saving, sameUrls, sameAccount, paymentFilled, hasError]);
298-306: Make the clickable banner accessible (keyboard + semantics)Clickable
<img>lacks keyboard access. Wrap with a button or add role/tabIndex handlers.- <div className="h-full min-h-[60px]"> - <img - src={banner} - alt="배너" - className={`object-fill min-h-[60px]`} - onClick={() => navigate("guides")} - /> - </div> + <div className="h-full min-h-[60px]"> + <button + type="button" + aria-label="가이드 보기" + onClick={() => navigate("guides")} + className="w-full" + > + <img + src={banner} + alt="배너" + className="object-fill min-h-[60px] w-full" + /> + </button> + </div>
40-40: Avoid JS breakpoint for one-off margin; prefer CSS to reduce hydration risk
isSmallis only used for banner margins. Use a Tailwind max-width breakpoint instead; then removeisSmall.- const isSmall = width < 415;And update the banner container (Line 298):
- <div className={`h-full ${isSmall ? "my-4" : "my-10"}`}> + <div className="h-full my-10 max-[414px]:my-4">
170-170: Remove noisy console logs in production
console.log(curAccount, "즉각 반영", sameAccount);can leak data and clutter logs. Guard by env or remove.apps/nowait-admin/src/components/closeButton.tsx (1)
15-20: Improve a11y: label the button itselfAdd an explicit accessible name on the button; relying on inner img alt is fragile.
- <button + <button type="button" onPointerDown={(e) => e.stopPropagation()} onClick={onClick} - className={`flex items-center justify-center h-6 w-6`} + aria-label="닫기" + title="닫기" + className={`flex items-center justify-center h-6 w-6`} >apps/nowait-admin/src/pages/AdminBooth/components/Swipe/SwipeableRow.tsx (5)
104-125: Do not override consumer event handlers from contentPropsYour handlers replace
contentPropshandlers. Safely call both to preserve consumer behavior.- onTouchStart={onTouchStart} + onTouchStart={(e) => { + contentProps?.onTouchStart?.(e); + onTouchStart(e); + }} - onTouchMove={onTouchMove} + onTouchMove={(e) => { + contentProps?.onTouchMove?.(e); + onTouchMove(e); + }} - onTouchEnd={onTouchEnd} + onTouchEnd={(e) => { + contentProps?.onTouchEnd?.(e); + onTouchEnd(); + }} - onMouseDown={onMouseDown} + onMouseDown={(e) => { + contentProps?.onMouseDown?.(e); + onMouseDown(e); + }} - onMouseMove={onMouseMove} + onMouseMove={(e) => { + contentProps?.onMouseMove?.(e); + onMouseMove(e); + }} - onMouseUp={onMouseUp} + onMouseUp={(e) => { + contentProps?.onMouseUp?.(e); + onMouseUp(); + }}(Repeat pattern for
onMouseLeaveif needed.)
119-125: Handle touch cancel to reset stateInterrupted gestures (e.g., OS alerts) may leave
dragging=true. AddonTouchCancel.onTouchEnd={onTouchEnd} + onTouchCancel={onTouchEnd}
91-101: Make delete affordance interactive and accessibleThe visible “삭제” area isn’t a control. Wrap it in a button to support click/tap/keyboard (while keeping swipe-to-delete).
- <div + <div className="absolute top-[16px] right-0 flex items-center justify-end" style={{ width: BTN_WIDTH + GAP, height: 70 }} > - <div - className="rounded-[8px] bg-[#FFF0EB] flex items-center justify-center" - style={{ width: BTN_WIDTH, height: 70 }} - > - <span className="text-[#FF4103] text-14-semibold">삭제</span> - </div> + <button + type="button" + aria-label="삭제" + onClick={onDelete} + className="rounded-[8px] bg-[#FFF0EB] flex items-center justify-center" + style={{ width: BTN_WIDTH, height: 70 }} + > + <span className="text-[#FF4103] text-14-semibold" aria-hidden> + 삭제 + </span> + </button> </div>
112-118: Minor perf: hint compositor for smoother swipesAdding
will-change: transformhelps GPU promote the layer during drag.transition: dragging || mouseDragging ? "none" : "transform 200ms ease", position: "relative", zIndex: 1, width: "100%", userSelect: "none", touchAction: "pan-y", + willChange: "transform",
18-21: Consider exposing swipe threshold/width as propsHard-coded
BTN_WIDTH/GAP(70/14) may not suit all rows or locales.apps/nowait-admin/src/components/NewOrderToast.tsx (3)
98-99: Avoid navigating to an empty store route.When storeId is missing, navigate to a safe fallback.
- const storeId = localStorage.getItem("storeId") ?? ""; + const storeId = localStorage.getItem("storeId"); @@ - onTap={() => navigate(`/admin/orders/${storeId}`)} // ← 클릭 이동은 여기서! + onTap={() => + navigate(storeId ? `/admin/orders/${storeId}` : "/admin/orders") + } // ← 클릭 이동은 여기서!Please confirm the fallback route “/admin/orders” exists.
Also applies to: 136-138
140-147: Improve toast accessibility.Augment role="alert" with aria-live and aria-atomic.
- <div - role="alert" + <div + role="alert" + aria-live="assertive" + aria-atomic="true" className={[
181-182: Deduplicate keyframes; move toastIn to global CSS.Inline keyframes per toast inflate DOM and can cause style thrash. Define once in global.css and drop the inline <style>.
- <style>{`@keyframes toastIn{to{transform:translateY(0);opacity:1}}`}</style>Add to global stylesheet (outside this file):
@keyframes toastIn { to { transform: translateY(0); opacity: 1; } }apps/nowait-admin/src/pages/AdminBooth/AdminBooth.tsx (1)
313-313: LGTM: width constraint added.max-w-[614px] centers and constrains content nicely.
If 614px is a design token, consider centralizing via Tailwind theme (theme.extend.maxWidth) to avoid magic numbers across files.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
apps/nowait-user/src/assets/login/login_img_4.svgis excluded by!**/*.svg
📒 Files selected for processing (12)
apps/nowait-admin/src/components/NewOrderToast.tsx(1 hunks)apps/nowait-admin/src/components/closeButton.tsx(1 hunks)apps/nowait-admin/src/global.css(2 hunks)apps/nowait-admin/src/layout/AdminLayout.tsx(0 hunks)apps/nowait-admin/src/pages/AdminBooth/AdminBooth.tsx(1 hunks)apps/nowait-admin/src/pages/AdminBooth/components/AccountPage.tsx(3 hunks)apps/nowait-admin/src/pages/AdminBooth/components/BoothSection.tsx(3 hunks)apps/nowait-admin/src/pages/AdminBooth/components/MenuSection.tsx(4 hunks)apps/nowait-admin/src/pages/AdminBooth/components/NoticeEditor.tsx(1 hunks)apps/nowait-admin/src/pages/AdminBooth/components/OperatingTimeSelector.tsx(1 hunks)apps/nowait-admin/src/pages/AdminBooth/components/Swipe/SwipeableRow.tsx(1 hunks)apps/nowait-user/src/pages/login/LoginPage.tsx(2 hunks)
💤 Files with no reviewable changes (1)
- apps/nowait-admin/src/layout/AdminLayout.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
apps/nowait-admin/src/pages/AdminBooth/components/MenuSection.tsx (1)
apps/nowait-admin/src/pages/AdminBooth/components/Swipe/SwipeableRow.tsx (1)
SwipeableRow(10-138)
apps/nowait-admin/src/components/NewOrderToast.tsx (1)
apps/nowait-admin/src/hooks/useNewOrderToastStore.tsx (1)
useNewOrderToastStore(41-71)
🔇 Additional comments (4)
apps/nowait-user/src/pages/login/LoginPage.tsx (1)
6-6: LGTM: new SVG illustration importImport pattern with ?react is consistent with existing usage.
apps/nowait-admin/src/pages/AdminBooth/components/AccountPage.tsx (1)
310-310: Confirm layout after removing w-full from sectionDropping
className="w-full"may change width in some parents. Please verify no regression on narrow containers.apps/nowait-admin/src/pages/AdminBooth/components/MenuSection.tsx (2)
239-239: LGTM: constrained list width.max-w-[614px] aligns with the form layout.
205-213: Confirm sortOrder indexing
The payload currently sends thesortOrdervalue (0-based) directly. Verify whether the/admin/menus/update-sortendpoint expects 1-based indexes—in which case you’d need to sendsortOrder + 1.
| const endDrag = () => { | ||
| if (!dragging) return; | ||
|
|
||
| const didSwipe = tx >= THRESHOLD; | ||
| if (didSwipe) { | ||
| onDismiss(); // 스와이프 → 제거 | ||
| } else if (!moved.current) { | ||
| onTap?.(); // 거의 안 움직였으면 → 탭으로 처리 | ||
| } | ||
| setTx(0); | ||
| setDragging(false); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Ghost-click suppression won’t work as intended (tx resets before click).
endDrag resets tx to 0 before the click event fires, so onClickCapture(tx >= THRESHOLD) won’t block the ghost click. Use a ref flag set during swipe-dismiss and clear it on capture.
Apply:
const moved = React.useRef(false);
+ const suppressClickRef = React.useRef(false);
@@
const endDrag = () => {
if (!dragging) return;
const didSwipe = tx >= THRESHOLD;
if (didSwipe) {
- onDismiss(); // 스와이프 → 제거
+ suppressClickRef.current = true;
+ onDismiss(); // 스와이프 → 제거
} else if (!moved.current) {
onTap?.(); // 거의 안 움직였으면 → 탭으로 처리
}
setTx(0);
setDragging(false);
};
@@
// 스와이프 후 발생하는 "유령 클릭" 1회 차단(탭은 통과)
const onClickCapture = (e: React.MouseEvent) => {
- if (tx >= THRESHOLD) {
+ if (suppressClickRef.current) {
e.stopPropagation();
e.preventDefault();
+ // @ts-ignore
+ (e.nativeEvent as any)?.stopImmediatePropagation?.();
+ suppressClickRef.current = false;
}
};Also applies to: 67-73
🤖 Prompt for AI Agents
In apps/nowait-admin/src/components/NewOrderToast.tsx around lines 54-65 (also
apply same fix to lines 67-73): the problem is resetting tx to 0 inside endDrag
before the click fires so onClickCapture(tx >= THRESHOLD) cannot detect a swipe
and suppress the ghost click. Fix by introducing a ref (e.g., swipedRef) that
you set to true when didSwipe is true inside endDrag, then call onDismiss; do
not rely on tx for click suppression. In your click-capture handler check
swipedRef.current and, if true, prevent the click (clear swipedRef.current =
false in that capture handler so subsequent clicks behave normally). Ensure you
still reset tx and dragging as needed after setting the ref.
| )} | ||
| </div> | ||
| <div className="flex justify-between h-[52px] w-full min-w-[236px] items-center bg-black-5 rounded-xl border border-[#dddddd] pl-4 pr-[10px] py-4"> | ||
| <div className="flex justify-between h-[52px] w-[474px] min-w-[236px] items-center bg-black-5 rounded-xl border border-[#dddddd] pl-4 pr-[10px] py-4"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Prevent horizontal overflow on small screens
Hard-coding w-[474px] can overflow on ~360px devices. Let the row grow/shrink responsively.
- <div className="flex justify-between h-[52px] w-[474px] min-w-[236px] items-center bg-black-5 rounded-xl border border-[#dddddd] pl-4 pr-[10px] py-4">
+ <div className="flex justify-between h-[52px] flex-1 min-w-0 md:max-w-[474px] min-w-[236px] items-center bg-black-5 rounded-xl border border-[#dddddd] pl-4 pr-[10px] py-4">📝 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.
| <div className="flex justify-between h-[52px] w-[474px] min-w-[236px] items-center bg-black-5 rounded-xl border border-[#dddddd] pl-4 pr-[10px] py-4"> | |
| <div className="flex justify-between h-[52px] flex-1 min-w-0 md:max-w-[474px] min-w-[236px] items-center bg-black-5 rounded-xl border border-[#dddddd] pl-4 pr-[10px] py-4"> |
🤖 Prompt for AI Agents
In apps/nowait-admin/src/pages/AdminBooth/components/AccountPage.tsx around line
341, the container uses a hard-coded w-[474px] which can cause horizontal
overflow on small (~360px) screens; replace the fixed width with responsive
sizing so the row can shrink: use w-full and max-w-[474px] (and add min-w-0 to
allow flex children to shrink) — e.g. change the class list to include w-full
max-w-[474px] min-w-0 while keeping other utility classes (you can keep or
adjust min-w-[236px] if needed).
fix: 스와이프 메뉴 삭제 로직 구현 & 애니메이션 효과는 미완성
There was a problem hiding this 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
♻️ Duplicate comments (1)
apps/nowait-admin/src/components/NewOrderToast.tsx (1)
25-38: Fix ghost-click suppression and avoid unintended navigation when tapping CloseButton.
- Ghost-click suppression checks tx, but tx is reset to 0 in endDrag before click fires → suppression fails. Use a ref flag.
- Tapping the CloseButton (no movement) triggers onTap navigation because tap detection doesn’t exclude interactive targets. Track this and skip onTap.
Apply:
const startX = React.useRef(0); const startY = React.useRef(0); const moved = React.useRef(false); + const suppressClickRef = React.useRef(false); // blocks one ghost click after swipe-dismiss + const ignoreTapRef = React.useRef(false); // true if pointer started on interactive control const [tx, setTx] = React.useState(0); const [dragging, setDragging] = React.useState(false); const onPointerDown = (e: React.PointerEvent<HTMLDivElement>) => { startX.current = e.clientX; startY.current = e.clientY; moved.current = false; + // ignore tap if started on an interactive element + const targetEl = e.target as HTMLElement; + ignoreTapRef.current = !!targetEl.closest( + 'button,a,[role="button"],input,select,textarea,[data-interactive],[data-no-tap]' + ); setDragging(true); e.currentTarget.setPointerCapture?.(e.pointerId); }; @@ const endDrag = () => { if (!dragging) return; const didSwipe = tx >= THRESHOLD; if (didSwipe) { - onDismiss(); // 스와이프 → 제거 + suppressClickRef.current = true; + onDismiss(); // 스와이프 → 제거 } else if (!moved.current) { - onTap?.(); // 거의 안 움직였으면 → 탭으로 처리 + if (!ignoreTapRef.current) onTap?.(); // 거의 안 움직였으면 → 탭으로 처리(단, 버튼 등은 제외) } setTx(0); setDragging(false); + ignoreTapRef.current = false; }; @@ // 스와이프 후 발생하는 "유령 클릭" 1회 차단(탭은 통과) const onClickCapture = (e: React.MouseEvent) => { - if (tx >= THRESHOLD) { + if (suppressClickRef.current) { e.stopPropagation(); e.preventDefault(); + (e.nativeEvent as any)?.stopImmediatePropagation?.(); + suppressClickRef.current = false; } };Also applies to: 54-65, 67-73
🧹 Nitpick comments (5)
apps/nowait-admin/src/components/NewOrderToast.tsx (5)
121-124: Make total computation resilient to string inputs.API payloads often carry numbers as strings; coerce defensively to avoid NaN totals.
- const { quantity = 0, price = 0 } = menuDetails[name] ?? {}; - return sum + quantity * price; + const { quantity = 0, price = 0 } = menuDetails[name] ?? {}; + const q = Number(quantity) || 0; + const p = Number(price) || 0; + return sum + q * p;
110-116: Avoid navigating to a trailing-slash route when storeId is empty; prefer meta.storeId fallback.- const tableId = t.meta?.tableId as number | undefined; + const tableId = t.meta?.tableId as number | undefined; const orderName = t.meta?.orderName ?? "신규 주문"; const menuDetails = (t.meta?.menuDetails ?? {}) as Record< string, { quantity?: number; price?: number } >; + const navStoreId = (t.meta?.storeId as string | undefined) ?? storeId; @@ - onTap={() => navigate(`/admin/orders/${storeId}`)} // ← 클릭 이동은 여기서! + onTap={() => + navigate(navStoreId ? `/admin/orders/${navStoreId}` : `/admin/orders`) + } // ← 클릭 이동은 여기서!Please confirm expected route when no storeId exists.
Also applies to: 137-138
153-160: Add accessible label to CloseButton.<CloseButton width={9.41} height={9.41} + aria-label="알림 닫기" onClick={(e) => { e.stopPropagation(); removeToast(t.id); }} />
181-182: Hoist keyframes to CSS; avoid per-toast <style> duplication.- <style>{`@keyframes toastIn{to{transform:translateY(0);opacity:1}}`}</style> + {/* keyframes moved to global CSS */}Add to apps/nowait-admin/src/global.css:
@keyframes toastIn { to { transform: translateY(0); opacity: 1 } }
103-107: Ensure single audio element per page.This component renders a fixed-id audio element. If NewOrderToast mounts in more than one place, IDs will duplicate. Consider lifting the audio tag to a top-level singleton or gating with a feature flag.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
apps/nowait-admin/src/components/NewOrderToast.tsx(1 hunks)apps/nowait-admin/src/pages/AdminBooth/components/AccountPage.tsx(3 hunks)apps/nowait-admin/src/pages/AdminBooth/components/MenuSection.tsx(4 hunks)apps/nowait-admin/src/pages/AdminBooth/components/Swipe/SwipeableRow.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- apps/nowait-admin/src/pages/AdminBooth/components/Swipe/SwipeableRow.tsx
- apps/nowait-admin/src/pages/AdminBooth/components/AccountPage.tsx
- apps/nowait-admin/src/pages/AdminBooth/components/MenuSection.tsx
feat: user부분 Sentry 도입
작업 내용
문제점 및 어려움
해결 방안
공유 사항
Summary by CodeRabbit
New Features
Improvements
Style
Chores