Migrate iOS app to system colors and improve UI/UX

- Remove AppColors struct, migrate to iOS system colors throughout
- Redesign ContractorFormSheet to use native SwiftUI Form components
- Add color-coded icons to contractor form sections
- Improve dark mode contrast for task cards
- Add background colors to document detail fields
- Fix text alignment issues in ContractorDetailView
- Make task completion lists expandable/collapsible by default
- Clear app badge on launch and when app becomes active
- Update button styling with proper gradients and shadows
- Improve form field focus states and accessibility

🤖 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-13 22:22:52 -06:00
parent 29c136d612
commit a2de0f3454
29 changed files with 475 additions and 593 deletions

View File

@@ -77,7 +77,7 @@ fun DocumentsScreen(
Icon( Icon(
if (showActiveOnly) Icons.Default.CheckCircle else Icons.Default.CheckCircleOutline, if (showActiveOnly) Icons.Default.CheckCircle else Icons.Default.CheckCircleOutline,
"Filter active", "Filter active",
tint = if (showActiveOnly) Color(0xFF10B981) else LocalContentColor.current tint = if (showActiveOnly) MaterialTheme.colorScheme.secondary else LocalContentColor.current
) )
} }
} }
@@ -89,7 +89,7 @@ fun DocumentsScreen(
Icons.Default.FilterList, Icons.Default.FilterList,
"Filters", "Filters",
tint = if (selectedCategory != null || selectedDocType != null) tint = if (selectedCategory != null || selectedDocType != null)
Color(0xFF3B82F6) else LocalContentColor.current MaterialTheme.colorScheme.primary else LocalContentColor.current
) )
} }

View File

@@ -4,7 +4,7 @@
<dict> <dict>
<key>com.apple.security.application-groups</key> <key>com.apple.security.application-groups</key>
<array> <array>
<string>group.com.tt.mycrib.MyCrib</string> <string>group.com.tt.mycrib.MyCribDev</string>
</array> </array>
</dict> </dict>
</plist> </plist>

View File

@@ -394,7 +394,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.tt.mycrib.MyCrib; PRODUCT_BUNDLE_IDENTIFIER = com.tt.mycrib.MyCribDev;
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
@@ -423,7 +423,7 @@
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.tt.mycrib.MyCrib.MyCrib; PRODUCT_BUNDLE_IDENTIFIER = com.tt.mycrib.MyCribDev.MyCribDev;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -457,7 +457,7 @@
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.tt.mycrib.MyCrib.MyCrib; PRODUCT_BUNDLE_IDENTIFIER = com.tt.mycrib.MyCribDev.MyCribDev;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -663,7 +663,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.tt.mycrib.MyCrib; PRODUCT_BUNDLE_IDENTIFIER = com.tt.mycrib.MyCribDev;
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";

View File

