Files
honeyDueKMP/iosApp/iosApp/Documents/Components/DocumentCard.swift
T
Trey T db65db6232
Android UI Tests / ui-tests (push) Has been cancelled
i18n: complete app-wide localization (10 languages) + audit tooling
Localize all user-facing strings across iOS (SwiftUI), shared Kotlin, and
Android Compose into en/es/fr/de/pt/it/ja/ko/nl/zh:
- iOS String Catalogs: main + widget Localizable.xcstrings, InfoPlist.xcstrings
  (permissions), plural variations, ~200 new keys translated
- Shared Kotlin ClientStrings table + Android composeResources/values-* (884 keys
  ×10), routed Api/ViewModel/util error & UI strings through localization
- Backend-localized lookups/suggestions consumed via display names
- Widget extension catalog; theme names, home-profile fallbacks, validation,
  network errors, accessibility labels all localized

Add re-runnable verification gates:
- scripts/i18n_audit.py  — enumerate every literal, partition to GAP=0
- scripts/i18n_coverage.py — all 10 locales translated, format-specifier parity

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 20:52:28 -05:00

107 lines
3.5 KiB
Swift

import SwiftUI
import ComposeApp
struct DocumentCard: View {
let document: Document
var typeColor: Color {
switch document.documentType {
case "warranty": return Color.appPrimary
case "manual": return Color.appSecondary
case "receipt": return Color.appAccent
case "inspection": return Color.appAccent
default: return Color.appTextSecondary
}
}
var typeIcon: String {
switch document.documentType {
case "photo": return "photo"
case "warranty", "insurance": return "checkmark.shield"
case "manual": return "book"
case "receipt": return "receipt"
default: return "doc.text"
}
}
var body: some View {
HStack(spacing: AppSpacing.md) {
// Document Icon
VStack {
Image(systemName: typeIcon)
.font(.system(size: 24))
.foregroundColor(typeColor)
.background(content: {
RoundedRectangle(cornerRadius: 8)
.fill(typeColor.opacity(0.1))
.frame(width: 56, height: 56)
})
.padding(AppSpacing.md)
.accessibilityHidden(true)
Spacer()
}
VStack(alignment: .leading, spacing: 4) {
Text(document.title)
.font(.title3.weight(.semibold))
.fontWeight(.bold)
.foregroundColor(Color.appTextPrimary)
// .lineLimit(1)
if let description = document.description_, !description.isEmpty {
Text(description)
.font(.callout)
.foregroundColor(Color.appTextSecondary)
.lineLimit(2)
}
HStack(spacing: 8) {
Text(getDocTypeDisplayName(document.documentType))
.font(.caption.weight(.medium))
.foregroundColor(typeColor)
.padding(.horizontal, 6)
.padding(.vertical, 2)
.background(typeColor.opacity(0.2))
.cornerRadius(4)
if let fileSize = document.fileSize {
Text(formatFileSize(Int(fileSize)))
.font(.caption.weight(.medium))
.foregroundColor(Color.appTextSecondary)
}
}
}
Spacer()
Image(systemName: "chevron.right")
.foregroundColor(Color.appTextSecondary)
.font(.system(size: 14))
.accessibilityHidden(true)
}
.padding(AppSpacing.md)
.background(Color.appBackgroundSecondary)
.cornerRadius(AppRadius.md)
.shadow(color: Color.black.opacity(0.05), radius: 2, x: 0, y: 1)
.accessibilityElement(children: .combine)
}
private func getDocTypeDisplayName(_ type: String) -> String {
return DocumentTypeHelper.displayName(for: type)
}
private func formatFileSize(_ bytes: Int) -> String {
var size = Double(bytes)
let units = ["B", "KB", "MB", "GB"]
var unitIndex = 0
while size >= 1024 && unitIndex < units.count - 1 {
size /= 1024
unitIndex += 1
}
return String(format: "%.1f %@", size, units[unitIndex])
}
}