You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
90 lines
3.8 KiB
90 lines
3.8 KiB
import SwiftUI
|
|
|
|
struct HistoryRow: Identifiable {
|
|
let entry: Entry
|
|
let task: String
|
|
let person: String
|
|
let partner: String
|
|
var id: String { entry.id }
|
|
var date: String { entry.date }
|
|
var note: String { entry.note }
|
|
}
|
|
|
|
struct HistoryView: View {
|
|
@EnvironmentObject var store: Store
|
|
@EnvironmentObject var ui: UIState
|
|
@State private var query = ""
|
|
@State private var taskFilter = ""
|
|
@State private var selection = Set<String>()
|
|
@State private var sortOrder = [KeyPathComparator(\HistoryRow.date, order: .reverse)]
|
|
|
|
private var rows: [HistoryRow] {
|
|
let q = query.lowercased()
|
|
return store.db.entries
|
|
.filter { taskFilter.isEmpty || $0.task == taskFilter }
|
|
.map { e in
|
|
HistoryRow(entry: e, task: store.task(e.task)?.name ?? e.task, person: store.name(e.person),
|
|
partner: e.partner.isEmpty ? "" : store.name(e.partner))
|
|
}
|
|
.filter { q.isEmpty || "\($0.task) \($0.person) \($0.partner) \($0.note)".lowercased().contains(q) }
|
|
.sorted(using: sortOrder)
|
|
}
|
|
|
|
var body: some View {
|
|
let rows = rows
|
|
VStack(spacing: 0) {
|
|
HStack(spacing: 12) {
|
|
TextField("Suchen (Name, Notiz …)", text: $query)
|
|
.textFieldStyle(.roundedBorder)
|
|
.frame(width: 260)
|
|
Picker("Aufgabe", selection: $taskFilter) {
|
|
Text("Alle Aufgaben").tag("")
|
|
ForEach(store.db.tasks.filter { !$0.partnerColumn }) { Text($0.name).tag($0.id) }
|
|
}
|
|
.frame(width: 280)
|
|
Text("\(rows.count) Einträge").foregroundStyle(Theme.muted)
|
|
Spacer()
|
|
Button("Bearbeiten") { edit(selection.first) }.disabled(selection.count != 1)
|
|
Button("Löschen", role: .destructive) { delete(selection) }.disabled(selection.isEmpty)
|
|
}
|
|
.padding(16)
|
|
|
|
Table(rows, selection: $selection, sortOrder: $sortOrder) {
|
|
TableColumn("Datum", value: \.date) { Text(Day.display($0.date)).monospacedDigit() }.width(90)
|
|
TableColumn("Aufgabe", value: \.task).width(min: 160, ideal: 220)
|
|
TableColumn("Person", value: \.person).width(min: 140, ideal: 200)
|
|
TableColumn("Partner", value: \.partner).width(min: 120, ideal: 180)
|
|
TableColumn("Thema / Notiz", value: \.note) { Text($0.note).foregroundStyle(Theme.muted) }
|
|
}
|
|
.contextMenu(forSelectionType: String.self) { ids in
|
|
if ids.count == 1 { Button("Bearbeiten") { edit(ids.first) } }
|
|
Button("Löschen", role: .destructive) { delete(ids) }
|
|
} primaryAction: { ids in edit(ids.first) }
|
|
.onDeleteCommand { delete(selection) }
|
|
.overlay {
|
|
if store.db.entries.isEmpty {
|
|
Text("Noch keine Einträge. Mit ⌘N oder einem Klick in die Übersicht geht's los.")
|
|
.foregroundStyle(Theme.muted)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private func edit(_ id: String?) {
|
|
guard let id, let e = store.db.entries.first(where: { $0.id == id }) else { return }
|
|
ui.draft = e
|
|
}
|
|
|
|
private func delete(_ ids: Set<String>) {
|
|
guard !ids.isEmpty else { return }
|
|
let alert = NSAlert()
|
|
alert.messageText = ids.count == 1 ? "Eintrag löschen?" : "\(ids.count) Einträge löschen?"
|
|
alert.informativeText = "Das lässt sich nicht rückgängig machen."
|
|
alert.addButton(withTitle: "Löschen").hasDestructiveAction = true
|
|
alert.addButton(withTitle: "Abbrechen")
|
|
guard alert.runModal() == .alertFirstButtonReturn else { return }
|
|
ids.forEach(store.delete(entry:))
|
|
selection.subtract(ids)
|
|
}
|
|
}
|