Skip to content

[Feature] 콜밴 글쓰기 참여 인원 및 제출 버튼 추가#1326

Open
JaeYoung290 wants to merge 6 commits intofeature/#1283-callvan-create-time-fieldfrom
feature/#1283-callvan-create-participants-and-submit
Open

[Feature] 콜밴 글쓰기 참여 인원 및 제출 버튼 추가#1326
JaeYoung290 wants to merge 6 commits intofeature/#1283-callvan-create-time-fieldfrom
feature/#1283-callvan-create-participants-and-submit

Conversation

@JaeYoung290
Copy link
Contributor

@JaeYoung290 JaeYoung290 commented Mar 7, 2026

PR 개요

이슈 번호: #1283

PR 체크리스트

  • Code convention을 잘 지켰나요?
  • Lint check를 수행하였나요?
  • Assignees를 추가했나요?

작업사항

  • 버그 수정
  • 신규 기능
  • 코드 스타일 수정 (포맷팅 등)
  • 리팩토링 (기능 수정 X, API 수정 X)
  • 기타

작업사항의 상세한 설명

참여 인원 선택 섹션과 글쓰기 제출 바텀바를 추가합니다.

  • CallvanParticipantsSection: ± 버튼으로 최대 인원(1~8명) 조절
  • CallvanSubmitBottomBar: 폼 완성 여부에 따른 버튼 활성화/비활성화 처리

논의 사항

스크린샷

추가내용

  • develop, sprint 브랜치를 향하고 있습니다
  • production 브랜치를 향하고 있습니다

Summary by CodeRabbit

  • 새 기능
    • 콜밴 생성 화면에 참여자 수 선택 섹션 추가
    • 참여자 수 실시간 표시 및 증가/감소 버튼으로 조절 가능
    • 참여자 수 형식 문자열 리소스 추가(동적 표시)
    • 폼 완성 여부와 제출 상태에 따른 제출 버튼 바 추가(활성/비활성/로딩 상태 미리보기 포함)

@JaeYoung290 JaeYoung290 requested a review from a team as a code owner March 7, 2026 12:29
@coderabbitai
Copy link

coderabbitai bot commented Mar 7, 2026

Walkthrough

CallVan 생성 UI에 참여자 수 조정 섹션과 제출 하단 바 컴포넌트, 및 참여자 수 표시용 문자열 리소스가 추가되었습니다.

Changes

Cohort / File(s) Summary
CallVan 참여자 관리 UI 컴포넌트
feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/component/CallvanParticipantsSection.kt
참여자 수 표시, 증감 버튼, 레이블/힌트 텍스트를 포함한 Compose 컴포넌트 추가. count, onDecrement, onIncrement 공개 API 제공 및 Preview 포함.
CallVan 제출 하단 바 컴포넌트
feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/component/CallvanSubmitBottomBar.kt
폼 완료/제출 상태에 따라 활성화되는 전체 너비 제출 버튼, 안내 텍스트 및 Divider를 렌더링하는 Compose 컴포넌트 추가. 활성/비활성/로딩 Preview 포함.
문자열 리소스
feature/callvan/src/main/res/values/strings.xml
참여자 수를 포맷하기 위한 callvan_create_participants_count 문자열 리소스 추가 ("%d 명").

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

Suggested labels

coderabbit:complete

Suggested reviewers

  • TTRR1007
🚥 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 변경사항의 주요 내용을 명확하게 반영하고 있습니다. 새로운 콜밴 참여 인원 조절 기능과 제출 버튼이라는 핵심 변경사항을 구체적으로 설명합니다.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/#1283-callvan-create-participants-and-submit
📝 Coding Plan
  • Generate coding plan for human review comments

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.

@github-actions github-actions bot requested review from KYM-P and TTRR1007 March 7, 2026 12:29
@JaeYoung290 JaeYoung290 force-pushed the feature/#1283-callvan-create-time-field branch from 73b4a7a to 766e48f Compare March 7, 2026 12:49
@JaeYoung290 JaeYoung290 force-pushed the feature/#1283-callvan-create-participants-and-submit branch from 7eba53e to 463355a Compare March 7, 2026 12:50
Copy link
Member

@kongwoojin kongwoojin left a comment

Choose a reason for hiding this comment

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

고생하셨습니다.
코멘트 확인해주세요.

Comment on lines +36 to +39
participantsText: String,
count: Int,
onDecrement: () -> Unit,
onIncrement: () -> Unit
Copy link
Member

Choose a reason for hiding this comment

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

parameter 동일 이슈

Comment on lines +53 to +54
style = KoinTheme.typography.medium16,
color = RebrandKoinTheme.colors.primary500
Copy link
Member

Choose a reason for hiding this comment

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

디자인시스템 통일 이슈