@@ -10,12 +10,12 @@ struct ContractorCard: View {
// Avatar // Avatar
ZStack { ZStack {
Circle() Circle()
.fill(AppColors.primary.opacity(0.1)) .fill(.blue.opacity(0.1))
.frame(width: 56, height: 56) .frame(width: 56, height: 56)
Image(systemName: "person.fill") Image(systemName: "person.fill")
.font(.title2) .font(.title2)
.foregroundColor(AppColors.primary) .foregroundColor(.blue)
} }
// Content // Content
@@ -24,13 +24,13 @@ struct ContractorCard: View {
HStack(spacing: AppSpacing.xxs) { HStack(spacing: AppSpacing.xxs) {
Text(contractor.name) Text(contractor.name)
.font(.title3.weight(.semibold)) .font(.title3.weight(.semibold))
.foregroundColor(AppColors.textPrimary) .foregroundColor(Color(.label))
.lineLimit(1) .lineLimit(1)
if contractor.isFavorite { if contractor.isFavorite {
Image(systemName: "star.fill") Image(systemName: "star.fill")
.font(.caption) .font(.caption)
.foregroundColor(AppColors.warning) .foregroundColor(.orange)
} }
} }
@@ -38,7 +38,7 @@ struct ContractorCard: View {
if let company = contractor.company { if let company = contractor.company {
Text(company) Text(company)
.font(.body) .font(.body)
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
.lineLimit(1) .lineLimit(1)
} }
@@ -48,21 +48,21 @@ struct ContractorCard: View {
if let specialty = contractor.specialty { if let specialty = contractor.specialty {
Label(specialty, systemImage: "wrench.and.screwdriver") Label(specialty, systemImage: "wrench.and.screwdriver")
.font(.caption.weight(.medium)) .font(.caption.weight(.medium))
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
} }
// Rating // Rating
if let rating = contractor.averageRating, rating.doubleValue > 0 { if let rating = contractor.averageRating, rating.doubleValue > 0 {
Label(String(format: "%.1f", rating.doubleValue), systemImage: "star.fill") Label(String(format: "%.1f", rating.doubleValue), systemImage: "star.fill")
.font(.caption.weight(.medium)) .font(.caption.weight(.medium))
.foregroundColor(AppColors.warning) .foregroundColor(.orange)
} }
// Task count // Task count
if contractor.taskCount > 0 { if contractor.taskCount > 0 {
Label("\(contractor.taskCount) tasks", systemImage: "checkmark.circle") Label("\(contractor.taskCount) tasks", systemImage: "checkmark.circle")
.font(.caption.weight(.medium)) .font(.caption.weight(.medium))
.foregroundColor(AppColors.success) .foregroundColor(.green)
} }
} }
} }
@@ -73,17 +73,17 @@ struct ContractorCard: View {
Button(action: onToggleFavorite) { Button(action: onToggleFavorite) {
Image(systemName: contractor.isFavorite ? "star.fill" : "star") Image(systemName: contractor.isFavorite ? "star.fill" : "star")
.font(.title3) .font(.title3)
.foregroundColor(contractor.isFavorite ? AppColors.warning : AppColors.textTertiary) .foregroundColor(contractor.isFavorite ? .orange : Color(.tertiaryLabel))
} }
.buttonStyle(PlainButtonStyle()) .buttonStyle(PlainButtonStyle())
// Chevron // Chevron
Image(systemName: "chevron.right") Image(systemName: "chevron.right")
.font(.caption) .font(.caption)
.foregroundColor(AppColors.textTertiary) .foregroundColor(Color(.tertiaryLabel))
} }
.padding(AppSpacing.md) .padding(AppSpacing.md)
.background(AppColors.surface) .background(Color(.secondarySystemGroupedBackground))
.cornerRadius(AppRadius.lg) .cornerRadius(AppRadius.lg)
.shadow(color: AppShadow.sm.color, radius: AppShadow.sm.radius, x: AppShadow.sm.x, y: AppShadow.sm.y) .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 { var body: some View {
ZStack { ZStack {
AppColors.background.ignoresSafeArea() Color(.systemGroupedBackground).ignoresSafeArea()
if viewModel.isLoading { if viewModel.isLoading {
ProgressView() ProgressView()
@@ -29,24 +29,24 @@ struct ContractorDetailView: View {
// Avatar // Avatar
ZStack { ZStack {
Circle() Circle()
.fill(AppColors.primary.opacity(0.1)) .fill(.blue.opacity(0.1))
.frame(width: 80, height: 80) .frame(width: 80, height: 80)
Image(systemName: "person.fill") Image(systemName: "person.fill")
.font(.system(size: 40)) .font(.system(size: 40))
.foregroundColor(AppColors.primary) .foregroundColor(.blue)
} }
// Name // Name
Text(contractor.name) Text(contractor.name)
.font(.title3.weight(.semibold)) .font(.title3.weight(.semibold))
.foregroundColor(AppColors.textPrimary) .foregroundColor(Color(.label))
// Company // Company
if let company = contractor.company { if let company = contractor.company {
Text(company) Text(company)
.font(.title3.weight(.semibold)) .font(.title3.weight(.semibold))
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
} }
// Specialty Badge // Specialty Badge
@@ -59,8 +59,8 @@ struct ContractorDetailView: View {
} }
.padding(.horizontal, AppSpacing.sm) .padding(.horizontal, AppSpacing.sm)
.padding(.vertical, AppSpacing.xxs) .padding(.vertical, AppSpacing.xxs)
.background(AppColors.primary.opacity(0.1)) .background(.blue.opacity(0.1))
.foregroundColor(AppColors.primary) .foregroundColor(.blue)
.cornerRadius(AppRadius.full) .cornerRadius(AppRadius.full)
} }
@@ -69,41 +69,41 @@ struct ContractorDetailView: View {
HStack(spacing: AppSpacing.xxs) { HStack(spacing: AppSpacing.xxs) {
ForEach(0..<5) { index in ForEach(0..<5) { index in
Image(systemName: index < Int(rating.doubleValue) ? "star.fill" : "star") Image(systemName: index < Int(rating.doubleValue) ? "star.fill" : "star")
.foregroundColor(AppColors.warning) .foregroundColor(.orange)
.font(.caption) .font(.caption)
} }
Text(String(format: "%.1f", rating.doubleValue)) Text(String(format: "%.1f", rating.doubleValue))
.font(.title3.weight(.semibold)) .font(.title3.weight(.semibold))
.foregroundColor(AppColors.textPrimary) .foregroundColor(Color(.label))
} }
if contractor.taskCount > 0 { if contractor.taskCount > 0 {
Text("\(contractor.taskCount) completed tasks") Text("\(contractor.taskCount) completed tasks")
.font(.callout) .font(.callout)
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
} }
} }
} }
.padding(AppSpacing.lg) .padding(AppSpacing.lg)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.background(AppColors.surface) .background(Color(.secondarySystemGroupedBackground))
.cornerRadius(AppRadius.lg) .cornerRadius(AppRadius.lg)
.shadow(color: AppShadow.sm.color, radius: AppShadow.sm.radius, x: AppShadow.sm.x, y: AppShadow.sm.y) .shadow(color: AppShadow.sm.color, radius: AppShadow.sm.radius, x: AppShadow.sm.x, y: AppShadow.sm.y)
// Contact Information // Contact Information
DetailSection(title: "Contact Information") { DetailSection(title: "Contact Information") {
DetailRow(icon: "phone", label: "Phone", value: contractor.phone, iconColor: AppColors.primary) DetailRow(icon: "phone", label: "Phone", value: contractor.phone, iconColor: .blue)
if let email = contractor.email { if let email = contractor.email {
DetailRow(icon: "envelope", label: "Email", value: email, iconColor: AppColors.accent) DetailRow(icon: "envelope", label: "Email", value: email, iconColor: .purple)
} }
if let secondaryPhone = contractor.secondaryPhone { if let secondaryPhone = contractor.secondaryPhone {
DetailRow(icon: "phone", label: "Secondary Phone", value: secondaryPhone, iconColor: AppColors.success) DetailRow(icon: "phone", label: "Secondary Phone", value: secondaryPhone, iconColor: .green)
} }
if let website = contractor.website { if let website = contractor.website {
DetailRow(icon: "globe", label: "Website", value: website, iconColor: AppColors.warning) DetailRow(icon: "globe", label: "Website", value: website, iconColor: .orange)
} }
} }
@@ -111,7 +111,7 @@ struct ContractorDetailView: View {
if contractor.licenseNumber != nil { if contractor.licenseNumber != nil {
DetailSection(title: "Business Details") { DetailSection(title: "Business Details") {
if let licenseNumber = contractor.licenseNumber { if let licenseNumber = contractor.licenseNumber {
DetailRow(icon: "doc.badge", label: "License Number", value: licenseNumber, iconColor: AppColors.primary) DetailRow(icon: "doc.badge", label: "License Number", value: licenseNumber, iconColor: .blue)
} }
} }
} }
@@ -130,7 +130,7 @@ struct ContractorDetailView: View {
icon: "mappin.circle", icon: "mappin.circle",
label: "Location", label: "Location",
value: addressComponents.joined(separator: "\n"), value: addressComponents.joined(separator: "\n"),
iconColor: AppColors.error iconColor: .red
) )
} }
} }
@@ -141,7 +141,8 @@ struct ContractorDetailView: View {
DetailSection(title: "Notes") { DetailSection(title: "Notes") {
Text(notes) Text(notes)
.font(.body) .font(.body)
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
.frame(maxWidth: .infinity, alignment: .leading)
.padding(AppSpacing.md) .padding(AppSpacing.md)
} }
} }
@@ -150,11 +151,11 @@ struct ContractorDetailView: View {
DetailSection(title: "Task History") { DetailSection(title: "Task History") {
HStack { HStack {
Image(systemName: "checkmark.circle") Image(systemName: "checkmark.circle")
.foregroundColor(AppColors.success) .foregroundColor(.green)
Spacer() Spacer()
Text("\(contractor.taskCount) completed tasks") Text("\(contractor.taskCount) completed tasks")
.font(.body) .font(.body)
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
} }
.padding(AppSpacing.md) .padding(AppSpacing.md)
} }
@@ -188,7 +189,7 @@ struct ContractorDetailView: View {
} }
} label: { } label: {
Image(systemName: "ellipsis.circle") Image(systemName: "ellipsis.circle")
.foregroundColor(AppColors.primary) .foregroundColor(.blue)
} }
} }
} }
@@ -236,13 +237,13 @@ struct DetailSection<Content: View>: View {
VStack(alignment: .leading, spacing: AppSpacing.sm) { VStack(alignment: .leading, spacing: AppSpacing.sm) {
Text(title) Text(title)
.font(.headline) .font(.headline)
.foregroundColor(AppColors.textPrimary) .foregroundColor(Color(.label))
.padding(.horizontal, AppSpacing.md) .padding(.horizontal, AppSpacing.md)
VStack(spacing: 0) { VStack(spacing: 0) {
content() content()
} }
.background(AppColors.surface) .background(Color(.secondarySystemGroupedBackground))
.cornerRadius(AppRadius.lg) .cornerRadius(AppRadius.lg)
.shadow(color: AppShadow.sm.color, radius: AppShadow.sm.radius, x: AppShadow.sm.x, y: AppShadow.sm.y) .shadow(color: AppShadow.sm.color, radius: AppShadow.sm.radius, x: AppShadow.sm.x, y: AppShadow.sm.y)
} }
@@ -254,7 +255,7 @@ struct DetailRow: View {
let icon: String let icon: String
let label: String let label: String
let value: String let value: String
var iconColor: Color = AppColors.textSecondary var iconColor: Color = Color(.secondaryLabel)
var body: some View { var body: some View {
HStack(alignment: .top, spacing: AppSpacing.sm) { HStack(alignment: .top, spacing: AppSpacing.sm) {
@@ -265,11 +266,11 @@ struct DetailRow: View {
VStack(alignment: .leading, spacing: AppSpacing.xxs) { VStack(alignment: .leading, spacing: AppSpacing.xxs) {
Text(label) Text(label)
.font(.caption.weight(.medium)) .font(.caption.weight(.medium))
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
Text(value) Text(value)
.font(.body) .font(.body)
.foregroundColor(AppColors.textPrimary) .foregroundColor(Color(.label))
} }
Spacer() Spacer()

View File

@@ -47,38 +47,235 @@ struct ContractorFormSheet: View {
var body: some View { var body: some View {
NavigationStack { NavigationStack {
ZStack { Form {
AppColors.background.ignoresSafeArea() // Basic Information
Section {
ScrollView { HStack {
VStack(spacing: AppSpacing.lg) { Image(systemName: "person")
basicInformationSection .foregroundColor(.blue)
contactInformationSection .frame(width: 24)
businessDetailsSection TextField("Name", text: $name)
addressSection .focused($focusedField, equals: .name)
notesSection }
favoriteToggle
errorMessage HStack {
Image(systemName: "building.2")
.foregroundColor(.purple)
.frame(width: 24)
TextField("Company", text: $company)
.focused($focusedField, equals: .company)
}
} header: {
Text("Basic Information")
}
// Contact Information
Section {
HStack {
Image(systemName: "phone.fill")
.foregroundColor(.green)
.frame(width: 24)
TextField("Phone", text: $phone)
.keyboardType(.phonePad)
.focused($focusedField, equals: .phone)
}
HStack {
Image(systemName: "envelope.fill")
.foregroundColor(.orange)
.frame(width: 24)
TextField("Email", text: $email)
.keyboardType(.emailAddress)
.textInputAutocapitalization(.never)
.autocorrectionDisabled()
.focused($focusedField, equals: .email)
}
HStack {
Image(systemName: "phone.badge.plus")
.foregroundColor(.green)
.frame(width: 24)
TextField("Secondary Phone", text: $secondaryPhone)
.keyboardType(.phonePad)
.focused($focusedField, equals: .secondaryPhone)
}
} header: {
Text("Contact Information")
} footer: {
Text("Required: Name and Phone")
.font(.caption)
}
// Business Details
Section {
Button(action: { showingSpecialtyPicker = true }) {
HStack {
Image(systemName: "wrench.and.screwdriver")
.foregroundColor(.blue)
.frame(width: 24)
Text(specialty.isEmpty ? "Specialty" : specialty)
.foregroundColor(specialty.isEmpty ? Color(.placeholderText) : Color(.label))
Spacer()
Image(systemName: "chevron.down")
.font(.caption)
.foregroundColor(Color(.tertiaryLabel))
}
}
HStack {
Image(systemName: "doc.badge")
.foregroundColor(.purple)
.frame(width: 24)
TextField("License Number", text: $licenseNumber)
.focused($focusedField, equals: .licenseNumber)
}
HStack {
Image(systemName: "globe")
.foregroundColor(.blue)
.frame(width: 24)
TextField("Website", text: $website)
.keyboardType(.URL)
.textInputAutocapitalization(.never)
.autocorrectionDisabled()
.focused($focusedField, equals: .website)
}
} header: {
Text("Business Details")
}
// Address
Section {
HStack {
Image(systemName: "location.fill")
.foregroundColor(.red)
.frame(width: 24)
TextField("Street Address", text: $address)
.focused($focusedField, equals: .address)
}
HStack {
Image(systemName: "building.2.crop.circle")
.foregroundColor(.blue)
.frame(width: 24)
TextField("City", text: $city)
.focused($focusedField, equals: .city)
}
HStack(spacing: AppSpacing.sm) {
HStack {
Image(systemName: "map")
.foregroundColor(.green)
.frame(width: 24)
TextField("State", text: $state)
.focused($focusedField, equals: .state)
}
Divider()
.frame(height: 24)
TextField("ZIP", text: $zipCode)
.keyboardType(.numberPad)
.focused($focusedField, equals: .zipCode)
.frame(maxWidth: 100)
}
} header: {
Text("Address")
}
// Notes
Section {
HStack(alignment: .top) {
Image(systemName: "note.text")
.foregroundColor(.orange)
.frame(width: 24)
.padding(.top, 8)
TextEditor(text: $notes)
.frame(height: 100)
.focused($focusedField, equals: .notes)
}
} header: {
Text("Notes")
} footer: {
Text("Private notes about this contractor")
.font(.caption)
}
// Favorite
Section {
Toggle(isOn: $isFavorite) {
Label("Mark as Favorite", systemImage: "star.fill")
.foregroundColor(isFavorite ? .orange : Color(.label))
}
.tint(.orange)
}
// Error Message
if let error = viewModel.errorMessage {
Section {
HStack {
Image(systemName: "exclamationmark.triangle.fill")
.foregroundColor(.red)
Text(error)
.font(.callout)
.foregroundColor(.red)
}
} }
.padding(AppSpacing.md)
} }
} }
.navigationTitle(contractor == nil ? "Add Contractor" : "Edit Contractor") .navigationTitle(contractor == nil ? "Add Contractor" : "Edit Contractor")
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
.toolbar { .toolbar {
ToolbarItem(placement: .navigationBarLeading) { ToolbarItem(placement: .cancellationAction) {
cancelButton Button("Cancel") {
dismiss()
}
} }
ToolbarItem(placement: .navigationBarTrailing) { ToolbarItem(placement: .confirmationAction) {
saveButton Button(action: saveContractor) {
if viewModel.isCreating || viewModel.isUpdating {
ProgressView()
} else {
Text(contractor == nil ? "Add" : "Save")
.bold()
}
}
.disabled(!canSave || viewModel.isCreating || viewModel.isUpdating)
} }
} }
.sheet(isPresented: $showingSpecialtyPicker) { .sheet(isPresented: $showingSpecialtyPicker) {
SpecialtyPickerView( NavigationStack {
selectedSpecialty: $specialty, List {
specialties: specialties ForEach(specialties, id: \.self) { spec in
) Button(action: {
specialty = spec
showingSpecialtyPicker = false
}) {
HStack {
Text(spec)
.foregroundColor(Color(.label))
Spacer()
if specialty == spec {
Image(systemName: "checkmark")
.foregroundColor(.blue)
}
}
}
}
}
.navigationTitle("Select Specialty")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .confirmationAction) {
Button("Done") {
showingSpecialtyPicker = false
}
}
}
}
.presentationDetents([.medium, .large])
} }
.onAppear { .onAppear {
loadContractorData() loadContractorData()
@@ -87,229 +284,6 @@ struct ContractorFormSheet: View {
} }
} }
// MARK: - Toolbar Items
private var cancelButton: some View {
Button("Cancel") {
dismiss()
}
.foregroundColor(AppColors.textSecondary)
}
private var saveButton: some View {
Button(action: saveContractor) {
if viewModel.isCreating || viewModel.isUpdating {
ProgressView()
} else {
Text(contractor == nil ? "Add" : "Save")
.foregroundColor(canSave ? AppColors.primary : AppColors.textTertiary)
}
}
.disabled(!canSave || viewModel.isCreating || viewModel.isUpdating)
}
// MARK: - Form Sections
private var basicInformationSection: some View {
VStack(spacing: AppSpacing.sm) {
SectionHeader(title: "Basic Information")
FormTextField(
title: "Name *",
text: $name,
icon: "person",
focused: $focusedField,
field: .name
)
FormTextField(
title: "Company",
text: $company,
icon: "building.2",
focused: $focusedField,
field: .company
)
}
}
private var contactInformationSection: some View {
VStack(spacing: AppSpacing.sm) {
SectionHeader(title: "Contact Information")
FormTextField(
title: "Phone *",
text: $phone,
icon: "phone",
keyboardType: .phonePad,
focused: $focusedField,
field: .phone
)
FormTextField(
title: "Email",
text: $email,
icon: "envelope",
keyboardType: .emailAddress,
focused: $focusedField,
field: .email
)
FormTextField(
title: "Secondary Phone",
text: $secondaryPhone,
icon: "phone",
keyboardType: .phonePad,
focused: $focusedField,
field: .secondaryPhone
)
}
}
private var businessDetailsSection: some View {
VStack(spacing: AppSpacing.sm) {
SectionHeader(title: "Business Details")
specialtyPickerButton
FormTextField(
title: "License Number",
text: $licenseNumber,
icon: "doc.badge",
focused: $focusedField,
field: .licenseNumber
)
FormTextField(
title: "Website",
text: $website,
icon: "globe",
keyboardType: .URL,
focused: $focusedField,
field: .website
)
}
}
private var specialtyPickerButton: some View {
Button(action: { showingSpecialtyPicker = true }) {
HStack {
Image(systemName: "wrench.and.screwdriver")
.foregroundColor(AppColors.textSecondary)
.frame(width: 20)
Text(specialty.isEmpty ? "Specialty" : specialty)
.foregroundColor(specialty.isEmpty ? AppColors.textTertiary : AppColors.textPrimary)
Spacer()
Image(systemName: "chevron.down")
.font(.caption)
.foregroundColor(AppColors.textTertiary)
}
.padding(AppSpacing.md)
.background(AppColors.surface)
.cornerRadius(AppRadius.md)
.overlay(
RoundedRectangle(cornerRadius: AppRadius.md)
.stroke(AppColors.border, lineWidth: 1)
)
}
}
private var addressSection: some View {
VStack(spacing: AppSpacing.sm) {
SectionHeader(title: "Address")
FormTextField(
title: "Street Address",
text: $address,
icon: "mappin",
focused: $focusedField,
field: .address
)
HStack(spacing: AppSpacing.sm) {
FormTextField(
title: "City",
text: $city,
focused: $focusedField,
field: .city
)
FormTextField(
title: "State",
text: $state,
focused: $focusedField,
field: .state
)
.frame(maxWidth: 100)
}
FormTextField(
title: "ZIP Code",
text: $zipCode,
keyboardType: .numberPad,
focused: $focusedField,
field: .zipCode
)
}
}
private var notesSection: some View {
VStack(alignment: .leading, spacing: AppSpacing.xxs) {
SectionHeader(title: "Notes")
HStack {
Image(systemName: "note.text")
.foregroundColor(AppColors.textSecondary)
.frame(width: 20)
Text("Private Notes")
.font(.footnote.weight(.medium))
.foregroundColor(AppColors.textSecondary)
}
TextEditor(text: $notes)
.frame(height: 100)
.padding(AppSpacing.sm)
.background(AppColors.surface)
.cornerRadius(AppRadius.md)
.overlay(
RoundedRectangle(cornerRadius: AppRadius.md)
.stroke(AppColors.border, lineWidth: 1)
)
.focused($focusedField, equals: .notes)
}
}
private var favoriteToggle: some View {
Toggle(isOn: $isFavorite) {
HStack {
Image(systemName: "star.fill")
.foregroundColor(isFavorite ? AppColors.warning : AppColors.textSecondary)
Text("Mark as Favorite")
.font(.body)
.foregroundColor(AppColors.textPrimary)
}
}
.padding(AppSpacing.md)
.background(AppColors.surface)
.cornerRadius(AppRadius.md)
}
@ViewBuilder
private var errorMessage: some View {
if let error = viewModel.errorMessage {
Text(error)
.font(.callout)
.foregroundColor(AppColors.error)
.padding(AppSpacing.sm)
.frame(maxWidth: .infinity)
.background(AppColors.error.opacity(0.1))
.cornerRadius(AppRadius.md)
}
}
// MARK: - Data Loading // MARK: - Data Loading
private func loadContractorData() { private func loadContractorData() {
@@ -399,98 +373,3 @@ struct ContractorFormSheet: View {
} }
} }
} }
// MARK: - Section Header
struct SectionHeader: View {
let title: String
var body: some View {
HStack {
Text(title)
.font(.headline)
.foregroundColor(AppColors.textPrimary)
Spacer()
}
}
}
// MARK: - Form Text Field
struct FormTextField: View {
let title: String
@Binding var text: String
var icon: String? = nil
var keyboardType: UIKeyboardType = .default
var focused: FocusState<ContractorFormField?>.Binding
var field: ContractorFormField
var body: some View {
VStack(alignment: .leading, spacing: AppSpacing.xxs) {
if let icon = icon {
HStack {
Image(systemName: icon)
.foregroundColor(AppColors.textSecondary)
.frame(width: 20)
Text(title)
.font(.footnote.weight(.medium))
.foregroundColor(AppColors.textSecondary)
}
} else {
Text(title)
.font(.footnote.weight(.medium))
.foregroundColor(AppColors.textSecondary)
}
TextField("", text: $text)
.keyboardType(keyboardType)
.autocapitalization(keyboardType == .emailAddress ? .none : .words)
.padding(AppSpacing.md)
.background(AppColors.surface)
.cornerRadius(AppRadius.md)
.overlay(
RoundedRectangle(cornerRadius: AppRadius.md)
.stroke(AppColors.border, lineWidth: 1)
)
.focused(focused, equals: field)
}
}
}
// MARK: - Specialty Picker
struct SpecialtyPickerView: View {
@Environment(\.dismiss) private var dismiss
@Binding var selectedSpecialty: String
let specialties: [String]
var body: some View {
NavigationView {
List {
ForEach(specialties, id: \.self) { specialty in
Button(action: {
selectedSpecialty = specialty
dismiss()
}) {
HStack {
Text(specialty)
.foregroundColor(AppColors.textPrimary)
Spacer()
if selectedSpecialty == specialty {
Image(systemName: "checkmark")
.foregroundColor(AppColors.primary)
}
}
}
}
}
.navigationTitle("Select Specialty")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Done") {
dismiss()
}
}
}
}
}
}

View File

@@ -26,7 +26,7 @@ struct ContractorsListView: View {
var body: some View { var body: some View {
ZStack { ZStack {
AppColors.background.ignoresSafeArea() Color(.systemGroupedBackground).ignoresSafeArea()
VStack(spacing: 0) { VStack(spacing: 0) {
// Search Bar // Search Bar
@@ -115,7 +115,7 @@ struct ContractorsListView: View {
loadContractors() loadContractors()
}) { }) {
Image(systemName: showFavoritesOnly ? "star.fill" : "star") Image(systemName: showFavoritesOnly ? "star.fill" : "star")
.foregroundColor(showFavoritesOnly ? AppColors.warning : AppColors.textSecondary) .foregroundColor(showFavoritesOnly ? .orange : Color(.secondaryLabel))
} }
// Specialty Filter // Specialty Filter
@@ -139,14 +139,14 @@ struct ContractorsListView: View {
} }
} label: { } label: {
Image(systemName: "line.3.horizontal.decrease.circle") Image(systemName: "line.3.horizontal.decrease.circle")
.foregroundColor(selectedSpecialty != nil ? AppColors.primary : AppColors.textSecondary) .foregroundColor(selectedSpecialty != nil ? .blue : Color(.secondaryLabel))
} }
// Add Button // Add Button
Button(action: { showingAddSheet = true }) { Button(action: { showingAddSheet = true }) {
Image(systemName: "plus.circle.fill") Image(systemName: "plus.circle.fill")
.font(.title2) .font(.title2)
.foregroundColor(AppColors.primary) .foregroundColor(.blue)
} }
} }
} }
@@ -201,7 +201,7 @@ struct SearchBar: View {
var body: some View { var body: some View {
HStack(spacing: AppSpacing.sm) { HStack(spacing: AppSpacing.sm) {
Image(systemName: "magnifyingglass") Image(systemName: "magnifyingglass")
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
TextField(placeholder, text: $text) TextField(placeholder, text: $text)
.font(.body) .font(.body)
@@ -209,12 +209,12 @@ struct SearchBar: View {
if !text.isEmpty { if !text.isEmpty {
Button(action: { text = "" }) { Button(action: { text = "" }) {
Image(systemName: "xmark.circle.fill") Image(systemName: "xmark.circle.fill")
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
} }
} }
} }
.padding(AppSpacing.sm) .padding(AppSpacing.sm)
.background(AppColors.surface) .background(Color(.secondarySystemGroupedBackground))
.cornerRadius(AppRadius.md) .cornerRadius(AppRadius.md)
.shadow(color: AppShadow.sm.color, radius: AppShadow.sm.radius, x: AppShadow.sm.x, y: AppShadow.sm.y) .shadow(color: AppShadow.sm.color, radius: AppShadow.sm.radius, x: AppShadow.sm.x, y: AppShadow.sm.y)
} }
@@ -242,8 +242,8 @@ struct FilterChip: View {
} }
.padding(.horizontal, AppSpacing.sm) .padding(.horizontal, AppSpacing.sm)
.padding(.vertical, AppSpacing.xxs) .padding(.vertical, AppSpacing.xxs)
.background(AppColors.primary.opacity(0.1)) .background(.blue.opacity(0.1))
.foregroundColor(AppColors.primary) .foregroundColor(.blue)
.cornerRadius(AppRadius.full) .cornerRadius(AppRadius.full)
} }
} }
@@ -256,16 +256,16 @@ struct EmptyContractorsView: View {
VStack(spacing: AppSpacing.md) { VStack(spacing: AppSpacing.md) {
Image(systemName: "person.badge.plus") Image(systemName: "person.badge.plus")
.font(.system(size: 64)) .font(.system(size: 64))
.foregroundColor(AppColors.textTertiary) .foregroundColor(Color(.tertiaryLabel))
Text(hasFilters ? "No contractors found" : "No contractors yet") Text(hasFilters ? "No contractors found" : "No contractors yet")
.font(.title3.weight(.semibold)) .font(.title3.weight(.semibold))
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
if !hasFilters { if !hasFilters {
Text("Add your first contractor to get started") Text("Add your first contractor to get started")
.font(.callout) .font(.callout)
.foregroundColor(AppColors.textTertiary) .foregroundColor(Color(.tertiaryLabel))
} }
} }
.padding(AppSpacing.xl) .padding(AppSpacing.xl)

View File

@@ -3,55 +3,6 @@ import SwiftUI
// MARK: - Design System // MARK: - Design System
// Modern, sleek design system for MyCrib with Light and Dark mode support // Modern, sleek design system for MyCrib with Light and Dark mode support
struct AppColors {
// Primary Colors - Modern blue gradient
static let primary = Color(hex: "2563EB") ?? .blue
static let primaryLight = Color(hex: "3B82F6") ?? .blue
static let primaryDark = Color(hex: "1E40AF") ?? .blue
// Accent Colors
static let accent = Color(hex: "8B5CF6") ?? .purple
static let accentLight = Color(hex: "A78BFA") ?? .purple
// Semantic Colors
static let success = Color(hex: "10B981") ?? .green
static let warning = Color(hex: "F59E0B") ?? .orange
static let error = Color(hex: "EF4444") ?? .red
static let info = Color(hex: "3B82F6") ?? .blue
// Adaptive Neutral Colors - Automatically adapt to light/dark mode
static let background = Color(uiColor: .systemGroupedBackground)
static let surface = Color(uiColor: .secondarySystemGroupedBackground)
static let surfaceSecondary = Color(uiColor: .tertiarySystemGroupedBackground)
static let textPrimary = Color(uiColor: .label)
static let textSecondary = Color(uiColor: .secondaryLabel)
static let textTertiary = Color(uiColor: .tertiaryLabel)
static let border = Color(uiColor: .separator)
static let borderLight = Color(uiColor: .opaqueSeparator)
// Task Status Colors
static let taskUpcoming = Color(hex: "3B82F6") ?? .blue
static let taskInProgress = Color(hex: "F59E0B") ?? .orange
static let taskCompleted = Color(hex: "10B981") ?? .green
static let taskCanceled = Color(hex: "6B7280") ?? .gray
static let taskArchived = Color(hex: "9CA3AF") ?? .gray
// Gradient
static let primaryGradient = LinearGradient(
colors: [primary, primaryLight],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
static let accentGradient = LinearGradient(
colors: [accent, accentLight],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
}
struct AppSpacing { struct AppSpacing {
static let xxs: CGFloat = 4 static let xxs: CGFloat = 4
static let xs: CGFloat = 8 static let xs: CGFloat = 8
@@ -102,7 +53,7 @@ struct CardStyle: ViewModifier {
func body(content: Content) -> some View { func body(content: Content) -> some View {
content content
.background(AppColors.surface) .background(Color(.secondarySystemGroupedBackground))
.cornerRadius(AppRadius.lg) .cornerRadius(AppRadius.lg)
.shadow(color: shadow.color, radius: shadow.radius, x: shadow.x, y: shadow.y) .shadow(color: shadow.color, radius: shadow.radius, x: shadow.x, y: shadow.y)
} }
@@ -118,7 +69,7 @@ struct PrimaryButtonStyle: ButtonStyle {
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.frame(height: 56) .frame(height: 56)
.background( .background(
configuration.isPressed ? AppColors.primaryDark : AppColors.primary configuration.isPressed ? .blue : .blue
) )
.cornerRadius(AppRadius.md) .cornerRadius(AppRadius.md)
.scaleEffect(configuration.isPressed ? 0.98 : 1.0) .scaleEffect(configuration.isPressed ? 0.98 : 1.0)
@@ -130,10 +81,10 @@ struct SecondaryButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View { func makeBody(configuration: Configuration) -> some View {
configuration.label configuration.label
.font(.headline) .font(.headline)
.foregroundColor(AppColors.primary) .foregroundColor(.blue)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.frame(height: 56) .frame(height: 56)
.background(AppColors.surfaceSecondary) .background(Color(.tertiarySystemGroupedBackground))
.cornerRadius(AppRadius.md) .cornerRadius(AppRadius.md)
.scaleEffect(configuration.isPressed ? 0.98 : 1.0) .scaleEffect(configuration.isPressed ? 0.98 : 1.0)
.animation(.easeInOut(duration: 0.1), value: configuration.isPressed) .animation(.easeInOut(duration: 0.1), value: configuration.isPressed)
@@ -144,11 +95,11 @@ struct TextFieldStyle: ViewModifier {
func body(content: Content) -> some View { func body(content: Content) -> some View {
content content
.padding(AppSpacing.md) .padding(AppSpacing.md)
.background(AppColors.surfaceSecondary) .background(Color(.tertiarySystemGroupedBackground))
.cornerRadius(AppRadius.md) .cornerRadius(AppRadius.md)
.overlay( .overlay(
RoundedRectangle(cornerRadius: AppRadius.md) RoundedRectangle(cornerRadius: AppRadius.md)
.stroke(AppColors.borderLight, lineWidth: 1) .stroke(Color(.opaqueSeparator), lineWidth: 1)
) )
} }
} }

View File

@@ -8,8 +8,8 @@ struct DocumentCard: View {
switch document.documentType { switch document.documentType {
case "warranty": return .blue case "warranty": return .blue
case "manual": return .purple case "manual": return .purple
case "receipt": return AppColors.success case "receipt": return .green
case "inspection": return AppColors.warning case "inspection": return .orange
default: return .gray default: return .gray
} }
} }
@@ -41,13 +41,13 @@ struct DocumentCard: View {
Text(document.title) Text(document.title)
.font(.title3.weight(.semibold)) .font(.title3.weight(.semibold))
.fontWeight(.bold) .fontWeight(.bold)
.foregroundColor(AppColors.textPrimary) .foregroundColor(Color(.label))
.lineLimit(1) .lineLimit(1)
if let description = document.description_, !description.isEmpty { if let description = document.description_, !description.isEmpty {
Text(description) Text(description)
.font(.callout) .font(.callout)
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
.lineLimit(2) .lineLimit(2)
} }
@@ -63,7 +63,7 @@ struct DocumentCard: View {
if let fileSize = document.fileSize { if let fileSize = document.fileSize {
Text(formatFileSize(Int(fileSize))) Text(formatFileSize(Int(fileSize)))
.font(.caption.weight(.medium)) .font(.caption.weight(.medium))
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
} }
} }
} }
@@ -71,11 +71,11 @@ struct DocumentCard: View {
Spacer() Spacer()
Image(systemName: "chevron.right") Image(systemName: "chevron.right")
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
.font(.system(size: 14)) .font(.system(size: 14))
} }
.padding(AppSpacing.md) .padding(AppSpacing.md)
.background(AppColors.surface) .background(Color(.secondarySystemGroupedBackground))
.cornerRadius(AppRadius.md) .cornerRadius(AppRadius.md)
.shadow(color: Color.black.opacity(0.05), radius: 2, x: 0, y: 1) .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) { VStack(spacing: AppSpacing.md) {
Image(systemName: icon) Image(systemName: icon)
.font(.system(size: 64)) .font(.system(size: 64))
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
Text(title) Text(title)
.font(.title3.weight(.semibold)) .font(.title3.weight(.semibold))
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
Text(message) Text(message)
.font(.body) .font(.body)
.foregroundColor(AppColors.textTertiary) .foregroundColor(Color(.tertiaryLabel))
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
} }
.padding(AppSpacing.lg) .padding(AppSpacing.lg)

View File

@@ -10,10 +10,10 @@ struct WarrantyCard: View {
var statusColor: Color { var statusColor: Color {
if !document.isActive { return .gray } if !document.isActive { return .gray }
if daysUntilExpiration < 0 { return AppColors.error } if daysUntilExpiration < 0 { return .red }
if daysUntilExpiration < 30 { return AppColors.warning } if daysUntilExpiration < 30 { return .orange }
if daysUntilExpiration < 90 { return .yellow } if daysUntilExpiration < 90 { return .yellow }
return AppColors.success return .green
} }
var statusText: String { var statusText: String {
@@ -31,11 +31,11 @@ struct WarrantyCard: View {
Text(document.title) Text(document.title)
.font(.title3.weight(.semibold)) .font(.title3.weight(.semibold))
.fontWeight(.bold) .fontWeight(.bold)
.foregroundColor(AppColors.textPrimary) .foregroundColor(Color(.label))
Text(document.itemName ?? "") Text(document.itemName ?? "")
.font(.body) .font(.body)
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
} }
Spacer() Spacer()
@@ -58,11 +58,11 @@ struct WarrantyCard: View {
VStack(alignment: .leading, spacing: 2) { VStack(alignment: .leading, spacing: 2) {
Text("Provider") Text("Provider")
.font(.caption.weight(.medium)) .font(.caption.weight(.medium))
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
Text(document.provider ?? "N/A") Text(document.provider ?? "N/A")
.font(.body) .font(.body)
.fontWeight(.medium) .fontWeight(.medium)
.foregroundColor(AppColors.textPrimary) .foregroundColor(Color(.label))
} }
Spacer() Spacer()
@@ -70,11 +70,11 @@ struct WarrantyCard: View {
VStack(alignment: .trailing, spacing: 2) { VStack(alignment: .trailing, spacing: 2) {
Text("Expires") Text("Expires")
.font(.caption.weight(.medium)) .font(.caption.weight(.medium))
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
Text(document.endDate ?? "N/A") Text(document.endDate ?? "N/A")
.font(.body) .font(.body)
.fontWeight(.medium) .fontWeight(.medium)
.foregroundColor(AppColors.textPrimary) .foregroundColor(Color(.label))
} }
} }
@@ -96,7 +96,7 @@ struct WarrantyCard: View {
} }
} }
.padding(AppSpacing.md) .padding(AppSpacing.md)
.background(AppColors.surface) .background(Color(.secondarySystemGroupedBackground))
.cornerRadius(AppRadius.md) .cornerRadius(AppRadius.md)
.shadow(color: Color.black.opacity(0.05), radius: 2, x: 0, y: 1) .shadow(color: Color.black.opacity(0.05), radius: 2, x: 0, y: 1)
} }

