Compare commits

...

2 Commits

Author SHA1 Message Date
Trey t
4e874f60d7 Move Settings to Dashboard toolbar to kill iPhone More overflow
Six tabs forced iPhone to spill Course into the system "More" tab, whose
own NavigationStack nested with CourseView's and produced a double back
chevron on every week/deck push. Drop the Settings tab, reach it from a
gear button on Dashboard that presents SettingsView as a sheet, and keep
the visible tab count at five so no More overflow exists.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 18:22:27 -05:00
Trey t
644dbb7230 Scope week tests to the selected course
WeekTestView filtered decks and TestResults by weekNumber alone, so Week 3
of any course pulled content from every course's Week 3 simultaneously.
Thread courseName through the navigation destination, quiz view, and
TestResult model so quiz cards, score history, and focus-area missed items
are all scoped to the active course.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 18:17:30 -05:00
6 changed files with 55 additions and 13 deletions

View File

@@ -9,6 +9,7 @@ struct MissedCourseItem: Codable, Hashable, Sendable {
@Model @Model
final class TestResult { final class TestResult {
var id: String = "" var id: String = ""
var courseName: String = ""
var weekNumber: Int = 0 var weekNumber: Int = 0
var quizType: String = "" var quizType: String = ""
var totalQuestions: Int = 0 var totalQuestions: Int = 0
@@ -25,8 +26,9 @@ final class TestResult {
return Int(round(Double(correctCount) / Double(totalQuestions) * 100)) return Int(round(Double(correctCount) / Double(totalQuestions) * 100))
} }
init(weekNumber: Int, quizType: String, totalQuestions: Int, correctCount: Int, missedItems: [MissedCourseItem]) { init(courseName: String, weekNumber: Int, quizType: String, totalQuestions: Int, correctCount: Int, missedItems: [MissedCourseItem]) {
self.id = UUID().uuidString self.id = UUID().uuidString
self.courseName = courseName
self.weekNumber = weekNumber self.weekNumber = weekNumber
self.quizType = quizType self.quizType = quizType
self.totalQuestions = totalQuestions self.totalQuestions = totalQuestions
@@ -35,8 +37,9 @@ final class TestResult {
self.missedItems = missedItems self.missedItems = missedItems
} }
convenience init(weekNumber: Int, quizType: String, totalQuestions: Int, correctCount: Int, missedFronts: [String], missedBacks: [String]) { convenience init(courseName: String, weekNumber: Int, quizType: String, totalQuestions: Int, correctCount: Int, missedFronts: [String], missedBacks: [String]) {
self.init( self.init(
courseName: courseName,
weekNumber: weekNumber, weekNumber: weekNumber,
quizType: quizType, quizType: quizType,
totalQuestions: totalQuestions, totalQuestions: totalQuestions,

View File

@@ -6,6 +6,7 @@ import PencilKit
struct CourseQuizView: View { struct CourseQuizView: View {
let cards: [VocabCard] let cards: [VocabCard]
let quizType: QuizType let quizType: QuizType
let courseName: String
let weekNumber: Int let weekNumber: Int
let isFocusMode: Bool let isFocusMode: Bool
@@ -508,6 +509,7 @@ struct CourseQuizView: View {
private func saveResult() { private func saveResult() {
let result = TestResult( let result = TestResult(
courseName: courseName,
weekNumber: weekNumber, weekNumber: weekNumber,
quizType: quizType.rawValue, quizType: quizType.rawValue,
totalQuestions: shuffledCards.count, totalQuestions: shuffledCards.count,
@@ -521,7 +523,13 @@ struct CourseQuizView: View {
#Preview { #Preview {
NavigationStack { NavigationStack {
CourseQuizView(cards: [], quizType: .mcEsToEn, weekNumber: 1, isFocusMode: false) CourseQuizView(
cards: [],
quizType: .mcEsToEn,
courseName: "LanGo Spanish | Beginner I",
weekNumber: 1,
isFocusMode: false
)
} }
.modelContainer(for: [TestResult.self, VocabCard.self], inMemory: true) .modelContainer(for: [TestResult.self, VocabCard.self], inMemory: true)
} }

View File

@@ -31,7 +31,9 @@ struct CourseView: View {
} }
private func bestScore(for week: Int) -> Int? { private func bestScore(for week: Int) -> Int? {
let results = testResults.filter { $0.weekNumber == week } let results = testResults.filter {
$0.courseName == activeCourse && $0.weekNumber == week
}
guard !results.isEmpty else { return nil } guard !results.isEmpty else { return nil }
return results.map(\.scorePercent).max() return results.map(\.scorePercent).max()
} }
@@ -70,7 +72,7 @@ struct CourseView: View {
ForEach(weekGroups, id: \.week) { week, weekDecks in ForEach(weekGroups, id: \.week) { week, weekDecks in
Section { Section {
// Test button // Test button
NavigationLink(value: week) { NavigationLink(value: WeekTestDestination(courseName: activeCourse, weekNumber: week)) {
HStack(spacing: 12) { HStack(spacing: 12) {
Image(systemName: "pencil.and.list.clipboard") Image(systemName: "pencil.and.list.clipboard")
.font(.title3) .font(.title3)
@@ -112,8 +114,8 @@ struct CourseView: View {
.navigationDestination(for: CourseDeck.self) { deck in .navigationDestination(for: CourseDeck.self) { deck in
DeckStudyView(deck: deck) DeckStudyView(deck: deck)
} }
.navigationDestination(for: Int.self) { week in .navigationDestination(for: WeekTestDestination.self) { dest in
WeekTestView(weekNumber: week) WeekTestView(courseName: dest.courseName, weekNumber: dest.weekNumber)
} }
} }
} }
@@ -123,6 +125,13 @@ struct CourseView: View {
} }
} }
// MARK: - Navigation
struct WeekTestDestination: Hashable {
let courseName: String
let weekNumber: Int
}
// MARK: - Deck Row // MARK: - Deck Row
private struct DeckRowView: View { private struct DeckRowView: View {

View File

@@ -3,6 +3,7 @@ import SharedModels
import SwiftData import SwiftData
struct WeekTestView: View { struct WeekTestView: View {
let courseName: String
let weekNumber: Int let weekNumber: Int
@Environment(\.modelContext) private var modelContext @Environment(\.modelContext) private var modelContext
@Environment(\.cloudModelContextProvider) private var cloudModelContextProvider @Environment(\.cloudModelContextProvider) private var cloudModelContextProvider
@@ -60,6 +61,7 @@ struct WeekTestView: View {
CourseQuizView( CourseQuizView(
cards: weekCards, cards: weekCards,
quizType: type, quizType: type,
courseName: courseName,
weekNumber: weekNumber, weekNumber: weekNumber,
isFocusMode: false isFocusMode: false
) )
@@ -139,6 +141,7 @@ struct WeekTestView: View {
CourseQuizView( CourseQuizView(
cards: focusCards, cards: focusCards,
quizType: .mcEsToEn, quizType: .mcEsToEn,
courseName: courseName,
weekNumber: weekNumber, weekNumber: weekNumber,
isFocusMode: true isFocusMode: true
) )
@@ -206,7 +209,11 @@ struct WeekTestView: View {
} }
private func loadCards() { private func loadCards() {
let weekDecks = allDecks.filter { $0.weekNumber == weekNumber && !$0.isReversed } let course = courseName
let week = weekNumber
let weekDecks = allDecks.filter {
$0.courseName == course && $0.weekNumber == week && !$0.isReversed
}
let deckIds = weekDecks.map(\.id) let deckIds = weekDecks.map(\.id)
var cards: [VocabCard] = [] var cards: [VocabCard] = []
for deckId in deckIds { for deckId in deckIds {
@@ -221,8 +228,12 @@ struct WeekTestView: View {
} }
private func loadResults() { private func loadResults() {
let course = courseName
let week = weekNumber
let descriptor = FetchDescriptor<TestResult>( let descriptor = FetchDescriptor<TestResult>(
predicate: #Predicate<TestResult> { $0.weekNumber == weekNumber }, predicate: #Predicate<TestResult> {
$0.courseName == course && $0.weekNumber == week
},
sortBy: [SortDescriptor(\TestResult.dateTaken, order: .reverse)] sortBy: [SortDescriptor(\TestResult.dateTaken, order: .reverse)]
) )
weekResults = (try? cloudModelContext.fetch(descriptor)) ?? [] weekResults = (try? cloudModelContext.fetch(descriptor)) ?? []
@@ -237,7 +248,7 @@ struct WeekTestView: View {
#Preview { #Preview {
NavigationStack { NavigationStack {
WeekTestView(weekNumber: 1) WeekTestView(courseName: "LanGo Spanish | Beginner I", weekNumber: 1)
} }
.modelContainer(for: [TestResult.self, CourseDeck.self, VocabCard.self], inMemory: true) .modelContainer(for: [TestResult.self, CourseDeck.self, VocabCard.self], inMemory: true)
} }

View File

@@ -8,6 +8,7 @@ struct DashboardView: View {
@State private var dailyLogs: [DailyLog] = [] @State private var dailyLogs: [DailyLog] = []
@State private var testResults: [TestResult] = [] @State private var testResults: [TestResult] = []
@State private var reviewCards: [ReviewCard] = [] @State private var reviewCards: [ReviewCard] = []
@State private var showingSettings = false
private var cloudModelContext: ModelContext { cloudModelContextProvider() } private var cloudModelContext: ModelContext { cloudModelContextProvider() }
@@ -33,6 +34,19 @@ struct DashboardView: View {
.adaptiveContainer(maxWidth: 800) .adaptiveContainer(maxWidth: 800)
} }
.navigationTitle("Dashboard") .navigationTitle("Dashboard")
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button {
showingSettings = true
} label: {
Image(systemName: "gearshape")
}
.accessibilityLabel("Settings")
}
}
.sheet(isPresented: $showingSettings) {
SettingsView()
}
.onAppear(perform: loadData) .onAppear(perform: loadData)
} }
} }

View File

@@ -18,9 +18,6 @@ struct MainTabView: View {
Tab("Course", systemImage: "list.clipboard") { Tab("Course", systemImage: "list.clipboard") {
CourseView() CourseView()
} }
Tab("Settings", systemImage: "gearshape") {
SettingsView()
}
} }
} }
} }