.padding(horizontal = 16.dp, vertical = 8.dp)
)
VerticalDivider(
modifier = Modifier.height(38.dp),
Copy link
Member

Choose a reason for hiding this comment

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

?

Comment on lines +42 to +43
style = KoinTheme.typography.regular12,
color = KoinTheme.colors.neutral500
Copy link
Member

Choose a reason for hiding this comment

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

RebrandKoinTheme 이슈

@JaeYoung290 JaeYoung290 self-assigned this Mar 9, 2026
@kongwoojin
Copy link
Member

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Mar 15, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@kongwoojin
Copy link
Member

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Mar 15, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@JaeYoung290
Copy link
Contributor Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Mar 15, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@JaeYoung290
Copy link
Contributor Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Mar 15, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Comment on lines +37 to +42
fun CallvanParticipantsSection(
participantsText: String,
count: Int,
modifier: Modifier = Modifier,
onDecrement: () -> Unit = {},
onIncrement: () -> Unit = {}
Copy link

Choose a reason for hiding this comment

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

[Major] 스텝퍼 버튼의 활성화/비활성화 상태 표현 불가

현재 컴포넌트는 감소/증가 버튼의 활성화 상태를 외부에서 제어할 수 없습니다. 참여 인원이 최솟값(1명)이거나 최댓값(8명)일 때 해당 버튼을 비활성화하여 사용자에게 명확한 피드백을 주어야 합니다.

Suggested change
fun CallvanParticipantsSection(
participantsText: String,
count: Int,
modifier: Modifier = Modifier,
onDecrement: () -> Unit = {},
onIncrement: () -> Unit = {}
fun CallvanParticipantsSection(
participantsText: String,
count: Int,
modifier: Modifier = Modifier,
isDecrementEnabled: Boolean = true,
isIncrementEnabled: Boolean = true,
onDecrement: () -> Unit = {},
onIncrement: () -> Unit = {}

이후 각 아이콘 tint 색상도 활성화 상태에 따라 분기:

tint = if (isDecrementEnabled) RebrandKoinTheme.colors.primary500 else RebrandKoinTheme.colors.neutral400

호출 측(ViewModel/Screen)에서는 다음처럼 제어:

isDecrementEnabled = count > 1,
isIncrementEnabled = count < 8,

Comment on lines +89 to +97
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_remove),
contentDescription = null,
tint = RebrandKoinTheme.colors.primary500,
modifier = Modifier
.clickable(onClick = onDecrement)
.padding(horizontal = 8.dp, vertical = 7.dp)
.size(24.dp)
)
Copy link

Choose a reason for hiding this comment

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

[Major] 터치 타겟 크기 미달 및 접근성 누락

두 가지 문제가 있습니다:

  1. 터치 타겟 크기: 현재 유효 터치 영역은 40dp × 38dp입니다 (패딩 8+8=16dp, 7+7=14dp + 아이콘 24dp). Material Design 가이드라인에서는 최소 48dp × 48dp를 요구합니다. 작은 타겟은 특히 모바일에서 오조작을 유발합니다.

  2. 접근성: contentDescription = null로 설정되어 있어 TalkBack 사용자가 이 버튼의 용도를 알 수 없습니다.

IconButton을 사용하면 두 문제를 모두 해결할 수 있습니다:

Suggested change
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_remove),
contentDescription = null,
tint = RebrandKoinTheme.colors.primary500,
modifier = Modifier
.clickable(onClick = onDecrement)
.padding(horizontal = 8.dp, vertical = 7.dp)
.size(24.dp)
)
IconButton(
onClick = onDecrement,
enabled = isDecrementEnabled,
) {
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_remove),
contentDescription = stringResource(R.string.callvan_create_decrement),
tint = if (isDecrementEnabled) RebrandKoinTheme.colors.primary500 else RebrandKoinTheme.colors.neutral400,
modifier = Modifier.size(24.dp)
)
}

IconButton은 기본적으로 48dp 터치 타겟을 보장합니다.

Comment on lines +110 to +118
Icon(
imageVector = Icons.Default.Add,
contentDescription = null,
tint = RebrandKoinTheme.colors.primary500,
modifier = Modifier
.clickable(onClick = onIncrement)
.padding(horizontal = 8.dp, vertical = 7.dp)
.size(24.dp)
)
Copy link

Choose a reason for hiding this comment

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

[Major] 터치 타겟 크기 미달 및 접근성 누락 (감소 버튼과 동일)

증가 버튼도 동일하게 IconButton으로 교체하고 contentDescription을 지정해야 합니다:

Suggested change
Icon(
imageVector = Icons.Default.Add,
contentDescription = null,
tint = RebrandKoinTheme.colors.primary500,
modifier = Modifier
.clickable(onClick = onIncrement)
.padding(horizontal = 8.dp, vertical = 7.dp)
.size(24.dp)
)
IconButton(
onClick = onIncrement,
enabled = isIncrementEnabled,
) {
Icon(
imageVector = Icons.Default.Add,
contentDescription = stringResource(R.string.callvan_create_increment),
tint = if (isIncrementEnabled) RebrandKoinTheme.colors.primary500 else RebrandKoinTheme.colors.neutral400,
modifier = Modifier.size(24.dp)
)
}

Comment on lines +77 to +84
Text(
text = participantsText,
style = RebrandKoinTheme.typography.regular14,
color = RebrandKoinTheme.colors.neutral800,
modifier = Modifier
.weight(1f)
.padding(horizontal = 16.dp, vertical = 8.dp)
)
Copy link

Choose a reason for hiding this comment

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

[Minor] participantsTextcount의 중복 데이터

현재 participantsText("4 명")와 count(4)는 동일한 인원 수를 두 개의 파라미터로 받고 있습니다. 호출 측에서 두 값이 항상 동기화되어야 하는 부담이 있으며, 불일치 버그가 생길 수 있습니다.

