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>
160 lines
5.0 KiB
Swift
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()
|
|
}
|
|
}
|
|
}
|