Files
honeyDueKMP/iosApp/iosApp/Components/TaskSummaryCard.swift
Trey T af73f8861b iOS VoiceOver accessibility overhaul — 67 files
New framework:
- AccessibilityLabels.swift: centralized A11y struct with VoiceOver strings
- AccessibilityModifiers.swift: reusable .a11yHeader, .a11yDecorative,
  .a11yButton, .a11yCard, .a11yStatValue View extensions

Shared components: decorative elements hidden, stat views combined,
status/priority badges labeled, error views announced, empty states grouped

Cards: ResidenceCard, TaskCard, DynamicTaskCard, ContractorCard,
DocumentCard, WarrantyCard — all grouped with combined labels,
chevrons hidden, action buttons labeled

Main screens: Login, Register, Residences, Tasks, Contractors, Documents —
toolbar buttons labeled, section headers marked, form field hints added

Onboarding: all 10 views — header traits, button hints, task selection
state, progress indicator, decorative backgrounds hidden

Profile/Subscription: toggle hints, theme selection state, feature
comparison table accessibility, subscription button labels

iOS build verified: BUILD SUCCEEDED
2026-03-26 14:51:29 -05:00

166 lines
5.1 KiB
Swift

//
// TaskSummaryCard.swift
// iosApp
//
// Displays a dynamic task summary with categories from the backend.
// The backend provides icons, colors, and counts, making the UI fully data-driven.
//
import SwiftUI
import ComposeApp
/// Displays a task summary with dynamic categories from the backend
struct TaskSummaryCard: View {
let taskSummary: ResidenceTaskSummary
var visibleCategories: [String]? = nil
private var filteredCategories: [TaskCategorySummary] {
if let visible = visibleCategories {
return taskSummary.categories.filter { visible.contains($0.name) }
}
return taskSummary.categories
}
var body: some View {
VStack(alignment: .leading, spacing: 12) {
Text("Tasks")
.font(.headline)
.fontWeight(.bold)
.foregroundColor(Color.appTextPrimary)
.accessibilityAddTraits(.isHeader)
ForEach(filteredCategories, id: \.name) { category in
TaskCategoryRow(category: category)
}
}
.padding(16)
.background(Color.appBackgroundSecondary)
.cornerRadius(12)
.shadow(color: Color.black.opacity(0.1), radius: 4, x: 0, y: 2)
}
}
/// Displays a single task category with icon, name, and count
struct TaskCategoryRow: View {
let category: TaskCategorySummary
private var categoryColor: Color {
Color(hex: category.color) ?? .gray
}
var body: some View {
HStack(spacing: 12) {
// Icon with colored background
ZStack {
Circle()
.fill(categoryColor)
.frame(width: 32, height: 32)
Image(systemName: category.icons.ios)
.foregroundColor(Color.appTextOnPrimary)
.font(.system(size: 14, weight: .semibold))
}
// Category name
Text(category.displayName)
.font(.body)
.foregroundColor(Color.appTextPrimary)
Spacer()
// Count badge
Text("\(category.count)")
.font(.subheadline)
.fontWeight(.bold)
.foregroundColor(Color.appTextOnPrimary)
.padding(.horizontal, 12)
.padding(.vertical, 4)
.background(categoryColor)
.cornerRadius(12)
}
.padding(12)
.background(categoryColor.opacity(0.1))
.cornerRadius(8)
.accessibilityElement(children: .combine)
}
}
// MARK: - Preview
#if DEBUG
struct TaskSummaryCard_Previews: PreviewProvider {
static var previews: some View {
VStack(spacing: 20) {
// Preview with all categories
TaskSummaryCard(taskSummary: mockTaskSummary)
.padding()
// Preview with filtered categories
TaskSummaryCard(
taskSummary: mockTaskSummary,
visibleCategories: ["overdue_tasks", "current_tasks", "in_progress_tasks"]
)
.padding()
}
.background(Color(.systemGroupedBackground))
}
static var mockTaskSummary: ResidenceTaskSummary {
ResidenceTaskSummary(
categories: [
TaskCategorySummary(
name: "overdue_tasks",
displayName: "Overdue",
icons: TaskCategoryIcons(
android: "Warning",
ios: "exclamationmark.triangle"
),
color: "#FF3B30",
count: 3
),
TaskCategorySummary(
name: "current_tasks",
displayName: "Current",
icons: TaskCategoryIcons(
android: "CalendarToday",
ios: "calendar"
),
color: "#007AFF",
count: 8
),
TaskCategorySummary(
name: "in_progress_tasks",
displayName: "In Progress",
icons: TaskCategoryIcons(
android: "PlayCircle",
ios: "play.circle"
),
color: "#FF9500",
count: 2
),
TaskCategorySummary(
name: "backlog_tasks",
displayName: "Backlog",
icons: TaskCategoryIcons(
android: "Inbox",
ios: "tray"
),
color: "#5856D6",
count: 7
),
TaskCategorySummary(
name: "done_tasks",
displayName: "Done",
icons: TaskCategoryIcons(
android: "CheckCircle",
ios: "checkmark.circle"
),
color: "#34C759",
count: 5
)
]
)
}
}
#endif