participantsText가 단순히 인원 수의 포맷된 표현이라면, 컴포넌트 내부에서 count를 기반으로 생성하는 것을 고려해 보세요:

// 컴포넌트 내부
val participantsText = stringResource(R.string.callvan_create_participants_format, count)
// strings.xml: <string name="callvan_create_participants_format">%d 명</string>

만약 participantsText가 별도의 의미를 가진다면(예: 다른 포맷), 그 의도가 명확히 드러나도록 파라미터명이나 주석으로 구분을 명시해 주세요.

Comment on lines +123 to +132
@Preview(showBackground = true)
@Composable
private fun CallvanParticipantsSectionPreview() {
CallvanParticipantsSection(
participantsText = "4 명",
count = 4,
onDecrement = {},
onIncrement = {}
)
}
Copy link

Choose a reason for hiding this comment

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

[Trivial] Preview에 테마 래퍼 누락

RebrandKoinTheme으로 감싸지 않으면 커스텀 색상(primary500, neutral400 등)과 타이포그래피가 Preview에서 올바르게 렌더링되지 않습니다.

Suggested change
@Preview(showBackground = true)
@Composable
private fun CallvanParticipantsSectionPreview() {
CallvanParticipantsSection(
participantsText = "4 명",
count = 4,
onDecrement = {},
onIncrement = {}
)
}
@Preview(showBackground = true)
@Composable
private fun CallvanParticipantsSectionPreview() {
RebrandKoinTheme {
CallvanParticipantsSection(
participantsText = "4 명",
count = 4,
onDecrement = {},
onIncrement = {}
)
}
}

Comment on lines +90 to +98
imageVector = ImageVector.vectorResource(R.drawable.ic_remove),
contentDescription = null,
tint = RebrandKoinTheme.colors.primary500,
modifier = Modifier
.clickable(onClick = onDecrement)
.padding(horizontal = 8.dp, vertical = 7.dp)
.size(24.dp)
)
Box(
Copy link

Choose a reason for hiding this comment

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

[Major] 접근성: 클릭 가능한 아이콘에 contentDescription 누락

clickable이 적용된 IconcontentDescription = null을 사용하면 스크린 리더(TalkBack) 사용자가 해당 버튼의 역할을 알 수 없습니다. 접근성 가이드라인(WCAG 2.1 / Android Accessibility) 위반입니다.

또한 터치 타겟 크기가 24dp(icon) + 8dp*2(horizontal) + 7dp*2(vertical) = 40x38dp로, Android 권장 최소 터치 타겟 크기인 48x48dp 미만입니다.

Suggested change
imageVector = ImageVector.vectorResource(R.drawable.ic_remove),
contentDescription = null,
tint = RebrandKoinTheme.colors.primary500,
modifier = Modifier
.clickable(onClick = onDecrement)
.padding(horizontal = 8.dp, vertical = 7.dp)
.size(24.dp)
)
Box(
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_remove),
contentDescription = stringResource(R.string.callvan_create_participants_decrement),
tint = RebrandKoinTheme.colors.primary500,
modifier = Modifier
.clickable(onClick = onDecrement)
.padding(horizontal = 12.dp, vertical = 12.dp)
.size(24.dp)
)

strings.xml에 callvan_create_participants_decrement ("인원 감소") 리소스 추가 필요

Comment on lines +111 to +119
imageVector = Icons.Default.Add,
contentDescription = null,
tint = RebrandKoinTheme.colors.primary500,
modifier = Modifier
.clickable(onClick = onIncrement)
.padding(horizontal = 8.dp, vertical = 7.dp)
.size(24.dp)
)
}
Copy link

Choose a reason for hiding this comment

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

[Major] 접근성: 증가 버튼도 contentDescription 누락

감소 버튼과 동일한 이슈입니다. 스크린 리더 사용자에게 이 버튼이 무엇을 하는지 설명이 필요합니다.

Suggested change
imageVector = Icons.Default.Add,
contentDescription = null,
tint = RebrandKoinTheme.colors.primary500,
modifier = Modifier
.clickable(onClick = onIncrement)
.padding(horizontal = 8.dp, vertical = 7.dp)
.size(24.dp)
)
}
Icon(
imageVector = Icons.Default.Add,
contentDescription = stringResource(R.string.callvan_create_participants_increment),
tint = RebrandKoinTheme.colors.primary500,
modifier = Modifier
.clickable(onClick = onIncrement)
.padding(horizontal = 12.dp, vertical = 12.dp)
.size(24.dp)
)

strings.xml에 callvan_create_participants_increment ("인원 증가") 리소스 추가 필요

Comment on lines +61 to +66
withStyle(SpanStyle(fontWeight = FontWeight.SemiBold)) {
append(stringResource(R.string.callvan_create_participants_hint_bold))
}
append(stringResource(R.string.callvan_create_participants_hint_normal))
},
style = RebrandKoinTheme.typography.regular12,
Copy link

Choose a reason for hiding this comment

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

[Minor] 불필요한 리컴포지션: buildAnnotatedString이 매 컴포지션마다 재생성됨

