From d680aeff0ee9bcf478a86c8c14eccafe11a023cf Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Wed, 22 Jul 2026 17:35:10 +0200 Subject: [PATCH] Mengenfeld beschriftet und "Angemeldet bleiben" ergaenzt Beim Einlagern stand in der Chargenzeile eine nackte 1 ohne Beschriftung. Das las sich wie eine Nummerierung der Charge, war aber das Mengenfeld. Die Zeile hat jetzt links die Beschriftung "Menge", der Wert steht rechtsbuendig; die 1 bleibt als Vorgabe, weil meistens genau ein Stueck eingelagert wird. Beim Auslagern war dieselbe Zeile ebenso unbeschriftet und ist jetzt gleich aufgebaut. Anmeldung: Das Token wanderte bisher immer dauerhaft in den Schluesselbund. Der neue Haken "Angemeldet bleiben" laesst sich abwaehlen - dann gilt die Anmeldung nur, solange die App laeuft, und es bleibt nichts auf dem Geraet zurueck. Die Wahl wird gemerkt, damit der Haken beim naechsten Mal richtig steht. Dabei ist aufgefallen, dass der Benutzername nirgends gespeichert wurde: Nach einem Neustart stand im Menue nur noch "Angemeldet als" ohne Namen. Er wird jetzt zusammen mit dem Token abgelegt und beim Abmelden wieder entfernt. Co-Authored-By: Claude Opus 4.8 --- ios/AppIcon.icon/icon.json | 8 ++++++++ ios/Sources/CheckInFormView.swift | 6 ++++++ ios/Sources/CheckOutView.swift | 9 ++++++++- ios/Sources/LoginView.swift | 11 ++++++++++- ios/Sources/Session.swift | 23 +++++++++++++++++++++-- 5 files changed, 53 insertions(+), 4 deletions(-) diff --git a/ios/AppIcon.icon/icon.json b/ios/AppIcon.icon/icon.json index a2cc60d..f940700 100644 --- a/ios/AppIcon.icon/icon.json +++ b/ios/AppIcon.icon/icon.json @@ -6,6 +6,14 @@ { "layers" : [ { + "fill-specializations" : [ + { + "appearance" : "dark", + "value" : { + "solid" : "extended-gray:1.00000,1.00000" + } + } + ], "glass" : false, "image-name" : "barcode-gelesen.svg", "name" : "barcode-gelesen", diff --git a/ios/Sources/CheckInFormView.swift b/ios/Sources/CheckInFormView.swift index 7ed8073..7920280 100644 --- a/ios/Sources/CheckInFormView.swift +++ b/ios/Sources/CheckInFormView.swift @@ -67,8 +67,14 @@ struct CheckInFormView: View { ForEach($lines) { $line in VStack(alignment: .leading, spacing: 6) { HStack { + // Ohne Beschriftung sah die vorbelegte 1 wie eine + // Nummerierung der Charge aus. + Text("Menge") + Spacer(minLength: 12) TextField("Menge", text: $line.quantity) .keyboardType(.decimalPad) + .multilineTextAlignment(.trailing) + .frame(maxWidth: 110) if lines.count > 1 { Button(role: .destructive) { lines.removeAll { $0.id == line.id } diff --git a/ios/Sources/CheckOutView.swift b/ios/Sources/CheckOutView.swift index 1566751..f2b886e 100644 --- a/ios/Sources/CheckOutView.swift +++ b/ios/Sources/CheckOutView.swift @@ -155,7 +155,14 @@ struct CheckOutFormView: View { } Section("Menge") { - TextField("Menge", text: $quantity).keyboardType(.decimalPad) + HStack { + Text("Menge") + Spacer(minLength: 12) + TextField("Menge", text: $quantity) + .keyboardType(.decimalPad) + .multilineTextAlignment(.trailing) + .frame(maxWidth: 110) + } Picker("Einheit", selection: $unit) { ForEach(unitOptions, id: \.self) { Text($0).tag($0) } } diff --git a/ios/Sources/LoginView.swift b/ios/Sources/LoginView.swift index 849aa49..f363a95 100644 --- a/ios/Sources/LoginView.swift +++ b/ios/Sources/LoginView.swift @@ -8,6 +8,7 @@ struct LoginView: View { @State private var password = "" @State private var error: String? @State private var busy = false + @State private var stayLoggedIn = Session.shared.stayLoggedIn var body: some View { NavigationStack { @@ -23,6 +24,13 @@ struct LoginView: View { .textInputAutocapitalization(.never) .autocorrectionDisabled() SecureField("Passwort", text: $password) + Toggle("Angemeldet bleiben", isOn: $stayLoggedIn) + } + if !stayLoggedIn { + Section { + Text("Die Anmeldung gilt nur, solange die App geoeffnet ist.") + .font(.caption).foregroundStyle(.secondary) + } } if let error { Section { Text(error).foregroundStyle(.red).font(.callout) } @@ -43,7 +51,8 @@ struct LoginView: View { session.setServer(server) do { let token = try await APIClient.shared.login(username: username, password: password) - session.store(token: token.accessToken, username: token.username, isAdmin: token.role == "admin") + session.store(token: token.accessToken, username: token.username, + isAdmin: token.role == "admin", persist: stayLoggedIn) } catch { self.error = error.localizedDescription } diff --git a/ios/Sources/Session.swift b/ios/Sources/Session.swift index 3f03cbe..bc7bb0a 100644 --- a/ios/Sources/Session.swift +++ b/ios/Sources/Session.swift @@ -7,12 +7,16 @@ final class Session: ObservableObject { static let shared = Session() private let urlKey = "server_url" + private let usernameKey = "username" + private let stayKey = "stay_logged_in" private let keychainAccount = "project-good-token" @Published private(set) var baseURL: URL? @Published private(set) var token: String? @Published var username: String = "" @Published var isAdmin: Bool = false + /// Merkt die letzte Wahl, damit der Haken im Login richtig steht. + @Published private(set) var stayLoggedIn: Bool = true var isLoggedIn: Bool { token != nil && baseURL != nil } @@ -20,7 +24,10 @@ final class Session: ObservableObject { if let stored = UserDefaults.standard.string(forKey: urlKey) { baseURL = Session.normalize(stored) } + // Ohne bisherige Wahl bleibt man angemeldet - das ist der Alltagsfall. + stayLoggedIn = UserDefaults.standard.object(forKey: stayKey) as? Bool ?? true token = Keychain.read(account: keychainAccount) + username = UserDefaults.standard.string(forKey: usernameKey) ?? "" } /// Sorgt für eine URL mit Schema und abschließendem "/", damit relative @@ -39,11 +46,22 @@ final class Session: ObservableObject { UserDefaults.standard.set(url.absoluteString, forKey: urlKey) } - func store(token newToken: String, username name: String, isAdmin admin: Bool) { + /// `persist == false` heisst: die Anmeldung gilt nur, solange die App laeuft. + /// Auf dem Geraet bleibt dann nichts zurueck. + func store(token newToken: String, username name: String, isAdmin admin: Bool, persist: Bool) { token = newToken username = name isAdmin = admin - Keychain.write(newToken, account: keychainAccount) + stayLoggedIn = persist + UserDefaults.standard.set(persist, forKey: stayKey) + + if persist { + Keychain.write(newToken, account: keychainAccount) + UserDefaults.standard.set(name, forKey: usernameKey) + } else { + Keychain.delete(account: keychainAccount) + UserDefaults.standard.removeObject(forKey: usernameKey) + } } func logout() { @@ -51,6 +69,7 @@ final class Session: ObservableObject { username = "" isAdmin = false Keychain.delete(account: keychainAccount) + UserDefaults.standard.removeObject(forKey: usernameKey) } }