🏗️ Reorganize files

This commit is contained in:
Victor Bodinaud
2024-11-14 10:07:56 +01:00
parent d962262874
commit 6e711be25d
128 changed files with 1614 additions and 69 deletions

View File

@@ -269,11 +269,13 @@
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = AlloVoisinsSwiftUI/Info.plist;
INFOPLIST_KEY_NSCameraUsageDescription = "Nous utilisons la caméra afin de compléter la photo de profil";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UIUserInterfaceStyle = Light;
IPHONEOS_DEPLOYMENT_TARGET = 16.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -299,11 +301,13 @@
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = AlloVoisinsSwiftUI/Info.plist;
INFOPLIST_KEY_NSCameraUsageDescription = "Nous utilisons la caméra afin de compléter la photo de profil";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UIUserInterfaceStyle = Light;
IPHONEOS_DEPLOYMENT_TARGET = 16.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",

View File

@@ -12,7 +12,7 @@ struct AlloVoisinsSwiftUIApp: App {
var body: some Scene {
WindowGroup {
NavigationStack {
ResiliationNavigationScreen(viewModel: ResiliationViewModel(resiliationType: .apProWithTrial))
MarketingSupportSelectionView()
}
}
}

View File

@@ -13,16 +13,18 @@ struct SQButton: View {
var textColor: Color = .black
var textSize: CGFloat = 16
var font: SQTextFont = .demiBold
var icon: SQIcon?
var isFull: Bool = true
@Binding var isLoading: Bool
let action: () -> Void
init(_ title: String, color: Color = .sqNeutral(10), textColor: Color = .black, textSize: CGFloat = 16, font: SQTextFont = .demiBold, isFull: Bool = true, isLoading: Binding<Bool> = .constant(false), action: @escaping () -> Void) {
init(_ title: String, color: Color = .sqNeutral(10), textColor: Color = .black, textSize: CGFloat = 16, font: SQTextFont = .demiBold, icon: SQIcon? = nil, isFull: Bool = true, isLoading: Binding<Bool> = .constant(false), action: @escaping () -> Void) {
self.title = title
self.color = color
self.textColor = textColor
self.textSize = textSize
self.font = font
self.icon = icon
self.isFull = isFull
self._isLoading = isLoading
self.action = action
@@ -45,17 +47,23 @@ struct SQButton: View {
.stroke(color, lineWidth: 1)
)
} else {
SQText(title, size: textSize, font: font, textColor: textColor)
.padding(.horizontal, 30)
.padding(.vertical, 12)
.frame(height: 40, alignment: .center)
.background(isFull ? color : .clear)
.cornerRadius(100)
.overlay(
RoundedRectangle(cornerRadius: 100)
.inset(by: 0.5)
.stroke(color, lineWidth: 1)
)
HStack {
if (icon != nil) {
icon
}
SQText(title, size: textSize, font: font, textColor: textColor)
}
.padding(.horizontal, 30)
.padding(.vertical, 12)
.frame(height: 40, alignment: .center)
.background(isFull ? color : .clear)
.cornerRadius(100)
.overlay(
RoundedRectangle(cornerRadius: 100)
.inset(by: 0.5)
.stroke(color, lineWidth: 1)
)
}
})
.buttonStyle(PlainButtonStyle())
@@ -89,5 +97,7 @@ enum SQButtonStyle {
.sqStyle()
SQButton("C'est parti !", color: .sqOrange(50), textColor: .white) {}
.sqStyle()
SQButton("Imprimer", color: .sqNeutral(100), textColor: .white, icon: SQIcon(.print, color: .white)) {}
.sqStyle()
}
}

View File

