Skip to content

[Feat/#73] 사장님 홈화면 QR 적립 모달창 생성#74

Merged
skyblue1232 merged 1 commit intodevelopfrom
feat/#73/owner-home-qr
Mar 29, 2026
Merged

[Feat/#73] 사장님 홈화면 QR 적립 모달창 생성#74
skyblue1232 merged 1 commit intodevelopfrom
feat/#73/owner-home-qr

Conversation

@skyblue1232
Copy link
Copy Markdown
Contributor

@skyblue1232 skyblue1232 commented Mar 29, 2026

✅ 작업 내용

📝 Description

사장님 홈 화면에서 QR 적립용 풀스크린 모달을 구현했습니다.
카메라를 배경으로 사용하고, 중앙 스캔 가이드와 적립 버튼을 오버레이 형태로 배치했습니다.

작업한 내용을 체크해주세요.

  • QR 적립 모달 UI 구현
  • 카메라 스트림 연결 및 종료 처리
  • 중앙 스캔 가이드 영역 구현
  • 배경 dim 처리 및 버튼 배치

🚀 설계 의도 및 개선점

  • 라우팅 없이도 페이지처럼 보이도록 풀스크린 모달 구조로 구현
  • 카메라, dim, UI를 레이어로 분리해 구조를 단순하게 정리
  • 스캔 가이드와 오버레이 기준을 맞춰 중앙 투명 영역이 정확히 보이도록 수정
  • 가이드 코너 두께를 0.738rem로 조정해 디자인에 맞춤

📸 스크린샷 (선택)

  • QR 적립 모달 화면
image

📎 기타 참고사항

  • 카메라 권한 허용 필요
  • 모달 종료 시 카메라 스트림 정리 처리

Fixes #73

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 스탬프 아이콘을 클릭하여 QR 적립 모달을 열 수 있습니다.
    • QR 스캔을 위한 카메라 기능이 추가되었습니다.
  • 사용자 인터페이스

    • 스탬프 아이콘이 상호작용 가능하도록 업데이트되었습니다.
    • QR 적립 모달에 스캔 가이드와 오류 메시지 처리가 포함되었습니다.

@skyblue1232 skyblue1232 self-assigned this Mar 29, 2026
@skyblue1232 skyblue1232 added feat 기능 구현 및 생성 style 스타일 관련 적용 labels Mar 29, 2026
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 29, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
compasser-customer Ready Ready Preview, Comment Mar 29, 2026 8:53am
compasser-owner Ready Ready Preview, Comment Mar 29, 2026 8:53am

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 29, 2026

📝 Walkthrough

Walkthrough

새로운 QR 적립 모달 기능을 구현했습니다. CafeIntro 컴포넌트에 모달 상태 관리를 추가하고 스탬프 아이콘을 클릭 가능하게 수정했으며, 카메라 접근 및 비디오 스트림을 처리하는 QRStampModal 컴포넌트를 새로 생성했습니다.

Changes

Cohort / File(s) Summary
CafeIntro 컴포넌트 통합
apps/owner/src/app/(tabs)/main/_components/CafeIntro.tsx
isQrModalOpen 상태 추가, 모달 열기/닫기 핸들러 구현, 스탬프 아이콘을 대화형으로 변경, QRStampModal 컴포넌트와 연결. formatCafeName 들여쓰기 조정.
QRStampModal 새 컴포넌트
apps/owner/src/app/(tabs)/main/_components/QRStampModal.tsx
카메라 접근 요청(navigator.mediaDevices.getUserMedia), 비디오 스트림 재생, 카메라 접근 실패 시 에러 표시. 스캔 프레임 및 코너 가이드 UI, 뷰포트 정리(useEffect cleanup).

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant CafeIntro
    participant QRStampModal
    participant Browser as Browser<br/>(MediaDevices API)

    User->>CafeIntro: 스탬프 아이콘 클릭
    CafeIntro->>CafeIntro: isQrModalOpen = true
    CafeIntro->>QRStampModal: open={true} 전달
    QRStampModal->>QRStampModal: useEffect 실행
    QRStampModal->>Browser: getUserMedia({video, audio:false})
    alt 카메라 접근 성공
        Browser-->>QRStampModal: MediaStream 반환
        QRStampModal->>QRStampModal: video.srcObject 설정
        QRStampModal->>QRStampModal: video.play() 실행
        QRStampModal-->>User: 카메라 피드 표시
    else 카메라 접근 실패
        Browser-->>QRStampModal: 에러 반환
        QRStampModal-->>User: 에러 메시지 표시
    end
    User->>QRStampModal: "적립하기" 버튼 클릭
    QRStampModal->>QRStampModal: onSubmit() 호출
    User->>QRStampModal: 닫기 버튼 클릭
    QRStampModal->>QRStampModal: cleanup - 모든 트랙 정지
    QRStampModal->>CafeIntro: onClose() 호출
    CafeIntro->>CafeIntro: isQrModalOpen = false
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 스탬프를 누르면 카메라 켜지고,
QR 코드를 향해 렌즈가 열리네요.
적립을 위한 새 모달, 반짝반짝!
토끼도 춤을 춘답니다! ✨📱

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

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.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed 제목은 PR의 주요 변경사항을 명확하게 요약하고 있습니다. 사장님 홈화면에 QR 적립 모달창 생성이라는 핵심 기능 추가를 정확히 반영합니다.
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
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#73/owner-home-qr

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
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: 2

🧹 Nitpick comments (3)
apps/owner/src/app/(tabs)/main/_components/QRStampModal.tsx (2)

63-68: 중복된 카메라 스트림 정리 로직

useEffect는 첫 번째 useEffect(lines 28-61)의 cleanup 함수와 동일한 작업을 수행합니다. openfalse로 변경되면 첫 번째 effect의 cleanup이 자동으로 실행되므로, 이 effect는 불필요합니다.

♻️ 중복 useEffect 제거
-  useEffect(() => {
-    if (!open && streamRef.current) {
-      streamRef.current.getTracks().forEach((track) => track.stop());
-      streamRef.current = null;
-    }
-  }, [open]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/owner/src/app/`(tabs)/main/_components/QRStampModal.tsx around lines 63
- 68, Remove the duplicate useEffect that runs on [open] and stops tracks on
streamRef.current; the cleanup is already handled in the earlier useEffect's
cleanup, so delete the second effect (the one that checks if (!open &&
streamRef.current) and calls getTracks().forEach(track => track.stop()) and sets
streamRef.current = null) and keep the existing cleanup logic in the original
useEffect so camera tracks are stopped only once.

72-129: 풀스크린 모달 접근성 개선 권장

풀스크린 모달에서 다음 접근성 기능 추가를 권장합니다:

  1. ESC 키로 모달 닫기: 키보드 사용자를 위한 표준 패턴
  2. Focus trap: 모달 내부로 포커스 제한
  3. ARIA 속성: role="dialog", aria-modal="true", aria-labelledby
♿ ESC 키 핸들링 예시
useEffect(() => {
  if (!open) return;
  
  const handleKeyDown = (e: KeyboardEvent) => {
    if (e.key === "Escape") {
      onClose();
    }
  };
  
  document.addEventListener("keydown", handleKeyDown);
  return () => document.removeEventListener("keydown", handleKeyDown);
}, [open, onClose]);

루트 <div>에 ARIA 속성 추가:

-    <div className="fixed inset-0 z-[999] bg-black">
+    <div 
+      role="dialog"
+      aria-modal="true"
+      aria-labelledby="qr-modal-title"
+      className="fixed inset-0 z-[999] bg-black"
+    >
-              <h1 className="head2-sb text-primary-variant">적립</h1>
+              <h1 id="qr-modal-title" className="head2-sb text-primary-variant">적립</h1>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/owner/src/app/`(tabs)/main/_components/QRStampModal.tsx around lines 72
- 129, This modal (QRStampModal component) needs keyboard/ARIA accessibility:
add an effect in QRStampModal that when open mounts it attaches a keydown
handler to call onClose() on Escape and restores cleanup; implement a focus trap
so focus is constrained inside the modal while open (ensure initial focus moves
into the modal and returned to the previously focused element on close) and
ensure the close button (onClose) is focusable; add ARIA attributes to the root
modal container (role="dialog", aria-modal="true") and give the title element
(the h1 with text "적립") an id referenced by aria-labelledby on the root so
assistive tech announces the dialog. Use existing symbols (onClose, onSubmit,
videoRef, cameraError, ScanFrame, SCAN_BOX_SIZE) to locate the component code
and wire these changes.
apps/owner/src/app/(tabs)/main/_components/CafeIntro.tsx (1)

48-50: handleSubmitStamp에서 QR 스캔 데이터 처리 로직 누락

현재 handleSubmitStamp는 콘솔 로그만 출력하며, QRStampModalonSubmit prop도 파라미터 없이 정의되어 있습니다. 실제 QR 코드 스캔 결과를 전달받아 처리하는 로직이 필요합니다.

향후 QR 스캔 라이브러리 연동 시 인터페이스 변경이 필요합니다:

// QRStampModal의 interface 변경 필요
onSubmit?: (qrData: string) => void;

QR 코드 디코딩 및 데이터 전달 로직 구현을 도와드릴까요?

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

In `@apps/owner/src/app/`(tabs)/main/_components/CafeIntro.tsx around lines 48 -
50, handleSubmitStamp currently only logs and doesn’t accept QR data; update the
QR handling by changing handleSubmitStamp to accept a string parameter (e.g.,
qrData) and implement processing/validation logic (parse/validate qrData, call
existing stamp API or state updater, handle errors and success feedback), then
pass that handler into QRStampModal via the onSubmit prop so
QRStampModal.onSubmit?: (qrData: string) => void will receive the scanned
string; locate and modify the handleSubmitStamp function and the QRStampModal
usage in CafeIntro.tsx to wire the qrData through and add basic try/catch +
success/error handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/owner/src/app/`(tabs)/main/_components/QRStampModal.tsx:
- Around line 104-111: Remove the isInteractive prop from the Icon rendered
inside the button to avoid nested interactive elements; locate the Icon
component usage within the QRStampModal (the <Icon name="CloseButton" ... />
instance) and delete the isInteractive attribute so the outer <button> remains
the sole interactive control.
- Around line 44-47: The startCamera function risks a race where after awaiting
getUserMedia the component unmounted or open became false and videoRef.current
is null; update startCamera to capture any necessary state/ref into local
variables before awaiting (e.g., const ref = videoRef.current and const isOpen =
open), then after await verify that ref is still non-null and isOpen is still
true before assigning ref.srcObject and calling ref.play; if the check fails,
immediately stop/cleanup the obtained MediaStream tracks
(stream.getTracks().forEach(t => t.stop())) to avoid leaking the camera and
return early. Ensure all references in this logic mention startCamera, videoRef,
stream and open so reviewers can find and apply the fix.

---

Nitpick comments:
In `@apps/owner/src/app/`(tabs)/main/_components/CafeIntro.tsx:
- Around line 48-50: handleSubmitStamp currently only logs and doesn’t accept QR
data; update the QR handling by changing handleSubmitStamp to accept a string
parameter (e.g., qrData) and implement processing/validation logic
(parse/validate qrData, call existing stamp API or state updater, handle errors
and success feedback), then pass that handler into QRStampModal via the onSubmit
prop so QRStampModal.onSubmit?: (qrData: string) => void will receive the
scanned string; locate and modify the handleSubmitStamp function and the
QRStampModal usage in CafeIntro.tsx to wire the qrData through and add basic
try/catch + success/error handling.

In `@apps/owner/src/app/`(tabs)/main/_components/QRStampModal.tsx:
- Around line 63-68: Remove the duplicate useEffect that runs on [open] and
stops tracks on streamRef.current; the cleanup is already handled in the earlier
useEffect's cleanup, so delete the second effect (the one that checks if (!open
&& streamRef.current) and calls getTracks().forEach(track => track.stop()) and
sets streamRef.current = null) and keep the existing cleanup logic in the
original useEffect so camera tracks are stopped only once.
- Around line 72-129: This modal (QRStampModal component) needs keyboard/ARIA
accessibility: add an effect in QRStampModal that when open mounts it attaches a
keydown handler to call onClose() on Escape and restores cleanup; implement a
focus trap so focus is constrained inside the modal while open (ensure initial
focus moves into the modal and returned to the previously focused element on
close) and ensure the close button (onClose) is focusable; add ARIA attributes
to the root modal container (role="dialog", aria-modal="true") and give the
title element (the h1 with text "적립") an id referenced by aria-labelledby on the
root so assistive tech announces the dialog. Use existing symbols (onClose,
onSubmit, videoRef, cameraError, ScanFrame, SCAN_BOX_SIZE) to locate the
component code and wire these changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 62cb6451-785f-4430-8311-35fe90706f13

📥 Commits

Reviewing files that changed from the base of the PR and between df3eda0 and d1fe2dd.

⛔ Files ignored due to path filters (2)
  • packages/design-system/src/icons/generated/spriteSymbols.ts is excluded by !**/generated/**
  • packages/design-system/src/icons/source/CloseButton.svg is excluded by !**/*.svg
📒 Files selected for processing (2)
  • apps/owner/src/app/(tabs)/main/_components/CafeIntro.tsx
  • apps/owner/src/app/(tabs)/main/_components/QRStampModal.tsx

@skyblue1232 skyblue1232 merged commit d3fe0b7 into develop Mar 29, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 기능 구현 및 생성 style 스타일 관련 적용

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] 사장님 홈화면 QR 적립 모달창 구현

1 participant