From 5833227865aa870caeeb70015fbffc94738a1e85 Mon Sep 17 00:00:00 2001 From: Ryan Priebe Date: Mon, 14 Oct 2024 21:51:19 -0600 Subject: [PATCH 1/3] rounds the edges of tabbed interfaces --- KWCore/Sources/Query/CoreDataTasks.swift | 13 +++ KlockWork.xcodeproj/project.pbxproj | 4 + KlockWork/Views/Planning/Planning.swift | 11 ++- .../Planning/Tabs/Planning.NoDueDate.swift | 97 +++++++++++++++++++ 4 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 KlockWork/Views/Planning/Tabs/Planning.NoDueDate.swift diff --git a/KWCore/Sources/Query/CoreDataTasks.swift b/KWCore/Sources/Query/CoreDataTasks.swift index 52dfa6bb..bc98f0fc 100644 --- a/KWCore/Sources/Query/CoreDataTasks.swift +++ b/KWCore/Sources/Query/CoreDataTasks.swift @@ -327,6 +327,19 @@ public class CoreDataTasks { return query(predicate, sort) } + /// Find tasks that have no due date + /// - Returns: Array + public func noDueDate() -> [LogTask] { + let predicate = NSPredicate( + format: "due == nil && (completedDate == nil && cancelledDate == nil && owner.project.company.hidden == false)" + ) + let sort = [ + NSSortDescriptor(keyPath: \LogTask.owner?.title, ascending: true) + ] + + return query(predicate, sort) + } + /// Find upcoming tasks /// - Returns: Array public func upcoming(_ date: Date = Date()) -> [LogTask] { diff --git a/KlockWork.xcodeproj/project.pbxproj b/KlockWork.xcodeproj/project.pbxproj index e686e0a1..818410a5 100644 --- a/KlockWork.xcodeproj/project.pbxproj +++ b/KlockWork.xcodeproj/project.pbxproj @@ -112,6 +112,7 @@ 5360429D2CBA35050030D72D /* RecordDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5360429C2CBA35020030D72D /* RecordDetail.swift */; }; 536043CA2CBE06A60030D72D /* LogTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536043C92CBE06A40030D72D /* LogTask.swift */; }; 536043CC2CBE0E6C0030D72D /* NotificationSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536043CB2CBE0E670030D72D /* NotificationSettings.swift */; }; + 536044332CBE1B1F0030D72D /* Planning.NoDueDate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536044322CBE1B170030D72D /* Planning.NoDueDate.swift */; }; 5363B6782A6BB2CC00C2FBB8 /* CompanyDashboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5363B6772A6BB2CC00C2FBB8 /* CompanyDashboard.swift */; }; 5363B67A2A6BB75F00C2FBB8 /* CompanyBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5363B6792A6BB75F00C2FBB8 /* CompanyBlock.swift */; }; 5363B67C2A6BB78900C2FBB8 /* CompanyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5363B67B2A6BB78900C2FBB8 /* CompanyView.swift */; }; @@ -410,6 +411,7 @@ 5360429C2CBA35020030D72D /* RecordDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordDetail.swift; sourceTree = ""; }; 536043C92CBE06A40030D72D /* LogTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogTask.swift; sourceTree = ""; }; 536043CB2CBE0E670030D72D /* NotificationSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettings.swift; sourceTree = ""; }; + 536044322CBE1B170030D72D /* Planning.NoDueDate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Planning.NoDueDate.swift; sourceTree = ""; }; 5363B6772A6BB2CC00C2FBB8 /* CompanyDashboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompanyDashboard.swift; sourceTree = ""; }; 5363B6792A6BB75F00C2FBB8 /* CompanyBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompanyBlock.swift; sourceTree = ""; }; 5363B67B2A6BB78900C2FBB8 /* CompanyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompanyView.swift; sourceTree = ""; }; @@ -865,6 +867,7 @@ 5354C8F42AA045C2001C1779 /* Tabs */ = { isa = PBXGroup; children = ( + 536044322CBE1B170030D72D /* Planning.NoDueDate.swift */, 533544812CB19C450067A944 /* Planning.Overdue.swift */, 5335447F2CB19C3F0067A944 /* Planning.Upcoming.swift */, 5354C8F52AA045CA001C1779 /* Planning.Today.swift */, @@ -1759,6 +1762,7 @@ 535DDE5E2A7982F3008350D4 /* SidebarItem.swift in Sources */, 53E202702A80984400B4DF70 /* DateSelectorWidget.swift in Sources */, 53EDDFA02963487A008D34C7 /* CustomPickerItem.swift in Sources */, + 536044332CBE1B1F0030D72D /* Planning.NoDueDate.swift in Sources */, 53E2026C2A80457400B4DF70 /* JobProjectGroup.swift in Sources */, 535C1AE329F48F6800CD95FD /* Widgets.swift in Sources */, 53C000942B38E5C500D5EC04 /* TodayViewTab.swift in Sources */, diff --git a/KlockWork/Views/Planning/Planning.swift b/KlockWork/Views/Planning/Planning.swift index df364cba..d7e0946b 100644 --- a/KlockWork/Views/Planning/Planning.swift +++ b/KlockWork/Views/Planning/Planning.swift @@ -33,17 +33,24 @@ struct Planning: View { // ) ToolbarButton( id: 1, - helpText: "Upcoming", + helpText: "Incomplete tasks", icon: "hourglass", labelText: "Upcoming", contents: AnyView(Planning.Upcoming()) ), ToolbarButton( id: 2, - helpText: "Overdue", + helpText: "Overdue tasks", icon: "alarm", labelText: "Overdue", contents: AnyView(Planning.Overdue()) + ), + ToolbarButton( + id: 3, + helpText: "Tasks that have no due date", + icon: "exclamationmark.triangle", + labelText: "No Due Date", + contents: AnyView(Planning.NoDueDate()) ) ] diff --git a/KlockWork/Views/Planning/Tabs/Planning.NoDueDate.swift b/KlockWork/Views/Planning/Tabs/Planning.NoDueDate.swift new file mode 100644 index 00000000..929e89bd --- /dev/null +++ b/KlockWork/Views/Planning/Tabs/Planning.NoDueDate.swift @@ -0,0 +1,97 @@ +// +// Planning.NoDueDate.swift +// KlockWork +// +// Created by Ryan Priebe on 2024-10-14. +// Copyright © 2024 YegCollective. All rights reserved. +// + +import SwiftUI +import KWCore + +extension Planning { + struct NoDueDate: View { + @EnvironmentObject public var nav: Navigation + @State private var tasks: [LogTask] = [] + @State private var overdue: [UpcomingRow] = [] + @State private var id: UUID = UUID() + private let page: PageConfiguration.AppPage = .planning + + var body: some View { + VStack(alignment: .leading, spacing: 1) { + if !self.overdue.isEmpty { + ForEach(self.overdue, id: \.id) { row in + Section { + ForEach(row.tasks) { task in + TaskItem(task: task, callback: self.actionTaskActionTap) + } + } header: { + Timestamp(text: "\(row.tasks.count) on \(row.date)", fullWidth: true, alignment: .leading, clear: true) + .background(Theme.base.opacity(0.6)) + } + } + } else { + VStack(alignment: .center) { + HStack(alignment: .center) { + Text("All tasks have due dates, well done.") + .foregroundColor(.gray) + Spacer() + } + } + + .padding() + .background(Theme.rowColour) + } + + Spacer() + } + .id(self.id) + .onAppear(perform: self.actionOnAppear) + } + } +} + +extension Planning.NoDueDate { + /// Fires when the Forecast callback is fired + /// - Returns: Void + private func actionForecastCallback() -> Void { + self.actionTaskActionTap() + } + + /// Fires when a task is interacted with + /// - Returns: Void + private func actionTaskActionTap() -> Void { + self.tasks = [] + self.actionOnAppear() + } + + /// Onload handler + /// - Returns: Void + private func actionOnAppear() -> Void { + self.id = UUID() + self.tasks = CoreDataTasks(moc: self.nav.moc).noDueDate() + self.overdue = [] + let grouped = Dictionary(grouping: self.tasks, by: {$0.due!.formatted(date: .abbreviated, time: .omitted)}) + let sorted = Array(grouped) + .sorted(by: { + let df = DateFormatter() + df.dateStyle = .medium + df.timeStyle = .none + if let d1 = df.date(from: $0.key) { + if let d2 = df.date(from: $1.key) { + return d1 < d2 + } + } + return false + }) + + for group in sorted { + self.overdue.append( + UpcomingRow( + date: group.key, + tasks: group.value.sorted(by: {$0.due! < $1.due!}) + ) + ) + } + } +} From a4e4b3cdb52d184d02b0e79f81afecd472d6b235 Mon Sep 17 00:00:00 2001 From: Ryan Priebe Date: Mon, 14 Oct 2024 21:51:41 -0600 Subject: [PATCH 2/3] bugfix: view didn't extend across the page --- KlockWork/Views/Planning/Tabs/Planning.Overdue.swift | 1 + KlockWork/Views/Planning/Tabs/Planning.Upcoming.swift | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/KlockWork/Views/Planning/Tabs/Planning.Overdue.swift b/KlockWork/Views/Planning/Tabs/Planning.Overdue.swift index 42d3f40d..0bd7b1d3 100644 --- a/KlockWork/Views/Planning/Tabs/Planning.Overdue.swift +++ b/KlockWork/Views/Planning/Tabs/Planning.Overdue.swift @@ -35,6 +35,7 @@ extension Planning { HStack(alignment: .center) { Text("No overdue tasks!") .foregroundColor(.gray) + Spacer() } } diff --git a/KlockWork/Views/Planning/Tabs/Planning.Upcoming.swift b/KlockWork/Views/Planning/Tabs/Planning.Upcoming.swift index 79492671..183adb0b 100644 --- a/KlockWork/Views/Planning/Tabs/Planning.Upcoming.swift +++ b/KlockWork/Views/Planning/Tabs/Planning.Upcoming.swift @@ -40,8 +40,9 @@ extension Planning { } else { VStack(alignment: .center) { HStack(alignment: .center) { - Text("No upcoming due dates") + Text("No tasks with upcoming due dates") .foregroundColor(.gray) + Spacer() } } .padding() From c834d6641eb73a06172742463b9a7ceb590456df Mon Sep 17 00:00:00 2001 From: Ryan Priebe Date: Mon, 14 Oct 2024 21:54:11 -0600 Subject: [PATCH 3/3] soften tabs --- KlockWork/Views/Shared/Fancy/FancyGenericToolbar.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/KlockWork/Views/Shared/Fancy/FancyGenericToolbar.swift b/KlockWork/Views/Shared/Fancy/FancyGenericToolbar.swift index daa53630..1c3fcd59 100644 --- a/KlockWork/Views/Shared/Fancy/FancyGenericToolbar.swift +++ b/KlockWork/Views/Shared/Fancy/FancyGenericToolbar.swift @@ -100,6 +100,7 @@ struct FancyGenericToolbar: View { } } } + .clipShape(.rect(topLeadingRadius: 5, topTrailingRadius: 5)) } } } @@ -118,6 +119,7 @@ struct FancyGenericToolbar: View { ForEach(buttons, id: \ToolbarButton.id) { button in if button.id == selected && button.contents != nil { button.contents + .clipShape(.rect(bottomLeadingRadius: 5, bottomTrailingRadius: 5)) } } }