Skip to content

Commit cc6ec4c

Browse files
committed
Add calendar legend and Help & Guide page
Introduce a CalendarLegendView (and LegendItem) to show color/dot meanings and embed it in CalendarView. Add a new HelpView (with GuideRow and TipItem) exposing user guide, navigation tips, privacy notes and links; add a Settings link to navigate to HelpView. Update documentation and checklists (cycleone_checklist.md, project_overview.md, test_checklist.md) to reflect the new legend, Help & Guide entry, and related tests.
1 parent e35b725 commit cc6ec4c

7 files changed

Lines changed: 190 additions & 0 deletions

File tree

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//
2+
// CalendarLegendView.swift
3+
// CycleOne
4+
//
5+
6+
import SwiftUI
7+
8+
struct CalendarLegendView: View {
9+
var body: some View {
10+
VStack(alignment: .leading, spacing: 12) {
11+
Text("Calendar Legend")
12+
.font(.subheadline)
13+
.fontWeight(.semibold)
14+
.foregroundColor(.secondary)
15+
16+
VStack(alignment: .leading, spacing: 8) {
17+
HStack(spacing: 16) {
18+
LegendItem(color: .systemPink, label: "Period", isCustomView: true)
19+
LegendItem(color: .systemGray, label: "Predicted Period")
20+
}
21+
HStack(spacing: 16) {
22+
LegendItem(color: .systemTeal, label: "Ovulation Day")
23+
LegendItem(color: .systemTeal.withAlphaComponent(0.3), label: "Fertile Window")
24+
}
25+
LegendItem(color: .secondaryLabel, label: "Logged (Mood/Symptom)")
26+
}
27+
}
28+
.padding()
29+
.background(Color(.secondarySystemBackground).opacity(0.5))
30+
.cornerRadius(12)
31+
}
32+
}
33+
34+
struct LegendItem: View {
35+
let color: UIColor
36+
let label: String
37+
var isCustomView: Bool = false
38+
39+
var body: some View {
40+
HStack(spacing: 8) {
41+
Circle()
42+
.fill(Color(color))
43+
.frame(width: isCustomView ? 10 : 6, height: isCustomView ? 10 : 6)
44+
Text(label)
45+
.font(.caption)
46+
.foregroundColor(.primary)
47+
}
48+
}
49+
}
50+
51+
#Preview {
52+
CalendarLegendView()
53+
.padding()
54+
}