@@ -0,0 +1,60 @@
//
// SQChips.swift
// AlloVoisinsSwiftUI
//
// Created by Victor on 24/10/2024.
//
import SwiftUI
struct SQChipOption: Identifiable, Equatable {
var id: Int
let label: String
let bgColor: Color
}
struct SQChips: View {
var options: [SQChipOption]
@Binding var selectedOption: SQChipOption
var body: some View {
HStack {
ForEach(options) { option in
SQChip(option: option, selectedOption: $selectedOption)
.onTapGesture {
self.selectedOption = option
}
}
}
}
}
fileprivate struct SQChip: View {
var option: SQChipOption
@Binding var selectedOption: SQChipOption
private var isSelected: Bool {
selectedOption.id == option.id
}
var body: some View {
SQText(option.label, size: 14, font: .demiBold, textColor: isSelected ? .white : Color.sqNeutral(100))
.padding(.horizontal, 12)
.padding(.vertical, 8)
.background(isSelected ? option.bgColor : Color.clear)
.cornerRadius(8, corners: .allCorners)
.overlay(
RoundedRectangle(cornerRadius: 8)
.stroke(
isSelected && option.bgColor != .white ? option.bgColor : Color.sqNeutral(100), lineWidth: 1
)
)
}
}
#Preview {
SQChips(options: [
SQChipOption(id: 0, label: "Toutes", bgColor: .sqNeutral(100)),
SQChipOption(id: 1, label: "Non lues", bgColor: .sqSemanticBlue),
SQChipOption(id: 2, label: "Archivées", bgColor: .sqSemanticCritical)
], selectedOption: .constant(SQChipOption(id: 1, label: "Non lues", bgColor: .sqSemanticBlue)))
}

View File

@@ -0,0 +1,150 @@
//
// SQColorPicker.swift
// AlloVoisinsSwiftUI
//
// Created by Victor on 13/11/2024.
//
import SwiftUI
struct SQColorPicker: View {
@Binding var selectedColor: Color
@State private var showColorPicker = false
@State private var isCustomColor = false
let colors: [Color] = [
.sqNeutral(),
.sqGreen(50),
.sqPink(),
.sqBlue(50),
.sqOrange(50),
.sqGrape(80),
.sqForest(80)
]
var title: String
var subtitle: String?
init(_ title: String, selectedColor: Binding<Color>, subtitle: String? = nil) {
self.title = title
self.subtitle = subtitle
self._selectedColor = selectedColor
let initialColor = selectedColor.wrappedValue
_isCustomColor = State(initialValue: !colors.contains(initialColor))
}
var body: some View {
VStack(alignment: .leading) {
VStack(alignment: .leading) {
SQText(title, size: 18, font: .bold)
SQText(subtitle ?? "")
}
HStack {
ForEach(colors, id: \.self) { color in
colorDot(color)
Spacer()
}
SQImage("multicolor", height: 32)
.overlay {
if isCustomColor {
ZStack {
Circle()
.foregroundColor(selectedColor)
.frame(width: 20, height: 20)
Circle()
.inset(by: -2)
.strokeBorder(Color.white, lineWidth: 3)
.frame(width: 20, height: 20)
}
}
}
.onTapGesture {
isCustomColor = true
showColorPicker.toggle()
}
}
}
.sheet(isPresented: $showColorPicker) {
UIColorPickerViewController_SwiftUI(selectedColor: $selectedColor)
.background(Color.white)
.sqNavigationBar(title: "Choisir une couleur personnalisée")
SQButton("Valider", color: .sqNeutral(100), textColor: .white) {
showColorPicker.toggle()
}
}
}
private func colorDot(_ color: Color) -> some View {
HStack(alignment: .center, spacing: 8) {}
.padding(4)
.frame(width: 32, height: 32, alignment: .center)
.background(color)
.cornerRadius(32)
.overlay(
ZStack {
if selectedColor == color && !isCustomColor {
Circle()
.foregroundColor(color)
.frame(width: 20, height: 20)
Circle()
.inset(by: -2)
.strokeBorder(Color.white, lineWidth: 3)
.frame(width: 20, height: 20)
}
}
)
.onTapGesture {
selectedColor = color
isCustomColor = false
}
}
}
struct UIColorPickerViewController_SwiftUI: UIViewControllerRepresentable {
@Binding var selectedColor: Color
func makeUIViewController(context: Context) -> UIColorPickerViewController {
let picker = UIColorPickerViewController()
picker.delegate = context.coordinator
picker.selectedColor = UIColor(selectedColor)
return picker
}
func updateUIViewController(_ uiViewController: UIColorPickerViewController, context: Context) {
uiViewController.selectedColor = UIColor(selectedColor)
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UIColorPickerViewControllerDelegate {
var parent: UIColorPickerViewController_SwiftUI
init(_ parent: UIColorPickerViewController_SwiftUI) {
self.parent = parent
}
func colorPickerViewControllerDidSelectColor(_ viewController: UIColorPickerViewController) {
parent.selectedColor = Color(viewController.selectedColor)
}
func colorPickerViewControllerDidFinish(_ viewController: UIColorPickerViewController) {
parent.selectedColor = Color(viewController.selectedColor)
}
}
}
// Preview
struct ColorPickerPreview: View {
@State private var selectedColor: Color = .sqNeutral()
var body: some View {
SQColorPicker("Couleur principale", selectedColor: $selectedColor, subtitle: "Choisissez la couleur qui sera appliquée sur vos cartes de visite.")
.padding()
}
}
#Preview {
ColorPickerPreview()
}

