From f8d2bccd532b95990638e21d848efa0510c66e75 Mon Sep 17 00:00:00 2001 From: Catherine Zhang <137839789+czhang771@users.noreply.github.com> Date: Fri, 1 Mar 2024 14:00:05 -0500 Subject: [PATCH] Added Onboarding Survey on Dashboard (#38) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Added Onboarding Survey to Dashboard ## :recycle: Current situation & Problem Made the onboarding survey optional during onboarding process and appear on the dashboard if not completed. ## :gear: Release Notes - created new screen for the onboarding questionnaire that links to the questionnaire view - added questionnaire to dashboard if not completed - added sliders to questionnaire format Screenshot 2024-02-28 at 7 19 34 pm Screenshot 2024-02-28 at 7 20 29 pm Screenshot 2024-02-28 at 7 21 05 pm ## :books: Documentation *Please ensure that you properly document any additions in conformance to [Spezi Documentation Guide](https://github.com/StanfordSpezi/.github/blob/main/DOCUMENTATIONGUIDE.md).* Used @AppStorage to keep track of if the survey was completed and used sheets on both the onboarding and dashboard page to display questionnaire. ## :white_check_mark: Testing ## :pencil: Code of Conduct & Contributing Guidelines By submitting creating this pull request, you agree to follow our [Code of Conduct](https://github.com/CS342/.github/blob/main/CODE_OF_CONDUCT.md) and [Contributing Guidelines](https://github.com/CS342/.github/blob/main/CONTRIBUTING.md): - [x] I agree to follow the [Code of Conduct](https://github.com/CS342/.github/blob/main/CODE_OF_CONDUCT.md) and [Contributing Guidelines](https://github.com/CS342/.github/blob/main/CONTRIBUTING.md). --- PICS.xcodeproj/project.pbxproj | 62 ++++---- PICS/Onboarding/AccountQuestionnaire.swift | 73 ++++++++++ PICS/Onboarding/OnboardingFlow.swift | 2 +- ...re.swift => OnboardingQuestionnaire.swift} | 13 +- PICS/Resources/Localizable.xcstrings | 137 +++++++++++++----- PICS/Resources/Onboarding-Questionnaire.json | 2 +- .../OnboardingQuestionnaireDashboard.swift | 36 +++++ PICS/Schedule/OnboardingSurveyView.swift | 75 ++++++++++ PICS/Schedule/ScheduleView.swift | 55 +++---- 9 files changed, 360 insertions(+), 95 deletions(-) create mode 100644 PICS/Onboarding/AccountQuestionnaire.swift rename PICS/Onboarding/{Onboarding_Questionnaire.swift => OnboardingQuestionnaire.swift} (60%) create mode 100644 PICS/Schedule/OnboardingQuestionnaireDashboard.swift create mode 100644 PICS/Schedule/OnboardingSurveyView.swift diff --git a/PICS.xcodeproj/project.pbxproj b/PICS.xcodeproj/project.pbxproj index 6125653..fbb759e 100644 --- a/PICS.xcodeproj/project.pbxproj +++ b/PICS.xcodeproj/project.pbxproj @@ -7,8 +7,14 @@ objects = { /* Begin PBXBuildFile section */ - 22306A2A2B7C4E8E000A8EC1 /* Onboarding_Questionnaire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22306A292B7C4E8E000A8EC1 /* Onboarding_Questionnaire.swift */; }; - 22306A2E2B8581C3000A8EC1 /* Onboarding-Questionnaire.json in Resources */ = {isa = PBXBuildFile; fileRef = 22306A2D2B8581C3000A8EC1 /* Onboarding-Questionnaire.json */; }; + 22201EE82B9259B800E38F47 /* EQ5D5L.json in Resources */ = {isa = PBXBuildFile; fileRef = 22201EE52B9259B800E38F47 /* EQ5D5L.json */; }; + 22201EE92B9259B800E38F47 /* PHQ-4.json in Resources */ = {isa = PBXBuildFile; fileRef = 22201EE62B9259B800E38F47 /* PHQ-4.json */; }; + 22201EEA2B9259B800E38F47 /* Self-MNA.json in Resources */ = {isa = PBXBuildFile; fileRef = 22201EE72B9259B800E38F47 /* Self-MNA.json */; }; + 22306A2A2B7C4E8E000A8EC1 /* OnboardingQuestionnaire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22306A292B7C4E8E000A8EC1 /* OnboardingQuestionnaire.swift */; }; + 22306A3E2B8F0097000A8EC1 /* OnboardingSurveyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22306A3D2B8F0097000A8EC1 /* OnboardingSurveyView.swift */; }; + 22306A412B8F20C4000A8EC1 /* AccountQuestionnaire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22306A402B8F20C4000A8EC1 /* AccountQuestionnaire.swift */; }; + 22306A462B8FB173000A8EC1 /* OnboardingQuestionnaireDashboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22306A452B8FB173000A8EC1 /* OnboardingQuestionnaireDashboard.swift */; }; + 22365B782B902C9100C4528E /* Onboarding-Questionnaire.json in Resources */ = {isa = PBXBuildFile; fileRef = 22365B772B902C9100C4528E /* Onboarding-Questionnaire.json */; }; 226C16F02B69DF3500FBA97D /* HeightKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 226C16EF2B69DF3500FBA97D /* HeightKey.swift */; }; 226C16F22B6C820C00FBA97D /* WeightKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 226C16F12B6C820B00FBA97D /* WeightKey.swift */; }; 27FA29902A388E9B009CAC45 /* ModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27FA298F2A388E9B009CAC45 /* ModalView.swift */; }; @@ -70,13 +76,6 @@ 653A255528338800005D4D48 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 653A255428338800005D4D48 /* Assets.xcassets */; }; 653A256228338800005D4D48 /* PICSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 653A256128338800005D4D48 /* PICSTests.swift */; }; 741110F22B5767DF00DFC79E /* PROM-Questionnaire.json in Resources */ = {isa = PBXBuildFile; fileRef = 741110F12B5767DE00DFC79E /* PROM-Questionnaire.json */; }; - 748117182B74BC3E00801A9A /* EQ5D5L.json.license in Resources */ = {isa = PBXBuildFile; fileRef = 748117162B74BC3E00801A9A /* EQ5D5L.json.license */; }; - 74AE057F2B8FFF5200AB5287 /* EQ5D5L.json in Resources */ = {isa = PBXBuildFile; fileRef = 74AE057E2B8FFF5200AB5287 /* EQ5D5L.json */; }; - 74AE05812B90068100AB5287 /* PHQ-4.json in Resources */ = {isa = PBXBuildFile; fileRef = 74AE05802B90068100AB5287 /* PHQ-4.json */; }; - 74AE05842B90099900AB5287 /* Self-MNA.json in Resources */ = {isa = PBXBuildFile; fileRef = 74AE05832B90099900AB5287 /* Self-MNA.json */; }; - 74C593952B720781002D0274 /* (null) in Resources */ = {isa = PBXBuildFile; }; - 74C593972B720B5E002D0274 /* (null) in Resources */ = {isa = PBXBuildFile; }; - 74C5939A2B720F1B002D0274 /* (null) in Resources */ = {isa = PBXBuildFile; }; 86400D192B7C4B6D009FEC10 /* ApptInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86400D182B7C4B6D009FEC10 /* ApptInfo.swift */; }; 8644E6712B6C7243001218D0 /* AppointmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8644E6702B6C7243001218D0 /* AppointmentView.swift */; }; 8644E67B2B6C75B1001218D0 /* MapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8644E67A2B6C75B1001218D0 /* MapView.swift */; }; @@ -117,8 +116,14 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 22306A292B7C4E8E000A8EC1 /* Onboarding_Questionnaire.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Onboarding_Questionnaire.swift; sourceTree = ""; }; - 22306A2D2B8581C3000A8EC1 /* Onboarding-Questionnaire.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "Onboarding-Questionnaire.json"; sourceTree = ""; }; + 22201EE52B9259B800E38F47 /* EQ5D5L.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = EQ5D5L.json; sourceTree = ""; }; + 22201EE62B9259B800E38F47 /* PHQ-4.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "PHQ-4.json"; sourceTree = ""; }; + 22201EE72B9259B800E38F47 /* Self-MNA.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "Self-MNA.json"; sourceTree = ""; }; + 22306A292B7C4E8E000A8EC1 /* OnboardingQuestionnaire.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingQuestionnaire.swift; sourceTree = ""; }; + 22306A3D2B8F0097000A8EC1 /* OnboardingSurveyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingSurveyView.swift; sourceTree = ""; }; + 22306A402B8F20C4000A8EC1 /* AccountQuestionnaire.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountQuestionnaire.swift; sourceTree = ""; }; + 22306A452B8FB173000A8EC1 /* OnboardingQuestionnaireDashboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingQuestionnaireDashboard.swift; sourceTree = ""; }; + 22365B772B902C9100C4528E /* Onboarding-Questionnaire.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "Onboarding-Questionnaire.json"; sourceTree = ""; }; 226C16EF2B69DF3500FBA97D /* HeightKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeightKey.swift; sourceTree = ""; }; 226C16F12B6C820B00FBA97D /* WeightKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeightKey.swift; sourceTree = ""; }; 27FA298F2A388E9B009CAC45 /* ModalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalView.swift; sourceTree = ""; }; @@ -165,10 +170,6 @@ 653A256728338800005D4D48 /* PICSUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PICSUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 653A258928339462005D4D48 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 741110F12B5767DE00DFC79E /* PROM-Questionnaire.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "PROM-Questionnaire.json"; sourceTree = ""; }; - 748117162B74BC3E00801A9A /* EQ5D5L.json.license */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = EQ5D5L.json.license; sourceTree = ""; }; - 74AE057E2B8FFF5200AB5287 /* EQ5D5L.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = EQ5D5L.json; sourceTree = ""; }; - 74AE05802B90068100AB5287 /* PHQ-4.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "PHQ-4.json"; sourceTree = ""; }; - 74AE05832B90099900AB5287 /* Self-MNA.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "Self-MNA.json"; sourceTree = ""; }; 86400D182B7C4B6D009FEC10 /* ApptInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApptInfo.swift; sourceTree = ""; }; 8644E6702B6C7243001218D0 /* AppointmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppointmentView.swift; sourceTree = ""; }; 8644E67A2B6C75B1001218D0 /* MapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapView.swift; sourceTree = ""; }; @@ -283,7 +284,8 @@ 2FE5DCAC29EE6107004B9AB4 /* AccountOnboarding.swift */, 2FE5DC2F29EDD7CA004B9AB4 /* Consent.swift */, 2FE5DC3029EDD7CA004B9AB4 /* HealthKitPermissions.swift */, - 22306A292B7C4E8E000A8EC1 /* Onboarding_Questionnaire.swift */, + 22306A292B7C4E8E000A8EC1 /* OnboardingQuestionnaire.swift */, + 22306A402B8F20C4000A8EC1 /* AccountQuestionnaire.swift */, ); path = Onboarding; sourceTree = ""; @@ -291,11 +293,10 @@ 2FE5DC2D29EDD792004B9AB4 /* Resources */ = { isa = PBXGroup; children = ( - 74AE05832B90099900AB5287 /* Self-MNA.json */, - 74AE05802B90068100AB5287 /* PHQ-4.json */, - 74AE057E2B8FFF5200AB5287 /* EQ5D5L.json */, - 22306A2D2B8581C3000A8EC1 /* Onboarding-Questionnaire.json */, - 748117162B74BC3E00801A9A /* EQ5D5L.json.license */, + 22201EE52B9259B800E38F47 /* EQ5D5L.json */, + 22201EE62B9259B800E38F47 /* PHQ-4.json */, + 22201EE72B9259B800E38F47 /* Self-MNA.json */, + 22365B772B902C9100C4528E /* Onboarding-Questionnaire.json */, 741110F12B5767DE00DFC79E /* PROM-Questionnaire.json */, 653A255428338800005D4D48 /* Assets.xcassets */, 2FA0BFEC2ACC977500E0EF83 /* Localizable.xcstrings */, @@ -315,6 +316,8 @@ 2FE5DC4C29EDD7FA004B9AB4 /* PICSScheduler.swift */, 2FE5DC4B29EDD7FA004B9AB4 /* PICSTaskContext.swift */, 27FA298F2A388E9B009CAC45 /* ModalView.swift */, + 22306A3D2B8F0097000A8EC1 /* OnboardingSurveyView.swift */, + 22306A452B8FB173000A8EC1 /* OnboardingQuestionnaireDashboard.swift */, ); path = Schedule; sourceTree = ""; @@ -616,19 +619,15 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 22201EEA2B9259B800E38F47 /* Self-MNA.json in Resources */, 2FC3439229EE634B002D773C /* ConsentDocument.md in Resources */, 653A255528338800005D4D48 /* Assets.xcassets in Resources */, - 74C5939A2B720F1B002D0274 /* (null) in Resources */, + 22201EE92B9259B800E38F47 /* PHQ-4.json in Resources */, 2FC3439029EE6346002D773C /* SocialSupportQuestionnaire.json in Resources */, - 74AE057F2B8FFF5200AB5287 /* EQ5D5L.json in Resources */, - 748117182B74BC3E00801A9A /* EQ5D5L.json.license in Resources */, - 74C593952B720781002D0274 /* (null) in Resources */, - 22306A2E2B8581C3000A8EC1 /* Onboarding-Questionnaire.json in Resources */, + 22201EE82B9259B800E38F47 /* EQ5D5L.json in Resources */, + 22365B782B902C9100C4528E /* Onboarding-Questionnaire.json in Resources */, 2FA0BFED2ACC977500E0EF83 /* Localizable.xcstrings in Resources */, 741110F22B5767DF00DFC79E /* PROM-Questionnaire.json in Resources */, - 74C593972B720B5E002D0274 /* (null) in Resources */, - 74AE05812B90068100AB5287 /* PHQ-4.json in Resources */, - 74AE05842B90099900AB5287 /* Self-MNA.json in Resources */, 2F6025CB29BBE70F0045459E /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -680,7 +679,7 @@ 86400D192B7C4B6D009FEC10 /* ApptInfo.swift in Sources */, 2F4FC8D729EE69D300BFFE26 /* MockUpload.swift in Sources */, 2FE5DC3A29EDD7CA004B9AB4 /* Welcome.swift in Sources */, - 22306A2A2B7C4E8E000A8EC1 /* Onboarding_Questionnaire.swift in Sources */, + 22306A2A2B7C4E8E000A8EC1 /* OnboardingQuestionnaire.swift in Sources */, 2FE5DC3829EDD7CA004B9AB4 /* InterestingModules.swift in Sources */, 2FE5DC3529EDD7CA004B9AB4 /* Consent.swift in Sources */, 2FE5DC4529EDD7F2004B9AB4 /* Binding+Negate.swift in Sources */, @@ -710,14 +709,17 @@ A480C7C42B6D721A00B29A07 /* HKVisualizationItem.swift in Sources */, 56F6F2A02AB441930022FE5A /* ContributionsList.swift in Sources */, 566155292AB8447C00209B80 /* Package+LicenseType.swift in Sources */, + 22306A412B8F20C4000A8EC1 /* AccountQuestionnaire.swift in Sources */, 5680DD392AB8983D004E6D4A /* PackageCell.swift in Sources */, A480C7C02B6D5A3700B29A07 /* HKVisualization.swift in Sources */, 2F5E32BD297E05EA003432F8 /* PICSDelegate.swift in Sources */, A459932C2B906C3B00A98C95 /* ResultsViz.swift in Sources */, 2FE5DC5229EDD7FA004B9AB4 /* PICSScheduler.swift in Sources */, A9FE7AD02AA39BAB0077B045 /* AccountSheet.swift in Sources */, + 22306A462B8FB173000A8EC1 /* OnboardingQuestionnaireDashboard.swift in Sources */, 653A2551283387FE005D4D48 /* PICS.swift in Sources */, 2FE5DC3629EDD7CA004B9AB4 /* HealthKitPermissions.swift in Sources */, + 22306A3E2B8F0097000A8EC1 /* OnboardingSurveyView.swift in Sources */, 8644E6872B7204A7001218D0 /* AppointmentBlock.swift in Sources */, 5661552E2AB854C000209B80 /* PackageHelper.swift in Sources */, 27FA29902A388E9B009CAC45 /* ModalView.swift in Sources */, diff --git a/PICS/Onboarding/AccountQuestionnaire.swift b/PICS/Onboarding/AccountQuestionnaire.swift new file mode 100644 index 0000000..389ba1d --- /dev/null +++ b/PICS/Onboarding/AccountQuestionnaire.swift @@ -0,0 +1,73 @@ +// +// This source file is part of the PICS based on the Stanford Spezi Template Application project +// +// SPDX-FileCopyrightText: 2023 Stanford University +// +// SPDX-License-Identifier: MIT +// + +import SpeziHealthKit +import SpeziOnboarding +import SwiftUI + + +struct AccountQuestionnaire: View { + @Environment(OnboardingNavigationPath.self) private var onboardingNavigationPath + @AppStorage("isSurveyCompleted") var isSurveyCompleted = false + @State var isSheetPresented = false + @State private var showOnboardingQuestionnaire = false + + var body: some View { + OnboardingView( + contentView: { + VStack { + OnboardingTitleView( + title: "ONBOARDING_TITLE", + subtitle: "ONBOARDING_SUBTITLE" + ) + Spacer() + Image(systemName: "list.bullet.clipboard.fill") + .font(.system(size: 130)) + .foregroundColor(.accentColor) + .accessibilityHidden(true) + Text("ONBOARDING_INSTRUCTION") + .multilineTextAlignment(.center) + .padding(.vertical, 15) + Spacer() + } + }, actionView: { + VStack { + OnboardingActionsView( + "Take Questionnaire", + action: { + isSheetPresented = true + } + ) + .padding(.vertical, 16) + OnboardingActionsView( + "Skip", + action: { + onboardingNavigationPath.nextStep() + } + ) + .sheet(isPresented: $isSheetPresented) { + OnboardingQuestionnaire(isSheetPresented: $isSheetPresented) + } + } + } + ) + // .navigationBarBackButtonHidden(healthKitProcessing) + // Small fix as otherwise "Login" or "Sign up" is still shown in the nav bar + .navigationTitle(Text(verbatim: "")) + } +} + + +#if DEBUG +#Preview { + OnboardingStack { + AccountQuestionnaire() + } + .previewWith(standard: PICSStandard()) {} +} +#endif diff --git a/PICS/Onboarding/OnboardingFlow.swift b/PICS/Onboarding/OnboardingFlow.swift index 4eea7ab..fe59fa6 100644 --- a/PICS/Onboarding/OnboardingFlow.swift +++ b/PICS/Onboarding/OnboardingFlow.swift @@ -38,7 +38,7 @@ struct OnboardingFlow: View { if !FeatureFlags.disableFirebase { AccountOnboarding() } - OnboardingQuestionnaire() + AccountQuestionnaire() #if !(targetEnvironment(simulator) && (arch(i386) || arch(x86_64))) Consent() #endif diff --git a/PICS/Onboarding/Onboarding_Questionnaire.swift b/PICS/Onboarding/OnboardingQuestionnaire.swift similarity index 60% rename from PICS/Onboarding/Onboarding_Questionnaire.swift rename to PICS/Onboarding/OnboardingQuestionnaire.swift index 4fe1232..8b29337 100644 --- a/PICS/Onboarding/Onboarding_Questionnaire.swift +++ b/PICS/Onboarding/OnboardingQuestionnaire.swift @@ -10,21 +10,30 @@ import SpeziQuestionnaire import SwiftUI struct OnboardingQuestionnaire: View { @Environment(OnboardingNavigationPath.self) private var onboardingNavigationPath + @Environment(PICSStandard.self) private var standard + @AppStorage("isSurveyCompleted") var isSurveyCompleted = false + @Binding var isSheetPresented: Bool + var body: some View { QuestionnaireView( questionnaire: Bundle.main.questionnaire(withName: "Onboarding-Questionnaire") ) { result in guard case let .completed(response) = result else { + isSheetPresented = false + onboardingNavigationPath.nextStep() return // user cancelled } + isSurveyCompleted = true + isSheetPresented = false + await standard.add(response: response) onboardingNavigationPath.nextStep() - // ... save the FHIR response to your data store } } } #Preview { OnboardingStack { - OnboardingQuestionnaire() + OnboardingQuestionnaire(isSheetPresented: .constant(true)) } + .previewWith(standard: PICSStandard()) {} } diff --git a/PICS/Resources/Localizable.xcstrings b/PICS/Resources/Localizable.xcstrings index a39bcc9..e85e21e 100644 --- a/PICS/Resources/Localizable.xcstrings +++ b/PICS/Resources/Localizable.xcstrings @@ -12,39 +12,6 @@ }, "%lld kg" : { - }, - "ASSESSMENT_TM_START_BTN" : { - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Start Trail Making Assessment" - } - } - } - }, - "ASSESSMENTS_NAVIGATION_TITLE" : { - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Assessments" - } - } - } - }, - "ASSESSMENTS_TAB_TITLE" : { - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Assessments" - } - } - } }, "ACCOUNT_NEXT" : { "localizations" : { @@ -207,6 +174,39 @@ } } }, + "ASSESSMENT_TM_START_BTN" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Start Trail Making Assessment" + } + } + } + }, + "ASSESSMENTS_NAVIGATION_TITLE" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Assessments" + } + } + } + }, + "ASSESSMENTS_TAB_TITLE" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Assessments" + } + } + } + }, "CLOSE" : { "comment" : "MARK: General", "localizations" : { @@ -636,6 +636,9 @@ } } }, + "Key" : { + "extractionState" : "manual" + }, "LELAND_STANFORD_BIO" : { "extractionState" : "stale", "localizations" : { @@ -701,13 +704,66 @@ } } }, - "ONBOARDING_QUESTIONNAIRE" : { + "Now" : { + + }, + "Onboarding Questionnaire" : { + + }, + "Onboarding Task" : { + + }, + "ONBOARDING_CAPTION" : { "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Static information on basic profile info" + "value" : "Please complete the Onboarding questionnaire before your first appointment." + } + } + } + }, + "ONBOARDING_DESCRIPTION" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Please fill out the Onboarding Questionnaire to collect some basic information before your first appointment." + } + } + } + }, + "ONBOARDING_INSTRUCTION" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "You may skip this step during the onboarding and fill it out before your first appointment." + } + } + } + }, + "ONBOARDING_SUBTITLE" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Please fill out this onboarding survey to collect your basic information." + } + } + } + }, + "ONBOARDING_TITLE" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Onboarding Questionnaire" } } } @@ -807,6 +863,12 @@ } } } + }, + "Skip" : { + + }, + "Start Questionnaire" : { + }, "STREET_ADDRESS" : { "extractionState" : "manual", @@ -818,6 +880,9 @@ } } } + }, + "Take Questionnaire" : { + }, "TASK_CONTEXT_ACTION_QUESTIONNAIRE" : { "localizations" : { @@ -1050,4 +1115,4 @@ } }, "version" : "1.0" -} +} \ No newline at end of file diff --git a/PICS/Resources/Onboarding-Questionnaire.json b/PICS/Resources/Onboarding-Questionnaire.json index 302c689..da1ff96 100644 --- a/PICS/Resources/Onboarding-Questionnaire.json +++ b/PICS/Resources/Onboarding-Questionnaire.json @@ -1 +1 @@ -{"title":"Onboarding Survey","resourceType":"Questionnaire","language":"en-US","status":"draft","publisher":"Stanford Biodesign Digital Health","meta":{"profile":["http://spezi.health/fhir/StructureDefinition/sdf-Questionnaire"],"tag":[{"system":"urn:ietf:bcp:47","code":"en-US","display":"English"}]},"useContext":[{"code":{"system":"http://hl7.org/fhir/ValueSet/usage-context-type","code":"focus","display":"Clinical Focus"},"valueCodeableConcept":{"coding":[{"system":"urn:oid:2.16.578.1.12.4.1.1.8655","display":"Onboarding Survey"}]}}],"contact":[{"name":"http://spezi.health"}],"subjectType":["Patient"],"url":"http://spezi.health/fhir/questionnaire/2623b4a2-6244-4846-b23e-5262a8878b68","item":[{"linkId":"b84fded4-3be9-4e5a-8f66-1b58806765cb","type":"display","text":"In order to better assess your current physical and psychological well-being, we need to collect some basic information in different areas.","required":false},{"linkId":"10c9f106-f55e-4794-8a6f-8c19f275dfa0","type":"choice","required":true,"answerOption":[{"valueCoding":{"id":"eab8aeb9-62a2-4328-8186-556a49f9d08d","code":"male","system":"urn:uuid:ffce077a-a871-4c9e-823e-b0e498b6c75a","display":"Male"}},{"valueCoding":{"id":"6a080723-9de9-46b2-d593-20b4cbf8dcc3","code":"female","system":"urn:uuid:ffce077a-a871-4c9e-823e-b0e498b6c75a","display":"Female"}},{"valueCoding":{"id":"03c39e4e-a678-444c-ea78-b025ad8db13d","code":"other","system":"urn:uuid:ffce077a-a871-4c9e-823e-b0e498b6c75a","display":"Other"}}],"text":"Gender"},{"linkId":"f60517a5-5494-4075-b317-bc5dcec45119","type":"integer","text":"Age","required":true},{"linkId":"c88d0efe-a8d2-4cd1-8d01-858df93acdd5","type":"integer","text":"Height (cm)","required":true},{"linkId":"e6dbcba0-7a71-415b-eb7a-ab3d303557bf","type":"integer","text":"Weight (kg)","required":true},{"linkId":"3d0a62ca-52b4-4e40-8c4e-c335dd8c489d","type":"integer","text":"Need for Care (1-5)","extension":[{"url":"http://hl7.org/fhir/StructureDefinition/maxValue","valueInteger":5},{"url":"http://hl7.org/fhir/StructureDefinition/minValue","valueInteger":-2},{"url":"http://ehelse.no/fhir/StructureDefinition/validationtext","valueString":"Input must be 1-5"}],"required":true},{"linkId":"9657ae60-ab44-46c1-81b2-9caa78dc1cd1","type":"integer","text":"Degree of disability (0-100)","extension":[{"url":"http://hl7.org/fhir/StructureDefinition/minValue","valueInteger":0},{"url":"http://hl7.org/fhir/StructureDefinition/maxValue","valueInteger":100},{"url":"http://ehelse.no/fhir/StructureDefinition/validationtext","valueString":"Input must be 0-100"}],"required":true},{"linkId":"d568aaa5-8cfd-4fef-8e49-3213b053175d","type":"choice","text":"Were you able to return to your original job after the intensive care unit/serious illness?","required":true,"answerOption":[{"valueCoding":{"id":"0ea9166b-4838-4b22-acbe-17a055a95bc4","code":"yes","system":"urn:uuid:b2b6bcfe-a64f-4e3f-8d1c-cb86650e0104","display":"Yes"}},{"valueCoding":{"id":"9852e452-2440-481b-824c-ea599f0d6fb4","code":"no","system":"urn:uuid:b2b6bcfe-a64f-4e3f-8d1c-cb86650e0104","display":"No"}},{"valueCoding":{"id":"2901a9d6-53d6-417a-82bc-568d3be903f1","code":"not-applicable","system":"urn:uuid:b2b6bcfe-a64f-4e3f-8d1c-cb86650e0104","display":"Not Applicable"}},{"valueCoding":{"id":"29d13d61-7bbe-4a48-e0b3-f632f43db033","code":"i-am-retired.","system":"urn:uuid:b2b6bcfe-a64f-4e3f-8d1c-cb86650e0104","display":"I am retired."}}]},{"linkId":"a85e750e-f3dd-4fd0-8b52-409580b7273d","type":"choice","text":"Marital Status","required":true,"answerOption":[{"valueCoding":{"id":"90cbde0b-2376-41d8-8c6d-4a2da252f948","code":"single","system":"urn:uuid:1ec9af15-42ab-4ad7-8446-4ba8a42a42f4","display":"Single"}},{"valueCoding":{"id":"ad32cb57-b1aa-4a9f-8c9f-4d373a86b229","code":"married/partnership","system":"urn:uuid:1ec9af15-42ab-4ad7-8446-4ba8a42a42f4","display":"Married/Partnership"}},{"valueCoding":{"id":"1556fcde-8362-4f18-8720-91d4693e80e9","code":"divorced","system":"urn:uuid:1ec9af15-42ab-4ad7-8446-4ba8a42a42f4","display":"Divorced"}},{"valueCoding":{"id":"4eec350d-20b0-4dda-de3c-3f254a98baa1","code":"widowed","system":"urn:uuid:1ec9af15-42ab-4ad7-8446-4ba8a42a42f4","display":"Widowed"}}]},{"linkId":"078280d9-5b33-4aa6-85f4-b06f8d9fe651","type":"open-choice","text":"Current living situation","required":true,"answerOption":[{"valueCoding":{"id":"5028c5ee-c99d-45af-c752-955399eebe42","code":"living-alone","system":"urn:uuid:13f24035-c3c7-4710-96c0-7fe882ca7950","display":"Living Alone"}},{"valueCoding":{"id":"98f1f387-d4d5-4c60-8c00-a388242ddcd1","code":"living-together-with-partner","system":"urn:uuid:13f24035-c3c7-4710-96c0-7fe882ca7950","display":"Living together with partner"}},{"valueCoding":{"id":"8236a70c-e166-46f8-cc13-e366cc2485e8","code":"living-together-with-children","system":"urn:uuid:13f24035-c3c7-4710-96c0-7fe882ca7950","display":"Living together with children"}},{"valueCoding":{"id":"cb435bad-8b5d-4552-9510-d9a949676d37","code":"living-together-with-parents","system":"urn:uuid:13f24035-c3c7-4710-96c0-7fe882ca7950","display":"Living together with parents"}},{"valueCoding":{"id":"589a0a8c-af84-419d-c777-56db696b3d35","code":"other-","system":"urn:uuid:13f24035-c3c7-4710-96c0-7fe882ca7950","display":"Other "}}]},{"linkId":"e2f115c2-bf0e-4660-da7f-892ce3852bda","type":"choice","text":"What is your highest level of education?","required":true,"answerOption":[{"valueCoding":{"id":"84ca31bd-d484-4951-8084-b854156710de","code":"no-school-certificate","system":"urn:uuid:bc044025-fcd7-492e-8dc9-1b3edb4a7ad8","display":"No school certificate"}},{"valueCoding":{"id":"0923ce87-b37b-4647-d5e6-70cbc128beb9","code":"high-school-diploma","system":"urn:uuid:bc044025-fcd7-492e-8dc9-1b3edb4a7ad8","display":"High school diploma"}},{"valueCoding":{"id":"ca7f6be5-ce9e-4b9a-8b25-a57adf584881","code":"college-degree","system":"urn:uuid:bc044025-fcd7-492e-8dc9-1b3edb4a7ad8","display":"College degree"}},{"valueCoding":{"id":"838e838f-e4ad-4669-8900-18bb69fb1ad1","code":"master-degree","system":"urn:uuid:bc044025-fcd7-492e-8dc9-1b3edb4a7ad8","display":"Master degree"}},{"valueCoding":{"id":"03df41cc-af06-44e7-8215-2b1743777df3","code":"doctorate","system":"urn:uuid:bc044025-fcd7-492e-8dc9-1b3edb4a7ad8","display":"Doctorate"}}]}]} +{"title":"Onboarding Survey","resourceType":"Questionnaire","language":"en-US","status":"draft","publisher":"Stanford Biodesign Digital Health","meta":{"profile":["http://spezi.health/fhir/StructureDefinition/sdf-Questionnaire"],"tag":[{"system":"urn:ietf:bcp:47","code":"en-US","display":"English"}]},"useContext":[{"code":{"system":"http://hl7.org/fhir/ValueSet/usage-context-type","code":"focus","display":"Clinical Focus"},"valueCodeableConcept":{"coding":[{"system":"urn:oid:2.16.578.1.12.4.1.1.8655","display":"Onboarding Survey"}]}}],"contact":[{"name":"http://spezi.health"}],"subjectType":["Patient"],"url":"http://spezi.health/fhir/questionnaire/4846c2b6-84f5-4154-8e9b-92d3a0ea101d","item":[{"linkId":"57544351-981e-4bc0-8202-81baa7a515cf","type":"display","text":"In order to better assess your current physical and psychological well-being, we need to collect some basic information in different areas. ","required":false},{"linkId":"62233356-ef0d-4e83-b90d-db2d54f75d95","type":"choice","text":"Gender","required":true,"answerOption":[{"valueCoding":{"id":"90e4af7d-fb60-4209-8b98-81b62bdb73f5","code":"male","system":"urn:uuid:f2555f86-0fb2-491b-abb0-2339f8411c5b","display":"Male"}},{"valueCoding":{"id":"c4baf7b8-9690-4b3c-8668-982b90f9220a","code":"female","system":"urn:uuid:f2555f86-0fb2-491b-abb0-2339f8411c5b","display":"Female"}},{"valueCoding":{"id":"e6dd8c90-95f7-4d8a-9503-2a542a159cb9","code":"other","system":"urn:uuid:f2555f86-0fb2-491b-abb0-2339f8411c5b","display":"Other"}}]},{"linkId":"48b5965b-b68e-4266-8e1c-1b7832be2773","type":"integer","text":"Age","required":true},{"linkId":"a6a4321a-609a-41e9-dc59-f81d6772a40a","type":"integer","text":"Height (cm)","required":true},{"linkId":"737e8c57-e3b4-4015-8787-815467cfd886","type":"integer","text":"Weight (kg)","required":true},{"linkId":"275ecc49-06b4-470d-8751-993b7cbb22d1","type":"integer","text":"Need for Care","extension":[{"url":"http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl","valueCodeableConcept":{"coding":[{"system":"http://hl7.org/fhir/questionnaire-item-control","code":"slider","display":"Slider"}]}},{"url":"http://hl7.org/fhir/StructureDefinition/minValue","valueInteger":1},{"url":"http://hl7.org/fhir/StructureDefinition/maxValue","valueInteger":5},{"url":"http://hl7.org/fhir/StructureDefinition/questionnaire-sliderStepValue","valueInteger":1}],"required":true},{"linkId":"a87c831f-c9af-4cf6-8782-009d5d250827","type":"integer","text":"Degree of Disability","extension":[{"url":"http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl","valueCodeableConcept":{"coding":[{"system":"http://hl7.org/fhir/questionnaire-item-control","code":"slider","display":"Slider"}]}},{"url":"http://hl7.org/fhir/StructureDefinition/minValue","valueInteger":0},{"url":"http://hl7.org/fhir/StructureDefinition/maxValue","valueInteger":100},{"url":"http://hl7.org/fhir/StructureDefinition/questionnaire-sliderStepValue","valueInteger":10}],"required":true},{"linkId":"4e05da2e-f11a-4120-9baa-e38039d98f4f","type":"choice","text":"Were you able to return to your original job after the intensive care unit/serious illness?","required":true,"answerOption":[{"valueCoding":{"id":"bf350307-5eda-40b0-8039-b4b347dfcd8d","code":"yes","system":"urn:uuid:2690f1cb-41d4-4c88-bf50-7a9b8edafa01","display":"Yes"}},{"valueCoding":{"id":"947c70c6-c147-437b-f493-ef553c54b866","code":"no","system":"urn:uuid:2690f1cb-41d4-4c88-bf50-7a9b8edafa01","display":"No"}},{"valueCoding":{"id":"ec6e697f-ba69-4ac3-85c5-ec1dbbc115d9","code":"not-applicable","system":"urn:uuid:2690f1cb-41d4-4c88-bf50-7a9b8edafa01","display":"Not Applicable"}},{"valueCoding":{"id":"18ea0b3e-7989-45ef-b4e4-c2351e0c73ed","code":"i-am-retired","system":"urn:uuid:2690f1cb-41d4-4c88-bf50-7a9b8edafa01","display":"I am retired"}}]},{"linkId":"1006500f-6bd3-4077-acf5-1536014cad68","type":"choice","text":"Marital Status","required":true,"answerOption":[{"valueCoding":{"id":"9fdad7db-ac20-451e-890d-d4a5dd14cadf","code":"single","system":"urn:uuid:2a72d108-bcc3-4ce2-89b9-ab7ff6fc8fb5","display":"Single"}},{"valueCoding":{"id":"bd892d60-6445-43f5-d549-fbe38f7e7ec1","code":"married/partnership","system":"urn:uuid:2a72d108-bcc3-4ce2-89b9-ab7ff6fc8fb5","display":"Married/Partnership"}},{"valueCoding":{"id":"0315e5a7-a3f2-46d3-b58e-bb2c5d3959d9","code":"divorced","system":"urn:uuid:2a72d108-bcc3-4ce2-89b9-ab7ff6fc8fb5","display":"Divorced"}},{"valueCoding":{"id":"8d5b1925-610f-4510-eb34-b521f0a6377d","code":"widowed","system":"urn:uuid:2a72d108-bcc3-4ce2-89b9-ab7ff6fc8fb5","display":"Widowed"}}]},{"linkId":"07938c2f-1784-4301-d12b-5fd5a16c1d5d","type":"choice","text":"Current living situation","required":true,"answerOption":[{"valueCoding":{"id":"15a92c88-57af-4c72-8025-4e5373269ea0","code":"living-alone","system":"urn:uuid:cb237cf9-07ed-4518-f53c-b4f695952822","display":"Living alone"}},{"valueCoding":{"id":"5b562b4f-cb92-4e6b-9e23-94bd964dd7fe","code":"living-together-with-partner","system":"urn:uuid:cb237cf9-07ed-4518-f53c-b4f695952822","display":"Living together with partner"}},{"valueCoding":{"id":"891a3878-04fa-421a-8f13-a928fab20a10","code":"living-together-with-children","system":"urn:uuid:cb237cf9-07ed-4518-f53c-b4f695952822","display":"Living together with children"}},{"valueCoding":{"id":"ae1904b0-af98-4d76-861c-f20ad91ef6ab","code":"living-together-with-parents","system":"urn:uuid:cb237cf9-07ed-4518-f53c-b4f695952822","display":"Living together with parents"}},{"valueCoding":{"id":"395e1d34-2ce0-4831-e5fe-2d37068d6dc0","code":"other-","system":"urn:uuid:cb237cf9-07ed-4518-f53c-b4f695952822","display":"Other "}}]},{"linkId":"bc384e95-37e4-45ba-8036-f64542eb6643","type":"choice","text":"What is your highest level of education?","required":true,"answerOption":[{"valueCoding":{"id":"d7127dec-b257-417a-b196-891efe184d5a","code":"no-school-certificate","system":"urn:uuid:309e68e7-bd65-4daa-cd63-c4259de23771","display":"No school certificate"}},{"valueCoding":{"id":"672cbd04-a88a-48eb-9a9c-ee4886d0b7ca","code":"high-school-diploma","system":"urn:uuid:309e68e7-bd65-4daa-cd63-c4259de23771","display":"High school diploma"}},{"valueCoding":{"id":"a331b190-e47c-4d4c-8622-d8f2aa7554d7","code":"college-degree","system":"urn:uuid:309e68e7-bd65-4daa-cd63-c4259de23771","display":"College degree"}},{"valueCoding":{"id":"86551785-fbf8-4a0e-d841-3e035b8309ee","code":"master-degree","system":"urn:uuid:309e68e7-bd65-4daa-cd63-c4259de23771","display":"Master degree"}},{"valueCoding":{"id":"7361aa77-62e4-4b7e-918b-a71e3d8c78f2","code":"doctorate","system":"urn:uuid:309e68e7-bd65-4daa-cd63-c4259de23771","display":"Doctorate"}}]}]} \ No newline at end of file diff --git a/PICS/Schedule/OnboardingQuestionnaireDashboard.swift b/PICS/Schedule/OnboardingQuestionnaireDashboard.swift new file mode 100644 index 0000000..f2d3da8 --- /dev/null +++ b/PICS/Schedule/OnboardingQuestionnaireDashboard.swift @@ -0,0 +1,36 @@ +// +// This source file is part of the PICS based on the Stanford Spezi Template Application project +// +// SPDX-FileCopyrightText: 2023 Stanford University +// +// SPDX-License-Identifier: MIT +// +import SpeziOnboarding +import SpeziQuestionnaire +import SwiftUI +struct OnboardingQuestionnaireDashboard: View { + @Environment(PICSStandard.self) private var standard + @AppStorage("isSurveyCompleted") var isSurveyCompleted = false + @Binding var isSheetPresented: Bool + + var body: some View { + QuestionnaireView( + questionnaire: Bundle.main.questionnaire(withName: "Onboarding-Questionnaire") + ) { result in + guard case let .completed(response) = result else { + isSheetPresented = false + return // user cancelled + } + isSurveyCompleted = true + isSheetPresented = false + await standard.add(response: response) + } + } +} + +#Preview { + OnboardingStack { + OnboardingQuestionnaireDashboard(isSheetPresented: .constant(true)) + } + .previewWith(standard: PICSStandard()) {} +} diff --git a/PICS/Schedule/OnboardingSurveyView.swift b/PICS/Schedule/OnboardingSurveyView.swift new file mode 100644 index 0000000..c4ee2fe --- /dev/null +++ b/PICS/Schedule/OnboardingSurveyView.swift @@ -0,0 +1,75 @@ +// +// This source file is part of the PICS based on the Stanford Spezi Template Application project +// +// SPDX-FileCopyrightText: 2023 Stanford University +// +// SPDX-License-Identifier: MIT +// +import SwiftUI + +struct OnboardingSurveyView: View { + @Environment(PICSStandard.self) private var standard + @AppStorage("isSurveyCompleted") var isSurveyCompleted = false + @State var isSheetPresented = false + + var body: some View { + HStack { + onboardingSurveyDetailsStack + } + .contentShape(Rectangle()) + } + + private var onboardingSurveyDetailsStack: some View { + VStack(alignment: .leading) { + HStack(alignment: .top) { // Align items to top + statusIcon + VStack(alignment: .leading) { + Text("Onboarding Questionnaire") + .font(.headline) + Text("Now") + .font(.subheadline) + } + } + Divider() + descriptionText + actionTypeText + } + } + + private var statusIcon: some View { + Image(systemName: "exclamationmark.circle.fill") + .foregroundColor(.yellow) + .font(.system(size: 30)) + .accessibilityHidden(true) + } + + private var descriptionText: some View { + Text("ONBOARDING_CAPTION") + .font(.callout) + } + + private var actionTypeText: some View { + Button(action: { + isSheetPresented = true + }) { + Text("Start Questionnaire") + .frame(maxWidth: .infinity, minHeight: 50) + .foregroundColor(.white) + .background(Color.accentColor) + .clipShape(RoundedRectangle(cornerRadius: 8)) + .padding(.top, 8) + } + .sheet(isPresented: $isSheetPresented) { + OnboardingQuestionnaireDashboard(isSheetPresented: $isSheetPresented) + } + } +} + +#if DEBUG +struct OnboardingSurveyView_Previews: PreviewProvider { + static var previews: some View { + OnboardingSurveyView() + .padding() + } +} +#endif diff --git a/PICS/Schedule/ScheduleView.swift b/PICS/Schedule/ScheduleView.swift index ff88365..ed5f20f 100644 --- a/PICS/Schedule/ScheduleView.swift +++ b/PICS/Schedule/ScheduleView.swift @@ -20,42 +20,47 @@ struct ScheduleView: View { @Binding private var presentingAccount: Bool - + @AppStorage("isSurveyCompleted") var isSurveyCompleted = false private var startOfDays: [Date] { Array(eventContextsByDate.keys) } - - var body: some View { NavigationStack { - List(startOfDays, id: \.timeIntervalSinceNow) { startOfDay in - Section(format(startOfDay: startOfDay)) { - ForEach(eventContextsByDate[startOfDay] ?? [], id: \.event) { eventContext in - EventContextView(eventContext: eventContext) - .onTapGesture { - if !eventContext.event.complete { - presentedContext = eventContext + List { + if !isSurveyCompleted { + Section(header: Text("Onboarding Task")) { + OnboardingSurveyView() + } + } + ForEach(startOfDays, id: \.timeIntervalSinceNow) { startOfDay in + Section(format(startOfDay: startOfDay)) { + ForEach(eventContextsByDate[startOfDay] ?? [], id: \.event) { eventContext in + EventContextView(eventContext: eventContext) + .onTapGesture { + if !eventContext.event.complete { + presentedContext = eventContext + } } - } + } } } } - .onChange(of: scheduler) { - calculateEventContextsByDate() - } - .task { - calculateEventContextsByDate() - } - .sheet(item: $presentedContext) { presentedContext in - destination(withContext: presentedContext) - } - .toolbar { - if AccountButton.shouldDisplay { - AccountButton(isPresented: $presentingAccount) - } + .onChange(of: scheduler) { + calculateEventContextsByDate() + } + .task { + calculateEventContextsByDate() + } + .sheet(item: $presentedContext) { presentedContext in + destination(withContext: presentedContext) + } + .toolbar { + if AccountButton.shouldDisplay { + AccountButton(isPresented: $presentingAccount) } - .navigationTitle("SCHEDULE_LIST_TITLE") + } + .navigationTitle("SCHEDULE_LIST_TITLE") } }