Add lead workflows, PDF signing, AR projects and photo gallery integration

This commit is contained in:
Dennis Nemec
2026-11-08 17:49:39 +01:00
parent a343bde741
commit 8d18873412
82 changed files with 6401 additions and 34 deletions

View File

@ -0,0 +1,58 @@
import Foundation
/// Transport boundary. A future URLSession implementation must enforce a finite
/// request timeout and throw HTTP errors before decoding the response body.
@MainActor
protocol LeadRemoteService {
func fetchLeads(salesCode: String, query: LeadQuery) async throws -> [LeadDTO]
func fetchLead(id: String, salesCode: String) async throws -> LeadDTO
func setBookmark(_ bookmarked: Bool, id: String, salesCode: String) async throws -> LeadDTO
}
enum LeadRemoteError: Error {
case http(statusCode: Int)
case malformedResponse
}
/// Provisional transport shape, independent of the UI and domain model.
struct LeadDTO: Codable, Sendable {
let id: String
let companyName: String?
let contactTitle: String?
let firstName: String?
let lastName: String?
let street: String?
let postalCode: String
let city: String
let phoneNumber: String?
let createdAt: Date
let offerCount: Int
let unsignedContractCount: Int
var isBookmarked: Bool
func toDomain() throws -> Lead {
func clean(_ value: String?) -> String? {
guard let text = value?.trimmingCharacters(in: .whitespacesAndNewlines),
!text.isEmpty else { return nil }
return text
}
let company = clean(companyName)
let first = clean(firstName)
let last = clean(lastName)
guard clean(id) != nil, company != nil || first != nil || last != nil,
clean(postalCode) != nil, clean(city) != nil,
offerCount >= 0, unsignedContractCount >= 0 else {
throw LeadError.invalidResponse
}
let contact = first != nil || last != nil
? LeadContact(title: clean(contactTitle), firstName: first ?? "", lastName: last ?? "")
: nil
return Lead(
id: id, companyName: company, contact: contact,
address: LeadAddress(street: clean(street), postalCode: postalCode, city: city),
phoneNumber: clean(phoneNumber), createdAt: createdAt,
offerCount: offerCount, unsignedContractCount: unsignedContractCount,
isBookmarked: isBookmarked
)
}
}