b477a3e04b
Statt Fragen einzeln zu ersetzen, werden sie jetzt nacheinander von unten eingeblendet und bleiben sichtbar: - Tippen auf einen Dot zeigt die nächste Frage darunter an - "Überspringen" blendet ebenfalls die nächste Frage ein - Beantwortete Fragen bleiben sichtbar und können angepasst werden - Nach der letzten Frage erscheint ein "Speichern"-Button - Gilt für Sofort-Bewertung (MeetingRatingFlowView) und Nachwirkung (AftermathRatingFlowView) - Neuer QuestionCard-Component in RatingQuestionView.swift Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
149 lines
5.1 KiB
Swift
149 lines
5.1 KiB
Swift
import SwiftUI
|
|
|
|
// MARK: - QuestionCard
|
|
// Einzelne Bewertungsfrage als Card für den scrollbaren Flow.
|
|
// isActive = letzte sichtbare Frage (noch nicht bestätigt).
|
|
|
|
struct QuestionCard: View {
|
|
@Environment(\.nahbarTheme) var theme
|
|
let question: RatingQuestion
|
|
let index: Int
|
|
let total: Int
|
|
let isActive: Bool
|
|
@Binding var value: Int?
|
|
let onAnswer: () -> Void // Dot ausgewählt (nur wenn isActive)
|
|
let onSkip: () -> Void // Überspringen getippt (nur wenn isActive)
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 16) {
|
|
HStack {
|
|
Text("\(index + 1) / \(total)")
|
|
.font(.caption.weight(.medium))
|
|
.foregroundStyle(theme.contentTertiary)
|
|
Spacer()
|
|
HStack(spacing: 5) {
|
|
Image(systemName: question.category.icon)
|
|
.font(.caption.bold())
|
|
Text(LocalizedStringKey(question.category.rawValue))
|
|
.font(.caption.bold())
|
|
}
|
|
.foregroundStyle(question.category.color)
|
|
.padding(.horizontal, 10)
|
|
.padding(.vertical, 5)
|
|
.background(question.category.color.opacity(0.12), in: Capsule())
|
|
}
|
|
|
|
Text(LocalizedStringKey(question.text))
|
|
.font(.system(size: 16, weight: .semibold, design: theme.displayDesign))
|
|
.foregroundStyle(theme.contentPrimary)
|
|
.fixedSize(horizontal: false, vertical: true)
|
|
|
|
RatingDotPicker(
|
|
value: $value,
|
|
negativePole: question.negativePole,
|
|
positivePole: question.positivePole
|
|
)
|
|
.onChange(of: value) { _, newValue in
|
|
if isActive, newValue != nil {
|
|
onAnswer()
|
|
}
|
|
}
|
|
|
|
if isActive {
|
|
Button {
|
|
value = nil
|
|
onSkip()
|
|
} label: {
|
|
Text("Überspringen")
|
|
.font(.subheadline)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
.buttonStyle(.plain)
|
|
}
|
|
}
|
|
.padding(16)
|
|
.background(theme.surfaceCard)
|
|
.clipShape(RoundedRectangle(cornerRadius: theme.radiusCard))
|
|
.opacity(isActive ? 1.0 : 0.75)
|
|
}
|
|
}
|
|
|
|
// MARK: - RatingQuestionView
|
|
// Zeigt eine einzelne Bewertungsfrage mit Kategorie-Badge, Fragetext,
|
|
// RatingDotPicker und "Überspringen"-Button.
|
|
|
|
struct RatingQuestionView: View {
|
|
let question: RatingQuestion
|
|
let index: Int // 0-basiert innerhalb des aktuellen Flows
|
|
let total: Int
|
|
@Binding var value: Int?
|
|
|
|
var body: some View {
|
|
VStack(spacing: 0) {
|
|
// Fortschrittsbalken
|
|
GeometryReader { geo in
|
|
ZStack(alignment: .leading) {
|
|
Rectangle()
|
|
.fill(Color(.systemGray5))
|
|
Rectangle()
|
|
.fill(question.category.color)
|
|
.frame(width: geo.size.width * CGFloat(index + 1) / CGFloat(total))
|
|
.animation(.easeInOut(duration: 0.3), value: index)
|
|
}
|
|
}
|
|
.frame(height: 4)
|
|
|
|
ScrollView {
|
|
VStack(spacing: 32) {
|
|
// Kategorie-Badge
|
|
HStack(spacing: 6) {
|
|
Image(systemName: question.category.icon)
|
|
.font(.caption.bold())
|
|
Text(LocalizedStringKey(question.category.rawValue))
|
|
.font(.caption.bold())
|
|
}
|
|
.foregroundStyle(question.category.color)
|
|
.padding(.horizontal, 14)
|
|
.padding(.vertical, 6)
|
|
.background(question.category.color.opacity(0.12), in: Capsule())
|
|
.padding(.top, 32)
|
|
|
|
// Fragetext
|
|
Text(LocalizedStringKey(question.text))
|
|
.font(.title3.weight(.semibold))
|
|
.multilineTextAlignment(.center)
|
|
.padding(.horizontal, 24)
|
|
|
|
// Picker
|
|
RatingDotPicker(value: $value,
|
|
negativePole: question.negativePole,
|
|
positivePole: question.positivePole)
|
|
.padding(.horizontal, 24)
|
|
|
|
// Überspringen
|
|
Button {
|
|
value = nil
|
|
} label: {
|
|
Text("Überspringen")
|
|
.font(.subheadline)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
.buttonStyle(.plain)
|
|
.padding(.top, 8)
|
|
}
|
|
.padding(.bottom, 32)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
@Previewable @State var val: Int? = nil
|
|
RatingQuestionView(
|
|
question: RatingQuestion.all[0],
|
|
index: 0,
|
|
total: 9,
|
|
value: $val
|
|
)
|
|
}
|