Files
honeyDueKMP/iosApp/iosApp/Subviews/Task/DynamicTaskCard.swift
Trey t 77a118a6f7 Refactor iOS and Android views into separate files
Organized view components by extracting composables and views into separate files following single responsibility principle.

iOS Changes:
- Split MainTabView → extracted ProfileTabView
- Split CompleteTaskView → extracted ImageThumbnailView, CameraPickerView
- Split ManageUsersView → extracted ShareCodeCard, UserListItem
- Consolidated task action buttons into single TaskActionButtons.swift file
- Split HomeScreenView → extracted OverviewCard, StatView, HomeNavigationCard
- Split AllTasksView → extracted DynamicTaskColumnView, DynamicTaskCard
- Split ContentView → extracted ComposeView, CustomView

Android Changes:
- Split ResetPasswordScreen → extracted RequirementItem component
- Split TasksScreen → extracted TaskPill component
- Created TaskDisplayUtils for shared helper functions (getIconFromName, hexToColor)

All extracted components properly organized in:
- iOS: Subviews/Common, Subviews/Task, Subviews/Residence, Profile
- Android: ui/components/auth, ui/components/task, ui/utils

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-10 11:38:17 -06:00

160 lines
5.0 KiB
Swift

import SwiftUI
import ComposeApp
/// Task card that dynamically renders buttons based on the column's button types
struct DynamicTaskCard: View {
let task: TaskDetail
let buttonTypes: [String]
let onEdit: () -> Void
let onCancel: () -> Void
let onUncancel: () -> Void
let onMarkInProgress: () -> Void
let onComplete: () -> Void
let onArchive: () -> Void
let onUnarchive: () -> Void
var body: some View {
VStack(alignment: .leading, spacing: 12) {
HStack {
VStack(alignment: .leading, spacing: 4) {
Text(task.title)
.font(.headline)
.foregroundColor(.primary)
if let status = task.status {
StatusBadge(status: status.name)
}
}
Spacer()
PriorityBadge(priority: task.priority.name)
}
if let description = task.description_, !description.isEmpty {
Text(description)
.font(.subheadline)
.foregroundColor(.secondary)
.lineLimit(2)
}
HStack {
Label(task.frequency.displayName, systemImage: "repeat")
.font(.caption)
.foregroundColor(.secondary)
Spacer()
if let due_date = task.dueDate {
Label(formatDate(due_date), systemImage: "calendar")
.font(.caption)
.foregroundColor(.secondary)
}
}
if task.completions.count > 0 {
Divider()
VStack(alignment: .leading, spacing: 8) {
HStack {
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.green)
Text("Completions (\(task.completions.count))")
.font(.caption)
.fontWeight(.semibold)
}
ForEach(task.completions, id: \.id) { completion in
CompletionCardView(completion: completion)
}
}
}
// Render buttons based on buttonTypes array
VStack(spacing: 8) {
ForEach(Array(buttonTypes.enumerated()), id: \.offset) { index, buttonType in
renderButton(for: buttonType)
}
}
}
.padding(16)
.background(Color(.systemBackground))
.cornerRadius(12)
.shadow(color: Color.black.opacity(0.05), radius: 3, x: 0, y: 2)
}
private func formatDate(_ dateString: String) -> String {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
if let date = formatter.date(from: dateString) {
formatter.dateStyle = .medium
return formatter.string(from: date)
}
return dateString
}
@ViewBuilder
private func renderButton(for buttonType: String) -> some View {
switch buttonType {
case "mark_in_progress":
MarkInProgressButton(
taskId: task.id,
onCompletion: onMarkInProgress,
onError: { error in
print("Error marking in progress: \(error)")
}
)
case "complete":
CompleteTaskButton(
taskId: task.id,
onCompletion: onComplete,
onError: { error in
print("Error completing task: \(error)")
}
)
case "edit":
EditTaskButton(
taskId: task.id,
onCompletion: onEdit,
onError: { error in
print("Error editing task: \(error)")
}
)
case "cancel":
CancelTaskButton(
taskId: task.id,
onCompletion: onCancel,
onError: { error in
print("Error cancelling task: \(error)")
}
)
case "uncancel":
UncancelTaskButton(
taskId: task.id,
onCompletion: onUncancel,
onError: { error in
print("Error restoring task: \(error)")
}
)
case "archive":
ArchiveTaskButton(
taskId: task.id,
onCompletion: onArchive,
onError: { error in
print("Error archiving task: \(error)")
}
)
case "unarchive":
UnarchiveTaskButton(
taskId: task.id,
onCompletion: onUnarchive,
onError: { error in
print("Error unarchiving task: \(error)")
}
)
default:
EmptyView()
}
}
}