View File

@@ -0,0 +1,38 @@
//
// SQFooter.swift
// AlloVoisinsSwiftUI
//
// Created by Victor on 13/11/2024.
//
import SwiftUI
struct SQFooter<Content: View>: View {
let content: Content
init(@ViewBuilder content: () -> Content) {
self.content = content()
}
var body: some View {
HStack {
content
}
.padding()
.frame(maxWidth: .infinity, alignment: .center)
.background(Color.white)
.cornerRadius(8)
.shadow(color: Color(red: 0.09, green: 0.14, blue: 0.2).opacity(0.1), radius: 8, x: 0, y: -4)
}
}
#Preview {
SQFooter {
SQButton("test") {
}
SQButton("test") {
}
}
}

View File

@@ -0,0 +1,29 @@
//
// SQImage.swift
// AlloVoisinsSwiftUI
//
// Created by Victor on 13/11/2024.
//
import SwiftUI
struct SQImage: View {
var imageName: String
var height: CGFloat
init(_ imageName: String, height: CGFloat) {
self.imageName = imageName
self.height = height
}
var body: some View {
Image(imageName)
.resizable()
.scaledToFit()
.frame(height: height)
}
}
#Preview {
SQImage("flyers", height: 100)
}

View File

@@ -0,0 +1,87 @@
//
// SQNavigationBar.swift
// AlloVoisinsSwiftUI
//
// Created by Victor on 14/10/2024.
//
import SwiftUI
struct SQNavigationBar: ViewModifier {
@Environment(\.dismiss) private var dismiss
let title: String
let style: SQNavigationBarStyle
let showBackButton: Bool
let backAction: (() -> Void)?
func body(content: Content) -> some View {
content
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .principal) {
SQText(title, size: 18, font: .bold)
.foregroundColor(style.foregroundColor)
}
if backAction != nil {
ToolbarItem(placement: .navigationBarLeading) {
if showBackButton {
Button(action: {
backAction?()
}) {
style.closeIcon
}
}
}
} else {
if showBackButton {
ToolbarItem(placement: .navigationBarLeading) {
Button(action: {
dismiss()
}) {
style.closeIcon
}
}
}
}
}
.toolbarBackground(style.backgroundColor, for: .navigationBar)
.toolbarBackground(.visible, for: .navigationBar)
.navigationBarBackButtonHidden()
}
}
extension View {
func sqNavigationBar(title: String, style: SQNavigationBarStyle = .white, showBackButton: Bool = true, backAction: (() -> Void)? = nil) -> some View {
modifier(SQNavigationBar(title: title, style: style, showBackButton: showBackButton, backAction: backAction))
}
}
enum SQNavigationBarStyle {
case white
case booster
case boosterFreeTrialResiliation
var backgroundColor: Color {
switch self {
case .white: return .white
case .booster: return .sqRoyal(60)
case .boosterFreeTrialResiliation: return .sqRoyal(60)
}
}
var foregroundColor: Color {
switch self {
case .white: return .sqNeutral(100)
case .booster: return .white
case .boosterFreeTrialResiliation: return .white
}
}
var closeIcon: SQIcon {
switch self {
case .white: return SQIcon(.chevron_left, size: .l, color: foregroundColor)
case .booster: return SQIcon(.xmark, size: .l, color: foregroundColor)
case .boosterFreeTrialResiliation: return SQIcon(.chevron_left, size: .l, color: foregroundColor)
}
}
}

