UX Phase 2: Important — 위계/일관성 회복

스크린샷에서 발견된 시각 위계/정보 비대칭 4건 fix.

5. Voiding 버튼 fill 격상 (가장 자주 누르는 액션을 primary로)
   - containerColor: MlPrimary.copy(alpha=0.1f) → MlPrimary 솔리드
   - text color: MlPrimary → White
   - Auto Scan(주황) / Single Scan(청록)과 동급 강도로 일관성 회복
   - 환자가 가장 자주 누르는 액션이 옅게 디자인되어 있던 문제 해결

6. InfoCard 우측 라벨 명시 ("Session Max")
   - 이전: title="X mL / Y mL" + value="65%" (라벨 없음, 의미 모호)
   - 새로: title="Session Max", value="X" + unit="mL"
   - 좌측 "Current"와 같은 톤. fill % 정보는 도넛이 이미 시각화 중이라 카드는 raw 숫자만
   - 좌측 title도 "Current Measurement" → "Current"로 단순화

7. Alignment Hint 박스 톤다운 (3중 강조 해소)
   - fontSize 28sp → 20sp
   - padding horizontal 20→16dp, vertical 18→12dp
   - 배경 alpha 1.0 → 0.7
   - corner 16→14dp
   - "pubic bone" 빨강 강조는 유지 (의도된 1점 강조)

8. Step 1/3 도트 indicator 추가 (텍스트 한 줄 → 시각 progress)
   - 이전: 13sp 회색 텍스트 한 줄 (정보 위계 너무 약함)
   - 새로: 14sp Bold MlPrimary + 3-dot indicator
     ○ 완료된 step: MlPrimary 0.6 alpha, 8dp
     ● 현재 step: MlPrimary 솔리드, 12dp (강조)
     ○ 미완료 step: 회색 0.3 alpha, 8dp

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-26 16:04:03 +09:00
parent 59267866b4
commit 52bd58ac87
2 changed files with 47 additions and 19 deletions
@@ -1095,11 +1095,12 @@ fun PiezoMonitoringView(appState: AppState) {
// Auto 윈도우 < 5일 때는 표시 안 함
val showMeasurement = if (isAutoMeasuring) autoVolumeWindow.size >= 5 && lastVolumeMl > 0 else lastVolumeMl > 0
val currentMeasurement = if (showMeasurement) "%.0f".format(lastVolumeMl) else "—"
InfoCard("Current Measurement", currentMeasurement, "mL", urgency.accentColor, Modifier.weight(1f))
InfoCard("Current", currentMeasurement, "mL", urgency.accentColor, Modifier.weight(1f))
// 우측 카드: title 명시 "Session Max", 값은 maxMeasured (단위 mL).
// 이전엔 title이 "X mL / Y mL"로 들어가고 값에 % 표기되어 의미 모호했음.
// % 정보는 도넛 차트가 이미 시각화 중 → 카드는 raw 숫자만.
val maxMeasured = if (displayMaxVolumeMl > 0) "%.0f".format(displayMaxVolumeMl) else "—"
val bladderMax = "${appState.piezoSettings.maxVolume}"
val fillMain = if (fillPct > 0) "${"%.0f".format(fillPct)}%" else "—"
InfoCard("$maxMeasured mL / $bladderMax mL", fillMain, "", urgency.accentColor, Modifier.weight(1f))
InfoCard("Session Max", maxMeasured, "mL", urgency.accentColor, Modifier.weight(1f))
}
// Control buttons: Void/Catheterize / Auto / Spot
@@ -1129,12 +1130,14 @@ fun PiezoMonitoringView(appState: AppState) {
modifier = Modifier.weight(1f).height(btnHeight),
shape = RoundedCornerShape(14.dp),
colors = ButtonDefaults.buttonColors(
containerColor = MlPrimary.copy(alpha = 0.1f),
disabledContainerColor = Color.Gray.copy(alpha = 0.1f)
// 환자가 가장 자주 누르는 액션 — Auto/Single과 동일한 fill 강도로 격상.
// MlPrimary 솔리드 (이전: alpha 0.1로 묻혀있던 옅은 파랑)
containerColor = MlPrimary,
disabledContainerColor = Color.Gray.copy(alpha = 0.3f)
)
) {
// 단일 라벨로 단순화 — 이전 2줄(Voiding/Catheterization)이 좁은 폭에서 3줄로 깨짐
Text("Voiding", fontSize = (14 * fontScale).sp, fontWeight = FontWeight.SemiBold, color = MlPrimary)
Text("Voiding", fontSize = (14 * fontScale).sp,
fontWeight = FontWeight.SemiBold, color = Color.White)
}
// Auto (Spot 진행 중 봉인)
@@ -198,19 +198,41 @@ fun PlacementGuideView(appState: AppState) {
horizontalAlignment = Alignment.CenterHorizontally
) {
// Step indicator + mode badge (이미지 위에 배치)
// Step progress — 텍스트 + 3-dot indicator (현재 단계 강조)
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.padding(vertical = 4.dp)
horizontalArrangement = Arrangement.spacedBy(10.dp),
modifier = Modifier.padding(vertical = 6.dp)
) {
val currentStepIdx = when (placementPhase) {
PlacementPhase.VERTICAL -> 0
PlacementPhase.LATERAL -> 1
PlacementPhase.GREEN -> 2
}
Text(
text = when (placementPhase) {
PlacementPhase.VERTICAL -> "Step 1/3"
PlacementPhase.LATERAL -> "Step 2/3"
PlacementPhase.GREEN -> "Step 3/3"
},
fontSize = 13.sp, fontWeight = FontWeight.SemiBold, color = MlSecondaryText
text = "Step ${currentStepIdx + 1}/3",
fontSize = 14.sp, fontWeight = FontWeight.Bold, color = MlPrimary
)
// 3-dot indicator: 완료(○ → ●), 현재(큰 ●), 미완료(○)
Row(horizontalArrangement = Arrangement.spacedBy(6.dp),
verticalAlignment = Alignment.CenterVertically) {
repeat(3) { i ->
val isDone = i < currentStepIdx
val isCurrent = i == currentStepIdx
val dotSize = if (isCurrent) 12.dp else 8.dp
val dotColor = when {
isCurrent -> MlPrimary
isDone -> MlPrimary.copy(alpha = 0.6f)
else -> Color.Gray.copy(alpha = 0.3f)
}
Box(
modifier = Modifier
.size(dotSize)
.clip(CircleShape)
.background(dotColor)
)
}
}
if (isSweepMode) {
Text(
"SWEEP",
@@ -263,16 +285,19 @@ fun PlacementGuideView(appState: AppState) {
append(rawHint)
}
}
// Hint 톤다운 — 28sp+큰 박스+빨강 강조 3중 중첩이었음.
// 폰트 28→20sp, padding 18→12dp, 박스 alpha 살짝 ↓.
// "pubic bone" 빨강 강조는 유지 (의도된 1점 강조).
Text(
text = displayHint,
fontSize = 28.sp,
fontSize = 20.sp,
fontWeight = FontWeight.SemiBold,
color = hintFg,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.background(hintBg, RoundedCornerShape(16.dp))
.padding(horizontal = 20.dp, vertical = 18.dp)
.background(hintBg.copy(alpha = 0.7f), RoundedCornerShape(14.dp))
.padding(horizontal = 16.dp, vertical = 12.dp)
)
// 큰 타이틀과 몸 사이 간격 (Body 상단 Spacer 32→8dp)