From 437ed913cdce57ff3793aa993a1833e2523cfad1 Mon Sep 17 00:00:00 2001 From: Nguyen Ngo Date: Sun, 17 May 2026 11:21:16 -0400 Subject: [PATCH] 05/17 Fix bugs 4 --- .../Views/Dashboard/DashboardView.swift | 50 +++++++++++++------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/JanitorialQC/Views/Dashboard/DashboardView.swift b/JanitorialQC/Views/Dashboard/DashboardView.swift index 0466b0b..8e64501 100644 --- a/JanitorialQC/Views/Dashboard/DashboardView.swift +++ b/JanitorialQC/Views/Dashboard/DashboardView.swift @@ -986,17 +986,17 @@ struct SettingsView: View { @EnvironmentObject private var auth: AuthManager @EnvironmentObject private var sync: SyncManager @Environment(\.modelContext) private var context - + @State private var showClearCacheAlert = false @State private var cacheCleared = false - + var body: some View { List { Section("Account") { LabeledContent("Username", value: auth.currentUsername) LabeledContent("Role", value: auth.currentUserRole.capitalized) } - + Section("Sync") { Button { Task { await sync.triggerSync() } @@ -1004,17 +1004,17 @@ struct SettingsView: View { Label("Sync Now", systemImage: "arrow.clockwise") } .disabled(!sync.isOnline || sync.isSyncing) - + if let error = sync.syncError { Text(error).font(.caption).foregroundStyle(.red) } - + if let lastSync = sync.lastSyncAt { LabeledContent("Last Sync", - value: lastSync.formatted(date: .abbreviated, time: .shortened)) + value: lastSync.formatted(date: .abbreviated, time: .shortened)) } } - + Section("Cache") { Button { showClearCacheAlert = true @@ -1025,22 +1025,29 @@ struct SettingsView: View { Text("Clears locally cached facilities, areas, and templates. Your pending inspections are not affected. Data will re-sync on the next connection.") .font(.caption) .foregroundStyle(.secondary) - + if cacheCleared { Label("Cache cleared.", systemImage: "checkmark.circle.fill") .foregroundStyle(.green) .font(.callout) } } - + Section { Button(role: .destructive) { - Task { await auth.logout() } + Task { + // Clear server-pulled issues before logging out so stale + // records from a previous server/domain don't persist into + // the next session. Device-created pending issues are preserved. + clearServerPulledData() + sync.resetNotificationPoller() + await auth.logout() + } } label: { Label("Log Out", systemImage: "rectangle.portrait.and.arrow.right") } } - + Section("App Info") { LabeledContent("Version", value: "\(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0") (\(Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "1"))") LabeledContent("Server", value: Constants.baseURL) @@ -1054,23 +1061,36 @@ struct SettingsView: View { Text("Facilities, areas, and templates will be removed from local storage and re-downloaded on the next sync. Pending inspections are not affected.") } } - + private func clearCache() { // Delete only reference data — never touch LocalInspection, LocalIssue, PendingPhoto let facilities = (try? context.fetch(FetchDescriptor())) ?? [] let templates = (try? context.fetch(FetchDescriptor())) ?? [] let areas = (try? context.fetch(FetchDescriptor())) ?? [] - + facilities.forEach { context.delete($0) } templates.forEach { context.delete($0) } areas.forEach { context.delete($0) } - + try? context.save() cacheCleared = true - + // Re-pull immediately if online if sync.isOnline { Task { await sync.pullReferenceData() } } } + + /// Delete all server-pulled LocalIssue records (syncStatus == "synced" and + /// inspectionLocalId == ""). These are issues fetched from the server and + /// reconciled by pullAssignedIssues — they must be cleared on logout so + /// stale records from a previous server domain or user session don't persist. + /// Device-created issues (inspectionLocalId != "") are never touched. + private func clearServerPulledData() { + let allIssues = (try? context.fetch(FetchDescriptor())) ?? [] + allIssues + .filter { $0.syncStatus == "synced" && $0.inspectionLocalId == "" } + .forEach { context.delete($0) } + try? context.save() + } }