Skip to content

[Feat] 프로모션 코드 입력 화면 구현 #27#29

Open
ProtossManse wants to merge 2 commits intodevelopfrom
feat/login-promotion-code-#26
Open

[Feat] 프로모션 코드 입력 화면 구현 #27#29
ProtossManse wants to merge 2 commits intodevelopfrom
feat/login-promotion-code-#26

Conversation

@ProtossManse
Copy link
Copy Markdown
Contributor

@ProtossManse ProtossManse commented Mar 25, 2026

🔗 관련 이슈

📙 작업 설명

  • 화면 UI를 구현했습니다.
  • UiState 및 ViewModel을 추가했습니다.
  • Route 및 내비게이션용 함수를 추가했습니다.

📸 스크린샷 또는 시연 영상 (선택)

기능 미리보기 기능 미리보기
기능 설명 image 기능 설명 image

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능
    • 어르신 프로모션 코드 인증 화면 추가: 8자리 영문 대문자 및 숫자로 구성된 프로모션 코드를 입력하고 확인할 수 있는 기능 추가
    • 코드 인증 완료 시 확인 다이얼로그 및 체크마크 아이콘 표시
    • 인증 완료 후 어르신 등록 화면으로 자동 이동

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 25, 2026

Walkthrough

새로운 로그인 어르신 프로모션 코드 입력 화면이 추가됩니다. 화면 UI, ViewModel, 상태 관리, 그리고 내비게이션 통합이 포함되어 있습니다. 사용자는 8자 대문자/숫자 형식의 코드를 입력하고, 확인 버튼 클릭 시 다이얼로그가 표시됩니다.

Changes

Cohort / File(s) Summary
리소스
composeApp/src/commonMain/composeResources/drawable/ic_check_green.xml
초록색 체크마크 아이콘 추가 (66×66dp)
UI 화면
composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/feature/login/elder/screen/LoginElderPromotionScreen.kt
프로모션 코드 입력/확인 화면 구현. TextField로 코드 입력, 8자 알파벳/숫자 정규식 검증, 확인 시 다이얼로그 표시
상태 및 ViewModel
composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/feature/login/elder/viewmodel/LoginElderPromotionUiState.kt, ...LoginElderPromotionViewModel.kt
UI 상태 클래스(inputValue, showDialog)와 이벤트 핸들러(onPromotionCodeChange, onConfirm, onDismissRequest) 포함 ViewModel
내비게이션
composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/feature/login/navigation/LoginNavigation.kt, composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/navigation/Route.kt, composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/navigation/MainNavigator.kt, composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/navigation/NavGraph.kt
새 라우트 LoginPromotion 등록, 내비게이션 헬퍼 함수 추가, NavGraph에 프로모션 콜백 연결

Sequence Diagram

sequenceDiagram
    participant User
    participant Screen as LoginElderPromotionScreen
    participant ViewModel as LoginElderPromotionViewModel
    participant Navigation
    
    User->>Screen: 프로모션 코드 입력
    Screen->>ViewModel: onPromotionCodeChange(code)
    ViewModel->>ViewModel: inputValue 업데이트
    
    User->>Screen: 확인 버튼 클릭
    Screen->>ViewModel: onConfirm()
    ViewModel->>ViewModel: showDialog = true
    ViewModel-->>Screen: uiState 업데이트
    Screen->>Screen: Dialog 표시
    
    User->>Screen: 확인 버튼 클릭 (Dialog)
    Screen->>ViewModel: onDismissRequest()
    ViewModel->>ViewModel: showDialog = false
    Screen->>Navigation: navigateToElderRegister()
    Navigation->>Navigation: 어르신 등록 화면으로 이동
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • alswlekk

Poem

초록 체크마크가 반짝이고 ✨
프로모션 코드 입력창이 준비되고 📝
8자 코드를 입력하면 🔐
다이얼로그가 "인증되었습니다!" 라며 반겨주네 🎉
어르신 등록 화면으로 출발! 🚀

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 프로모션 코드 입력 화면 구현이라는 주요 변경사항을 명확하게 요약하고 있습니다.
Linked Issues check ✅ Passed 코드 변경사항이 issue #27의 모든 요구사항(프로모션 코드 입력 화면 UI 구현, 네비게이션 등록)을 충족합니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 프로모션 코드 입력 화면 구현과 네비게이션 등록이라는 범위 내에 있습니다.

