import SwiftUI import ComposeApp enum DocumentWarrantyTab { case warranties case documents } struct DocumentsWarrantiesView: View { @StateObject private var documentViewModel = DocumentViewModel() @StateObject private var subscriptionCache = SubscriptionCacheWrapper.shared @State private var selectedTab: DocumentWarrantyTab = .warranties @State private var searchText = "" @State private var selectedCategory: String? = nil @State private var selectedDocType: String? = nil @State private var showActiveOnly = true @State private var showFilterMenu = false @State private var showAddSheet = false @State private var showingUpgradePrompt = false let residenceId: Int32? var warranties: [Document] { documentViewModel.documents.filter { doc in guard doc.documentType == "warranty" else { return false } if showActiveOnly && doc.isActive != true { return false } if let category = selectedCategory, doc.category != category { return false } return true } } var documents: [Document] { documentViewModel.documents.filter { doc in guard doc.documentType != "warranty" else { return false } if let docType = selectedDocType, doc.documentType != docType { return false } return true } } private var shouldShowUpgrade: Bool { subscriptionCache.shouldShowUpgradePrompt(currentCount: 0, limitKey: "documents") } var body: some View { ZStack { WarmGradientBackground() VStack(spacing: 0) { // Segmented Control OrganicSegmentedControl(selection: $selectedTab) .padding(.horizontal, 16) .padding(.top, 8) // Search Bar OrganicDocSearchBar(text: $searchText, placeholder: L10n.Documents.searchPlaceholder) .padding(.horizontal, 16) .padding(.top, 8) // Active Filters if selectedCategory != nil || selectedDocType != nil || (selectedTab == .warranties && showActiveOnly) { ScrollView(.horizontal, showsIndicators: false) { HStack(spacing: 8) { if selectedTab == .warranties && showActiveOnly { OrganicDocFilterChip( title: L10n.Documents.activeOnly, icon: "checkmark.circle.fill", onRemove: { showActiveOnly = false } ) } if let category = selectedCategory, selectedTab == .warranties { OrganicDocFilterChip( title: category, onRemove: { selectedCategory = nil } ) } if let docType = selectedDocType, selectedTab == .documents { OrganicDocFilterChip( title: docType, onRemove: { selectedDocType = nil } ) } } .padding(.horizontal, 16) } .padding(.vertical, 8) } // Content if selectedTab == .warranties { WarrantiesTabContent( viewModel: documentViewModel, searchText: searchText ) } else { DocumentsTabContent( viewModel: documentViewModel, searchText: searchText ) } } } .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarTrailing) { HStack(spacing: 12) { // Active Filter (for warranties) if selectedTab == .warranties { Button(action: { showActiveOnly.toggle() }) { Image(systemName: showActiveOnly ? "checkmark.circle.fill" : "checkmark.circle") .font(.system(size: 16, weight: .medium)) .foregroundColor(showActiveOnly ? Color.appPrimary : Color.appTextSecondary) } } // Filter Menu Menu { if selectedTab == .warranties { Button(action: { selectedCategory = nil }) { Label(L10n.Documents.allCategories, systemImage: selectedCategory == nil ? "checkmark" : "") } Divider() ForEach(DocumentCategory.allCases, id: \.self) { category in Button(action: { selectedCategory = category.displayName }) { Label(category.displayName, systemImage: selectedCategory == category.displayName ? "checkmark" : "") } } } else { Button(action: { selectedDocType = nil }) { Label(L10n.Documents.allTypes, systemImage: selectedDocType == nil ? "checkmark" : "") } Divider() ForEach(DocumentType.allCases, id: \.self) { type in Button(action: { selectedDocType = type.displayName }) { Label(type.displayName, systemImage: selectedDocType == type.displayName ? "checkmark" : "") } } } } label: { Image(systemName: "line.3.horizontal.decrease.circle") .font(.system(size: 16, weight: .medium)) .foregroundColor((selectedCategory != nil || selectedDocType != nil) ? Color.appPrimary : Color.appTextSecondary) } // Add Button Button(action: { let currentCount = documentViewModel.documents.count if subscriptionCache.shouldShowUpgradePrompt(currentCount: currentCount, limitKey: "documents") { PostHogAnalytics.shared.capture(AnalyticsEvents.documentsPaywallShown, properties: ["current_count": currentCount]) showingUpgradePrompt = true } else { showAddSheet = true } }) { OrganicDocToolbarButton() } } } } .onAppear { PostHogAnalytics.shared.screen(AnalyticsEvents.documentsScreenShown) loadAllDocuments() } .sheet(isPresented: $showAddSheet) { AddDocumentView( residenceId: residenceId, initialDocumentType: selectedTab == .warranties ? "warranty" : "other", isPresented: $showAddSheet, documentViewModel: documentViewModel ) } .sheet(isPresented: $showingUpgradePrompt) { UpgradePromptView(triggerKey: "view_documents", isPresented: $showingUpgradePrompt) } } private func loadAllDocuments(forceRefresh: Bool = false) { documentViewModel.loadDocuments(forceRefresh: forceRefresh) } private func loadWarranties() { loadAllDocuments() } private func loadDocuments() { loadAllDocuments() } } // MARK: - Organic Segmented Control private struct OrganicSegmentedControl: View { @Binding var selection: DocumentWarrantyTab var body: some View { HStack(spacing: 0) { OrganicSegmentButton( title: L10n.Documents.warranties, icon: "checkmark.shield", isSelected: selection == .warranties, action: { selection = .warranties } ) OrganicSegmentButton( title: L10n.Documents.documents, icon: "doc.text", isSelected: selection == .documents, action: { selection = .documents } ) } .padding(4) .background(Color.appBackgroundSecondary) .clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous)) .naturalShadow(.subtle) } } private struct OrganicSegmentButton: View { let title: String let icon: String let isSelected: Bool let action: () -> Void var body: some View { Button(action: action) { HStack(spacing: 6) { Image(systemName: icon) .font(.system(size: 13, weight: .semibold)) Text(title) .font(.system(size: 14, weight: .semibold)) } .foregroundColor(isSelected ? Color.appTextOnPrimary : Color.appTextSecondary) .frame(maxWidth: .infinity) .padding(.vertical, 10) .background(isSelected ? Color.appPrimary : Color.clear) .clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous)) } } } // MARK: - Organic Doc Search Bar private struct OrganicDocSearchBar: View { @Binding var text: String var placeholder: String var body: some View { HStack(spacing: 12) { ZStack { Circle() .fill(Color.appPrimary.opacity(0.1)) .frame(width: 32, height: 32) Image(systemName: "magnifyingglass") .font(.system(size: 14, weight: .medium)) .foregroundColor(Color.appPrimary) } TextField(placeholder, text: $text) .font(.system(size: 16, weight: .medium)) if !text.isEmpty { Button(action: { text = "" }) { Image(systemName: "xmark.circle.fill") .font(.system(size: 18)) .foregroundColor(Color.appTextSecondary) } } } .padding(14) .background(Color.appBackgroundSecondary) .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) .naturalShadow(.subtle) } } // MARK: - Organic Doc Filter Chip private struct OrganicDocFilterChip: View { let title: String var icon: String? = nil let onRemove: () -> Void var body: some View { HStack(spacing: 6) { if let icon = icon { Image(systemName: icon) .font(.system(size: 12, weight: .semibold)) } Text(title) .font(.system(size: 13, weight: .semibold)) Button(action: onRemove) { Image(systemName: "xmark") .font(.system(size: 10, weight: .bold)) } } .padding(.horizontal, 12) .padding(.vertical, 8) .background(Color.appPrimary.opacity(0.15)) .foregroundColor(Color.appPrimary) .clipShape(Capsule()) } } // MARK: - Organic Doc Toolbar Button private struct OrganicDocToolbarButton: View { var body: some View { ZStack { Circle() .fill(Color.appPrimary) .frame(width: 32, height: 32) Image(systemName: "plus") .font(.system(size: 14, weight: .bold)) .foregroundColor(Color.appTextOnPrimary) } } } // MARK: - Supporting Types extension DocumentCategory: CaseIterable { public static var allCases: [DocumentCategory] { return [.appliance, .hvac, .plumbing, .electrical, .roofing, .structural, .other] } var displayName: String { switch self { case .appliance: return "Appliance" case .hvac: return "HVAC" case .plumbing: return "Plumbing" case .electrical: return "Electrical" case .roofing: return "Roofing" case .structural: return "Structural" case .other: return "Other" default: return "Unknown" } } } extension DocumentType: CaseIterable { public static var allCases: [DocumentType] { return [.warranty, .manual, .receipt, .inspection, .permit, .deed, .insurance, .contract, .photo, .other] } var displayName: String { switch self { case .warranty: return "Warranty" case .manual: return "Manual" case .receipt: return "Receipt" case .inspection: return "Inspection" case .permit: return "Permit" case .deed: return "Deed" case .insurance: return "Insurance" case .contract: return "Contract" case .photo: return "Photo" case .other: return "Other" default: return "Unknown" } } }