From 7738f4b52f422d38e76ea2330f2b81f780e87e71 Mon Sep 17 00:00:00 2001 From: Paul Date: Thu, 12 Feb 2026 17:47:36 +0100 Subject: [PATCH] feat: add extended fields (notes, deadline, priority) to todo editor --- MindDump/Models/Priority.swift | 23 +++++ MindDump/Models/TodoItem.swift | 19 +++- MindDump/ViewModels/ListStore.swift | 28 +++++- MindDump/Views/TodoEditorView.swift | 142 +++++++++++++++++++++++++++- MindDump/Views/TodoRowView.swift | 32 ++++++- 5 files changed, 231 insertions(+), 13 deletions(-) create mode 100644 MindDump/Models/Priority.swift diff --git a/MindDump/Models/Priority.swift b/MindDump/Models/Priority.swift new file mode 100644 index 0000000..0d06570 --- /dev/null +++ b/MindDump/Models/Priority.swift @@ -0,0 +1,23 @@ +import SwiftUI + +enum Priority: Int, CaseIterable { + case low = 0 + case medium = 1 + case high = 2 + + var label: String { + switch self { + case .low: "Niedrig" + case .medium: "Mittel" + case .high: "Hoch" + } + } + + var color: Color { + switch self { + case .low: .green + case .medium: .yellow + case .high: .red + } + } +} diff --git a/MindDump/Models/TodoItem.swift b/MindDump/Models/TodoItem.swift index df91469..df71cbf 100644 --- a/MindDump/Models/TodoItem.swift +++ b/MindDump/Models/TodoItem.swift @@ -5,11 +5,28 @@ struct TodoItem: Identifiable { var title: String var isCompleted: Bool let createdAt: Date + var notes: String? + var deadline: Date? + var priority: Priority? + var modifiedAt: Date? - init(id: UUID = UUID(), title: String, isCompleted: Bool = false, createdAt: Date = Date()) { + init( + id: UUID = UUID(), + title: String, + isCompleted: Bool = false, + createdAt: Date = Date(), + notes: String? = nil, + deadline: Date? = nil, + priority: Priority? = nil, + modifiedAt: Date? = nil + ) { self.id = id self.title = title self.isCompleted = isCompleted self.createdAt = createdAt + self.notes = notes + self.deadline = deadline + self.priority = priority + self.modifiedAt = modifiedAt } } diff --git a/MindDump/ViewModels/ListStore.swift b/MindDump/ViewModels/ListStore.swift index c4525fb..41898ab 100644 --- a/MindDump/ViewModels/ListStore.swift +++ b/MindDump/ViewModels/ListStore.swift @@ -23,9 +23,15 @@ class ListStore { lists.removeAll { $0.id == list.id } } - func addItem(to listID: UUID, title: String) { + func addItem( + to listID: UUID, + title: String, + notes: String? = nil, + deadline: Date? = nil, + priority: Priority? = nil + ) { guard let index = lists.firstIndex(where: { $0.id == listID }) else { return } - let item = TodoItem(title: title) + let item = TodoItem(title: title, notes: notes, deadline: deadline, priority: priority) lists[index].items.append(item) } @@ -34,15 +40,31 @@ class ListStore { lists[listIndex].items.removeAll { $0.id == itemID } } - func updateItem(_ itemID: UUID, in listID: UUID, title: String) { + func updateItem( + _ itemID: UUID, + in listID: UUID, + title: String, + notes: String? = nil, + deadline: Date? = nil, + priority: Priority? = nil, + isCompleted: Bool? = nil + ) { guard let listIndex = lists.firstIndex(where: { $0.id == listID }), let itemIndex = lists[listIndex].items.firstIndex(where: { $0.id == itemID }) else { return } lists[listIndex].items[itemIndex].title = title + lists[listIndex].items[itemIndex].notes = notes + lists[listIndex].items[itemIndex].deadline = deadline + lists[listIndex].items[itemIndex].priority = priority + if let isCompleted { + lists[listIndex].items[itemIndex].isCompleted = isCompleted + } + lists[listIndex].items[itemIndex].modifiedAt = Date() } func toggleItemCompleted(_ itemID: UUID, in listID: UUID) { guard let listIndex = lists.firstIndex(where: { $0.id == listID }), let itemIndex = lists[listIndex].items.firstIndex(where: { $0.id == itemID }) else { return } lists[listIndex].items[itemIndex].isCompleted.toggle() + lists[listIndex].items[itemIndex].modifiedAt = Date() } } diff --git a/MindDump/Views/TodoEditorView.swift b/MindDump/Views/TodoEditorView.swift index 1cbf7e8..a97bf98 100644 --- a/MindDump/Views/TodoEditorView.swift +++ b/MindDump/Views/TodoEditorView.swift @@ -8,15 +8,93 @@ struct TodoEditorView: View { var item: TodoItem? @State private var title: String = "" + @State private var notes: String = "" + @State private var deadline: Date = Date() + @State private var priority: Priority = .medium + + @State private var showNotes: Bool = false + @State private var showDeadline: Bool = false + @State private var showPriority: Bool = false + @FocusState private var titleFocused: Bool private var isEditing: Bool { item != nil } + private var hasHiddenFields: Bool { + !showNotes || !showDeadline || !showPriority + } + var body: some View { NavigationStack { Form { - TextField("Titel", text: $title) - .focused($titleFocused) + // Titel + Section { + TextField("Titel", text: $title) + .focused($titleFocused) + } + + // Notiz + if showNotes { + Section { + TextField("Notiz hinzufügen...", text: $notes, axis: .vertical) + .lineLimit(3...6) + } header: { + sectionHeader("Notiz", show: $showNotes) + } + } + + // Deadline + if showDeadline { + Section { + DatePicker("Fällig am", selection: $deadline, displayedComponents: [.date, .hourAndMinute]) + } header: { + sectionHeader("Deadline", show: $showDeadline) + } + } + + // Prioritaet + if showPriority { + Section { + Picker("Priorität", selection: $priority) { + ForEach(Priority.allCases, id: \.self) { p in + Label(p.label, systemImage: "circle.fill") + .foregroundStyle(p.color) + .tag(p) + } + } + } header: { + sectionHeader("Priorität", show: $showPriority) + } + } + + // Hinzufuegen + if hasHiddenFields { + Section { + if !showNotes { + addFieldButton("Notiz", systemImage: "note.text") { showNotes = true } + } + if !showDeadline { + addFieldButton("Deadline", systemImage: "calendar") { showDeadline = true } + } + if !showPriority { + addFieldButton("Priorität", systemImage: "flag") { showPriority = true } + } + } header: { + Text("Hinzufügen") + } + } + + // Meta + if isEditing, let item { + Section { + LabeledContent("Erstellt am", value: item.createdAt.formatted(date: .abbreviated, time: .shortened)) + if let modifiedAt = item.modifiedAt { + LabeledContent("Geändert am", value: modifiedAt.formatted(date: .abbreviated, time: .shortened)) + } + } header: { + Text("Info") + } + } } .navigationTitle(isEditing ? "Bearbeiten" : "Neuer Eintrag") .navigationBarTitleDisplayMode(.inline) @@ -32,20 +110,76 @@ struct TodoEditorView: View { .onAppear { if let item { title = item.title + + if let n = item.notes, !n.isEmpty { + notes = n + showNotes = true + } + if let d = item.deadline { + deadline = d + showDeadline = true + } + if let p = item.priority { + priority = p + showPriority = true + } } titleFocused = true } } } + // Helpers + + private func sectionHeader(_ label: String, show: Binding) -> some View { + HStack { + Text(label) + Spacer() + Button { + withAnimation { show.wrappedValue = false } + } label: { + Image(systemName: "xmark.circle.fill") + .foregroundStyle(.secondary) + } + } + } + + private func addFieldButton(_ label: String, systemImage: String, action: @escaping () -> Void) -> some View { + Button { + withAnimation { action() } + } label: { + Label(label, systemImage: systemImage) + .font(.callout) + } + } + private func save() { let trimmed = title.trimmingCharacters(in: .whitespaces) guard !trimmed.isEmpty else { return } + let finalNotes: String? = showNotes && !notes.trimmingCharacters(in: .whitespaces).isEmpty + ? notes.trimmingCharacters(in: .whitespaces) + : nil + let finalDeadline: Date? = showDeadline ? deadline : nil + let finalPriority: Priority? = showPriority ? priority : nil + if let item { - store.updateItem(item.id, in: listID, title: trimmed) + store.updateItem( + item.id, + in: listID, + title: trimmed, + notes: finalNotes, + deadline: finalDeadline, + priority: finalPriority + ) } else { - store.addItem(to: listID, title: trimmed) + store.addItem( + to: listID, + title: trimmed, + notes: finalNotes, + deadline: finalDeadline, + priority: finalPriority + ) } dismiss() } diff --git a/MindDump/Views/TodoRowView.swift b/MindDump/Views/TodoRowView.swift index 2bc7b41..ca8d151 100644 --- a/MindDump/Views/TodoRowView.swift +++ b/MindDump/Views/TodoRowView.swift @@ -15,13 +15,35 @@ struct TodoRowView: View { .buttonStyle(.borderless) Button(action: { onTap?() }) { - Text(item.title) - .strikethrough(item.isCompleted) - .foregroundStyle(item.isCompleted ? .secondary : .primary) - .frame(maxWidth: .infinity, alignment: .leading) - .contentShape(Rectangle()) + VStack(alignment: .leading, spacing: 2) { + Text(item.title) + .strikethrough(item.isCompleted) + .foregroundStyle(item.isCompleted ? .secondary : .primary) + + if let notes = item.notes, !notes.isEmpty { + Text(noteSnippet(notes)) + .font(.caption) + .foregroundStyle(.secondary) + .lineLimit(1) + } + } + .frame(maxWidth: .infinity, alignment: .leading) + .contentShape(Rectangle()) } .buttonStyle(.plain) + + if let priority = item.priority { + Image(systemName: "circle.fill") + .font(.system(size: 8)) + .foregroundStyle(priority.color) + } } } + + private func noteSnippet(_ text: String) -> String { + if text.count > 50 { + return String(text.prefix(50)) + "..." + } + return text + } }