Skip to content

Commit a046946

Browse files
dashboard view ui touch ups and create export pdf feature (#116)
# *dashboard view ui touch ups and create export pdf feature* ## ♻️ Current situation & Problem *Link any open issues or pull requests (PRs) related to this PR. Please ensure that all non-trivial PRs are first tracked and discussed in an existing GitHub issue or discussion.* ## ⚙️ Release Notes *Add a bullet point list summary of the feature and possible migration guides if this is a breaking change so this section can be added to the release notes.* *Include code snippets that provide examples of the feature implemented or links to the documentation if it appends or changes the public interface.* ## 📚 Documentation *Please ensure that you properly document any additions in conformance to [Spezi Documentation Guide](https://github.com/StanfordSpezi/.github/blob/main/DOCUMENTATIONGUIDE.md).* *You can use this section to describe your solution, but we encourage contributors to document your reasoning and changes using in-line documentation.* ## ✅ Testing *Please ensure that the PR meets the testing requirements set by CodeCov and that new functionality is appropriately tested.* *This section describes important information about the tests and why some elements might not be testable.* ## 📝 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): - [ ] 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).
1 parent 332a655 commit a046946

20 files changed

Lines changed: 439 additions & 76 deletions

Stanford360.xcodeproj/project.pbxproj

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
C15624902D6D6E6E00298CEB /* InfiniteDayTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C156248F2D6D6E6E00298CEB /* InfiniteDayTimelineView.swift */; };
8787
C15C64082D75CF8500979C62 /* LogWeightSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C15C64072D75CF8500979C62 /* LogWeightSheet.swift */; };
8888
C15C640A2D75CFF700979C62 /* LogWeightView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C15C64092D75CFF700979C62 /* LogWeightView.swift */; };
89+
C18772D72D82A57D004967EC /* TPPDF in Frameworks */ = {isa = PBXBuildFile; productRef = C18772D62D82A57D004967EC /* TPPDF */; };
8990
C1CF13CF2D6D72C40057BC77 /* DailyScheduleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CF13CE2D6D72C40057BC77 /* DailyScheduleView.swift */; };
9091
C1CF13DA2D6D76BE0057BC77 /* ScheduleTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CF13D92D6D76BE0057BC77 /* ScheduleTask.swift */; };
9192
C1CF13E72D6D7CF20057BC77 /* TaskStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CF13E62D6D7CF20057BC77 /* TaskStore.swift */; };
@@ -195,6 +196,7 @@
195196
2FB099B62A875E2B00B20952 /* HealthKitOnFHIR in Frameworks */,
196197
56E7083B2BB06F6F00B08F0A /* SwiftPackageList in Frameworks */,
197198
56E708352BB06B7100B08F0A /* SpeziLicense in Frameworks */,
199+
C18772D72D82A57D004967EC /* TPPDF in Frameworks */,
198200
2FE5DC8A29EDD972004B9AB4 /* SpeziLocalStorage in Frameworks */,
199201
4D2965DE2D7A8084000664B4 /* SpeziLLMLocalDownload in Frameworks */,
200202
2FE5DC8C29EDD972004B9AB4 /* SpeziSecureStorage in Frameworks */,
@@ -469,6 +471,7 @@
469471
4D2965DB2D7A8084000664B4 /* SpeziLLMLocal */,
470472
4D2965DD2D7A8084000664B4 /* SpeziLLMLocalDownload */,
471473
4D2965DF2D7A8084000664B4 /* SpeziLLMOpenAI */,
474+
C18772D62D82A57D004967EC /* TPPDF */,
472475
);
473476
productName = Stanford360;
474477
productReference = 653A254D283387FE005D4D48 /* Stanford360.app */;
@@ -577,6 +580,7 @@
577580
A94DDFFB2CBD1190004930BD /* XCRemoteSwiftPackageReference "SpeziNotifications" */,
578581
4DD4BE2A2D79C04F00B5FFC0 /* XCRemoteSwiftPackageReference "llama.cpp" */,
579582
4D2965D62D7A8084000664B4 /* XCRemoteSwiftPackageReference "SpeziLLM" */,
583+
C18772D22D82A401004967EC /* XCRemoteSwiftPackageReference "TPPDF" */,
580584
);
581585
productRefGroup = 653A254E283387FE005D4D48 /* Products */;
582586
projectDirPath = "";
@@ -1360,6 +1364,14 @@
13601364
minimumVersion = 1.0.2;
13611365
};
13621366
};
1367+
C18772D22D82A401004967EC /* XCRemoteSwiftPackageReference "TPPDF" */ = {
1368+
isa = XCRemoteSwiftPackageReference;
1369+
repositoryURL = "https://github.com/techprimate/TPPDF";
1370+
requirement = {
1371+
kind = upToNextMajorVersion;
1372+
minimumVersion = 2.6.1;
1373+
};
1374+
};
13631375
/* End XCRemoteSwiftPackageReference section */
13641376