View File

@@ -0,0 +1,118 @@
//
// SQPicker.swift
// AlloVoisinsSwiftUI
//
// Created by Victor on 18/10/2024.
//
import SwiftUI
struct PickerOption: Identifiable, Hashable {
let id = UUID()
let text: String
}
struct SQPicker: View {
@Binding var selection: PickerOption
let options: [PickerOption]
@State private var isPresenting = false
var body: some View {
GeometryReader { geometry in
ZStack {
// Contenu visuel
HStack {
SQText(selection.text)
Spacer()
SQIcon(.chevron_down)
}
.padding()
.frame(width: geometry.size.width)
.overlay(
RoundedRectangle(cornerRadius: 8)
.inset(by: 0.5)
.stroke(Color.sqNeutral(30), lineWidth: 1)
)
// Menu invisible mais cliquable
MenuPicker(selection: $selection, options: options, isPresenting: $isPresenting)
.frame(width: geometry.size.width)
}
}
.frame(maxWidth: .infinity)
.fixedSize(horizontal: false, vertical: true)
.tint(.sqNeutral(100))
}
func triggerPickerMenu() {
isPresenting = true
}
}
struct MenuPicker: UIViewRepresentable {
@Binding var selection: PickerOption
let options: [PickerOption]
@Binding var isPresenting: Bool
func makeUIView(context: Context) -> UIButton {
let button = UIButton(type: .custom)
button.setTitle("", for: .normal)
updateMenu(button)
return button
}
func updateUIView(_ uiView: UIButton, context: Context) {
updateMenu(uiView)
if isPresenting {
uiView.sendActions(for: .menuActionTriggered)
isPresenting = false
}
}
private func updateMenu(_ button: UIButton) {
let menu = UIMenu(title: "", children: options.map { option in
UIAction(title: option.text, state: option == selection ? .on : .off) { _ in
self.selection = option
}
}.reversed())
button.showsMenuAsPrimaryAction = true
button.menu = menu
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject {
var parent: MenuPicker
init(_ parent: MenuPicker) {
self.parent = parent
}
}
}
struct SQPickerPreview: View {
@State private var selectedDuration: PickerOption
let durationOptions: [PickerOption]
init() {
let options = [
PickerOption(text: "1 mois"),
PickerOption(text: "2 mois"),
PickerOption(text: "3 mois")
]
_selectedDuration = State(initialValue: options[0])
durationOptions = options
}
var body: some View {
SQPicker(selection: $selectedDuration, options: durationOptions)
.padding()
}
}
#Preview {
SQPickerPreview()
}

View File