이 힌트 텍스트는 어떤 상태에도 의존하지 않지만, CallvanParticipantsSection이 리컴포지션될 때마다 buildAnnotatedString이 새 객체를 생성합니다. remember로 캐싱하면 불필요한 객체 생성을 방지할 수 있습니다.

Suggested change
withStyle(SpanStyle(fontWeight = FontWeight.SemiBold)) {
append(stringResource(R.string.callvan_create_participants_hint_bold))
}
append(stringResource(R.string.callvan_create_participants_hint_normal))
},
style = RebrandKoinTheme.typography.regular12,
text = remember {
buildAnnotatedString {
withStyle(SpanStyle(fontWeight = FontWeight.SemiBold)) {
append(stringResource(R.string.callvan_create_participants_hint_bold))
}
append(stringResource(R.string.callvan_create_participants_hint_normal))
}
},

단, stringResource는 Composable 컨텍스트가 필요하므로 변수로 먼저 추출하는 방법도 유효합니다:

val boldHint = stringResource(R.string.callvan_create_participants_hint_bold)
val normalHint = stringResource(R.string.callvan_create_participants_hint_normal)
val hintText = remember(boldHint, normalHint) {
    buildAnnotatedString {
        withStyle(SpanStyle(fontWeight = FontWeight.SemiBold)) { append(boldHint) }
        append(normalHint)
    }
}

Comment on lines +99 to +110
modifier = Modifier
.border(width = 1.dp, color = RebrandKoinTheme.colors.neutral400)
.padding(horizontal = 24.dp, vertical = 8.dp),
contentAlignment = Alignment.Center
) {
Text(
text = count.toString(),
style = RebrandKoinTheme.typography.regular14,
color = RebrandKoinTheme.colors.neutral800
)
}
Icon(
Copy link

Choose a reason for hiding this comment

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

[Info] 카운트가 두 곳에서 표시됨 — 의도된 디자인인지 확인 필요

현재 인원 수가 두 군데에 렌더링됩니다:

  • 왼쪽: participantsText"4 명" (포맷된 문자열)
  • 중앙 Box: count.toString()"4" (숫자만)

디자인 스펙상 의도된 레이아웃이라면 문제없지만, 만약 왼쪽의 "4 명" 텍스트가 필요 없다면 제거를 검토해 주세요. 또한 중앙 Box의 숫자 텍스트에도 contentDescription이 있으면 접근성이 향상됩니다.

isSubmitting: Boolean,
modifier: Modifier = Modifier,
onSubmit: () -> Unit = {}
) {
Copy link

Choose a reason for hiding this comment

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

[Minor] 제출 콜백의 기본값 {} — 조용한 실패(silent failure) 위험

onSubmit: () -> Unit = {}에 기본값 {}을 주면, 상위 컴포저블에서 실수로 콜백을 전달하지 않아도 컴파일 오류 없이 버튼을 눌러도 아무 일도 일어나지 않습니다. 제출 버튼은 핵심 기능이므로 콜백을 필수 파라미터로 만드는 것을 권장합니다.

Suggested change
) {
onSubmit: () -> Unit

반면 modifier: Modifier = Modifier처럼 순수 UI 파라미터의 기본값은 적절합니다.

Comment on lines +45 to +59
text = stringResource(R.string.callvan_create_submit),
onClick = onSubmit,
enabled = isFormComplete && !isSubmitting,
modifier = Modifier.fillMaxWidth(),
shape = RebrandKoinTheme.shapes.small,
textStyle = RebrandKoinTheme.typography.bold16,
contentPadding = PaddingValues(vertical = 14.dp),
colors = ButtonDefaults.buttonColors(
containerColor = RebrandKoinTheme.colors.primary500,
disabledContainerColor = RebrandKoinTheme.colors.neutral400,
contentColor = RebrandKoinTheme.colors.neutral0,
disabledContentColor = RebrandKoinTheme.colors.neutral0
)
)
}
Copy link

Choose a reason for hiding this comment

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

[Minor] isSubmitting 중 로딩 피드백 없음 — UX 문제

isSubmitting = true일 때 버튼이 비활성화되지만, 사용자 입장에서는 버튼이 왜 눌리지 않는지 알기 어렵습니다. 로딩 인디케이터를 추가하면 제출 진행 중임을 명확히 전달할 수 있습니다.

Suggested change
text = stringResource(R.string.callvan_create_submit),
onClick = onSubmit,
enabled = isFormComplete && !isSubmitting,
modifier = Modifier.fillMaxWidth(),
shape = RebrandKoinTheme.shapes.small,
textStyle = RebrandKoinTheme.typography.bold16,
contentPadding = PaddingValues(vertical = 14.dp),
colors = ButtonDefaults.buttonColors(
containerColor = RebrandKoinTheme.colors.primary500,
disabledContainerColor = RebrandKoinTheme.colors.neutral400,
contentColor = RebrandKoinTheme.colors.neutral0,
disabledContentColor = RebrandKoinTheme.colors.neutral0
)
)
}
FilledButton(
text = if (isSubmitting) "" else stringResource(R.string.callvan_create_submit),
onClick = onSubmit,
enabled = isFormComplete && !isSubmitting,
modifier = Modifier.fillMaxWidth(),
shape = RebrandKoinTheme.shapes.small,
textStyle = RebrandKoinTheme.typography.bold16,
contentPadding = PaddingValues(vertical = 14.dp),
colors = ButtonDefaults.buttonColors(
containerColor = RebrandKoinTheme.colors.primary500,
disabledContainerColor = RebrandKoinTheme.colors.neutral400,
contentColor = RebrandKoinTheme.colors.neutral0,
disabledContentColor = RebrandKoinTheme.colors.neutral0
)
) {
if (isSubmitting) {
CircularProgressIndicator(
modifier = Modifier.size(20.dp),
color = RebrandKoinTheme.colors.neutral0,
strokeWidth = 2.dp
)
}
}

