09120e9d9d
Android UI Tests / ui-tests (push) Has been cancelled
All screen-level empty states now use a single OrganicEmptyScreen that fills the screen and centers its icon/title/subtitle/action in the dead middle (both axes), with the three animated FloatingLeaf footer on every empty screen. - Add canonical OrganicEmptyScreen (Shared/Components/SharedEmptyStateView) - Fix ListAsyncContentView: empty/error content used minHeight 60% of the screen (placeholder sat in the top portion) → use full height so it centers dead-center regardless of headers - Hide Contractors' filter bar when the list is empty so the placeholder stays screen-centered - Route Properties / Tasks / Contractors / Documents / Warranties empties through OrganicEmptyScreen; preserve the Tasks empty's branching (no-residences vs add-task vs upgrade-prompt) - Remove the duplicate/dead empty components Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
77 lines
2.5 KiB
Swift
77 lines
2.5 KiB
Swift
import SwiftUI
|
|
import ComposeApp
|
|
|
|
struct DocumentsTabContent: View {
|
|
@ObservedObject var viewModel: DocumentViewModel
|
|
@StateObject private var subscriptionCache = SubscriptionCacheWrapper.shared
|
|
let searchText: String
|
|
|
|
var filteredDocuments: [Document] {
|
|
if searchText.isEmpty {
|
|
return viewModel.documents
|
|
}
|
|
return viewModel.documents.filter {
|
|
$0.title.localizedCaseInsensitiveContains(searchText) ||
|
|
($0.description_ ?? "").localizedCaseInsensitiveContains(searchText)
|
|
}
|
|
}
|
|
|
|
var body: some View {
|
|
ListAsyncContentView(
|
|
items: filteredDocuments,
|
|
isLoading: viewModel.isLoading,
|
|
errorMessage: viewModel.errorMessage,
|
|
content: { documents in
|
|
DocumentsListContent(documents: documents)
|
|
},
|
|
emptyContent: {
|
|
if !subscriptionCache.shouldShowUpgradePrompt(currentCount: filteredDocuments.count, limitKey: "documents") {
|
|
OrganicEmptyScreen(
|
|
icon: "doc",
|
|
title: L10n.Documents.noDocumentsFound,
|
|
subtitle: L10n.Documents.noDocumentsMessage
|
|
)
|
|
} else {
|
|
UpgradeFeatureView(
|
|
triggerKey: "view_documents",
|
|
icon: "doc.text.fill"
|
|
)
|
|
}
|
|
},
|
|
onRefresh: {
|
|
viewModel.loadDocuments(forceRefresh: true)
|
|
for await loading in viewModel.$isLoading.values {
|
|
if !loading { break }
|
|
}
|
|
},
|
|
onRetry: {
|
|
viewModel.loadDocuments()
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
// MARK: - Documents List Content
|
|
|
|
private struct DocumentsListContent: View {
|
|
let documents: [Document]
|
|
|
|
var body: some View {
|
|
ScrollView {
|
|
LazyVStack(spacing: AppSpacing.sm) {
|
|
ForEach(documents, id: \.id) { document in
|
|
NavigationLink(destination: DocumentDetailView(documentId: document.id?.int32Value ?? 0)) {
|
|
DocumentCard(document: document)
|
|
}
|
|
.buttonStyle(PlainButtonStyle())
|
|
}
|
|
}
|
|
.padding(AppSpacing.md)
|
|
.padding(.bottom, AppSpacing.xxxl)
|
|
}
|
|
.safeAreaInset(edge: .bottom) {
|
|
Color.clear.frame(height: 0)
|
|
}
|
|
}
|
|
}
|