✏️ 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/login-promotion-code-#26

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.

@ProtossManse ProtossManse changed the title [Feat] 알림 [Feat] 프로모션 코드 입력 화면 구현 #27 Mar 25, 2026
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 (4)
composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/navigation/Route.kt (1)

35-36: 포맷팅 일관성 확인 필요

다른 Route들은 Splash : Route처럼 콜론 앞뒤에 공백이 있는데, LoginPromotion: Route는 콜론 뒤에만 공백이 없습니다. 기존 스타일과 일관성을 맞추는 것이 좋겠습니다.

♻️ 제안된 수정
     `@Serializable`
-    data object LoginPromotion: Route
+    data object LoginPromotion : Route
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/navigation/Route.kt`
around lines 35 - 36, The declaration for the serializable route is inconsistent
with the project's spacing style; update the data object declaration for
LoginPromotion so the colon has spaces on both sides like the other routes
(change "LoginPromotion: Route" to "LoginPromotion : Route") while preserving
the `@Serializable` annotation and the data object keyword to match the style used
by other Route declarations.
composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/feature/login/elder/viewmodel/LoginElderPromotionViewModel.kt (2)

14-16: 입력값 검증 고려

UI에서는 ^[A-Z0-9]{8}$ 정규식으로 버튼 활성화를 제어하지만, ViewModel에서는 입력값에 대한 검증이 없습니다.

입력을 대문자로 변환하거나 최대 길이를 제한하는 로직을 ViewModel에서 처리하면 UI와 비즈니스 로직의 분리가 더 명확해집니다.

♻️ 선택적 개선안
 fun onPromotionCodeChange(value: String) {
-    _uiState.update { it.copy(inputValue = value) }
+    val sanitized = value.uppercase().take(8)
+    _uiState.update { it.copy(inputValue = sanitized) }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/feature/login/elder/viewmodel/LoginElderPromotionViewModel.kt`
around lines 14 - 16, onPromotionCodeChange currently just stores raw input;
update it in LoginElderPromotionViewModel to normalize and validate input:
convert value to uppercase, trim and cap to 8 characters, then update _uiState
with the processed inputValue and a validity flag computed against the regex
^[A-Z0-9]{8}$ (or set isValid true only when it matches); keep
normalization/validation inside onPromotionCodeChange so UI stays
presentation-only and business rules live in the ViewModel.

18-21: 프로모션 검증 로직 누락

TODO 주석에서 언급된 대로 프로모션 검증 로직이 없습니다. 현재 onConfirm()은 실제 검증 없이 바로 성공 다이얼로그를 표시합니다.

실제 프로모션 코드 검증(API 호출 등)이 추가될 때까지 사용자에게 잘못된 피드백을 줄 수 있습니다. 검증 실패 케이스에 대한 UI 처리도 고려해 주세요.

프로모션 검증 로직 구현을 위한 이슈를 생성해 드릴까요?

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

In
`@composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/feature/login/elder/viewmodel/LoginElderPromotionViewModel.kt`
around lines 18 - 21, onConfirm currently skips promotion validation and
immediately sets _uiState to showDialog=true; implement actual validation: call
or create a validation method (e.g., verifyPromotionCode or validatePromotion)
from onConfirm (handle as suspend if using coroutines), set a loading flag on
_uiState while awaiting the result, and on success update
_uiState.copy(showDialog=true) but on failure update _uiState to reflect the
error (e.g., showDialog=false plus errorMessage or showError flag) so the UI can
display failure feedback; remove the TODO and ensure error and loading state
fields exist/are used in the same ViewModel (referencing onConfirm, _uiState and
showDialog).
composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/feature/login/elder/screen/LoginElderPromotionScreen.kt (1)

105-110: Regex 객체 재생성 최적화

"^[A-Z0-9]{8}\$".toRegex()가 매 recomposition마다 새로 생성됩니다. remember를 사용하거나 companion object에 정의하여 불필요한 객체 생성을 방지하는 것이 좋습니다.