FilledButton이 content slot을 지원하지 않는 경우, Box로 버튼 위에 CircularProgressIndicator를 오버레이하는 방식도 가능합니다.

@kongwoojin
Copy link
Member

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Mar 16, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/component/CallvanParticipantsSection.kt`:
- Around line 87-115: Replace the two standalone Icon composables used for
increment/decrement (the Icon with
ImageVector.vectorResource(R.drawable.ic_remove) using onDecrement and the Icon
with Icons.Default.Add using onIncrement) with IconButton wrappers and provide
explicit contentDescription strings via stringResource (e.g.,
stringResource(R.string.decrease_participants) and
stringResource(R.string.increase_participants)); keep the same imageVector,
tint, size and padding, move the clickable behavior to the IconButton (onClick =
onDecrement / onClick = onIncrement), and remove contentDescription = null so
screen readers can announce the buttons.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 15f23322-dc7b-45a7-b4fa-cbadf73f52bc

📥 Commits

Reviewing files that changed from the base of the PR and between 1b0e7a7 and 4deb0bc.

📒 Files selected for processing (1)
  • feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/component/CallvanParticipantsSection.kt

Comment on lines +87 to +115
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_remove),
contentDescription = null,
tint = RebrandKoinTheme.colors.primary500,
modifier = Modifier
.clickable(onClick = onDecrement)
.padding(horizontal = 8.dp, vertical = 7.dp)
.size(24.dp)
)
Box(
modifier = Modifier
.border(width = 1.dp, color = RebrandKoinTheme.colors.neutral400)
.padding(horizontal = 24.dp, vertical = 8.dp),
contentAlignment = Alignment.Center
) {
Text(
text = count.toString(),
style = RebrandKoinTheme.typography.regular14,
color = RebrandKoinTheme.colors.neutral800
)
}
Icon(
imageVector = Icons.Default.Add,
contentDescription = null,
tint = RebrandKoinTheme.colors.primary500,
modifier = Modifier
.clickable(onClick = onIncrement)
.padding(horizontal = 8.dp, vertical = 7.dp)
.size(24.dp)
Copy link

@coderabbitai coderabbitai bot Mar 16, 2026

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:

cd /tmp/repo && wc -l feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/component/CallvanParticipantsSection.kt

Repository: BCSDLab/KOIN_ANDROID

Length of output: 123


🏁 Script executed:

cd /tmp/repo && sed -n '80,120p' feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/component/CallvanParticipantsSection.kt

Repository: BCSDLab/KOIN_ANDROID

Length of output: 123


🏁 Script executed:

cd /tmp/repo && cat -n feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/component/CallvanParticipantsSection.kt

Repository: BCSDLab/KOIN_ANDROID

Length of output: 123


🏁 Script executed:

cd /tmp/repo && fd -e strings.xml feature/callvan && head -50 feature/callvan/src/main/res/values/strings.xml

Repository: BCSDLab/KOIN_ANDROID

Length of output: 123


🏁 Script executed:

cd /tmp/repo && rg "IconButton" feature/callvan --type kt -A 3 | head -50

Repository: BCSDLab/KOIN_ANDROID

Length of output: 123


🏁 Script executed:

wc -l feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/component/CallvanParticipantsSection.kt

Repository: BCSDLab/KOIN_ANDROID

Length of output: 181


🏁 Script executed:

sed -n '80,120p' feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/component/CallvanParticipantsSection.kt

Repository: BCSDLab/KOIN_ANDROID

Length of output: 1625


🏁 Script executed:

cat -n feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/component/CallvanParticipantsSection.kt

Repository: BCSDLab/KOIN_ANDROID

Length of output: 6155


🏁 Script executed:

fd -e strings.xml feature/callvan

Repository: BCSDLab/KOIN_ANDROID

Length of output: 429


🏁 Script executed:

rg "IconButton" feature/callvan --type kt -A 3 | head -100

Repository: BCSDLab/KOIN_ANDROID

Length of output: 90


🏁 Script executed:

find feature/callvan -name "strings.xml" -type f

Repository: BCSDLab/KOIN_ANDROID

Length of output: 111


🏁 Script executed:

cat feature/callvan/src/main/res/values/strings.xml

Repository: BCSDLab/KOIN_ANDROID

Length of output: 6301


🏁 Script executed:

rg "callvan_create_participants" feature/callvan/src/main/res/values/strings.xml

Repository: BCSDLab/KOIN_ANDROID

Length of output: 358


🏁 Script executed:

rg "IconButton" feature --type kotlin -B 2 -A 5

Repository: BCSDLab/KOIN_ANDROID

Length of output: 50376


🏁 Script executed:

rg "contentDescription" feature/callvan --type kotlin -B 1 -A 1

Repository: BCSDLab/KOIN_ANDROID

Length of output: 10298


🏁 Script executed:

rg "callvan_create_participants" feature/callvan/src/main/res/values/ --type kotlin

Repository: BCSDLab/KOIN_ANDROID

Length of output: 46


🏁 Script executed:

grep -r "decrement\|increment" feature/callvan/src/main/res/values/

Repository: BCSDLab/KOIN_ANDROID

Length of output: 46


🏁 Script executed:

find feature/callvan/src/main/res/values -name "strings.xml" -exec grep -l "button\|icon\|action" {} \;

Repository: BCSDLab/KOIN_ANDROID

Length of output: 46


클릭 가능한 증감 버튼에 접근성 라벨이 없어 스크린리더 사용자가 기능을 식별할 수 없습니다.

88-89번 줄과 109-110번 줄의 클릭 가능한 아이콘 2개가 contentDescription = null로 설정되어 있어 스크린리더 사용자에게 증가/감소 버튼의 의미가 전달되지 않습니다. IconButton으로 변경하고 stringResource를 통해 명시적 contentDescription을 제공해 주세요.

수정 예시
+import androidx.compose.material3.IconButton
@@
-            Icon(
-                imageVector = ImageVector.vectorResource(R.drawable.ic_remove),
-                contentDescription = null,
-                tint = RebrandKoinTheme.colors.primary500,
-                modifier = Modifier
-                    .clickable(onClick = onDecrement)
-                    .padding(horizontal = 8.dp, vertical = 7.dp)
-                    .size(24.dp)
-            )
+            IconButton(onClick = onDecrement) {
+                Icon(
+                    imageVector = ImageVector.vectorResource(R.drawable.ic_remove),
+                    contentDescription = stringResource(R.string.callvan_create_participants_decrement),
+                    tint = RebrandKoinTheme.colors.primary500,
+                )
+            }
@@
-            Icon(
-                imageVector = Icons.Default.Add,
-                contentDescription = null,
-                tint = RebrandKoinTheme.colors.primary500,
-                modifier = Modifier
-                    .clickable(onClick = onIncrement)
-                    .padding(horizontal = 8.dp, vertical = 7.dp)
-                    .size(24.dp)
-            )
+            IconButton(onClick = onIncrement) {
+                Icon(
+                    imageVector = Icons.Default.Add,
+                    contentDescription = stringResource(R.string.callvan_create_participants_increment),
+                    tint = RebrandKoinTheme.colors.primary500,
+                )
+            }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/component/CallvanParticipantsSection.kt`
around lines 87 - 115, Replace the two standalone Icon composables used for
increment/decrement (the Icon with
ImageVector.vectorResource(R.drawable.ic_remove) using onDecrement and the Icon
with Icons.Default.Add using onIncrement) with IconButton wrappers and provide
explicit contentDescription strings via stringResource (e.g.,
stringResource(R.string.decrease_participants) and
stringResource(R.string.increase_participants)); keep the same imageVector,
tint, size and padding, move the clickable behavior to the IconButton (onClick =
onDecrement / onClick = onIncrement), and remove contentDescription = null so
screen readers can announce the buttons.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

