Add Warm Organic design system to iOS app

- Add OrganicDesign.swift with reusable components:
  - WarmGradientBackground, OrganicBlobShape, GrainTexture
  - OrganicDivider, OrganicCardBackground, NaturalShadow modifier
  - OrganicSpacing constants (cozy, comfortable, spacious, airy)

- Update high-priority screens with organic styling:
  - LoginView: hero glow, organic card background, rounded fonts
  - ResidenceDetailView, ResidencesListView: warm backgrounds
  - ResidenceCard, SummaryCard, PropertyHeaderCard: organic cards
  - TaskCard: metadata pills, secondary buttons, card background
  - TaskFormView: organic loading overlay, templates button
  - CompletionHistorySheet: organic loading/error/empty states
  - ProfileView, NotificationPreferencesView, ThemeSelectionView

- Update task badges with icons and capsule styling:
  - PriorityBadge: priority-specific icons
  - StatusBadge: status-specific icons

- Fix TaskCard isOverdue error using DateUtils.isOverdue()

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-12-16 20:15:32 -06:00
parent 67f8dcc80f
commit 3598a8d57f
15 changed files with 2318 additions and 703 deletions

View File

@@ -98,6 +98,9 @@ struct TaskFormView: View {
var body: some View {
NavigationStack {
ZStack {
WarmGradientBackground()
.ignoresSafeArea()
Form {
// Residence Picker (only if needed)
if needsResidenceSelection, let residences = residences {
@@ -130,31 +133,40 @@ struct TaskFormView: View {
Button {
showingTemplatesBrowser = true
} label: {
HStack {
Image(systemName: "list.bullet.rectangle")
.font(.system(size: 18))
.foregroundColor(Color.appPrimary)
.frame(width: 28)
HStack(spacing: 14) {
ZStack {
Circle()
.fill(Color.appPrimary.opacity(0.12))
.frame(width: 40, height: 40)
Image(systemName: "list.bullet.rectangle")
.font(.system(size: 16, weight: .semibold))
.foregroundColor(Color.appPrimary)
}
Text("Browse Task Templates")
.foregroundColor(Color.appTextPrimary)
VStack(alignment: .leading, spacing: 2) {
Text("Browse Task Templates")
.font(.system(size: 16, weight: .semibold, design: .rounded))
.foregroundColor(Color.appTextPrimary)
Text("\(dataManager.taskTemplateCount) common tasks")
.font(.system(size: 13, weight: .medium))
.foregroundColor(Color.appTextSecondary)
}
Spacer()
Text("\(dataManager.taskTemplateCount) tasks")
.font(.caption)
.foregroundColor(Color.appTextSecondary)
Image(systemName: "chevron.right")
.font(.caption)
.font(.system(size: 14, weight: .medium))
.foregroundColor(Color.appTextSecondary)
}
.padding(.vertical, 4)
}
} header: {
Text("Quick Start")
.font(.system(size: 13, weight: .semibold, design: .rounded))
} footer: {
Text("Choose from common home maintenance tasks or create your own below")
.font(.caption)
.font(.system(size: 12, weight: .medium))
.foregroundColor(Color.appTextSecondary)
}
.listRowBackground(Color.appBackgroundSecondary)
@@ -291,19 +303,36 @@ struct TaskFormView: View {
.blur(radius: isLoadingLookups ? 3 : 0)
if isLoadingLookups {
VStack(spacing: 16) {
ProgressView()
.scaleEffect(1.5)
VStack(spacing: OrganicSpacing.comfortable) {
ZStack {
Circle()
.fill(Color.appPrimary.opacity(0.1))
.frame(width: 64, height: 64)
ProgressView()
.scaleEffect(1.2)
.tint(Color.appPrimary)
}
Text(L10n.Tasks.loading)
.font(.system(size: 15, weight: .medium, design: .rounded))
.foregroundColor(Color.appTextSecondary)
}
.padding(OrganicSpacing.spacious)
.background(
RoundedRectangle(cornerRadius: 24, style: .continuous)
.fill(Color.appBackgroundSecondary)
.overlay(
GrainTexture(opacity: 0.015)
.clipShape(RoundedRectangle(cornerRadius: 24, style: .continuous))
)
)
.naturalShadow(.medium)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.appBackgroundPrimary.opacity(0.8))
.background(Color.appBackgroundPrimary.opacity(0.9))
}
}
.listStyle(.plain)
.scrollContentBackground(.hidden)
.background(Color.appBackgroundPrimary)
.background(Color.clear)
.navigationTitle(isEditMode ? L10n.Tasks.editTitle : L10n.Tasks.addTitle)
.navigationBarTitleDisplayMode(.inline)
.toolbar {