@@ -34,6 +34,9 @@ struct SQRadio: View {
Group {
if orientation == .horizontal {
HorizontalRadioButtons(options: options, selectedIndex: $selectedIndex)
.background(Color.blue)
.frame(maxWidth: .infinity)
} else {
VStack(alignment: .leading, spacing: 16) {
radioButtons
@@ -63,7 +66,8 @@ private struct HorizontalRadioButtons: View {
var body: some View {
GeometryReader { geometry in
HStack(alignment: .top, spacing: 16) {
HStack(alignment: .top, spacing: 0) {
Spacer()
ForEach(Array(options.enumerated()), id: \.offset) { index, option in
RadioButton(
title: option,
@@ -73,10 +77,12 @@ private struct HorizontalRadioButtons: View {
),
orientation: .horizontal
)
.frame(width: (geometry.size.width - CGFloat(options.count - 1) * 16) / CGFloat(options.count))
.frame(width: geometry.size.width / CGFloat(options.count))
}
Spacer()
}
}
.frame(height: 80)
}
}
@@ -125,6 +131,6 @@ private struct RadioButton: View {
#Preview {
VStack(spacing: 32) {
SQRadio(title: "Vertical", options: ["Option 1", "Option 2", "Option 3"], selectedIndex: .constant(0))
SQRadio(title: "Horizontal", orientation: .horizontal, options: ["Option 1", "Option 2", "Option 3"], selectedIndex: .constant(2))
SQRadio(title: "Horizontal", orientation: .horizontal, options: ["1\n Non Jamais", "2", "3", "4", "5\nOui"], selectedIndex: .constant(2))
}
}

View File

@@ -44,6 +44,14 @@ extension Color {
return Color("GRAPE_\(variant)")
}
static func sqPink(_ variant: Int = 60) -> Color {
return Color("PINK_\(variant)")
}
static func sqForest(_ variant: Int = 60) -> Color {
return Color("FOREST_\(variant)")
}
static let sqSemanticRed = Color("SEMANTIC_RED")
static let sqSemanticOrange = Color("SEMANTIC_CRITICAL")
static let sqSemanticGreen = Color("SEMANTIC_GREEN")
@@ -52,4 +60,33 @@ extension Color {
static let sqSemanticWarning = Color("SEMANTIC_WARNING")
static let sqSemanticPositive = Color("SEMANTIC_GREEN")
static let sqSemanticNegative = Color("SEMANTIC_RED")
var isLight: Bool {
let components = UIColor(self).cgColor.components ?? [0, 0, 0, 0]
if components.count == 2 {
let gray = components[0]
let luminance = 0.299 * gray + 0.587 * gray + 0.114 * gray
return luminance > 0.5
}
if components.count >= 3 {
let red = components[0]
let green = components[1]
let blue = components[2]
let r = red * 255
let g = green * 255
let b = blue * 255
let luminance = 0.299 * r + 0.587 * g + 0.114 * b
return luminance > 128
}
return false
}
var appropriateTextColor: Color {
isLight ? .black : .white
}
}

View File

@@ -0,0 +1,51 @@
//
// CardColorSelectionView.swift
// AlloVoisinsSwiftUI
//
// Created by Victor on 12/11/2024.
//
import SwiftUI
struct CardColorSelectionView: View {
@State var goToNext: Bool = false
@State private var applyToAll: Bool = true
@State private var selectedColor: Color = .sqNeutral()
var selectedTemplate: CardTemplate
var body: some View {
VStack {
VStack(alignment: .leading, spacing: 32) {
VStack(alignment: .leading, spacing: 16) {
SQColorPicker("Couleur principale", selectedColor: $selectedColor, subtitle: "Choisissez la couleur qui sera appliquée sur vos cartes de visite.")
SQText("Aperçu", size: 18, font: .bold)
selectedTemplate.imageColorTemplate(isLight: selectedColor.isLight)
.background(selectedColor)
.frame(maxWidth: .infinity)
}
VStack(alignment: .leading) {
Toggle(isOn: $applyToAll) {
SQText("Appliquer cette couleur sur mes prospectus, mes devis et mes factures")
}
.tint(Color.sqNeutral(100))
SQText("Nous vous recommandons dappliquer la même couleur sur tous vos documents.", size: 12)
}
Spacer()
}
.padding()
SQFooter {
SQButton("Continuer", color: .sqNeutral(100), textColor: .white) {
self.goToNext.toggle()
}
}
}
.sqNavigationBar(title: "Choix de la couleur ")
.navigationDestination(isPresented: $goToNext) {
CardFormView()
}
}
}
#Preview {
CardColorSelectionView(selectedTemplate: CardTemplate.template4)
}

View File

@@ -0,0 +1,164 @@
//
// CardFormView.swift
// AlloVoisinsSwiftUI
//
// Created by Victor on 12/11/2024.
//
import PhotosUI
import SwiftUI
struct CardFormView: View {
@State var title: String = ""
@State var subtitle: String = ""
@State var job: String = ""
@State var showRating: Bool = true
@State var phoneNumber: String = ""
@State var address: String = ""
@State var selectedPicture: PhotosPickerItem?
@State private var image: Image?
@State private var showImagePicker = false
@State private var showPhotoPicker = false
@State private var showActionSheet = false
@State private var useCamera = false
var body: some View {
VStack {
ScrollView {
VStack {
VStack(alignment: .leading, spacing: 16) {
VStack(alignment: .leading) {
SQText("Informations", size: 24, font: .bold)
SQText("Les modifications apportées sur votre carte de visite ne seront pas reportées sur votre profil.")
}
VStack(alignment: .leading) {
SQText("Image", font: .demiBold)
ZStack(alignment: .bottomTrailing) {
Button(action: { showActionSheet = true }) {
if let image = image {
image
.resizable()
.scaledToFill()
.frame(width: 160, height: 160)
.clipShape(Circle())
} else {
SQIcon(.camera, customSize: 40)
.frame(width: 160, height: 160)
.background(Color.sqNeutral(10))
.clipShape(Circle())
}
}
if image != nil {
SQIcon(.pen_to_square, size: .m)
.padding(8)
.background(Circle().fill(Color.sqNeutral(15)))
.shadow(color: .sqNeutral(100).opacity(0.1), radius: 4, x: 0, y: 2)
.offset(x: -8, y: -8)
}
}
.frame(maxWidth: .infinity)
}
SQTextField("Titre", placeholder: "Ex : Pro Solutions", errorText: "", text: $title)
SQTextField("Sous-titre", placeholder: "Ex : Martin Dupont", errorText: "", text: $subtitle, isOptional: true)
SQTextField("Métier", placeholder: "Ex : Dépannage électroménager", errorText: "", text: $job, isOptional: true)
VStack(alignment: .leading, spacing: 0) {
Toggle(isOn: $showRating) {
SQText("Afficher ma note AlloVoisins", font: .demiBold)
}
.tint(Color.sqNeutral(100))
HStack(spacing: 4) {
SQIcon(.star, type: .solid, color: .sqGold(50))
SQText("4,7/5 sur", size: 12, font: .demiBold)
SQImage("logo", height: 14)
}
}
.padding(.horizontal, 2)
}
.padding()
Rectangle()
.frame(height: 16)
.foregroundColor(Color.sqNeutral(10))
VStack(alignment: .leading, spacing: 16) {
SQTextField("Numéro de téléphone", placeholder: "Ex : 06 12 34 56 78", errorText: "", text: $phoneNumber)
SQTextField("Adresse complète", placeholder: "Ex : 1 rue de la gare, 67000 Strasbourg", errorText: "", text: $address)
}
.padding()
}
}
SQFooter {
SQButton("Aperçu", color: .sqNeutral(100), textColor: .sqNeutral(100), icon: SQIcon(.eye, color: .sqNeutral(100)), isFull: false) {}
.sqStyle()
SQButton("Imprimer", color: .sqNeutral(100), textColor: .white, icon: SQIcon(.print, color: .white)) {}
.sqStyle()
}
}
.confirmationDialog("Choisir une image", isPresented: $showActionSheet, actions: {
Button("Appareil photo") {
useCamera = true
showImagePicker = true
}
Button("Galerie") {
useCamera = false
showPhotoPicker = true
}
Button("Annuler", role: .cancel) {}
})
.photosPicker(isPresented: $showPhotoPicker, selection: $selectedPicture)
.sheet(isPresented: $showImagePicker) {
if useCamera {
ImagePicker(image: $image, sourceType: .camera)
}
}
.task(id: selectedPicture) {
if let data = try? await selectedPicture?.loadTransferable(type: Image.self) {
image = data
}
}
.sqNavigationBar(title: "Ma carte de visite")
}
}
struct ImagePicker: UIViewControllerRepresentable {
@Binding var image: Image?
let sourceType: UIImagePickerController.SourceType
func makeUIViewController(context: Context) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.sourceType = sourceType
picker.delegate = context.coordinator
return picker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
let parent: ImagePicker
init(_ parent: ImagePicker) {
self.parent = parent
}
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any])
{
if let uiImage = info[.originalImage] as? UIImage {
parent.image = Image(uiImage: uiImage)
}
picker.dismiss(animated: true)
}
}
}
#Preview {
CardFormView()
}