이 사항은 제외하고 검토 후 approve

Copy link

Choose a reason for hiding this comment

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

🧩 Analysis chain

@JaeYoung290, 해당 접근성 관련 사항(increment/decrement 아이콘의 contentDescription)은 검토에서 제외하겠습니다. PR을 승인 처리하였습니다.


🏁 Script executed:

gh pr review 1326 --repo BCSDLab/KOIN_ANDROID --approve --body "접근성 관련 사항(contentDescription)은 개발자 요청에 따라 제외하고 검토하였습니다. 나머지 구현 사항에 이상 없어 승인합니다."

Length of output: 0


@JaeYoung290, PR #1326 승인이 완료되었습니다. increment/decrement 아이콘의 contentDescription 관련 접근성 사항은 향후 리뷰에서도 제외하도록 기록해 두겠습니다.


✏️ Learnings added
Learnt from: JaeYoung290
Repo: BCSDLab/KOIN_ANDROID PR: 1326
File: feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/component/CallvanParticipantsSection.kt:87-115
Timestamp: 2026-03-16T04:02:16.200Z
Learning: In BCSDLab/KOIN_ANDROID feature/callvan module, the use of `contentDescription = null` on clickable Icon composables (e.g., increment/decrement buttons in CallvanParticipantsSection.kt) is intentional per developer (JaeYoung290) preference. Do not flag missing contentDescription as an accessibility issue in this module.

Learnt from: JaeYoung290
Repo: BCSDLab/KOIN_ANDROID PR: 1326
File: feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/component/CallvanParticipantsSection.kt:87-115
Timestamp: 2026-03-16T04:02:16.200Z
Learning: In BCSDLab/KOIN_ANDROID feature/callvan module, the use of `contentDescription = null` on clickable Icon composables (e.g., increment/decrement buttons in CallvanParticipantsSection.kt) is intentional per developer (JaeYoung290) preference. Do not flag missing contentDescription as an accessibility issue in this module.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: CR
Repo: BCSDLab/KOIN_ANDROID PR: 0
File: feature/article/AGENTS.md:0-0
Timestamp: 2026-02-26T14:13:24.114Z
Learning: Applies to feature/article/src/main/java/**/component/**/*.kt : Create Compose components following design system patterns with Composable annotation, state parameters, and Preview composables