View File

@@ -400,6 +400,9 @@ struct DocumentDetailView: View {
.font(.body) .font(.body)
} }
.frame(maxWidth: .infinity, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading)
.padding(12)
.background(Color(.secondarySystemGroupedBackground))
.cornerRadius(8)
} }
private func getStatusColor(isActive: Bool, daysUntilExpiration: Int32) -> Color { private func getStatusColor(isActive: Bool, daysUntilExpiration: Int32) -> Color {

View File

@@ -28,7 +28,7 @@ struct DocumentsWarrantiesView: View {
var body: some View { var body: some View {
ZStack { ZStack {
AppColors.background.ignoresSafeArea() Color(.systemGroupedBackground).ignoresSafeArea()
VStack(spacing: 0) { VStack(spacing: 0) {
// Segmented Control for Tabs // Segmented Control for Tabs
@@ -104,7 +104,7 @@ struct DocumentsWarrantiesView: View {
loadWarranties() loadWarranties()
}) { }) {
Image(systemName: showActiveOnly ? "checkmark.circle.fill" : "checkmark.circle") Image(systemName: showActiveOnly ? "checkmark.circle.fill" : "checkmark.circle")
.foregroundColor(showActiveOnly ? AppColors.success : AppColors.textSecondary) .foregroundColor(showActiveOnly ? .green : Color(.secondaryLabel))
} }
} }
@@ -149,7 +149,7 @@ struct DocumentsWarrantiesView: View {
} }
} label: { } label: {
Image(systemName: "line.3.horizontal.decrease.circle") Image(systemName: "line.3.horizontal.decrease.circle")
.foregroundColor((selectedCategory != nil || selectedDocType != nil) ? AppColors.primary : AppColors.textSecondary) .foregroundColor((selectedCategory != nil || selectedDocType != nil) ? .blue : Color(.secondaryLabel))
} }
// Add Button // Add Button
@@ -158,7 +158,7 @@ struct DocumentsWarrantiesView: View {
}) { }) {
Image(systemName: "plus.circle.fill") Image(systemName: "plus.circle.fill")
.font(.title2) .font(.title2)
.foregroundColor(AppColors.primary) .foregroundColor(.blue)
} }
} }
} }