View File

@@ -0,0 +1,30 @@
//
// CardPrintView.swift
// AlloVoisinsSwiftUI
//
// Created by Victor on 13/11/2024.
//
import SwiftUI
struct CardPrintView: View {
var body: some View {
VStack(alignment: .leading, spacing: 16) {
SQText("Génération dune planche au format A4 comprenant 8 cartes de visite.", font: .demiBold)
Image("visit_card_print_template")
.resizable()
.scaledToFit()
.frame(height: 100)
.frame(maxWidth: .infinity)
SQText("Nous vous recommandons : \n • Une impression haute qualité, en couleur \n • Lutilisation dun papier dau moins200 g/m2 ")
SQButton("Imprimer", color: .sqNeutral(100), textColor: .white) {}
.frame(maxWidth: .infinity)
Spacer()
}
.padding()
}
}
#Preview {
CardPrintView()
}

View File

@@ -0,0 +1,87 @@
//
// CardTemplateSelectionView.swift
// AlloVoisinsSwiftUI
//
// Created by Victor on 12/11/2024.
//
import SwiftUI
enum CardTemplate: String, CaseIterable {
case template1
case template2
case template3
case template4
var image: SQImage {
switch self {
case .template1:
SQImage("visit_card_template_1", height: 222)
case .template2:
SQImage("visit_card_template_2", height: 222)
case .template3:
SQImage("visit_card_template_3", height: 222)
case .template4:
SQImage("visit_card_template_4", height: 222)
}
}
func imageColorTemplate(isLight: Bool) -> SQImage {
switch self {
case .template1:
return SQImage("visit_card_color_template_1", height: 222)
case .template2:
return SQImage("visit_card_color_template_2", height: 222)
case .template3:
return SQImage("visit_card_color_template_3", height: 222)
case .template4:
if isLight {
return SQImage("visit_card_color_template_4_black", height: 222)
} else {
return SQImage("visit_card_color_template_4_white", height: 222)
}
}
}
}
struct CardTemplateSelectionView: View {
@State var goToNext: Bool = false
@State var selectedTemplate: CardTemplate?
var body: some View {
VStack {
ScrollView {
LazyVStack(spacing: 16) {
ForEach(CardTemplate.allCases, id: \.self) { template in
template.image
.overlay(
Rectangle()
.inset(by: selectedTemplate == template ? 1 : 0.5)
.stroke(selectedTemplate == template ? Color.sqNeutral(100) : Color.sqNeutral(20), lineWidth: selectedTemplate == template ? 2 : 1)
)
.opacity(selectedTemplate != nil ? selectedTemplate == template ? 1 : 0.5 : 1)
.onTapGesture {
selectedTemplate = template
}
}
}
}
SQFooter {
SQButton("Continuer", color: .sqNeutral(100), textColor: .white) {
if selectedTemplate != nil {
goToNext.toggle()
}
}
.disabled(selectedTemplate == nil)
}
}
.sqNavigationBar(title: "Choix du modèle")
.navigationDestination(isPresented: $goToNext) {
CardColorSelectionView(selectedTemplate: selectedTemplate ?? CardTemplate.template1)
}
}
}
#Preview {
CardTemplateSelectionView()
}

