Add lead workflows, PDF signing, AR projects and photo gallery integration
This commit is contained in:
110
GasTankAR/Views/Leads/Detail/LeadDetailPresentation.swift
Normal file
110
GasTankAR/Views/Leads/Detail/LeadDetailPresentation.swift
Normal 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."
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user