Eingerueckt wurde mit fuehrenden Leerzeichen. Die verschieben aber nur die
ERSTE Zeile: Bricht ein langer Name um ("Schublade Links Unten"), faengt die
Fortsetzung wieder ganz links an und die Hierarchie ist weg.
In einem aufklappenden Menue laesst sich das nicht loesen - dort liefert man
nur Text ab, das System ignoriert jedes Layout (auch lineLimit). Die
Lagerort- und Kategorieauswahl erscheint deshalb jetzt als Liste
(.pickerStyle(.navigationLink)). Dort ist jede Zeile eine echte Ansicht, ein
Innenabstand rueckt den ganzen Block ein - auch die zweite Zeile. Das liest
sich bei vielen Lagerorten ohnehin besser als ein langes Aufklappmenue.
Zwei enge Zeilen bleiben Menues: Mindestbestand je Ort bei Artikel und
Gruppe, wo direkt daneben das Mengenfeld steht. Dort wuerde eine
Listenauswahl die Zeile sprengen; sie behalten die Leerzeichen-Einrueckung.
Der Loeschdialog war ein confirmationDialog und fuhr als Blase von unten
herein. Jetzt ein alert: steht mittig und will gelesen werden.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
99 lines
3.3 KiB
Swift
99 lines
3.3 KiB
Swift
import SwiftUI
|
||
|
||
/// Auswahl einer Kategorie mit eingerueckten Unterkategorien.
|
||
///
|
||
/// Kategorie und Gruppe sind zwei verschiedene Dinge: Die Kategorie ordnet nur
|
||
/// die Artikelliste, die Gruppe zaehlt Bestaende mehrerer Marken zusammen.
|
||
struct CategoryPicker: View {
|
||
let categories: [CategoryItem]
|
||
@Binding var selection: Int?
|
||
/// Nur Kategorien dieses Typs zeigen ("food"/"object"); nil = alle.
|
||
var tracking: String? = nil
|
||
|
||
var body: some View {
|
||
Picker("Kategorie", selection: $selection) {
|
||
Text("– keine –").tag(Int?.none)
|
||
ForEach(baum(gefiltert)) { eintrag in
|
||
BaumZeile(zeile: eintrag).tag(Int?.some(eintrag.item.id))
|
||
}
|
||
}
|
||
// Als Liste statt als Menue: Nur so rueckt ein umbrechender langer
|
||
// Name auch in der zweiten Zeile ein.
|
||
.pickerStyle(.navigationLink)
|
||
}
|
||
|
||
private var gefiltert: [CategoryItem] {
|
||
guard let tracking else { return categories }
|
||
return categories.filter { $0.tracking == tracking }
|
||
}
|
||
}
|
||
|
||
/// Legt eine Gruppe an Ort und Stelle an, damit man das Formular nicht
|
||
/// verlassen muss. Nur fuer Administratoren - POST /groups verlangt das.
|
||
struct NewGroupSheet: View {
|
||
let units: [Unit]
|
||
var onCreated: (GroupItem) -> Void
|
||
|
||
@Environment(\.dismiss) private var dismiss
|
||
|
||
@State private var name = ""
|
||
@State private var minStock = ""
|
||
@State private var unitId: Int?
|
||
@State private var busy = false
|
||
@State private var error: String?
|
||
|
||
var body: some View {
|
||
Form {
|
||
Section {
|
||
LabeledField(label: "Name", text: $name)
|
||
} footer: {
|
||
Text("Eine Gruppe zählt Bestände mehrerer Marken zusammen – "
|
||
+ "z. B. Mehl von verschiedenen Herstellern als „5 kg Mehl“.")
|
||
}
|
||
|
||
Section("Mindestbestand (optional)") {
|
||
QuantityField(label: "Menge", text: $minStock)
|
||
Picker("Einheit", selection: $unitId) {
|
||
Text("– Basiseinheit –").tag(Int?.none)
|
||
ForEach(units) { unit in
|
||
Text(unit.name).tag(Int?.some(unit.id))
|
||
}
|
||
}
|
||
}
|
||
|
||
if let error {
|
||
Section { Text(error).foregroundStyle(.red).font(.callout) }
|
||
}
|
||
|
||
Section {
|
||
Button(busy ? "Anlegen…" : "Anlegen") { Task { await create() } }
|
||
.disabled(busy || name.trimmingCharacters(in: .whitespaces).isEmpty)
|
||
}
|
||
}
|
||
.navigationTitle("Neue Gruppe")
|
||
.navigationBarTitleDisplayMode(.inline)
|
||
.toolbar {
|
||
ToolbarItem(placement: .topBarLeading) { Button("Abbrechen") { dismiss() } }
|
||
}
|
||
}
|
||
|
||
private func create() async {
|
||
error = nil
|
||
busy = true
|
||
defer { busy = false }
|
||
do {
|
||
let created = try await APIClient.shared.createGroup(
|
||
NewGroupRequest(
|
||
name: name.trimmingCharacters(in: .whitespaces),
|
||
minStock: Double(minStock.replacingOccurrences(of: ",", with: ".")),
|
||
minStockUnitId: unitId
|
||
)
|
||
)
|
||
onCreated(created)
|
||
dismiss()
|
||
} catch {
|
||
self.error = error.localizedDescription
|
||
}
|
||
}
|
||
}
|