BusinessCard/BusinessCard/Views/CustomizeCardView.swift

173 lines
6.8 KiB
Swift

import SwiftUI
import Bedrock
import SwiftData
struct CustomizeCardView: View {
@Environment(AppState.self) private var appState
@State private var showingEditCard = false
@State private var showingDeleteConfirmation = false
var body: some View {
NavigationStack {
ScrollView {
VStack(spacing: Design.Spacing.large) {
Text("Customize your card")
.font(.title2)
.bold()
.foregroundStyle(Color.Text.primary)
if let card = appState.cardStore.selectedCard {
BusinessCardView(card: card)
CardActionsView(
onEdit: { showingEditCard = true },
onDelete: { showingDeleteConfirmation = true },
canDelete: appState.cardStore.cards.count > 1
)
CardStylePickerView(selectedTheme: card.theme) { theme in
appState.cardStore.setSelectedTheme(theme)
}
CardLayoutPickerView(selectedLayout: card.layoutStyle) { layout in
appState.cardStore.setSelectedLayout(layout)
}
} else {
EmptyStateView(
title: String.localized("No card selected"),
message: String.localized("Select a card to start customizing.")
)
}
}
.padding(.horizontal, Design.Spacing.large)
.padding(.vertical, Design.Spacing.xLarge)
}
.background(Color.AppBackground.base)
.navigationTitle(String.localized("Edit your card"))
.sheet(isPresented: $showingEditCard) {
if let card = appState.cardStore.selectedCard {
CardEditorView(card: card) { updatedCard in
appState.cardStore.updateCard(updatedCard)
}
}
}
.alert(String.localized("Delete Card"), isPresented: $showingDeleteConfirmation) {
Button(String.localized("Cancel"), role: .cancel) { }
Button(String.localized("Delete"), role: .destructive) {
if let card = appState.cardStore.selectedCard {
appState.cardStore.deleteCard(card)
}
}
} message: {
Text("Are you sure you want to delete this card? This action cannot be undone.")
}
}
}
}
private struct CardActionsView: View {
let onEdit: () -> Void
let onDelete: () -> Void
let canDelete: Bool
var body: some View {
HStack(spacing: Design.Spacing.medium) {
Button(String.localized("Edit Details"), systemImage: "pencil", action: onEdit)
.buttonStyle(.bordered)
.tint(Color.Accent.ink)
.accessibilityHint(String.localized("Edit card name, email, and other details"))
if canDelete {
Button(String.localized("Delete"), systemImage: "trash", role: .destructive, action: onDelete)
.buttonStyle(.bordered)
.accessibilityHint(String.localized("Permanently delete this card"))
}
}
.padding(.vertical, Design.Spacing.small)
}
}
private struct CardStylePickerView: View {
let selectedTheme: CardTheme
let onSelect: (CardTheme) -> Void
var body: some View {
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
Text("Card style")
.font(.headline)
.bold()
.foregroundStyle(Color.Text.primary)
LazyVGrid(columns: gridColumns, spacing: Design.Spacing.small) {
ForEach(CardTheme.all) { theme in
Button(action: { onSelect(theme) }) {
VStack(spacing: Design.Spacing.xSmall) {
RoundedRectangle(cornerRadius: Design.CornerRadius.medium)
.fill(theme.primaryColor)
.frame(height: Design.CardSize.avatarSize)
.overlay(
RoundedRectangle(cornerRadius: Design.CornerRadius.medium)
.stroke(
selectedTheme.id == theme.id ? Color.Accent.red : Color.Text.inverted.opacity(Design.Opacity.medium),
lineWidth: Design.LineWidth.medium
)
)
Text(theme.localizedName)
.font(.caption)
.foregroundStyle(Color.Text.secondary)
}
.frame(maxWidth: .infinity)
}
.buttonStyle(.plain)
.accessibilityLabel(String.localized("Card style"))
.accessibilityValue(theme.localizedName)
}
}
}
.padding(Design.Spacing.large)
.background(Color.AppBackground.elevated)
.clipShape(.rect(cornerRadius: Design.CornerRadius.large))
}
private var gridColumns: [GridItem] {
Array(repeating: GridItem(.flexible(), spacing: Design.Spacing.small), count: 3)
}
}
private struct CardLayoutPickerView: View {
let selectedLayout: CardLayoutStyle
let onSelect: (CardLayoutStyle) -> Void
var body: some View {
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
Text("Images & layout")
.font(.headline)
.bold()
.foregroundStyle(Color.Text.primary)
Picker(String.localized("Layout"), selection: Binding(
get: { selectedLayout },
set: { onSelect($0) }
)) {
ForEach(CardLayoutStyle.allCases) { layout in
Text(layout.displayName)
.tag(layout)
}
}
.pickerStyle(.segmented)
Text("Change image layout")
.font(.subheadline)
.foregroundStyle(Color.Text.secondary)
}
.padding(Design.Spacing.large)
.background(Color.AppBackground.elevated)
.clipShape(.rect(cornerRadius: Design.CornerRadius.large))
}
}
#Preview {
CustomizeCardView()
.environment(AppState(modelContext: try! ModelContainer(for: BusinessCard.self, Contact.self).mainContext))
}