Files
Vorrania/ios/Sources/ServerListView.swift
Scarriffle 1648d1b917 iOS: Kassenzettel in den Scannen-Tab, Markenblau statt System-Blau, weisse Kacheln, luftigere Pruefen-Liste
- Kassenzettel-Kachel vom Listen- in den Scannen-Tab verschoben (passt thematisch).
- App-Akzent auf Markenblau (#3F51B5/#7C8AE8, wie Web/App-Icon) via .tint; alle
  Color.accentColor -> Color.marke. System-Blau verschwindet.
- Kachel-Buttons/Links auf .buttonStyle(.plain): weisse Schrift/Symbole statt
  getoentem Blau.
- Kassenzettel-Pruefen-Liste: je Artikel eine eigene Section (insetGrouped) mit
  Artikel/Menge/Lagerort in eigenen Zeilen - klar gruppiert, nicht mehr gequetscht.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-30 19:05:03 +02:00

212 lines
8.3 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import SwiftUI
/// Wechselt den Server aus der Werkzeugleiste heraus - der schnelle Weg.
/// Die vollstaendige Verwaltung liegt in `ServerListView`.
struct ServerMenu: View {
@EnvironmentObject private var session: Session
@Binding var manageShown: Bool
var body: some View {
Menu {
if session.profiles.count > 1 {
Section("Server") {
ForEach(session.profiles) { profile in
Button {
guard profile.id != session.activeProfileID else { return }
session.switchTo(profile.id)
} label: {
// Der Haken zeigt, wo man gerade ist. Ein leerer
// Symbolname wuerde eine kaputte Lucke hinterlassen,
// deshalb zwei getrennte Beschriftungen.
if profile.id == session.activeProfileID {
Label(profile.name, systemImage: "checkmark")
} else {
Text(profile.name)
}
}
}
}
}
Button("Server verwalten…", systemImage: "server.rack") { manageShown = true }
if session.isLoggedIn {
Divider()
Text("Angemeldet als \(session.username)")
Button("Abmelden", role: .destructive) { session.logout() }
}
} label: {
Image(systemName: "person.circle")
}
}
}
struct ServerListView: View {
@EnvironmentObject private var session: Session
@Environment(\.dismiss) private var dismiss
@State private var editing: ServerProfile?
@State private var addShown = false
@State private var pendingDeletion: ServerProfile?
@State private var notifyFor: ServerProfile?
var body: some View {
NavigationStack {
List {
Section {
ForEach(session.profiles) { profile in
HStack(spacing: 8) {
// Zeile wechselt den Server, die Glocke oeffnet die
// Benachrichtigungen - zwei getrennte Ziele in einer Zeile.
Button {
session.switchTo(profile.id)
dismiss()
} label: {
row(for: profile)
}
.buttonStyle(.plain)
Button {
notifyFor = profile
} label: {
Image(systemName: NotificationStore.config(for: profile.id).enabled
? "bell.fill" : "bell")
.foregroundStyle(Color.marke)
}
.buttonStyle(.borderless)
}
.swipeActions(edge: .trailing) {
Button("Löschen", role: .destructive) { pendingDeletion = profile }
Button("Bearbeiten") { editing = profile }.tint(.gray)
}
}
} footer: {
Text("Jeder Server merkt sich seine eigene Anmeldung. Ein Wechsel meldet dich nicht ab. Die Glocke stellt Ablauf-Erinnerungen ein.")
}
if session.profiles.isEmpty {
Text("Noch kein Server hinterlegt.")
.foregroundStyle(.secondary)
}
}
.navigationTitle("Server")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button("Hinzufügen", systemImage: "plus") { addShown = true }
}
ToolbarItem(placement: .topBarLeading) {
Button("Fertig") { dismiss() }
}
}
.sheet(isPresented: $addShown) {
ServerEditView(profile: nil)
}
.sheet(item: $editing) { profile in
ServerEditView(profile: profile)
}
.sheet(item: $notifyFor) { profile in
NavigationStack {
NotificationSettingsView(profileID: profile.id, serverName: profile.name)
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Button("Fertig") { notifyFor = nil }
}
}
}
}
.confirmationDialog(
pendingDeletion.map { "\($0.name)“ löschen?" } ?? "",
isPresented: Binding(get: { pendingDeletion != nil },
set: { if !$0 { pendingDeletion = nil } }),
titleVisibility: .visible
) {
Button("Löschen", role: .destructive) {
if let profile = pendingDeletion { session.removeProfile(id: profile.id) }
pendingDeletion = nil
}
Button("Abbrechen", role: .cancel) { pendingDeletion = nil }
} message: {
Text("Die gespeicherte Anmeldung für diesen Server wird mit entfernt. Auf dem Server selbst ändert sich nichts.")
}
}
}
private func row(for profile: ServerProfile) -> some View {
HStack {
VStack(alignment: .leading, spacing: 2) {
Text(profile.name).font(.body)
Text(profile.urlString).font(.caption).foregroundStyle(.secondary)
.lineLimit(1).truncationMode(.middle)
}
Spacer()
if profile.id == session.activeProfileID {
Image(systemName: "checkmark").foregroundStyle(Color.marke)
}
}
.foregroundStyle(.primary)
}
}
/// Anlegen und Bearbeiten in einem - der Unterschied ist nur, ob schon ein
/// Profil hereingereicht wurde.
struct ServerEditView: View {
@EnvironmentObject private var session: Session
@Environment(\.dismiss) private var dismiss
let profile: ServerProfile?
@State private var name: String
@State private var urlString: String
init(profile: ServerProfile?) {
self.profile = profile
_name = State(initialValue: profile?.name ?? "")
_urlString = State(initialValue: profile?.urlString ?? "")
}
private var canSave: Bool {
Session.normalize(urlString) != nil
}
var body: some View {
NavigationStack {
Form {
// Der Name steht bewusst oben: Er ist das, was in der App zu
// sehen ist ("Zuhause"), die Adresse braucht man nur einmal.
Section {
TextField("z. B. Zuhause", text: $name)
} header: {
Text("Name")
} footer: {
Text("So heißt der Server in der App. Ohne Angabe wird der Rechnername verwendet meist die IP-Adresse.")
}
Section("Adresse") {
TextField("http://192.168.1.10:8080", text: $urlString)
.textInputAutocapitalization(.never)
.autocorrectionDisabled()
.keyboardType(.URL)
}
}
.navigationTitle(profile == nil ? "Server hinzufügen" : "Server bearbeiten")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Button("Abbrechen") { dismiss() }
}
ToolbarItem(placement: .topBarTrailing) {
Button("Sichern") { save() }.disabled(!canSave)
}
}
}
}
private func save() {
if let profile {
session.updateProfile(id: profile.id, name: name, urlString: urlString)
} else {
// Nach dem Anlegen ist der neue Server der aktive; fehlt dort eine
// Anmeldung, erscheint sie von selbst.
session.addProfile(name: name, urlString: urlString)
}
dismiss()
}
}