173 lines
6.8 KiB
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))
|
|
}
|