13651377
/* Begin XCSwiftPackageProductDependency section */
@@ -1557,6 +1569,11 @@
15571569
package = 2F66D20D2BB723180010D555 /* XCRemoteSwiftPackageReference "SwiftLint" */;
15581570
productName = "plugin:SwiftLintBuildToolPlugin";
15591571
};
1572+
C18772D62D82A57D004967EC /* TPPDF */ = {
1573+
isa = XCSwiftPackageProductDependency;
1574+
package = C18772D22D82A401004967EC /* XCRemoteSwiftPackageReference "TPPDF" */;
1575+
productName = TPPDF;
1576+
};
15601577
/* End XCSwiftPackageProductDependency section */
15611578
};
15621579
rootObject = 653A2545283387FE005D4D48 /* Project object */;

Stanford360.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 10 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Stanford360/Dashboard/Views/DashboardChart.swift

Lines changed: 56 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -20,82 +20,72 @@ struct DashboardChart: View {
2020

2121
var timeFrame: TimeFrame
2222

23-
private var filteredActivities: [Date] {
23+
private var dates: [Date] {
2424
let (startDate, endDate) = timeFrame.dateRange()
25-
return Array(activityManager.activitiesByDate.keys)
26-
.filter { date in
27-
date >= startDate && date <= endDate
28-
}
29-
.sorted()
30-
}
31-
32-
private var filteredHydration: [Date] {
33-
let (startDate, endDate) = timeFrame.dateRange()
34-
return Array(hydrationManager.hydrationByDate.keys)
35-
.filter { date in
36-
date >= startDate && date <= endDate
37-
}
38-
.sorted()
39-
}
40-
41-
private var filteredMeals: [Date] {
42-
let (startDate, endDate) = timeFrame.dateRange()
43-
return Array(proteinManager.mealsByDate.keys)
44-
.filter { date in
45-
date >= startDate && date <= endDate
46-
}
47-
.sorted()
25+
var dates: [Date] = []
26+
let calendar = Calendar.current
27+
var currentDate = calendar.startOfDay(for: startDate)
28+
29+
while currentDate <= endDate {
30+
dates.append(currentDate)
31+
currentDate = calendar.date(byAdding: .day, value: 1, to: currentDate) ?? Date()
32+
}
33+
34+
return dates
4835
}
4936

5037
var body: some View {
5138
Chart {
52-
ForEach(filteredActivities, id: \.self) { date in
53-
if let activities = activityManager.activitiesByDate[date] {
54-
LineMark(
55-
x: .value("Week", date),
56-
y: .value("Activity Minutes", activityManager.getTotalActivityMinutes(activities)),
57-
series: .value("Metric", "Activity")
58-
)
59-
.applyChartStyle(color: Color.activityColor)
60-
}
61-
}
62-
63-
ForEach(filteredHydration, id: \.self) { date in
64-
if let hydration = hydrationManager.hydrationByDate[date] {
65-
LineMark(
66-
x: .value("Week", date),
67-
y: .value("Hydration Ounces", hydrationManager.getTotalHydrationOunces(hydration)),
68-
series: .value("Metric", "Hydration")
69-
)
70-
.applyChartStyle(color: Color.hydrationColor)
71-
}
72-
}
73-
74-
ForEach(filteredMeals, id: \.self) { date in
75-
if let meals = proteinManager.mealsByDate[date] {
76-
LineMark(
77-
x: .value("Week", date),
78-
y: .value("Protein Grams", proteinManager.getTotalProteinGrams(meals)),
79-
series: .value("Metric", "Protein")
80-
)
81-
.applyChartStyle(color: Color.proteinColor)
82-
}
39+
ForEach(dates, id: \.self) { date in
40+
let activities = getActivities(from: date)
41+
LineMark(
42+
x: .value("Week", date),
43+
y: .value("Activity Minutes", activityManager.getTotalActivityMinutes(activities)),
44+
series: .value("Metric", "Activity")
45+
)
46+
.applyChartStyle(color: Color.activityColor)
47+
48+
let hydration = getHydration(from: date)
49+
LineMark(
50+
x: .value("Week", date),
51+
y: .value("Hydration Ounces", hydrationManager.getTotalHydrationOunces(hydration)),
52+
series: .value("Metric", "Hydration")
53+
)
54+
.applyChartStyle(color: Color.hydrationColor)
55+
56+
let meals = getMeals(from: date)
57+
LineMark(
58+
x: .value("Week", date),
59+
y: .value("Protein Grams", proteinManager.getTotalProteinGrams(meals)),
60+
series: .value("Metric", "Protein")
61+
)
62+
.applyChartStyle(color: Color.proteinColor)
8363
}
8464

8565
goalLine()
8666
}
8767
.frame(height: 300)
88-
.padding()
68+
.padding(.leading, 40)
69+
.padding(.trailing, 10)
8970
.chartXAxis {
90-
AxisMarks(values: .automatic) { value in
71+
AxisMarks(values: .stride(by: .day)) { value in
9172
if let date = value.as(Date.self) {
92-
AxisValueLabel {
93-
Text(date, format: .dateTime.month().day())
94-
}
73+
AxisValueLabel(date.formatted(.dateTime.weekday(.abbreviated)))
9574
}
9675
}
9776
}
98-
.chartXScale(domain: timeFrame.dateRange().start...timeFrame.dateRange().end)
77+
}
78+
79+
private func getActivities(from date: Date) -> [Activity] {
80+
activityManager.activitiesByDate[date] ?? []
81+
}
82+
83+
private func getHydration(from date: Date) -> [HydrationLog] {
84+
hydrationManager.hydrationByDate[date] ?? []
85+
}
86+
87+
private func getMeals(from date: Date) -> [Meal] {
88+
proteinManager.mealsByDate[date] ?? []
9989
}
10090
}
10191

@@ -106,16 +96,18 @@ extension LineMark {
10696
.symbol {
10797
Circle()
10898
.fill(color)
109-
.frame(width: 8, height: 8)
99+
.frame(width: 5, height: 5)
110100
}
111101
}
112102
}
113103

114104
#Preview {
115-
@Previewable @State var activityManager = ActivityManager()
105+
@Previewable @State var activityManager = ActivityManager(activities: activitiesData)
106+
@Previewable @State var hydrationManager = HydrationManager(hydration: hydrationData)
116107
@Previewable @State var proteinManager = ProteinManager(meals: mealsData)
117108

118-
DashboardChart(timeFrame: .month)
109+
DashboardChart(timeFrame: .week)
119110
.environment(activityManager)
111+
.environment(hydrationManager)
120112
.environment(proteinManager)
121113
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//
2+
// DashboardPDFView.swift
3+
// Stanford360
4+
//
5+
// Created by Kelly Bonilla Guzmán on 3/13/25.
6+
//
7+
// SPDX-FileCopyrightText: 2025 Stanford University
8+
//
9+
// SPDX-License-Identifier: MIT
10+
//
11+
12+
import PDFKit
13+
import SwiftUI
14+
import TPPDF
15+
16+
struct DashboardPDFView: UIViewRepresentable {
17+
var image: UIImage
18+
19+
func makeUIView(context: Context) -> PDFView {
20+
let url = PDFDocument.generatePDF(image: image)
21+
let pdfView = PDFView()
22+
pdfView.document = PDFKit.PDFDocument(url: url)
23+
return pdfView
24+
}
25+
26+
func updateUIView(_ uiView: PDFView, context: Context) { }
27+
}
28+
29+
#Preview {
30+
DashboardPDFView(image: UIImage(systemName: "chart.xyaxis.line") ?? UIImage())
31+
}

Stanford360/Dashboard/Views/DashboardTimeFrameView.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ struct DashboardTimeFrameView: View {
2323
.tag(TimeFrame.today)
2424
DashboardWeeklyView()
2525
.tag(TimeFrame.week)
26-
DashboardMonthlyView()
27-
.tag(TimeFrame.month)
26+
// DashboardMonthlyView()
27+
// .tag(TimeFrame.month)
2828
}
2929
.tabViewStyle(PageTabViewStyle())
3030
}