View File

@@ -0,0 +1,69 @@
//
// MarketingSupportSelectionView.swift
// AlloVoisinsSwiftUI
//
// Created by Victor on 12/11/2024.
//
import SwiftUI
struct MarketingSupportSelectionView: View {
@State var goToNext: Bool = false
var body: some View {
ScrollView {
Spacer()
.frame(height: 16)
// Visit cards
VStack(alignment: .leading, spacing: 0) {
SQImage("visit_card_new", height: 200)
.cornerRadius(8, corners: [.topLeft, .topRight])
SQText("Mes cartes de visite", size: 18, font: .bold)
.padding(16)
}
.overlay(
RoundedRectangle(cornerRadius: 8)
.inset(by: 0.5)
.stroke(Color.sqNeutral(20), lineWidth: 1)
)
.onTapGesture {
self.goToNext.toggle()
}
// Flyers
VStack(alignment: .leading, spacing: 0) {
SQImage("flyers", height: 200)
.cornerRadius(8, corners: [.topLeft, .topRight])
SQText("Mes prospetus", size: 18, font: .bold)
.padding(16)
}
.overlay(
RoundedRectangle(cornerRadius: 8)
.inset(by: 0.5)
.stroke(Color.sqNeutral(20), lineWidth: 1)
)
// Quotes
VStack(alignment: .leading, spacing: 0) {
SQImage("quotes", height: 200)
.cornerRadius(8, corners: [.topLeft, .topRight])
SQText("Mes prospetus", size: 18, font: .bold)
.padding(16)
}
.overlay(
RoundedRectangle(cornerRadius: 8)
.inset(by: 0.5)
.stroke(Color.sqNeutral(20), lineWidth: 1)
)
}
.scrollIndicators(.hidden)
.sqNavigationBar(title: "Mes supports de communication")
.navigationDestination(isPresented: $goToNext) {
CardTemplateSelectionView()
}
}
}
#Preview {
MarketingSupportSelectionView()
}

