import ManaCore import SwiftData import SwiftUI /// Deck-Detail mit Aktionen: Lernen, Karte hinzufügen, Bearbeiten, Löschen. /// Wird per Tap auf eine Deck-Row aus der DeckListView geöffnet. struct DeckDetailView: View { let deckId: String @Environment(AuthClient.self) private var auth @Environment(\.modelContext) private var context @Environment(\.dismiss) private var dismiss @Query private var decks: [CachedDeck] @State private var showEditor = false @State private var showCardEditor = false @State private var showDeleteConfirm = false @State private var navigateToStudy = false @State private var deleteError: String? init(deckId: String) { self.deckId = deckId _decks = Query(filter: #Predicate { $0.id == deckId }) } var body: some View { ZStack { CardsTheme.background.ignoresSafeArea() if let deck = decks.first { content(deck: deck) } else { ContentUnavailableView("Deck nicht gefunden", systemImage: "questionmark.folder") .foregroundStyle(CardsTheme.mutedForeground) } } .navigationTitle(decks.first?.name ?? "") #if os(iOS) .navigationBarTitleDisplayMode(.inline) #endif .sheet(isPresented: $showEditor) { NavigationStack { DeckEditorView( mode: .edit(deckId: deckId), existing: decks.first ) { _ in Task { await refreshAfterEdit() } } } } .sheet(isPresented: $showCardEditor) { NavigationStack { CardEditorView(deckId: deckId) { _ in Task { await refreshAfterEdit() } } } } .confirmationDialog( "Deck löschen?", isPresented: $showDeleteConfirm, titleVisibility: .visible ) { Button("Löschen", role: .destructive) { Task { await delete() } } Button("Abbrechen", role: .cancel) {} } message: { Text("Alle Karten und Reviews dieses Decks werden ebenfalls gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.") } .navigationDestination(isPresented: $navigateToStudy) { if let deck = decks.first { StudySessionView(deckId: deck.id, deckName: deck.name) } } } private func content(deck: CachedDeck) -> some View { VStack(alignment: .leading, spacing: 16) { header(deck: deck) actions(deck: deck) if let deleteError { Text(deleteError) .font(.footnote) .foregroundStyle(CardsTheme.error) .padding(.horizontal, 16) } Spacer() } .padding(.vertical, 16) } private func header(deck: CachedDeck) -> some View { VStack(alignment: .leading, spacing: 8) { HStack { Text(deck.name) .font(.title.bold()) .foregroundStyle(CardsTheme.foreground) if deck.isFromMarketplace { Image(systemName: "globe") .foregroundStyle(CardsTheme.mutedForeground) } } if let description = deck.deckDescription, !description.isEmpty { Text(description) .foregroundStyle(CardsTheme.mutedForeground) } HStack(spacing: 16) { Label("\(deck.cardCount) Karten", systemImage: "rectangle.stack") if deck.dueCount > 0 { Label("\(deck.dueCount) fällig", systemImage: "clock.badge.exclamationmark") .foregroundStyle(CardsTheme.primary) } if let category = deck.category { Text(category.label) .foregroundStyle(CardsTheme.mutedForeground) } } .font(.footnote) } .padding(.horizontal, 16) } private func actions(deck: CachedDeck) -> some View { VStack(spacing: 12) { Button { navigateToStudy = true } label: { Label("Karten lernen", systemImage: "play.fill") .frame(maxWidth: .infinity) .padding(.vertical, 12) .background(CardsTheme.primary, in: RoundedRectangle(cornerRadius: 10)) .foregroundStyle(CardsTheme.primaryForeground) } .buttonStyle(.plain) .disabled(deck.dueCount == 0) Button { showCardEditor = true } label: { Label("Karte hinzufügen", systemImage: "plus.rectangle.on.rectangle") .frame(maxWidth: .infinity) .padding(.vertical, 12) .background(CardsTheme.surface, in: RoundedRectangle(cornerRadius: 10)) .foregroundStyle(CardsTheme.foreground) .overlay( RoundedRectangle(cornerRadius: 10) .stroke(CardsTheme.border, lineWidth: 1) ) } .buttonStyle(.plain) HStack(spacing: 12) { Button { showEditor = true } label: { Label("Bearbeiten", systemImage: "pencil") .frame(maxWidth: .infinity) .padding(.vertical, 10) .background(CardsTheme.surface, in: RoundedRectangle(cornerRadius: 10)) .foregroundStyle(CardsTheme.foreground) .overlay( RoundedRectangle(cornerRadius: 10) .stroke(CardsTheme.border, lineWidth: 1) ) } .buttonStyle(.plain) Button { showDeleteConfirm = true } label: { Label("Löschen", systemImage: "trash") .frame(maxWidth: .infinity) .padding(.vertical, 10) .background(CardsTheme.error.opacity(0.1), in: RoundedRectangle(cornerRadius: 10)) .foregroundStyle(CardsTheme.error) } .buttonStyle(.plain) } } .padding(.horizontal, 16) } private func refreshAfterEdit() async { let store = DeckListStore(auth: auth, context: context) await store.refresh() } private func delete() async { deleteError = nil let api = CardsAPI(auth: auth) do { try await api.deleteDeck(id: deckId) // Cache nachziehen if let deck = decks.first { context.delete(deck) try? context.save() } dismiss() } catch { deleteError = (error as? LocalizedError)?.errorDescription ?? String(describing: error) } } }