ContactDraftFieldsView.swift 130 lignes · 5133 octets
import SwiftUI

// Port of ui/ContactDraftFields.kt. When `phonesAsList` is set (edit screen),
// phones/e-mails render as editable per-item lists instead of the Android
// comma-joined single field.

struct ContactDraftFieldsView: View {
    @Binding var card: ContactCard
    var phonesAsList: Bool = false

    /// Kotlin `scan_champ_a_verifier` — hint under low-confidence OCR fields,
    /// cleared as soon as the user corrects the field.
    private static let aVerifier = "À vérifier — lecture incertaine"

    var body: some View {
        VStack(alignment: .leading, spacing: 10) {
            C2VTextField(
                label: "Nom complet",
                text: optionalBinding(\.fullName, douteuxKey: "fullName"),
                supportingText: sousTexte("fullName")
            )
            C2VTextField(label: "Prénom", text: optionalBinding(\.firstName))
            C2VTextField(label: "Nom de famille", text: optionalBinding(\.lastName))
            C2VTextField(
                label: "Société",
                text: optionalBinding(\.company, douteuxKey: "company"),
                supportingText: sousTexte("company")
            )
            C2VTextField(
                label: "Poste",
                text: optionalBinding(\.jobTitle, douteuxKey: "jobTitle"),
                supportingText: sousTexte("jobTitle")
            )
            if phonesAsList {
                EditableStringList(label: "Téléphone(s)", items: $card.phones)
                EditableStringList(label: "E-mail(s)", items: $card.emails)
            } else {
                C2VTextField(label: "Téléphone(s)", text: joinedBinding(\.phones))
                    .keyboardType(.phonePad)
                C2VTextField(label: "E-mail(s)", text: joinedBinding(\.emails))
                    .keyboardType(.emailAddress)
                    .textInputAutocapitalization(.never)
            }
            C2VTextField(label: "Site web", text: optionalBinding(\.website))
                .keyboardType(.URL)
                .textInputAutocapitalization(.never)
            C2VTextField(
                label: "Adresse",
                text: optionalBinding(\.address, douteuxKey: "address"),
                supportingText: sousTexte("address")
            )
            C2VTextField(label: "Note", text: optionalBinding(\.note), minLines: 2)
        }
    }

    private func sousTexte(_ cle: String) -> String? {
        card.champsDouteux.contains(cle) ? Self.aVerifier : nil
    }

    /// `douteuxKey`: editing the field clears its « à vérifier » flag.
    private func optionalBinding(
        _ keyPath: WritableKeyPath<ContactCard, String?>,
        douteuxKey: String? = nil
    ) -> Binding<String> {
        Binding(
            get: { card[keyPath: keyPath] ?? "" },
            set: { value in
                card[keyPath: keyPath] = value.nilIfBlank
                if let douteuxKey {
                    card.champsDouteux.remove(douteuxKey)
                }
            }
        )
    }

    // Kotlin joined with ", " and split on ','/';'.
    private func joinedBinding(_ keyPath: WritableKeyPath<ContactCard, [String]>) -> Binding<String> {
        Binding(
            get: { card[keyPath: keyPath].joined(separator: ", ") },
            set: { value in
                card[keyPath: keyPath] = value
                    .split(whereSeparator: { $0 == "," || $0 == ";" })
                    .map { $0.trimmingCharacters(in: .whitespaces) }
                    .filter { !$0.isEmpty }
            }
        )
    }
}

/// Editable list of strings: one square field per entry, remove button, add row.
struct EditableStringList: View {
    let label: String
    @Binding var items: [String]

    var body: some View {
        VStack(alignment: .leading, spacing: 6) {
            Text(label)
                .font(C2VFont.labelSmall)
                .foregroundColor(C2VColor.body)
            ForEach(items.indices, id: \.self) { index in
                HStack(spacing: 8) {
                    C2VTextField(label: "", text: Binding(
                        get: { index < items.count ? items[index] : "" },
                        set: { value in
                            if index < items.count { items[index] = value }
                        }
                    ))
                    Button {
                        if index < items.count { items.remove(at: index) }
                    } label: {
                        Image(systemName: "minus")
                            .foregroundColor(C2VColor.ink)
                            .frame(width: 32, height: 32)
                            .overlay(Rectangle().stroke(C2VColor.bordure, lineWidth: 1))
                    }
                    .accessibilityLabel("Supprimer")
                }
            }
            Button {
                items.append("")
            } label: {
                Text("Ajouter")
                    .font(C2VFont.labelMedium)
                    .foregroundColor(C2VColor.ink)
                    .padding(.vertical, 4)
            }
            .buttonStyle(.plain)
        }
    }
}