View File

@@ -8,7 +8,7 @@ struct HomeScreenView: View {
var body: some View { var body: some View {
NavigationView { NavigationView {
ZStack { ZStack {
AppColors.background Color(.systemGroupedBackground)
.ignoresSafeArea() .ignoresSafeArea()
if viewModel.isLoading { if viewModel.isLoading {
@@ -17,7 +17,7 @@ struct HomeScreenView: View {
.scaleEffect(1.2) .scaleEffect(1.2)
Text("Loading...") Text("Loading...")
.font(.body) .font(.body)
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
} }
} else { } else {
ScrollView(showsIndicators: false) { ScrollView(showsIndicators: false) {
@@ -27,11 +27,11 @@ struct HomeScreenView: View {
Text("Hello!") Text("Hello!")
.font(.title.weight(.bold)) .font(.title.weight(.bold))
.fontWeight(.bold) .fontWeight(.bold)
.foregroundColor(AppColors.textPrimary) .foregroundColor(Color(.label))
Text("Welcome to MyCrib") Text("Welcome to MyCrib")
.font(.body) .font(.body)
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
} }
.frame(maxWidth: .infinity, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, AppSpacing.md) .padding(.horizontal, AppSpacing.md)
@@ -80,7 +80,7 @@ struct HomeScreenView: View {
Image(systemName: "rectangle.portrait.and.arrow.right") Image(systemName: "rectangle.portrait.and.arrow.right")
.font(.system(size: 18, weight: .semibold)) .font(.system(size: 18, weight: .semibold))
} }
.foregroundColor(AppColors.error) .foregroundColor(.red)
} }
} }
} }

View File

@@ -15,6 +15,8 @@
</array> </array>
</dict> </dict>
</array> </array>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>UIBackgroundModes</key> <key>UIBackgroundModes</key>
<array> <array>
<string>remote-notification</string> <string>remote-notification</string>

View File

@@ -25,7 +25,7 @@ struct LoginView: View {
private var buttonBackgroundColor: Color { private var buttonBackgroundColor: Color {
if viewModel.isLoading || !isFormValid { if viewModel.isLoading || !isFormValid {
return AppColors.textTertiary return Color(.tertiaryLabel)
} }
return .clear return .clear
} }
@@ -38,7 +38,7 @@ struct LoginView: View {
NavigationView { NavigationView {
ZStack { ZStack {
// Background gradient // Background gradient
AppColors.background Color(.systemGroupedBackground)
.ignoresSafeArea() .ignoresSafeArea()
ScrollView { ScrollView {
@@ -51,9 +51,9 @@ struct LoginView: View {
// App Icon with gradient // App Icon with gradient
ZStack { ZStack {
Circle() Circle()
.fill(AppColors.primaryGradient) .fill(LinearGradient(colors: [.blue, .blue.opacity(0.8)], startPoint: .topLeading, endPoint: .bottomTrailing))
.frame(width: 100, height: 100) .frame(width: 100, height: 100)
.shadow(color: AppColors.primary.opacity(0.3), radius: 20, y: 10) .shadow(color: .blue.opacity(0.3), radius: 20, y: 10)
Image(systemName: "house.fill") Image(systemName: "house.fill")
.font(.system(size: 50, weight: .semibold)) .font(.system(size: 50, weight: .semibold))
@@ -63,11 +63,11 @@ struct LoginView: View {
VStack(spacing: AppSpacing.xs) { VStack(spacing: AppSpacing.xs) {
Text("Welcome Back") Text("Welcome Back")
.font(.title2.weight(.bold)) .font(.title2.weight(.bold))
.foregroundColor(AppColors.textPrimary) .foregroundColor(Color(.label))
Text("Sign in to manage your properties") Text("Sign in to manage your properties")
.font(.body) .font(.body)
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
} }
} }
@@ -77,11 +77,11 @@ struct LoginView: View {
VStack(alignment: .leading, spacing: AppSpacing.xs) { VStack(alignment: .leading, spacing: AppSpacing.xs) {
Text("Email or Username") Text("Email or Username")
.font(.subheadline.weight(.medium)) .font(.subheadline.weight(.medium))
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
HStack(spacing: AppSpacing.sm) { HStack(spacing: AppSpacing.sm) {
Image(systemName: "envelope.fill") Image(systemName: "envelope.fill")
.foregroundColor(AppColors.textTertiary) .foregroundColor(Color(.tertiaryLabel))
.frame(width: 20) .frame(width: 20)
TextField("Enter your email", text: $viewModel.username) TextField("Enter your email", text: $viewModel.username)
@@ -98,13 +98,13 @@ struct LoginView: View {
} }
} }
.padding(AppSpacing.md) .padding(AppSpacing.md)
.background(AppColors.surface) .background(Color(.secondarySystemGroupedBackground))
.cornerRadius(AppRadius.md) .cornerRadius(AppRadius.md)
.overlay( .overlay(
RoundedRectangle(cornerRadius: AppRadius.md) RoundedRectangle(cornerRadius: AppRadius.md)
.stroke(focusedField == .username ? AppColors.primary : AppColors.border, lineWidth: 1.5) .stroke(focusedField == .username ? .blue : Color(.separator), lineWidth: 1.5)
) )
.shadow(color: focusedField == .username ? AppColors.primary.opacity(0.1) : .clear, radius: 8) .shadow(color: focusedField == .username ? .blue.opacity(0.1) : .clear, radius: 8)
.animation(.easeInOut(duration: 0.2), value: focusedField) .animation(.easeInOut(duration: 0.2), value: focusedField)
} }
@@ -112,11 +112,11 @@ struct LoginView: View {
VStack(alignment: .leading, spacing: AppSpacing.xs) { VStack(alignment: .leading, spacing: AppSpacing.xs) {
Text("Password") Text("Password")
.font(.subheadline.weight(.medium)) .font(.subheadline.weight(.medium))
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
HStack(spacing: AppSpacing.sm) { HStack(spacing: AppSpacing.sm) {
Image(systemName: "lock.fill") Image(systemName: "lock.fill")
.foregroundColor(AppColors.textTertiary) .foregroundColor(Color(.tertiaryLabel))
.frame(width: 20) .frame(width: 20)
Group { Group {
@@ -143,18 +143,18 @@ struct LoginView: View {
isPasswordVisible.toggle() isPasswordVisible.toggle()
}) { }) {
Image(systemName: isPasswordVisible ? "eye.slash.fill" : "eye.fill") Image(systemName: isPasswordVisible ? "eye.slash.fill" : "eye.fill")
.foregroundColor(AppColors.textTertiary) .foregroundColor(Color(.tertiaryLabel))
.frame(width: 20) .frame(width: 20)
} }
} }
.padding(AppSpacing.md) .padding(AppSpacing.md)
.background(AppColors.surface) .background(Color(.secondarySystemGroupedBackground))
.cornerRadius(AppRadius.md) .cornerRadius(AppRadius.md)
.overlay( .overlay(
RoundedRectangle(cornerRadius: AppRadius.md) RoundedRectangle(cornerRadius: AppRadius.md)
.stroke(focusedField == .password ? AppColors.primary : AppColors.border, lineWidth: 1.5) .stroke(focusedField == .password ? .blue : Color(.separator), lineWidth: 1.5)
) )
.shadow(color: focusedField == .password ? AppColors.primary.opacity(0.1) : .clear, radius: 8) .shadow(color: focusedField == .password ? .blue.opacity(0.1) : .clear, radius: 8)
.animation(.easeInOut(duration: 0.2), value: focusedField) .animation(.easeInOut(duration: 0.2), value: focusedField)
.onChange(of: viewModel.password) { _, _ in .onChange(of: viewModel.password) { _, _ in
viewModel.clearError() viewModel.clearError()
@@ -168,21 +168,21 @@ struct LoginView: View {
showPasswordReset = true showPasswordReset = true
} }
.font(.subheadline.weight(.medium)) .font(.subheadline.weight(.medium))
.foregroundColor(AppColors.primary) .foregroundColor(.blue)
} }
// Error Message // Error Message
if let errorMessage = viewModel.errorMessage { if let errorMessage = viewModel.errorMessage {
HStack(spacing: AppSpacing.sm) { HStack(spacing: AppSpacing.sm) {
Image(systemName: "exclamationmark.circle.fill") Image(systemName: "exclamationmark.circle.fill")
.foregroundColor(AppColors.error) .foregroundColor(.red)
Text(errorMessage) Text(errorMessage)
.font(.callout) .font(.callout)
.foregroundColor(AppColors.error) .foregroundColor(.red)
Spacer() Spacer()
} }
.padding(AppSpacing.md) .padding(AppSpacing.md)
.background(AppColors.error.opacity(0.1)) .background(.red.opacity(0.1))
.cornerRadius(AppRadius.md) .cornerRadius(AppRadius.md)
} }
@@ -196,18 +196,18 @@ struct LoginView: View {
HStack(spacing: AppSpacing.xs) { HStack(spacing: AppSpacing.xs) {
Text("Don't have an account?") Text("Don't have an account?")
.font(.body) .font(.body)
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
Button("Sign Up") { Button("Sign Up") {
showingRegister = true showingRegister = true
} }
.font(.body) .font(.body)
.fontWeight(.semibold) .fontWeight(.semibold)
.foregroundColor(AppColors.primary) .foregroundColor(.blue)
} }
} }
.padding(AppSpacing.xl) .padding(AppSpacing.xl)
.background(AppColors.surface) .background(Color(.secondarySystemGroupedBackground))
.cornerRadius(AppRadius.xxl) .cornerRadius(AppRadius.xxl)
.shadow(color: .black.opacity(0.08), radius: 20, y: 10) .shadow(color: .black.opacity(0.08), radius: 20, y: 10)
.padding(.horizontal, AppSpacing.lg) .padding(.horizontal, AppSpacing.lg)
@@ -285,18 +285,17 @@ struct LoginView: View {
.background(loginButtonBackground) .background(loginButtonBackground)
.cornerRadius(AppRadius.md) .cornerRadius(AppRadius.md)
.shadow( .shadow(
color: shouldShowShadow ? AppColors.primary.opacity(0.3) : .clear, color: shouldShowShadow ? .blue.opacity(0.3) : .clear,
radius: 10, radius: 10,
y: 5 y: 5
) )
} }
@ViewBuilder private var loginButtonBackground: AnyShapeStyle {
private var loginButtonBackground: some View {
if viewModel.isLoading || !isFormValid { if viewModel.isLoading || !isFormValid {
AppColors.textTertiary AnyShapeStyle(Color(.tertiaryLabel))
} else { } else {
AppColors.primaryGradient AnyShapeStyle(LinearGradient(colors: [.blue, .blue.opacity(0.8)], startPoint: .topLeading, endPoint: .bottomTrailing))
} }
} }
} }

View File

@@ -16,9 +16,23 @@ class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDele
await PushNotificationManager.shared.requestNotificationPermission() await PushNotificationManager.shared.requestNotificationPermission()
} }
// Clear badge when app launches
Task { @MainActor in
PushNotificationManager.shared.clearBadge()
}
return true return true
} }
// MARK: - App Lifecycle
func applicationDidBecomeActive(_ application: UIApplication) {
// Clear badge when app becomes active
Task { @MainActor in
PushNotificationManager.shared.clearBadge()
}
}
// MARK: - Remote Notifications // MARK: - Remote Notifications
func application( func application(

View File

@@ -8,7 +8,7 @@ struct ResidencesListView: View {
var body: some View { var body: some View {
ZStack { ZStack {
AppColors.background Color(.systemGroupedBackground)
.ignoresSafeArea() .ignoresSafeArea()
if viewModel.myResidences == nil && viewModel.isLoading { if viewModel.myResidences == nil && viewModel.isLoading {
@@ -17,7 +17,7 @@ struct ResidencesListView: View {
.scaleEffect(1.2) .scaleEffect(1.2)
Text("Loading properties...") Text("Loading properties...")
.font(.body) .font(.body)
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
} }
} else if let error = viewModel.errorMessage { } else if let error = viewModel.errorMessage {
ErrorView(message: error) { ErrorView(message: error) {
@@ -39,10 +39,10 @@ struct ResidencesListView: View {
VStack(alignment: .leading, spacing: AppSpacing.xxs) { VStack(alignment: .leading, spacing: AppSpacing.xxs) {
Text("Your Properties") Text("Your Properties")
.font(.title3.weight(.semibold)) .font(.title3.weight(.semibold))
.foregroundColor(AppColors.textPrimary) .foregroundColor(Color(.label))
Text("\(response.residences.count) \(response.residences.count == 1 ? "property" : "properties")") Text("\(response.residences.count) \(response.residences.count == 1 ? "property" : "properties")")
.font(.callout) .font(.callout)
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
} }
Spacer() Spacer()
} }
@@ -77,7 +77,7 @@ struct ResidencesListView: View {
}) { }) {
Image(systemName: "person.badge.plus") Image(systemName: "person.badge.plus")
.font(.system(size: 18, weight: .semibold)) .font(.system(size: 18, weight: .semibold))
.foregroundColor(AppColors.primary) .foregroundColor(.blue)
} }
Button(action: { Button(action: {
@@ -85,7 +85,7 @@ struct ResidencesListView: View {
}) { }) {
Image(systemName: "plus.circle.fill") Image(systemName: "plus.circle.fill")
.font(.system(size: 22, weight: .semibold)) .font(.system(size: 22, weight: .semibold))
.foregroundColor(AppColors.primary) .foregroundColor(.blue)
} }
} }
} }

View File

@@ -10,9 +10,9 @@ struct HomeNavigationCard: View {
// Icon with gradient background // Icon with gradient background
ZStack { ZStack {
RoundedRectangle(cornerRadius: AppRadius.md) RoundedRectangle(cornerRadius: AppRadius.md)
.fill(AppColors.primaryGradient) .fill(LinearGradient(colors: [.blue, .blue.opacity(0.8)], startPoint: .topLeading, endPoint: .bottomTrailing))
.frame(width: 60, height: 60) .frame(width: 60, height: 60)
.shadow(color: AppColors.primary.opacity(0.3), radius: 8, y: 4) .shadow(color: .blue.opacity(0.3), radius: 8, y: 4)
Image(systemName: icon) Image(systemName: icon)
.font(.system(size: 28, weight: .semibold)) .font(.system(size: 28, weight: .semibold))
@@ -24,11 +24,11 @@ struct HomeNavigationCard: View {
Text(title) Text(title)
.font(.title3.weight(.semibold)) .font(.title3.weight(.semibold))
.fontWeight(.semibold) .fontWeight(.semibold)
.foregroundColor(AppColors.textPrimary) .foregroundColor(Color(.label))
Text(subtitle) Text(subtitle)
.font(.callout) .font(.callout)
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
} }
Spacer() Spacer()
@@ -36,10 +36,10 @@ struct HomeNavigationCard: View {
// Chevron // Chevron
Image(systemName: "chevron.right") Image(systemName: "chevron.right")
.font(.system(size: 16, weight: .semibold)) .font(.system(size: 16, weight: .semibold))
.foregroundColor(AppColors.textTertiary) .foregroundColor(Color(.tertiaryLabel))
} }
.padding(AppSpacing.lg) .padding(AppSpacing.lg)
.background(AppColors.surface) .background(Color(.secondarySystemGroupedBackground))
.cornerRadius(AppRadius.lg) .cornerRadius(AppRadius.lg)
.shadow(color: AppShadow.md.color, radius: AppShadow.md.radius, x: AppShadow.md.x, y: AppShadow.md.y) .shadow(color: AppShadow.md.color, radius: AppShadow.md.radius, x: AppShadow.md.x, y: AppShadow.md.y)
} }

View File

@@ -11,7 +11,7 @@ struct OverviewCard: View {
HStack(spacing: AppSpacing.sm) { HStack(spacing: AppSpacing.sm) {
ZStack { ZStack {
Circle() Circle()
.fill(AppColors.primaryGradient) .fill(LinearGradient(colors: [.blue, .blue.opacity(0.8)], startPoint: .topLeading, endPoint: .bottomTrailing))
.frame(width: 44, height: 44) .frame(width: 44, height: 44)
Image(systemName: "chart.bar.fill") Image(systemName: "chart.bar.fill")
@@ -21,7 +21,7 @@ struct OverviewCard: View {
Text("Overview") Text("Overview")
.font(.title3.weight(.semibold)) .font(.title3.weight(.semibold))
.foregroundColor(AppColors.textPrimary) .foregroundColor(Color(.label))
} }
Spacer() Spacer()
} }
@@ -32,7 +32,7 @@ struct OverviewCard: View {
icon: "house.fill", icon: "house.fill",
value: "\(summary.totalResidences)", value: "\(summary.totalResidences)",
label: "Properties", label: "Properties",
color: AppColors.primary color: .blue
) )
Divider() Divider()
@@ -42,7 +42,7 @@ struct OverviewCard: View {
icon: "list.bullet", icon: "list.bullet",
value: "\(summary.totalTasks)", value: "\(summary.totalTasks)",
label: "Total Tasks", label: "Total Tasks",
color: AppColors.info color: .blue
) )
Divider() Divider()
@@ -52,12 +52,12 @@ struct OverviewCard: View {
icon: "clock.fill", icon: "clock.fill",
value: "\(summary.totalPending)", value: "\(summary.totalPending)",
label: "Pending", label: "Pending",
color: AppColors.warning color: .orange
) )
} }
} }
.padding(AppSpacing.xl) .padding(AppSpacing.xl)
.background(AppColors.surface) .background(Color(.secondarySystemGroupedBackground))
.cornerRadius(AppRadius.xl) .cornerRadius(AppRadius.xl)
.shadow(color: AppShadow.lg.color, radius: AppShadow.lg.radius, x: AppShadow.lg.x, y: AppShadow.lg.y) .shadow(color: AppShadow.lg.color, radius: AppShadow.lg.radius, x: AppShadow.lg.x, y: AppShadow.lg.y)
.padding(.horizontal, AppSpacing.md) .padding(.horizontal, AppSpacing.md)

View File

@@ -4,7 +4,7 @@ struct StatView: View {
let icon: String let icon: String
let value: String let value: String
let label: String let label: String
var color: Color = AppColors.primary var color: Color = .blue
var body: some View { var body: some View {
VStack(spacing: AppSpacing.sm) { VStack(spacing: AppSpacing.sm) {
@@ -21,11 +21,11 @@ struct StatView: View {
Text(value) Text(value)
.font(.title2.weight(.semibold)) .font(.title2.weight(.semibold))
.fontWeight(.bold) .fontWeight(.bold)
.foregroundColor(AppColors.textPrimary) .foregroundColor(Color(.label))
Text(label) Text(label)
.font(.footnote.weight(.medium)) .font(.footnote.weight(.medium))
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
} }
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)

View File

@@ -10,9 +10,9 @@ struct ResidenceCard: View {
HStack(spacing: AppSpacing.sm) { HStack(spacing: AppSpacing.sm) {
ZStack { ZStack {
RoundedRectangle(cornerRadius: AppRadius.sm) RoundedRectangle(cornerRadius: AppRadius.sm)
.fill(AppColors.primaryGradient) .fill(LinearGradient(colors: [.blue, .blue.opacity(0.8)], startPoint: .topLeading, endPoint: .bottomTrailing))
.frame(width: 44, height: 44) .frame(width: 44, height: 44)
.shadow(color: AppColors.primary.opacity(0.3), radius: 6, y: 3) .shadow(color: .blue.opacity(0.3), radius: 6, y: 3)
Image(systemName: "house.fill") Image(systemName: "house.fill")
.font(.system(size: 20, weight: .semibold)) .font(.system(size: 20, weight: .semibold))
@@ -23,12 +23,12 @@ struct ResidenceCard: View {
Text(residence.name) Text(residence.name)
.font(.title3.weight(.semibold)) .font(.title3.weight(.semibold))
.fontWeight(.bold) .fontWeight(.bold)
.foregroundColor(AppColors.textPrimary) .foregroundColor(Color(.label))
.lineLimit(1) .lineLimit(1)
Text(residence.propertyType) Text(residence.propertyType)
.font(.caption.weight(.medium)) .font(.caption.weight(.medium))
.foregroundColor(AppColors.textTertiary) .foregroundColor(Color(.tertiaryLabel))
.textCase(.uppercase) .textCase(.uppercase)
.tracking(0.5) .tracking(0.5)
} }
@@ -38,11 +38,11 @@ struct ResidenceCard: View {
if residence.isPrimary { if residence.isPrimary {
ZStack { ZStack {
Circle() Circle()
.fill(AppColors.accent.opacity(0.1)) .fill(.purple.opacity(0.1))
.frame(width: 32, height: 32) .frame(width: 32, height: 32)
Image(systemName: "star.fill") Image(systemName: "star.fill")
.font(.system(size: 14, weight: .bold)) .font(.system(size: 14, weight: .bold))
.foregroundColor(AppColors.accent) .foregroundColor(.purple)
} }
} }
} }
@@ -52,19 +52,19 @@ struct ResidenceCard: View {
HStack(spacing: AppSpacing.xxs) { HStack(spacing: AppSpacing.xxs) {
Image(systemName: "mappin.circle.fill") Image(systemName: "mappin.circle.fill")
.font(.system(size: 12, weight: .medium)) .font(.system(size: 12, weight: .medium))
.foregroundColor(AppColors.textTertiary) .foregroundColor(Color(.tertiaryLabel))
Text(residence.streetAddress) Text(residence.streetAddress)
.font(.callout) .font(.callout)
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
} }
HStack(spacing: AppSpacing.xxs) { HStack(spacing: AppSpacing.xxs) {
Image(systemName: "location.fill") Image(systemName: "location.fill")
.font(.system(size: 12, weight: .medium)) .font(.system(size: 12, weight: .medium))
.foregroundColor(AppColors.textTertiary) .foregroundColor(Color(.tertiaryLabel))
Text("\(residence.city), \(residence.stateProvince)") Text("\(residence.city), \(residence.stateProvince)")
.font(.callout) .font(.callout)
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
} }
} }
.padding(.vertical, AppSpacing.xs) .padding(.vertical, AppSpacing.xs)
@@ -77,26 +77,26 @@ struct ResidenceCard: View {
icon: "list.bullet", icon: "list.bullet",
value: "\(residence.taskSummary.total)", value: "\(residence.taskSummary.total)",
label: "Tasks", label: "Tasks",
color: AppColors.info color: .blue
) )
TaskStatChip( TaskStatChip(
icon: "checkmark.circle.fill", icon: "checkmark.circle.fill",
value: "\(residence.taskSummary.completed)", value: "\(residence.taskSummary.completed)",
label: "Done", label: "Done",
color: AppColors.success color: .green
) )
TaskStatChip( TaskStatChip(
icon: "clock.fill", icon: "clock.fill",
value: "\(residence.taskSummary.pending)", value: "\(residence.taskSummary.pending)",
label: "Pending", label: "Pending",
color: AppColors.warning color: .orange
) )
} }
} }
.padding(AppSpacing.md) .padding(AppSpacing.md)
.background(AppColors.surface) .background(Color(.secondarySystemGroupedBackground))
.cornerRadius(AppRadius.lg) .cornerRadius(AppRadius.lg)
.shadow(color: AppShadow.md.color, radius: AppShadow.md.radius, x: AppShadow.md.x, y: AppShadow.md.y) .shadow(color: AppShadow.md.color, radius: AppShadow.md.radius, x: AppShadow.md.x, y: AppShadow.md.y)
} }
@@ -138,5 +138,5 @@ struct ResidenceCard: View {
updatedAt: "2024-01-01T00:00:00Z" updatedAt: "2024-01-01T00:00:00Z"
)) ))
.padding() .padding()
.background(AppColors.background) .background(Color(.systemGroupedBackground))
} }

