Files
MindDump/MindDump/Views/ListDetailView.swift

189 lines
7.2 KiB
Swift

import SwiftUI
struct ListDetailView: View {
@Environment(ListStore.self) private var store
let listID: UUID
@State private var editorItem: TodoItem?
@State private var moveItem: TodoItem?
@State private var showCompleted = true
@State private var filterUrgent = false
@State private var filterDue = false
@State private var selectedSort: ListDetailSort = .oldest
private var todoList: TodoList? {
store.lists.first { $0.id == listID }
}
var body: some View {
Group {
if let todoList {
if todoList.items.isEmpty {
ContentUnavailableView("Keine Einträge", systemImage: "tray")
} else {
let filteredItems = store.filteredOpenItems(
for: todoList, urgent: filterUrgent, due: filterDue, sort: selectedSort
)
List {
Section {
HStack(spacing: 12) {
chipButton("Dringend", icon: "exclamationmark.triangle", isActive: filterUrgent, color: .orange) {
withAnimation { filterUrgent.toggle() }
}
chipButton("Fällig", icon: "calendar.badge.clock", isActive: filterDue, color: .red) {
withAnimation { filterDue.toggle() }
}
sortMenu
}
.frame(maxWidth: .infinity)
.listRowInsets(EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16))
.listRowBackground(Color.clear)
}
Section {
if filteredItems.isEmpty {
emptyState
} else {
TodoListView(
items: filteredItems,
onToggle: { item in
store.toggleItemCompleted(item.id, in: listID)
},
onTap: { item in
editorItem = item
},
onDelete: { item in
store.deleteItem(item.id, from: listID)
},
onMove: { item in
moveItem = item
}
)
}
}
if !todoList.completedItems.isEmpty {
Section {
if showCompleted {
TodoListView(
items: todoList.completedItems,
onToggle: { item in
store.toggleItemCompleted(item.id, in: listID)
},
onTap: { item in
editorItem = item
},
onDelete: { item in
store.deleteItem(item.id, from: listID)
},
onMove: { item in
moveItem = item
}
)
}
} header: {
Button {
withAnimation {
showCompleted.toggle()
}
} label: {
HStack {
Text("Erledigt (\(todoList.completedItems.count))")
Spacer()
Image(systemName: showCompleted ? "chevron.down" : "chevron.right")
}
}
}
}
}
}
}
}
.navigationTitle(todoList?.name ?? "")
.sheet(item: $editorItem) { item in
TodoEditorView(listID: listID, item: item)
}
.sheet(item: $moveItem) { item in
MoveToListView(itemID: item.id, currentListID: listID)
}
}
// MARK: - Filter Chip
@ViewBuilder
private func chipButton(_ label: String, icon: String, isActive: Bool, color: Color, action: @escaping () -> Void) -> some View {
Button(action: action) {
Image(systemName: icon)
.font(.subheadline.weight(.medium))
.padding(.horizontal, 12)
.padding(.vertical, 6)
.background(isActive ? color.opacity(0.15) : Color(.systemGray6))
.foregroundStyle(isActive ? color : .secondary)
.clipShape(Capsule())
}
.buttonStyle(.plain)
}
// MARK: - Sort Menu
private var sortMenu: some View {
Menu {
Picker("Sortierung", selection: $selectedSort) {
ForEach(ListDetailSort.allCases, id: \.self) { sort in
Text(sort.label).tag(sort)
}
}
} label: {
HStack(spacing: 4) {
Image(systemName: "arrow.up.arrow.down")
ZStack(alignment: .leading) {
ForEach(ListDetailSort.allCases, id: \.self) { sort in
Text(sort.shortLabel)
.opacity(sort == selectedSort ? 1 : 0)
}
}
}
.font(.subheadline)
.foregroundStyle(.secondary)
}
}
// MARK: - Empty State
private var emptyState: some View {
VStack(spacing: 8) {
Image(systemName: emptyIcon)
.font(.title2)
.foregroundStyle(.secondary)
Text(emptyMessage)
.font(.subheadline)
.foregroundStyle(.secondary)
}
.frame(maxWidth: .infinity)
.padding(.vertical, 24)
.listRowBackground(Color.clear)
}
private var emptyMessage: String {
if filterUrgent && filterDue {
"Keine dringenden oder fälligen Aufgaben"
} else if filterUrgent {
"Keine dringenden Aufgaben"
} else if filterDue {
"Keine fälligen Aufgaben"
} else {
"Keine offenen Aufgaben"
}
}
private var emptyIcon: String {
if filterUrgent {
"exclamationmark.triangle"
} else if filterDue {
"calendar.badge.checkmark"
} else {
"checkmark.circle"
}
}
}