Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Android/app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@
-keep class * implements skip.bridge.** { *; }
-keep class **._ModuleBundleAccessor_* { *; }
-keep class pi.ckadmin.** { *; }
-dontwarn kotlin.reflect.full.KClasses
-dontwarn kotlin.reflect.full.KTypes
-dontwarn kotlin.reflect.jvm.KTypesJvm
2 changes: 2 additions & 0 deletions Android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

<!-- permissions needed for using the internet or an embedded WebKit browser -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<!-- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> -->

<!-- Android 13+ notification permission -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ final class BugReportViewModel {
bodyData.append("--\(boundary)--\r\n".data(using: .utf8)!)

let endpoint = BugReportAPI.uploadImages(boundary: boundary, body: bodyData)
let response = try await APIClient.shared.request(endpoint, responseType: [String].self)
return response
let response = try await APIClient.shared.request(endpoint, responseType: ImageUploadResponse.self)
return response.fileNames
}

private func submitReport(fileNames: [String]) async throws {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,16 @@ struct StudentAttendanceCell: View {
.pickText(type: .body2, textColor: statusColor(student.status))
.padding(.horizontal, 16)
.padding(.vertical, 8)
.background(statusBackgroundColor(student.status))
.cornerRadius(8)
.background(
RoundedRectangle(cornerRadius: 8)
.fill(statusBackgroundColor(student.status))
)
.overlay(
RoundedRectangle(cornerRadius: 8)
.stroke(statusBorderColor(student.status), lineWidth: 1)
)
Comment on lines +25 to +32
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 | ⚡ Quick win

"출석"과 "이동" 상태의 테두리가 보이지 않습니다.

"출석" 상태는 배경색과 테두리색이 모두 Primary.primary500로 동일하고, "이동" 상태는 둘 다 Gray.gray700입니다. 배경과 테두리가 같은 색상이면 테두리가 시각적으로 구분되지 않습니다.

의도된 디자인인지 확인이 필요합니다. 만약 테두리를 표시하려는 의도였다면, 테두리 색상을 배경색과 구분되는 색상으로 변경해야 합니다.

🎨 테두리를 보이게 하려면 색상 수정 예시
 private func statusBorderColor(_ status: String) -> Color {
     switch status {
     case "출석":
-        return .Primary.primary500
+        return .Primary.primary600  // 또는 더 진한 색상
     case "이동":
-        return .Gray.gray700
+        return .Gray.gray800  // 또는 더 진한 색상
     case "귀가", "외출":
         return .Error.error

Also applies to: 73-86

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Sources/PiCKAdmin/Features/SelfStudyCheck/Components/SelfStudyCheckComponents.swift`
around lines 25 - 32, The border is invisible for statuses where
statusBackgroundColor(student.status) equals statusBorderColor(student.status)
(e.g., "출석" and "이동"); update the statusBorderColor function (or wherever
statusBorderColor is defined/returned) to return a contrasting color for those
specific status values (use student.status matching "출석"/"이동" or the enum cases)
so the RoundedRectangle.stroke(...) overlay is visually distinct from the
RoundedRectangle.fill(...) background; ensure the change affects all places
using statusBorderColor(student.status) (including the other occurrence
referenced) so the border becomes visible.

}
.buttonStyle(.plain)
}
.padding(.vertical, 12)
.padding(.horizontal, 16)
Expand All @@ -36,13 +43,13 @@ struct StudentAttendanceCell: View {
private func statusColor(_ status: String) -> Color {
switch status {
case "출석":
return .Primary.primary500
return .Normal.white
case "이동":
return .Gray.gray700
return .Normal.white
Comment on lines +46 to +48
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: 디자인 시스템의 색상 정의를 찾아 Primary.primary500, Gray.gray700, Normal.white 값 확인

rg -n "primary500|gray700" --type swift -A 2 -B 2

Repository: DSM-PICK/PiCK_Android_ADMIN

Length of output: 40673


🏁 Script executed:

# Find the Normal.white color definition
rg -n "Normal.*white|white.*=" --type swift Sources/PiCKAdmin/DesignSystem/ -A 1 -B 1

Repository: DSM-PICK/PiCK_Android_ADMIN

Length of output: 937


🏁 Script executed:

# Also examine the specific file and lines mentioned in the review
head -80 Sources/PiCKAdmin/Features/SelfStudyCheck/Components/SelfStudyCheckComponents.swift | tail -40

Repository: DSM-PICK/PiCK_Android_ADMIN

Length of output: 1090


🏁 Script executed:

# Calculate contrast ratios for the color combinations
python3 << 'EOF'
from fractions import Fraction

def hex_to_rgb(hex_color):
    hex_color = hex_color.lstrip('#')
    return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))

def get_relative_luminance(r, g, b):
    """Calculate relative luminance according to WCAG formula"""
    r = r / 255
    g = g / 255
    b = b / 255
    
    r = r / 12.92 if r <= 0.03928 else ((r + 0.055) / 1.055) ** 2.4
    g = g / 12.92 if g <= 0.03928 else ((g + 0.055) / 1.055) ** 2.4
    b = b / 12.92 if b <= 0.03928 else ((b + 0.055) / 1.055) ** 2.4
    
    return 0.2126 * r + 0.7152 * g + 0.0722 * b

def get_contrast_ratio(lum1, lum2):
    """Calculate contrast ratio"""
    lighter = max(lum1, lum2)
    darker = min(lum1, lum2)
    return (lighter + 0.05) / (darker + 0.05)

# Colors
white_rgb = (255, 255, 255)  # Normal.white
primary500_rgb = hex_to_rgb("`#9650FA`")
gray700_rgb = hex_to_rgb("`#79777C`")

# Calculate luminances
white_lum = get_relative_luminance(*white_rgb)
primary500_lum = get_relative_luminance(*primary500_rgb)
gray700_lum = get_relative_luminance(*gray700_rgb)

# Calculate contrast ratios
ratio_white_on_primary = get_contrast_ratio(white_lum, primary500_lum)
ratio_white_on_gray = get_contrast_ratio(white_lum, gray700_lum)

print(f"White (`#FFFFFF`) on Primary.primary500 (`#9650FA`): {ratio_white_on_primary:.2f}:1")
print(f"White (`#FFFFFF`) on Gray.gray700 (`#79777C`): {ratio_white_on_gray:.2f}:1")
print()
print("WCAG AA Requirements:")
print("- Normal text: 4.5:1")
print("- Large text (18pt+/bold 14pt+): 3:1")
print()
if ratio_white_on_primary < 4.5:
    print(f"✗ Primary.primary500: FAILS (Current: {ratio_white_on_primary:.2f}:1)")
if ratio_white_on_gray < 4.5:
    print(f"✗ Gray.gray700: FAILS (Current: {ratio_white_on_gray:.2f}:1)")
EOF

Repository: DSM-PICK/PiCK_Android_ADMIN

Length of output: 345


접근성: 흰색 텍스트의 명암비가 WCAG AA 기준을 충족하지 못합니다.

"출석" 상태는 흰색 텍스트를 Primary.primary500(#9650FA) 배경에 표시하고, "이동" 상태는 흰색 텍스트를 Gray.gray700(#79777C) 배경에 표시합니다.

실제 명암비 계산 결과:

  • 흰색 on Primary.primary500: 4.35:1 (필요: 4.5:1)
  • 흰색 on Gray.gray700: 4.43:1 (필요: 4.5:1)

두 조합 모두 일반 텍스트 기준(4.5:1)을 0.07~0.15 포인트 하회합니다. 텍스트 색상을 밝게 변경하거나 배경색을 어둡게 하여 명암비를 4.5:1 이상으로 개선해야 합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Sources/PiCKAdmin/Features/SelfStudyCheck/Components/SelfStudyCheckComponents.swift`
around lines 46 - 48, Switch cases for "출석" and "이동" currently return
.Normal.white which yields contrast ratios below WCAG AA (4.5:1) against
Primary.primary500 and Gray.gray700; update the return values for the "출석" and
"이동" branches so the text color (or the background color used with them) meets
at least 4.5:1 contrast—either pick a lighter text color token that achieves
≥4.5:1 on Primary.primary500 and Gray.gray700, or darken those background tokens
and verify with a contrast checker; ensure the changes are applied where the
code returns .Normal.white for the "출석" and "이동" cases.

case "귀가", "외출":
return .Primary.primary400
return .Error.error
case "현체", "취업":
return .Gray.gray600
return .Gray.gray900
default:
return .Normal.black
}
Expand All @@ -51,15 +58,30 @@ struct StudentAttendanceCell: View {
private func statusBackgroundColor(_ status: String) -> Color {
switch status {
case "출석":
return .Primary.primary50
return .Primary.primary500
case "이동":
return .Gray.gray100
return .Gray.gray700
case "귀가", "외출":
return .Primary.primary50.opacity(0.5)
return .Error.errorLight
case "현체", "취업":
return .Gray.gray100
return .Gray.gray200
default:
return .Gray.gray100
}
}

private func statusBorderColor(_ status: String) -> Color {
switch status {
case "출석":
return .Primary.primary500
case "이동":
return .Gray.gray700
case "귀가", "외출":
return .Error.error
case "현체", "취업":
return .Gray.gray600
default:
return .Gray.gray400
}
}
}