import SwiftUI struct RequestDetailSheet: View { let request: ApprovalRequest @ObservedObject var model: AppViewModel @Environment(\.dismiss) private var dismiss var body: some View { NavigationStack { AppScrollScreen( compactLayout: true, bottomPadding: AppLayout.compactBottomDockPadding ) { RequestDetailHero(request: request) AppSectionCard(title: "Summary", compactLayout: true) { AppKeyValue(label: "Source", value: request.source) AppKeyValue(label: "Requested", value: request.createdAt.formatted(date: .abbreviated, time: .shortened)) AppKeyValue(label: "Risk", value: request.risk.summary) AppKeyValue(label: "Type", value: request.kind.title) } AppSectionCard(title: "Proof details", compactLayout: true) { if request.scopes.isEmpty { Text("No explicit proof details were provided by the mock backend.") .foregroundStyle(.secondary) } else { Text(request.scopes.joined(separator: "\n")) .font(.body.monospaced()) .foregroundStyle(.secondary) } } AppSectionCard(title: "Guidance", compactLayout: true) { Text(request.trustDetail) .foregroundStyle(.secondary) Text(request.risk.guidance) .font(.headline) } if request.status == .pending { AppSectionCard(title: "Actions", compactLayout: true) { VStack(spacing: 12) { Button { Task { await model.approve(request) dismiss() } } label: { if model.activeRequestID == request.id { ProgressView() } else { Label("Verify identity", systemImage: "checkmark.circle.fill") .frame(maxWidth: .infinity) } } .buttonStyle(.borderedProminent) .disabled(model.activeRequestID == request.id) Button(role: .destructive) { Task { await model.reject(request) dismiss() } } label: { Label("Decline", systemImage: "xmark.circle.fill") .frame(maxWidth: .infinity) } .buttonStyle(.bordered) .disabled(model.activeRequestID == request.id) } } } } .navigationTitle("Review Proof") .inlineNavigationTitleOnIOS() .toolbar { ToolbarItem(placement: .cancellationAction) { Button("Close") { dismiss() } } } } } } private struct RequestDetailHero: View { let request: ApprovalRequest private var accent: Color { switch request.status { case .approved: .green case .rejected: .red case .pending: request.risk == .routine ? dashboardAccent : .orange } } var body: some View { AppPanel(compactLayout: true, radius: AppLayout.largeCardRadius) { AppBadge(title: request.kind.title, tone: accent) Text(request.title) .font(.system(size: 30, weight: .bold, design: .rounded)) .lineLimit(3) Text(request.subtitle) .foregroundStyle(.secondary) HStack(spacing: 8) { AppStatusTag(title: request.status.title, tone: accent) AppStatusTag(title: request.risk.title, tone: request.risk == .routine ? dashboardAccent : .orange) } } } } struct OneTimePasscodeSheet: View { let session: AuthSession @Environment(\.dismiss) private var dismiss @Environment(\.horizontalSizeClass) private var horizontalSizeClass var body: some View { NavigationStack { TimelineView(.periodic(from: .now, by: 1)) { context in let code = OneTimePasscodeGenerator.code(for: session.pairingCode, at: context.date) let secondsRemaining = OneTimePasscodeGenerator.renewalCountdown(at: context.date) AppScrollScreen(compactLayout: compactLayout) { AppPanel(compactLayout: compactLayout, radius: AppLayout.largeCardRadius) { AppBadge(title: "One-time passcode", tone: dashboardGold) Text("OTP") .font(.system(size: compactLayout ? 32 : 40, weight: .bold, design: .rounded)) Text("Share this code only with the site or device asking you to prove that it is really you.") .font(.subheadline) .foregroundStyle(.secondary) Text(code) .font(.system(size: compactLayout ? 42 : 54, weight: .bold, design: .rounded).monospacedDigit()) .tracking(compactLayout ? 4 : 6) .frame(maxWidth: .infinity) .padding(.vertical, compactLayout ? 16 : 20) .background(AppTheme.mutedFill, in: RoundedRectangle(cornerRadius: 24, style: .continuous)) .overlay( RoundedRectangle(cornerRadius: 24, style: .continuous) .stroke(AppTheme.border, lineWidth: 1) ) HStack(spacing: 8) { AppStatusTag(title: "Renews in \(secondsRemaining)s", tone: dashboardGold) AppStatusTag(title: session.originHost, tone: dashboardAccent) } Divider() AppKeyValue(label: "Client", value: session.deviceName) AppKeyValue(label: "Linked", value: session.pairedAt.formatted(date: .abbreviated, time: .shortened)) } } } .navigationTitle("OTP") .inlineNavigationTitleOnIOS() .toolbar { ToolbarItem(placement: .cancellationAction) { Button("Close") { dismiss() } } } } } private var compactLayout: Bool { #if os(iOS) horizontalSizeClass == .compact #else false #endif } }