Implement custom 5-color design system across entire iOS app

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 <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-11-21 07:58:01 -06:00
parent a4ba6794d5
commit a2b81a244b
70 changed files with 920 additions and 417 deletions

View File

@@ -1,7 +1,7 @@
{
"images" : [
{
"filename" : "icon.png",
"filename" : "logo_primary.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.031",
"green" : "0.784",
"red" : "0.941"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.031",
"green" : "0.784",
"red" : "0.941"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.816",
"green" : "0.945",
"red" : "1.000"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.161",
"green" : "0.098",
"red" : "0.039"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.247",
"green" : "0.184",
"red" : "0.102"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.102",
"green" : "0.110",
"red" : "0.867"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.267",
"green" : "0.267",
"red" : "1.000"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.765",
"green" : "0.627",
"red" : "0.027"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.765",
"green" : "0.627",
"red" : "0.027"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.533",
"green" : "0.404",
"red" : "0.031"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.745",
"green" : "0.624",
"red" : "0.039"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.533",
"green" : "0.404",
"red" : "0.031"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.816",
"green" : "0.945",
"red" : "1.000"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "0.600",
"blue" : "0.533",
"green" : "0.404",
"red" : "0.031"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "0.700",
"blue" : "0.816",
"green" : "0.945",
"red" : "1.000"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -26,13 +26,14 @@ struct TaskSummaryCard: View {
Text("Tasks")
.font(.headline)
.fontWeight(.bold)
.foregroundColor(Color.appTextPrimary)
ForEach(filteredCategories, id: \.name) { category in
TaskCategoryRow(category: category)
}
}
.padding(16)
.background(Color(.systemBackground))
.background(Color.appBackgroundSecondary)
.cornerRadius(12)
.shadow(color: Color.black.opacity(0.1), radius: 4, x: 0, y: 2)
}
@@ -55,14 +56,14 @@ struct TaskCategoryRow: View {
.frame(width: 32, height: 32)
Image(systemName: category.icons.ios)
.foregroundColor(.white)
.foregroundColor(Color.appTextOnPrimary)
.font(.system(size: 14, weight: .semibold))
}
// Category name
Text(category.displayName)
.font(.body)
.foregroundColor(.primary)
.foregroundColor(Color.appTextPrimary)
Spacer()
@@ -70,7 +71,7 @@ struct TaskCategoryRow: View {
Text("\(category.count)")
.font(.subheadline)
.fontWeight(.bold)
.foregroundColor(.white)
.foregroundColor(Color.appTextOnPrimary)
.padding(.horizontal, 12)
.padding(.vertical, 4)
.background(categoryColor)

View File

@@ -10,12 +10,12 @@ struct ContractorCard: View {
// Avatar
ZStack {
Circle()
.fill(.blue.opacity(0.1))
.fill(Color.appPrimary.opacity(0.1))
.frame(width: 56, height: 56)
Image(systemName: "person.fill")
.font(.title2)
.foregroundColor(.blue)
.foregroundColor(Color.appPrimary)
}
// Content
@@ -24,13 +24,13 @@ struct ContractorCard: View {
HStack(spacing: AppSpacing.xxs) {
Text(contractor.name)
.font(.title3.weight(.semibold))
.foregroundColor(Color(.label))
.foregroundColor(Color.appTextPrimary)
.lineLimit(1)
if contractor.isFavorite {
Image(systemName: "star.fill")
.font(.caption)
.foregroundColor(.orange)
.foregroundColor(Color.appAccent)
}
}
@@ -38,7 +38,7 @@ struct ContractorCard: View {
if let company = contractor.company {
Text(company)
.font(.body)
.foregroundColor(Color(.secondaryLabel))
.foregroundColor(Color.appTextSecondary)
.lineLimit(1)
}
@@ -48,21 +48,21 @@ struct ContractorCard: View {
if let specialty = contractor.specialty {
Label(specialty, systemImage: "wrench.and.screwdriver")
.font(.caption.weight(.medium))
.foregroundColor(Color(.secondaryLabel))
.foregroundColor(Color.appTextSecondary)
}
// Rating
if let rating = contractor.averageRating, rating.doubleValue > 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)
}

View File

@@ -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<Content: View>: 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()

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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
)
}
}

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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)
}
}
}

View File

@@ -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))
}
}
}

View File

@@ -51,6 +51,7 @@ struct MainTabView: View {
.tag(4)
.accessibilityIdentifier(AccessibilityIdentifiers.Navigation.profileTab)
}
.tint(Color.appPrimary)
.onChange(of: authManager.isAuthenticated) { _ in
selectedTab = 0
}

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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)

View File

@@ -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()

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}

View File

@@ -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))

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)

View File

@@ -19,7 +19,7 @@ struct TaskStatChip: View {
Text(label)
.font(.caption)
.foregroundColor(.secondary)
.foregroundColor(Color.appTextSecondary)
}
}
}

View File

@@ -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)
}

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)
}
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}
}

View File

@@ -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)
}

View File

@@ -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()
}

View File

@@ -20,6 +20,7 @@ struct TasksSection: View {
Text("Tasks")
.font(.title2)
.fontWeight(.bold)
.foregroundColor(Color.appTextPrimary)
if hasNoTasks {
EmptyTasksView()

View File

@@ -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 {

View File

@@ -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)
}
}
}

View File

@@ -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 {

View File

@@ -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)