Learnt from: CR
Repo: BCSDLab/KOIN_ANDROID PR: 0
File: core/designsystem/AGENTS.md:0-0
Timestamp: 2026-02-26T14:11:23.114Z
Learning: Applies to core/designsystem/**/component/button/*.kt : Button components must provide loading state, handle overflow text gracefully, support icons, and be accessible with proper content descriptions

Learnt from: KYM-P
Repo: BCSDLab/KOIN_ANDROID PR: 1313
File: feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/component/CallvanPersonIcon.kt:44-55
Timestamp: 2026-03-07T09:47:59.923Z
Learning: In `feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/component/CallvanPersonIcon.kt`, the hardcoded color values `Color(0xFFD39AFE)` (case 1) and `Color(0xFFC969FC)` (case 3) in `callvanPersonIconColor()` are intentional to match design specs. These intermediate purple shades (~primary250 and ~primary350 level) do not have corresponding tokens in `RebrandKoinColorPalette` defined in `core/designsystem/src/main/java/in/koreatech/koin/core/designsystem/theme/Color.kt`, so hardcoding is unavoidable until new palette tokens are added.

Learnt from: JaeYoung290
Repo: BCSDLab/KOIN_ANDROID PR: 1324
File: feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/component/CallvanDateField.kt:0-0
Timestamp: 2026-03-15T17:07:10.727Z
Learning: In BCSDLab/KOIN_ANDROID feature/callvan module, import ordering violations should be excluded from code review; user (JaeYoung290) has explicitly requested not to flag import order issues in this module.

Learnt from: CR
Repo: BCSDLab/KOIN_ANDROID PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-05T13:15:27.906Z
Learning: Applies to feature/article/**/*.kt : feature/article module uses hybrid UI: Article/search/keyword screens use XML Fragments + Navigation Component; Lost & Found uses pure Compose with Orbit MVI; new features SHOULD use Compose

Learnt from: CR
Repo: BCSDLab/KOIN_ANDROID PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-05T13:15:27.906Z
Learning: Applies to feature/**/*.kt : feature/* modules (except article) MUST use Jetpack Compose with *Screen + *ScreenImpl pattern and Orbit MVI

Learnt from: CR
Repo: BCSDLab/KOIN_ANDROID PR: 0
File: core/onboarding/AGENTS.md:0-0
Timestamp: 2026-02-26T14:12:12.825Z
Learning: Applies to core/onboarding/src/main/java/**/OnboardingType.kt : Use StringRes annotation for descriptionResId property in OnboardingType enum. Use 0 for descriptionResId when the onboarding type triggers an action instead of showing a tooltip

Learnt from: CR
Repo: BCSDLab/KOIN_ANDROID PR: 0
File: koin/AGENTS.md:0-0
Timestamp: 2026-02-26T14:16:01.759Z
Learning: Applies to koin/src/main/java/in/koreatech/koin/**/{*Activity,*Fragment}.kt : MUST embed new Compose widgets via `ComposeView.setContent {}` within XML layouts instead of creating full Compose screens

Learnt from: CR
Repo: BCSDLab/KOIN_ANDROID PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-05T13:15:27.906Z
Learning: Applies to koin/**/*.kt : New features in koin/ module SHOULD embed Compose widgets via ComposeView.setContent {} within XML layouts (NOT full Compose screens)

Learnt from: CR
Repo: BCSDLab/KOIN_ANDROID PR: 0
File: core/designsystem/AGENTS.md:0-0
Timestamp: 2026-02-26T14:11:23.114Z
Learning: Applies to core/designsystem/**/component/dialog/*.kt : Create dialog components using scale-based color tokens and weight+size typography tokens, handle dismiss request properly, and follow Material Design dialog patterns

Learnt from: CR
Repo: BCSDLab/KOIN_ANDROID PR: 0
File: core/AGENTS.md:0-0
Timestamp: 2026-02-26T14:10:51.888Z
Learning: Applies to core/**/*.kt : MUST maintain backwards compatibility when modifying utilities used across modules. SHOULD add Deprecated annotations with replaceWith when utilities are superseded by Compose equivalents.

Learnt from: CR
Repo: BCSDLab/KOIN_ANDROID PR: 0
File: feature/store/AGENTS.md:0-0
Timestamp: 2026-02-26T14:15:11.068Z
Learning: Applies to feature/store/src/main/java/in/koreatech/koin/feature/store/ui/**/*.kt : Follow the two-function Compose pattern: outer screen function handles ViewModel state collection and side effects, inner Impl function contains composable UI implementation with preview

If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

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.

접근성 관련 사항(contentDescription)은 개발자 요청에 따라 제외하고 검토하였습니다. 나머지 구현 사항에 이상 없어 승인합니다.