Stanford360/Dashboard/Views/DashboardTodayView.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,23 @@ struct DashboardTodayView: View {
2525
ProgressCard(
2626
title: "Activity",
2727
progress: CGFloat(activityManager.getTodayTotalMinutes()),
28+
unit: "min",
2829
color: .activityColor,
2930
streak: activityManager.streak
3031
)
3132

3233
ProgressCard(
3334
title: "Hydration",
3435
progress: CGFloat(hydrationManager.getTodayTotalOunces()),
36+
unit: "oz",
3537
color: .hydrationColor,
3638
streak: hydrationManager.streak
3739
)
3840

3941
ProgressCard(
4042
title: "Protein",
4143
progress: CGFloat(proteinManager.getTodayTotalGrams()),
44+
unit: "g",
4245
color: .proteinColor,
4346
streak: proteinManager.streak
4447
)

Stanford360/Dashboard/Views/DashboardWeeklyView.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,22 @@
1212
import SwiftUI
1313

1414
struct DashboardWeeklyView: View {
15+
@State var showPDFExportSheet: Bool = false
16+
@State var size: CGSize?
17+
1518
var body: some View {
16-
DashboardChartView(timeFrame: .week)
19+
VStack {
20+
HStack {
21+
Spacer()
22+
ExportPDFButton(showingSheet: $showPDFExportSheet)
23+
.padding(.trailing, 20)
24+
}
25+
26+
DashboardChartView(timeFrame: .week)
27+
}
28+
.sheet(isPresented: $showPDFExportSheet) {
29+
ExportPDFSheet()
30+
}
1731
}
1832
}
1933

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//
2+
// ExportPDFButton.swift
3+
// Stanford360
4+
//
5+
// Created by Kelly Bonilla Guzmán on 3/12/25.
6+
//
7+
// SPDX-FileCopyrightText: 2025 Stanford University
8+
//
9+
// SPDX-License-Identifier: MIT
10+
//
11+
12+
import SwiftUI
13+
14+
struct ExportPDFButton: View {
15+
@Binding var showingSheet: Bool
16+
17+
var body: some View {
18+
Button(action: { showingSheet = true }) {
19+
Text("View as PDF")
20+
Image(systemName: "eye")
21+
.accessibilityLabel(Text("View as PDF Button"))
22+
}
23+
}
24+
}
25+
26+
#Preview {
27+
@Previewable @State var showingSheet: Bool = false
28+
ExportPDFButton(showingSheet: $showingSheet)
29+
}

0 commit comments

Comments
 (0)