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,140 @@
import SwiftUI
struct ARProjectCreateView: View {
let isEditing: Bool
let onCreate: (String, Tank, Bool) -> Void
@State private var name = ""
@State private var tank = Tank()
@Environment(\.tankCatalog) private var catalog
init(project: ARProject? = nil, onCreate: @escaping (String, Tank, Bool) -> Void) {
isEditing = project != nil
self.onCreate = onCreate
_name = State(initialValue: project?.name ?? "")
_tank = State(initialValue: project?.tank ?? Tank())
}
private var canCreate: Bool {
!name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
&& catalog.definition(for: tank) != nil
}
var body: some View {
Form {
Section {
VStack(spacing: 8) {
ARProjectTankImage(tank: tank)
.frame(height: 150)
.frame(maxWidth: .infinity)
Text("\(tank.size.volumeTitle) · \(tank.placement.displayName) · \(catalog.displayName(for: tank.color))")
.font(.subheadline).foregroundStyle(.secondary)
}
.padding(.vertical, 8)
}
Section("Name") {
TextField("Projektname", text: $name)
.textInputAutocapitalization(.sentences)
}
Section("Tankkonfiguration") {
ARProjectConfigurationView(tank: $tank)
}
}
.navigationTitle(isEditing ? "Projekt bearbeiten" : "Neues Projekt")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .confirmationAction) {
Button(isEditing ? "Speichern" : "Anlegen") { onCreate(name, tank, false) }
.disabled(!canCreate)
}
}
.safeAreaInset(edge: .bottom) {
Button { onCreate(name, tank, true) } label: {
Label(isEditing ? "Speichern und AR-Modus starten" : "Anlegen und AR-Modus starten", systemImage: "arkit")
.font(.headline).frame(maxWidth: .infinity).padding(.vertical, 10)
}
.buttonStyle(.borderedProminent)
.disabled(!canCreate)
.padding()
.background(.bar)
}
.onAppear { tank = catalog.normalized(tank) }
}
}
struct ARProjectConfigurationView: View {
@Binding var tank: Tank
@Environment(\.tankCatalog) private var catalog
var body: some View {
VStack(alignment: .leading, spacing: 10) {
Text("Volumen").font(.subheadline)
Picker("Volumen", selection: $tank.size) {
ForEach(catalog.sizes(for: tank.placement)) { size in
Text(size.volumeTitle).tag(size)
}
}
.pickerStyle(.segmented)
}
.padding(.vertical, 4)
VStack(alignment: .leading, spacing: 10) {
Text("Aufstellung").font(.subheadline)
Picker("Aufstellung", selection: $tank.placement) {
ForEach(catalog.placements) { placement in
Text(placement.displayName).tag(placement)
}
}
.pickerStyle(.segmented)
}
.padding(.vertical, 4)
HStack {
Text("Farbe")
Spacer()
ForEach(catalog.colors(for: tank.size, placement: tank.placement)) { color in
Button { tank.color = color } label: {
Circle().fill(swatch(color))
.frame(width: 28, height: 28)
.overlay { Circle().strokeBorder(.gray.opacity(0.5), lineWidth: 1) }
.padding(4)
.overlay { Circle().strokeBorder(tank.color == color ? Color.accentColor : .clear, lineWidth: 2) }
.frame(minWidth: 44, minHeight: 44)
}
.buttonStyle(.plain)
.accessibilityLabel(catalog.displayName(for: color))
.accessibilityAddTraits(tank.color == color ? .isSelected : [])
}
Text(catalog.displayName(for: tank.color)).foregroundStyle(.secondary).font(.subheadline)
}
.onChange(of: tank.placement) { _, _ in normalize() }
.onChange(of: tank.size) { _, _ in normalize() }
}
private func normalize() {
tank = catalog.normalized(tank)
tank.showLidOnly = false
}
private func swatch(_ color: TankColor) -> Color {
switch color {
case .white: .white
case .green: Color(red: 0.49, green: 0.65, blue: 0.42)
case .olive: Color(red: 0.37, green: 0.40, blue: 0.22)
}
}
}
#Preview("Projekt konfigurieren") {
NavigationStack {
ARProjectCreateView { _, _, _ in }
}
.frame(width: 600, height: 760)
}
#Preview("Projekt konfigurieren – Dunkel") {
NavigationStack {
ARProjectCreateView { _, _, _ in }
}
.frame(width: 600, height: 760)
.preferredColorScheme(.dark)
}