View File

@@ -0,0 +1,58 @@
//
// MessageCenterChipFiltersCellView.swift
// AlloVoisinsSwiftUI
//
// Created by Victor on 24/10/2024.
//
import SwiftUI
enum MessageCenterFilter: CaseIterable, Equatable {
case all
case unread
case archived
var chipOption: SQChipOption {
switch self {
case .all:
return SQChipOption(id: 0, label: "Toutes", bgColor: Color.sqNeutral(100))
case .unread:
return SQChipOption(id: 1, label: "Non lues", bgColor: Color.sqSemanticBlue)
case .archived:
return SQChipOption(id: 2, label: "Archivées", bgColor: Color.sqSemanticCritical)
}
}
}
struct MessageCenterChipFiltersCellView: View {
@State var selectedOption: SQChipOption
let options: [SQChipOption]
let onFilterSelected: (MessageCenterFilter) -> Void
init(onFilterSelected: @escaping (MessageCenterFilter) -> Void) {
self.options = [
MessageCenterFilter.all.chipOption,
MessageCenterFilter.unread.chipOption,
MessageCenterFilter.archived.chipOption
]
self._selectedOption = State(initialValue: MessageCenterFilter.all.chipOption)
self.onFilterSelected = onFilterSelected
}
var body: some View {
VStack {
SQChips(options: options, selectedOption: $selectedOption)
.onChange(of: selectedOption) { newValue in
if let filter = MessageCenterFilter.allCases.first(where: { $0.chipOption.id == newValue.id }) {
onFilterSelected(filter)
}
}
.padding()
}
}
}
#Preview {
MessageCenterChipFiltersCellView { _ in
}
}

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xFC",
"green" : "0xFF",
"red" : "0xEB"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x40",
"green" : "0x4D",
"red" : "0x00"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xF7",
"green" : "0xFD",
"red" : "0xD6"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xF1",
"green" : "0xFB",
"red" : "0xC1"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xE4",
"green" : "0xF4",
"red" : "0x96"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xD3",
"green" : "0xE8",
"red" : "0x6C"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xBE",
"green" : "0xD6",
"red" : "0x43"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xA3",
"green" : "0xBE",
"red" : "0x1E"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x83",
"green" : "0x9E",
"red" : "0x01"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x63",
"green" : "0x77",
"red" : "0x00"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xF1",
"green" : "0xEB",
"red" : "0xFF"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x16",
"green" : "0x00",
"red" : "0x4D"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.894",
"green" : "0.851",
"red" : "1.000"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xD6",
"green" : "0xC6",
"red" : "0xFF"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xB6",
"green" : "0x99",
"red" : "0xFF"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x95",
"green" : "0x6B",
"red" : "0xFF"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.455",
"green" : "0.235",
"red" : "1.000"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x4F",
"green" : "0x16",
"red" : "0xDC"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.200",
"green" : "0.000",
"red" : "0.698"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x25",
"green" : "0x00",
"red" : "0x80"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "image (1).png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Logo.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Some files were not shown because too many files have changed in this diff Show More