Skip to content

[Feat/#36] participant overview, dropdown 구현#43

Merged
cmj7271 merged 28 commits intodevelopfrom
feat/#36-participant-overview-and-dropdown
Jan 16, 2026
Merged

[Feat/#36] participant overview, dropdown 구현#43
cmj7271 merged 28 commits intodevelopfrom
feat/#36-participant-overview-and-dropdown

Conversation

@cmj7271
Copy link
Copy Markdown
Contributor

@cmj7271 cmj7271 commented Jan 15, 2026

Related issue 🛠️

Work Description ✏️

  • participant overview 구현
  • participant dropdown 구현

Screenshot 📸

스크린샷 영상
스크린샷 2026-01-15 오후 6 03 48
스크린샷 2026-01-15 오후 6 04 31
2026-01-15.11.15.14.mov

Uncompleted Tasks 😅

To Reviewers 📢

Summary by CodeRabbit

  • 새 기능

    • 히스토리 참가자 카드·드롭다운·상세·오버뷰 UI 추가.
    • 파티 상세 화면 대폭 강화(이미지 페이저, 참여자·업로더·배송 정보, 콘텐츠 섹션).
    • 사진 업로드 UI, 옵션 가격 편집 컴포넌트, 힌트 툴팁, 다양한 상품/팟 카드 추가.
  • 문서/현지화

    • 참가자·파티 관련 다수 문자열 리소스 추가/정리.
  • 개선

    • 프로필 이미지 로딩 안정화, 금액 표시 포맷(toMoneyString) 추가, 필드 오류 표시 조건 개선, 내비게이션 처리 개선.

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

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 15, 2026

📝 Walkthrough

Walkthrough

히스토리 관련 UI(Overview, Detail, Dropdown)와 다수의 파티/유저 도메인 모델 추가, 내비게이션·PartyDetail 화면·뷰모델 흐름 변경, 디자인시스템 필드·에러 처리 로직 수정, 여러 신규 Compose 컴포넌블이 추가되었습니다.

Changes

Cohort / File(s) 변경 사항
History: Detail 컴포넌트 & 모델
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDetail.kt
DepositItem 데이터 클래스, DetailState sealed 인터페이스(구현체 포함), FieldType enum 추가. HistoryParticipantDetail @Composable 추가(아바타·예치금 목록·동적 필드·선택적 액션 버튼). 프리뷰 포함.
History: Dropdown 컴포넌트
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDropdown.kt
HistoryParticipantDropdown @Composable 추가(헤더, 상태 라벨, 확장 토글 via AnimatedVisibility). 내부 ParticipantDropdownHeader 및 프리뷰 포함.
History: Overview 컴포넌트
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt
HistoryParticipantOverview @Composable 추가(멤버리스트·유저정보·배송/가격 칩), 프리뷰 포함.
History: 카드 항목
app/src/main/java/com/poti/android/presentation/history/component/HistoryCardItem.kt
HistoryCardItem 컴포저블 및 CardHistorySize enum 추가(이미지, 텍스트 스타일 분기, 클릭 처리).
문자열 리소스
app/src/main/res/values/strings.xml
다수 문자열 추가·재배치(액션 버튼, 파티 상세, 히스토리 필드 등).
디자인시스템: 프로필·필드·에러 처리
app/src/main/java/com/poti/android/core/designsystem/...
PotiProfileSummary의 image url을 nullable로 변경하고 AsyncImage로 교체. FieldErrorMessage 제거, 여러 필드에서 PotiErrorMessage로 조건부 렌더링 전환. PotiBasicField/PotiShortTextField 등에서 trailingIcon을 nullable로 변경.
레이아웃: 반응형 패딩 유틸 적용
app/src/main/java/com/poti/android/core/designsystem/component/navigation/*
고정 패딩 → screenWidthDp(...) 기반 반응형 패딩으로 변경(헤더/버튼 관련).
유틸/도메인 모델 추가
app/src/main/java/com/poti/android/core/common/extension/IntExt.kt, app/src/main/java/com/poti/android/domain/model/...
Int.toMoneyString() 확장 추가. DeliveryOption, Participant, PartyDetail, PartyImage, UserSummary 데이터 클래스 및 PartyStatusType enum 추가.
내비게이션·라우트 변경
app/src/main/java/com/poti/android/presentation/*/navigation/*, app/src/main/java/com/poti/android/presentation/main/PotiNavHost.kt
여러 NavGraph API가 콜백→NavController 주입으로 변경. Profile/PartyDetail 라우트가 파라미터화(유저Id/partyId)로 변경 및 관련 NavController 확장 함수 시그니처 변경.
PartyDetail: 화면·VM·컴포넌트 변경
app/src/main/java/com/poti/android/presentation/party/detail/*
Route/Screen/Contracts/PartyDetailViewModel 대대적 변경: BaseViewModel 패턴·SavedStateHandle 사용, Intent/Effect 도입, LoadPartyDetail 흐름, LaunchedEffect 기반 네비게이션 처리. Screen이 Scaffold 기반으로 확장되고 여러 서브컴포넌트(헤더, 콘텐츠, 참가자, 업로더 등) 추가.
Party create / home / goods UI 컴포넌트 추가
app/src/main/java/com/poti/android/presentation/party/create/component/*, .../home/component/*, .../goodsfilter/component/PotsCard.kt
CreatePhotoUpload, EditOptionPrice, HintToolTip, GoodsLargeCard, GoodsSmallCard, PotsCard 등 다수 UI 컴포저블 추가(프리뷰 포함).

Sequence Diagram(s)

sequenceDiagram
    participant UI as UI (화면)
    participant Route as PartyDetailRoute
    participant VM as PartyDetailViewModel
    participant Repo as DataProvider
    participant Nav as NavController
    participant Screen as PartyDetailScreen

    UI->>Route: 진입(partyId)
    Route->>VM: init -> processIntent(LoadPartyDetail)
    VM->>Repo: loadPartyDetail(partyId)
    Repo-->>VM: PartyDetail (ApiState.Success)
    VM-->>Route: uiState 업데이트 (partyDetail: Success)
    Route->>Screen: render(partyDetail)
    Note right of VM: 사용자 액션 -> processIntent(OnJoinClick / OnBackClick / OnUploaderClick)
    VM-->>Route: effect 발행 (NavigateToJoin / NavigateBack / NavigateToProfile)
    Route->>Nav: 네비게이션 실행
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

🌟 Feat

Suggested reviewers

  • sonyerim
  • jyvnee
  • doyeon0307
🚥 Pre-merge checks | ✅ 2 | ❌ 3
❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning PR에는 이슈 #36 범위를 벗어난 변경사항들이 다수 포함되어 있습니다. PartyDetail 관련 데이터 클래스, PartyDetailScreen 구현, 네비게이션 변경, field 컴포넌트 수정, 문자열 리소스 추가 등이 원래 이슈 범위를 초과합니다. 이슈 #36에서 요구하는 participant 컴포넌트(overview, detail, dropdown) 구현에만 집중하도록 변경사항을 줄이거나, 추가 변경사항들을 별도 이슈로 분리해주세요.
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.
Linked Issues check ❓ Inconclusive PR에서 participant-overview [#36], participant-dropdown [#36] 구현이 완료되었으나, 이슈 #36에서 요구하는 participant-detail 구현은 미완료된 상태입니다. 이슈 #36의 세 가지 요구사항 중 participant-detail 구현 상태를 명확히 하고, 필요시 별도 이슈로 분리하여 추적하거나 본 PR에 포함시킬지 결정해주세요.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 participant overview와 dropdown 구현이라는 핵심 변경사항을 명확하게 요약하고 있습니다.
Description check ✅ Passed PR 설명이 관련 이슈, 작업 내용, 스크린샷을 포함하고 있으며 템플릿 구조를 따르고 있습니다.

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

✨ Finishing touches
  • 📝 Generate docstrings

🧹 Recent nitpick comments
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDetail.kt (1)

39-109: DepositItem/DetailState 안정성(@Immutable) 적용 여부를 검토해 주세요.
UiState로 쓰인다면 List/Pair 때문에 Compose가 불안정 타입으로 판단해 불필요한 리컴포지션이 생길 수 있습니다. 불변성을 보장할 수 있다면 @Immutable(또는 immutable 컬렉션) 적용을 고려해 주세요. 코딩 가이드라인 기준입니다.

♻️ 적용 예시
+import androidx.compose.runtime.Immutable
+
+@Immutable
 data class DepositItem(
     val type: PotiItemOptionType,
     val name: String,
     val price: Int,
 )

+@Immutable
 sealed interface DetailState {
     val fields: List<Pair<FieldType, String>>
     ...
 }

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8554a06 and f69eb54.

📒 Files selected for processing (3)
  • app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDetail.kt
  • app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt
  • app/src/main/res/values/strings.xml
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt

⚙️ CodeRabbit configuration file

**/*.kt: 코드를 리뷰할 때 다음 Kotlin 및 Jetpack Compose 모범 사례를 중점적으로 확인:

  1. Jetpack Compose 성능 및 UI 최적화:

    • Recomposition 방지: 불필요한 리컴포지션을 유발하는 코드를 지적해줘. 특히 LazyColumn 등 리스트에서 key 파라미터가 누락되었는지 확인해줘.
    • 스마트한 계산: 계산 비용이 높은 로직은 rememberderivedStateOf로 감싸져 있는지 확인해줘.
    • 안정성(Stability): List나 일반 클래스를 State로 사용할 때, 불필요한 리컴포지션을 막기 위해 @Immutable이나 @Stable 어노테이션 사용을 제안해줘.
    • Modifier 규칙: Modifier는 항상 컴포저블 함수의 첫 번째 선택적 파라미터로 받아야 하며, 재사용성을 위해 외부에서 주입받아야 해.
    • 리소스 사용: 하드코딩된 값(색상, 크기, 문자열) 대신 Theme, Dimens, strings.xml 리소스를 사용하는지 체크해줘.
    • 접근성 예외: Icon이나 Image 컴포저블의 contentDescriptionnull인 경우는 장식용(Decorative) 이미지로 간주하고 지적하지 마.
  2. MVI (Model-View-Intent) 아키텍처 및 상태 관리:

    • 단방향 데이터 흐름(UDF): UI는 상태를 직접 변경할 수 없고, 반드시 Intent(또는 Event)를 통해 ViewModel로 요청을 보내야 해.
    • 불변 상태(Immutable State): UiState는 반드시 data class + val 프로퍼티로 구성되어야 하며, 상태 업데이트 시 .copy()를 사용해야 해.
    • Side Effect 분리: 네비게이션, Toast 같은 일회성 이벤트는 UiState가 아닌 Channel이나 SharedFlow로 처리하여 화면 회전 시 재발생하지 않도록 해야 해.
    • 안전한 수집: UI에서 Flow 수집 시 collectAsStateWithLifecycle()을 사용하여 생명주기를 준수하는지 확인해줘.
    • 상태 노출: ViewModel 내부에서 StateFlowSharedFlow를 올바르게 노출하고 있는지 확인해줘.
    • 호이스팅(Hoisting): 상태나 UI 이벤트 처리가 적절하게 호이스팅되었는지 확인해줘. UI 로직과 비즈니스 로직을 분리하고, UI가 로직을 직접 수행하지 않도록 확인해줘.
  3. Clean Architecture (Dependency Rule) 준수:

    • Domain 순수성: Domain 레이어(domain 패키지)는 절대로 안드로이드 의존성(android.*, R 클래스 등)을 가져서는 안 돼. 순수 Kotlin 코드인지 엄격히 확인해줘.
    • 데이터 노출 금지: Data 레이어의 DTO(@Entity, @SerializedName)가 UI나 Domain 레이어로 바로 노출되면 안 돼. 반드시 Mapper를 통해 변환했는지 확인해줘.
    • 계층 간 의존성: UI -> Domain <- Data 방향이 올바른지, UI가 Data 레이어를 직접 참조하고 있진 않은지 감시해줘.
  4. Kotlin & Coroutines 안전성:

    • Scope 사용: GlobalScope 사용을 금지하고, viewModelScopelifecycleScope 사용을 권장해줘.
    • Dispatcher 주입: Dispatchers.IO 등을 하드코딩하지 말고, 테스트 용이성을 위해 생성자를 통해 주입(Injection)받도록 제안해줘.
    • 예외 처리: Coroutine 실행 시 runCatching이나 CoroutineExceptionHandler 등을 통해 예외 처리가 누락되지 않았는지 확인해줘.
    • Null-Safety: 불필요한 !! 연산자 사용을 지적...

Files:

  • app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDetail.kt
  • app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt
🧬 Code graph analysis (2)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDetail.kt (5)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiDivider.kt (1)
  • PotiDivider (28-39)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiListOptionPrice.kt (1)
  • PotiListOptionPrice (36-63)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiItemOption.kt (1)
  • PotiItemOption (41-67)
app/src/main/java/com/poti/android/core/designsystem/component/button/PotiInlineButton.kt (1)
  • PotiInlineButton (30-71)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt (4)
app/src/main/java/com/poti/android/core/common/util/DimensionUtil.kt (1)
  • screenHeightDp (12-18)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantStateLabel.kt (1)
  • HistoryParticipantStateLabel (40-56)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiItemOption.kt (1)
  • PotiItemOption (41-67)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
🔇 Additional comments (1)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt (1)

76-88: 가격 포맷을 stringResource로 처리한 점 좋습니다.
통화 단위 포맷을 리소스로 분리해 둔 덕분에 다국어/포맷 변경 대응이 쉬워졌어요.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@cmj7271 cmj7271 self-assigned this Jan 15, 2026
@cmj7271 cmj7271 requested a review from a team January 15, 2026 09:08
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

🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDetail.kt`:
- Around line 168-172: Replace the hardcoded "입금 금액" literal in the Text
composable inside HistoryParticipantDetail (HistoryParticipantDetail.kt) with a
string resource: add <string name="history_participant_deposit_amount">입금
금액</string> to strings.xml and update the Text call to use
stringResource(R.string.history_participant_deposit_amount) (import
androidx.compose.ui.res.stringResource if needed).

In
`@app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt`:
- Around line 82-83: The priceText utility uses String.format without an
explicit Locale which triggers ImplicitDefaultLocale; update the
priceText(price: Int) function to call String.format with an explicit Locale
(use Locale.KOREA to match HistoryParticipantDetail.kt) so the formatted
currency string is produced deterministically across locales.
- Around line 24-35: The Modifier parameter in HistoryParticipantOverview is
placed before required params but should be the first optional parameter; move
"modifier: Modifier = Modifier" so it comes after all required parameters
(memberList, userName, address, phone, deliveryMethod, price,
participantStageType, participantStatusType) and keep its default value, then
update any call sites that rely on positional args to use named arguments if
necessary.
🧹 Nitpick comments (8)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDetail.kt (4)

39-44: @Immutable 어노테이션 추가를 고려해 주세요.

DepositItemList로 Composable에 전달되고 있습니다. Compose의 안정성(Stability) 최적화를 위해 @Immutable 어노테이션을 추가하면 불필요한 리컴포지션을 방지할 수 있습니다.

♻️ 제안하는 변경사항
+import androidx.compose.runtime.Immutable
+
 // TODO [천민재] 맵핑 확장함수 추후 구현
+@Immutable
 data class DepositItem(
     val type: PotiItemOptionType,
     val name: String,
     val price: Int
 )

46-49: onClickLabelId@StringRes 어노테이션을 추가해 주세요.

onClickLabelId가 string resource ID를 담고 있으므로, 타입 안전성을 위해 @StringRes 어노테이션을 추가하시면 좋겠습니다.

♻️ 제안하는 변경사항
 sealed interface DetailState {
     val fields: List<Pair<FieldType, String>>
+    `@get`:StringRes
     val onClickLabelId: Int?
     val onConfirmClick: (() -> Unit)?

237-245: !! 연산자 사용을 개선해 주세요.

if 조건에서 null 체크를 했지만, sealed interface의 프로퍼티는 스마트 캐스트가 적용되지 않아 !!를 사용하고 계십니다. 로컬 변수를 활용하면 더 안전하고 깔끔하게 작성할 수 있습니다.

♻️ 제안하는 변경사항
-        if(detailState.onConfirmClick != null && detailState.onClickLabelId != null) {
-            PotiInlineButton(
-                text = stringResource(detailState.onClickLabelId!!),
-                onClick = detailState.onConfirmClick!!,
-                showIcon = false,
-                modifier = Modifier
-                    .fillMaxWidth()
-            )
-        }
+        val onConfirmClick = detailState.onConfirmClick
+        val onClickLabelId = detailState.onClickLabelId
+        if (onConfirmClick != null && onClickLabelId != null) {
+            PotiInlineButton(
+                text = stringResource(onClickLabelId),
+                onClick = onConfirmClick,
+                showIcon = false,
+                modifier = Modifier.fillMaxWidth()
+            )
+        }

249-252: 중복 함수를 공통 유틸리티로 추출하는 것을 고려해 주세요.

priceText 함수가 HistoryParticipantOverview.kt에도 동일하게 존재합니다. TODO 주석대로 추후 공통 유틸리티 함수로 추출하시면 DRY 원칙을 준수할 수 있습니다.

app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDropdown.kt (3)

55-61: 불필요한 람다 래퍼를 제거해 주세요.

onToggle = { onToggle() }에서 람다 래퍼가 불필요합니다. 함수 참조를 직접 전달하면 됩니다.

♻️ 제안하는 변경사항
             ParticipantDropdownHeader(
                 name = userName,
                 stageType = stageType,
                 statusType = statusType,
                 expanded = isExpanded,
-                onToggle = { onToggle() }
+                onToggle = onToggle
             )

77-84: Modifier 파라미터를 추가해 주세요.

코딩 가이드라인에 따르면, Modifier는 항상 컴포저블 함수의 첫 번째 선택적 파라미터로 받아야 합니다. private 함수라도 재사용성과 일관성을 위해 Modifier를 받는 것이 좋습니다.

♻️ 제안하는 변경사항
 `@Composable`
 private fun ParticipantDropdownHeader(
     name: String,
     stageType: ParticipantStateLabelStage,
     statusType: ParticipantStateLabelStatus,
     expanded: Boolean,
-    onToggle: () -> Unit
+    onToggle: () -> Unit,
+    modifier: Modifier = Modifier
 ) {
     Row(
-        modifier = Modifier
+        modifier = modifier
             .fillMaxWidth()
             .noRippleClickable { onToggle() },
         verticalAlignment = Alignment.CenterVertically
     ) {

113-115: Preview 함수를 private으로 변경해 주세요.

다른 파일들의 Preview 함수는 private으로 선언되어 있습니다. 일관성을 위해 이 Preview도 private으로 변경해 주세요.

♻️ 제안하는 변경사항
 `@Preview`
 `@Composable`
-fun HistoryParticipantDropdownPreview() {
+private fun HistoryParticipantDropdownPreview() {
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt (1)

27-27: 코드베이스 일관성을 위해 ImmutableList 사용을 고려해 주세요.

프로젝트에서 이미 PotiListRadio와 같은 다른 컴포저블들이 ImmutableList<String>을 사용하고 있습니다. memberList도 같은 패턴으로 변경하면 Compose 안정성 측면에서 더 일관된 코드가 될 수 있습니다. memberList.joinToString(", ")만 수행하는 단순한 사용 패턴이므로 긴급한 것은 아니지만, 컴포저블 파라미터의 안정성을 위해 다음 중 하나를 추천합니다:

  • memberList: ImmutableList<String> 변경
  • 호출 시 remember 래핑
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bdc6c07 and 1ee7e91.

📒 Files selected for processing (4)
  • app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDetail.kt
  • app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDropdown.kt
  • app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt
  • app/src/main/res/values/strings.xml
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt

⚙️ CodeRabbit configuration file

**/*.kt: 코드를 리뷰할 때 다음 Kotlin 및 Jetpack Compose 모범 사례를 중점적으로 확인:

  1. Jetpack Compose 성능 및 UI 최적화:

    • Recomposition 방지: 불필요한 리컴포지션을 유발하는 코드를 지적해줘. 특히 LazyColumn 등 리스트에서 key 파라미터가 누락되었는지 확인해줘.
    • 스마트한 계산: 계산 비용이 높은 로직은 rememberderivedStateOf로 감싸져 있는지 확인해줘.
    • 안정성(Stability): List나 일반 클래스를 State로 사용할 때, 불필요한 리컴포지션을 막기 위해 @Immutable이나 @Stable 어노테이션 사용을 제안해줘.
    • Modifier 규칙: Modifier는 항상 컴포저블 함수의 첫 번째 선택적 파라미터로 받아야 하며, 재사용성을 위해 외부에서 주입받아야 해.
    • 리소스 사용: 하드코딩된 값(색상, 크기, 문자열) 대신 Theme, Dimens, strings.xml 리소스를 사용하는지 체크해줘.
    • 접근성 예외: Icon이나 Image 컴포저블의 contentDescriptionnull인 경우는 장식용(Decorative) 이미지로 간주하고 지적하지 마.
  2. MVI (Model-View-Intent) 아키텍처 및 상태 관리:

    • 단방향 데이터 흐름(UDF): UI는 상태를 직접 변경할 수 없고, 반드시 Intent(또는 Event)를 통해 ViewModel로 요청을 보내야 해.
    • 불변 상태(Immutable State): UiState는 반드시 data class + val 프로퍼티로 구성되어야 하며, 상태 업데이트 시 .copy()를 사용해야 해.
    • Side Effect 분리: 네비게이션, Toast 같은 일회성 이벤트는 UiState가 아닌 Channel이나 SharedFlow로 처리하여 화면 회전 시 재발생하지 않도록 해야 해.
    • 안전한 수집: UI에서 Flow 수집 시 collectAsStateWithLifecycle()을 사용하여 생명주기를 준수하는지 확인해줘.
    • 상태 노출: ViewModel 내부에서 StateFlowSharedFlow를 올바르게 노출하고 있는지 확인해줘.
    • 호이스팅(Hoisting): 상태나 UI 이벤트 처리가 적절하게 호이스팅되었는지 확인해줘. UI 로직과 비즈니스 로직을 분리하고, UI가 로직을 직접 수행하지 않도록 확인해줘.
  3. Clean Architecture (Dependency Rule) 준수:

    • Domain 순수성: Domain 레이어(domain 패키지)는 절대로 안드로이드 의존성(android.*, R 클래스 등)을 가져서는 안 돼. 순수 Kotlin 코드인지 엄격히 확인해줘.
    • 데이터 노출 금지: Data 레이어의 DTO(@Entity, @SerializedName)가 UI나 Domain 레이어로 바로 노출되면 안 돼. 반드시 Mapper를 통해 변환했는지 확인해줘.
    • 계층 간 의존성: UI -> Domain <- Data 방향이 올바른지, UI가 Data 레이어를 직접 참조하고 있진 않은지 감시해줘.
  4. Kotlin & Coroutines 안전성:

    • Scope 사용: GlobalScope 사용을 금지하고, viewModelScopelifecycleScope 사용을 권장해줘.
    • Dispatcher 주입: Dispatchers.IO 등을 하드코딩하지 말고, 테스트 용이성을 위해 생성자를 통해 주입(Injection)받도록 제안해줘.
    • 예외 처리: Coroutine 실행 시 runCatching이나 CoroutineExceptionHandler 등을 통해 예외 처리가 누락되지 않았는지 확인해줘.
    • Null-Safety: 불필요한 !! 연산자 사용을 지적...

Files:

  • app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt
  • app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDropdown.kt
  • app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDetail.kt
🧬 Code graph analysis (3)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt (3)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantStateLabel.kt (1)
  • HistoryParticipantStateLabel (40-56)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiItemOption.kt (1)
  • PotiItemOption (41-67)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDropdown.kt (3)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDetail.kt (1)
  • HistoryParticipantDetail (119-247)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantStateLabel.kt (1)
  • HistoryParticipantStateLabel (40-56)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDetail.kt (5)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiDivider.kt (1)
  • PotiDivider (28-39)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiListOptionPrice.kt (1)
  • PotiListOptionPrice (36-63)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiItemOption.kt (1)
  • PotiItemOption (41-67)
app/src/main/java/com/poti/android/core/designsystem/component/button/PotiInlineButton.kt (1)
  • PotiInlineButton (30-71)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
🪛 detekt (1.23.8)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt

[warning] 83-83: String.format("%,d원", price) uses implicitly default locale for string formatting.

(detekt.potential-bugs.ImplicitDefaultLocale)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: PR CI Check (Lint & Build)
🔇 Additional comments (2)
app/src/main/res/values/strings.xml (1)

26-34: LGTM!

새로운 문자열 리소스가 잘 추가되었습니다. 네이밍 컨벤션도 기존 history_participant_* 패턴을 따르고 있어 일관성이 좋습니다.

app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDetail.kt (1)

249-252: LGTM!

Locale.KOREA를 명시적으로 지정하신 점이 좋습니다. 이 패턴을 HistoryParticipantOverview.kt의 동일한 함수에도 적용해 주시면 좋겠습니다.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Copy Markdown
Contributor

@doyeon0307 doyeon0307 left a comment

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

@doyeon0307 doyeon0307 left a comment

Choose a reason for hiding this comment

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

궁그미

doyeon0307 and others added 3 commits January 15, 2026 20:53
* [Feat/#41] ShortTextField에 trailingIcon 추가

* [Chore/#41] kdoc 주석 추가

* [Refactor/#41] FieldErrorMessage 삭제 및 필드에 PotiErrorMessage 적용

* [Refactor/#41] trailingIcon 기본값 null로 변경

* [Refactor/#41] 에러메시지 노출 조건 isNotEmpty -> isNotBlank 변경
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: 1

♻️ Duplicate comments (2)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt (2)

24-35: Modifier 파라미터 위치를 조정해 주세요.

코딩 가이드라인에 따르면, Modifier는 "첫 번째 선택적 파라미터"여야 합니다. 현재는 필수 파라미터들보다 앞에 위치해 있어요. HistoryParticipantStateLabel처럼 필수 파라미터들 뒤로 이동시켜 주세요.

♻️ 제안하는 변경사항
 `@Composable`
 fun HistoryParticipantOverview(
-    modifier: Modifier = Modifier,
     memberList: List<String>,
     userName: String,
     address: String,
     phone: String,
     deliveryMethod: String,
     price: Int,
     participantStageType: ParticipantStateLabelStage,
     participantStatusType: ParticipantStateLabelStatus,
+    modifier: Modifier = Modifier,
 ) {

85-86: Locale을 명시적으로 지정해 주세요.

HistoryParticipantDetail.kt의 동일한 함수와 일관성을 위해 Locale.KOREA를 명시적으로 지정해 주세요. 또한, 동일한 유틸 함수가 두 파일에 중복되어 있으니 공통 유틸로 추출하는 것도 고려해 보시면 좋겠어요.

🐛 제안하는 수정사항
+import java.util.Locale
+
 // TODO: [천민재] 임시 util 함수
-private fun priceText(price: Int) = String.format("%,d원", price)
+private fun priceText(price: Int) = String.format(
+    locale = Locale.KOREA,
+    format = "%,d원",
+    price,
+)
🧹 Nitpick comments (6)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt (1)

48-54: joinToString 결과를 remember로 캐싱하는 것을 권장드려요.

memberList.joinToString(", ")은 리컴포지션마다 새로운 문자열을 생성합니다. remember로 감싸서 불필요한 재계산을 방지하시면 성능에 도움이 될 수 있어요.

♻️ 제안하는 변경사항
+ val memberText = remember(memberList) { memberList.joinToString(", ") }
+
  Row(
      modifier = Modifier.fillMaxWidth(),
      horizontalArrangement = Arrangement.SpaceBetween,
  ) {
      Text(
-         text = memberList.joinToString(", "),
+         text = memberText,
          style = PotiTheme.typography.body16m,
          color = colors.black,
          maxLines = 1,
          overflow = TextOverflow.Ellipsis,
          modifier = Modifier.weight(1f),
      )
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDetail.kt (3)

40-44: 데이터 클래스에 @Immutable 어노테이션 추가를 고려해 주세요.

DepositItemList로 전달되어 Compose에서 사용되므로, 불필요한 리컴포지션을 방지하기 위해 @Immutable 어노테이션을 추가하시면 안정성(Stability) 측면에서 더 좋습니다.

♻️ 제안하는 변경사항
+import androidx.compose.runtime.Immutable
+
 // TODO [천민재] 맵핑 확장함수 추후 구현
+@Immutable
 data class DepositItem(
     val type: PotiItemOptionType,
     val name: String,
     val price: Int,
 )

188-191: PotiDividerfillMaxWidth() Modifier가 중복됩니다.

PotiDivider 컴포넌트 내부에 이미 fillMaxWidth()가 기본 적용되어 있어서, 여기서 추가로 지정하실 필요가 없습니다.

♻️ 제안하는 변경사항
                 PotiDivider(
                     styleType = PotiDividerStyle.SMALL,
-                    modifier = Modifier.fillMaxWidth(),
                 )

250-255: 중복된 priceText 함수를 공통 유틸리티로 추출해 주세요.

HistoryParticipantOverview.kt에도 동일한 priceText 함수가 존재합니다. DRY 원칙에 따라 공통 유틸리티 파일로 추출하시면 유지보수가 용이해집니다.

♻️ 제안하는 변경사항

예를 들어, core/common/util/PriceFormatter.kt에 추출:

package com.poti.android.core.common.util

import java.util.Locale

fun formatPrice(price: Int): String = String.format(
    locale = Locale.KOREA,
    format = "%,d원",
    price,
)

그 후 두 파일에서 공통 함수를 import하여 사용하시면 됩니다.

app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDropdown.kt (2)

54-59: 불필요한 람다 래핑을 제거해 주세요.

onToggle = { onToggle() } 형태는 매번 새로운 람다 객체를 생성하여 불필요한 리컴포지션을 유발할 수 있습니다. 함수 참조를 직접 전달하시면 됩니다.

♻️ 제안하는 변경사항
             ParticipantDropdownHeader(
                 name = userName,
                 stageType = stageType,
                 statusType = statusType,
                 expanded = isExpanded,
-                onToggle = { onToggle() },
+                onToggle = onToggle,
             )

119-121: Preview 함수를 private으로 변경해 주세요.

HistoryParticipantDropdownPreviewpublic으로 선언되어 있습니다. Preview 함수는 외부에 노출될 필요가 없으므로 private으로 변경하시는 것이 좋습니다.

♻️ 제안하는 변경사항
 `@Preview`
 `@Composable`
-fun HistoryParticipantDropdownPreview() {
+private fun HistoryParticipantDropdownPreview() {
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9ff7b3c and 6935dda.

📒 Files selected for processing (4)
  • app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDetail.kt
  • app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDropdown.kt
  • app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt
  • app/src/main/res/values/strings.xml
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/main/res/values/strings.xml
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt

⚙️ CodeRabbit configuration file

**/*.kt: 코드를 리뷰할 때 다음 Kotlin 및 Jetpack Compose 모범 사례를 중점적으로 확인:

  1. Jetpack Compose 성능 및 UI 최적화:

    • Recomposition 방지: 불필요한 리컴포지션을 유발하는 코드를 지적해줘. 특히 LazyColumn 등 리스트에서 key 파라미터가 누락되었는지 확인해줘.
    • 스마트한 계산: 계산 비용이 높은 로직은 rememberderivedStateOf로 감싸져 있는지 확인해줘.
    • 안정성(Stability): List나 일반 클래스를 State로 사용할 때, 불필요한 리컴포지션을 막기 위해 @Immutable이나 @Stable 어노테이션 사용을 제안해줘.
    • Modifier 규칙: Modifier는 항상 컴포저블 함수의 첫 번째 선택적 파라미터로 받아야 하며, 재사용성을 위해 외부에서 주입받아야 해.
    • 리소스 사용: 하드코딩된 값(색상, 크기, 문자열) 대신 Theme, Dimens, strings.xml 리소스를 사용하는지 체크해줘.
    • 접근성 예외: Icon이나 Image 컴포저블의 contentDescriptionnull인 경우는 장식용(Decorative) 이미지로 간주하고 지적하지 마.
  2. MVI (Model-View-Intent) 아키텍처 및 상태 관리:

    • 단방향 데이터 흐름(UDF): UI는 상태를 직접 변경할 수 없고, 반드시 Intent(또는 Event)를 통해 ViewModel로 요청을 보내야 해.
    • 불변 상태(Immutable State): UiState는 반드시 data class + val 프로퍼티로 구성되어야 하며, 상태 업데이트 시 .copy()를 사용해야 해.
    • Side Effect 분리: 네비게이션, Toast 같은 일회성 이벤트는 UiState가 아닌 Channel이나 SharedFlow로 처리하여 화면 회전 시 재발생하지 않도록 해야 해.
    • 안전한 수집: UI에서 Flow 수집 시 collectAsStateWithLifecycle()을 사용하여 생명주기를 준수하는지 확인해줘.
    • 상태 노출: ViewModel 내부에서 StateFlowSharedFlow를 올바르게 노출하고 있는지 확인해줘.
    • 호이스팅(Hoisting): 상태나 UI 이벤트 처리가 적절하게 호이스팅되었는지 확인해줘. UI 로직과 비즈니스 로직을 분리하고, UI가 로직을 직접 수행하지 않도록 확인해줘.
  3. Clean Architecture (Dependency Rule) 준수:

    • Domain 순수성: Domain 레이어(domain 패키지)는 절대로 안드로이드 의존성(android.*, R 클래스 등)을 가져서는 안 돼. 순수 Kotlin 코드인지 엄격히 확인해줘.
    • 데이터 노출 금지: Data 레이어의 DTO(@Entity, @SerializedName)가 UI나 Domain 레이어로 바로 노출되면 안 돼. 반드시 Mapper를 통해 변환했는지 확인해줘.
    • 계층 간 의존성: UI -> Domain <- Data 방향이 올바른지, UI가 Data 레이어를 직접 참조하고 있진 않은지 감시해줘.
  4. Kotlin & Coroutines 안전성:

    • Scope 사용: GlobalScope 사용을 금지하고, viewModelScopelifecycleScope 사용을 권장해줘.
    • Dispatcher 주입: Dispatchers.IO 등을 하드코딩하지 말고, 테스트 용이성을 위해 생성자를 통해 주입(Injection)받도록 제안해줘.
    • 예외 처리: Coroutine 실행 시 runCatching이나 CoroutineExceptionHandler 등을 통해 예외 처리가 누락되지 않았는지 확인해줘.
    • Null-Safety: 불필요한 !! 연산자 사용을 지적...

Files:

  • app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDetail.kt
  • app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt
  • app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDropdown.kt
🧬 Code graph analysis (2)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDetail.kt (6)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiDivider.kt (1)
  • PotiDivider (28-39)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiListOptionPrice.kt (1)
  • PotiListOptionPrice (36-63)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt (1)
  • priceText (86-86)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiItemOption.kt (1)
  • PotiItemOption (41-67)
app/src/main/java/com/poti/android/core/designsystem/component/button/PotiInlineButton.kt (1)
  • PotiInlineButton (30-71)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt (3)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantStateLabel.kt (1)
  • HistoryParticipantStateLabel (40-56)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiItemOption.kt (1)
  • PotiItemOption (41-67)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDetail.kt (1)
  • priceText (251-255)
🪛 detekt (1.23.8)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt

[warning] 86-86: String.format("%,d원", price) uses implicitly default locale for string formatting.

(detekt.potential-bugs.ImplicitDefaultLocale)

🔇 Additional comments (6)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt (1)

88-105: LGTM!

Preview가 PotiTheme으로 올바르게 감싸져 있고, 모든 파라미터에 대한 샘플 데이터가 잘 제공되어 있어요. 좋은 구현이에요!

app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDetail.kt (2)

46-108: DetailState sealed interface 설계가 잘 되어 있습니다.

각 상태별로 필요한 필드를 강제하여 불가능한 상태 조합을 컴파일 타임에 방지하는 좋은 설계입니다. 상태 패턴을 활용하여 코드의 재사용성과 타입 안전성을 모두 확보하셨네요.


257-302: Preview 함수는 private으로 선언하시는 것이 좋습니다.

HistoryParticipantDetailPreview가 이미 private으로 선언되어 있어 좋습니다. Preview 함수가 외부에 노출될 필요가 없으므로 올바른 접근 제어입니다.

app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDropdown.kt (3)

32-43: 상태 호이스팅이 잘 적용되어 있습니다.

isExpandedonToggle을 외부에서 주입받아 단방향 데이터 흐름(UDF)을 준수하고 있습니다. Modifier도 마지막 선택적 파라미터로 올바르게 배치되어 있네요.


61-69: AnimatedVisibility 사용이 적절합니다.

드롭다운 확장/축소 애니메이션을 AnimatedVisibility로 처리하셨네요. HistoryParticipantDetailModifier.padding(top = 20.dp)을 직접 전달하여 이전 리뷰 피드백을 잘 반영하셨습니다.


74-117: Header 컴포넌트 구현이 깔끔합니다.

noRippleClickable로 전체 Row를 클릭 가능하게 처리하고, 확장 상태에 따라 아이콘을 동적으로 변경하는 로직이 잘 구현되어 있습니다. maxLines = 1TextOverflow.Ellipsis로 긴 텍스트 처리도 잘 되어 있네요.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

jyvnee and others added 4 commits January 16, 2026 00:40
* [feat/#35] 헤더 컴포넌트 구현

* [feat/#35] 분철팟 상세 뷰 UI 구현

* [feat/#35] 분철팟 상세 뷰 UI 구현

* [feat/#35] 이미지 비율 수정

* [feat/#35] 내비게이션 연결

* [feat/#35] 패키지 분리

* [feat/#35] 내비게이션 파라미터 수정

* [feat/#35] 코리 반영

* [feat/#35] 내비 수정

* [feat/#35] 주석 수정

* [feat/#35] 뷰모델 연결
* [feat/#32] card-history implement

* [fix/#32] 코리 반영
* [Feat/#27] GoodsSmallCard

* [Feat/#27] GoodsLargeCard

* [Feat/#27] PostsCard

* [Refactor/#27] 디자인 수정사항 반영

* [Refactor/#27] string 추출

* [Refactor/#27] 코드 리뷰 반영
* [Feat/#31] Edit Option Price 컴포넌트 구현

* [Feat/#31] 이미지 관련 버튼 컴포넌트 구현

* [Feat/#31] 힌트 팝업 컴포넌트 구현

* [Chore/#31] klint 적용

* [Rename/#31] Hint.kt -> HintTooltip.kt

* [Refactor/#31] Edit Option Price 레이아웃 리팩토링 및 스트링 리소스 추출

* [Fix/#31] 툴팁 위치 계산식 수정
- 중앙 배치 위해 xOffset에 툴팁 폭 포함

* [Chore/#31] 미사용 코드 삭제

* [Chore/#31] klint 적용

* [Feat/#31] 갤러리 이미지 선택 기능 추가

* [Chore/#31] 오탈자 수정

* [Refactor/#31] 이미지 피커 개선
- Row -> LazyRow 변경해 contentPadding 적용
- distinct 추가해 중복 이미지 방지
- 이미지만 선택 가능하도록 조건 추가

* [Refactor/#31] Edit Option Price 개선
- 최대 길이 제한
- 특수문자 입력 제한
- take 대신 반복문 사용해 불필요한 string 객체 생성 방지
- TransformedText 콤마 추가 로직 단순화

* [Chore/#31] klint 적용

* [Refactor/#31] core 확장함수 적용

* [Fix/#31] strings 충돌 이슈 해결
Copy link
Copy Markdown
Contributor

@sonyerim sonyerim 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 +24 to +35
@Composable
fun HistoryParticipantOverview(
modifier: Modifier = Modifier,
memberList: List<String>,
userName: String,
address: String,
phone: String,
deliveryMethod: String,
price: Int,
participantStageType: ParticipantStateLabelStage,
participantStatusType: ParticipantStateLabelStatus,
) {
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: 파라미터 순서 바꿔주세용

Comment on lines +62 to +80
) }

Text(
text = "$userName\n$address\n$phone",
style = PotiTheme.typography.body14m,
color = colors.gray800,
)
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
PotiItemOption(
optionType = PotiItemOptionType.DELIVERY,
sizeType = PotiItemOptionSize.SMALL,
text = deliveryMethod,
)
PotiItemOption(
optionType = PotiItemOptionType.PRICE,
sizeType = PotiItemOptionSize.SMALL,
text = priceText(price),
) }
}}
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.

P3: {} () 괄호 배치 정리하면 가독성 더 좋을 것 같아용

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ktlint 적용 전이었나보넹... 부끄럽다

Copy link
Copy Markdown
Contributor

@jyvnee jyvnee left a comment

Choose a reason for hiding this comment

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

반영하고 알려줘~~


sealed interface DetailState {
val fields: List<Pair<FieldType, String>>
val buttonLabelId: Int?
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: StringRes 사용해주세용

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

스크린샷 2026-01-16 오후 3 35 50

이렇게 에러가 떠서... 아래처럼 적어야하는데 이대로 할까요?

@get:StringRes
    val buttonLabelId: Int?

)

sealed interface DetailState {
val fields: List<Pair<FieldType, String>>
Copy link
Copy Markdown
Contributor

@jyvnee jyvnee Jan 16, 2026

Choose a reason for hiding this comment

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

P2: 가독성 측면에서 봤을 때 Pair 쓰기보다 data class 로 분리하고 label, price 등 이름을 명시하는게 좋을 것 같은데 어떻게 생각하시나요??

Comment on lines +166 to +168
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(12.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.

P3: 위에 Column spacedBy 빼면 여기서 Column으로 한 번 더 안 감싸도 Text에 bottom 패딩 주거나 Spacer 써도 될 것 같은데??

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

피그마는 확인해서 vertical = 2.dp 는 뺐어용

Column 제거는
의미적으로 입금 금액으로 묶이는게 더 좋다고 생각해서
성능 이슈가 따로 없으면 유지하고 싶어요

)

Text(
text = priceText(depositItems.sumOf { it.price }),
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: 이거 서버에서 총 가격 내려주고 있는 것 같은데 확인 부탁!!!!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

있네용 파라미터로 받는걸로 할게여

) {
Column(
modifier = Modifier
.padding(vertical = 20.dp, horizontal = 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: horizontal에 screenWidth 적용해줘야 할 것 같아요

Comment on lines +105 to +115
Icon(
painter = painterResource(
id = if (expanded) {
R.drawable.ic_arrow_up_lg
} else {
R.drawable.ic_arrow_down_lg
},
),
contentDescription = null,
tint = PotiTheme.colors.gray700,
)
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.

P3: 여기도 드롭다운 필드처럼 Crossfade 적용하면 좋을지도?

horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(
text = memberList.joinToString(", "),
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: 이 코드는 리컴포지션 될 때마다 계속 실행되기 때문에, 데이터 가공은 가급적 뷰모델이나 매퍼 단계에서 실행하는 게 좋을 것 같습니다. 따라서 여기는 그냥 String 타입으로 받으면 될 것 같아요

modifier = modifier
.fillMaxWidth()
.background(colors.white)
.padding(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: 여기도 screenWidth 적용해줘야 할듯

}

Text(
text = "$userName\n$address\n$phone",
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.

P3: 이것도 뷰모델에서 가공해서 주는게 낫지 않을까욤? 서버에서 어떻게 주는지 나와야 알겠지만..

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

userName, zipcode, address, phone 로 분리되어있어서, userInfo 로 합쳤습니다

Copy link
Copy Markdown
Contributor

@jyvnee jyvnee 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 +213 to +218
Column(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp),
verticalArrangement = Arrangement.spacedBy(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.

이거는 왜 수정 안 해주세요
image

Default 일 때도 렌더링 돼서 아래 패딩 생김!!!!

Copy link
Copy Markdown
Contributor

@doyeon0307 doyeon0307 left a comment

Choose a reason for hiding this comment

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

고생띠니

tint = PotiTheme.colors.gray700,
)

Crossfade(
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.

p3: 왕 굿

userName: String,
address: String,
phone: String,
memberList: String,
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.

p3: String으로 바꿔준거 좋네염!!

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

Caution

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

⚠️ Outside diff range comments (7)
app/src/main/java/com/poti/android/presentation/user/profile/navigation/ProfileNavigation.kt (1)

19-23: Composable 블록이 비어 있고, paddingValues 파라미터가 사용되지 않고 있습니다.

현재 composable<ProfileRoute.Profile> { } 블록이 비어 있어서 Profile 화면으로 이동해도 아무 UI도 렌더링되지 않습니다. 또한 paddingValues 파라미터가 선언만 되어 있고 실제로 사용되지 않고 있네요.

의도적인 placeholder라면 TODO 주석을 추가하시거나, 실제 ProfileScreen 컴포저블을 연결해 주시기 바랍니다.

💡 예상되는 구현 형태
 fun NavGraphBuilder.profileNavGraph(
     paddingValues: PaddingValues,
 ) {
-    composable<ProfileRoute.Profile> { }
+    composable<ProfileRoute.Profile> { backStackEntry ->
+        val profile = backStackEntry.toRoute<ProfileRoute.Profile>()
+        ProfileScreen(
+            userId = profile.userId,
+            paddingValues = paddingValues,
+        )
+    }
 }
app/src/main/java/com/poti/android/core/designsystem/component/field/PotiLongTextField.kt (1)

53-57: isNotEmpty()isNotBlank() 조건 불일치가 있습니다.

status 결정 시에는 error.isNotEmpty() (Line 54)를 사용하고, 에러 메시지 렌더링 시에는 error.isNotBlank() (Line 88)를 사용하고 있습니다. 만약 error = " " (공백만 있는 문자열)인 경우, 테두리는 빨간색으로 변경되지만 에러 메시지는 표시되지 않는 시각적 불일치가 발생할 수 있습니다.

🔧 일관성을 위한 수정 제안
     val status = when {
-        error.isNotEmpty() -> FieldStatus.ERROR
+        error.isNotBlank() -> FieldStatus.ERROR
         isFocused -> FieldStatus.FOCUS
         else -> FieldStatus.DEFAULT
     }

Also applies to: 88-90

app/src/main/java/com/poti/android/core/designsystem/component/field/PotiDropdownField.kt (2)

91-95: isNotEmpty()isNotBlank() 조건 불일치가 있습니다.

PotiLongTextField와 동일하게 status 결정 시에는 error.isNotEmpty() (Line 92)를 사용하고, 에러 메시지 렌더링 시에는 error.isNotBlank() (Line 140)를 사용하고 있습니다. 일관성을 위해 동일한 조건을 사용하는 것이 좋습니다.

🔧 일관성을 위한 수정 제안
     val status = when {
-        error.isNotEmpty() -> FieldStatus.ERROR
+        error.isNotBlank() -> FieldStatus.ERROR
         expandedState.targetState -> FieldStatus.FOCUS
         else -> FieldStatus.DEFAULT
     }

Also applies to: 140-142


186-186: showBottomBorder 조건이 항상 true입니다.

itemsIndexed에서 index0부터 menuItems.size - 1까지의 범위이므로, index < menuItems.size는 항상 true입니다. 마지막 아이템의 하단 테두리를 숨기려는 의도라면 조건을 수정해야 합니다.

🔧 수정 제안
-                    showBottomBorder = index < menuItems.size,
+                    showBottomBorder = index < menuItems.lastIndex,
app/src/main/java/com/poti/android/core/designsystem/component/field/PotiSearchField.kt (1)

133-137: isNotEmpty()isNotBlank() 조건 불일치가 있습니다.

다른 필드 컴포넌트들과 동일하게 status 결정 시에는 error.isNotEmpty() (Line 134)를 사용하고, 에러 메시지 렌더링 시에는 error.isNotBlank() (Line 182)를 사용하고 있습니다.

🔧 일관성을 위한 수정 제안
     val status = when {
-        error.isNotEmpty() -> FieldStatus.ERROR
+        error.isNotBlank() -> FieldStatus.ERROR
         isFieldFocused -> FieldStatus.FOCUS
         else -> FieldStatus.DEFAULT
     }

Also applies to: 182-184

app/src/main/java/com/poti/android/core/designsystem/component/field/PotiShortTextField.kt (1)

59-63: isNotEmpty()isNotBlank() 조건 불일치가 있습니다.

다른 필드 컴포넌트들과 동일한 패턴으로 status 결정 시와 에러 메시지 렌더링 시의 조건이 다릅니다.

🔧 일관성을 위한 수정 제안
     val status = when {
-        error.isNotEmpty() -> FieldStatus.ERROR
+        error.isNotBlank() -> FieldStatus.ERROR
         isFocused -> FieldStatus.FOCUS
         else -> FieldStatus.DEFAULT
     }

Also applies to: 96-98

app/src/main/java/com/poti/android/core/designsystem/component/field/PotiCountField.kt (1)

65-69: isNotEmpty()isNotBlank() 사용이 일관되지 않습니다.

Line 66에서는 error.isNotEmpty()를 사용하여 FieldStatus.ERROR를 결정하고, Line 109에서는 error.isNotBlank()를 사용하여 에러 메시지 표시 여부를 결정하고 있습니다.

만약 error = " " (공백만 있는 문자열)인 경우:

  • 테두리는 빨간색으로 변경됨 (isNotEmpty() → true)
  • 에러 메시지는 표시되지 않음 (isNotBlank() → false)

일관성을 위해 둘 다 동일한 메서드를 사용하시는 것이 좋겠습니다. 문서에 "emptyString이 아닌 경우"라고 명시되어 있으므로, isNotBlank()로 통일하시는 것을 권장드립니다.

🔧 제안하는 수정 사항
     val status = when {
-        error.isNotEmpty() -> FieldStatus.ERROR
+        error.isNotBlank() -> FieldStatus.ERROR
         isFocused -> FieldStatus.FOCUS
         else -> FieldStatus.DEFAULT
     }

Also applies to: 109-111

🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/poti/android/presentation/party/create/component/EditOptionPrice.kt`:
- Around line 180-187: The PriceVisualTransformation.filter implementation uses
text.toInt() which can throw on non-numeric input; update filter in the
PriceVisualTransformation class to parse safely (e.g., use text.toIntOrNull() or
strip non-digit characters first) and handle null/overflow by falling back to a
safe value or returning the original text transformation; ensure the method
still calls toMoneyString() only on a valid parsed number and that non-numeric
inputs are preserved or formatted safely to avoid crashes in
VisualTransformation.filter.

In
`@app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailScreen.kt`:
- Around line 61-71: The current when block handling uiState.partyDetail only
renders PartyDetailScreen for ApiState.Success and leaves else -> {} which
yields an empty screen; update the branch to handle ApiState.Loading and
ApiState.Error (and any other non-success states) by rendering a minimal loading
indicator or an error view with a retry action that dispatches
viewModel.processIntent(PartyDetailIntent.Retry) (or PartyDetailIntent.OnRetry
if that exists), and ensure the Error UI surfaces the error message from
ApiState.Error so users can retry or see the failure; modify the when on
uiState.partyDetail (around ApiState.Success / PartyDetailScreen) to include
these explicit branches instead of else -> {}.
- Around line 117-121: The like button is non-functional because
PartyDetailHeaderInfo is passed an empty lambda for onLikeClick; replace
onLikeClick = {} with a real handler that either dispatches the intended Intent
or calls the viewmodel action (e.g., PartyDetailViewModel.onLikeClicked or a
navigation intent) so the click is handled, or if the feature isn’t ready
hide/disable the button inside PartyDetailHeaderInfo (or pass a flag like
isLikeEnabled = false) so it is not shown as interactive; locate
PartyDetailHeaderInfo usage in PartyDetailScreen and update the onLikeClick
argument accordingly or add an enabled/visibility flag to PartyDetailHeaderInfo
to reflect feature readiness.

In
`@app/src/main/java/com/poti/android/presentation/party/goodsfilter/component/PotsCard.kt`:
- Around line 51-52: The isClosed calculation using val isClosed = totalCount -
currentCount == 0 is unsafe when currentCount can exceed totalCount; update the
condition to treat any non-positive remaining seats as closed (for example
replace with val isClosed = totalCount - currentCount <= 0 or val isClosed =
currentCount >= totalCount) and keep contentAlpha derived from that isClosed
value (contentAlpha = if (isClosed) 0.5f else 1f) so the UI correctly reflects
closed state and remaining-count display.

In `@app/src/main/res/values/strings.xml`:
- Line 36: The string resource party_detail_announcement contains placeholder
text ("어쩌구 저쩌구") inappropriate for release; replace its value with the actual
announcement copy (keeping intentional line breaks if needed) or move this
placeholder into a preview-only resource (e.g., create a preview-specific string
name like party_detail_announcement_preview or place it in a preview/qa resource
qualifier) and update code to use the preview key only in preview builds so the
release build never exposes the dummy text.
♻️ Duplicate comments (1)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDropdown.kt (1)

56-62: 람다 생성 대신 onToggle 직접 전달이 더 깔끔합니다.
이미 이전 리뷰에서 언급된 내용과 동일하며, onToggle = onToggle로 넘기면 불필요한 람다 생성이 줄어듭니다.

♻️ 제안 수정안
-        ParticipantDropdownHeader(
+        ParticipantDropdownHeader(
             name = userName,
             stageType = stageType,
             statusType = statusType,
             expanded = isExpanded,
-            onToggle = { onToggle() },
+            onToggle = onToggle,
         )
🧹 Nitpick comments (17)
app/src/main/java/com/poti/android/core/designsystem/component/field/PotiDropdownField.kt (1)

160-188: itemsIndexedkey 파라미터 추가를 권장합니다.

FieldMenuItemid 프로퍼티가 있으므로, 리스트 아이템 변경 시 불필요한 리컴포지션을 방지하기 위해 key 파라미터를 사용하는 것이 좋습니다. 코딩 가이드라인에 따른 Compose 성능 최적화 권장 사항입니다.

♻️ 리컴포지션 최적화를 위한 수정 제안
-            itemsIndexed(menuItems) { index, item ->
+            itemsIndexed(
+                items = menuItems,
+                key = { _, item -> item.id },
+            ) { index, item ->
app/src/main/java/com/poti/android/core/designsystem/component/field/PotiSearchField.kt (1)

199-213: itemsIndexedkey 파라미터 추가 및 showBottomBorder 조건 수정을 권장합니다.

PotiDropdownField와 동일한 패턴으로, 다음 두 가지 개선을 권장합니다:

  1. key 파라미터를 추가하여 리컴포지션 성능을 최적화하세요.
  2. showBottomBorder = index < menuItems.size는 항상 true이므로, 마지막 아이템 테두리를 숨기려면 조건을 수정하세요.
♻️ 수정 제안
-            itemsIndexed(menuItems) { index, item ->
+            itemsIndexed(
+                items = menuItems,
+                key = { _, item -> item.id },
+            ) { index, item ->
                 PotiMenuItem(
                     option = item.option,
                     onClick = {
                         onItemClick(item)
                         expandedState.targetState = false
                         clearFocusAndHideKeyboard()
                     },
                     isSelected = item.id in selectedIds,
                     modifier = Modifier.fillMaxWidth(),
                     price = item.price,
                     disabled = item.disabled,
-                    showBottomBorder = index < menuItems.size,
+                    showBottomBorder = index < menuItems.lastIndex,
                 )
             }
app/src/main/java/com/poti/android/presentation/party/create/component/CreatePhotoUpload.kt (2)

93-100: LazyRow 항목에 안정적인 key를 지정해주세요.
Line 93-100의 itemsIndexed가 key 없이 인덱스 기반으로 동작해, 삭제/추가 시 항목 재사용이 꼬일 수 있습니다. 중복이 없다는 전제에서 Uri를 key로 주는 편이 안전합니다. 코딩 가이드라인 기준입니다.

♻️ 제안 수정안
-        itemsIndexed(imageUris) { index, uri ->
+        itemsIndexed(
+            items = imageUris,
+            key = { _, uri -> uri },
+        ) { index, uri ->

141-147: 플레이스홀더 배경색을 테마 색상으로 통일하면 좋겠습니다.
Line 145-147의 Color.Gray는 하드코딩이라 테마 일관성이 깨질 수 있습니다. 디자인 시스템 색상(예: PotiTheme.colors.gray100) 사용을 권장드립니다. 적용 시 Color import 정리도 필요합니다. 코딩 가이드라인 기준입니다.

🎨 제안 수정안
-                .background(Color.Gray),
+                .background(PotiTheme.colors.gray100),
app/src/main/java/com/poti/android/presentation/party/home/component/GoodsLargeCard.kt (1)

36-71: ImageRequest는 remember로 캐시하는 편이 좋습니다.

재구성마다 객체가 새로 만들어져 불필요한 할당이 발생할 수 있습니다.

♻️ 캐싱 적용 제안
-import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
@@
 fun GoodsLargeCard(
     imageUrl: String,
@@
 ) {
+    val context = LocalContext.current
+    val imageRequest = remember(imageUrl, context) {
+        ImageRequest.Builder(context)
+            .data(imageUrl)
+            .crossfade(true)
+            .build()
+    }
     Column(
@@
             AsyncImage(
-                model = ImageRequest.Builder(LocalContext.current)
-                    .data(imageUrl)
-                    .crossfade(true)
-                    .build(),
+                model = imageRequest,
                 contentDescription = null,
                 contentScale = ContentScale.Crop,
                 modifier = Modifier.fillMaxSize(),
             )
app/src/main/java/com/poti/android/presentation/party/home/component/GoodsSmallCard.kt (1)

68-72: AsyncImage 모델 생성은 remember로 캐싱하는 편이 안전합니다.
현재 ImageRequest.Builder(...)가 리컴포지션마다 새로 만들어질 수 있어 불필요한 객체 생성/로드가 발생할 수 있습니다. remember(imageUrl)로 모델을 캐싱하면 안정적입니다.

♻️ 제안 수정안
-            AsyncImage(
-                model = ImageRequest.Builder(LocalContext.current)
-                    .data(imageUrl)
-                    .crossfade(true)
-                    .build(),
+            val imageRequest = remember(imageUrl) {
+                ImageRequest.Builder(LocalContext.current)
+                    .data(imageUrl)
+                    .crossfade(true)
+                    .build()
+            }
+            AsyncImage(
+                model = imageRequest,
                 contentDescription = null,
                 contentScale = ContentScale.Crop,
                 modifier = Modifier.fillMaxSize(),
             )
app/src/main/java/com/poti/android/presentation/party/goodsfilter/component/PotsCard.kt (1)

157-162: ImageRequest는 remember로 캐싱하는 편이 좋아요.
Recomposition마다 ImageRequest가 새로 생성되어 불필요한 할당이 발생할 수 있습니다. remember로 캐싱해 주세요.

♻️ 제안 수정
+import androidx.compose.runtime.remember
...
+    val context = LocalContext.current
+    val imageRequest = remember(imageUrl) {
+        ImageRequest.Builder(context)
+            .data(imageUrl)
+            .crossfade(true)
+            .build()
+    }
...
-            AsyncImage(
-                model = ImageRequest.Builder(LocalContext.current)
-                    .data(imageUrl)
-                    .crossfade(true)
-                    .build(),
+            AsyncImage(
+                model = imageRequest,
                 contentDescription = null,
                 contentScale = ContentScale.Crop,
                 modifier = Modifier
                     .fillMaxHeight()
                     .aspectRatio(1f)
                     .clip(RoundedCornerShape(8.dp))
                     .background(PotiTheme.colors.gray300),
             )
app/src/main/java/com/poti/android/presentation/party/detail/component/PartyUploaderInfo.kt (1)

50-54: PotiIconButtononClick 람다 최적화를 고려해 보실 수 있습니다.

현재 onClick = { onClick(userSummary.userId) } 형태로 매 리컴포지션마다 새로운 람다가 생성됩니다. PotiIconButton이 내부적으로 noRippleClickable을 사용하고 있어 실제로는 큰 영향이 없을 수 있지만, remember를 사용하면 람다 재생성을 방지할 수 있습니다.

♻️ 선택적 최적화 제안
+    val onClickCallback = remember(userSummary.userId) { { onClick(userSummary.userId) } }
+
     PotiIconButton(
         iconRes = R.drawable.ic_arrow_right_lg,
-        onClick = { onClick(userSummary.userId) },
+        onClick = onClickCallback,
         tint = PotiTheme.colors.gray700,
     )
app/src/main/java/com/poti/android/presentation/party/detail/DummyPartyDetailData.kt (1)

54-76: 더미 데이터의 일관성을 확인해 주세요.

두 가지 잠재적 문제가 있습니다:

  1. userId = 1이 참여자 목록에서 중복되어 있습니다 (Line 56, 63). 이 데이터가 LazyColumnkey로 사용될 경우 문제가 발생할 수 있습니다.

  2. currentCount = 2이지만 participants 리스트에는 3명이 있습니다. 실제 API 응답에서 currentCount가 참여자 수와 다른 의미를 가진다면 괜찮지만, 동일한 의미라면 불일치가 있습니다.

더미 데이터이므로 심각한 문제는 아니지만, Preview에서 실제 시나리오와 일관된 동작을 테스트하려면 수정을 권장드립니다.

app/src/main/java/com/poti/android/presentation/party/detail/component/PartyParticipantsInfo.kt (1)

48-68: forEach 대신 key를 사용하여 리컴포지션을 최적화해 주세요.

현재 중첩된 forEach에서 각 아이템에 고유한 key가 없어 리스트가 변경될 때 불필요한 리컴포지션이 발생할 수 있습니다. 코딩 가이드라인에 따르면, 특히 LazyColumn 등 리스트에서 key 파라미터가 누락되었는지 확인해야 합니다.

참여자 목록이 작다면 현재 방식도 괜찮지만, 성능을 고려하여 key composable을 추가하시는 것을 권장드립니다.

♻️ key 적용 제안
         partyDetail.participants.forEach { participant ->
-            participant.selectedMembers.forEach { selectedMember ->
+            participant.selectedMembers.forEachIndexed { index, selectedMember ->
+                key("${participant.userId}-$index") {
                     Row(
                         verticalAlignment = Alignment.CenterVertically,
                     ) {
                         PotiProfileSummary(
                             profileImageUrl = participant.profileImage,
                             nickname = participant.nickname,
                             sizeType = PotiProfileSummarySize.LARGE,
                             rating = participant.rating.toString(),
                             modifier = Modifier.weight(1f),
                         )

                         PotiPrimaryTag(
                             text = selectedMember,
                             colorType = PotiPrimaryTagColor.GRAY,
                             sizeType = PotiPrimaryTagSize.LARGE,
                         )
                     }
+                }
             }
         }

참고: key composable을 사용하려면 import androidx.compose.runtime.key가 필요합니다.

app/src/main/java/com/poti/android/presentation/party/detail/component/PartyDetailHeaderInfo.kt (2)

33-48: 타이틀 텍스트에 maxLines 설정 고려

minLines = 2는 설정되어 있지만 maxLines가 없어서, 제목이 매우 길 경우 레이아웃이 예상치 않게 확장될 수 있습니다. 일관된 UI를 위해 maxLines도 함께 지정하시는 것을 권장드립니다.

♻️ 제안 코드
 Text(
     text = partyDetail.title,
     style = PotiTheme.typography.title18sb,
     color = PotiTheme.colors.black,
     minLines = 2,
+    maxLines = 2,
     overflow = TextOverflow.Ellipsis,
 )

29-78: 왼쪽 Column에 weight Modifier 추가 검토

현재 Row 내에서 왼쪽 Columnweight가 없어서, 텍스트가 길어지면 오른쪽의 하트 아이콘 버튼을 밀어낼 수 있습니다. Modifier.weight(1f)를 추가하시면 하트 버튼의 공간을 확보하면서 텍스트가 적절히 잘리도록 할 수 있습니다.

♻️ 제안 코드
 Row(
     modifier = modifier.fillMaxWidth(),
     horizontalArrangement = Arrangement.SpaceBetween,
 ) {
-    Column {
+    Column(modifier = Modifier.weight(1f)) {
         Text(
             text = partyDetail.artist,
app/src/main/java/com/poti/android/presentation/party/detail/component/PartyDetailContent.kt (1)

31-40: textHeightDp 계산에 remember 사용 권장

textHeightDp 계산이 매 리컴포지션마다 수행됩니다. textStyle이 변경되지 않는 한 동일한 값을 반환하므로, remember로 감싸서 불필요한 재계산을 방지하시는 것이 좋습니다.

♻️ 제안 코드
 val density = LocalDensity.current
 val textStyle = PotiTheme.typography.body14m

-val textHeightDp = with(density) {
+val textHeightDp = remember(textStyle, density) {
+    with(density) {
         if (textStyle.lineHeight.isSp) {
             textStyle.lineHeight.toDp()
         } else {
             textStyle.fontSize.toDp()
         }
     }
+}
app/src/main/java/com/poti/android/domain/model/party/PartyDetail.kt (1)

7-24: Compose 안정성을 위한 @Immutable 어노테이션 고려

PartyDetailList 타입의 프로퍼티(images, deliveryOptions, participants)를 포함하고 있습니다. Compose에서 List는 기본적으로 불안정(unstable)으로 간주되어 불필요한 리컴포지션이 발생할 수 있습니다.

이 데이터 클래스가 UI에서 직접 사용되므로, @Immutable 어노테이션을 추가하시면 Compose 컴파일러가 이를 안정적인 타입으로 인식하여 성능 최적화에 도움이 됩니다.

♻️ 제안 코드
 package com.poti.android.domain.model.party

+import androidx.compose.runtime.Immutable
 import com.poti.android.domain.model.delivery.DeliveryOption
 import com.poti.android.domain.model.user.UserSummary
 import com.poti.android.domain.type.PartyStatusType

+@Immutable
 data class PartyDetail(

다만, Domain 레이어에 Android 의존성(androidx.compose.runtime)을 추가하게 되므로 Clean Architecture 원칙과 충돌할 수 있습니다. 대안으로 Presentation 레이어에 별도의 UI 모델을 만들거나, Compose 컴파일러의 stability configuration 파일을 사용하는 방법도 고려해 보시기 바랍니다.

app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailViewModel.kt (1)

36-40: 에러 상태 처리 추가 권장

현재 loadPartyDetail에서 LoadingSuccess 상태만 처리하고 있습니다. 서버 연결 시 네트워크 오류 등의 상황을 대비하여 ApiState.Error 상태 처리를 추가하시는 것이 좋습니다.

♻️ 제안 코드 (서버 연결 시 참고)
private fun loadPartyDetail() = launchScope(
    onError = { e ->
        updateState { copy(partyDetail = ApiState.Error(e)) }
    }
) {
    updateState { copy(partyDetail = ApiState.Loading) }
    // 서버 호출 로직
    val result = repository.getPartyDetail(partyId)
    updateState { copy(partyDetail = ApiState.Success(result)) }
}
app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailScreen.kt (2)

51-58: 사이드이펙트 수집을 lifecycle-aware로 제한해 주세요.
현재 LaunchedEffect가 STOPPED 상태에서도 수집을 계속할 수 있어, 백그라운드 전환 중 의도치 않은 네비게이션이 발생할 여지가 있습니다. repeatOnLifecycle/flowWithLifecycle로 STARTED 구간에 한정 수집을 권장드립니다.

♻️ 적용 예시
+    val lifecycleOwner = LocalLifecycleOwner.current
-    LaunchedEffect(viewModel.sideEffect) {
-        viewModel.sideEffect.collect { effect ->
-            when (effect) {
-                PartyDetailEffect.NavigateBack -> onPopBackStack()
-                PartyDetailEffect.NavigateToJoin -> onNavigateToJoin()
-                is PartyDetailEffect.NavigateToProfile -> onNavigateToProfile(effect.userId)
-            }
-        }
-    }
+    LaunchedEffect(viewModel.sideEffect, lifecycleOwner) {
+        lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
+            viewModel.sideEffect.collect { effect ->
+                when (effect) {
+                    PartyDetailEffect.NavigateBack -> onPopBackStack()
+                    PartyDetailEffect.NavigateToJoin -> onNavigateToJoin()
+                    is PartyDetailEffect.NavigateToProfile -> onNavigateToProfile(effect.userId)
+                }
+            }
+        }
+    }

As per coding guidelines, lifecycle-aware 수집을 권장드립니다.


98-152: 스크롤 영역은 LazyColumn 전환을 권장드립니다.
현재 Column + verticalScroll은 참가자 수가 늘면 모든 항목을 한 번에 컴포즈/측정합니다. 섹션은 item {}로, 참가자 목록은 items(key=...)로 구성한 LazyColumn을 검토해 주세요.

As per coding guidelines, 리스트는 LazyColumnkey 사용을 권장드립니다.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 685217c and 30c663a.

⛔ Files ignored due to path filters (1)
  • app/src/main/res/drawable/img_create_hint.xml is excluded by !**/res/drawable*/**
📒 Files selected for processing (41)
  • app/src/main/java/com/poti/android/core/common/extension/IntExt.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/display/PotiProfileSummary.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/field/FieldErrorMessage.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/field/PotiBasicField.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/field/PotiCountField.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/field/PotiDropdownField.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/field/PotiLongTextField.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/field/PotiSearchField.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/field/PotiShortTextField.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/navigation/PotiBottomButton.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/navigation/PotiHeaderPage.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/navigation/PotiHeaderPrimary.kt
  • app/src/main/java/com/poti/android/domain/model/delivery/DeliveryOption.kt
  • app/src/main/java/com/poti/android/domain/model/party/Participant.kt
  • app/src/main/java/com/poti/android/domain/model/party/PartyDetail.kt
  • app/src/main/java/com/poti/android/domain/model/party/PartyImage.kt
  • app/src/main/java/com/poti/android/domain/model/user/UserSummary.kt
  • app/src/main/java/com/poti/android/domain/type/PartyStatusType.kt
  • app/src/main/java/com/poti/android/presentation/history/component/HistoryCardItem.kt
  • app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDetail.kt
  • app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDropdown.kt
  • app/src/main/java/com/poti/android/presentation/main/PotiNavHost.kt
  • app/src/main/java/com/poti/android/presentation/party/PartyNavigation.kt
  • app/src/main/java/com/poti/android/presentation/party/create/component/CreatePhotoUpload.kt
  • app/src/main/java/com/poti/android/presentation/party/create/component/EditOptionPrice.kt
  • app/src/main/java/com/poti/android/presentation/party/create/component/HintTooltip.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/DummyPartyDetailData.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailScreen.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailViewModel.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/component/PartyDetailContent.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/component/PartyDetailHeaderInfo.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/component/PartyParticipantsInfo.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/component/PartyUploaderInfo.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/model/Contracts.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/navigation/PartyDetailNavigation.kt
  • app/src/main/java/com/poti/android/presentation/party/goodsfilter/component/PotsCard.kt
  • app/src/main/java/com/poti/android/presentation/party/home/component/GoodsLargeCard.kt
  • app/src/main/java/com/poti/android/presentation/party/home/component/GoodsSmallCard.kt
  • app/src/main/java/com/poti/android/presentation/party/home/navigation/HomeNavigation.kt
  • app/src/main/java/com/poti/android/presentation/user/profile/navigation/ProfileNavigation.kt
  • app/src/main/res/values/strings.xml
💤 Files with no reviewable changes (1)
  • app/src/main/java/com/poti/android/core/designsystem/component/field/FieldErrorMessage.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDetail.kt
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt

⚙️ CodeRabbit configuration file

**/*.kt: 코드를 리뷰할 때 다음 Kotlin 및 Jetpack Compose 모범 사례를 중점적으로 확인:

  1. Jetpack Compose 성능 및 UI 최적화:

    • Recomposition 방지: 불필요한 리컴포지션을 유발하는 코드를 지적해줘. 특히 LazyColumn 등 리스트에서 key 파라미터가 누락되었는지 확인해줘.
    • 스마트한 계산: 계산 비용이 높은 로직은 rememberderivedStateOf로 감싸져 있는지 확인해줘.
    • 안정성(Stability): List나 일반 클래스를 State로 사용할 때, 불필요한 리컴포지션을 막기 위해 @Immutable이나 @Stable 어노테이션 사용을 제안해줘.
    • Modifier 규칙: Modifier는 항상 컴포저블 함수의 첫 번째 선택적 파라미터로 받아야 하며, 재사용성을 위해 외부에서 주입받아야 해.
    • 리소스 사용: 하드코딩된 값(색상, 크기, 문자열) 대신 Theme, Dimens, strings.xml 리소스를 사용하는지 체크해줘.
    • 접근성 예외: Icon이나 Image 컴포저블의 contentDescriptionnull인 경우는 장식용(Decorative) 이미지로 간주하고 지적하지 마.
  2. MVI (Model-View-Intent) 아키텍처 및 상태 관리:

    • 단방향 데이터 흐름(UDF): UI는 상태를 직접 변경할 수 없고, 반드시 Intent(또는 Event)를 통해 ViewModel로 요청을 보내야 해.
    • 불변 상태(Immutable State): UiState는 반드시 data class + val 프로퍼티로 구성되어야 하며, 상태 업데이트 시 .copy()를 사용해야 해.
    • Side Effect 분리: 네비게이션, Toast 같은 일회성 이벤트는 UiState가 아닌 Channel이나 SharedFlow로 처리하여 화면 회전 시 재발생하지 않도록 해야 해.
    • 안전한 수집: UI에서 Flow 수집 시 collectAsStateWithLifecycle()을 사용하여 생명주기를 준수하는지 확인해줘.
    • 상태 노출: ViewModel 내부에서 StateFlowSharedFlow를 올바르게 노출하고 있는지 확인해줘.
    • 호이스팅(Hoisting): 상태나 UI 이벤트 처리가 적절하게 호이스팅되었는지 확인해줘. UI 로직과 비즈니스 로직을 분리하고, UI가 로직을 직접 수행하지 않도록 확인해줘.
  3. Clean Architecture (Dependency Rule) 준수:

    • Domain 순수성: Domain 레이어(domain 패키지)는 절대로 안드로이드 의존성(android.*, R 클래스 등)을 가져서는 안 돼. 순수 Kotlin 코드인지 엄격히 확인해줘.
    • 데이터 노출 금지: Data 레이어의 DTO(@Entity, @SerializedName)가 UI나 Domain 레이어로 바로 노출되면 안 돼. 반드시 Mapper를 통해 변환했는지 확인해줘.
    • 계층 간 의존성: UI -> Domain <- Data 방향이 올바른지, UI가 Data 레이어를 직접 참조하고 있진 않은지 감시해줘.
  4. Kotlin & Coroutines 안전성:

    • Scope 사용: GlobalScope 사용을 금지하고, viewModelScopelifecycleScope 사용을 권장해줘.
    • Dispatcher 주입: Dispatchers.IO 등을 하드코딩하지 말고, 테스트 용이성을 위해 생성자를 통해 주입(Injection)받도록 제안해줘.
    • 예외 처리: Coroutine 실행 시 runCatching이나 CoroutineExceptionHandler 등을 통해 예외 처리가 누락되지 않았는지 확인해줘.
    • Null-Safety: 불필요한 !! 연산자 사용을 지적...

Files:

  • app/src/main/java/com/poti/android/core/common/extension/IntExt.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/field/PotiLongTextField.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/navigation/PotiHeaderPage.kt
  • app/src/main/java/com/poti/android/domain/model/delivery/DeliveryOption.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/navigation/PotiHeaderPrimary.kt
  • app/src/main/java/com/poti/android/domain/model/party/Participant.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/field/PotiShortTextField.kt
  • app/src/main/java/com/poti/android/presentation/party/PartyNavigation.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/field/PotiCountField.kt
  • app/src/main/java/com/poti/android/presentation/party/home/component/GoodsLargeCard.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/navigation/PotiBottomButton.kt
  • app/src/main/java/com/poti/android/presentation/user/profile/navigation/ProfileNavigation.kt
  • app/src/main/java/com/poti/android/presentation/party/home/component/GoodsSmallCard.kt
  • app/src/main/java/com/poti/android/domain/type/PartyStatusType.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/display/PotiProfileSummary.kt
  • app/src/main/java/com/poti/android/domain/model/user/UserSummary.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/component/PartyDetailHeaderInfo.kt
  • app/src/main/java/com/poti/android/presentation/party/create/component/HintTooltip.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/field/PotiSearchField.kt
  • app/src/main/java/com/poti/android/presentation/party/create/component/CreatePhotoUpload.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/DummyPartyDetailData.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/field/PotiDropdownField.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailViewModel.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/field/PotiBasicField.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/component/PartyParticipantsInfo.kt
  • app/src/main/java/com/poti/android/presentation/party/home/navigation/HomeNavigation.kt
  • app/src/main/java/com/poti/android/domain/model/party/PartyImage.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/model/Contracts.kt
  • app/src/main/java/com/poti/android/domain/model/party/PartyDetail.kt
  • app/src/main/java/com/poti/android/presentation/main/PotiNavHost.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailScreen.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/component/PartyDetailContent.kt
  • app/src/main/java/com/poti/android/presentation/party/create/component/EditOptionPrice.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/component/PartyUploaderInfo.kt
  • app/src/main/java/com/poti/android/presentation/party/goodsfilter/component/PotsCard.kt
  • app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDropdown.kt
  • app/src/main/java/com/poti/android/presentation/history/component/HistoryCardItem.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/navigation/PartyDetailNavigation.kt
🧬 Code graph analysis (23)
app/src/main/java/com/poti/android/core/designsystem/component/field/PotiLongTextField.kt (1)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiErrorMessage.kt (1)
  • PotiErrorMessage (17-39)
app/src/main/java/com/poti/android/core/designsystem/component/navigation/PotiHeaderPage.kt (1)
app/src/main/java/com/poti/android/core/common/util/DimensionUtil.kt (1)
  • screenWidthDp (20-26)
app/src/main/java/com/poti/android/core/designsystem/component/navigation/PotiHeaderPrimary.kt (1)
app/src/main/java/com/poti/android/core/common/util/DimensionUtil.kt (1)
  • screenWidthDp (20-26)
app/src/main/java/com/poti/android/presentation/party/PartyNavigation.kt (2)
app/src/main/java/com/poti/android/presentation/party/home/navigation/HomeNavigation.kt (1)
  • homeNavGraph (23-33)
app/src/main/java/com/poti/android/presentation/party/detail/navigation/PartyDetailNavigation.kt (1)
  • partyDetailNavGraph (31-48)
app/src/main/java/com/poti/android/core/designsystem/component/field/PotiCountField.kt (1)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiErrorMessage.kt (1)
  • PotiErrorMessage (17-39)
app/src/main/java/com/poti/android/presentation/party/home/component/GoodsLargeCard.kt (3)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiSecondaryTag.kt (1)
  • PotiSecondaryTag (30-47)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiPrimaryTag.kt (1)
  • PotiPrimaryTag (42-61)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
app/src/main/java/com/poti/android/core/designsystem/component/navigation/PotiBottomButton.kt (1)
app/src/main/java/com/poti/android/core/common/util/DimensionUtil.kt (1)
  • screenWidthDp (20-26)
app/src/main/java/com/poti/android/presentation/party/home/component/GoodsSmallCard.kt (4)
app/src/main/java/com/poti/android/core/common/util/DimensionUtil.kt (1)
  • screenWidthDp (20-26)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiSecondaryTag.kt (1)
  • PotiSecondaryTag (30-47)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiPrimaryTag.kt (1)
  • PotiPrimaryTag (42-61)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
app/src/main/java/com/poti/android/presentation/party/detail/component/PartyDetailHeaderInfo.kt (2)
app/src/main/java/com/poti/android/core/designsystem/component/button/PotiIconButton.kt (1)
  • PotiIconButton (19-35)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
app/src/main/java/com/poti/android/presentation/party/create/component/HintTooltip.kt (3)
app/src/main/java/com/poti/android/core/common/util/DimensionUtil.kt (1)
  • screenHeightDp (12-18)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
app/src/main/java/com/poti/android/core/designsystem/component/button/PotiInlineButton.kt (1)
  • PotiInlineButton (30-71)
app/src/main/java/com/poti/android/core/designsystem/component/field/PotiSearchField.kt (1)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiErrorMessage.kt (1)
  • PotiErrorMessage (17-39)
app/src/main/java/com/poti/android/presentation/party/create/component/CreatePhotoUpload.kt (3)
app/src/main/java/com/poti/android/core/common/util/DimensionUtil.kt (1)
  • screenWidthDp (20-26)
app/src/main/java/com/poti/android/core/designsystem/component/button/PotiDeleteButton.kt (1)
  • PotiDeleteButton (36-56)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
app/src/main/java/com/poti/android/core/designsystem/component/field/PotiDropdownField.kt (1)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiErrorMessage.kt (1)
  • PotiErrorMessage (17-39)
app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailViewModel.kt (1)
app/src/main/java/com/poti/android/core/base/BaseViewModel.kt (3)
  • sendEffect (34-38)
  • launchScope (42-54)
  • updateState (30-32)
app/src/main/java/com/poti/android/presentation/party/detail/component/PartyParticipantsInfo.kt (3)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiProfileSummary.kt (1)
  • PotiProfileSummary (33-93)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiPrimaryTag.kt (1)
  • PotiPrimaryTag (42-61)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
app/src/main/java/com/poti/android/presentation/party/home/navigation/HomeNavigation.kt (1)
app/src/main/java/com/poti/android/presentation/party/home/HomeScreen.kt (1)
  • HomeRoute (8-17)
app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailScreen.kt (9)
app/src/main/java/com/poti/android/core/designsystem/component/navigation/PotiHeaderPage.kt (1)
  • PotiHeaderPage (20-70)
app/src/main/java/com/poti/android/core/designsystem/component/navigation/PotiBottomButton.kt (1)
  • PotiBottomButton (17-48)
app/src/main/java/com/poti/android/presentation/party/detail/component/PartyDetailHeaderInfo.kt (1)
  • PartyDetailHeaderInfo (23-79)
app/src/main/java/com/poti/android/core/common/util/DimensionUtil.kt (1)
  • screenWidthDp (20-26)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiDivider.kt (1)
  • PotiDivider (28-39)
app/src/main/java/com/poti/android/presentation/party/detail/component/PartyDetailContent.kt (1)
  • PartyDetailContent (26-104)
app/src/main/java/com/poti/android/presentation/party/detail/component/PartyUploaderInfo.kt (1)
  • PartyUploaderInfo (22-57)
app/src/main/java/com/poti/android/presentation/party/detail/component/PartyParticipantsInfo.kt (1)
  • PartyParticipantsInfo (23-70)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
app/src/main/java/com/poti/android/presentation/party/detail/component/PartyDetailContent.kt (1)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
app/src/main/java/com/poti/android/presentation/party/create/component/EditOptionPrice.kt (2)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiCheckBox.kt (1)
  • PotiCheckBox (29-60)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
app/src/main/java/com/poti/android/presentation/party/detail/component/PartyUploaderInfo.kt (3)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiProfileSummary.kt (1)
  • PotiProfileSummary (33-93)
app/src/main/java/com/poti/android/core/designsystem/component/button/PotiIconButton.kt (1)
  • PotiIconButton (19-35)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
app/src/main/java/com/poti/android/presentation/party/goodsfilter/component/PotsCard.kt (3)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiProfileSummary.kt (1)
  • PotiProfileSummary (33-93)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiDivider.kt (1)
  • PotiDivider (28-39)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDropdown.kt (3)
app/src/main/java/com/poti/android/core/common/util/DimensionUtil.kt (1)
  • screenWidthDp (20-26)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDetail.kt (1)
  • HistoryParticipantDetail (122-254)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantStateLabel.kt (1)
  • HistoryParticipantStateLabel (40-56)
app/src/main/java/com/poti/android/presentation/party/detail/navigation/PartyDetailNavigation.kt (2)
app/src/main/java/com/poti/android/presentation/main/PotiNavigator.kt (1)
  • navigate (27-37)
app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailScreen.kt (1)
  • PartyDetailRoute (41-73)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: PR CI Check (Lint & Build)
🔇 Additional comments (42)
app/src/main/java/com/poti/android/presentation/user/profile/navigation/ProfileNavigation.kt (2)

10-12: LGTM! Type-safe Navigation 패턴이 잘 적용되었습니다.

data object에서 data class로 변경하여 userId를 전달하는 방식은 Modern Android Development의 Type-safe Navigation 권장 사항에 부합합니다. @Serializable 어노테이션도 올바르게 적용되어 있네요.


15-17: 네비게이션 확장 함수가 적절하게 업데이트되었습니다.

userId를 파라미터로 받아 ProfileRoute.Profile(userId)로 전달하는 방식이 깔끔합니다.

app/src/main/java/com/poti/android/core/designsystem/component/field/PotiLongTextField.kt (1)

20-21: 에러 메시지 조건부 렌더링 패턴이 잘 적용되었습니다.

PotiErrorMessage를 사용한 조건부 렌더링으로 변경하여 다른 필드 컴포넌트들과 일관된 에러 처리 패턴을 유지하고 있습니다. 불필요한 컴포지션을 방지하는 좋은 접근 방식입니다.

Also applies to: 88-90

app/src/main/java/com/poti/android/core/designsystem/component/field/PotiShortTextField.kt (1)

50-50: trailingIcon 파라미터 추가가 잘 구현되었습니다.

Nullable 타입과 기본값 null을 사용하여 선택적 trailing icon을 지원하는 것은 Compose API 설계의 좋은 패턴입니다. KDoc도 적절하게 작성되어 있습니다.

Also applies to: 93-93

app/src/main/java/com/poti/android/core/designsystem/component/field/PotiBasicField.kt (1)

47-47: trailingIcon의 nullable 변경이 적절하게 구현되었습니다.

trailingIcon을 nullable 타입으로 변경하고 ?.invoke()로 안전하게 호출하는 패턴은 Modern Android Development의 null-safety 권장 사항에 부합합니다. 기존 빈 람다 기본값({}) 대신 null을 사용하여 trailing icon이 없을 때 불필요한 컴포지션을 방지하는 효과도 있습니다.

Also applies to: 118-119

app/src/main/java/com/poti/android/core/designsystem/component/field/PotiCountField.kt (1)

23-23: LGTM!

PotiErrorMessage import가 올바르게 추가되었습니다. 다른 필드 컴포넌트들과 동일한 패턴으로 통일되어 일관성이 높아졌네요.

app/src/main/java/com/poti/android/presentation/party/create/component/CreatePhotoUpload.kt (2)

105-128: 업로드 버튼을 별도 컴포저블로 분리한 점이 좋습니다.
Line 105-128에서 클릭/스타일이 캡슐화되어 재사용성과 유지보수성이 좋아 보입니다.


160-173: Preview 예시가 사용법 파악에 도움이 됩니다.
Line 160-173의 미리보기는 컴포넌트 사용 흐름을 빠르게 확인하기에 좋습니다.

app/src/main/java/com/poti/android/core/designsystem/component/navigation/PotiBottomButton.kt (1)

12-12: 반응형 패딩 적용 좋습니다.
screenWidthDp로 일관된 반응형 여백을 적용해 디자인 시스템 방향성과 잘 맞습니다.

Also applies to: 25-29

app/src/main/java/com/poti/android/core/designsystem/component/navigation/PotiHeaderPrimary.kt (1)

21-21: 헤더 패딩의 반응형 전환이 일관성 있습니다.
다른 헤더 컴포넌트와 동일한 기준으로 맞춘 점이 좋습니다.

Also applies to: 34-38

app/src/main/java/com/poti/android/core/designsystem/component/navigation/PotiHeaderPage.kt (1)

16-16: 페이지 헤더도 반응형 여백으로 정리되어 좋습니다.
전체 UI 스케일링 전략과 일관되게 맞춰져 있습니다.

Also applies to: 28-32

app/src/main/java/com/poti/android/domain/model/party/PartyImage.kt (1)

1-5: 도메인 모델 추가가 명확합니다.
순수 Kotlin 데이터 모델로 깔끔하게 정의되어 있습니다.

app/src/main/java/com/poti/android/domain/model/party/Participant.kt (1)

1-8: Participant 도메인 모델 정의가 적절합니다.
필드 구성도 명확하고 도메인 레이어 순수성도 유지됩니다.

app/src/main/java/com/poti/android/presentation/party/PartyNavigation.kt (1)

18-35: 변경 사항 문제 없습니다.

그래프 구성 흐름이 안정적으로 유지되어 보입니다.

app/src/main/java/com/poti/android/presentation/party/create/component/EditOptionPrice.kt (1)

50-135: 구성 흐름이 깔끔합니다.

입력 제한과 레이아웃 구성이 명확하게 읽힙니다.

app/src/main/java/com/poti/android/core/common/extension/IntExt.kt (1)

5-7: 확장 함수 추가 확인했습니다.

간단하고 사용 의도가 명확합니다.

app/src/main/java/com/poti/android/core/designsystem/component/display/PotiProfileSummary.kt (1)

34-56: 프로필 이미지 처리 변경 확인했습니다.

시그니처 변경과 기본 표시 흐름이 자연스럽습니다.

app/src/main/java/com/poti/android/presentation/party/home/component/GoodsSmallCard.kt (1)

123-152: 프리뷰 케이스 구성이 적절합니다.
짧은/긴 텍스트 케이스를 함께 보여줘서 UI 확인이 쉬워 보입니다.

app/src/main/java/com/poti/android/presentation/history/component/HistoryCardItem.kt (2)

56-145: 구성/상태 처리 모두 깔끔합니다.
사이즈별 스타일 분기와 pressed-state 배경 처리도 명확합니다.


147-176: 프리뷰로 사이즈 비교가 잘 드러납니다.

app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDropdown.kt (1)

126-177: 프리뷰 구성은 의도 전달에 충분합니다.

app/src/main/java/com/poti/android/presentation/party/create/component/HintTooltip.kt (2)

98-129: 프리뷰에서 토글 흐름이 잘 드러납니다.


52-63: Compose의 기본 클립핑으로 인해 이미 화면 밖 렌더링이 방지되고 있습니다.

PopupProperties에서 clippingEnabled을 명시적으로 설정하지 않았으므로 기본값인 true가 적용되며, 이 경우 Compose는 자동으로 팝업을 화면 범위로 클리핑합니다. 따라서 y가 음수가 되더라도 팝업이 화면 밖으로 완전히 나가지 않으며, 상단에서 잘려서 표시됩니다. 제안하신 coerce 처리는 이미 Compose에 의해 내부적으로 수행되고 있어 불필요합니다.

더 나은 UX를 원하신다면 (클립핑 대신 재배치) PopupPositionProvider에서 공간 부족 시 앵커 아래로 위치를 변경하는 로직을 추가하는 것을 고려해볼 수 있습니다.

Likely an incorrect or invalid review comment.

app/src/main/java/com/poti/android/domain/model/delivery/DeliveryOption.kt (1)

3-6: 간결한 불변 모델로 잘 정의되어 있습니다.

app/src/main/java/com/poti/android/domain/type/PartyStatusType.kt (1)

3-9: 상태 열거형 구성이 명확합니다.

app/src/main/java/com/poti/android/presentation/main/PotiNavHost.kt (1)

32-35: NavController 직접 주입으로 일관성이 좋아졌습니다.

app/src/main/java/com/poti/android/presentation/party/home/navigation/HomeNavigation.kt (1)

23-31: 네비게이션 연결이 명확해져서 좋습니다.

app/src/main/java/com/poti/android/presentation/party/detail/component/PartyUploaderInfo.kt (1)

22-57: LGTM! 컴포넌트 구조가 잘 설계되었습니다.

전체적으로 Modern Android Development 권장사항을 잘 따르고 있습니다:

  • Modifier가 첫 번째 선택적 파라미터로 올바르게 배치되었습니다.
  • stringResourcePotiTheme을 사용하여 하드코딩을 피했습니다.
  • 이벤트 처리가 onClick 콜백으로 적절히 호이스팅되어 있습니다.
app/src/main/java/com/poti/android/domain/model/user/UserSummary.kt (1)

1-9: LGTM! Clean Architecture를 잘 준수한 도메인 모델입니다.

Domain 레이어에서 순수 Kotlin 코드만 사용하여 Android 의존성이 없습니다. data classval 프로퍼티를 사용하여 불변성을 보장하고 있습니다. 각 프로퍼티에 대한 한글 주석도 명확하게 작성되어 있어 가독성이 좋습니다.

app/src/main/java/com/poti/android/presentation/party/detail/model/Contracts.kt (1)

10-30: LGTM! MVI 아키텍처 패턴이 잘 적용되었습니다.

Modern Android Development의 단방향 데이터 흐름(UDF) 원칙을 잘 따르고 있습니다:

  • PartyDetailUiStatedata class + val로 구성되어 불변성이 보장됩니다.
  • 네비게이션과 같은 일회성 이벤트가 PartyDetailEffect로 분리되어 화면 회전 시 재발생하지 않습니다.
  • sealed interface를 사용하여 Kotlin의 모던한 문법을 활용하고 있습니다.
app/src/main/java/com/poti/android/presentation/party/detail/DummyPartyDetailData.kt (1)

10-79: 전체적으로 더미 데이터 구조가 잘 작성되었습니다.

Preview와 테스트를 위한 더미 데이터로서 적절하게 구성되어 있습니다. 도메인 모델들의 실제 사용 예시를 보여주어 개발 시 참고하기 좋습니다.

app/src/main/java/com/poti/android/presentation/party/detail/component/PartyParticipantsInfo.kt (1)

23-70: 컴포넌트의 전체적인 구조가 잘 설계되었습니다.

  • Modifier가 첫 번째 선택적 파라미터로 올바르게 배치되었습니다.
  • stringResourcePotiTheme을 사용하여 하드코딩을 피했습니다.
  • PotiProfileSummaryPotiPrimaryTag 같은 디자인 시스템 컴포넌트를 적절히 재사용하고 있습니다.
app/src/main/java/com/poti/android/presentation/party/detail/component/PartyDetailHeaderInfo.kt (1)

23-28: Modifier 파라미터 위치 및 컴포저블 구조 적절합니다.

Modifier가 필수 파라미터 뒤 첫 번째 선택적 파라미터로 올바르게 위치해 있고, PotiTheme의 typography와 colors를 적절히 사용하고 계십니다. stringResource를 통한 문자열 리소스 사용도 좋습니다.

app/src/main/java/com/poti/android/presentation/party/detail/component/PartyDetailContent.kt (2)

81-100: FlowRow와 VerticalDivider 조합이 훌륭합니다.

FlowRow를 사용하여 배송 옵션들을 유연하게 배치하고, forEachIndexed를 통해 마지막 항목을 제외한 구분선 처리를 깔끔하게 구현하셨습니다. IntrinsicSize.Min을 활용한 높이 처리도 적절합니다.


26-29: 컴포저블 시그니처가 올바릅니다.

Modifier가 필수 파라미터 뒤 첫 번째 선택적 파라미터로 적절히 배치되어 있습니다.

app/src/main/java/com/poti/android/domain/model/party/PartyDetail.kt (1)

1-6: Domain 레이어 순수성이 잘 유지되었습니다.

Android 의존성 없이 순수 Kotlin 코드로 작성되었고, 같은 Domain 레이어 내의 모델들만 import하고 있습니다. Clean Architecture의 의존성 규칙을 잘 준수하고 계십니다.

app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailViewModel.kt (2)

36-40: partyIdloadPartyDetail에서 사용되지 않고 있습니다.

partyId(line 21)가 추출되었지만 loadPartyDetail 함수에서 실제로 사용되지 않고 있습니다. TODO 주석에 서버 연결 예정이라고 되어 있으니, 서버 연결 시 partyId를 파라미터로 전달하는 것을 잊지 마시기 바랍니다.


14-34: MVI 패턴이 올바르게 구현되었습니다.

BaseViewModel을 상속하여 UiState, Intent, Effect를 제네릭으로 잘 활용하고 계십니다. @HiltViewModel@Inject 어노테이션이 적절히 사용되었고, processIntent를 통한 단방향 데이터 흐름이 잘 구현되어 있습니다. sendEffect를 통한 일회성 이벤트(네비게이션) 처리도 적절합니다.

app/src/main/java/com/poti/android/presentation/party/detail/navigation/PartyDetailNavigation.kt (3)

15-25: Type-safe Navigation이 잘 구현되었습니다.

@Serializable 어노테이션을 사용한 Type-safe Navigation 패턴이 적절히 적용되었습니다. Detaildata class로 변경하여 partyId를 전달하는 방식이 Modern Android Development 권장 사항에 부합합니다.


31-42: NavController 메서드 참조 활용이 깔끔합니다.

navController::popBackStack, navController::navigateToPartyJoin, navController::navigateToProfile과 같이 메서드 참조를 사용하여 콜백을 전달하는 방식이 간결하고 가독성이 좋습니다.


43-47: PartyJoinRoute의 콜백 파라미터 설계 의도 확인 필요합니다.

PartyJoinRoutePartyDetailRoute 함수 시그니처를 비교하면, PartyDetailRoute에는 onPopBackStack, onNavigateToJoin, onNavigateToProfile 콜백이 있지만 PartyJoinRoute에는 콜백이 없는 상태입니다.

다만 PartyJoinScreen.kt이 현재 스텁 상태(구현 미완)이므로, 화면이 완성되면 뒤로가기 콜백이 실제로 필요한지 판단할 수 있을 것 같습니다. UI 구현 시 필요하다면 onPopBackStack 콜백 추가를 검토해보세요.

app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailScreen.kt (1)

167-176: 프리뷰 구성 좋습니다.
더미 데이터로 화면 확인이 쉬워졌습니다.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +180 to +187
private class PriceVisualTransformation : VisualTransformation {
override fun filter(text: AnnotatedString): TransformedText {
val text = text.text

val textWithComma = when (text.length) {
0 -> text
else -> text.toInt().toMoneyString()
}
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

# First, let's find the file and check its size
find . -name "EditOptionPrice.kt" -type f 2>/dev/null

# Check the file size
if [ -f "app/src/main/java/com/poti/android/presentation/party/create/component/EditOptionPrice.kt" ]; then
  wc -l "app/src/main/java/com/poti/android/presentation/party/create/component/EditOptionPrice.kt"
fi

Repository: team-poti/POTI-ANDROID

Length of output: 251


🏁 Script executed:

#!/bin/bash

# Read the file to see the context around lines 180-187
cat -n "app/src/main/java/com/poti/android/presentation/party/create/component/EditOptionPrice.kt" | sed -n '170,200p'

Repository: team-poti/POTI-ANDROID

Length of output: 1252


🏁 Script executed:

#!/bin/bash

# Check if there are any input validators or filters on the TextField
rg -A 10 -B 10 "PriceVisualTransformation" "app/src/main/java/com/poti/android/presentation/party/create/component/EditOptionPrice.kt"

Repository: team-poti/POTI-ANDROID

Length of output: 1361


🏁 Script executed:

#!/bin/bash

# Read the full function context to see TextField configuration
cat -n "app/src/main/java/com/poti/android/presentation/party/create/component/EditOptionPrice.kt" | sed -n '140,180p'

Repository: team-poti/POTI-ANDROID

Length of output: 1547


🏁 Script executed:

#!/bin/bash

# Check the function signature and beginning
cat -n "app/src/main/java/com/poti/android/presentation/party/create/component/EditOptionPrice.kt" | head -30

Repository: team-poti/POTI-ANDROID

Length of output: 1691


toInt() 호출 시 안전하지 않은 파싱으로 크래시 위험이 있습니다.

KeyboardType.Number는 IME에 대한 힌트일 뿐이며, 클립보드 붙여넣기나 프로그래매틱 입력으로 숫자가 아닌 문자가 들어올 수 있습니다. VisualTransformation은 모든 텍스트 변경을 처리하므로, 범위를 벗어난 값이나 형식이 잘못된 입력도 toInt()에서 예외를 발생시킬 수 있어요.

🛠️ 안전 파싱 제안
-            else -> text.toInt().toMoneyString()
+            else -> text.toIntOrNull()?.toMoneyString() ?: 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
private class PriceVisualTransformation : VisualTransformation {
override fun filter(text: AnnotatedString): TransformedText {
val text = text.text
val textWithComma = when (text.length) {
0 -> text
else -> text.toInt().toMoneyString()
}
private class PriceVisualTransformation : VisualTransformation {
override fun filter(text: AnnotatedString): TransformedText {
val text = text.text
val textWithComma = when (text.length) {
0 -> text
else -> text.toIntOrNull()?.toMoneyString() ?: text
}
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/poti/android/presentation/party/create/component/EditOptionPrice.kt`
around lines 180 - 187, The PriceVisualTransformation.filter implementation uses
text.toInt() which can throw on non-numeric input; update filter in the
PriceVisualTransformation class to parse safely (e.g., use text.toIntOrNull() or
strip non-digit characters first) and handle null/overflow by falling back to a
safe value or returning the original text transformation; ensure the method
still calls toMoneyString() only on a valid parsed number and that non-numeric
inputs are preserved or formatted safely to avoid crashes in
VisualTransformation.filter.

Comment on lines +61 to +71
when (val state = uiState.partyDetail) {
is ApiState.Success -> {
PartyDetailScreen(
partyDetail = state.data,
onBackClick = { viewModel.processIntent(PartyDetailIntent.OnBackClick) },
onJoinClick = { viewModel.processIntent(PartyDetailIntent.OnJoinClick) },
onUploaderClick = { viewModel.processIntent(PartyDetailIntent.OnUploaderClick(it)) },
modifier = modifier,
)
}
else -> {}
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

성공 외 상태에서 빈 화면이 됩니다.
Loading/Error 상태에서도 최소한의 로딩/에러/재시도 UI를 노출해 주세요. 현재는 Line 61-71에서 else -> {}로 처리되어 화면이 비어 보입니다.

🤖 Prompt for AI Agents
In
`@app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailScreen.kt`
around lines 61 - 71, The current when block handling uiState.partyDetail only
renders PartyDetailScreen for ApiState.Success and leaves else -> {} which
yields an empty screen; update the branch to handle ApiState.Loading and
ApiState.Error (and any other non-success states) by rendering a minimal loading
indicator or an error view with a retry action that dispatches
viewModel.processIntent(PartyDetailIntent.Retry) (or PartyDetailIntent.OnRetry
if that exists), and ensure the Error UI surfaces the error message from
ApiState.Error so users can retry or see the failure; modify the when on
uiState.partyDetail (around ApiState.Success / PartyDetailScreen) to include
these explicit branches instead of else -> {}.

Comment on lines +117 to +121
PartyDetailHeaderInfo(
partyDetail = partyDetail,
onLikeClick = {},
modifier = Modifier.padding(horizontal = screenWidthDp(16.dp), vertical = 20.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

좋아요 버튼이 무동작 상태입니다.
onLikeClick = {}로 클릭 이벤트가 무시됩니다. 최소한 Intent로 연결하거나 기능 준비 전에는 비활성/숨김 처리해 주세요.

🤖 Prompt for AI Agents
In
`@app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailScreen.kt`
around lines 117 - 121, The like button is non-functional because
PartyDetailHeaderInfo is passed an empty lambda for onLikeClick; replace
onLikeClick = {} with a real handler that either dispatches the intended Intent
or calls the viewmodel action (e.g., PartyDetailViewModel.onLikeClicked or a
navigation intent) so the click is handled, or if the feature isn’t ready
hide/disable the button inside PartyDetailHeaderInfo (or pass a flag like
isLikeEnabled = false) so it is not shown as interactive; locate
PartyDetailHeaderInfo usage in PartyDetailScreen and update the onLikeClick
argument accordingly or add an enabled/visibility flag to PartyDetailHeaderInfo
to reflect feature readiness.

Comment on lines +51 to +52
val isClosed = totalCount - currentCount == 0
val contentAlpha = if (isClosed) 0.5f else 1f
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

마감 판단 조건을 안전하게 처리해주세요.
currentCounttotalCount를 초과하는 경우 마감 처리되지 않아 잔여 표기가 이상해질 수 있습니다. <= 0 기준으로 처리하는 쪽이 안전합니다.

🛠️ 제안 수정
-    val isClosed = totalCount - currentCount == 0
+    val isClosed = totalCount - currentCount <= 0
📝 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
val isClosed = totalCount - currentCount == 0
val contentAlpha = if (isClosed) 0.5f else 1f
val isClosed = totalCount - currentCount <= 0
val contentAlpha = if (isClosed) 0.5f else 1f
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/poti/android/presentation/party/goodsfilter/component/PotsCard.kt`
around lines 51 - 52, The isClosed calculation using val isClosed = totalCount -
currentCount == 0 is unsafe when currentCount can exceed totalCount; update the
condition to treat any non-positive remaining seats as closed (for example
replace with val isClosed = totalCount - currentCount <= 0 or val isClosed =
currentCount >= totalCount) and keep contentAlpha derived from that isClosed
value (contentAlpha = if (isClosed) 0.5f else 1f) so the UI correctly reflects
closed state and remaining-count display.

<string name="party_detail_participants">참여자</string>
<string name="party_detail_review_count">%d개의 평가</string>
<string name="party_detail_participants_count">%1$d/%2$d</string>
<string name="party_detail_announcement">안내 사항\n어쩌구 저쩌구\n어쩌구 저쩌구\n어쩌구 저쩌구\n</string>
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

party_detail_announcement의 더미 문구는 출시용 문자열로 부적절합니다.
“어쩌구 저쩌구”는 사용자 노출 시 품질 저하가 크니 실제 문구로 교체하거나, 프리뷰 전용 리소스로 분리해 주세요.

🤖 Prompt for AI Agents
In `@app/src/main/res/values/strings.xml` at line 36, The string resource
party_detail_announcement contains placeholder text ("어쩌구 저쩌구") inappropriate
for release; replace its value with the actual announcement copy (keeping
intentional line breaks if needed) or move this placeholder into a preview-only
resource (e.g., create a preview-specific string name like
party_detail_announcement_preview or place it in a preview/qa resource
qualifier) and update code to use the preview key only in preview builds so the
release build never exposes the dummy text.

}

// TODO: [천민재] 임시 util 함수
private fun priceText(price: Int) = String.format("%,d원", price)
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: 요기도 수정해조

@github-actions
Copy link
Copy Markdown

github-actions bot commented Jan 16, 2026

Ktlint check passed.

Run: https://github.com/team-poti/POTI-ANDROID/actions/runs/21066600229

@github-actions
Copy link
Copy Markdown

github-actions bot commented Jan 16, 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: 1

🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt`:
- Around line 18-22: HistoryParticipantOverview fails to compile because the
extension function toMoneyString is not imported; fix by adding the appropriate
import for the toMoneyString extension (or call it with its FQCN) where it’s
used in HistoryParticipantOverview and the other occurrences in the same file so
the compiler can resolve the extension function.
🧹 Nitpick comments (1)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt (1)

80-83: 빈 문자열 결합 제거

price.toMoneyString() + ""는 의미 없는 결합입니다. 그대로 문자열이므로 불필요한 연산을 제거해 주세요.

♻️ 제안하는 수정
-                text = price.toMoneyString() + "",
+                text = price.toMoneyString(),
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 43ae70f and 8554a06.

📒 Files selected for processing (2)
  • app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDetail.kt
  • app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantDetail.kt
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt

⚙️ CodeRabbit configuration file

**/*.kt: 코드를 리뷰할 때 다음 Kotlin 및 Jetpack Compose 모범 사례를 중점적으로 확인:

  1. Jetpack Compose 성능 및 UI 최적화:

    • Recomposition 방지: 불필요한 리컴포지션을 유발하는 코드를 지적해줘. 특히 LazyColumn 등 리스트에서 key 파라미터가 누락되었는지 확인해줘.
    • 스마트한 계산: 계산 비용이 높은 로직은 rememberderivedStateOf로 감싸져 있는지 확인해줘.
    • 안정성(Stability): List나 일반 클래스를 State로 사용할 때, 불필요한 리컴포지션을 막기 위해 @Immutable이나 @Stable 어노테이션 사용을 제안해줘.
    • Modifier 규칙: Modifier는 항상 컴포저블 함수의 첫 번째 선택적 파라미터로 받아야 하며, 재사용성을 위해 외부에서 주입받아야 해.
    • 리소스 사용: 하드코딩된 값(색상, 크기, 문자열) 대신 Theme, Dimens, strings.xml 리소스를 사용하는지 체크해줘.
    • 접근성 예외: Icon이나 Image 컴포저블의 contentDescriptionnull인 경우는 장식용(Decorative) 이미지로 간주하고 지적하지 마.
  2. MVI (Model-View-Intent) 아키텍처 및 상태 관리:

    • 단방향 데이터 흐름(UDF): UI는 상태를 직접 변경할 수 없고, 반드시 Intent(또는 Event)를 통해 ViewModel로 요청을 보내야 해.
    • 불변 상태(Immutable State): UiState는 반드시 data class + val 프로퍼티로 구성되어야 하며, 상태 업데이트 시 .copy()를 사용해야 해.
    • Side Effect 분리: 네비게이션, Toast 같은 일회성 이벤트는 UiState가 아닌 Channel이나 SharedFlow로 처리하여 화면 회전 시 재발생하지 않도록 해야 해.
    • 안전한 수집: UI에서 Flow 수집 시 collectAsStateWithLifecycle()을 사용하여 생명주기를 준수하는지 확인해줘.
    • 상태 노출: ViewModel 내부에서 StateFlowSharedFlow를 올바르게 노출하고 있는지 확인해줘.
    • 호이스팅(Hoisting): 상태나 UI 이벤트 처리가 적절하게 호이스팅되었는지 확인해줘. UI 로직과 비즈니스 로직을 분리하고, UI가 로직을 직접 수행하지 않도록 확인해줘.
  3. Clean Architecture (Dependency Rule) 준수:

    • Domain 순수성: Domain 레이어(domain 패키지)는 절대로 안드로이드 의존성(android.*, R 클래스 등)을 가져서는 안 돼. 순수 Kotlin 코드인지 엄격히 확인해줘.
    • 데이터 노출 금지: Data 레이어의 DTO(@Entity, @SerializedName)가 UI나 Domain 레이어로 바로 노출되면 안 돼. 반드시 Mapper를 통해 변환했는지 확인해줘.
    • 계층 간 의존성: UI -> Domain <- Data 방향이 올바른지, UI가 Data 레이어를 직접 참조하고 있진 않은지 감시해줘.
  4. Kotlin & Coroutines 안전성:

    • Scope 사용: GlobalScope 사용을 금지하고, viewModelScopelifecycleScope 사용을 권장해줘.
    • Dispatcher 주입: Dispatchers.IO 등을 하드코딩하지 말고, 테스트 용이성을 위해 생성자를 통해 주입(Injection)받도록 제안해줘.
    • 예외 처리: Coroutine 실행 시 runCatching이나 CoroutineExceptionHandler 등을 통해 예외 처리가 누락되지 않았는지 확인해줘.
    • Null-Safety: 불필요한 !! 연산자 사용을 지적...

Files:

  • app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt
🧬 Code graph analysis (1)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt (4)
app/src/main/java/com/poti/android/core/common/util/DimensionUtil.kt (1)
  • screenHeightDp (12-18)
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantStateLabel.kt (1)
  • HistoryParticipantStateLabel (40-56)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiItemOption.kt (1)
  • PotiItemOption (41-67)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
🪛 GitHub Actions: PR CI Check
app/src/main/java/com/poti/android/presentation/history/component/HistoryParticipantOverview.kt

[error] 82-82: Unresolved reference 'toMoneyString'. Build step './gradlew :app:assembleDebug' failed.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Copy Markdown
Contributor

@jyvnee jyvnee 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

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[Feat] participant-overview, detail, dropdown 구현

4 participants