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,110 @@
import SwiftUI
enum LeadDetailStyle {
static let blue = LeadOverviewStyle.blue
static let background = Color(uiColor: .systemGroupedBackground)
static let card = Color(uiColor: .secondarySystemGroupedBackground)
}
extension LeadDetail {
var initials: String {
String(lead.displayName.split(separator: " ").prefix(2).compactMap(\.first)).uppercased()
}
var contactDescription: String? {
let value = [contactName, contactRole].compactMap { $0 }.joined(separator: " · ")
return value.isEmpty ? nil : value
}
var phoneURL: URL? {
guard let phone = lead.phoneNumber else { return nil }
let digits = phone.filter { $0.isNumber || $0 == "+" }
return digits.isEmpty ? nil : URL(string: "tel:\(digits)")
}
var emailURL: URL? {
guard let email, !email.isEmpty else { return nil }
var url = URLComponents()
url.scheme = "mailto"
url.path = email
return url.url
}
var routeURL: URL? {
var url = URLComponents()
url.scheme = "https"
url.host = "maps.apple.com"
url.queryItems = [URLQueryItem(name: "daddr", value: lead.fullAddress)]
return url.url
}
}
extension LeadOffer {
var subtitle: String {
let kindName: String
switch kind {
case .purchase: kindName = "Kauf"
case .rental: kindName = "Miete"
case .service: kindName = "Service"
}
let price = amount.formatted(.currency(code: "EUR").locale(Locale(identifier: "de_DE")))
let interval: String
switch billing {
case .once: interval = "netto"
case .monthly: interval = "/ Monat"
case .annually: interval = "/ Jahr"
}
let date = acceptedAt ?? validUntil
let suffix = date.map {
(acceptedAt == nil ? "gültig bis " : "angenommen am ")
+ $0.formatted(.dateTime.day().month().year().locale(Locale(identifier: "de_DE")))
}
return [id, kindName, "\(price) \(interval)", suffix].compactMap { $0 }.joined(separator: " · ")
}
}
struct LeadSectionHeading: View {
let title: LocalizedStringKey
var body: some View {
Text(title).font(.caption).foregroundStyle(.secondary)
.padding(.horizontal, 16)
}
}
struct LeadSignatureProgress: View {
let signed: Int
let total: Int
var body: some View {
if total > 0 && signed == total {
Label("Vollständig", systemImage: "checkmark.circle.fill")
.foregroundStyle(.green).font(.subheadline)
} else {
Text("\(signed) von \(total)").foregroundStyle(.secondary).font(.subheadline)
}
}
}
struct LeadSignButton: View {
let action: () -> Void
var body: some View {
Button(action: action) {
Label("Unterschreiben", systemImage: "signature")
.font(.subheadline.weight(.medium))
}
.buttonStyle(.borderedProminent)
.buttonBorderShape(.capsule)
}
}
extension Error {
var leadFileMessage: LocalizedStringResource {
if let error = self as? LeadError { return error.message }
switch self as? LeadFileError {
case .notFound: return "Diese Datei ist nicht mehr verfügbar."
case .invalidFile: return "Die heruntergeladene Datei ist ungültig und kann nicht geöffnet werden."
case .storageUnavailable: return "Die Datei konnte nicht auf diesem Gerät gespeichert werden."
case nil: return "Die Datei konnte nicht geladen werden. Bitte versuche es erneut."
}
}
}