107 lines
3.9 KiB
Swift
107 lines
3.9 KiB
Swift
import Foundation
|
|
import Observation
|
|
|
|
@MainActor
|
|
@Observable
|
|
final class LeadDetailViewModel {
|
|
private(set) var detail: LeadDetail?
|
|
private(set) var error: LeadError?
|
|
var expandedOffers: Set<String> = []
|
|
var expandedGroups: Set<String> = []
|
|
var selectedDocument: LeadDocument?
|
|
var signingRequest: LeadSigningRequest?
|
|
private(set) var signedDocumentURLs: [String: URL] = [:]
|
|
private let leadID: String
|
|
private let salesCode: String
|
|
private let getDetail: GetLeadDetailUseCase
|
|
private var generation = 0
|
|
|
|
init(leadID: String, salesCode: String, repository: any LeadDetailRepository) {
|
|
self.leadID = leadID
|
|
self.salesCode = salesCode
|
|
self.getDetail = GetLeadDetailUseCase(repository: repository)
|
|
}
|
|
|
|
var lead: Lead? { detail?.lead }
|
|
var isAllExpanded: Bool {
|
|
guard let detail, !detail.offers.isEmpty else { return false }
|
|
return Set(detail.offers.map(\.id)).isSubset(of: expandedOffers)
|
|
&& Set(detail.offers.flatMap(\.groups).map(\.id)).isSubset(of: expandedGroups)
|
|
}
|
|
|
|
func load() async {
|
|
generation += 1
|
|
let request = generation
|
|
detail = nil
|
|
error = nil
|
|
do {
|
|
let result = try await getDetail.execute(id: leadID, salesCode: salesCode)
|
|
try Task.checkCancellation()
|
|
guard request == generation else { return }
|
|
detail = result
|
|
let documents = result.offers.flatMap(\.documents)
|
|
signedDocumentURLs = LeadSignedDocumentStore().savedURLs(
|
|
documents: documents, leadID: leadID, salesCode: salesCode
|
|
)
|
|
for document in documents {
|
|
if let url = signedDocumentURLs[document.id] { didSign(document, url: url) }
|
|
}
|
|
if let first = result.offers.first {
|
|
expandedOffers = [first.id]
|
|
expandedGroups = Set(first.groups.filter { !$0.isComplete }.map(\.id))
|
|
} else {
|
|
expandedOffers = []
|
|
expandedGroups = []
|
|
}
|
|
} catch is CancellationError {
|
|
} catch {
|
|
guard request == generation, !Task.isCancelled else { return }
|
|
self.error = error as? LeadError ?? .unexpected
|
|
}
|
|
}
|
|
|
|
func startSigning(offer: LeadOffer, group: LeadDocumentGroup? = nil) {
|
|
let documents = group?.documents ?? offer.documents
|
|
guard documents.contains(where: { $0.status == .pending }) else { return }
|
|
signingRequest = LeadSigningRequest(
|
|
offerID: offer.id,
|
|
signerName: detail?.contactName ?? detail?.lead.displayName ?? "Kunde",
|
|
documents: documents
|
|
)
|
|
}
|
|
|
|
func didSign(_ document: LeadDocument, url: URL) {
|
|
signedDocumentURLs[document.id] = url
|
|
guard var detail else { return }
|
|
for offerIndex in detail.offers.indices {
|
|
for groupIndex in detail.offers[offerIndex].groups.indices {
|
|
for documentIndex in detail.offers[offerIndex].groups[groupIndex].documents.indices {
|
|
if detail.offers[offerIndex].groups[groupIndex].documents[documentIndex].id == document.id {
|
|
detail.offers[offerIndex].groups[groupIndex].documents[documentIndex].status = .signed
|
|
}
|
|
}
|
|
}
|
|
}
|
|
self.detail = detail
|
|
}
|
|
|
|
func toggleOffer(_ id: String) {
|
|
if !expandedOffers.insert(id).inserted { expandedOffers.remove(id) }
|
|
}
|
|
|
|
func toggleGroup(_ id: String) {
|
|
if !expandedGroups.insert(id).inserted { expandedGroups.remove(id) }
|
|
}
|
|
|
|
func toggleAll() {
|
|
guard let detail else { return }
|
|
if isAllExpanded {
|
|
expandedOffers = []
|
|
expandedGroups = []
|
|
} else {
|
|
expandedOffers = Set(detail.offers.map(\.id))
|
|
expandedGroups = Set(detail.offers.flatMap(\.groups).map(\.id))
|
|
}
|
|
}
|
|
}
|