CycleOne/Views/Calendar/CalendarView.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ struct CalendarView: View {
3333
.padding(.horizontal)
3434
.frame(minHeight: 400) // Ensure it has enough space
3535

36+
CalendarLegendView()
37+
.padding(.horizontal)
38+
.padding(.top, 8)
39+
3640
CalendarDayDetailView(
3741
date: viewModel.selectedDate,
3842
log: viewModel.selectedDayLog,
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
//
2+
// HelpView.swift
3+
// CycleOne
4+
//
5+
6+
import SwiftUI
7+
8+
struct HelpView: View {
9+
var body: some View {
10+
List {
11+
Section("User Guide") {
12+
GuideRow(
13+
icon: "calendar",
14+
title: "Calendar",
15+
description: "The main screen shows your cycle at a glance. Tap any date to view details or log data."
16+
)
17+
GuideRow(
18+
icon: "pencil.and.outline",
19+
title: "Logging",
20+
description: "Log your flow, symptoms, mood, and energy. Data is auto-saved when you navigate away."
21+
)
22+
GuideRow(
23+
icon: "chart.bar.fill",
24+
title: "Insights",
25+
description: "View your average cycle length, period duration, and top symptoms over time."
26+
)
27+
GuideRow(
28+
icon: "bell.fill",
29+
title: "Reminders",
30+
description: "Enable notifications in Settings to get alerts before your next period or fertile window."
31+
)
32+
}
33+
34+
Section("Navigation Tips") {
35+
TipItem(text: "Swipe left or right on the calendar to change months.")
36+
TipItem(text: "Tap the 'Today' button in the calendar header to quickly return to current date.")
37+
TipItem(text: "Tap a cycle in the Insights history to see its full breakdown.")
38+
}
39+
40+
Section("App Philosophy") {
41+
VStack(alignment: .leading, spacing: 10) {
42+
Text("Privacy First")
43+
.font(.headline)
44+
Text(
45+
"CycleOne is designed with zero cloud sync and zero tracking. Your data is stored exclusively on your device in a secure local database."
46+
)
47+
.font(.subheadline)
48+
.foregroundColor(.secondary)
49+
50+
Divider()
51+
52+
Text("No Subscriptions")
53+
.font(.headline)
54+
Text("We believe you shouldn't pay monthly to access your own health data. " +
55+
"One purchase, forever yours.")
56+
.font(.subheadline)
57+
.foregroundColor(.secondary)
58+
}
59+
.padding(.vertical, 8)
60+
}
61+
62+
Section("Manual & Support") {
63+
Link(destination: URL(string: "https://github.com/VoxDroid/CycleOne")!) {
64+
Label("View Project on GitHub", systemImage: "link")
65+
}
66+
67+
NavigationLink(destination: PrivacyPolicyView()) {
68+
Label("Privacy Policy", systemImage: "shield.fill")
69+
}
70+
}
71+
}
72+
.navigationTitle("Help & Guide")
73+
.navigationBarTitleDisplayMode(.inline)
74+
}
75+
}
76+
77+
struct GuideRow: View {
78+
let icon: String
79+
let title: String
80+
let description: String
81+
82+
var body: some View {
83+
HStack(alignment: .top, spacing: 16) {
84+
Image(systemName: icon)
85+
.font(.title3)
86+
.foregroundColor(.themeAccent)
87+
.frame(width: 24)
88+
VStack(alignment: .leading, spacing: 4) {
89+
Text(title)
90+
.font(.headline)
91+
Text(description)
92+
.font(.caption)
93+
.foregroundColor(.secondary)
94+
}
95+
}
96+
.padding(.vertical, 4)
97+
}
98+
}
99+
100+
struct TipItem: View {
101+
let text: String
102+
103+
var body: some View {
104+
HStack(alignment: .top, spacing: 12) {
105+
Image(systemName: "lightbulb.fill")
106+
.foregroundColor(.yellow)
107+
.font(.caption)
108+
.padding(.top, 2)
109+
Text(text)
110+
.font(.subheadline)
111+
}
112+
}
113+
}
114+
115+
#Preview {
116+
NavigationStack {
117+
HelpView()
118+
}
119+
}

CycleOne/Views/Settings/SettingsView.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ struct SettingsView: View {
4646
}
4747

4848
Section("App") {
49+
NavigationLink(destination: HelpView()) {
50+
Label("Help & Guide", systemImage: "questionmark.circle.fill")
51+
}
52+
4953
if let url = URL(string: "https://github.com/VoxDroid/CycleOne") {
5054
Link(destination: url) {
5155
Label("Support & Feedback", systemImage: "link")

CycleOneDocs/cycleone_checklist.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,8 @@
4949
- [x] **Onboarding**: Lightweight first-launch tip overlay (Section 8 & 9.1)
5050
- [x] **UI Polish**: Prediction Engine Disclaimer Label (Section 9.3)
5151
- [x] **UI Polish**: Ovulation window line in Header Banner (Section 9.1)
52+
53+
## UX Upgrades (Phase 11)
54+
- [x] **Legend**: Calendar legend for dot decorations
55+
- [x] **Guide**: Dedicated Help & Guide page in Settings
56+
- [x] **Navigation**: Navigation tips and support links

CycleOneDocs/project_overview.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ ContentView (TabView)
383383
│ └── CycleHistoryList (NavigationLink)
384384
385385
└── Tab 3: SettingsView
386+
├── Help & Guide (NavigationLink)
386387
├── NotificationSettingsView (NavigationLink)
387388
└── ExportView (NavigationLink)
388389
```
@@ -418,6 +419,7 @@ ContentView (TabView)
418419

419420
**Interactions:**
420421
- Tap any day → opens `LogView` for that day
422+
- Legend at bottom of screen explains colors/dots
421423
- Swipe left/right to navigate months
422424
- Pull-to-refresh is not relevant (no network). Month navigation is via chevron buttons or swipe gesture.
423425

CycleOneDocs/test_checklist.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,5 @@
2121
- [x] Export: CSV file is valid and contains all logged data
2222
- [x] Disclaimer: Verify "Predictions are estimates only" label visibility
2323
- [x] Onboarding: Verify tip overlay appears on first launch (post-implementation)
24+
- [x] Legend: Verify legend visibility and accuracy on Calendar
25+
- [x] Help: Verify navigation to Help page and content readability

0 commit comments

Comments
 (0)