View File

@@ -36,7 +36,7 @@ struct CompletionCardView: View {
HStack(alignment: .top, spacing: 6) { HStack(alignment: .top, spacing: 6) {
Image(systemName: "wrench.and.screwdriver") Image(systemName: "wrench.and.screwdriver")
.font(.caption2) .font(.caption2)
.foregroundColor(AppColors.primary) .foregroundColor(.blue)
VStack(alignment: .leading, spacing: 2) { VStack(alignment: .leading, spacing: 2) {
Text("By: \(contractorDetails.name)") Text("By: \(contractorDetails.name)")

View File

@@ -13,6 +13,8 @@ struct DynamicTaskCard: View {
let onArchive: () -> Void let onArchive: () -> Void
let onUnarchive: () -> Void let onUnarchive: () -> Void
@State private var isCompletionsExpanded = false
var body: some View { var body: some View {
let _ = print("📋 DynamicTaskCard - Task: \(task.title), ButtonTypes: \(buttonTypes)") let _ = print("📋 DynamicTaskCard - Task: \(task.title), ButtonTypes: \(buttonTypes)")
@@ -20,7 +22,7 @@ struct DynamicTaskCard: View {
HStack { HStack {
VStack(alignment: .leading, spacing: 4) { VStack(alignment: .leading, spacing: 4) {
Text(task.title) Text(task.title)
.font(.headline) .font(.title3)
.foregroundColor(.primary) .foregroundColor(.primary)
if let status = task.status { if let status = task.status {
@@ -64,10 +66,23 @@ struct DynamicTaskCard: View {
Text("Completions (\(task.completions.count))") Text("Completions (\(task.completions.count))")
.font(.caption) .font(.caption)
.fontWeight(.semibold) .fontWeight(.semibold)
.foregroundColor(.primary)
Spacer()
Image(systemName: isCompletionsExpanded ? "chevron.up" : "chevron.down")
.font(.caption)
.foregroundColor(.secondary)
}
.contentShape(Rectangle())
.onTapGesture {
withAnimation(.spring(response: 0.3, dampingFraction: 0.7)) {
isCompletionsExpanded.toggle()
}
} }
ForEach(task.completions, id: \.id) { completion in if isCompletionsExpanded {
CompletionCardView(completion: completion) ForEach(task.completions, id: \.id) { completion in
CompletionCardView(completion: completion)
}
} }
} }
} }
@@ -87,18 +102,22 @@ struct DynamicTaskCard: View {
} }
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.padding(.vertical, 12) .padding(.vertical, 12)
.background(Color.blue) .background(Color.blue.opacity(0.1))
.foregroundColor(.white) .foregroundColor(.blue)
.cornerRadius(8) .cornerRadius(8)
.overlay(
RoundedRectangle(cornerRadius: 8)
.stroke(Color.blue, lineWidth: 2)
)
} }
.zIndex(10) .zIndex(10)
.menuOrder(.fixed) .menuOrder(.fixed)
} }
} }
.padding(16) .padding(16)
.background(Color(.systemBackground)) .background(Color(.secondarySystemGroupedBackground))
.cornerRadius(12) .cornerRadius(12)
.shadow(color: Color.black.opacity(0.05), radius: 3, x: 0, y: 2) .shadow(color: Color.black.opacity(0.1), radius: 5, x: 0, y: 2)
.simultaneousGesture(TapGesture(), including: .subviews) .simultaneousGesture(TapGesture(), including: .subviews)
} }

View File

@@ -21,10 +21,10 @@ struct PriorityBadge: View {
private var priorityColor: Color { private var priorityColor: Color {
switch priority.lowercased() { switch priority.lowercased() {
case "high": return AppColors.error case "high": return .red
case "medium": return AppColors.warning case "medium": return .orange
case "low": return AppColors.success case "low": return .green
default: return AppColors.textTertiary default: return Color(.tertiaryLabel)
} }
} }
} }
@@ -36,5 +36,5 @@ struct PriorityBadge: View {
PriorityBadge(priority: "low") PriorityBadge(priority: "low")
} }
.padding() .padding()
.background(AppColors.background) .background(Color(.systemGroupedBackground))
} }