♻️ 제안된 수정

Layout 함수 상단에 추가:

val promotionCodePattern = remember { "^[A-Z0-9]{8}$".toRegex() }

그리고 사용처에서:

 CTAButton(
-    if (uiState.inputValue.matches("^[A-Z0-9]{8}\$".toRegex())) CTAButtonType.GREEN
+    if (uiState.inputValue.matches(promotionCodePattern)) CTAButtonType.GREEN
     else CTAButtonType.DISABLED,
     text = "확인",
     onClick = { onConfirm() },
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/feature/login/elder/screen/LoginElderPromotionScreen.kt`
around lines 105 - 110, The inline regex "^[A-Z0-9]{8}\$".toRegex() is recreated
on every recomposition; inside the LoginElderPromotionScreen composable define a
single reusable Regex (e.g., val promotionCodePattern = remember {
"^[A-Z0-9]{8}$".toRegex() } at the top of the composable) and replace the inline
call in the CTAButton condition with
promotionCodePattern.matches(uiState.inputValue); alternatively, if you need a
static/global instance, move the Regex into a companion object or top-level
constant and reference that instead.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/feature/login/elder/screen/LoginElderPromotionScreen.kt`:
- Around line 141-152: The "확인" button only calls navigateToElderRegister(),
leaving the dialog state open; update the click handler on the Box in
LoginElderPromotionScreen (the clickable that currently invokes
navigateToElderRegister()) to first call onDismissRequest() to set showDialog =
false and then call navigateToElderRegister(), ensuring the dialog is closed
before navigation to avoid backstack issues.

In
`@composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/feature/login/navigation/LoginNavigation.kt`:
- Line 69: The parameter navigateToPromotion is declared on the loginNavGraph
function signature but never used inside loginNavGraph and is not passed to
LoginElderPromotionScreen; remove the unused parameter from the loginNavGraph
signature and any corresponding callers, and update any references to the
navigateToPromotion symbol so callers no longer supply it (ensure
LoginElderPromotionScreen usages remain unchanged).

---

Nitpick comments:
In
`@composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/feature/login/elder/screen/LoginElderPromotionScreen.kt`:
- Around line 105-110: The inline regex "^[A-Z0-9]{8}\$".toRegex() is recreated
on every recomposition; inside the LoginElderPromotionScreen composable define a
single reusable Regex (e.g., val promotionCodePattern = remember {
"^[A-Z0-9]{8}$".toRegex() } at the top of the composable) and replace the inline
call in the CTAButton condition with
promotionCodePattern.matches(uiState.inputValue); alternatively, if you need a
static/global instance, move the Regex into a companion object or top-level
constant and reference that instead.

In
`@composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/feature/login/elder/viewmodel/LoginElderPromotionViewModel.kt`:
- Around line 14-16: onPromotionCodeChange currently just stores raw input;
update it in LoginElderPromotionViewModel to normalize and validate input:
convert value to uppercase, trim and cap to 8 characters, then update _uiState
with the processed inputValue and a validity flag computed against the regex
^[A-Z0-9]{8}$ (or set isValid true only when it matches); keep
normalization/validation inside onPromotionCodeChange so UI stays
presentation-only and business rules live in the ViewModel.
- Around line 18-21: onConfirm currently skips promotion validation and
immediately sets _uiState to showDialog=true; implement actual validation: call
or create a validation method (e.g., verifyPromotionCode or validatePromotion)
from onConfirm (handle as suspend if using coroutines), set a loading flag on
_uiState while awaiting the result, and on success update
_uiState.copy(showDialog=true) but on failure update _uiState to reflect the
error (e.g., showDialog=false plus errorMessage or showError flag) so the UI can
display failure feedback; remove the TODO and ensure error and loading state
fields exist/are used in the same ViewModel (referencing onConfirm, _uiState and
showDialog).

In
`@composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/navigation/Route.kt`:
- Around line 35-36: The declaration for the serializable route is inconsistent
with the project's spacing style; update the data object declaration for
LoginPromotion so the colon has spaces on both sides like the other routes
(change "LoginPromotion: Route" to "LoginPromotion : Route") while preserving
the `@Serializable` annotation and the data object keyword to match the style used
by other Route declarations.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3a7dd488-ae7b-4957-8912-0c24fca04ac4

📥 Commits

Reviewing files that changed from the base of the PR and between 8e5e799 and 95f7a31.

📒 Files selected for processing (8)
  • composeApp/src/commonMain/composeResources/drawable/ic_check_green.xml
  • composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/feature/login/elder/screen/LoginElderPromotionScreen.kt
  • composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/feature/login/elder/viewmodel/LoginElderPromotionUiState.kt
  • composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/feature/login/elder/viewmodel/LoginElderPromotionViewModel.kt
  • composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/feature/login/navigation/LoginNavigation.kt
  • composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/navigation/MainNavigator.kt
  • composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/navigation/NavGraph.kt
  • composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/navigation/Route.kt

Comment on lines +141 to +152
Box(
Modifier.fillMaxWidth()
.clip(RoundedCornerShape(14.dp))
.background(G500)
.clickable { navigateToElderRegister() },
contentAlignment = Alignment.Center,
) {
Text(
"확인", color = White, style = MediCareCallTheme.typography.B_17,
modifier = Modifier.padding(vertical = 14.dp),
)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

다이얼로그 닫기 후 네비게이션 처리

다이얼로그의 "확인" 버튼 클릭 시 navigateToElderRegister()만 호출하고 onDismissRequest()를 호출하지 않습니다. 네비게이션 후에도 showDialogtrue로 남아있어 백스택 관련 이슈가 발생할 수 있습니다.

🐛 제안된 수정
 Box(
     Modifier.fillMaxWidth()
         .clip(RoundedCornerShape(14.dp))
         .background(G500)
-        .clickable { navigateToElderRegister() },
+        .clickable {
+            onDismissRequest()
+            navigateToElderRegister()
+        },
     contentAlignment = Alignment.Center,
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/feature/login/elder/screen/LoginElderPromotionScreen.kt`
around lines 141 - 152, The "확인" button only calls navigateToElderRegister(),
leaving the dialog state open; update the click handler on the Box in
LoginElderPromotionScreen (the clickable that currently invokes
navigateToElderRegister()) to first call onDismissRequest() to set showDialog =
false and then call navigateToElderRegister(), ensuring the dialog is closed
before navigation to avoid backstack issues.

navigateToRegisterElderHealth: () -> Unit,
navigateToCareCallSetting: () -> Unit,
navigateToCareCallSettingWithPopUpTo: () -> Unit,
navigateToPromotion: () -> Unit,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: navigateToPromotion이 실제로 사용되는 곳이 있는지 확인
rg -n "navigateToPromotion" --type=kotlin

Repository: Medicare-Call/Medicare-Call-KMP

Length of output: 376


🏁 Script executed:

cat -n composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/feature/login/navigation/LoginNavigation.kt | sed -n '50,160p'

Repository: Medicare-Call/Medicare-Call-KMP

Length of output: 4604


🏁 Script executed:

cat -n composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/navigation/NavGraph.kt | sed -n '120,140p'

Repository: Medicare-Call/Medicare-Call-KMP

Length of output: 1477


사용되지 않는 파라미터 제거

navigateToPromotion 파라미터가 함수 시그니처에는 있지만 loginNavGraph 본문에서 전혀 사용되지 않습니다. LoginElderPromotionScreen 호출 시에도 전달되지 않고 있으니, 필요 없다면 파라미터를 제거해 주세요.

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

In
`@composeApp/src/commonMain/kotlin/com/konkuk/medicarecall/ui/feature/login/navigation/LoginNavigation.kt`
at line 69, The parameter navigateToPromotion is declared on the loginNavGraph
function signature but never used inside loginNavGraph and is not passed to
LoginElderPromotionScreen; remove the unused parameter from the loginNavGraph
signature and any corresponding callers, and update any references to the
navigateToPromotion symbol so callers no longer supply it (ensure
LoginElderPromotionScreen usages remain unchanged).

Copy link
Copy Markdown
Contributor

@librawish808 librawish808 left a comment

Choose a reason for hiding this comment

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

좋습니다👍

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.

[Feat] 로그인 어르신 프로모션 코드 입력 화면 구현

2 participants