Skip to content

[FEAT/#95] 챌린지 미션프로그래스 뷰 컴포넌트 조립#101

Merged
hyeminililo merged 10 commits intodevelopfrom
feat/#95-challenge-missionprogress-view
Jan 19, 2026
Merged

[FEAT/#95] 챌린지 미션프로그래스 뷰 컴포넌트 조립#101
hyeminililo merged 10 commits intodevelopfrom
feat/#95-challenge-missionprogress-view

Conversation

@hyeminililo
Copy link
Copy Markdown
Contributor

@hyeminililo hyeminililo commented Jan 19, 2026

Related issue 🛠

Work Description ✏️

  • 챌린지 미션 진행 화면 UI 구현
  • LazyColumn 기반 전체 스크롤 구조로 전환
  • TO-DO 미션 리스트 체크/해제 상태 관리 연결
  • 서버 응답 기반 진행률(progressPercentage) 반영
  • 체리 성장 영역, 미션 카드, 완료 버튼 컴포넌트 연동
  • CherrishButton 변경
  • ktLint 실행

Screenshot 📸

Screen_Recording_20260120_005123_Cherrish.mp4

Uncompleted Tasks 😅

  • N/A

To Reviewers 📢

사랑합니다 나.소.수 언니들

Summary by CodeRabbit

  • New Features

    • Challenge Mission Progress 화면 추가: 도전 과제 진행을 한눈에 확인할 수 있습니다.
    • 일일 할일 항목을 탭해 완료 상태를 토글할 수 있습니다.
    • 전체 진행률(%) 및 완료된 항목 요약이 화면에 표시됩니다.
  • UI 개선

    • 할일 섹션의 버튼이 화면 너비에 맞춰 표시되도록 변경되었습니다.
    • 일부 컴포넌트의 세로 여백이 소폭 조정되었습니다.

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

@hyeminililo hyeminililo requested a review from a team as a code owner January 19, 2026 16:44
@hyeminililo hyeminililo added FEAT✨ 새로운 기능 구현 혜민🍒 혜민 담당 labels Jan 19, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 19, 2026

Walkthrough

챌린지 미션 진행 화면을 추가하고 ViewModel과 UI 상태(progressPercentage, hasCompletedAny)를 도입했으며, Compose 화면(라우트·스크린) 및 투두 섹션 레이아웃을 추가·조정했습니다.

Changes

Cohort / File(s) 변경 요약
버튼 스타일
app/src/main/java/com/cherrish/android/core/designsystem/component/button/CherrishButton.kt
패딩을 전체 10.dp에서 수직만 적용(vertical = 10.dp)으로 변경
투두 섹션 레이아웃
app/src/main/java/com/cherrish/android/presentation/challenge/component/ChallengeMissionTodoSection.kt
LazyColumn 제거 후 Column/forEach로 렌더링 전환; 버튼 modifier를 전체 폭 버튼으로 조정; 관련 imports 삭제
진행률 섹션 마진
app/src/main/java/com/cherrish/android/presentation/challenge/component/ChallengeMissionProgressCherrygrowth.kt
하단 패딩을 9.dp → 10.dp로 변경
UI 상태 모델 확장
app/src/main/java/com/cherrish/android/presentation/challenge/missionprogress/ChallengeMissionProgressUiState.kt
progressPercentage: Int 필드 추가 및 val hasCompletedAny: Boolean 계산 프로퍼티 추가
뷰모델 추가
app/src/main/java/com/cherrish/android/presentation/challenge/missionprogress/ChallengeMissionProgressViewModel.kt
신규 ViewModel 추가: uiState: StateFlow<UiState<...>>, onTodoClick(id: Long), onCompletedTodayClick() 구현 (routines 내 isCompleted 토글 등)
화면·라우트 추가
app/src/main/java/com/cherrish/android/presentation/challenge/missionprogress/ChallengeMissionprogressScreen.kt
Route(public) 및 Compose 화면 추가: 상태 수집, LazyColumn 기반 화면 구성(제목·진행률·투두 섹션), 프리뷰 및 콜백 연결

Sequence Diagram(s)

sequenceDiagram
    participant User as 사용자
    participant Route as ChallengeMissionprogressRoute
    participant VM as ChallengeMissionProgressViewModel
    participant State as UiState
    participant Screen as ChallengeMissionprogressScreen

    User->>Route: 화면 진입
    Route->>VM: ViewModel 구독 (uiState)
    VM->>State: UiState.Success 발행 (routines, progressPercentage 등)
    State->>Screen: 상태 전달
    Screen->>Screen: UI 렌더링 (제목, 진행률, 투두)
    User->>Screen: 투두 아이템 클릭
    Screen->>VM: onTodoClick(id)
    VM->>State: routines 내 isCompleted 토글로 상태 업데이트
    State->>Screen: 변경된 상태 반영
    User->>Screen: 완료하기 클릭
    Screen->>VM: onCompletedTodayClick()
    VM->>State: 완료 조건 확인 및 처리
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • usuuhyn
  • sohee6989
  • nhyeonii
🚥 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
Title check ✅ Passed PR 제목이 주요 변경사항인 '챌린지 미션프로그레스 뷰 컴포넌트 조립'을 명확하게 설명하며, 코드 변경사항과 일치합니다.
Description check ✅ Passed PR 설명이 템플릿의 모든 필수 섹션을 포함하고 있으며, 작업 내용, 스크린샷, 관련 이슈를 상세히 기술하고 있습니다.
Linked Issues check ✅ Passed 모든 주요 요구사항(ViewModel 선언, State 개선, 뷰 조립, 패딩 적용, ktLint 실행)이 코드 변경사항에서 구현되었습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 #95 이슈의 요구사항 범위 내에 있으며, 별도의 범위 밖 변경사항은 없습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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.

@hyeminililo hyeminililo self-assigned this Jan 19, 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: 4

Caution

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

⚠️ Outside diff range comments (1)
app/src/main/java/com/cherrish/android/core/designsystem/component/button/CherrishButton.kt (1)

66-71: 가로 패딩 제거로 기본 터치 타깃/가독성 저하 위험

가로 패딩이 0이면 wrap-content로 쓰일 때 버튼 폭이 매우 좁아져 48dp 최소 터치 타깃 기준을 쉽게 못 맞춥니다. 의도된 디자인이 아니라면 가로 패딩(또는 최소 크기 보장)을 유지하는 편이 안전합니다.

🛠️ 수정 제안
         modifier = modifier
             .scale(scale)
             .clip(RoundedCornerShape(12.dp))
             .background(backgroundColor)
-            .padding(vertical = 10.dp),
+            .padding(horizontal = 10.dp, vertical = 10.dp),
🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/cherrish/android/presentation/challenge/missionprogress/ChallengeMissionprogressScreen.kt`:
- Around line 82-83: ChallengeMissionSelectedTitle is rendering hardcoded text;
update it to accept the actual challenge data (or specific fields like
title/plan) from uiState.challenge and bind those values instead of static
strings. Locate where ChallengeMissionSelectedTitle() is invoked and pass
uiState.challenge (or uiState.challenge.title and uiState.challenge.plan) as
props/parameters, and modify the ChallengeMissionSelectedTitle composable to
read and display those fields (or fallback safely if uiState.challenge is null).
Also apply the same binding fix for the other occurrences mentioned (the blocks
rendering the title/plan around the 110-139 and 144-145 regions) so all
instances use uiState.challenge data.
- Around line 69-75: The LazyColumn is not applying the Scaffold's paddingValues
and may overlap system insets; update the LazyColumn's modifier (used in
ChallengeMissionprogressScreen) to apply .padding(paddingValues) at the start of
the chain so scaffold insets are honored, then verify whether
navigationBarsPadding() is still needed (remove if redundant) to avoid double
insets; ensure you modify the LazyColumn modifier that currently calls
.fillMaxSize().background(...).navigationBarsPadding() to apply
padding(paddingValues) first.

In
`@app/src/main/java/com/cherrish/android/presentation/challenge/missionprogress/ChallengeMissionProgressViewModel.kt`:
- Around line 64-68: onCompletedTodayClick currently returns early and does
nothing; implement the completion flow by, after verifying state is
UiState.Success and !state.data.hasCompletedAny, calling the domain/use-case or
repository method (e.g., challengeRepository.completeToday()/completeMission())
to perform the server call, handle success/failure (update _uiState with new
data or emit an error state) and emit any UI events (e.g., via existing
single-event channel) to inform the UI; ensure you reference and update _uiState
and use UiState.Success branch in ChallengeMissionProgressViewModel to reflect
the new completion status and side-effects.
- Around line 50-60: The onTodoClick handler currently only toggles the
routine's isCompleted in _uiState.updateSuccess but doesn't recalc
progressPercentage, so update state.copy to also compute the new
progressPercentage after mapping routines: derive the updated routines list (the
mapped result used in state.copy), count completed items (e.g.,
updatedRoutines.count { it.isCompleted }) and set progressPercentage =
(completedCount.toFloat() / updatedRoutines.size) * 100 (or your app's expected
scale), then return state.copy(routines = updatedRoutines.toPersistentList(),
progressPercentage = calculatedValue) so UI reflects the new completion
percentage; adjust types/rounding as needed.
🧹 Nitpick comments (1)
app/src/main/java/com/cherrish/android/presentation/challenge/component/ChallengeMissionTodoSection.kt (1)

79-87: 리스트 항목에 안정 키를 부여해 주세요

Column + forEach로 바뀌면서 항목별 키가 사라졌습니다. 내부에 remember 상태가 있는 컴포저블이라면 아이템 재정렬/교체 시 상태가 섞일 수 있으니 key(item.id)로 감싸는 게 안전합니다.

♻️ 개선 제안
+import androidx.compose.runtime.key
...
     Column(
         verticalArrangement = Arrangement.spacedBy(8.dp)
     ) {
         routines.forEach { item ->
-            ChallengeChecklist(
-                isChecked = item.isCompleted,
-                onChecklistClick = { onRoutineClick(item.id) },
-                checklistContent = item.routine
-            )
+            key(item.id) {
+                ChallengeChecklist(
+                    isChecked = item.isCompleted,
+                    onChecklistClick = { onRoutineClick(item.id) },
+                    checklistContent = item.routine
+                )
+            }
         }
     }

As per coding guidelines, ...

Comment on lines +50 to +60
fun onTodoClick(id: Long) {
_uiState.updateSuccess { state ->
state.copy(
routines = state.routines.map {
if (it.id == id) {
it.copy(isCompleted = !it.isCompleted)
} else {
it
}
}.toPersistentList()
)
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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find the ViewModel file
find . -name "ChallengeMissionProgressViewModel.kt" -type f

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 190


🏁 Script executed:

# Once found, let's examine the full file to understand the structure
git ls-files | grep -i "challengemissionprogress"

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 525


🏁 Script executed:

# Search for UiState definition related to challenge mission progress
rg "ChallengeMissionProgress.*UiState" -A 20 -t kt

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 100


🏁 Script executed:

# Look for progressPercentage usage in the codebase
rg "progressPercentage" -t kt -B 2 -A 2

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 100


🏁 Script executed:

# Check the file structure around the challenge/missionprogress directory
fd "." -t f "presentation/challenge/missionprogress" | head -20

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 199


🏁 Script executed:

# Read the UiState file
cat -n app/src/main/java/com/cherrish/android/presentation/challenge/missionprogress/ChallengeMissionProgressUiState.kt

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 911


🏁 Script executed:

# Read the ViewModel file
cat -n app/src/main/java/com/cherrish/android/presentation/challenge/missionprogress/ChallengeMissionProgressViewModel.kt

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 3025


🏁 Script executed:

# Search for progressPercentage without file type restriction
rg "progressPercentage" -B 2 -A 2

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 3197


onTodoClick() 시 progressPercentage를 함께 갱신해야 합니다

현재 코드는 체크 상태를 업데이트할 때 routines만 변경하고 progressPercentage는 그대로 유지됩니다. 초기 상태에서는 완료된 항목 1개 / 전체 4개 = 25%로 설정되었으나, 사용자가 체크박스를 토글해도 진행률이 갱신되지 않아 UI와 실제 완료 상태가 맞지 않을 수 있습니다.

수정 예시
     fun onTodoClick(id: Long) {
         _uiState.updateSuccess { state ->
-            state.copy(
-                routines = state.routines.map {
-                    if (it.id == id) {
-                        it.copy(isCompleted = !it.isCompleted)
-                    } else {
-                        it
-                    }
-                }.toPersistentList()
-            )
+            val updatedRoutines = state.routines.map {
+                if (it.id == id) {
+                    it.copy(isCompleted = !it.isCompleted)
+                } else {
+                    it
+                }
+            }.toPersistentList()
+            val completedCount = updatedRoutines.count { it.isCompleted }
+            val progress = if (updatedRoutines.isNotEmpty()) {
+                (completedCount * 100) / updatedRoutines.size
+            } else 0
+            state.copy(
+                routines = updatedRoutines,
+                progressPercentage = progress
+            )
         }
     }
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/cherrish/android/presentation/challenge/missionprogress/ChallengeMissionProgressViewModel.kt`
around lines 50 - 60, The onTodoClick handler currently only toggles the
routine's isCompleted in _uiState.updateSuccess but doesn't recalc
progressPercentage, so update state.copy to also compute the new
progressPercentage after mapping routines: derive the updated routines list (the
mapped result used in state.copy), count completed items (e.g.,
updatedRoutines.count { it.isCompleted }) and set progressPercentage =
(completedCount.toFloat() / updatedRoutines.size) * 100 (or your app's expected
scale), then return state.copy(routines = updatedRoutines.toPersistentList(),
progressPercentage = calculatedValue) so UI reflects the new completion
percentage; adjust types/rounding as needed.

Comment on lines +64 to +68
fun onCompletedTodayClick() {
val state = _uiState.value
if (state !is UiState.Success) return
if (!state.data.hasCompletedAny) return
}
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 | 🟠 Major

완료 버튼 클릭이 성공 상태에서도 무동작입니다

onCompletedTodayClick()가 성공 상태에서도 아무 동작을 하지 않아 버튼이 무의미해집니다. 서버 호출/이벤트 발행 등 실제 완료 처리 로직을 연결해야 합니다.

🤖 Prompt for AI Agents
In
`@app/src/main/java/com/cherrish/android/presentation/challenge/missionprogress/ChallengeMissionProgressViewModel.kt`
around lines 64 - 68, onCompletedTodayClick currently returns early and does
nothing; implement the completion flow by, after verifying state is
UiState.Success and !state.data.hasCompletedAny, calling the domain/use-case or
repository method (e.g., challengeRepository.completeToday()/completeMission())
to perform the server call, handle success/failure (update _uiState with new
data or emit an error state) and emit any UI events (e.g., via existing
single-event channel) to inform the UI; ensure you reference and update _uiState
and use UiState.Success branch in ChallengeMissionProgressViewModel to reflect
the new completion status and side-effects.

Copy link
Copy Markdown
Contributor

@sohee6989 sohee6989 left a comment

Choose a reason for hiding this comment

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

일단 여기까지!!
수고하셨습니다!
이거 같이 한 번더 확인해야될듯요!!!ㅎㅎ

verticalArrangement = Arrangement.spacedBy(14.dp)
) {
item {
Spacer(modifier = Modifier.height(38.dp))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p1: 이 값도 contentPadding으로 관리해줘도 되지 않나여?!

}

@Composable
private fun ChallengeMissionSelectedTitle(modifier: Modifier = Modifier) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p1: 줄바꿈!!ㅎㅎㅎ

}

item {
Spacer(modifier = Modifier.height(24.dp))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p1: 이거 contentPadding?

verticalAlignment = Alignment.CenterVertically
) {
Text(
"피부 컨디션 챌린지",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p1: 이거 뚫어주세요!!ㅎㅎ

color = CherrishTheme.colors.gray1000
)

Surface(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p1: 이거 굳이 surface 안 감싸도 되지 않나여??

cherryType = uiState.cherryType,
remainingRoutines = uiState.remainingCount,
challengeProgress = uiState.progressPercentage,
modifier = Modifier.padding(bottom = 16.dp)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p1: 10.dp인듯?!
더블체크부탁드립니다

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

문제 확인 다른 곳을 고침

Copy link
Copy Markdown
Contributor

@nhyeonii nhyeonii left a comment

Choose a reason for hiding this comment

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

뷰모델은 서버 연결하면서 바꾸자 ~~

@hyeminililo hyeminililo merged commit 1537963 into develop Jan 19, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

FEAT✨ 새로운 기능 구현 혜민🍒 혜민 담당

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] 챌린지 프로그래스바 및 체리 성장기 뷰 조립

3 participants