View File

@@ -24,11 +24,11 @@ struct StatusBadge: View {
private var statusColor: Color { private var statusColor: Color {
switch status { switch status {
case "completed": return AppColors.success case "completed": return .green
case "in_progress": return AppColors.taskInProgress case "in_progress": return .orange
case "pending": return AppColors.warning case "pending": return .orange
case "cancelled": return AppColors.error case "cancelled": return .red
default: return AppColors.textTertiary default: return Color(.tertiaryLabel)
} }
} }
} }
@@ -41,5 +41,5 @@ struct StatusBadge: View {
StatusBadge(status: "cancelled") StatusBadge(status: "cancelled")
} }
.padding() .padding()
.background(AppColors.background) .background(Color(.systemGroupedBackground))
} }

View File

@@ -11,6 +11,8 @@ struct TaskCard: View {
let onArchive: (() -> Void)? let onArchive: (() -> Void)?
let onUnarchive: (() -> Void)? let onUnarchive: (() -> Void)?
@State private var isCompletionsExpanded = false
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: AppSpacing.md) { VStack(alignment: .leading, spacing: AppSpacing.md) {
// Header // Header
@@ -18,7 +20,7 @@ struct TaskCard: View {
VStack(alignment: .leading, spacing: AppSpacing.xs) { VStack(alignment: .leading, spacing: AppSpacing.xs) {
Text(task.title) Text(task.title)
.font(.title3.weight(.semibold)) .font(.title3.weight(.semibold))
.foregroundColor(AppColors.textPrimary) .foregroundColor(Color(.label))
.lineLimit(2) .lineLimit(2)
if let status = task.status { if let status = task.status {
@@ -35,7 +37,7 @@ struct TaskCard: View {
if let description = task.description_, !description.isEmpty { if let description = task.description_, !description.isEmpty {
Text(description) Text(description)
.font(.callout) .font(.callout)
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
.lineLimit(3) .lineLimit(3)
} }
@@ -44,14 +46,14 @@ struct TaskCard: View {
HStack(spacing: AppSpacing.xxs) { HStack(spacing: AppSpacing.xxs) {
Image(systemName: "repeat") Image(systemName: "repeat")
.font(.system(size: 12, weight: .medium)) .font(.system(size: 12, weight: .medium))
.foregroundColor(AppColors.textTertiary) .foregroundColor(Color(.tertiaryLabel))
Text(task.frequency.displayName) Text(task.frequency.displayName)
.font(.caption.weight(.medium)) .font(.caption.weight(.medium))
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
} }
.padding(.horizontal, AppSpacing.sm) .padding(.horizontal, AppSpacing.sm)
.padding(.vertical, AppSpacing.xxs) .padding(.vertical, AppSpacing.xxs)
.background(AppColors.surfaceSecondary) .background(Color(.tertiarySystemGroupedBackground))
.cornerRadius(AppRadius.xs) .cornerRadius(AppRadius.xs)
Spacer() Spacer()
@@ -60,14 +62,14 @@ struct TaskCard: View {
HStack(spacing: AppSpacing.xxs) { HStack(spacing: AppSpacing.xxs) {
Image(systemName: "calendar") Image(systemName: "calendar")
.font(.system(size: 12, weight: .medium)) .font(.system(size: 12, weight: .medium))
.foregroundColor(AppColors.textTertiary) .foregroundColor(Color(.tertiaryLabel))
Text(formatDate(dueDate)) Text(formatDate(dueDate))
.font(.caption.weight(.medium)) .font(.caption.weight(.medium))
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
} }
.padding(.horizontal, AppSpacing.sm) .padding(.horizontal, AppSpacing.sm)
.padding(.vertical, AppSpacing.xxs) .padding(.vertical, AppSpacing.xxs)
.background(AppColors.surfaceSecondary) .background(Color(.tertiarySystemGroupedBackground))
.cornerRadius(AppRadius.xs) .cornerRadius(AppRadius.xs)
} }
} }
@@ -81,20 +83,32 @@ struct TaskCard: View {
HStack(spacing: AppSpacing.xs) { HStack(spacing: AppSpacing.xs) {
ZStack { ZStack {
Circle() Circle()
.fill(AppColors.success.opacity(0.1)) .fill(Color.green.opacity(0.1))
.frame(width: 24, height: 24) .frame(width: 24, height: 24)
Image(systemName: "checkmark.circle.fill") Image(systemName: "checkmark.circle.fill")
.font(.system(size: 14, weight: .semibold)) .font(.system(size: 14, weight: .semibold))
.foregroundColor(AppColors.success) .foregroundColor(.green)
} }
Text("Completions (\(task.completions.count))") Text("Completions (\(task.completions.count))")
.font(.footnote.weight(.medium)) .font(.footnote.weight(.medium))
.fontWeight(.semibold) .fontWeight(.semibold)
.foregroundColor(AppColors.textPrimary) .foregroundColor(Color(.label))
Spacer()
Image(systemName: isCompletionsExpanded ? "chevron.up" : "chevron.down")
.font(.caption)
.foregroundColor(Color(.secondaryLabel))
}
.contentShape(Rectangle())
.onTapGesture {
withAnimation(.spring(response: 0.3, dampingFraction: 0.7)) {
isCompletionsExpanded.toggle()
}
} }
ForEach(task.completions, id: \.id) { completion in if isCompletionsExpanded {
CompletionCardView(completion: completion) ForEach(task.completions, id: \.id) { completion in
CompletionCardView(completion: completion)
}
} }
} }
} }
@@ -113,8 +127,8 @@ struct TaskCard: View {
} }
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.frame(height: 44) .frame(height: 44)
.foregroundColor(AppColors.warning) .foregroundColor(.orange)
.background(AppColors.warning.opacity(0.1)) .background(Color.orange.opacity(0.1))
.cornerRadius(AppRadius.md) .cornerRadius(AppRadius.md)
} }
} }
@@ -131,7 +145,7 @@ struct TaskCard: View {
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.frame(height: 44) .frame(height: 44)
.foregroundColor(.white) .foregroundColor(.white)
.background(AppColors.success) .background(.green)
.cornerRadius(AppRadius.md) .cornerRadius(AppRadius.md)
} }
} }
@@ -150,8 +164,8 @@ struct TaskCard: View {
} }
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.frame(height: 36) .frame(height: 36)
.foregroundColor(AppColors.primary) .foregroundColor(.blue)
.background(AppColors.surfaceSecondary) .background(Color(.tertiarySystemGroupedBackground))
.cornerRadius(AppRadius.sm) .cornerRadius(AppRadius.sm)
} }
@@ -165,8 +179,8 @@ struct TaskCard: View {
} }
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.frame(height: 36) .frame(height: 36)
.foregroundColor(AppColors.error) .foregroundColor(.red)
.background(AppColors.error.opacity(0.1)) .background(Color.red.opacity(0.1))
.cornerRadius(AppRadius.sm) .cornerRadius(AppRadius.sm)
} }
} else if let onUncancel = onUncancel { } else if let onUncancel = onUncancel {
@@ -180,7 +194,7 @@ struct TaskCard: View {
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.frame(height: 36) .frame(height: 36)
.foregroundColor(.white) .foregroundColor(.white)
.background(AppColors.primary) .background(.blue)
.cornerRadius(AppRadius.sm) .cornerRadius(AppRadius.sm)
} }
} }
@@ -197,8 +211,8 @@ struct TaskCard: View {
} }
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.frame(height: 36) .frame(height: 36)
.foregroundColor(AppColors.primary) .foregroundColor(.blue)
.background(AppColors.surfaceSecondary) .background(Color(.tertiarySystemGroupedBackground))
.cornerRadius(AppRadius.sm) .cornerRadius(AppRadius.sm)
} }
} }
@@ -213,8 +227,8 @@ struct TaskCard: View {
} }
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.frame(height: 36) .frame(height: 36)
.foregroundColor(AppColors.textSecondary) .foregroundColor(Color(.secondaryLabel))
.background(AppColors.surfaceSecondary) .background(Color(.tertiarySystemGroupedBackground))
.cornerRadius(AppRadius.sm) .cornerRadius(AppRadius.sm)
} }
} }
@@ -222,7 +236,7 @@ struct TaskCard: View {
} }
} }
.padding(AppSpacing.md) .padding(AppSpacing.md)
.background(AppColors.surface) .background(Color(.secondarySystemGroupedBackground))
.cornerRadius(AppRadius.lg) .cornerRadius(AppRadius.lg)
.shadow(color: AppShadow.md.color, radius: AppShadow.md.radius, x: AppShadow.md.x, y: AppShadow.md.y) .shadow(color: AppShadow.md.color, radius: AppShadow.md.radius, x: AppShadow.md.x, y: AppShadow.md.y)
} }
@@ -271,5 +285,5 @@ struct TaskCard: View {
) )
} }
.padding() .padding()
.background(AppColors.background) .background(Color(.systemGroupedBackground))
} }

View File

@@ -393,7 +393,7 @@ struct ContractorPickerView: View {
Spacer() Spacer()
if selectedContractor == nil { if selectedContractor == nil {
Image(systemName: "checkmark") Image(systemName: "checkmark")
.foregroundStyle(AppColors.primary) .foregroundStyle(.blue)
} }
} }
} }
@@ -441,7 +441,7 @@ struct ContractorPickerView: View {
if selectedContractor?.id == contractor.id { if selectedContractor?.id == contractor.id {
Image(systemName: "checkmark") Image(systemName: "checkmark")
.foregroundStyle(AppColors.primary) .foregroundStyle(.blue)
} }
} }
} }

View File

@@ -6,7 +6,7 @@
<string>development</string> <string>development</string>
<key>com.apple.security.application-groups</key> <key>com.apple.security.application-groups</key>
<array> <array>
<string>group.com.tt.mycrib.MyCrib</string> <string>group.com.tt.mycrib.MyCribDev</string>
</array> </array>
</dict> </dict>
</plist> </plist>