Skip to content

Conversation

@hamxxn
Copy link
Contributor

@hamxxn hamxxn commented Aug 20, 2025

📌 Related Issues

✅ 체크 리스트

  • PR 제목의 형식을 잘 작성했나요? e.g. [Feat/#이슈번호] PR 템플릿 작성
  • 빌드가 성공했나요? (pnpm build)
  • 컨벤션을 지켰나요?
  • 이슈는 등록했나요?
  • 리뷰어와 라벨을 지정했나요?

📄 Tasks

⭐ PR Point (To Reviewer)

📷 Screenshot

스크린샷 2025-08-20 오후 10 06 55 스크린샷 2025-08-20 오후 10 07 28 스크린샷 2025-08-20 오후 10 07 59

🔔 ETC

Summary by CodeRabbit

  • 신기능
    • 약속 상태 화면에 ‘투표’와 ‘확정’ UI를 추가했습니다.
    • 장소·시간 선택 및 확인 버튼 제공, 선택 상태에 따라 버튼 활성화.
    • ‘확정’ 화면에서 약속 정보와 지도 미리보기(장소 중심) 제공.
    • 투표/확정 완료 시 홈으로 이동하는 내비게이션 동작 추가.
    • 호스트는 투표가 불가함을 명확히 안내.
  • 스타일
    • 투표/확정 화면의 레이아웃, 버튼, 고정 하단 액션 영역 등 시각적 구성을 개선했습니다.

@coderabbitai
Copy link

coderabbitai bot commented Aug 20, 2025

Walkthrough

PromiseStatus integrates updated Confirmed (prop change: remove isHost) and a new Voting implementation. Added Vanilla Extract styles for both pages. Implemented interactive UIs: Voting (selection, validation, navigation) and Confirmed (map lookup, details, navigation). Introduced mockUpVoting and mockUpConfirmed datasets.

Changes

Cohort / File(s) Summary
PromiseStatus 통합
src/pages/promiseStatus/PromiseStatus.tsx
Confirmed 호출 시 isHost 제거, 절대 경로 import로 변경.
Confirmed 기능 구현
src/pages/promiseStatus/components/Confirmed.tsx, src/pages/promiseStatus/components/Confirmed.css.ts
Confirmed 컴포넌트 전체 구현: mock 데이터 사용, 장소 좌표 조회, KakaoMap 렌더, PromiseDetail 구성, 확인 버튼으로 홈 이동. Props에서 isHost 제거. 신규 스타일 토큰 추가.
Voting 기능 구현
src/pages/promiseStatus/components/Voting.tsx, src/pages/promiseStatus/components/Voting.css.ts
Voting 컴포넌트 전체 구현: 장소/시간 토글 선택, 유효성 검사, 투표 후 네비게이션. 하단 고정 버튼 및 레이아웃 스타일 추가.
모의데이터 추가
src/pages/promiseStatus/mockUp.ts
mockUpVoting, mockUpConfirmed 신규 export: Voting/Confirmed용 이름, 장소, 시간, 멤버명(Confirmed) 데이터 제공.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant PromiseStatus
  participant Confirmed
  participant PlaceSearch as usePlaceSearch
  participant KakaoMap

  User->>PromiseStatus: 상태=CONFIRMED 페이지 진입
  PromiseStatus->>Confirmed: render({ promiseId })
  Confirmed->>PlaceSearch: 검색(placeAddress)
  PlaceSearch-->>Confirmed: 좌표(성공/실패)
  alt 좌표 존재
    Confirmed->>KakaoMap: render(center=좌표)
  else 좌표 없음
    Note over Confirmed: 지도 center 미설정
  end
  User->>Confirmed: "확인" 클릭
  Confirmed->>PromiseStatus: navigate("/")
Loading
sequenceDiagram
  autonumber
  actor User
  participant PromiseStatus
  participant Voting
  Note over Voting: isHost 여부 확인
  User->>PromiseStatus: 상태=VOTING 페이지 진입
  PromiseStatus->>Voting: render({ promiseId, isHost })
  Voting->>Voting: 선택 상태 관리(장소/시간 토글)
  User->>Voting: 장소/시간 선택
  Voting->>Voting: isFormValid 계산
  alt isHost 또는 미선택
    Voting->>User: 버튼 비활성화
  else 유효
    User->>Voting: "투표하기" 클릭
    Voting->>PromiseStatus: navigate("/")
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Assessment against linked issues

Objective Addressed Explanation
Voting page 구현 (#106)
Confirm page 구현 (#106)

Assessment against linked issues: Out-of-scope changes

(해당 없음)

Possibly related PRs

Suggested reviewers

  • junghyungo
  • Hwanggyuun

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/#106/voting-confirm-page

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions
Copy link

✅ 빌드에 성공했습니다! 🎉

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

🧹 Nitpick comments (10)
src/pages/promiseStatus/mockUp.ts (2)

101-123: 투표 가능 시간이 모두 동일 — 예시 데이터라도 구간을 다르게 두면 UX 검증에 유용합니다

샘플이라도 서로 다른 시간대로 구성하는 편이 실제 UI/로직 검증(정렬/선택/중복 처리)에 도움이 됩니다.

아래처럼 2~4번째 슬롯의 시간대를 변경하는 것을 제안드립니다:

   promiseAvailableTimes: [
     {
       date: '2025-04-03',
       startTime: '12:00:00',
       endTime: '12:30:00',
     },
     {
-      date: '2025-04-03',
-      startTime: '12:00:00',
-      endTime: '12:30:00',
+      date: '2025-04-03',
+      startTime: '12:30:00',
+      endTime: '13:00:00',
     },
     {
-      date: '2025-04-03',
-      startTime: '12:00:00',
-      endTime: '12:30:00',
+      date: '2025-04-03',
+      startTime: '13:00:00',
+      endTime: '13:30:00',
     },
     {
-      date: '2025-04-03',
-      startTime: '12:00:00',
-      endTime: '12:30:00',
+      date: '2025-04-03',
+      startTime: '13:30:00',
+      endTime: '14:00:00',
     },
   ],

80-81: 목업에도 타입을 명시해 드리면 추후 스키마 드리프트를 줄일 수 있습니다

간단한 로컬 타입을 두고 상수에 타입을 지정해두면 컴포넌트/어댑터 변경 시 컴파일 타임에 빨리 잡힙니다.

-export const mockUpVoting = {
+export const mockUpVoting: {
+  promiseName: string;
+  promisePlace: { placeName: string; placeAddress: string }[];
+  promiseAvailableTimes: { date: string; startTime: string; endTime: string }[];
+} = {
   ...
-export const mockUpConfirmed = {
+export const mockUpConfirmed: {
+  promiseName: string;
+  promisePlace: { placeName: string; placeAddress: string };
+  promiseAvailableTimes: { date: string; startTime: string; endTime: string };
+  promiseMembersNames: string[];
+} = {

추가로, 파일 상단(또는 인접 위치)에 아래 보조 타입 선언을 두면 가독성이 좋아집니다:

type MockTimeSlot = { date: string; startTime: string; endTime: string };
type MockPlace = { placeName: string; placeAddress: string };

Also applies to: 125-126

src/pages/promiseStatus/components/Confirmed.css.ts (1)

3-10: 하단 고정 버튼과의 겹침/안전영역 이슈 대비 패딩 보완 제안

고정(fixed) 버튼과 콘텐츠 겹침, iOS 안전영역 미반영을 피하기 위해 wrapper/버튼에 safe-area 패딩을 권장합니다.

 export const confirmedWrapper = style({
   position: 'relative',
   display: 'flex',
   flexDirection: 'column',
   alignItems: 'center',
   paddingTop: '9.8rem',
   height: '100vh',
+  paddingBottom: 'calc(env(safe-area-inset-bottom) + 8rem)',
 });
 
 export const confirmedButton = style({
   position: 'fixed',
   bottom: '0',
   width: '100%',
-  padding: '1.5rem',
+  padding: '1.5rem',
+  paddingBottom: 'calc(1.5rem + env(safe-area-inset-bottom))',
 });

Also applies to: 34-39

src/pages/promiseStatus/components/Voting.css.ts (1)

12-20: 중복 배경 제거 및 하단 안전영역 패딩 추가 제안

wrapper와 container의 배경이 중복됩니다. 또한 sticky 버튼의 안전영역 패딩을 추가하면 iOS에서 더 안전합니다.

 export const containerStyle = style({
   width: '100%',
   display: 'flex',
   flexDirection: 'column',
   alignItems: 'center',
   gap: '40px',
   minHeight: 'calc(100vh - 9.8rem - 8rem)',
-  background: vars.color.blue0,
 });
 
 export const votingButton = style({
   position: 'sticky',
   bottom: '0',
   left: '0',
   right: '0',
   width: '100%',
-  padding: '1.5rem',
+  padding: '1.5rem',
+  paddingBottom: 'calc(1.5rem + env(safe-area-inset-bottom))',
 });

Also applies to: 45-52

src/pages/promiseStatus/components/Confirmed.tsx (2)

56-58: 숫자 파싱 명시화(parseInt radix) 또는 Number 사용 권장

기본 동작에 의존하기보다 radix를 명시하거나 Number를 쓰는 편이 안전합니다. 빈 문자열 가능성도 고려해 주세요.

           <PromiseDetail
             status={PROMISE_STATUS.CONFIRMED}
-            promiseId={parseInt(promiseId)}
+            promiseId={parseInt(promiseId, 10)}
             promiseName={promiseName}

추가로, promiseId가 없을 경우 NaN이 될 수 있습니다. 라우터에서 필수 파라미터 보장을 하거나, 이 컴포넌트에서 가드 로직을 두는지 확인 부탁드립니다.


24-35: 비동기 처리 중 언마운트 가드 추가(메모리 경고 예방)

언마운트 후 setState 호출을 피하기 위한 가드를 권장합니다.

   useEffect(() => {
-    searchPlace(promisePlace.placeAddress)
+    let isMounted = true;
+    searchPlace(promisePlace.placeAddress)
       .then(res => res[0])
       .then(placeData => {
-        if (placeData) {
-          setPlace({ lat: placeData.lat, lng: placeData.lng });
+        if (placeData && isMounted) {
+          setPlace({ lat: placeData.lat, lng: placeData.lng });
         }
       })
       .catch(error => {
         console.error('Failed to search place:', error);
       });
-  }, [searchPlace, promisePlace.placeAddress]);
+    return () => {
+      isMounted = false;
+    };
+  }, [searchPlace, promisePlace.placeAddress]);
src/pages/promiseStatus/components/Voting.tsx (4)

19-22: 콘솔 사용 규칙 준수: console.log → console.info로 변경 또는 제거

빌드 체크에서 console.log가 경고로 보고됩니다. info 수준으로 변경하거나 제거하세요.

   const handleClose = () => {
-    console.log(promiseId);
+    console.info(`[Voting] close clicked: promiseId=${promiseId}`);
     navigate(`/`);
   };

38-44: 호스트는 상호작용 자체가 막히는 것이 UX에 더 적합

현재 호스트도 장소/시간 버튼을 눌러 선택 토글이 됩니다(버튼만 비활성처럼 보임). 호스트일 때 클릭 핸들러 초입에서 바로 반환하여 상호작용을 차단하는 것이 명확합니다.

   const handlePlaceClick = (place: { placeName: string; placeAddress: string }) => {
+    if (isHost) return;
     if (selectedPlace.includes(place)) {
       setSelectedPlace(prev => prev.filter(p => p !== place));
     } else {
       setSelectedPlace(prev => [...prev, place]);
     }
   };

   const handleTimeClick = (time: { date: string; startTime: string; endTime: string }) => {
+    if (isHost) return;
     if (selectedTime.includes(time)) {
-      setSelectedTime(prev => prev.filter(t => t !== time));
+      setSelectedTime(prev => prev.filter(t => t !== time));
     } else {
       setSelectedTime(prev => [...prev, time]);
     }
   };

Also applies to: 46-56


88-101: 키 안정성 개선(경미): placeName만 키로 쓰는 것은 잠재적 충돌 리스크

실데이터에서 동일한 placeName이 들어올 수 있습니다. 인덱스를 덧붙여 충돌을 예방하세요. 시간 리스트와 일관성도 좋아집니다.

-            {promisePlace.map(place => {
+            {promisePlace.map((place, idx) => {
               const isSelected = selectedPlace.includes(place);
               return (
                 <Button
-                  key={place.placeName}
+                  key={`${place.placeName}-${idx}`}
                   onClick={() => handlePlaceClick(place)}

58-61: 투표하기 버튼 disable 상태에서도 클릭이 가능함 — 안전장치 추가 권장

현재 BUTTON_VARIANTS.DISABLED는 스타일만 제어하며, 실제로는 <button>disabled 속성을 설정하지 않아 클릭 시에도 handleVote가 호출됩니다.
방어적으로 클릭 시점에 유효성 검사를 추가해 disable 상태일 때 아무 동작도 하지 않도록 해주세요.
또한 console.log 대신 console.info를 사용해 lint 경고를 제거합니다.

   const handleVote = () => {
-    console.log(selectedPlace, selectedTime);
-    handleClose();
+    // disable 상태 클릭 방지
+    if (!isFormValid) return;
+    console.info('[Voting] selections', { selectedPlace, selectedTime });
+    handleClose();
   };

   // 이미 정의된 유효성 검사 변수
   const isFormValid = !isHost && selectedPlace.length > 0 && selectedTime.length > 0;
📜 Review details

Configuration used: .coderabbit.yaml
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.

📥 Commits

Reviewing files that changed from the base of the PR and between 55df69e and 848eb91.

📒 Files selected for processing (6)
  • src/pages/promiseStatus/PromiseStatus.tsx (2 hunks)
  • src/pages/promiseStatus/components/Confirmed.css.ts (1 hunks)
  • src/pages/promiseStatus/components/Confirmed.tsx (1 hunks)
  • src/pages/promiseStatus/components/Voting.css.ts (1 hunks)
  • src/pages/promiseStatus/components/Voting.tsx (1 hunks)
  • src/pages/promiseStatus/mockUp.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (6)
src/pages/promiseStatus/components/Voting.css.ts (1)
src/shared/styles/theme.css.ts (1)
  • vars (3-64)
src/pages/promiseStatus/components/Confirmed.css.ts (2)
src/shared/components/promiseContainer/types/PromiseContainer.type.ts (1)
  • ConfirmedPromise (26-32)
api/data-contracts.ts (2)
  • ConfirmedPromiseResponseDTO (864-892)
  • PromiseStatusConfirmedPromiseResponseDTO (821-849)
src/pages/promiseStatus/PromiseStatus.tsx (2)
src/pages/promiseStatus/components/Confirmed.tsx (1)
  • Confirmed (18-76)
src/pages/promiseStatus/components/Pending.tsx (2)
  • Pending (28-114)
  • PendingProps (18-21)
src/pages/promiseStatus/mockUp.ts (3)
api/data-contracts.ts (2)
  • ConfirmedPromiseResponseDTO (864-892)
  • VotingPromiseResponseDTO (942-973)
src/shared/types/promisePlace.ts (1)
  • PromisePlace (1-4)
src/shared/components/promiseContainer/types/PromiseContainer.type.ts (2)
  • VotingPromise (17-24)
  • ConfirmedPromise (26-32)
src/pages/promiseStatus/components/Confirmed.tsx (8)
src/pages/promiseStatus/mockUp.ts (1)
  • mockUpConfirmed (125-138)
src/shared/components/container/Container.tsx (1)
  • Container (9-15)
src/shared/components/text/Text.tsx (1)
  • Text (57-69)
src/shared/components/kakaoMap/constant/mapSize.ts (1)
  • MAP_SIZE (1-5)
src/shared/components/promiseDetail/PromiseDetail.tsx (1)
  • PromiseDetail (9-45)
src/shared/constant/promiseStatus.ts (2)
  • PROMISE_STATUS (3-7)
  • PROMISE_STATUS_CONFIG (9-22)
src/shared/components/button/constant/button.ts (1)
  • BUTTON_VARIANTS (1-6)
src/shared/components/promiseContainer/types/PromiseContainer.type.ts (1)
  • ConfirmedPromise (26-32)
src/pages/promiseStatus/components/Voting.tsx (6)
src/pages/promiseStatus/mockUp.ts (1)
  • mockUpVoting (80-123)
src/shared/components/header/Header.tsx (1)
  • Header (17-46)
src/shared/components/container/Container.tsx (1)
  • Container (9-15)
src/shared/components/text/Text.tsx (1)
  • Text (57-69)
src/shared/components/button/constant/button.ts (1)
  • BUTTON_VARIANTS (1-6)
src/shared/constant/promiseStatus.ts (1)
  • PROMISE_STATUS_CONFIG (9-22)
🪛 GitHub Check: build
src/pages/promiseStatus/components/Confirmed.tsx

[warning] 38-38:
Unexpected console statement. Only these console methods are allowed: warn, error, info

src/pages/promiseStatus/components/Voting.tsx

[warning] 59-59:
Unexpected console statement. Only these console methods are allowed: warn, error, info


[warning] 20-20:
Unexpected console statement. Only these console methods are allowed: warn, error, info

🔇 Additional comments (2)
src/pages/promiseStatus/PromiseStatus.tsx (2)

2-5: Confirmed 컴포넌트 prop 단순화 및 절대 경로 import 전환 👍

Confirmed에 promiseId만 전달하도록 변경한 점과 alias import로 일관성 확보한 점 좋습니다.

Also applies to: 17-17


16-16: Voting 컴포넌트에서 isHost prop 유지 필요

VotingProps 정의 및 컴포넌트 내부에서 호스트 제어 로직에 isHost를 사용하고 있어, 전달을 유지해야 합니다.

  • src/pages/promiseStatus/components/Voting.tsx
    • Props 정의: isHost: boolean (line 15)
    • isFormValid 계산: !isHost && … (line 63)
    • 호스트 경고 메시지 렌더링: {isHost && …} (lines 76–78)

위 로직이 호스트의 투표 제한 및 경고 메시지 출력에 필요하므로, 호출부에서 isHost 전달을 그대로 유지해주세요.

Comment on lines +37 to +40
const handleClose = () => {
console.log(promiseId);
navigate(`/`);
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

불필요한 console.log 제거 (빌드 체크 경고 해결)

체크 도구가 log를 금지합니다. 필요 없다면 제거하거나 info로 교체하세요.

   const handleClose = () => {
-    console.log(promiseId);
     navigate(`/`);
   };
📝 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
const handleClose = () => {
console.log(promiseId);
navigate(`/`);
};
const handleClose = () => {
navigate(`/`);
};
🧰 Tools
🪛 GitHub Check: build

[warning] 38-38:
Unexpected console statement. Only these console methods are allowed: warn, error, info

🤖 Prompt for AI Agents
In src/pages/promiseStatus/components/Confirmed.tsx around lines 37 to 40,
remove the unnecessary console.log(promiseId) to satisfy the lint/build rule
that forbids console.* calls; if the information is needed, replace it with the
project's logging utility (e.g., logger.info) or dispatch it to a proper
debug/logging mechanism, then keep the navigate(`/`) call as-is.

Comment on lines +46 to +56
const handleTimeClick = (time: { date: string; startTime: string; endTime: string }) => {
if (selectedTime.includes(time)) {
setSelectedTime(prev =>
prev.filter(
t => t.date !== time.date && t.startTime !== time.startTime && t.endTime !== time.endTime
)
);
} else {
setSelectedTime(prev => [...prev, time]);
}
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

선택 해제 로직 버그: 동일 날짜/시간이 한 필드만 같아도 전부 제거됩니다

시간 선택 토글 해제 시 필터 조건이 !==&&로 묶고 있어, 한 필드(예: date)만 같아도 해당 항목이 제거됩니다. 동일 항목만 제거하려면 참조 비교 또는 3개 필드 모두의 일치 여부를 기준으로 제거해야 합니다.

다음과 같이 참조 비교로 간단히 수정하는 것을 권장합니다(상단의 includes 로직과도 일관적):

   const handleTimeClick = (time: { date: string; startTime: string; endTime: string }) => {
     if (selectedTime.includes(time)) {
-      setSelectedTime(prev =>
-        prev.filter(
-          t => t.date !== time.date && t.startTime !== time.startTime && t.endTime !== time.endTime
-        )
-      );
+      setSelectedTime(prev => prev.filter(t => t !== time));
     } else {
       setSelectedTime(prev => [...prev, time]);
     }
   };
📝 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
const handleTimeClick = (time: { date: string; startTime: string; endTime: string }) => {
if (selectedTime.includes(time)) {
setSelectedTime(prev =>
prev.filter(
t => t.date !== time.date && t.startTime !== time.startTime && t.endTime !== time.endTime
)
);
} else {
setSelectedTime(prev => [...prev, time]);
}
};
const handleTimeClick = (time: { date: string; startTime: string; endTime: string }) => {
if (selectedTime.includes(time)) {
setSelectedTime(prev => prev.filter(t => t !== time));
} else {
setSelectedTime(prev => [...prev, time]);
}
};
🤖 Prompt for AI Agents
In src/pages/promiseStatus/components/Voting.tsx around lines 46 to 56, the
deselect filter currently removes items if any single field differs because it
uses combined !== checks with &&; change the removal logic so it only removes
the exact same selected time — either by using reference equality consistent
with the includes check (compare objects directly) or by keeping value
comparison but only exclude items where all three fields match (date AND
startTime AND endTime); update the filter predicate accordingly so other items
with a matching single field are not removed.

Comment on lines +76 to +80
{isHost && (
<Text tag="body_bold_16" color="red1">
약속 생성자는 투표 할 수 없습니다.
</Text>
)}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

사용자 노출 문구 오탈자: ‘투표 할’ → ‘투표할’

사용자에게 직접 노출되는 문구의 띄어쓰기를 수정하세요.

-            <Text tag="body_bold_16" color="red1">
-              약속 생성자는 투표 할 수 없습니다.
-            </Text>
+            <Text tag="body_bold_16" color="red1">
+              약속 생성자는 투표할 수 없습니다.
+            </Text>
📝 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
{isHost && (
<Text tag="body_bold_16" color="red1">
약속 생성자는 투표 없습니다.
</Text>
)}
{isHost && (
<Text tag="body_bold_16" color="red1">
약속 생성자는 투표할 없습니다.
</Text>
)}
🤖 Prompt for AI Agents
In src/pages/promiseStatus/components/Voting.tsx around lines 76 to 80, the
displayed Korean string contains a spacing typo: "투표 할" should be corrected to
"투표할"; update the Text node content to use the corrected single-word form so the
rendered UI shows "약속 생성자는 투표할 수 없습니다."

Comment on lines +108 to +114
{promiseAvailableTimes.map(time => {
const isSelected = selectedTime.includes(time);
return (
<Button
key={`${time.date}-${time.startTime}-${time.endTime}`}
onClick={() => handleTimeClick(time)}
variant={isSelected ? BUTTON_VARIANTS.ENABLED : BUTTON_VARIANTS.WHITE}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

React key 중복 가능성: 동일 값의 시간 데이터가 여러 개면 키 충돌 발생

mockUpVoting의 promiseAvailableTimes가 동일 값으로 여러 개 존재합니다. 현재 키가 값 조합만 사용하므로 중복 키가 발생합니다. 인덱스를 덧붙여 고유 키를 보장하세요.

-            {promiseAvailableTimes.map(time => {
+            {promiseAvailableTimes.map((time, idx) => {
               const isSelected = selectedTime.includes(time);
               return (
                 <Button
-                  key={`${time.date}-${time.startTime}-${time.endTime}`}
+                  key={`${time.date}-${time.startTime}-${time.endTime}-${idx}`}
                   onClick={() => handleTimeClick(time)}
                   variant={isSelected ? BUTTON_VARIANTS.ENABLED : BUTTON_VARIANTS.WHITE}
📝 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
{promiseAvailableTimes.map(time => {
const isSelected = selectedTime.includes(time);
return (
<Button
key={`${time.date}-${time.startTime}-${time.endTime}`}
onClick={() => handleTimeClick(time)}
variant={isSelected ? BUTTON_VARIANTS.ENABLED : BUTTON_VARIANTS.WHITE}
{promiseAvailableTimes.map((time, idx) => {
const isSelected = selectedTime.includes(time);
return (
<Button
key={`${time.date}-${time.startTime}-${time.endTime}-${idx}`}
onClick={() => handleTimeClick(time)}
variant={isSelected ? BUTTON_VARIANTS.ENABLED : BUTTON_VARIANTS.WHITE}
>
{/* … */}
</Button>
);
})}
🤖 Prompt for AI Agents
In src/pages/promiseStatus/components/Voting.tsx around lines 108 to 114, the
map uses a composite key of time.date-time.startTime-time.endTime which can
collide when identical time objects exist; change the map callback to include
the index (e.g., map((time, idx) => ...)) and append the idx to the key to
guarantee uniqueness (e.g.,
`${time.date}-${time.startTime}-${time.endTime}-${idx}`), ensuring stable
rendering without introducing other logic changes.

Comment on lines +125 to +138
export const mockUpConfirmed = {
promiseName: 'KUIT BARO 2차 회의',
promisePlace: {
placeName: '강남역 스타벅스',
placeAddress: '서울특별시 강남구 역삼동 123-123',
},

promiseAvailableTimes: {
date: '2025-04-03',
startTime: '12:00:00',
endTime: '12:30:00',
},
promiseMembersNames: ['John Doe', 'Jane Doe', 'John Smith', 'Jane Smith'],
};
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

API DTO와 목업 구조가 상이합니다 — 추후 실제 연동 시 어댑터/타입 정합성 확인 필요

현재 Confirmed 목업은 place와 time을 중첩 객체로 두고 있으나, 공개된 DTO(ConfirmedPromiseResponseDTO)는 fixedDate와 placeName을 최상위 필드로 가집니다. 실제 API 연동 시 어댑터 또는 컴포넌트 prop 구조 정합성 점검이 필요합니다.

연동 영향도 확인 스크립트입니다(Confirmed DTO/타입 사용처 점검):


🏁 Script executed:

#!/bin/bash
# Confirmed 관련 DTO/타입 사용 지점 확인
set -euo pipefail

echo "== ConfirmedPromiseResponseDTO 사용처 =="
rg -n -C2 '\bConfirmedPromiseResponseDTO\b' api src

echo
echo "== PromiseStatus Confirmed 흐름 내 fixedDate/placeName 접근 여부 =="
rg -n -C3 '\bfixedDate\b|\bplaceName\b' src | sed -n '1,200p'

Length of output: 15465


MockUpConfirmed 구조가 ConfirmedPromiseResponseDTO와 불일치합니다 — 목업 객체 수정 또는 어댑터 적용 필요

목업에서

promisePlace: { placeName, placeAddress }
promiseAvailableTimes: { date, startTime, endTime }

를 중첩 객체로 두고 있으나, 실제 ConfirmedPromiseResponseDTO는 다음과 같이 최상위 필드로 정의되어 있습니다:

interface ConfirmedPromiseResponseDTO {
  id: number;
  promiseName: string;
  fixedDate: string;      // YYYY-MM-DD
  placeName: string;
  placeAddress: string;
  startTime: string;      // HH:MM:SS
  endTime: string;        // HH:MM:SS
  promiseMembersNames: string[];
}

실제 API 연동 전, 아래 둘 중 하나를 선택해 일치시켜야 합니다:

  • 목업 객체를 DTO 형태에 맞춰 변경
  • 네트워크 응답을 어댑터에서 변환해 컴포넌트에 전달

수정 예시 (src/pages/promiseStatus/mockUp.ts):

-import { mockUpConfirmed } from './mockUp';
+import type { ConfirmedPromiseResponseDTO } from 'api/data-contracts';

-export const mockUpConfirmed = {
-  promiseName: 'KUIT BARO 2차 회의',
-  promisePlace: {
-    placeName: '강남역 스타벅스',
-    placeAddress: '서울특별시 강남구 역삼동 123-123',
-  },
-
-  promiseAvailableTimes: {
-    date: '2025-04-03',
-    startTime: '12:00:00',
-    endTime: '12:30:00',
-  },
-  promiseMembersNames: ['John Doe', 'Jane Doe', 'John Smith', 'Jane Smith'],
-};
+export const mockUpConfirmed: ConfirmedPromiseResponseDTO = {
+  id: 1,
+  promiseName: 'KUIT BARO 2차 회의',
+  fixedDate: '2025-04-03',
+  placeName: '강남역 스타벅스',
+  placeAddress: '서울특별시 강남구 역삼동 123-123',
+  startTime: '12:00:00',
+  endTime: '12:30:00',
+  promiseMembersNames: ['John Doe', 'Jane Doe', 'John Smith', 'Jane Smith'],
+};
  • 대상 파일: src/pages/promiseStatus/mockUp.ts
  • 참조 타입: api/data-contracts.ts 의 ConfirmedPromiseResponseDTO
🤖 Prompt for AI Agents
In src/pages/promiseStatus/mockUp.ts around lines 125 to 138, the mock object
shape does not match ConfirmedPromiseResponseDTO; update the mock to match the
DTO or add an adapter — specifically, replace nested promisePlace and
promiseAvailableTimes with top-level fields: id (number), promiseName (string),
fixedDate (YYYY-MM-DD), placeName (string), placeAddress (string), startTime
(HH:MM:SS), endTime (HH:MM:SS), and keep promiseMembersNames as string[]; ensure
types/values conform to api/data-contracts.ts ConfirmedPromiseResponseDTO or
implement a small adapter function that converts the current nested mock into
the DTO shape before it’s consumed by components.

Copy link
Member

@junghyungo junghyungo left a comment

Choose a reason for hiding this comment

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

리뷰 확인해주세요!

text="투표하기"
size="long"
variant={isFormValid ? BUTTON_VARIANTS.ENABLED : BUTTON_VARIANTS.DISABLED}
onClick={handleVote}
Copy link
Member

Choose a reason for hiding this comment

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

약속장소, 시간 선택 안 했을때 (투표하기가 회색일때)에도 버튼을 누르면 handleVote가 동작하여 홈으로 이동합니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] voting, confirm page

3 participants