From a2b81a244bb75e89493e2d59f6b08193fd074d15 Mon Sep 17 00:00:00 2001 From: Trey t Date: Fri, 21 Nov 2025 07:58:01 -0600 Subject: [PATCH] Implement custom 5-color design system across entire iOS app MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apply consistent branding colors (BlueGreen, Cerulean, BrightAmber, PrimaryScarlet, cream backgrounds) to all screens, components, buttons, icons, and text throughout the app. Update all Form/List views with proper list row backgrounds to ensure visual consistency with card-based layouts. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../AppIcon.appiconset/Contents.json | 2 +- .../AppIcon.appiconset/icon.png | Bin 32665 -> 0 bytes .../AppIcon.appiconset/logo_primary.png | Bin 0 -> 21764 bytes .../Assets.xcassets/Colors/Contents.json | 6 ++ .../Semantic/Accent.colorset/Contents.json | 38 ++++++++ .../BackgroundPrimary.colorset/Contents.json | 38 ++++++++ .../Contents.json | 38 ++++++++ .../Colors/Semantic/Contents.json | 6 ++ .../Semantic/Error.colorset/Contents.json | 38 ++++++++ .../Semantic/Primary.colorset/Contents.json | 38 ++++++++ .../Semantic/Secondary.colorset/Contents.json | 38 ++++++++ .../TextOnPrimary.colorset/Contents.json | 38 ++++++++ .../TextPrimary.colorset/Contents.json | 38 ++++++++ .../TextSecondary.colorset/Contents.json | 38 ++++++++ .../iosApp/Components/TaskSummaryCard.swift | 9 +- iosApp/iosApp/Contractor/ContractorCard.swift | 22 ++--- .../Contractor/ContractorDetailView.swift | 50 +++++------ .../Contractor/ContractorFormSheet.swift | 56 +++++++----- .../Contractor/ContractorsListView.swift | 24 ++--- iosApp/iosApp/Design/DesignSystem.swift | 85 ++++++++++-------- .../Documents/Components/DocumentCard.swift | 20 ++--- .../Documents/Components/EmptyStateView.swift | 6 +- .../Documents/Components/WarrantyCard.swift | 28 +++--- .../iosApp/Documents/DocumentFormView.swift | 28 ++++-- .../Documents/DocumentsWarrantiesView.swift | 8 +- iosApp/iosApp/Login/LoginView.swift | 56 ++++++------ iosApp/iosApp/MainTabView.swift | 1 + .../PasswordReset/ForgotPasswordView.swift | 21 +++-- .../PasswordReset/ResetPasswordView.swift | 32 ++++--- .../PasswordReset/VerifyResetCodeView.swift | 30 ++++--- iosApp/iosApp/Profile/ProfileTabView.swift | 18 ++-- iosApp/iosApp/Profile/ProfileView.swift | 21 +++-- iosApp/iosApp/Register/RegisterView.swift | 15 +++- .../iosApp/Residence/JoinResidenceView.swift | 10 ++- iosApp/iosApp/Residence/ManageUsersView.swift | 5 +- .../Residence/ResidenceDetailView.swift | 10 +-- .../iosApp/Residence/ResidencesListView.swift | 12 +-- iosApp/iosApp/ResidenceFormView.swift | 14 ++- iosApp/iosApp/Subviews/Auth/LoginHeader.swift | 3 +- .../iosApp/Subviews/Auth/RegisterHeader.swift | 5 +- .../Subviews/Common/ErrorMessageView.swift | 8 +- iosApp/iosApp/Subviews/Common/ErrorView.swift | 8 +- .../Subviews/Common/HomeNavigationCard.swift | 14 +-- .../Subviews/Common/ImageThumbnailView.swift | 2 +- .../iosApp/Subviews/Common/OverviewCard.swift | 14 +-- iosApp/iosApp/Subviews/Common/StatView.swift | 6 +- .../Residence/EmptyResidencesView.swift | 5 +- .../Residence/PropertyDetailItem.swift | 5 +- .../Residence/PropertyHeaderCard.swift | 13 +-- .../Subviews/Residence/ResidenceCard.swift | 28 +++--- .../Subviews/Residence/ShareCodeCard.swift | 10 +-- .../Subviews/Residence/SummaryCard.swift | 3 +- .../Subviews/Residence/SummaryStatView.swift | 5 +- .../Subviews/Residence/TaskStatChip.swift | 2 +- .../Subviews/Residence/UserListItem.swift | 13 +-- .../Subviews/Task/CompletionCardView.swift | 24 ++--- .../Subviews/Task/DynamicTaskCard.swift | 20 ++--- .../Subviews/Task/DynamicTaskColumnView.swift | 6 +- .../iosApp/Subviews/Task/EmptyTasksView.swift | 6 +- .../Subviews/Task/PhotoViewerSheet.swift | 14 +-- .../iosApp/Subviews/Task/PriorityBadge.swift | 10 +-- iosApp/iosApp/Subviews/Task/StatusBadge.swift | 12 +-- .../Subviews/Task/TaskActionButtons.swift | 8 +- iosApp/iosApp/Subviews/Task/TaskCard.swift | 56 ++++++------ iosApp/iosApp/Subviews/Task/TaskPill.swift | 6 +- .../iosApp/Subviews/Task/TasksSection.swift | 1 + iosApp/iosApp/Task/AllTasksView.swift | 17 ++-- iosApp/iosApp/Task/CompleteTaskView.swift | 23 +++-- iosApp/iosApp/Task/TaskFormView.swift | 30 ++++--- .../iosApp/VerifyEmail/VerifyEmailView.swift | 23 +++-- 70 files changed, 920 insertions(+), 417 deletions(-) delete mode 100644 iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/icon.png create mode 100644 iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/logo_primary.png create mode 100644 iosApp/iosApp/Assets.xcassets/Colors/Contents.json create mode 100644 iosApp/iosApp/Assets.xcassets/Colors/Semantic/Accent.colorset/Contents.json create mode 100644 iosApp/iosApp/Assets.xcassets/Colors/Semantic/BackgroundPrimary.colorset/Contents.json create mode 100644 iosApp/iosApp/Assets.xcassets/Colors/Semantic/BackgroundSecondary.colorset/Contents.json create mode 100644 iosApp/iosApp/Assets.xcassets/Colors/Semantic/Contents.json create mode 100644 iosApp/iosApp/Assets.xcassets/Colors/Semantic/Error.colorset/Contents.json create mode 100644 iosApp/iosApp/Assets.xcassets/Colors/Semantic/Primary.colorset/Contents.json create mode 100644 iosApp/iosApp/Assets.xcassets/Colors/Semantic/Secondary.colorset/Contents.json create mode 100644 iosApp/iosApp/Assets.xcassets/Colors/Semantic/TextOnPrimary.colorset/Contents.json create mode 100644 iosApp/iosApp/Assets.xcassets/Colors/Semantic/TextPrimary.colorset/Contents.json create mode 100644 iosApp/iosApp/Assets.xcassets/Colors/Semantic/TextSecondary.colorset/Contents.json diff --git a/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json index c68da6c..3cad146 100644 --- a/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "icon.png", + "filename" : "logo_primary.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" diff --git a/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/icon.png b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/icon.png deleted file mode 100644 index b1e6f70c8ef1353c23e2ecd2ab1bafd51a362df1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32665 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�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 S%*G5}^e4 0 { Label(String(format: "%.1f", rating.doubleValue), systemImage: "star.fill") .font(.caption.weight(.medium)) - .foregroundColor(.orange) + .foregroundColor(Color.appAccent) } // Task count if contractor.taskCount > 0 { Label("\(contractor.taskCount) tasks", systemImage: "checkmark.circle") .font(.caption.weight(.medium)) - .foregroundColor(.green) + .foregroundColor(Color.appPrimary) } } } @@ -73,17 +73,17 @@ struct ContractorCard: View { Button(action: onToggleFavorite) { Image(systemName: contractor.isFavorite ? "star.fill" : "star") .font(.title3) - .foregroundColor(contractor.isFavorite ? .orange : Color(.tertiaryLabel)) + .foregroundColor(contractor.isFavorite ? Color.appAccent : Color.appTextSecondary.opacity(0.7)) } .buttonStyle(PlainButtonStyle()) // Chevron Image(systemName: "chevron.right") .font(.caption) - .foregroundColor(Color(.tertiaryLabel)) + .foregroundColor(Color.appTextSecondary.opacity(0.7)) } .padding(AppSpacing.md) - .background(Color(.secondarySystemGroupedBackground)) + .background(Color.appBackgroundSecondary) .cornerRadius(AppRadius.lg) .shadow(color: AppShadow.sm.color, radius: AppShadow.sm.radius, x: AppShadow.sm.x, y: AppShadow.sm.y) } diff --git a/iosApp/iosApp/Contractor/ContractorDetailView.swift b/iosApp/iosApp/Contractor/ContractorDetailView.swift index ccd1b7b..10b2fe7 100644 --- a/iosApp/iosApp/Contractor/ContractorDetailView.swift +++ b/iosApp/iosApp/Contractor/ContractorDetailView.swift @@ -12,7 +12,7 @@ struct ContractorDetailView: View { var body: some View { ZStack { - Color(.systemGroupedBackground).ignoresSafeArea() + Color.appBackgroundPrimary.ignoresSafeArea() if viewModel.isLoading { ProgressView() @@ -29,24 +29,24 @@ struct ContractorDetailView: View { // Avatar ZStack { Circle() - .fill(.blue.opacity(0.1)) + .fill(Color.appPrimary.opacity(0.1)) .frame(width: 80, height: 80) Image(systemName: "person.fill") .font(.system(size: 40)) - .foregroundColor(.blue) + .foregroundColor(Color.appPrimary) } // Name Text(contractor.name) .font(.title3.weight(.semibold)) - .foregroundColor(Color(.label)) + .foregroundColor(Color.appTextPrimary) // Company if let company = contractor.company { Text(company) .font(.title3.weight(.semibold)) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) } // Specialty Badge @@ -59,8 +59,8 @@ struct ContractorDetailView: View { } .padding(.horizontal, AppSpacing.sm) .padding(.vertical, AppSpacing.xxs) - .background(.blue.opacity(0.1)) - .foregroundColor(.blue) + .background(Color.appPrimary.opacity(0.1)) + .foregroundColor(Color.appPrimary) .cornerRadius(AppRadius.full) } @@ -69,43 +69,43 @@ struct ContractorDetailView: View { HStack(spacing: AppSpacing.xxs) { ForEach(0..<5) { index in Image(systemName: index < Int(rating.doubleValue) ? "star.fill" : "star") - .foregroundColor(.orange) + .foregroundColor(Color.appAccent) .font(.caption) } Text(String(format: "%.1f", rating.doubleValue)) .font(.title3.weight(.semibold)) - .foregroundColor(Color(.label)) + .foregroundColor(Color.appTextPrimary) } if contractor.taskCount > 0 { Text("\(contractor.taskCount) completed tasks") .font(.callout) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) } } } .padding(AppSpacing.lg) .frame(maxWidth: .infinity) - .background(Color(.secondarySystemGroupedBackground)) + .background(Color.appBackgroundSecondary) .cornerRadius(AppRadius.lg) .shadow(color: AppShadow.sm.color, radius: AppShadow.sm.radius, x: AppShadow.sm.x, y: AppShadow.sm.y) // Contact Information DetailSection(title: "Contact Information") { if let phone = contractor.phone { - DetailRow(icon: "phone", label: "Phone", value: phone, iconColor: .blue) + DetailRow(icon: "phone", label: "Phone", value: phone, iconColor: Color.appPrimary) } if let email = contractor.email { - DetailRow(icon: "envelope", label: "Email", value: email, iconColor: .purple) + DetailRow(icon: "envelope", label: "Email", value: email, iconColor: Color.appPrimary) } if let secondaryPhone = contractor.secondaryPhone { - DetailRow(icon: "phone", label: "Secondary Phone", value: secondaryPhone, iconColor: .green) + DetailRow(icon: "phone", label: "Secondary Phone", value: secondaryPhone, iconColor: Color.appAccent) } if let website = contractor.website { - DetailRow(icon: "globe", label: "Website", value: website, iconColor: .orange) + DetailRow(icon: "globe", label: "Website", value: website, iconColor: Color.appAccent) } } @@ -113,7 +113,7 @@ struct ContractorDetailView: View { if contractor.licenseNumber != nil { DetailSection(title: "Business Details") { if let licenseNumber = contractor.licenseNumber { - DetailRow(icon: "doc.badge", label: "License Number", value: licenseNumber, iconColor: .blue) + DetailRow(icon: "doc.badge", label: "License Number", value: licenseNumber, iconColor: Color.appPrimary) } } } @@ -132,7 +132,7 @@ struct ContractorDetailView: View { icon: "mappin.circle", label: "Location", value: addressComponents.joined(separator: "\n"), - iconColor: .red + iconColor: Color.appError ) } } @@ -143,7 +143,7 @@ struct ContractorDetailView: View { DetailSection(title: "Notes") { Text(notes) .font(.body) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) .frame(maxWidth: .infinity, alignment: .leading) .padding(AppSpacing.md) } @@ -153,11 +153,11 @@ struct ContractorDetailView: View { DetailSection(title: "Task History") { HStack { Image(systemName: "checkmark.circle") - .foregroundColor(.green) + .foregroundColor(Color.appAccent) Spacer() Text("\(contractor.taskCount) completed tasks") .font(.body) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) } .padding(AppSpacing.md) } @@ -191,7 +191,7 @@ struct ContractorDetailView: View { } } label: { Image(systemName: "ellipsis.circle") - .foregroundColor(.blue) + .foregroundColor(Color.appPrimary) } } } @@ -243,13 +243,13 @@ struct DetailSection: View { VStack(alignment: .leading, spacing: AppSpacing.sm) { Text(title) .font(.headline) - .foregroundColor(Color(.label)) + .foregroundColor(Color.appTextPrimary) .padding(.horizontal, AppSpacing.md) VStack(spacing: 0) { content() } - .background(Color(.secondarySystemGroupedBackground)) + .background(Color.appBackgroundSecondary) .cornerRadius(AppRadius.lg) .shadow(color: AppShadow.sm.color, radius: AppShadow.sm.radius, x: AppShadow.sm.x, y: AppShadow.sm.y) } @@ -272,11 +272,11 @@ struct DetailRow: View { VStack(alignment: .leading, spacing: AppSpacing.xxs) { Text(label) .font(.caption.weight(.medium)) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) Text(value) .font(.body) - .foregroundColor(Color(.label)) + .foregroundColor(Color.appTextPrimary) } Spacer() diff --git a/iosApp/iosApp/Contractor/ContractorFormSheet.swift b/iosApp/iosApp/Contractor/ContractorFormSheet.swift index 9577424..a1c21de 100644 --- a/iosApp/iosApp/Contractor/ContractorFormSheet.swift +++ b/iosApp/iosApp/Contractor/ContractorFormSheet.swift @@ -52,7 +52,7 @@ struct ContractorFormSheet: View { Section { HStack { Image(systemName: "person") - .foregroundColor(.blue) + .foregroundColor(Color.appPrimary) .frame(width: 24) TextField("Name", text: $name) .focused($focusedField, equals: .name) @@ -60,7 +60,7 @@ struct ContractorFormSheet: View { HStack { Image(systemName: "building.2") - .foregroundColor(.purple) + .foregroundColor(Color.appPrimary) .frame(width: 24) TextField("Company", text: $company) .focused($focusedField, equals: .company) @@ -70,14 +70,15 @@ struct ContractorFormSheet: View { } footer: { Text("Required: Name") .font(.caption) - .foregroundColor(.red) + .foregroundColor(Color.appError) } + .listRowBackground(Color.appBackgroundSecondary) // Contact Information Section { HStack { Image(systemName: "phone.fill") - .foregroundColor(.green) + .foregroundColor(Color.appPrimary) .frame(width: 24) TextField("Phone", text: $phone) .keyboardType(.phonePad) @@ -86,7 +87,7 @@ struct ContractorFormSheet: View { HStack { Image(systemName: "envelope.fill") - .foregroundColor(.orange) + .foregroundColor(Color.appAccent) .frame(width: 24) TextField("Email", text: $email) .keyboardType(.emailAddress) @@ -97,7 +98,7 @@ struct ContractorFormSheet: View { HStack { Image(systemName: "phone.badge.plus") - .foregroundColor(.green) + .foregroundColor(Color.appPrimary) .frame(width: 24) TextField("Secondary Phone", text: $secondaryPhone) .keyboardType(.phonePad) @@ -106,28 +107,29 @@ struct ContractorFormSheet: View { } header: { Text("Contact Information") } footer: { - + } + .listRowBackground(Color.appBackgroundSecondary) // Business Details Section { Button(action: { showingSpecialtyPicker = true }) { HStack { Image(systemName: "wrench.and.screwdriver") - .foregroundColor(.blue) + .foregroundColor(Color.appPrimary) .frame(width: 24) Text(specialty.isEmpty ? "Specialty" : specialty) - .foregroundColor(specialty.isEmpty ? Color(.placeholderText) : Color(.label)) + .foregroundColor(specialty.isEmpty ? Color.appTextSecondary.opacity(0.5) : Color.appTextPrimary) Spacer() Image(systemName: "chevron.down") .font(.caption) - .foregroundColor(Color(.tertiaryLabel)) + .foregroundColor(Color.appTextSecondary.opacity(0.7)) } } HStack { Image(systemName: "doc.badge") - .foregroundColor(.purple) + .foregroundColor(Color.appPrimary) .frame(width: 24) TextField("License Number", text: $licenseNumber) .focused($focusedField, equals: .licenseNumber) @@ -135,7 +137,7 @@ struct ContractorFormSheet: View { HStack { Image(systemName: "globe") - .foregroundColor(.blue) + .foregroundColor(Color.appAccent) .frame(width: 24) TextField("Website", text: $website) .keyboardType(.URL) @@ -146,12 +148,13 @@ struct ContractorFormSheet: View { } header: { Text("Business Details") } + .listRowBackground(Color.appBackgroundSecondary) // Address Section { HStack { Image(systemName: "location.fill") - .foregroundColor(.red) + .foregroundColor(Color.appError) .frame(width: 24) TextField("Street Address", text: $address) .focused($focusedField, equals: .address) @@ -159,7 +162,7 @@ struct ContractorFormSheet: View { HStack { Image(systemName: "building.2.crop.circle") - .foregroundColor(.blue) + .foregroundColor(Color.appPrimary) .frame(width: 24) TextField("City", text: $city) .focused($focusedField, equals: .city) @@ -168,7 +171,7 @@ struct ContractorFormSheet: View { HStack(spacing: AppSpacing.sm) { HStack { Image(systemName: "map") - .foregroundColor(.green) + .foregroundColor(Color.appAccent) .frame(width: 24) TextField("State", text: $state) .focused($focusedField, equals: .state) @@ -185,12 +188,13 @@ struct ContractorFormSheet: View { } header: { Text("Address") } + .listRowBackground(Color.appBackgroundSecondary) // Notes Section { HStack(alignment: .top) { Image(systemName: "note.text") - .foregroundColor(.orange) + .foregroundColor(Color.appAccent) .frame(width: 24) .padding(.top, 8) @@ -204,29 +208,35 @@ struct ContractorFormSheet: View { Text("Private notes about this contractor") .font(.caption) } + .listRowBackground(Color.appBackgroundSecondary) // Favorite Section { Toggle(isOn: $isFavorite) { Label("Mark as Favorite", systemImage: "star.fill") - .foregroundColor(isFavorite ? .orange : Color(.label)) + .foregroundColor(isFavorite ? Color.appAccent : Color.appTextPrimary) } - .tint(.orange) + .tint(Color.appAccent) } + .listRowBackground(Color.appBackgroundSecondary) // Error Message if let error = viewModel.errorMessage { Section { HStack { Image(systemName: "exclamationmark.triangle.fill") - .foregroundColor(.red) + .foregroundColor(Color.appError) Text(error) .font(.callout) - .foregroundColor(.red) + .foregroundColor(Color.appError) } } + .listRowBackground(Color.appBackgroundSecondary) } } + .listStyle(.plain) + .scrollContentBackground(.hidden) + .background(Color.appBackgroundPrimary) .navigationTitle(contractor == nil ? "Add Contractor" : "Edit Contractor") .navigationBarTitleDisplayMode(.inline) .toolbar { @@ -258,16 +268,18 @@ struct ContractorFormSheet: View { }) { HStack { Text(spec) - .foregroundColor(Color(.label)) + .foregroundColor(Color.appTextPrimary) Spacer() if specialty == spec { Image(systemName: "checkmark") - .foregroundColor(.blue) + .foregroundColor(Color.appPrimary) } } } } } + .scrollContentBackground(.hidden) + .background(Color.appBackgroundPrimary) .navigationTitle("Select Specialty") .navigationBarTitleDisplayMode(.inline) .toolbar { diff --git a/iosApp/iosApp/Contractor/ContractorsListView.swift b/iosApp/iosApp/Contractor/ContractorsListView.swift index aa2bf95..a9827f7 100644 --- a/iosApp/iosApp/Contractor/ContractorsListView.swift +++ b/iosApp/iosApp/Contractor/ContractorsListView.swift @@ -26,7 +26,7 @@ struct ContractorsListView: View { var body: some View { ZStack { - Color(.systemGroupedBackground).ignoresSafeArea() + Color.appBackgroundPrimary.ignoresSafeArea() VStack(spacing: 0) { // Search Bar @@ -115,7 +115,7 @@ struct ContractorsListView: View { loadContractors() }) { Image(systemName: showFavoritesOnly ? "star.fill" : "star") - .foregroundColor(showFavoritesOnly ? .orange : Color(.secondaryLabel)) + .foregroundColor(showFavoritesOnly ? Color.appAccent : Color.appTextSecondary) } // Specialty Filter @@ -139,14 +139,14 @@ struct ContractorsListView: View { } } label: { Image(systemName: "line.3.horizontal.decrease.circle") - .foregroundColor(selectedSpecialty != nil ? .blue : Color(.secondaryLabel)) + .foregroundColor(selectedSpecialty != nil ? Color.appPrimary : Color.appTextSecondary) } // Add Button Button(action: { showingAddSheet = true }) { Image(systemName: "plus.circle.fill") .font(.title2) - .foregroundColor(.blue) + .foregroundColor(Color.appPrimary) } .accessibilityIdentifier(AccessibilityIdentifiers.Contractor.addButton) } @@ -206,7 +206,7 @@ struct SearchBar: View { var body: some View { HStack(spacing: AppSpacing.sm) { Image(systemName: "magnifyingglass") - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) TextField(placeholder, text: $text) .font(.body) @@ -214,12 +214,12 @@ struct SearchBar: View { if !text.isEmpty { Button(action: { text = "" }) { Image(systemName: "xmark.circle.fill") - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) } } } .padding(AppSpacing.sm) - .background(Color(.secondarySystemGroupedBackground)) + .background(Color.appBackgroundSecondary) .cornerRadius(AppRadius.md) .shadow(color: AppShadow.sm.color, radius: AppShadow.sm.radius, x: AppShadow.sm.x, y: AppShadow.sm.y) } @@ -247,8 +247,8 @@ struct FilterChip: View { } .padding(.horizontal, AppSpacing.sm) .padding(.vertical, AppSpacing.xxs) - .background(.blue.opacity(0.1)) - .foregroundColor(.blue) + .background(Color.appPrimary.opacity(0.1)) + .foregroundColor(Color.appPrimary) .cornerRadius(AppRadius.full) } } @@ -261,16 +261,16 @@ struct EmptyContractorsView: View { VStack(spacing: AppSpacing.md) { Image(systemName: "person.badge.plus") .font(.system(size: 64)) - .foregroundColor(Color(.tertiaryLabel)) + .foregroundColor(Color.appTextSecondary.opacity(0.7)) Text(hasFilters ? "No contractors found" : "No contractors yet") .font(.title3.weight(.semibold)) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) if !hasFilters { Text("Add your first contractor to get started") .font(.callout) - .foregroundColor(Color(.tertiaryLabel)) + .foregroundColor(Color.appTextSecondary.opacity(0.7)) } } .padding(AppSpacing.xl) diff --git a/iosApp/iosApp/Design/DesignSystem.swift b/iosApp/iosApp/Design/DesignSystem.swift index f60ea54..2ee9ea6 100644 --- a/iosApp/iosApp/Design/DesignSystem.swift +++ b/iosApp/iosApp/Design/DesignSystem.swift @@ -3,6 +3,49 @@ import SwiftUI // MARK: - Design System // Modern, sleek design system for MyCrib with Light and Dark mode support +// MARK: - Colors + +extension Color { + // MARK: - Semantic Colors (Use These in UI) + static let appPrimary = Color("Primary") + static let appSecondary = Color("Secondary") + static let appAccent = Color("Accent") + static let appBackgroundPrimary = Color("BackgroundPrimary") + static let appBackgroundSecondary = Color("BackgroundSecondary") + static let appError = Color("Error") + static let appTextPrimary = Color("TextPrimary") + static let appTextSecondary = Color("TextSecondary") + static let appTextOnPrimary = Color("TextOnPrimary") + + // MARK: - Hex Support + init?(hex: String) { + let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted) + var int: UInt64 = 0 + Scanner(string: hex).scanHexInt64(&int) + let a, r, g, b: UInt64 + switch hex.count { + case 3: // RGB (12-bit) + (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17) + case 6: // RGB (24-bit) + (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF) + case 8: // ARGB (32-bit) + (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF) + default: + return nil + } + + self.init( + .sRGB, + red: Double(r) / 255, + green: Double(g) / 255, + blue: Double(b) / 255, + opacity: Double(a) / 255 + ) + } +} + +// MARK: - Spacing + struct AppSpacing { static let xxs: CGFloat = 4 static let xs: CGFloat = 8 @@ -53,7 +96,7 @@ struct CardStyle: ViewModifier { func body(content: Content) -> some View { content - .background(Color(.secondarySystemGroupedBackground)) + .background(Color.appBackgroundSecondary) .cornerRadius(AppRadius.lg) .shadow(color: shadow.color, radius: shadow.radius, x: shadow.x, y: shadow.y) } @@ -65,11 +108,11 @@ struct PrimaryButtonStyle: ButtonStyle { func makeBody(configuration: Configuration) -> some View { configuration.label .font(.headline) - .foregroundColor(.white) + .foregroundColor(.appTextOnPrimary) .frame(maxWidth: .infinity) .frame(height: 56) .background( - configuration.isPressed ? .blue : .blue + configuration.isPressed ? Color.appPrimary.opacity(0.8) : Color.appPrimary ) .cornerRadius(AppRadius.md) .scaleEffect(configuration.isPressed ? 0.98 : 1.0) @@ -81,10 +124,10 @@ struct SecondaryButtonStyle: ButtonStyle { func makeBody(configuration: Configuration) -> some View { configuration.label .font(.headline) - .foregroundColor(.blue) + .foregroundColor(.appPrimary) .frame(maxWidth: .infinity) .frame(height: 56) - .background(Color(.tertiarySystemGroupedBackground)) + .background(Color.appPrimary.opacity(0.1)) .cornerRadius(AppRadius.md) .scaleEffect(configuration.isPressed ? 0.98 : 1.0) .animation(.easeInOut(duration: 0.1), value: configuration.isPressed) @@ -95,11 +138,11 @@ struct TextFieldStyle: ViewModifier { func body(content: Content) -> some View { content .padding(AppSpacing.md) - .background(Color(.tertiarySystemGroupedBackground)) + .background(Color.appBackgroundSecondary) .cornerRadius(AppRadius.md) .overlay( RoundedRectangle(cornerRadius: AppRadius.md) - .stroke(Color(.opaqueSeparator), lineWidth: 1) + .stroke(Color.appTextSecondary.opacity(0.3), lineWidth: 1) ) } } @@ -116,31 +159,3 @@ extension View { } } -// MARK: - Color Extension for Hex Support - -extension Color { - init?(hex: String) { - let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted) - var int: UInt64 = 0 - Scanner(string: hex).scanHexInt64(&int) - let a, r, g, b: UInt64 - switch hex.count { - case 3: // RGB (12-bit) - (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17) - case 6: // RGB (24-bit) - (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF) - case 8: // ARGB (32-bit) - (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF) - default: - return nil - } - - self.init( - .sRGB, - red: Double(r) / 255, - green: Double(g) / 255, - blue: Double(b) / 255, - opacity: Double(a) / 255 - ) - } -} diff --git a/iosApp/iosApp/Documents/Components/DocumentCard.swift b/iosApp/iosApp/Documents/Components/DocumentCard.swift index 782673b..be96b9a 100644 --- a/iosApp/iosApp/Documents/Components/DocumentCard.swift +++ b/iosApp/iosApp/Documents/Components/DocumentCard.swift @@ -6,11 +6,11 @@ struct DocumentCard: View { var typeColor: Color { switch document.documentType { - case "warranty": return .blue - case "manual": return .purple - case "receipt": return .green - case "inspection": return .orange - default: return .gray + case "warranty": return Color.appPrimary + case "manual": return Color.appSecondary + case "receipt": return Color.appAccent + case "inspection": return Color.appAccent + default: return Color.appTextSecondary } } @@ -41,13 +41,13 @@ struct DocumentCard: View { Text(document.title) .font(.title3.weight(.semibold)) .fontWeight(.bold) - .foregroundColor(Color(.label)) + .foregroundColor(Color.appTextPrimary) .lineLimit(1) if let description = document.description_, !description.isEmpty { Text(description) .font(.callout) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) .lineLimit(2) } @@ -63,7 +63,7 @@ struct DocumentCard: View { if let fileSize = document.fileSize { Text(formatFileSize(Int(fileSize))) .font(.caption.weight(.medium)) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) } } } @@ -71,11 +71,11 @@ struct DocumentCard: View { Spacer() Image(systemName: "chevron.right") - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) .font(.system(size: 14)) } .padding(AppSpacing.md) - .background(Color(.secondarySystemGroupedBackground)) + .background(Color.appBackgroundSecondary) .cornerRadius(AppRadius.md) .shadow(color: Color.black.opacity(0.05), radius: 2, x: 0, y: 1) } diff --git a/iosApp/iosApp/Documents/Components/EmptyStateView.swift b/iosApp/iosApp/Documents/Components/EmptyStateView.swift index eed9b74..c7c4edb 100644 --- a/iosApp/iosApp/Documents/Components/EmptyStateView.swift +++ b/iosApp/iosApp/Documents/Components/EmptyStateView.swift @@ -9,15 +9,15 @@ struct EmptyStateView: View { VStack(spacing: AppSpacing.md) { Image(systemName: icon) .font(.system(size: 64)) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) Text(title) .font(.title3.weight(.semibold)) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) Text(message) .font(.body) - .foregroundColor(Color(.tertiaryLabel)) + .foregroundColor(Color.appTextSecondary.opacity(0.7)) .multilineTextAlignment(.center) } .padding(AppSpacing.lg) diff --git a/iosApp/iosApp/Documents/Components/WarrantyCard.swift b/iosApp/iosApp/Documents/Components/WarrantyCard.swift index a923e73..851ead7 100644 --- a/iosApp/iosApp/Documents/Components/WarrantyCard.swift +++ b/iosApp/iosApp/Documents/Components/WarrantyCard.swift @@ -9,11 +9,11 @@ struct WarrantyCard: View { } var statusColor: Color { - if !document.isActive { return .gray } - if daysUntilExpiration < 0 { return .red } - if daysUntilExpiration < 30 { return .orange } - if daysUntilExpiration < 90 { return .yellow } - return .green + if !document.isActive { return Color.appTextSecondary } + if daysUntilExpiration < 0 { return Color.appError } + if daysUntilExpiration < 30 { return Color.appAccent } + if daysUntilExpiration < 90 { return Color.appAccent.opacity(0.7) } + return Color.appPrimary } var statusText: String { @@ -31,11 +31,11 @@ struct WarrantyCard: View { Text(document.title) .font(.title3.weight(.semibold)) .fontWeight(.bold) - .foregroundColor(Color(.label)) + .foregroundColor(Color.appTextPrimary) Text(document.itemName ?? "") .font(.body) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) } Spacer() @@ -58,11 +58,11 @@ struct WarrantyCard: View { VStack(alignment: .leading, spacing: 2) { Text("Provider") .font(.caption.weight(.medium)) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) Text(document.provider ?? "N/A") .font(.body) .fontWeight(.medium) - .foregroundColor(Color(.label)) + .foregroundColor(Color.appTextPrimary) } Spacer() @@ -70,11 +70,11 @@ struct WarrantyCard: View { VStack(alignment: .trailing, spacing: 2) { Text("Expires") .font(.caption.weight(.medium)) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) Text(document.endDate ?? "N/A") .font(.body) .fontWeight(.medium) - .foregroundColor(Color(.label)) + .foregroundColor(Color.appTextPrimary) } } @@ -88,15 +88,15 @@ struct WarrantyCard: View { if let category = document.category { Text(getCategoryDisplayName(category)) .font(.caption.weight(.medium)) - .foregroundColor(Color(hex: "374151")) + .foregroundColor(Color.appTextPrimary) .padding(.horizontal, 8) .padding(.vertical, 4) - .background(Color(hex: "E5E7EB")) + .background(Color.appBackgroundSecondary.opacity(0.8)) .cornerRadius(4) } } .padding(AppSpacing.md) - .background(Color(.secondarySystemGroupedBackground)) + .background(Color.appBackgroundSecondary) .cornerRadius(AppRadius.md) .shadow(color: Color.black.opacity(0.05), radius: 2, x: 0, y: 1) } diff --git a/iosApp/iosApp/Documents/DocumentFormView.swift b/iosApp/iosApp/Documents/DocumentFormView.swift index bfab175..b464ce7 100644 --- a/iosApp/iosApp/Documents/DocumentFormView.swift +++ b/iosApp/iosApp/Documents/DocumentFormView.swift @@ -119,7 +119,7 @@ struct DocumentFormView: View { if !itemNameError.isEmpty { Text(itemNameError) .font(.caption) - .foregroundColor(.red) + .foregroundColor(Color.appError) } TextField("Model Number (optional)", text: $modelNumber) @@ -129,7 +129,7 @@ struct DocumentFormView: View { if !providerError.isEmpty { Text(providerError) .font(.caption) - .foregroundColor(.red) + .foregroundColor(Color.appError) } TextField("Provider Contact (optional)", text: $providerContact) @@ -138,8 +138,9 @@ struct DocumentFormView: View { } footer: { Text("Required for warranties: Item Name and Provider") .font(.caption) - .foregroundColor(.red) + .foregroundColor(Color.appError) } + .listRowBackground(Color.appBackgroundSecondary) Section("Warranty Claims") { TextField("Claim Phone (optional)", text: $claimPhone) @@ -149,12 +150,14 @@ struct DocumentFormView: View { TextField("Claim Website (optional)", text: $claimWebsite) .keyboardType(.URL) } + .listRowBackground(Color.appBackgroundSecondary) Section("Warranty Dates") { TextField("Purchase Date (YYYY-MM-DD)", text: $purchaseDate) TextField("Warranty Start Date (YYYY-MM-DD)", text: $startDate) TextField("Warranty End Date (YYYY-MM-DD)", text: $endDate) } + .listRowBackground(Color.appBackgroundSecondary) } } @@ -181,6 +184,7 @@ struct DocumentFormView: View { .frame(height: 200) } } + .listRowBackground(Color.appBackgroundSecondary) } Section("Photos") { @@ -200,6 +204,7 @@ struct DocumentFormView: View { .foregroundColor(.secondary) } } + .listRowBackground(Color.appBackgroundSecondary) } var body: some View { @@ -207,6 +212,9 @@ struct DocumentFormView: View { Form { formContent } + .listStyle(.plain) + .scrollContentBackground(.hidden) + .background(Color.appBackgroundPrimary) .navigationTitle(isEditMode ? (isWarranty ? "Edit Warranty" : "Edit Document") : (isWarranty ? "Add Warranty" : "Add Document")) .navigationBarTitleDisplayMode(.inline) .toolbar { @@ -282,7 +290,7 @@ struct DocumentFormView: View { if !residenceError.isEmpty { Text(residenceError) .font(.caption) - .foregroundColor(.red) + .foregroundColor(Color.appError) } } } header: { @@ -290,8 +298,9 @@ struct DocumentFormView: View { } footer: { Text("Required") .font(.caption) - .foregroundColor(.red) + .foregroundColor(Color.appError) } + .listRowBackground(Color.appBackgroundSecondary) } // Document Type @@ -314,6 +323,7 @@ struct DocumentFormView: View { } } } + .listRowBackground(Color.appBackgroundSecondary) // Basic Information Section { @@ -321,7 +331,7 @@ struct DocumentFormView: View { if !titleError.isEmpty { Text(titleError) .font(.caption) - .foregroundColor(.red) + .foregroundColor(Color.appError) } TextField("Description (optional)", text: $description, axis: .vertical) @@ -331,8 +341,9 @@ struct DocumentFormView: View { } footer: { Text("Required: Title") .font(.caption) - .foregroundColor(.red) + .foregroundColor(Color.appError) } + .listRowBackground(Color.appBackgroundSecondary) // Warranty-specific fields warrantySection @@ -347,6 +358,7 @@ struct DocumentFormView: View { } } } + .listRowBackground(Color.appBackgroundSecondary) } // Additional Information @@ -356,12 +368,14 @@ struct DocumentFormView: View { TextField("Notes (optional)", text: $notes, axis: .vertical) .lineLimit(3...6) } + .listRowBackground(Color.appBackgroundSecondary) // Active Status (Edit mode only) if isEditMode { Section { Toggle("Active", isOn: $isActive) } + .listRowBackground(Color.appBackgroundSecondary) } // Photos diff --git a/iosApp/iosApp/Documents/DocumentsWarrantiesView.swift b/iosApp/iosApp/Documents/DocumentsWarrantiesView.swift index d01eac0..00278b4 100644 --- a/iosApp/iosApp/Documents/DocumentsWarrantiesView.swift +++ b/iosApp/iosApp/Documents/DocumentsWarrantiesView.swift @@ -28,7 +28,7 @@ struct DocumentsWarrantiesView: View { var body: some View { ZStack { - Color(.systemGroupedBackground).ignoresSafeArea() + Color.appBackgroundPrimary.ignoresSafeArea() VStack(spacing: 0) { // Segmented Control for Tabs @@ -104,7 +104,7 @@ struct DocumentsWarrantiesView: View { loadWarranties() }) { Image(systemName: showActiveOnly ? "checkmark.circle.fill" : "checkmark.circle") - .foregroundColor(showActiveOnly ? .green : Color(.secondaryLabel)) + .foregroundColor(showActiveOnly ? Color.appPrimary : Color.appTextSecondary) } } @@ -149,7 +149,7 @@ struct DocumentsWarrantiesView: View { } } label: { Image(systemName: "line.3.horizontal.decrease.circle") - .foregroundColor((selectedCategory != nil || selectedDocType != nil) ? .blue : Color(.secondaryLabel)) + .foregroundColor((selectedCategory != nil || selectedDocType != nil) ? Color.appPrimary : Color.appTextSecondary) } // Add Button @@ -158,7 +158,7 @@ struct DocumentsWarrantiesView: View { }) { Image(systemName: "plus.circle.fill") .font(.title2) - .foregroundColor(.blue) + .foregroundColor(Color.appPrimary) } } } diff --git a/iosApp/iosApp/Login/LoginView.swift b/iosApp/iosApp/Login/LoginView.swift index 79227a6..d7827bf 100644 --- a/iosApp/iosApp/Login/LoginView.swift +++ b/iosApp/iosApp/Login/LoginView.swift @@ -26,7 +26,7 @@ struct LoginView: View { private var buttonBackgroundColor: Color { if viewModel.isLoading || !isFormValid { - return Color(.tertiaryLabel) + return Color.appTextSecondary } return .clear } @@ -39,7 +39,7 @@ struct LoginView: View { NavigationView { ZStack { // Background gradient - Color(.systemGroupedBackground) + Color.appBackgroundPrimary .ignoresSafeArea() ScrollView { @@ -52,9 +52,9 @@ struct LoginView: View { // App Icon with gradient ZStack { Circle() - .fill(LinearGradient(colors: [.blue, .blue.opacity(0.8)], startPoint: .topLeading, endPoint: .bottomTrailing)) + .fill(LinearGradient(colors: [Color.appPrimary, Color.appPrimary.opacity(0.8)], startPoint: .topLeading, endPoint: .bottomTrailing)) .frame(width: 100, height: 100) - .shadow(color: .blue.opacity(0.3), radius: 20, y: 10) + .shadow(color: Color.appPrimary.opacity(0.3), radius: 20, y: 10) Image(systemName: "house.fill") .font(.system(size: 50, weight: .semibold)) @@ -64,11 +64,11 @@ struct LoginView: View { VStack(spacing: AppSpacing.xs) { Text("Welcome Back") .font(.title2.weight(.bold)) - .foregroundColor(Color(.label)) + .foregroundColor(Color.appTextPrimary) Text("Sign in to manage your properties") .font(.body) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) } } @@ -78,11 +78,11 @@ struct LoginView: View { VStack(alignment: .leading, spacing: AppSpacing.xs) { Text("Email or Username") .font(.subheadline.weight(.medium)) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) HStack(spacing: AppSpacing.sm) { Image(systemName: "envelope.fill") - .foregroundColor(Color(.tertiaryLabel)) + .foregroundColor(Color.appTextSecondary) .frame(width: 20) TextField("Enter your email", text: $viewModel.username) @@ -100,13 +100,13 @@ struct LoginView: View { .accessibilityIdentifier(AccessibilityIdentifiers.Authentication.usernameField) } .padding(AppSpacing.md) - .background(Color(.secondarySystemGroupedBackground)) + .background(Color.appBackgroundSecondary) .cornerRadius(AppRadius.md) .overlay( RoundedRectangle(cornerRadius: AppRadius.md) - .stroke(focusedField == .username ? .blue : Color(.separator), lineWidth: 1.5) + .stroke(focusedField == .username ? Color.appPrimary : Color.appTextSecondary.opacity(0.3), lineWidth: 1.5) ) - .shadow(color: focusedField == .username ? .blue.opacity(0.1) : .clear, radius: 8) + .shadow(color: focusedField == .username ? Color.appPrimary.opacity(0.1) : .clear, radius: 8) .animation(.easeInOut(duration: 0.2), value: focusedField) } @@ -114,11 +114,11 @@ struct LoginView: View { VStack(alignment: .leading, spacing: AppSpacing.xs) { Text("Password") .font(.subheadline.weight(.medium)) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) HStack(spacing: AppSpacing.sm) { Image(systemName: "lock.fill") - .foregroundColor(Color(.tertiaryLabel)) + .foregroundColor(Color.appTextSecondary) .frame(width: 20) Group { @@ -147,19 +147,19 @@ struct LoginView: View { isPasswordVisible.toggle() }) { Image(systemName: isPasswordVisible ? "eye.slash.fill" : "eye.fill") - .foregroundColor(Color(.tertiaryLabel)) + .foregroundColor(Color.appTextSecondary) .frame(width: 20) } .accessibilityIdentifier(AccessibilityIdentifiers.Authentication.passwordVisibilityToggle) } .padding(AppSpacing.md) - .background(Color(.secondarySystemGroupedBackground)) + .background(Color.appBackgroundSecondary) .cornerRadius(AppRadius.md) .overlay( RoundedRectangle(cornerRadius: AppRadius.md) - .stroke(focusedField == .password ? .blue : Color(.separator), lineWidth: 1.5) + .stroke(focusedField == .password ? Color.appPrimary : Color.appTextSecondary.opacity(0.3), lineWidth: 1.5) ) - .shadow(color: focusedField == .password ? .blue.opacity(0.1) : .clear, radius: 8) + .shadow(color: focusedField == .password ? Color.appPrimary.opacity(0.1) : .clear, radius: 8) .animation(.easeInOut(duration: 0.2), value: focusedField) .onChange(of: viewModel.password) { _, _ in viewModel.clearError() @@ -173,7 +173,7 @@ struct LoginView: View { showPasswordReset = true } .font(.subheadline.weight(.medium)) - .foregroundColor(.blue) + .foregroundColor(Color.appPrimary) .accessibilityIdentifier(AccessibilityIdentifiers.Authentication.forgotPasswordButton) } @@ -181,14 +181,14 @@ struct LoginView: View { if let errorMessage = viewModel.errorMessage { HStack(spacing: AppSpacing.sm) { Image(systemName: "exclamationmark.circle.fill") - .foregroundColor(.red) + .foregroundColor(Color.appError) Text(errorMessage) .font(.callout) - .foregroundColor(.red) + .foregroundColor(Color.appError) Spacer() } .padding(AppSpacing.md) - .background(.red.opacity(0.1)) + .background(Color.appError.opacity(0.1)) .cornerRadius(AppRadius.md) } @@ -203,19 +203,19 @@ struct LoginView: View { HStack(spacing: AppSpacing.xs) { Text("Don't have an account?") .font(.body) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) Button("Sign Up") { showingRegister = true } .font(.body) .fontWeight(.semibold) - .foregroundColor(.blue) + .foregroundColor(Color.appPrimary) .accessibilityIdentifier(AccessibilityIdentifiers.Authentication.signUpButton) } } .padding(AppSpacing.xl) - .background(Color(.secondarySystemGroupedBackground)) + .background(Color.appBackgroundSecondary) .cornerRadius(AppRadius.xxl) .shadow(color: .black.opacity(0.08), radius: 20, y: 10) .padding(.horizontal, AppSpacing.lg) @@ -282,11 +282,11 @@ struct LoginView: View { } .frame(maxWidth: .infinity) .frame(height: 56) - .foregroundColor(.white) + .foregroundColor(Color.appTextOnPrimary) .background(loginButtonBackground) .cornerRadius(AppRadius.md) .shadow( - color: shouldShowShadow ? .blue.opacity(0.3) : .clear, + color: shouldShowShadow ? Color.appPrimary.opacity(0.3) : .clear, radius: 10, y: 5 ) @@ -294,9 +294,9 @@ struct LoginView: View { private var loginButtonBackground: AnyShapeStyle { if viewModel.isLoading || !isFormValid { - AnyShapeStyle(Color(.tertiaryLabel)) + AnyShapeStyle(Color.appTextSecondary) } else { - AnyShapeStyle(LinearGradient(colors: [.blue, .blue.opacity(0.8)], startPoint: .topLeading, endPoint: .bottomTrailing)) + AnyShapeStyle(LinearGradient(colors: [Color.appPrimary, Color.appPrimary.opacity(0.8)], startPoint: .topLeading, endPoint: .bottomTrailing)) } } } diff --git a/iosApp/iosApp/MainTabView.swift b/iosApp/iosApp/MainTabView.swift index 80e04ce..ebf3289 100644 --- a/iosApp/iosApp/MainTabView.swift +++ b/iosApp/iosApp/MainTabView.swift @@ -51,6 +51,7 @@ struct MainTabView: View { .tag(4) .accessibilityIdentifier(AccessibilityIdentifiers.Navigation.profileTab) } + .tint(Color.appPrimary) .onChange(of: authManager.isAuthenticated) { _ in selectedTab = 0 } diff --git a/iosApp/iosApp/PasswordReset/ForgotPasswordView.swift b/iosApp/iosApp/PasswordReset/ForgotPasswordView.swift index 0d8ef95..3778d2e 100644 --- a/iosApp/iosApp/PasswordReset/ForgotPasswordView.swift +++ b/iosApp/iosApp/PasswordReset/ForgotPasswordView.swift @@ -13,7 +13,7 @@ struct ForgotPasswordView: View { VStack(spacing: 12) { Image(systemName: "key.fill") .font(.system(size: 60)) - .foregroundStyle(.blue.gradient) + .foregroundStyle(Color.appPrimary.gradient) .padding(.vertical) Text("Forgot Password?") @@ -22,7 +22,7 @@ struct ForgotPasswordView: View { Text("Enter your email address and we'll send you a verification code") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) .multilineTextAlignment(.center) } .frame(maxWidth: .infinity) @@ -49,30 +49,33 @@ struct ForgotPasswordView: View { } footer: { Text("We'll send a 6-digit verification code to this address") } + .listRowBackground(Color.appBackgroundSecondary) // Error/Success Messages if let errorMessage = viewModel.errorMessage { Section { Label { Text(errorMessage) - .foregroundColor(.red) + .foregroundColor(Color.appError) } icon: { Image(systemName: "exclamationmark.triangle.fill") - .foregroundColor(.red) + .foregroundColor(Color.appError) } } + .listRowBackground(Color.appBackgroundSecondary) } if let successMessage = viewModel.successMessage { Section { Label { Text(successMessage) - .foregroundColor(.green) + .foregroundColor(Color.appAccent) } icon: { Image(systemName: "checkmark.circle.fill") - .foregroundColor(.green) + .foregroundColor(Color.appAccent) } } + .listRowBackground(Color.appBackgroundSecondary) } // Send Code Button @@ -99,12 +102,16 @@ struct ForgotPasswordView: View { HStack { Spacer() Text("Back to Login") - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) Spacer() } } } + .listRowBackground(Color.appBackgroundSecondary) } + .listStyle(.plain) + .scrollContentBackground(.hidden) + .background(Color.appBackgroundPrimary) .navigationTitle("Reset Password") .navigationBarTitleDisplayMode(.inline) .onAppear { diff --git a/iosApp/iosApp/PasswordReset/ResetPasswordView.swift b/iosApp/iosApp/PasswordReset/ResetPasswordView.swift index ef7c055..86be831 100644 --- a/iosApp/iosApp/PasswordReset/ResetPasswordView.swift +++ b/iosApp/iosApp/PasswordReset/ResetPasswordView.swift @@ -20,16 +20,17 @@ struct ResetPasswordView: View { VStack(spacing: 12) { Image(systemName: "lock.rotation") .font(.system(size: 60)) - .foregroundStyle(.blue.gradient) + .foregroundStyle(Color.appPrimary.gradient) .padding(.vertical) Text("Set New Password") .font(.title2) .fontWeight(.bold) + .foregroundColor(Color.appTextPrimary) Text("Create a strong password to secure your account") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) .multilineTextAlignment(.center) } .frame(maxWidth: .infinity) @@ -42,30 +43,34 @@ struct ResetPasswordView: View { VStack(alignment: .leading, spacing: 8) { HStack(spacing: 8) { Image(systemName: viewModel.newPassword.count >= 8 ? "checkmark.circle.fill" : "circle") - .foregroundColor(viewModel.newPassword.count >= 8 ? .green : .secondary) + .foregroundColor(viewModel.newPassword.count >= 8 ? Color.appPrimary : Color.appTextSecondary) Text("At least 8 characters") .font(.caption) + .foregroundColor(Color.appTextPrimary) } HStack(spacing: 8) { Image(systemName: hasLetter ? "checkmark.circle.fill" : "circle") - .foregroundColor(hasLetter ? .green : .secondary) + .foregroundColor(hasLetter ? Color.appPrimary : Color.appTextSecondary) Text("Contains letters") .font(.caption) + .foregroundColor(Color.appTextPrimary) } HStack(spacing: 8) { Image(systemName: hasNumber ? "checkmark.circle.fill" : "circle") - .foregroundColor(hasNumber ? .green : .secondary) + .foregroundColor(hasNumber ? Color.appPrimary : Color.appTextSecondary) Text("Contains numbers") .font(.caption) + .foregroundColor(Color.appTextPrimary) } HStack(spacing: 8) { Image(systemName: passwordsMatch ? "checkmark.circle.fill" : "circle") - .foregroundColor(passwordsMatch ? .green : .secondary) + .foregroundColor(passwordsMatch ? Color.appPrimary : Color.appTextSecondary) Text("Passwords match") .font(.caption) + .foregroundColor(Color.appTextPrimary) } } } header: { @@ -97,7 +102,7 @@ struct ResetPasswordView: View { isNewPasswordVisible.toggle() }) { Image(systemName: isNewPasswordVisible ? "eye.slash.fill" : "eye.fill") - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } .buttonStyle(.plain) } @@ -133,7 +138,7 @@ struct ResetPasswordView: View { isConfirmPasswordVisible.toggle() }) { Image(systemName: isConfirmPasswordVisible ? "eye.slash.fill" : "eye.fill") - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } .buttonStyle(.plain) } @@ -149,10 +154,10 @@ struct ResetPasswordView: View { Section { Label { Text(errorMessage) - .foregroundColor(.red) + .foregroundColor(Color.appError) } icon: { Image(systemName: "exclamationmark.triangle.fill") - .foregroundColor(.red) + .foregroundColor(Color.appError) } } } @@ -161,11 +166,11 @@ struct ResetPasswordView: View { Section { Label { Text(successMessage) - .foregroundColor(.green) + .foregroundColor(Color.appPrimary) .multilineTextAlignment(.center) } icon: { Image(systemName: "checkmark.circle.fill") - .foregroundColor(.green) + .foregroundColor(Color.appPrimary) } } } @@ -204,6 +209,9 @@ struct ResetPasswordView: View { } } } + .listStyle(.plain) + .scrollContentBackground(.hidden) + .background(Color.appBackgroundPrimary) .navigationTitle("Reset Password") .navigationBarTitleDisplayMode(.inline) .navigationBarBackButtonHidden(true) diff --git a/iosApp/iosApp/PasswordReset/VerifyResetCodeView.swift b/iosApp/iosApp/PasswordReset/VerifyResetCodeView.swift index b6616f7..3d971e4 100644 --- a/iosApp/iosApp/PasswordReset/VerifyResetCodeView.swift +++ b/iosApp/iosApp/PasswordReset/VerifyResetCodeView.swift @@ -13,21 +13,22 @@ struct VerifyResetCodeView: View { VStack(spacing: 12) { Image(systemName: "envelope.badge.fill") .font(.system(size: 60)) - .foregroundStyle(.blue.gradient) + .foregroundStyle(Color.appPrimary.gradient) .padding(.vertical) Text("Check Your Email") .font(.title2) .fontWeight(.bold) + .foregroundColor(Color.appTextPrimary) Text("We sent a 6-digit code to") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) Text(viewModel.email) .font(.subheadline) .fontWeight(.semibold) - .foregroundColor(.primary) + .foregroundColor(Color.appTextPrimary) } .frame(maxWidth: .infinity) .padding(.vertical) @@ -39,11 +40,13 @@ struct VerifyResetCodeView: View { Label { Text("Code expires in 15 minutes") .fontWeight(.semibold) + .foregroundColor(Color.appTextPrimary) } icon: { Image(systemName: "clock.fill") - .foregroundColor(.orange) + .foregroundColor(Color.appAccent) } } + .listRowBackground(Color.appBackgroundSecondary) // Code Input Section Section { @@ -66,30 +69,33 @@ struct VerifyResetCodeView: View { } footer: { Text("Enter the 6-digit code from your email") } + .listRowBackground(Color.appBackgroundSecondary) // Error/Success Messages if let errorMessage = viewModel.errorMessage { Section { Label { Text(errorMessage) - .foregroundColor(.red) + .foregroundColor(Color.appError) } icon: { Image(systemName: "exclamationmark.triangle.fill") - .foregroundColor(.red) + .foregroundColor(Color.appError) } } + .listRowBackground(Color.appBackgroundSecondary) } if let successMessage = viewModel.successMessage { Section { Label { Text(successMessage) - .foregroundColor(.green) + .foregroundColor(Color.appPrimary) } icon: { Image(systemName: "checkmark.circle.fill") - .foregroundColor(.green) + .foregroundColor(Color.appPrimary) } } + .listRowBackground(Color.appBackgroundSecondary) } // Verify Button @@ -110,13 +116,14 @@ struct VerifyResetCodeView: View { } .disabled(viewModel.code.count != 6 || viewModel.isLoading) } + .listRowBackground(Color.appBackgroundSecondary) // Help Section Section { VStack(spacing: 12) { Text("Didn't receive the code?") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) Button(action: { // Clear code and go back to request new one @@ -131,13 +138,16 @@ struct VerifyResetCodeView: View { Text("Check your spam folder if you don't see it") .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) .multilineTextAlignment(.center) } .frame(maxWidth: .infinity) } .listRowBackground(Color.clear) } + .listStyle(.plain) + .scrollContentBackground(.hidden) + .background(Color.appBackgroundPrimary) .navigationTitle("Verify Code") .navigationBarTitleDisplayMode(.inline) .navigationBarBackButtonHidden(true) diff --git a/iosApp/iosApp/Profile/ProfileTabView.swift b/iosApp/iosApp/Profile/ProfileTabView.swift index 7007184..139c4bb 100644 --- a/iosApp/iosApp/Profile/ProfileTabView.swift +++ b/iosApp/iosApp/Profile/ProfileTabView.swift @@ -11,18 +11,20 @@ struct ProfileTabView: View { Image(systemName: "person.circle.fill") .resizable() .frame(width: 60, height: 60) - .foregroundColor(.blue) + .foregroundColor(Color.appPrimary) VStack(alignment: .leading, spacing: 4) { Text("User Profile") .font(.headline) + .foregroundColor(Color.appTextPrimary) Text("Manage your account") .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } } .padding(.vertical, 8) + .listRowBackground(Color.appBackgroundSecondary) } Section("Account") { @@ -30,7 +32,7 @@ struct ProfileTabView: View { showingProfileEdit = true }) { Label("Edit Profile", systemImage: "person.crop.circle") - .foregroundColor(.primary) + .foregroundColor(Color.appTextPrimary) } NavigationLink(destination: Text("Notifications")) { @@ -41,15 +43,17 @@ struct ProfileTabView: View { Label("Privacy", systemImage: "lock.shield") } } + .listRowBackground(Color.appBackgroundSecondary) Section { Button(action: { showingLogoutAlert = true }) { Label("Log Out", systemImage: "rectangle.portrait.and.arrow.right") - .foregroundColor(.red) + .foregroundColor(Color.appError) } .accessibilityIdentifier(AccessibilityIdentifiers.Profile.logoutButton) + .listRowBackground(Color.appBackgroundSecondary) } Section { @@ -57,13 +61,17 @@ struct ProfileTabView: View { Text("MyCrib") .font(.caption) .fontWeight(.semibold) + .foregroundColor(Color.appTextPrimary) Text("Version 1.0.0") .font(.caption2) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } + .listRowBackground(Color.appBackgroundSecondary) } } + .scrollContentBackground(.hidden) + .background(Color.appBackgroundPrimary) .navigationTitle("Profile") .sheet(isPresented: $showingProfileEdit) { ProfileView() diff --git a/iosApp/iosApp/Profile/ProfileView.swift b/iosApp/iosApp/Profile/ProfileView.swift index 93d0f00..491220f 100644 --- a/iosApp/iosApp/Profile/ProfileView.swift +++ b/iosApp/iosApp/Profile/ProfileView.swift @@ -16,7 +16,7 @@ struct ProfileView: View { ProgressView() Text("Loading profile...") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) .padding(.top, 8) } } else { @@ -25,11 +25,12 @@ struct ProfileView: View { VStack(spacing: 16) { Image(systemName: "person.circle.fill") .font(.system(size: 60)) - .foregroundStyle(.blue.gradient) + .foregroundStyle(Color.appPrimary.gradient) Text("Profile Settings") .font(.title2) .fontWeight(.bold) + .foregroundColor(Color.appTextPrimary) } .frame(maxWidth: .infinity) .padding(.vertical) @@ -57,6 +58,7 @@ struct ProfileView: View { } header: { Text("Personal Information") } + .listRowBackground(Color.appBackgroundSecondary) Section { TextField("Email", text: $viewModel.email) @@ -73,29 +75,32 @@ struct ProfileView: View { } footer: { Text("Email is required and must be unique") } + .listRowBackground(Color.appBackgroundSecondary) if let errorMessage = viewModel.errorMessage { Section { HStack { Image(systemName: "exclamationmark.triangle.fill") - .foregroundColor(.red) + .foregroundColor(Color.appError) Text(errorMessage) - .foregroundColor(.red) + .foregroundColor(Color.appError) .font(.subheadline) } } + .listRowBackground(Color.appBackgroundSecondary) } if let successMessage = viewModel.successMessage { Section { HStack { Image(systemName: "checkmark.circle.fill") - .foregroundColor(.green) + .foregroundColor(Color.appPrimary) Text(successMessage) - .foregroundColor(.green) + .foregroundColor(Color.appPrimary) .font(.subheadline) } } + .listRowBackground(Color.appBackgroundSecondary) } Section { @@ -113,7 +118,11 @@ struct ProfileView: View { } .disabled(viewModel.isLoading || viewModel.email.isEmpty) } + .listRowBackground(Color.appBackgroundSecondary) } + .listStyle(.plain) + .scrollContentBackground(.hidden) + .background(Color.appBackgroundPrimary) .navigationTitle("Profile") .navigationBarTitleDisplayMode(.inline) .toolbar { diff --git a/iosApp/iosApp/Register/RegisterView.swift b/iosApp/iosApp/Register/RegisterView.swift index 4841995..dbdf164 100644 --- a/iosApp/iosApp/Register/RegisterView.swift +++ b/iosApp/iosApp/Register/RegisterView.swift @@ -18,7 +18,7 @@ struct RegisterView: View { VStack(spacing: 16) { Image(systemName: "person.badge.plus") .font(.system(size: 60)) - .foregroundStyle(.blue.gradient) + .foregroundStyle(Color.appPrimary.gradient) Text("Join MyCrib") .font(.largeTitle) @@ -26,7 +26,7 @@ struct RegisterView: View { Text("Start managing your properties today") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } .frame(maxWidth: .infinity) .padding(.vertical) @@ -57,6 +57,7 @@ struct RegisterView: View { } header: { Text("Account Information") } + .listRowBackground(Color.appBackgroundSecondary) Section { SecureField("Password", text: $viewModel.password) @@ -79,17 +80,19 @@ struct RegisterView: View { } footer: { Text("Password must be secure") } + .listRowBackground(Color.appBackgroundSecondary) if let errorMessage = viewModel.errorMessage { Section { HStack { Image(systemName: "exclamationmark.triangle.fill") - .foregroundColor(.red) + .foregroundColor(Color.appError) Text(errorMessage) - .foregroundColor(.red) + .foregroundColor(Color.appError) .font(.subheadline) } } + .listRowBackground(Color.appBackgroundSecondary) } Section { @@ -108,7 +111,11 @@ struct RegisterView: View { .disabled(viewModel.isLoading) .accessibilityIdentifier(AccessibilityIdentifiers.Authentication.registerButton) } + .listRowBackground(Color.appBackgroundSecondary) } + .listStyle(.plain) + .scrollContentBackground(.hidden) + .background(Color.appBackgroundPrimary) .navigationTitle("Create Account") .navigationBarTitleDisplayMode(.inline) .toolbar { diff --git a/iosApp/iosApp/Residence/JoinResidenceView.swift b/iosApp/iosApp/Residence/JoinResidenceView.swift index f9ca98c..40396b7 100644 --- a/iosApp/iosApp/Residence/JoinResidenceView.swift +++ b/iosApp/iosApp/Residence/JoinResidenceView.swift @@ -28,14 +28,16 @@ struct JoinResidenceView: View { Text("Enter Share Code") } footer: { Text("Enter the 6-character code shared with you to join a residence") - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } + .listRowBackground(Color.appBackgroundSecondary) if let error = viewModel.errorMessage { Section { Text(error) - .foregroundColor(.red) + .foregroundColor(Color.appError) } + .listRowBackground(Color.appBackgroundSecondary) } Section { @@ -54,7 +56,11 @@ struct JoinResidenceView: View { } .disabled(shareCode.count != 6 || viewModel.isLoading) } + .listRowBackground(Color.appBackgroundSecondary) } + .listStyle(.plain) + .scrollContentBackground(.hidden) + .background(Color.appBackgroundPrimary) .navigationTitle("Join Residence") .navigationBarTitleDisplayMode(.inline) .toolbar { diff --git a/iosApp/iosApp/Residence/ManageUsersView.swift b/iosApp/iosApp/Residence/ManageUsersView.swift index 9eb813d..d245653 100644 --- a/iosApp/iosApp/Residence/ManageUsersView.swift +++ b/iosApp/iosApp/Residence/ManageUsersView.swift @@ -17,7 +17,7 @@ struct ManageUsersView: View { var body: some View { NavigationView { ZStack { - Color(.systemGroupedBackground) + Color.appBackgroundPrimary .ignoresSafeArea() if isLoading { @@ -63,6 +63,9 @@ struct ManageUsersView: View { } } } + .listStyle(.plain) + .scrollContentBackground(.hidden) + .background(Color.appBackgroundPrimary) .navigationTitle("Manage Users") .navigationBarTitleDisplayMode(.inline) .toolbar { diff --git a/iosApp/iosApp/Residence/ResidenceDetailView.swift b/iosApp/iosApp/Residence/ResidenceDetailView.swift index c0ba2f0..7874321 100644 --- a/iosApp/iosApp/Residence/ResidenceDetailView.swift +++ b/iosApp/iosApp/Residence/ResidenceDetailView.swift @@ -30,9 +30,9 @@ struct ResidenceDetailView: View { var body: some View { ZStack { - Color(.systemGroupedBackground) + Color.appBackgroundPrimary .ignoresSafeArea() - + mainContent } .navigationBarTitleDisplayMode(.inline) @@ -174,7 +174,7 @@ private extension ResidenceDetailView { ProgressView() Text("Loading residence...") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } } @@ -209,7 +209,7 @@ private extension ResidenceDetailView { ProgressView("Loading tasks...") } else if let tasksError = tasksError { Text("Error loading tasks: \(tasksError)") - .foregroundColor(.red) + .foregroundColor(Color.appError) .padding() } } @@ -266,7 +266,7 @@ private extension ResidenceDetailView { showDeleteConfirmation = true } label: { Image(systemName: "trash") - .foregroundStyle(.red) + .foregroundStyle(Color.appError) } .accessibilityIdentifier(AccessibilityIdentifiers.Residence.deleteButton) } diff --git a/iosApp/iosApp/Residence/ResidencesListView.swift b/iosApp/iosApp/Residence/ResidencesListView.swift index d2bd0b0..fe09794 100644 --- a/iosApp/iosApp/Residence/ResidencesListView.swift +++ b/iosApp/iosApp/Residence/ResidencesListView.swift @@ -10,7 +10,7 @@ struct ResidencesListView: View { var body: some View { ZStack { - Color(.systemGroupedBackground) + Color.appBackgroundPrimary .ignoresSafeArea() if viewModel.myResidences == nil && viewModel.isLoading { @@ -19,7 +19,7 @@ struct ResidencesListView: View { .scaleEffect(1.2) Text("Loading properties...") .font(.body) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) } } else if let response = viewModel.myResidences { if response.residences.isEmpty { @@ -37,10 +37,10 @@ struct ResidencesListView: View { VStack(alignment: .leading, spacing: AppSpacing.xxs) { Text("Your Properties") .font(.title3.weight(.semibold)) - .foregroundColor(Color(.label)) + .foregroundColor(Color.appTextPrimary) Text("\(response.residences.count) \(response.residences.count == 1 ? "property" : "properties")") .font(.callout) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) } Spacer() } @@ -75,7 +75,7 @@ struct ResidencesListView: View { }) { Image(systemName: "person.badge.plus") .font(.system(size: 18, weight: .semibold)) - .foregroundColor(.blue) + .foregroundColor(Color.appPrimary) } Button(action: { @@ -83,7 +83,7 @@ struct ResidencesListView: View { }) { Image(systemName: "plus.circle.fill") .font(.system(size: 22, weight: .semibold)) - .foregroundColor(.blue) + .foregroundColor(Color.appPrimary) } .accessibilityIdentifier(AccessibilityIdentifiers.Residence.addButton) } diff --git a/iosApp/iosApp/ResidenceFormView.swift b/iosApp/iosApp/ResidenceFormView.swift index 3dd7a3c..0f98886 100644 --- a/iosApp/iosApp/ResidenceFormView.swift +++ b/iosApp/iosApp/ResidenceFormView.swift @@ -55,7 +55,7 @@ struct ResidenceFormView: View { if !nameError.isEmpty { Text(nameError) .font(.caption) - .foregroundColor(.red) + .foregroundColor(Color.appError) } Picker("Property Type", selection: $selectedPropertyType) { @@ -70,8 +70,9 @@ struct ResidenceFormView: View { } footer: { Text("Required: Name") .font(.caption) - .foregroundColor(.red) + .foregroundColor(Color.appError) } + .listRowBackground(Color.appBackgroundSecondary) Section { TextField("Street Address", text: $streetAddress) @@ -100,6 +101,7 @@ struct ResidenceFormView: View { } header: { Text("Address") } + .listRowBackground(Color.appBackgroundSecondary) Section(header: Text("Property Features")) { HStack { @@ -139,6 +141,7 @@ struct ResidenceFormView: View { .focused($focusedField, equals: .yearBuilt) .accessibilityIdentifier(AccessibilityIdentifiers.Residence.yearBuiltField) } + .listRowBackground(Color.appBackgroundSecondary) Section(header: Text("Additional Details")) { TextField("Description (optional)", text: $description, axis: .vertical) @@ -148,15 +151,20 @@ struct ResidenceFormView: View { Toggle("Primary Residence", isOn: $isPrimary) .accessibilityIdentifier(AccessibilityIdentifiers.Residence.isPrimaryToggle) } + .listRowBackground(Color.appBackgroundSecondary) if let errorMessage = viewModel.errorMessage { Section { Text(errorMessage) - .foregroundColor(.red) + .foregroundColor(Color.appError) .font(.caption) } + .listRowBackground(Color.appBackgroundSecondary) } } + .listStyle(.plain) + .scrollContentBackground(.hidden) + .background(Color.appBackgroundPrimary) .navigationTitle(isEditMode ? "Edit Residence" : "Add Residence") .navigationBarTitleDisplayMode(.inline) .toolbar { diff --git a/iosApp/iosApp/Subviews/Auth/LoginHeader.swift b/iosApp/iosApp/Subviews/Auth/LoginHeader.swift index 114ce82..5a0991f 100644 --- a/iosApp/iosApp/Subviews/Auth/LoginHeader.swift +++ b/iosApp/iosApp/Subviews/Auth/LoginHeader.swift @@ -7,11 +7,12 @@ struct LoginHeader: View { .resizable() .aspectRatio(contentMode: .fit) .frame(width: 80, height: 80) - .foregroundColor(.blue) + .foregroundColor(Color.appPrimary) Text("MyCrib") .font(.largeTitle) .fontWeight(.bold) + .foregroundColor(Color.appTextPrimary) } .padding(.top, 60) .padding(.bottom, 20) diff --git a/iosApp/iosApp/Subviews/Auth/RegisterHeader.swift b/iosApp/iosApp/Subviews/Auth/RegisterHeader.swift index 436131b..92de4f6 100644 --- a/iosApp/iosApp/Subviews/Auth/RegisterHeader.swift +++ b/iosApp/iosApp/Subviews/Auth/RegisterHeader.swift @@ -7,15 +7,16 @@ struct RegisterHeader: View { .resizable() .aspectRatio(contentMode: .fit) .frame(width: 64, height: 64) - .foregroundColor(.blue) + .foregroundColor(Color.appPrimary) Text("Join MyCrib") .font(.largeTitle) .fontWeight(.bold) + .foregroundColor(Color.appTextPrimary) Text("Start managing your properties today") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } .padding(.top, 40) .padding(.bottom, 20) diff --git a/iosApp/iosApp/Subviews/Common/ErrorMessageView.swift b/iosApp/iosApp/Subviews/Common/ErrorMessageView.swift index 3b2bcd3..1530102 100644 --- a/iosApp/iosApp/Subviews/Common/ErrorMessageView.swift +++ b/iosApp/iosApp/Subviews/Common/ErrorMessageView.swift @@ -7,21 +7,21 @@ struct ErrorMessageView: View { var body: some View { HStack { Image(systemName: "exclamationmark.triangle.fill") - .foregroundColor(.red) + .foregroundColor(Color.appError) Text(message) .font(.caption) - .foregroundColor(.red) + .foregroundColor(Color.appError) Spacer() Button(action: onDismiss) { Image(systemName: "xmark.circle.fill") - .foregroundColor(.red) + .foregroundColor(Color.appError) } } .padding() - .background(Color.red.opacity(0.1)) + .background(Color.appError.opacity(0.1)) .cornerRadius(8) } } diff --git a/iosApp/iosApp/Subviews/Common/ErrorView.swift b/iosApp/iosApp/Subviews/Common/ErrorView.swift index c43d23a..d0c47ed 100644 --- a/iosApp/iosApp/Subviews/Common/ErrorView.swift +++ b/iosApp/iosApp/Subviews/Common/ErrorView.swift @@ -8,18 +8,18 @@ struct ErrorView: View { VStack(spacing: 16) { Image(systemName: "exclamationmark.triangle") .font(.system(size: 64)) - .foregroundColor(.red) + .foregroundColor(Color.appError) Text("Error: \(message)") - .foregroundColor(.red) + .foregroundColor(Color.appError) .multilineTextAlignment(.center) Button(action: retryAction) { Text("Retry") .padding(.horizontal, 32) .padding(.vertical, 12) - .background(Color.blue) - .foregroundColor(.white) + .background(Color.appPrimary) + .foregroundColor(Color.appTextOnPrimary) .cornerRadius(8) } } diff --git a/iosApp/iosApp/Subviews/Common/HomeNavigationCard.swift b/iosApp/iosApp/Subviews/Common/HomeNavigationCard.swift index 8f147cb..3cb977e 100644 --- a/iosApp/iosApp/Subviews/Common/HomeNavigationCard.swift +++ b/iosApp/iosApp/Subviews/Common/HomeNavigationCard.swift @@ -10,13 +10,13 @@ struct HomeNavigationCard: View { // Icon with gradient background ZStack { RoundedRectangle(cornerRadius: AppRadius.md) - .fill(LinearGradient(colors: [.blue, .blue.opacity(0.8)], startPoint: .topLeading, endPoint: .bottomTrailing)) + .fill(LinearGradient(colors: [Color.appPrimary, Color.appPrimary.opacity(0.8)], startPoint: .topLeading, endPoint: .bottomTrailing)) .frame(width: 60, height: 60) - .shadow(color: .blue.opacity(0.3), radius: 8, y: 4) + .shadow(color: Color.appPrimary.opacity(0.3), radius: 8, y: 4) Image(systemName: icon) .font(.system(size: 28, weight: .semibold)) - .foregroundColor(.white) + .foregroundColor(Color.appTextOnPrimary) } // Text Content @@ -24,11 +24,11 @@ struct HomeNavigationCard: View { Text(title) .font(.title3.weight(.semibold)) .fontWeight(.semibold) - .foregroundColor(Color(.label)) + .foregroundColor(Color.appTextPrimary) Text(subtitle) .font(.callout) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) } Spacer() @@ -36,10 +36,10 @@ struct HomeNavigationCard: View { // Chevron Image(systemName: "chevron.right") .font(.system(size: 16, weight: .semibold)) - .foregroundColor(Color(.tertiaryLabel)) + .foregroundColor(Color.appTextSecondary.opacity(0.7)) } .padding(AppSpacing.lg) - .background(Color(.secondarySystemGroupedBackground)) + .background(Color.appBackgroundSecondary) .cornerRadius(AppRadius.lg) .shadow(color: AppShadow.md.color, radius: AppShadow.md.radius, x: AppShadow.md.x, y: AppShadow.md.y) } diff --git a/iosApp/iosApp/Subviews/Common/ImageThumbnailView.swift b/iosApp/iosApp/Subviews/Common/ImageThumbnailView.swift index e1ca048..19d26d4 100644 --- a/iosApp/iosApp/Subviews/Common/ImageThumbnailView.swift +++ b/iosApp/iosApp/Subviews/Common/ImageThumbnailView.swift @@ -19,7 +19,7 @@ struct ImageThumbnailView: View { Button(action: onRemove) { Image(systemName: "xmark.circle.fill") .font(.title3) - .foregroundStyle(.white) + .foregroundStyle(Color.appTextOnPrimary) .background { Circle() .fill(.black.opacity(0.6)) diff --git a/iosApp/iosApp/Subviews/Common/OverviewCard.swift b/iosApp/iosApp/Subviews/Common/OverviewCard.swift index e41422e..d46ad91 100644 --- a/iosApp/iosApp/Subviews/Common/OverviewCard.swift +++ b/iosApp/iosApp/Subviews/Common/OverviewCard.swift @@ -11,17 +11,17 @@ struct OverviewCard: View { HStack(spacing: AppSpacing.sm) { ZStack { Circle() - .fill(LinearGradient(colors: [.blue, .blue.opacity(0.8)], startPoint: .topLeading, endPoint: .bottomTrailing)) + .fill(LinearGradient(colors: [Color.appPrimary, Color.appPrimary.opacity(0.8)], startPoint: .topLeading, endPoint: .bottomTrailing)) .frame(width: 44, height: 44) Image(systemName: "chart.bar.fill") .font(.system(size: 20, weight: .semibold)) - .foregroundColor(.white) + .foregroundColor(Color.appTextOnPrimary) } Text("Overview") .font(.title3.weight(.semibold)) - .foregroundColor(Color(.label)) + .foregroundColor(Color.appTextPrimary) } Spacer() } @@ -32,7 +32,7 @@ struct OverviewCard: View { icon: "house.fill", value: "\(summary.totalResidences)", label: "Properties", - color: .blue + color: Color.appPrimary ) Divider() @@ -42,7 +42,7 @@ struct OverviewCard: View { icon: "list.bullet", value: "\(summary.totalTasks)", label: "Total Tasks", - color: .blue + color: Color.appPrimary ) Divider() @@ -52,12 +52,12 @@ struct OverviewCard: View { icon: "clock.fill", value: "\(summary.totalPending)", label: "Pending", - color: .orange + color: Color.appAccent ) } } .padding(AppSpacing.xl) - .background(Color(.secondarySystemGroupedBackground)) + .background(Color.appBackgroundSecondary) .cornerRadius(AppRadius.xl) .shadow(color: AppShadow.lg.color, radius: AppShadow.lg.radius, x: AppShadow.lg.x, y: AppShadow.lg.y) .padding(.horizontal, AppSpacing.md) diff --git a/iosApp/iosApp/Subviews/Common/StatView.swift b/iosApp/iosApp/Subviews/Common/StatView.swift index 2228126..4b87be0 100644 --- a/iosApp/iosApp/Subviews/Common/StatView.swift +++ b/iosApp/iosApp/Subviews/Common/StatView.swift @@ -4,7 +4,7 @@ struct StatView: View { let icon: String let value: String let label: String - var color: Color = .blue + var color: Color = Color.appPrimary var body: some View { VStack(spacing: AppSpacing.sm) { @@ -21,11 +21,11 @@ struct StatView: View { Text(value) .font(.title2.weight(.semibold)) .fontWeight(.bold) - .foregroundColor(Color(.label)) + .foregroundColor(Color.appTextPrimary) Text(label) .font(.footnote.weight(.medium)) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) .multilineTextAlignment(.center) } .frame(maxWidth: .infinity) diff --git a/iosApp/iosApp/Subviews/Residence/EmptyResidencesView.swift b/iosApp/iosApp/Subviews/Residence/EmptyResidencesView.swift index d01e3ad..dbad3e4 100644 --- a/iosApp/iosApp/Subviews/Residence/EmptyResidencesView.swift +++ b/iosApp/iosApp/Subviews/Residence/EmptyResidencesView.swift @@ -5,15 +5,16 @@ struct EmptyResidencesView: View { VStack(spacing: 16) { Image(systemName: "house") .font(.system(size: 80)) - .foregroundColor(.blue.opacity(0.6)) + .foregroundColor(Color.appPrimary.opacity(0.6)) Text("No properties yet") .font(.title2) .fontWeight(.semibold) + .foregroundColor(Color.appTextPrimary) Text("Add your first property to get started!") .font(.body) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } } } diff --git a/iosApp/iosApp/Subviews/Residence/PropertyDetailItem.swift b/iosApp/iosApp/Subviews/Residence/PropertyDetailItem.swift index 6b19073..3513310 100644 --- a/iosApp/iosApp/Subviews/Residence/PropertyDetailItem.swift +++ b/iosApp/iosApp/Subviews/Residence/PropertyDetailItem.swift @@ -9,15 +9,16 @@ struct PropertyDetailItem: View { VStack(spacing: 4) { Image(systemName: icon) .font(.caption) - .foregroundColor(.blue) + .foregroundColor(Color.appPrimary) Text(value) .font(.subheadline) .fontWeight(.semibold) + .foregroundColor(Color.appTextPrimary) Text(label) .font(.caption2) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } } } diff --git a/iosApp/iosApp/Subviews/Residence/PropertyHeaderCard.swift b/iosApp/iosApp/Subviews/Residence/PropertyHeaderCard.swift index b250407..810057d 100644 --- a/iosApp/iosApp/Subviews/Residence/PropertyHeaderCard.swift +++ b/iosApp/iosApp/Subviews/Residence/PropertyHeaderCard.swift @@ -9,17 +9,18 @@ struct PropertyHeaderCard: View { HStack { Image(systemName: "house.fill") .font(.title2) - .foregroundColor(.blue) + .foregroundColor(Color.appPrimary) VStack(alignment: .leading, spacing: 4) { Text(residence.name) .font(.title2) .fontWeight(.bold) + .foregroundColor(Color.appTextPrimary) if let propertyType = residence.propertyType { Text(propertyType) .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } } @@ -32,18 +33,19 @@ struct PropertyHeaderCard: View { if let streetAddress = residence.streetAddress { Label(streetAddress, systemImage: "mappin.circle.fill") .font(.subheadline) + .foregroundColor(Color.appTextPrimary) } if residence.city != nil || residence.stateProvince != nil || residence.postalCode != nil { Text("\(residence.city ?? ""), \(residence.stateProvince ?? "") \(residence.postalCode ?? "")") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } if let country = residence.country, !country.isEmpty { Text(country) .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } } @@ -62,8 +64,9 @@ struct PropertyHeaderCard: View { } } .padding(20) - .background(Color.blue.opacity(0.1)) + .background(Color.appBackgroundSecondary) .cornerRadius(16) + .shadow(color: AppShadow.md.color, radius: AppShadow.md.radius, x: AppShadow.md.x, y: AppShadow.md.y) } } diff --git a/iosApp/iosApp/Subviews/Residence/ResidenceCard.swift b/iosApp/iosApp/Subviews/Residence/ResidenceCard.swift index ee16808..119c5f7 100644 --- a/iosApp/iosApp/Subviews/Residence/ResidenceCard.swift +++ b/iosApp/iosApp/Subviews/Residence/ResidenceCard.swift @@ -10,26 +10,26 @@ struct ResidenceCard: View { HStack(spacing: AppSpacing.sm) { ZStack { RoundedRectangle(cornerRadius: AppRadius.sm) - .fill(LinearGradient(colors: [.blue, .blue.opacity(0.8)], startPoint: .topLeading, endPoint: .bottomTrailing)) + .fill(LinearGradient(colors: [Color.appPrimary, Color.appPrimary.opacity(0.8)], startPoint: .topLeading, endPoint: .bottomTrailing)) .frame(width: 44, height: 44) - .shadow(color: .blue.opacity(0.3), radius: 6, y: 3) + .shadow(color: Color.appPrimary.opacity(0.3), radius: 6, y: 3) Image(systemName: "house.fill") .font(.system(size: 20, weight: .semibold)) - .foregroundColor(.white) + .foregroundColor(Color.appTextOnPrimary) } VStack(alignment: .leading, spacing: AppSpacing.xxs) { Text(residence.name) .font(.title3.weight(.semibold)) .fontWeight(.bold) - .foregroundColor(Color(.label)) + .foregroundColor(Color.appTextPrimary) .lineLimit(1) if let propertyType = residence.propertyType { Text(propertyType) .font(.caption.weight(.medium)) - .foregroundColor(Color(.tertiaryLabel)) + .foregroundColor(Color.appTextSecondary) .textCase(.uppercase) .tracking(0.5) } @@ -40,11 +40,11 @@ struct ResidenceCard: View { if residence.isPrimary { ZStack { Circle() - .fill(.purple.opacity(0.1)) + .fill(Color.appAccent.opacity(0.2)) .frame(width: 32, height: 32) Image(systemName: "star.fill") .font(.system(size: 14, weight: .bold)) - .foregroundColor(.purple) + .foregroundColor(Color.appAccent) } } } @@ -55,10 +55,10 @@ struct ResidenceCard: View { HStack(spacing: AppSpacing.xxs) { Image(systemName: "mappin.circle.fill") .font(.system(size: 12, weight: .medium)) - .foregroundColor(Color(.tertiaryLabel)) + .foregroundColor(Color.appTextSecondary) Text(streetAddress) .font(.callout) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) } } @@ -66,10 +66,10 @@ struct ResidenceCard: View { HStack(spacing: AppSpacing.xxs) { Image(systemName: "location.fill") .font(.system(size: 12, weight: .medium)) - .foregroundColor(Color(.tertiaryLabel)) + .foregroundColor(Color.appTextSecondary) Text("\(residence.city ?? ""), \(residence.stateProvince ?? "")") .font(.callout) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) } } } @@ -86,13 +86,13 @@ struct ResidenceCard: View { icon: category.icons.ios, value: "\(category.count)", label: category.displayName, - color: Color(hex: category.color) ?? .gray + color: Color(hex: category.color) ?? Color.appTextSecondary ) } } } .padding(AppSpacing.md) - .background(Color(.secondarySystemGroupedBackground)) + .background(Color.appBackgroundSecondary) .cornerRadius(AppRadius.lg) .shadow(color: AppShadow.md.color, radius: AppShadow.md.radius, x: AppShadow.md.x, y: AppShadow.md.y) } @@ -160,5 +160,5 @@ struct ResidenceCard: View { updatedAt: "2024-01-01T00:00:00Z" )) .padding() - .background(Color(.systemGroupedBackground)) + .background(Color.appBackgroundPrimary) } diff --git a/iosApp/iosApp/Subviews/Residence/ShareCodeCard.swift b/iosApp/iosApp/Subviews/Residence/ShareCodeCard.swift index 29445fc..9b51ac3 100644 --- a/iosApp/iosApp/Subviews/Residence/ShareCodeCard.swift +++ b/iosApp/iosApp/Subviews/Residence/ShareCodeCard.swift @@ -14,17 +14,17 @@ struct ShareCodeCard: View { VStack(alignment: .leading, spacing: 4) { Text("Share Code") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) if let shareCode = shareCode { Text(shareCode.code) .font(.title) .fontWeight(.bold) - .foregroundColor(.blue) + .foregroundColor(Color.appPrimary) } else { Text("No active code") .font(.body) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } } @@ -43,11 +43,11 @@ struct ShareCodeCard: View { if shareCode != nil { Text("Share this code with others to give them access to \(residenceName)") .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } } .padding() - .background(Color.blue.opacity(0.1)) + .background(Color.appPrimary.opacity(0.1)) .cornerRadius(12) } } diff --git a/iosApp/iosApp/Subviews/Residence/SummaryCard.swift b/iosApp/iosApp/Subviews/Residence/SummaryCard.swift index f4fd69b..7208eb8 100644 --- a/iosApp/iosApp/Subviews/Residence/SummaryCard.swift +++ b/iosApp/iosApp/Subviews/Residence/SummaryCard.swift @@ -46,8 +46,9 @@ struct SummaryCard: View { } } .padding(20) - .background(Color.blue.opacity(0.1)) + .background(Color.appBackgroundSecondary) .cornerRadius(16) + .shadow(color: AppShadow.md.color, radius: AppShadow.md.radius, x: AppShadow.md.x, y: AppShadow.md.y) } } diff --git a/iosApp/iosApp/Subviews/Residence/SummaryStatView.swift b/iosApp/iosApp/Subviews/Residence/SummaryStatView.swift index eb4e808..f78b5d1 100644 --- a/iosApp/iosApp/Subviews/Residence/SummaryStatView.swift +++ b/iosApp/iosApp/Subviews/Residence/SummaryStatView.swift @@ -9,15 +9,16 @@ struct SummaryStatView: View { VStack(spacing: 8) { Image(systemName: icon) .font(.title3) - .foregroundColor(.blue) + .foregroundColor(Color.appPrimary) Text(value) .font(.title2) .fontWeight(.bold) + .foregroundColor(Color.appTextPrimary) Text(label) .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) .multilineTextAlignment(.center) } .frame(maxWidth: .infinity) diff --git a/iosApp/iosApp/Subviews/Residence/TaskStatChip.swift b/iosApp/iosApp/Subviews/Residence/TaskStatChip.swift index c2701ef..cfc0149 100644 --- a/iosApp/iosApp/Subviews/Residence/TaskStatChip.swift +++ b/iosApp/iosApp/Subviews/Residence/TaskStatChip.swift @@ -19,7 +19,7 @@ struct TaskStatChip: View { Text(label) .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } } } diff --git a/iosApp/iosApp/Subviews/Residence/UserListItem.swift b/iosApp/iosApp/Subviews/Residence/UserListItem.swift index 05dd0e7..2f4f78a 100644 --- a/iosApp/iosApp/Subviews/Residence/UserListItem.swift +++ b/iosApp/iosApp/Subviews/Residence/UserListItem.swift @@ -15,15 +15,16 @@ struct UserListItem: View { Text(user.username) .font(.body) .fontWeight(.medium) + .foregroundColor(Color.appTextPrimary) if isOwner { Text("Owner") .font(.caption) .fontWeight(.semibold) - .foregroundColor(.blue) + .foregroundColor(Color.appPrimary) .padding(.horizontal, 6) .padding(.vertical, 2) - .background(Color.blue.opacity(0.1)) + .background(Color.appPrimary.opacity(0.1)) .cornerRadius(4) } } @@ -31,7 +32,7 @@ struct UserListItem: View { if !user.email.isEmpty { Text(user.email) .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } let fullName = [user.firstName, user.lastName] @@ -42,7 +43,7 @@ struct UserListItem: View { if !fullName.isEmpty { Text(fullName) .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } } @@ -51,12 +52,12 @@ struct UserListItem: View { if isPrimaryOwner && !isOwner { Button(action: onRemove) { Image(systemName: "trash") - .foregroundColor(.red) + .foregroundColor(Color.appError) } } } .padding() - .background(Color(.systemBackground)) + .background(Color.appBackgroundSecondary) .cornerRadius(12) .padding(.horizontal) } diff --git a/iosApp/iosApp/Subviews/Task/CompletionCardView.swift b/iosApp/iosApp/Subviews/Task/CompletionCardView.swift index 3b00d5c..71f5218 100644 --- a/iosApp/iosApp/Subviews/Task/CompletionCardView.swift +++ b/iosApp/iosApp/Subviews/Task/CompletionCardView.swift @@ -11,7 +11,7 @@ struct CompletionCardView: View { Text(formatDate(completion.completionDate)) .font(.caption) .fontWeight(.semibold) - .foregroundColor(.blue) + .foregroundColor(Color.appPrimary) Spacer() @@ -23,10 +23,10 @@ struct CompletionCardView: View { .font(.caption) .fontWeight(.bold) } - .foregroundColor(.orange) + .foregroundColor(Color.appAccent) .padding(.horizontal, 8) .padding(.vertical, 4) - .background(Color.orange.opacity(0.1)) + .background(Color.appAccent.opacity(0.1)) .cornerRadius(6) } } @@ -36,38 +36,38 @@ struct CompletionCardView: View { HStack(alignment: .top, spacing: 6) { Image(systemName: "wrench.and.screwdriver") .font(.caption2) - .foregroundColor(.blue) + .foregroundColor(Color.appPrimary) VStack(alignment: .leading, spacing: 2) { Text("By: \(contractorDetails.name)") .font(.caption2) .fontWeight(.medium) - .foregroundColor(.primary) + .foregroundColor(Color.appTextPrimary) if let company = contractorDetails.company { Text(company) .font(.caption2) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } } } } else if let completedBy = completion.completedByName { Text("By: \(completedBy)") .font(.caption2) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } if let cost = completion.actualCost { Text("Cost: $\(cost)") .font(.caption2) - .foregroundColor(.green) + .foregroundColor(Color.appPrimary) .fontWeight(.medium) } if let notes = completion.notes { Text(notes) .font(.caption2) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) .lineLimit(2) } @@ -85,14 +85,14 @@ struct CompletionCardView: View { } .frame(maxWidth: .infinity) .padding(.vertical, 8) - .background(Color.blue.opacity(0.1)) - .foregroundColor(.blue) + .background(Color.appPrimary.opacity(0.1)) + .foregroundColor(Color.appPrimary) .cornerRadius(8) } } } .padding(12) - .background(Color(.systemGray6)) + .background(Color.appBackgroundSecondary.opacity(0.5)) .cornerRadius(8) .sheet(isPresented: $showPhotoSheet) { if let images = completion.images { diff --git a/iosApp/iosApp/Subviews/Task/DynamicTaskCard.swift b/iosApp/iosApp/Subviews/Task/DynamicTaskCard.swift index 9f6e852..0061456 100644 --- a/iosApp/iosApp/Subviews/Task/DynamicTaskCard.swift +++ b/iosApp/iosApp/Subviews/Task/DynamicTaskCard.swift @@ -38,21 +38,21 @@ struct DynamicTaskCard: View { if let description = task.description_, !description.isEmpty { Text(description) .font(.subheadline) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) .lineLimit(2) } HStack { Label(task.frequency.displayName, systemImage: "repeat") .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) Spacer() if let due_date = task.dueDate { Label(formatDate(due_date), systemImage: "calendar") .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } } @@ -62,15 +62,15 @@ struct DynamicTaskCard: View { VStack(alignment: .leading, spacing: 8) { HStack { Image(systemName: "checkmark.circle.fill") - .foregroundColor(.green) + .foregroundColor(Color.appAccent) Text("Completions (\(task.completions.count))") .font(.caption) .fontWeight(.semibold) - .foregroundColor(.primary) + .foregroundColor(Color.appTextPrimary) Spacer() Image(systemName: isCompletionsExpanded ? "chevron.up" : "chevron.down") .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } .contentShape(Rectangle()) .onTapGesture { @@ -102,12 +102,12 @@ struct DynamicTaskCard: View { } .frame(maxWidth: .infinity) .padding(.vertical, 12) - .background(Color.blue.opacity(0.1)) - .foregroundColor(.blue) + .background(Color.appPrimary.opacity(0.1)) + .foregroundColor(Color.appPrimary) .cornerRadius(8) .overlay( RoundedRectangle(cornerRadius: 8) - .stroke(Color.blue, lineWidth: 2) + .stroke(Color.appPrimary, lineWidth: 2) ) } .zIndex(10) @@ -115,7 +115,7 @@ struct DynamicTaskCard: View { } } .padding(16) - .background(Color(.secondarySystemGroupedBackground)) + .background(Color.appBackgroundSecondary) .cornerRadius(12) .shadow(color: Color.black.opacity(0.1), radius: 5, x: 0, y: 2) .simultaneousGesture(TapGesture(), including: .subviews) diff --git a/iosApp/iosApp/Subviews/Task/DynamicTaskColumnView.swift b/iosApp/iosApp/Subviews/Task/DynamicTaskColumnView.swift index bcced8a..b981902 100644 --- a/iosApp/iosApp/Subviews/Task/DynamicTaskColumnView.swift +++ b/iosApp/iosApp/Subviews/Task/DynamicTaskColumnView.swift @@ -18,7 +18,7 @@ struct DynamicTaskColumnView: View { } private var columnColor: Color { - Color(hex: column.color) ?? .primary + Color(hex: column.color) ?? Color.appTextPrimary } var body: some View { @@ -40,7 +40,7 @@ struct DynamicTaskColumnView: View { Text("\(column.count)") .font(.caption) .fontWeight(.semibold) - .foregroundColor(.white) + .foregroundColor(Color.appTextOnPrimary) .padding(.horizontal, 8) .padding(.vertical, 4) .background(columnColor) @@ -55,7 +55,7 @@ struct DynamicTaskColumnView: View { Text("No tasks") .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } .frame(maxWidth: .infinity) .padding(.top, 40) diff --git a/iosApp/iosApp/Subviews/Task/EmptyTasksView.swift b/iosApp/iosApp/Subviews/Task/EmptyTasksView.swift index a9fddd3..8d1b5cc 100644 --- a/iosApp/iosApp/Subviews/Task/EmptyTasksView.swift +++ b/iosApp/iosApp/Subviews/Task/EmptyTasksView.swift @@ -5,15 +5,15 @@ struct EmptyTasksView: View { VStack(spacing: 12) { Image(systemName: "checkmark.circle") .font(.system(size: 48)) - .foregroundColor(.gray.opacity(0.5)) + .foregroundColor(Color.appTextSecondary.opacity(0.5)) Text("No tasks yet") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } .frame(maxWidth: .infinity) .padding(32) - .background(Color(.systemBackground)) + .background(Color.appBackgroundSecondary) .cornerRadius(12) } } diff --git a/iosApp/iosApp/Subviews/Task/PhotoViewerSheet.swift b/iosApp/iosApp/Subviews/Task/PhotoViewerSheet.swift index 3025640..e0eaf8a 100644 --- a/iosApp/iosApp/Subviews/Task/PhotoViewerSheet.swift +++ b/iosApp/iosApp/Subviews/Task/PhotoViewerSheet.swift @@ -32,9 +32,9 @@ struct PhotoViewerSheet: View { VStack { Image(systemName: "photo") .font(.system(size: 60)) - .foregroundColor(.gray) + .foregroundColor(Color.appTextSecondary) Text("Failed to load image") - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } .frame(height: 300) @unknown default: @@ -46,12 +46,14 @@ struct PhotoViewerSheet: View { VStack(alignment: .leading, spacing: 8) { Text("Caption") .font(.headline) + .foregroundColor(Color.appTextPrimary) Text(caption) .font(.body) + .foregroundColor(Color.appTextPrimary) } .frame(maxWidth: .infinity, alignment: .leading) .padding() - .background(Color(.systemGray6)) + .background(Color.appBackgroundSecondary) .cornerRadius(12) .padding(.horizontal) } @@ -104,10 +106,10 @@ struct PhotoViewerSheet: View { VStack { Image(systemName: "photo") .font(.system(size: 40)) - .foregroundColor(.gray) + .foregroundColor(Color.appTextSecondary) Text("Failed to load") .font(.caption2) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } .frame(height: 150) @unknown default: @@ -119,7 +121,7 @@ struct PhotoViewerSheet: View { if let caption = image.caption { Text(caption) .font(.caption2) - .foregroundColor(.primary) + .foregroundColor(Color.appTextPrimary) .lineLimit(2) .multilineTextAlignment(.leading) } diff --git a/iosApp/iosApp/Subviews/Task/PriorityBadge.swift b/iosApp/iosApp/Subviews/Task/PriorityBadge.swift index e789bfa..98bc1a2 100644 --- a/iosApp/iosApp/Subviews/Task/PriorityBadge.swift +++ b/iosApp/iosApp/Subviews/Task/PriorityBadge.swift @@ -21,10 +21,10 @@ struct PriorityBadge: View { private var priorityColor: Color { switch priority.lowercased() { - case "high": return .red - case "medium": return .orange - case "low": return .green - default: return Color(.tertiaryLabel) + case "high": return Color.appError + case "medium": return Color.appAccent + case "low": return Color.appPrimary + default: return Color.appTextSecondary.opacity(0.7) } } } @@ -36,5 +36,5 @@ struct PriorityBadge: View { PriorityBadge(priority: "low") } .padding() - .background(Color(.systemGroupedBackground)) + .background(Color.appBackgroundPrimary) } diff --git a/iosApp/iosApp/Subviews/Task/StatusBadge.swift b/iosApp/iosApp/Subviews/Task/StatusBadge.swift index b42fc83..7b3063c 100644 --- a/iosApp/iosApp/Subviews/Task/StatusBadge.swift +++ b/iosApp/iosApp/Subviews/Task/StatusBadge.swift @@ -24,11 +24,11 @@ struct StatusBadge: View { private var statusColor: Color { switch status { - case "completed": return .green - case "in_progress": return .orange - case "pending": return .orange - case "cancelled": return .red - default: return Color(.tertiaryLabel) + case "completed": return Color.appPrimary + case "in_progress": return Color.appAccent + case "pending": return Color.appAccent + case "cancelled": return Color.appError + default: return Color.appTextSecondary.opacity(0.7) } } } @@ -41,5 +41,5 @@ struct StatusBadge: View { StatusBadge(status: "cancelled") } .padding() - .background(Color(.systemGroupedBackground)) + .background(Color.appBackgroundPrimary) } diff --git a/iosApp/iosApp/Subviews/Task/TaskActionButtons.swift b/iosApp/iosApp/Subviews/Task/TaskActionButtons.swift index c6d5d12..72f62a9 100644 --- a/iosApp/iosApp/Subviews/Task/TaskActionButtons.swift +++ b/iosApp/iosApp/Subviews/Task/TaskActionButtons.swift @@ -38,7 +38,7 @@ struct CancelTaskButton: View { .frame(maxWidth: .infinity) } .buttonStyle(.bordered) - .tint(.red) + .tint(Color.appError) .alert("Cancel Task", isPresented: $showConfirmation) { Button("Cancel", role: .cancel) { } Button("Cancel Task", role: .destructive) { @@ -79,7 +79,7 @@ struct UncancelTaskButton: View { .frame(maxWidth: .infinity) } .buttonStyle(.borderedProminent) - .tint(.blue) + .tint(Color.appPrimary) } } @@ -111,7 +111,7 @@ struct MarkInProgressButton: View { .frame(maxWidth: .infinity) } .buttonStyle(.bordered) - .tint(.orange) + .tint(Color.appAccent) } } @@ -198,6 +198,6 @@ struct UnarchiveTaskButton: View { .frame(maxWidth: .infinity) } .buttonStyle(.bordered) - .tint(.blue) + .tint(Color.appPrimary) } } diff --git a/iosApp/iosApp/Subviews/Task/TaskCard.swift b/iosApp/iosApp/Subviews/Task/TaskCard.swift index 759f92a..fc3f05f 100644 --- a/iosApp/iosApp/Subviews/Task/TaskCard.swift +++ b/iosApp/iosApp/Subviews/Task/TaskCard.swift @@ -20,7 +20,7 @@ struct TaskCard: View { VStack(alignment: .leading, spacing: AppSpacing.xs) { Text(task.title) .font(.title3.weight(.semibold)) - .foregroundColor(Color(.label)) + .foregroundColor(Color.appTextPrimary) .lineLimit(2) if let status = task.status { @@ -37,7 +37,7 @@ struct TaskCard: View { if let description = task.description_, !description.isEmpty { Text(description) .font(.callout) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) .lineLimit(3) } @@ -46,14 +46,14 @@ struct TaskCard: View { HStack(spacing: AppSpacing.xxs) { Image(systemName: "repeat") .font(.system(size: 12, weight: .medium)) - .foregroundColor(Color(.tertiaryLabel)) + .foregroundColor(Color.appTextSecondary.opacity(0.7)) Text(task.frequency.displayName) .font(.caption.weight(.medium)) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) } .padding(.horizontal, AppSpacing.sm) .padding(.vertical, AppSpacing.xxs) - .background(Color(.tertiarySystemGroupedBackground)) + .background(Color.appBackgroundSecondary) .cornerRadius(AppRadius.xs) Spacer() @@ -62,14 +62,14 @@ struct TaskCard: View { HStack(spacing: AppSpacing.xxs) { Image(systemName: "calendar") .font(.system(size: 12, weight: .medium)) - .foregroundColor(Color(.tertiaryLabel)) + .foregroundColor(Color.appTextSecondary.opacity(0.7)) Text(formatDate(dueDate)) .font(.caption.weight(.medium)) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) } .padding(.horizontal, AppSpacing.sm) .padding(.vertical, AppSpacing.xxs) - .background(Color(.tertiarySystemGroupedBackground)) + .background(Color.appBackgroundSecondary) .cornerRadius(AppRadius.xs) } } @@ -83,20 +83,20 @@ struct TaskCard: View { HStack(spacing: AppSpacing.xs) { ZStack { Circle() - .fill(Color.green.opacity(0.1)) + .fill(Color.appAccent.opacity(0.1)) .frame(width: 24, height: 24) Image(systemName: "checkmark.circle.fill") .font(.system(size: 14, weight: .semibold)) - .foregroundColor(.green) + .foregroundColor(Color.appAccent) } Text("Completions (\(task.completions.count))") .font(.footnote.weight(.medium)) .fontWeight(.semibold) - .foregroundColor(Color(.label)) + .foregroundColor(Color.appTextPrimary) Spacer() Image(systemName: isCompletionsExpanded ? "chevron.up" : "chevron.down") .font(.caption) - .foregroundColor(Color(.secondaryLabel)) + .foregroundColor(Color.appTextSecondary) } .contentShape(Rectangle()) .onTapGesture { @@ -127,8 +127,8 @@ struct TaskCard: View { } .frame(maxWidth: .infinity) .frame(height: 44) - .foregroundColor(.orange) - .background(Color.orange.opacity(0.1)) + .foregroundColor(Color.appAccent) + .background(Color.appAccent.opacity(0.1)) .cornerRadius(AppRadius.md) } } @@ -144,8 +144,8 @@ struct TaskCard: View { } .frame(maxWidth: .infinity) .frame(height: 44) - .foregroundColor(.white) - .background(.green) + .foregroundColor(Color.appTextOnPrimary) + .background(Color.appPrimary) .cornerRadius(AppRadius.md) } } @@ -164,8 +164,8 @@ struct TaskCard: View { } .frame(maxWidth: .infinity) .frame(height: 36) - .foregroundColor(.blue) - .background(Color(.tertiarySystemGroupedBackground)) + .foregroundColor(Color.appPrimary) + .background(Color.appBackgroundSecondary) .cornerRadius(AppRadius.sm) } @@ -179,8 +179,8 @@ struct TaskCard: View { } .frame(maxWidth: .infinity) .frame(height: 36) - .foregroundColor(.red) - .background(Color.red.opacity(0.1)) + .foregroundColor(Color.appError) + .background(Color.appError.opacity(0.1)) .cornerRadius(AppRadius.sm) } } else if let onUncancel = onUncancel { @@ -193,8 +193,8 @@ struct TaskCard: View { } .frame(maxWidth: .infinity) .frame(height: 36) - .foregroundColor(.white) - .background(.blue) + .foregroundColor(Color.appTextOnPrimary) + .background(Color.appPrimary) .cornerRadius(AppRadius.sm) } } @@ -211,8 +211,8 @@ struct TaskCard: View { } .frame(maxWidth: .infinity) .frame(height: 36) - .foregroundColor(.blue) - .background(Color(.tertiarySystemGroupedBackground)) + .foregroundColor(Color.appPrimary) + .background(Color.appBackgroundSecondary) .cornerRadius(AppRadius.sm) } } @@ -227,8 +227,8 @@ struct TaskCard: View { } .frame(maxWidth: .infinity) .frame(height: 36) - .foregroundColor(Color(.secondaryLabel)) - .background(Color(.tertiarySystemGroupedBackground)) + .foregroundColor(Color.appTextSecondary) + .background(Color.appBackgroundSecondary) .cornerRadius(AppRadius.sm) } } @@ -236,7 +236,7 @@ struct TaskCard: View { } } .padding(AppSpacing.md) - .background(Color(.secondarySystemGroupedBackground)) + .background(Color.appBackgroundSecondary) .cornerRadius(AppRadius.lg) .shadow(color: AppShadow.md.color, radius: AppShadow.md.radius, x: AppShadow.md.x, y: AppShadow.md.y) } @@ -287,5 +287,5 @@ struct TaskCard: View { ) } .padding() - .background(Color(.systemGroupedBackground)) + .background(Color.appBackgroundPrimary) } diff --git a/iosApp/iosApp/Subviews/Task/TaskPill.swift b/iosApp/iosApp/Subviews/Task/TaskPill.swift index 7980fe6..45c54fd 100644 --- a/iosApp/iosApp/Subviews/Task/TaskPill.swift +++ b/iosApp/iosApp/Subviews/Task/TaskPill.swift @@ -24,9 +24,9 @@ struct TaskPill: View { #Preview { HStack(spacing: 8) { - TaskPill(count: 12, label: "Total", color: .blue) - TaskPill(count: 5, label: "Pending", color: .orange) - TaskPill(count: 3, label: "Done", color: .green) + TaskPill(count: 12, label: "Total", color: Color.appPrimary) + TaskPill(count: 5, label: "Pending", color: Color.appAccent) + TaskPill(count: 3, label: "Done", color: Color.appPrimary) } .padding() } diff --git a/iosApp/iosApp/Subviews/Task/TasksSection.swift b/iosApp/iosApp/Subviews/Task/TasksSection.swift index c1a76c1..5185cc1 100644 --- a/iosApp/iosApp/Subviews/Task/TasksSection.swift +++ b/iosApp/iosApp/Subviews/Task/TasksSection.swift @@ -20,6 +20,7 @@ struct TasksSection: View { Text("Tasks") .font(.title2) .fontWeight(.bold) + .foregroundColor(Color.appTextPrimary) if hasNoTasks { EmptyTasksView() diff --git a/iosApp/iosApp/Task/AllTasksView.swift b/iosApp/iosApp/Task/AllTasksView.swift index 43c99d1..d48d805 100644 --- a/iosApp/iosApp/Task/AllTasksView.swift +++ b/iosApp/iosApp/Task/AllTasksView.swift @@ -99,7 +99,7 @@ struct AllTasksView: View { @ViewBuilder private var mainContent: some View { ZStack { - Color(.systemGroupedBackground) + Color.appBackgroundPrimary .ignoresSafeArea() if hasNoTasks && isLoadingTasks { @@ -113,18 +113,19 @@ struct AllTasksView: View { // Empty state with big button VStack(spacing: 24) { Spacer() - + Image(systemName: "checklist") .font(.system(size: 64)) - .foregroundStyle(.blue.opacity(0.6)) - + .foregroundStyle(Color.appPrimary.opacity(0.6)) + Text("No tasks yet") .font(.title2) .fontWeight(.semibold) - + .foregroundColor(Color.appTextPrimary) + Text("Create your first task to get started") .font(.body) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) .multilineTextAlignment(.center) Button(action: { @@ -147,7 +148,7 @@ struct AllTasksView: View { if residenceViewModel.myResidences?.residences.isEmpty ?? true { Text("Add a property first from the Residences tab") .font(.caption) - .foregroundColor(.red) + .foregroundColor(Color.appError) } Spacer() @@ -216,6 +217,8 @@ struct AllTasksView: View { } } } + .scrollContentBackground(.hidden) + .background(Color.appBackgroundPrimary) .navigationTitle("All Tasks") .navigationBarTitleDisplayMode(.inline) .toolbar { diff --git a/iosApp/iosApp/Task/CompleteTaskView.swift b/iosApp/iosApp/Task/CompleteTaskView.swift index fa267b1..350ab2f 100644 --- a/iosApp/iosApp/Task/CompleteTaskView.swift +++ b/iosApp/iosApp/Task/CompleteTaskView.swift @@ -52,6 +52,7 @@ struct CompleteTaskView: View { } header: { Text("Task Details") } + .listRowBackground(Color.appBackgroundSecondary) // Contractor Selection Section Section { @@ -89,6 +90,7 @@ struct CompleteTaskView: View { } footer: { Text("Select a contractor if they completed this work, or leave blank for manual entry.") } + .listRowBackground(Color.appBackgroundSecondary) // Completion Details Section Section { @@ -117,6 +119,7 @@ struct CompleteTaskView: View { } footer: { Text("Add any additional details about completing this task.") } + .listRowBackground(Color.appBackgroundSecondary) // Notes Section Section { @@ -132,6 +135,7 @@ struct CompleteTaskView: View { } footer: { Text("Optional notes about the work completed.") } + .listRowBackground(Color.appBackgroundSecondary) // Rating Section Section { @@ -165,6 +169,7 @@ struct CompleteTaskView: View { } footer: { Text("Rate the quality of work from 1 to 5 stars.") } + .listRowBackground(Color.appBackgroundSecondary) // Images Section Section { @@ -175,7 +180,7 @@ struct CompleteTaskView: View { }) { Label("Take Photo", systemImage: "camera") .frame(maxWidth: .infinity) - .foregroundStyle(.blue) + .foregroundStyle(Color.appPrimary) } .buttonStyle(.bordered) @@ -187,7 +192,7 @@ struct CompleteTaskView: View { ) { Label("Library", systemImage: "photo.on.rectangle.angled") .frame(maxWidth: .infinity) - .foregroundStyle(.blue) + .foregroundStyle(Color.appPrimary) } .buttonStyle(.bordered) } @@ -228,6 +233,7 @@ struct CompleteTaskView: View { } footer: { Text("Add up to 5 photos documenting the completed work.") } + .listRowBackground(Color.appBackgroundSecondary) // Complete Button Section Section { @@ -243,11 +249,14 @@ struct CompleteTaskView: View { .frame(maxWidth: .infinity) .fontWeight(.semibold) } - .listRowBackground(isSubmitting ? Color.gray : Color.green) - .foregroundStyle(.white) + .listRowBackground(isSubmitting ? Color.gray : Color.appPrimary) + .foregroundStyle(Color.appTextOnPrimary) .disabled(isSubmitting) } } + .listStyle(.plain) + .scrollContentBackground(.hidden) + .background(Color.appBackgroundPrimary) .navigationTitle("Complete Task") .navigationBarTitleDisplayMode(.inline) .toolbar { @@ -397,7 +406,7 @@ struct ContractorPickerView: View { Spacer() if selectedContractor == nil { Image(systemName: "checkmark") - .foregroundStyle(.blue) + .foregroundStyle(Color.appPrimary) } } } @@ -411,7 +420,7 @@ struct ContractorPickerView: View { } } else if let errorMessage = contractorViewModel.errorMessage { Text(errorMessage) - .foregroundStyle(.red) + .foregroundStyle(Color.appError) .font(.caption) } else { ForEach(contractorViewModel.contractors, id: \.id) { contractor in @@ -445,7 +454,7 @@ struct ContractorPickerView: View { if selectedContractor?.id == contractor.id { Image(systemName: "checkmark") - .foregroundStyle(.blue) + .foregroundStyle(Color.appPrimary) } } } diff --git a/iosApp/iosApp/Task/TaskFormView.swift b/iosApp/iosApp/Task/TaskFormView.swift index d4b8d81..113e6be 100644 --- a/iosApp/iosApp/Task/TaskFormView.swift +++ b/iosApp/iosApp/Task/TaskFormView.swift @@ -107,15 +107,16 @@ struct TaskFormView: View { if !residenceError.isEmpty { Text(residenceError) .font(.caption) - .foregroundColor(.red) + .foregroundColor(Color.appError) } } header: { Text("Property") } footer: { Text("Required") .font(.caption) - .foregroundColor(.red) + .foregroundColor(Color.appError) } + .listRowBackground(Color.appBackgroundSecondary) } Section { @@ -125,7 +126,7 @@ struct TaskFormView: View { if !titleError.isEmpty { Text(titleError) .font(.caption) - .foregroundColor(.red) + .foregroundColor(Color.appError) } TextField("Description (optional)", text: $description, axis: .vertical) @@ -136,8 +137,9 @@ struct TaskFormView: View { } footer: { Text("Required: Title") .font(.caption) - .foregroundColor(.red) + .foregroundColor(Color.appError) } + .listRowBackground(Color.appBackgroundSecondary) Section { Picker("Category", selection: $selectedCategory) { @@ -151,8 +153,9 @@ struct TaskFormView: View { } footer: { Text("Required") .font(.caption) - .foregroundColor(.red) + .foregroundColor(Color.appError) } + .listRowBackground(Color.appBackgroundSecondary) Section { Picker("Frequency", selection: $selectedFrequency) { @@ -174,8 +177,9 @@ struct TaskFormView: View { } footer: { Text("Required: Frequency") .font(.caption) - .foregroundColor(.red) + .foregroundColor(Color.appError) } + .listRowBackground(Color.appBackgroundSecondary) Section { Picker("Priority", selection: $selectedPriority) { @@ -196,21 +200,24 @@ struct TaskFormView: View { } footer: { Text("Required: Both Priority and Status") .font(.caption) - .foregroundColor(.red) + .foregroundColor(Color.appError) } + .listRowBackground(Color.appBackgroundSecondary) Section(header: Text("Cost")) { TextField("Estimated Cost (optional)", text: $estimatedCost) .keyboardType(.decimalPad) .focused($focusedField, equals: .estimatedCost) } + .listRowBackground(Color.appBackgroundSecondary) if let errorMessage = viewModel.errorMessage { Section { Text(errorMessage) - .foregroundColor(.red) + .foregroundColor(Color.appError) .font(.caption) } + .listRowBackground(Color.appBackgroundSecondary) } } .disabled(isLoadingLookups) @@ -221,12 +228,15 @@ struct TaskFormView: View { ProgressView() .scaleEffect(1.5) Text("Loading...") - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) } .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(Color(uiColor: .systemBackground).opacity(0.8)) + .background(Color.appBackgroundPrimary.opacity(0.8)) } } + .listStyle(.plain) + .scrollContentBackground(.hidden) + .background(Color.appBackgroundPrimary) .navigationTitle(isEditMode ? "Edit Task" : "Add Task") .navigationBarTitleDisplayMode(.inline) .toolbar { diff --git a/iosApp/iosApp/VerifyEmail/VerifyEmailView.swift b/iosApp/iosApp/VerifyEmail/VerifyEmailView.swift index 9652e94..09e935f 100644 --- a/iosApp/iosApp/VerifyEmail/VerifyEmailView.swift +++ b/iosApp/iosApp/VerifyEmail/VerifyEmailView.swift @@ -9,7 +9,7 @@ struct VerifyEmailView: View { var body: some View { NavigationView { ZStack { - Color(.systemGroupedBackground) + Color.appBackgroundPrimary .ignoresSafeArea() ScrollView { @@ -20,16 +20,17 @@ struct VerifyEmailView: View { VStack(spacing: 12) { Image(systemName: "envelope.badge.shield.half.filled") .font(.system(size: 60)) - .foregroundStyle(.blue.gradient) + .foregroundStyle(Color.appPrimary.gradient) .padding(.bottom, 8) Text("Verify Your Email") .font(.title) .fontWeight(.bold) + .foregroundColor(Color.appTextPrimary) Text("You must verify your email address to continue") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) .multilineTextAlignment(.center) .padding(.horizontal) } @@ -38,12 +39,12 @@ struct VerifyEmailView: View { GroupBox { HStack(spacing: 12) { Image(systemName: "exclamationmark.shield.fill") - .foregroundColor(.orange) + .foregroundColor(Color.appAccent) .font(.title2) Text("Email verification is required. Check your inbox for a 6-digit code.") .font(.subheadline) - .foregroundColor(.primary) + .foregroundColor(Color.appTextPrimary) .fontWeight(.semibold) } .padding(.vertical, 4) @@ -54,6 +55,7 @@ struct VerifyEmailView: View { VStack(alignment: .leading, spacing: 12) { Text("Verification Code") .font(.headline) + .foregroundColor(Color.appTextPrimary) .padding(.horizontal) TextField("000000", text: $viewModel.code) @@ -75,7 +77,7 @@ struct VerifyEmailView: View { Text("Code must be 6 digits") .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) .padding(.horizontal) } @@ -103,10 +105,10 @@ struct VerifyEmailView: View { .frame(height: 50) .background( viewModel.code.count == 6 && !viewModel.isLoading - ? Color.blue + ? Color.appPrimary : Color.gray.opacity(0.3) ) - .foregroundColor(.white) + .foregroundColor(Color.appTextOnPrimary) .cornerRadius(12) } .disabled(viewModel.code.count != 6 || viewModel.isLoading) @@ -117,12 +119,15 @@ struct VerifyEmailView: View { // Help Text Text("Didn't receive the code? Check your spam folder or contact support.") .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(Color.appTextSecondary) .multilineTextAlignment(.center) .padding(.horizontal, 32) } } } + .listStyle(.plain) + .scrollContentBackground(.hidden) + .background(Color.appBackgroundPrimary) .navigationBarTitleDisplayMode(.inline) .navigationBarBackButtonHidden(true) .interactiveDismissDisabled(true)