mana-swift-ui/Sources/ManaFeedbackUI/ManaFeedbackCard.swift
Till JS c16694fc8e fix(ManaAccountView): full-width Karten ohne abgeschnittenen Border
Statt die randlosen Karten einzuruecken (zu schmal, Feedback Till): Anmelde-
CTA + Feedback-Karte verzichten full-bleed jetzt auf den Manual-Border und
definieren sich ueber Surface-Kontrast wie eine native grouped Section ->
volle Section-Breite, keine an den Ecken abgeschnittenen Ecken mehr.

Empirisch verifiziert: die inset-grouped Form-Maske clippt den Zelleninhalt
unabhaengig vom Karten-Eckradius (4/10/12pt = identische Ecken), ein Overlay-
strokeBorder laesst sich full-bleed nicht sauber schliessen. manaCardListRow()
kapselt jetzt nur noch die full-bleed-Row-Konfiguration + dokumentiert die Regel.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 23:37:50 +02:00

90 lines
3.5 KiB
Swift

import ManaCore
import ManaThemeUI
import ManaTokens
import SwiftUI
/// Prominenter Feedback-Block für den Konto-/Einstellungen-Tab — der
/// gewichtige Bruder der dezenten ``ManaFeedbackButton``-Listenzeile.
/// Icon + aktivierender Titel + ein Satz Einladung + gefüllter CTA in
/// Vereinsfarbe. Präsentiert dasselbe ``ManaFeedbackSheet``.
///
/// Wird typischerweise nicht von Hand platziert, sondern von
/// `ManaAccountView` an **fester Position** gerendert (direkt nach dem
/// Konto-/Anmelden-Block) — so steht Feedback in allen Verein-Apps gleich
/// weit oben und gleich auffällig, statt pro App handgebaut tief im
/// `extra`-Slot zu verschwinden.
///
/// Für Stand-alone-Einsatz (eigener Settings-Screen) in eine `Section`
/// mit geleertem Row-Background + Null-Insets legen, damit die Karte frei
/// in der `Form` schwebt:
///
/// ```swift
/// Section {
/// ManaFeedbackCard(client: client, isLoggedIn: session.isLoggedIn)
/// .listRowInsets(EdgeInsets())
/// .listRowBackground(Color.clear)
/// }
/// ```
public struct ManaFeedbackCard: View {
@Environment(\.manaTheme) private var theme
private let client: FeedbackClient
private let isLoggedIn: Bool
private let title: String
private let message: String
private let cta: String
@State private var isPresented = false
/// - Parameters:
/// - client: der App-`FeedbackClient` (trägt die `appId`).
/// - isLoggedIn: steuert das anonyme Kontakt-Mail-Feld im Sheet.
/// - title: aktivierende Überschrift. Default: Mitgestalten-Ton.
/// - message: ein Satz Einladung darunter.
/// - cta: Beschriftung des gefüllten Knopfs.
public init(
client: FeedbackClient,
isLoggedIn: Bool,
title: String = "Hilf mit, die App zu formen",
message: String = "Deine Wünsche steuern, was wir als Nächstes bauen — ein Mensch im Team liest mit.",
cta: String = "Vorschlag senden"
) {
self.client = client
self.isLoggedIn = isLoggedIn
self.title = title
self.message = message
self.cta = cta
}
public var body: some View {
VStack(alignment: .leading, spacing: 12) {
HStack(spacing: 10) {
Image(systemName: "bubble.left.and.bubble.right.fill")
.font(.title3)
.foregroundStyle(theme.primary)
Text(title)
.font(.headline)
.foregroundStyle(theme.foreground)
}
Text(message)
.font(.subheadline)
.foregroundStyle(theme.mutedForeground)
.fixedSize(horizontal: false, vertical: true)
Button { isPresented = true } label: {
Text(cta)
.fontWeight(.semibold)
.frame(maxWidth: .infinity)
.padding(.vertical, 12)
.background(theme.primary, in: RoundedRectangle.mana(Radius.md))
.foregroundStyle(theme.primaryForeground)
}
.buttonStyle(.plain)
}
// Full-bleed in der grouped `Form`: KEIN Manual-Border (einheitlich mit
// dem Login-CTA). Die inset-grouped Section-Maske clippt einen Overlay-
// Border an den Ecken → wir lassen ihn weg und definieren die Karte über
// Surface-Kontrast, wie eine native grouped Section.
.manaCard(surface: theme.surface)
.manaFeedbackSheet(isPresented: $isPresented, client: client, isLoggedIn: isLoggedIn)
}
}