import SwiftUI import SwiftData struct TaskTrackerView: View { @Environment(\.modelContext) private var modelContext @State private var viewModel = TaskViewModel() @State private var showAddSheet = false var body: some View { ZStack { Theme.background.ignoresSafeArea() Group { if viewModel.tasks.isEmpty { emptyState } else { taskList } } } .navigationTitle(String(localized: "tab.tasks")) .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button { showAddSheet = true } label: { Image(systemName: "plus") .foregroundStyle(Theme.green) } } } .sheet(isPresented: $showAddSheet) { AddTaskSheet { name, icon, type, category in viewModel.addTask(name: name, icon: icon, type: type, category: category) } } .onAppear { viewModel.setup(context: modelContext) } } private var emptyState: some View { VStack(spacing: 16) { Image(systemName: "checkmark.circle.badge.plus") .font(.system(size: 56)) .foregroundStyle(Theme.green.opacity(0.6)) Text(String(localized: "task.empty")) .foregroundStyle(Theme.primaryText.opacity(0.6)) } } private var taskList: some View { List { ForEach(viewModel.tasks) { task in TaskRow(task: task, viewModel: viewModel) .listRowBackground(Theme.surface) .swipeActions(edge: .trailing, allowsFullSwipe: false) { Button(role: .destructive) { viewModel.deleteTask(task) } label: { Label(String(localized: "task.delete"), systemImage: "trash") } } .swipeActions(edge: .leading, allowsFullSwipe: true) { if task.type == .count { Button { viewModel.addCount(to: task) } label: { Label(String(localized: "task.add.count"), systemImage: "plus") } .tint(Theme.green) } } } } .listStyle(.plain) .scrollContentBackground(.hidden) } } // MARK: - Task Row private struct TaskRow: View { let task: TaskItem let viewModel: TaskViewModel private var isRunning: Bool { viewModel.isTimerRunning(for: task.id) } var body: some View { HStack(spacing: 12) { Text(task.icon) .font(.title2) .frame(width: 36) VStack(alignment: .leading, spacing: 2) { Text(task.name) .font(.body) .fontWeight(.medium) .foregroundStyle(Theme.primaryText) if let category = task.category { Text(category.name) .font(.caption2) .foregroundStyle(Theme.green) } } Spacer() if task.type == .time { timeControls } else { countBadge } } .padding(.vertical, 4) } private var timeControls: some View { HStack(spacing: 8) { if isRunning { Text(viewModel.formattedElapsed(for: task.id)) .font(.system(.subheadline, design: .monospaced)) .foregroundStyle(Theme.green) .contentTransition(.numericText()) .animation(.linear(duration: 0.3), value: viewModel.elapsedSeconds[task.id]) } Button { viewModel.toggleTimer(for: task) } label: { Image(systemName: isRunning ? "stop.fill" : "play.fill") .font(.title3) .foregroundStyle(isRunning ? Theme.yellow : Theme.green) .frame(width: 36, height: 36) .background( Circle() .fill(isRunning ? Theme.yellow.opacity(0.15) : Theme.green.opacity(0.15)) ) } .buttonStyle(.plain) } } private var countBadge: some View { let count = viewModel.todayCount(for: task) return Text("×\(count)") .font(.headline) .monospacedDigit() .foregroundStyle(count > 0 ? Theme.green : Theme.primaryText.opacity(0.3)) } } // MARK: - Add Task Sheet private struct AddTaskSheet: View { @Environment(\.dismiss) private var dismiss @Query private var categories: [Category] @State private var name = "" @State private var icon = "⭐" @State private var type: TaskType = .count @State private var selectedCategory: Category? let onAdd: (String, String, TaskType, Category?) -> Void var body: some View { NavigationStack { Form { Section { TextField(String(localized: "task.name"), text: $name) TextField(String(localized: "task.icon"), text: $icon) } Section { Picker(String(localized: "task.type"), selection: $type) { Text(String(localized: "task.type.count")).tag(TaskType.count) Text(String(localized: "task.type.time")).tag(TaskType.time) } .pickerStyle(.segmented) } if !categories.isEmpty { Section { Picker(String(localized: "task.category"), selection: $selectedCategory) { Text(String(localized: "task.category.none")).tag(Optional.none) ForEach(categories) { cat in Text(cat.name).tag(Optional(cat)) } } } } } .navigationTitle(String(localized: "task.add")) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .cancellationAction) { Button(String(localized: "button.cancel")) { dismiss() } } ToolbarItem(placement: .confirmationAction) { Button(String(localized: "button.add")) { let trimmed = name.trimmingCharacters(in: .whitespaces) guard !trimmed.isEmpty else { return } let resolvedIcon = icon.trimmingCharacters(in: .whitespaces).isEmpty ? "⭐" : icon onAdd(trimmed, resolvedIcon, type, selectedCategory) dismiss() } .disabled(name.trimmingCharacters(in: .whitespaces).isEmpty) } } } } } #Preview { TaskTrackerView() }