Comment on lines +57 to +63
Text(
text = buildAnnotatedString {
withStyle(RebrandKoinTheme.typography.bold12.toSpanStyle()) {
append(stringResource(R.string.callvan_create_participants_hint_bold))
}
append(stringResource(R.string.callvan_create_participants_hint_normal))
},
Copy link

Choose a reason for hiding this comment

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

[Minor] buildAnnotatedString을 remember로 감싸지 않음

힌트 문자열은 컴포지션 내에서 절대 변하지 않는 정적 값이지만, 현재는 리컴포지션이 발생할 때마다 buildAnnotatedString 블록이 새로운 AnnotatedString 객체를 생성합니다. remember로 캐싱하면 불필요한 객체 생성을 방지할 수 있습니다.

Suggested change
Text(
text = buildAnnotatedString {
withStyle(RebrandKoinTheme.typography.bold12.toSpanStyle()) {
append(stringResource(R.string.callvan_create_participants_hint_bold))
}
append(stringResource(R.string.callvan_create_participants_hint_normal))
},
text = remember {
buildAnnotatedString {
withStyle(RebrandKoinTheme.typography.bold12.toSpanStyle()) {
append(stringResource(R.string.callvan_create_participants_hint_bold))
}
append(stringResource(R.string.callvan_create_participants_hint_normal))
}
},

참고: stringResource@Composable 함수 안에서만 호출 가능하므로, remember 블록 내에서 직접 호출하려면 미리 변수로 추출해야 합니다.

val boldText = stringResource(R.string.callvan_create_participants_hint_bold)
val normalText = stringResource(R.string.callvan_create_participants_hint_normal)
val hintText = remember(boldText, normalText) {
    buildAnnotatedString {
        withStyle(RebrandKoinTheme.typography.bold12.toSpanStyle()) { append(boldText) }
        append(normalText)
    }
}

)
}
Icon(
imageVector = Icons.Default.Add,
Copy link

Choose a reason for hiding this comment

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

[Trivial] 아이콘 소스 불일치

감소 버튼은 커스텀 벡터(R.drawable.ic_remove)를 사용하고, 증가 버튼은 Material 기본 아이콘(Icons.Default.Add)을 사용합니다. 시각적 일관성을 위해 두 아이콘을 같은 소스에서 사용하는 것이 좋습니다. 커스텀 아이콘 R.drawable.ic_add가 있다면 이를 활용하세요.

Suggested change
imageVector = Icons.Default.Add,
imageVector = ImageVector.vectorResource(R.drawable.ic_add),

Copy link
Collaborator

Choose a reason for hiding this comment

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

아이콘 소스 통일 필요

Comment on lines +44 to +57
FilledButton(
text = stringResource(R.string.callvan_create_submit),
onClick = onSubmit,
enabled = isFormComplete && !isSubmitting,
modifier = Modifier.fillMaxWidth(),
shape = RebrandKoinTheme.shapes.small,
textStyle = RebrandKoinTheme.typography.bold16,
contentPadding = PaddingValues(vertical = 14.dp),
colors = ButtonDefaults.buttonColors(
containerColor = RebrandKoinTheme.colors.primary500,
disabledContainerColor = RebrandKoinTheme.colors.neutral400,
contentColor = RebrandKoinTheme.colors.neutral0,
disabledContentColor = RebrandKoinTheme.colors.neutral0
)
Copy link

Choose a reason for hiding this comment

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

[Major] isSubmitting 상태에서 로딩 UI 없음

isSubmitting = true일 때 버튼이 비활성화되기만 할 뿐, 사용자에게 처리 중임을 알리는 시각적 피드백이 전혀 없습니다. 프리뷰 이름도 "Loading"이지만 실제 로딩 인디케이터가 없어 앱이 멈춘 것처럼 보일 수 있습니다.

FilledButton이 로딩 상태를 지원한다면:

FilledButton(
    text = if (isSubmitting) "" else stringResource(R.string.callvan_create_submit),
    onClick = onSubmit,
    enabled = isFormComplete && !isSubmitting,
    // ...
)

또는 버튼 내부에 CircularProgressIndicator를 조건부로 표시하는 방식을 고려하세요:

if (isSubmitting) {
    CircularProgressIndicator(
        modifier = Modifier.size(20.dp),
        color = RebrandKoinTheme.colors.neutral0,
        strokeWidth = 2.dp
    )
} else {
    Text(text = stringResource(R.string.callvan_create_submit))
}

FilledButton 컴포넌트가 isLoading 파라미터를 제공한다면 그것을 활용하는 것이 가장 이상적입니다.

Comment on lines +29 to +31
Column(modifier = modifier.fillMaxWidth()) {
HorizontalDivider(color = RebrandKoinTheme.colors.neutral100)
Column(
Copy link

Choose a reason for hiding this comment

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

[Trivial] 불필요한 Column 중첩

외부 ColumnHorizontalDivider와 내부 Column 하나만 담고 있습니다. Column 대신 modifier를 직접 내부 레이아웃에 적용하고 HorizontalDivider를 형제 요소로 두면 중첩을 줄일 수 있습니다. 다만 이 구조는 호출부에서 modifier를 바깥 컨테이너에 적용해야 할 때 유용할 수 있으므로 현재 맥락에서 의도된 것이라면 무시해도 됩니다.

@sonarqubecloud
Copy link

Copy link
Collaborator

@KYM-P KYM-P left a comment

Choose a reason for hiding this comment

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

Remember 상태
로딩 화면
정도는 필요해 보입니다.

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.

3 participants