💄 Add a lot of components
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// SQCheckbox.swift
|
||||
// AlloVoisinsSwiftUI
|
||||
//
|
||||
// Created by Victor on 25/11/2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SQCheckbox: View {
|
||||
var text: String
|
||||
@Binding var isChecked: Bool
|
||||
var errorText: String?
|
||||
@Binding var isInError: Bool
|
||||
|
||||
init(_ text: String, isChecked: Binding<Bool>, errorText: String? = nil, isInError: Binding<Bool> = .constant(false)) {
|
||||
self.text = text
|
||||
self._isChecked = isChecked
|
||||
self.errorText = errorText
|
||||
self._isInError = isInError
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
HStack(alignment: .top) {
|
||||
if isChecked {
|
||||
SQImage("checked_neutral", height: 20)
|
||||
} else if isInError {
|
||||
SQImage("checkbox_unchecked_error", height: 20)
|
||||
} else {
|
||||
SQImage("checkbox_unchecked", height: 20)
|
||||
}
|
||||
SQText(text)
|
||||
}
|
||||
.onTapGesture {
|
||||
isChecked.toggle()
|
||||
|
||||
if isInError && isChecked {
|
||||
isInError = false
|
||||
}
|
||||
}
|
||||
|
||||
HStack {
|
||||
SQIcon(.circle_exclamation, customSize: 13, type: .solid, color: .sqSemanticRed)
|
||||
SQText("Cette condition est obligatoire.", size: 12, font: .demiBold, textColor: .sqSemanticRed)
|
||||
}
|
||||
.isHidden(hidden: !isInError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
SQCheckbox("Je comprends que j’engage ma responsabilité sur l’exhaustivité et l’authenticité des informations renseignées ci-dessus.", isChecked: .constant(false))
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
//
|
||||
// SQImagePicker.swift
|
||||
// AlloVoisinsSwiftUI
|
||||
//
|
||||
// Created by Victor on 27/11/2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SQImagePicker: View {
|
||||
let nbOfImages: Int = 4
|
||||
var images: [Image] = []
|
||||
|
||||
private let itemSpacing: CGFloat = 8
|
||||
private var columns: [GridItem] {
|
||||
[
|
||||
GridItem(.fixed(160), spacing: itemSpacing),
|
||||
GridItem(.fixed(160), spacing: itemSpacing)
|
||||
]
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
SQText("Images", font: .demiBold)
|
||||
|
||||
LazyVGrid(columns: columns, alignment: .leading) {
|
||||
ForEach(0 ..< nbOfImages) { _ in
|
||||
SQImagePickerView()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SQImagePickerView: View {
|
||||
var image: Image?
|
||||
@State var showSelection: Bool = false
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
if let image = image {
|
||||
image
|
||||
.frame(width: 160, height: 160)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 8)
|
||||
.fill(Color.sqNeutral(10)))
|
||||
} else {
|
||||
HStack {
|
||||
SQIcon(.camera, size: .xl)
|
||||
}
|
||||
.frame(width: 160, height: 160)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 8)
|
||||
.fill(Color.sqNeutral(10)))
|
||||
}
|
||||
}
|
||||
.onTapGesture {
|
||||
showSelection = true
|
||||
}
|
||||
.sheet(isPresented: $showSelection) {
|
||||
SQImagePickerSelectorSheet()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SQImagePickerSelectorSheet: View {
|
||||
@Environment(\.dismiss) var dismiss
|
||||
|
||||
var categoriesImages: [CategoriesImages] = [
|
||||
CategoriesImages(name: "Entretien - Réparation autres véhicules", pictures: [
|
||||
"square.and.arrow.up.fill",
|
||||
"paperplane.circle.fill",
|
||||
"tray.fill"
|
||||
]),
|
||||
CategoriesImages(name: "Plomberie - Installation sanitaire", pictures: [
|
||||
"xmark.bin.circle.fill"
|
||||
]),
|
||||
CategoriesImages(name: "Carrelage", pictures: [
|
||||
"document.on.document",
|
||||
"richtext.page.fill.he"
|
||||
])
|
||||
]
|
||||
|
||||
private let itemSpacing: CGFloat = 8
|
||||
private var columns: [GridItem] {
|
||||
[
|
||||
GridItem(.fixed(160), spacing: itemSpacing),
|
||||
GridItem(.fixed(160), spacing: itemSpacing)
|
||||
]
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
HStack {
|
||||
Button {
|
||||
dismiss()
|
||||
} label: {
|
||||
SQIcon(.xmark, size: .l)
|
||||
}
|
||||
|
||||
SQText("Sélectionner une photo", size: 19, font: .bold)
|
||||
.frame(maxWidth: .infinity)
|
||||
Spacer()
|
||||
}
|
||||
.padding()
|
||||
ScrollView {
|
||||
ForEach(categoriesImages, id: \.name) { category in
|
||||
VStack {
|
||||
VStack(alignment: .leading) {
|
||||
SQText(category.name)
|
||||
LazyVGrid(columns: columns, alignment: .leading) {
|
||||
ForEach(category.pictures, id: \.self) { picture in
|
||||
Image(systemName: picture)
|
||||
.frame(width: 160, height: 120)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 8)
|
||||
.fill(Color.sqNeutral(10)))
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
|
||||
Rectangle()
|
||||
.frame(height: 16)
|
||||
.foregroundColor(Color.sqNeutral(10))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
SQImagePicker()
|
||||
.padding()
|
||||
}
|
||||
|
||||
struct CategoriesImages {
|
||||
var name: String
|
||||
var pictures: [String]
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
//
|
||||
// SQTextEditor.swift
|
||||
// AlloVoisinsSwiftUI
|
||||
//
|
||||
// Created by Victor on 27/11/2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SQTextEditor: View {
|
||||
var label: String
|
||||
var placeholder: String
|
||||
var type: SQTextFieldType = .text
|
||||
var errorText: String
|
||||
var icon: SQIcon?
|
||||
var isDisabled: Bool = false
|
||||
var isOptional: Bool = false
|
||||
var tooltipText: String?
|
||||
@Binding var isInError: Bool
|
||||
var minCharacters: Int?
|
||||
var maxCharacters: Int?
|
||||
@Binding var text: String
|
||||
@FocusState private var isFocused: Bool
|
||||
@State private var showTooltip = false
|
||||
let infoAction: (() -> Void)?
|
||||
let iconAction: (() -> Void)?
|
||||
|
||||
private var accentColor: Color = .sqNeutral(80)
|
||||
|
||||
init(_ label: String,
|
||||
placeholder: String,
|
||||
type: SQTextFieldType = .text,
|
||||
errorText: String = "",
|
||||
text: Binding<String>,
|
||||
icon: SQIcon? = nil,
|
||||
isDisabled: Bool = false,
|
||||
isOptional: Bool = false,
|
||||
tooltipText: String? = nil,
|
||||
isInError: Binding<Bool> = .constant(false),
|
||||
minCharacters: Int? = nil,
|
||||
maxCharacters: Int? = nil,
|
||||
infoAction: (() -> Void)? = nil,
|
||||
iconAction: (() -> Void)? = nil)
|
||||
{
|
||||
self.label = label
|
||||
self.placeholder = placeholder
|
||||
self.type = type
|
||||
self.errorText = errorText
|
||||
self._text = text
|
||||
self.icon = icon
|
||||
self.isDisabled = isDisabled
|
||||
self.isOptional = isOptional
|
||||
self.tooltipText = tooltipText
|
||||
self._isInError = isInError
|
||||
self.minCharacters = minCharacters
|
||||
self.maxCharacters = maxCharacters
|
||||
self.infoAction = infoAction
|
||||
self.iconAction = iconAction
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
HStack(spacing: 8) {
|
||||
SQText(label)
|
||||
Spacer()
|
||||
if isOptional {
|
||||
SQText("Optionnel", size: 12, textColor: .sqNeutral(50))
|
||||
}
|
||||
}
|
||||
TextEditor(text: Binding(
|
||||
get: { self.text },
|
||||
set: { self.text = String($0.prefix(self.maxCharacters ?? Int.max)) }
|
||||
))
|
||||
.onChange(of: self.text, perform: { _ in
|
||||
self.isInError = false
|
||||
})
|
||||
.font(.sq(.medium))
|
||||
.foregroundStyle(Color.sqNeutral(100))
|
||||
.tint(accentColor)
|
||||
.frame(height: 108)
|
||||
.foregroundStyle(Color.sqNeutral())
|
||||
.background(isDisabled ? Color.sqNeutral(10) : .clear)
|
||||
.padding(16)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 8)
|
||||
.inset(by: 0.5)
|
||||
.stroke(isInError ? .sqSemanticRed : isFocused ? accentColor : isDisabled ? Color.sqNeutral(20) : Color.sqNeutral(30), lineWidth: 1)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
SQTextEditor("Zone de texte", placeholder: "Ex : Pro Solutions propose une gamme de services complète pour tout type de dépannage électroménager. Bénéficiez de nos 10 ans d’expérience ! Devis gratuit", text: .constant("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."))
|
||||
.padding()
|
||||
}
|
||||
@@ -8,8 +8,8 @@
|
||||
import SwiftUI
|
||||
|
||||
enum SQTextFieldType {
|
||||
case text
|
||||
case phoneNumber
|
||||
case text
|
||||
case phoneNumber
|
||||
}
|
||||
|
||||
struct SQTextField: View {
|
||||
@@ -20,17 +20,33 @@ struct SQTextField: View {
|
||||
var icon: SQIcon?
|
||||
var isDisabled: Bool = false
|
||||
var isOptional: Bool = false
|
||||
var tooltipText: String?
|
||||
@Binding var isInError: Bool
|
||||
var minCharacters: Int?
|
||||
var maxCharacters: Int?
|
||||
@Binding var text: String
|
||||
@FocusState private var isFocused: Bool
|
||||
@State private var showTooltip = false
|
||||
let infoAction: (() -> Void)?
|
||||
let iconAction: (() -> Void)?
|
||||
|
||||
private var accentColor: Color = .sqNeutral(80)
|
||||
|
||||
init(_ label: String, placeholder: String, type: SQTextFieldType = .text, errorText: String = "", text: Binding<String>, icon: SQIcon? = nil, isDisabled: Bool = false, isOptional: Bool = false, isInError: Binding<Bool> = .constant(false), minCharacters: Int? = nil, maxCharacters: Int? = nil, infoAction: (() -> Void)? = nil, iconAction: (() -> Void)? = nil) {
|
||||
init(_ label: String,
|
||||
placeholder: String,
|
||||
type: SQTextFieldType = .text,
|
||||
errorText: String = "",
|
||||
text: Binding<String>,
|
||||
icon: SQIcon? = nil,
|
||||
isDisabled: Bool = false,
|
||||
isOptional: Bool = false,
|
||||
tooltipText: String? = nil,
|
||||
isInError: Binding<Bool> = .constant(false),
|
||||
minCharacters: Int? = nil,
|
||||
maxCharacters: Int? = nil,
|
||||
infoAction: (() -> Void)? = nil,
|
||||
iconAction: (() -> Void)? = nil)
|
||||
{
|
||||
self.label = label
|
||||
self.placeholder = placeholder
|
||||
self.type = type
|
||||
@@ -39,6 +55,7 @@ struct SQTextField: View {
|
||||
self.icon = icon
|
||||
self.isDisabled = isDisabled
|
||||
self.isOptional = isOptional
|
||||
self.tooltipText = tooltipText
|
||||
self._isInError = isInError
|
||||
self.minCharacters = minCharacters
|
||||
self.maxCharacters = maxCharacters
|
||||
@@ -50,7 +67,23 @@ struct SQTextField: View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
HStack(spacing: 8) {
|
||||
SQText(label)
|
||||
if let infoAction = infoAction {
|
||||
if let tooltipText = tooltipText {
|
||||
Button {
|
||||
withAnimation {
|
||||
showTooltip.toggle()
|
||||
}
|
||||
} label: {
|
||||
SQIcon(.circle_info, color: .sqNeutral(80))
|
||||
}
|
||||
.overlay(
|
||||
Group {
|
||||
if showTooltip {
|
||||
SQTooltip(text: tooltipText, isVisible: $showTooltip)
|
||||
.fixedSize()
|
||||
}
|
||||
}
|
||||
)
|
||||
} else if let infoAction = infoAction {
|
||||
Button {
|
||||
infoAction()
|
||||
} label: {
|
||||
@@ -125,6 +158,18 @@ struct SQTextField: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.overlay(
|
||||
Group {
|
||||
if showTooltip {
|
||||
Color.black.opacity(0.001)
|
||||
.onTapGesture {
|
||||
withAnimation {
|
||||
showTooltip = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private var characterCountText: String {
|
||||
@@ -164,6 +209,13 @@ struct SQTextField: View {
|
||||
SQTextField("Label name", placeholder: "Placeholder", text: .constant(""), isDisabled: true)
|
||||
SQTextField("Label name", placeholder: "Placeholder", errorText: "Champ invalide", text: .constant(""), isInError: .constant(true))
|
||||
SQTextField("Téléphone", placeholder: "01 23 45 67 89", type: .phoneNumber, text: .constant(""))
|
||||
SQTextField(
|
||||
"Numéro de RCS",
|
||||
placeholder: "RCS VILLE B 123456789",
|
||||
text: .constant(""),
|
||||
isOptional: true,
|
||||
tooltipText: "Vous pouvez trouver votre numéro RCS sur l'extrait Kbis de votre entreprise ou votre extrait K, si vous êtes un entrepreneur individuel.\n\nLe numéro RCS se compose tout d'abord de la mention RCS, puis du numéro d'immatriculation de la ville dans laquelle a été créée la société, d'une lettre et enfin du numéro SIREN à 9 chiffres."
|
||||
)
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// SQTooltip.swift
|
||||
// AlloVoisinsSwiftUI
|
||||
//
|
||||
// Created by Victor on 25/11/2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SQTooltip: View {
|
||||
let text: String
|
||||
@Binding var isVisible: Bool
|
||||
@State private var tooltipHeight: CGFloat = 0
|
||||
|
||||
private struct Constants {
|
||||
static let cornerRadius: CGFloat = 8
|
||||
static let padding: CGFloat = 16
|
||||
static let maxWidth: CGFloat = 300
|
||||
static let spacing: CGFloat = 4
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
SQText(text, textColor: .white)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.padding(Constants.padding)
|
||||
.background(
|
||||
GeometryReader { geo in
|
||||
Color.sqNeutral(100)
|
||||
.clipShape(RoundedRectangle(cornerRadius: Constants.cornerRadius))
|
||||
.onAppear {
|
||||
tooltipHeight = geo.size.height
|
||||
}
|
||||
}
|
||||
)
|
||||
.frame(maxWidth: Constants.maxWidth)
|
||||
}
|
||||
.position(x: 210, y: 0)
|
||||
.offset(y: -(tooltipHeight/2 + Constants.spacing))
|
||||
.opacity(isVisible ? 1 : 0)
|
||||
.animation(.easeInOut(duration: 0.2), value: isVisible)
|
||||
}
|
||||
}
|
||||
@@ -39,3 +39,28 @@ extension Font {
|
||||
.custom(font.rawValue, size: size)
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
func isHidden(hidden: Bool = false, remove: Bool = false) -> some View {
|
||||
modifier(
|
||||
IsHidden(
|
||||
hidden: hidden,
|
||||
remove: remove
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
struct IsHidden: ViewModifier {
|
||||
var hidden = false
|
||||
var remove = false
|
||||
func body(content: Content) -> some View {
|
||||
if hidden {
|
||||
if remove {
|
||||
} else {
|
||||
content.hidden()
|
||||
}
|
||||
} else {
|
||||
content
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// FlyerColorSelectionView.swift
|
||||
// AlloVoisinsSwiftUI
|
||||
//
|
||||
// Created by Victor on 25/11/2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct FlyerColorSelectionView: View {
|
||||
@State var goToNext: Bool = false
|
||||
@State private var applyToAll: Bool = true
|
||||
@State private var selectedColor: Color = .sqNeutral()
|
||||
var selectedTemplate: FlyerTemplate
|
||||
|
||||
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 prospectus.")
|
||||
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 cartes de visite, mes devis et mes factures")
|
||||
}
|
||||
.tint(Color.sqNeutral(100))
|
||||
SQText("Nous vous recommandons d’appliquer 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 {
|
||||
FlyerColorSelectionView(selectedTemplate: .template4)
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
//
|
||||
// FlyerFormView.swift
|
||||
// AlloVoisinsSwiftUI
|
||||
//
|
||||
// Created by Victor on 25/11/2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import _PhotosUI_SwiftUI
|
||||
|
||||
struct FlyerFormView: 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
|
||||
@State private var authentConfirm: Bool = false
|
||||
@State private var confirmIsInError: Bool = 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 prospectus 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) {
|
||||
SQTextEditor("Zone de text", placeholder: "Ex : Pro Solutions propose une gamme de services complète pour tout type de dépannage électroménager. Bénéficiez de nos 10 ans d’expérience ! Devis gratuit", text: .constant(""))
|
||||
|
||||
SQTextField("Prestation 1", placeholder: "Ex : Réparation lave-vaisselle", errorText: "", text: $job, isOptional: true)
|
||||
SQTextField("Prestation 2", placeholder: "Ex : Réparation machine à laver", errorText: "", text: $job, isOptional: true)
|
||||
SQTextField("Prestation 3", placeholder: "Ex : Réparation four", errorText: "", text: $job, isOptional: true)
|
||||
SQTextField("Prestation 4", placeholder: "Ex : Réparation outillage", errorText: "", text: $job, isOptional: true)
|
||||
SQTextField("Prestation 5", placeholder: "Ex : Dépannage électroménager", errorText: "", text: $job, isOptional: true)
|
||||
}
|
||||
.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()
|
||||
|
||||
Rectangle()
|
||||
.frame(height: 16)
|
||||
.foregroundColor(Color.sqNeutral(10))
|
||||
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
SQText("Mentions légales obligatoires", size: 18, font: .bold)
|
||||
|
||||
SQTextField("Dénomination sociale", placeholder: "Ex : Pro solutions", errorText: "", text: $job, isOptional: true)
|
||||
SQTextField("Adresse du siège social", placeholder: "Ex : 16 rue de la Redoute, 67500 Haguenau", errorText: "", text: $job, isOptional: true)
|
||||
VStack(spacing: 0) {
|
||||
SQTextField("Numéro SIRET", placeholder: "Ex : 12345678901234", errorText: "", text: $job, isOptional: true)
|
||||
SQText("Le numéro SIRET se compose de 14 chiffres : les 9 chiffres du SIREN + 5 chiffres propres à chaque établissement (NIC).", size: 12, textColor: .sqNeutral(50))
|
||||
}
|
||||
SQTextField("Numéro de RCS", placeholder: "Ex : RCS STRASBOURG B 123456789", errorText: "", text: $job, isOptional: true)
|
||||
SQTextField("Statut juridique", placeholder: "Ex : SARL", errorText: "", text: $job, isOptional: true)
|
||||
SQTextField("Montant du capital social (€)", placeholder: "Ex : 1 000,00", errorText: "", text: $job, isOptional: true)
|
||||
SQTextField("Autre champ relatif à votre activité", placeholder: "Ex : Pour votre santé, mangez 5 fruits et légumes par jour", errorText: "", text: $job, isOptional: true)
|
||||
SQCheckbox("Je comprends que j’engage ma responsabilité sur l’exhaustivité et l’authenticité des informations renseignées ci-dessus.", isChecked: $authentConfirm, isInError: $confirmIsInError)
|
||||
}
|
||||
.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)) {
|
||||
if authentConfirm == false {
|
||||
self.confirmIsInError = true
|
||||
}
|
||||
}
|
||||
.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: "Mon prospectus")
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
FlyerFormView()
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// FlyerPrintView.swift
|
||||
// AlloVoisinsSwiftUI
|
||||
//
|
||||
// Created by Victor on 25/11/2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct FlyerPrintView: View {
|
||||
var body: some View {
|
||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
FlyerPrintView()
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
//
|
||||
// FlyerTemplateSelectionView.swift
|
||||
// AlloVoisinsSwiftUI
|
||||
//
|
||||
// Created by Victor on 25/11/2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
enum FlyerTemplate: String, CaseIterable {
|
||||
case template1
|
||||
case template2
|
||||
case template3
|
||||
case template4
|
||||
|
||||
var image: SQImage {
|
||||
switch self {
|
||||
case .template1:
|
||||
SQImage("flyer_template_1", height: 284)
|
||||
case .template2:
|
||||
SQImage("flyer_template_2", height: 284)
|
||||
case .template3:
|
||||
SQImage("flyer_template_3", height: 284)
|
||||
case .template4:
|
||||
SQImage("flyer_template_4", height: 284)
|
||||
}
|
||||
}
|
||||
|
||||
var nbOfPictures: Int {
|
||||
switch self {
|
||||
case .template1:
|
||||
1
|
||||
case .template2:
|
||||
3
|
||||
case .template3:
|
||||
4
|
||||
case .template4:
|
||||
2
|
||||
}
|
||||
}
|
||||
|
||||
func imageColorTemplate(isLight: Bool) -> SQImage {
|
||||
switch self {
|
||||
case .template1:
|
||||
if isLight {
|
||||
return SQImage("flyer_color_template_1_black", height: 284)
|
||||
} else {
|
||||
return SQImage("flyer_color_template_1_white", height: 284)
|
||||
}
|
||||
case .template2:
|
||||
return SQImage("flyer_color_template_2", height: 284)
|
||||
case .template3:
|
||||
if isLight {
|
||||
return SQImage("flyer_color_template_3_black", height: 284)
|
||||
} else {
|
||||
return SQImage("flyer_color_template_3_white", height: 284)
|
||||
}
|
||||
case .template4:
|
||||
if isLight {
|
||||
return SQImage("flyer_color_template_4_black", height: 284)
|
||||
} else {
|
||||
return SQImage("flyer_color_template_4_white", height: 284)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FlyerTemplateSelectionView: View {
|
||||
@State var goToNext: Bool = false
|
||||
@State var selectedTemplate: FlyerTemplate?
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
ScrollView {
|
||||
LazyVStack(spacing: 16) {
|
||||
ForEach(FlyerTemplate.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) {
|
||||
// FlyerColorSelectionView(selectedTemplate: selectedTemplate ?? FlyerTemplate.template1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
FlyerTemplateSelectionView()
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "check.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "flyer_color_template_1_black.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "flyer_color_template_1.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "flyer_color_template_2.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "flyer_color_template_3_black.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "flyer_color_template_3.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "flyer_color_template_4_black.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "flyer_color_template_4.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "prospectus 1.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/flyer/flyer_template_1.imageset/prospectus 1.png
vendored
Normal file
BIN
AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/flyer/flyer_template_1.imageset/prospectus 1.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 204 KiB |
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "prospectus 2.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/flyer/flyer_template_2.imageset/prospectus 2.png
vendored
Normal file
BIN
AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/flyer/flyer_template_2.imageset/prospectus 2.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 287 KiB |
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "prospectus 3.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/flyer/flyer_template_3.imageset/prospectus 3.png
vendored
Normal file
BIN
AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/flyer/flyer_template_3.imageset/prospectus 3.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 375 KiB |
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "prospectus 4.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/flyer/flyer_template_4.imageset/prospectus 4.png
vendored
Normal file
BIN
AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/flyer/flyer_template_4.imageset/prospectus 4.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 241 KiB |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "image (1).png",
|
||||
"filename" : "image.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 242 KiB |
BIN
AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/flyers.imageset/image.png
vendored
Normal file
BIN
AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/flyers.imageset/image.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 450 KiB |
Reference in New Issue
Block a user