From e305b1697a0eaa972b2df1f7621b8f8c0bb37a4c Mon Sep 17 00:00:00 2001 From: Victor Bodinaud Date: Wed, 10 Dec 2025 16:31:08 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8New=20views?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AlloVoisinsSwiftUI.xcodeproj/project.pbxproj | 4 +- .../AlloVoisinsSwiftUIApp.swift | 2 +- .../Core/Managers/BaseNavigationManager.swift | 49 +++ .../Core/Views/MessageNotificationView.swift | 7 +- .../Components/OnlyForPremierView.swift | 6 +- .../Views/Modals/OnlyForPremierModal.swift | 25 +- .../RegulatedProfessionEditProfilModal.swift | 24 +- .../Modals/RegulatedProfessionModal.swift | 7 +- .../Sequoia/Components/NeighborBanner.swift | 3 +- .../Components/SQAddressModifyLine.swift | 7 +- .../Views/Sequoia/Components/SQButton.swift | 183 +++++---- .../Views/Sequoia/Components/SQCheckbox.swift | 5 +- .../Views/Sequoia/Components/SQChips.swift | 5 +- .../Sequoia/Components/SQCircleButton.swift | 4 +- .../Sequoia/Components/SQColorPicker.swift | 4 +- .../Sequoia/Components/SQDatePicker.swift | 143 +++++++ .../Sequoia/Components/SQImagePicker.swift | 7 +- .../Sequoia/Components/SQNavigationBar.swift | 4 +- .../Views/Sequoia/Components/SQPicker.swift | 11 +- .../Components/SQProgressIndicator.swift | 53 ++- .../Views/Sequoia/Components/SQRadio.swift | 12 +- .../Views/Sequoia/Components/SQTabBar.swift | 37 -- .../Views/Sequoia/Components/SQText.swift | 61 --- .../Sequoia/Components/SQText/SQText.swift | 88 ++++ .../SQText/SQTextConfiguration.swift | 30 ++ .../Components/SQText/SQTextModifiers.swift | 78 ++++ .../Sequoia/Components/SQTextEditor.swift | 16 +- .../Sequoia/Components/SQTextField.swift | 222 ---------- .../SQTextFields/SQPasswordField.swift | 112 ++++++ .../SQTextFields/SQPhoneField.swift | 106 +++++ .../Components/SQTextFields/SQTextField.swift | 138 +++++++ .../SQTextFields/SQTextFieldBase.swift | 170 ++++++++ .../SQTextFieldConfiguration.swift | 40 ++ .../SQTextFields/SQTextFieldModifiers.swift | 242 +++++++++++ .../Views/Sequoia/Components/SQToast.swift | 7 +- .../Views/Sequoia/Components/SQTooltip.swift | 3 +- .../Components/SqSearchBar/SQSearchBar.swift | 16 +- .../SqSearchBar/SQSearchBarButton.swift | 4 +- .../Views/Components/ChangeLoginView.swift | 39 ++ .../Components/PasswordRecommendations.swift | 53 +++ .../Account/Views/ConfirmAuthView.swift | 63 +++ .../Account/Views/LoginAndPasswordView.swift | 108 +++++ .../Views/BoosterConfirmationScreen.swift | 11 +- .../Views/BoosterKnowAboutScreen.swift | 30 +- .../BoosterSubscriptionManagementScreen.swift | 6 +- .../BoosterSubscriptionSelectionScreen.swift | 3 +- .../Components/BoosterActiveHeaderView.swift | 7 +- .../Components/BoosterFeaturesView.swift | 16 +- .../Components/BoosterHistoryCellView.swift | 3 +- .../BoosterLockedToPremierView.swift | 3 +- .../Components/BoosterPromotionView.swift | 8 +- .../Components/BoosterSelectionView.swift | 3 +- .../Views/Components/BoosterStatsView.swift | 39 +- .../BoosterSubscriptionCardView.swift | 16 +- .../BoosterSubscriptionOptionsView.swift | 4 +- .../ProfileComplimentListView.swift | 4 +- .../Views/Components/ComplimentPillView.swift | 4 +- .../Views/Components/ComplimentView.swift | 8 +- .../Views/Components/CurrentDebugUser.swift | 13 +- .../Views/Components/DebugActionView.swift | 31 ++ .../Views/ConfigPrestationSearchView.swift | 13 +- .../DebugLand/Views/DebugLandView.swift | 45 ++- .../IdentityValidationNavigationManager.swift | 20 + .../IdentityValidationViewModel.swift | 13 + ...dentityDocumentsValidationLoaderView.swift | 36 ++ ...dentityDocumentsValidationResultView.swift | 74 ++++ .../IdentityInformationsValidationView.swift | 52 +++ .../IdentityValidationNavigationView.swift | 48 +++ .../Identity/Views/LivenessErrorView.swift | 34 ++ .../Identity/Views/LivenessSuccessView.swift | 53 +++ .../Identity/Views/LivenessTutorialView.swift | 54 +++ .../Identity/Views/LivenessView.swift | 31 ++ .../Components/DocumentPreviewView.swift | 21 +- .../Components/IdentityDocumentButton.swift | 4 +- .../Views/Components/KYCDocumentButton.swift | 11 +- .../Components/KYCInformationAlertView.swift | 5 +- .../Views/Cards/CardColorSelectionView.swift | 7 +- .../Marketing/Views/Cards/CardFormView.swift | 37 +- .../Marketing/Views/Cards/CardPrintView.swift | 3 +- .../Flyers/FlyerColorSelectionView.swift | 7 +- .../Views/Flyers/FlyerFormView.swift | 93 +++-- .../Views/MarketingSupportSelectionView.swift | 12 +- .../Messenger/Views/CreateMeetingView.swift | 78 ++++ .../Messenger/Views/MessagingBlurView.swift | 40 ++ .../MoreNeighborsSelectedBadge.swift | 4 +- .../Components/NeighborsSelectionFooter.swift | 7 +- .../Views/Components/NeihborsCard.swift | 18 +- .../Views/CategorySelectorView.swift | 6 +- .../Views/Components/AllCategoriesCell.swift | 3 +- .../Views/Components/PrestationCell.swift | 4 +- .../PrestationHistorySearchCell.swift | 4 +- .../Views/PrestationSearchView.swift | 8 +- .../Profile/Views/AssurancesView.swift | 59 +++ .../Components/ProfileAnswerToCell.swift | 66 +++ .../Components/ProfileDisponibilityCell.swift | 53 +++ .../Components/ProfileInformationsCell.swift | 112 ++++++ .../Components/ProfilePresentationCell.swift | 37 ++ .../Components/ProfileStatisticsCell.swift | 44 ++ .../Profile/Views/WelcomeProModal.swift | 58 +++ .../Views/ReportConfirmationView.swift | 10 +- .../Views/ReportFinalizationView.swift | 11 +- .../PromotionalScreenViewModel.swift | 2 +- .../ViewModels/ResiliationViewModel.swift | 2 +- .../Views/AlloVoisinReputationScreen.swift | 33 +- .../Components/TrialWarningCellView.swift | 6 +- .../Views/ContinueAsParticularScreen.swift | 9 +- .../Views/GenericResiliationScreen.swift | 18 +- .../Views/GetMoreRatingsScreen.swift | 17 +- .../Resiliation/Views/MoreTimeScreen.swift | 14 +- .../Views/OnlyProRequestsScreen.swift | 20 +- .../Views/PersonalizedSupportScreen.swift | 13 +- .../Views/ProfileCompletionScreen.swift | 36 +- .../Views/ResiliationCheckStepsScreen.swift | 6 +- .../Views/ResiliationConfirmationScreen.swift | 4 +- .../Views/ResiliationReasonScreen.swift | 5 +- .../Views/ResizePerimeterScreen.swift | 13 +- .../Views/SoftwarePresentationScreen.swift | 27 +- .../Views/StatusChangeScreen.swift | 5 +- .../Resiliation/Views/WebinaireScreen.swift | 18 +- .../Components/NewPerimeterCellView.swift | 11 +- .../Views/Components/Pricing/FAQRow.swift | 7 +- .../Views/Components/Pricing/FAQSection.swift | 3 +- .../Views/Components/Pricing/FeatureRow.swift | 10 +- .../Components/Pricing/FeatureSection.swift | 8 +- .../Pricing/PricingSubscribeFooter.swift | 15 +- .../Pricing/PricingSubscribeHeader.swift | 4 +- .../Pricing/ReassuranceIndicatorSection.swift | 3 +- .../Components/Pricing/SwitchAndInfo.swift | 9 +- .../Visio/Views/VisioPermissionsModal.swift | 9 +- .../Images/messaging.imageset/Contents.json | 12 + ...ils conversation - Utilisateur suspendu.png | Bin 0 -> 150127 bytes .../Contents.json | 12 + .../Visuel accessoires.png | Bin 0 -> 2410 bytes .../portrait_angle.imageset/Contents.json | 12 + .../Visuel bon angle.png | Bin 0 -> 2701 bytes .../Contents.json | 12 + .../Visuel reflet.png | Bin 0 -> 2605 bytes .../logo-pro-color.imageset/Contents.json | 12 + .../allovoisinspro.pdf | 379 ++++++++++++++++++ 139 files changed, 3743 insertions(+), 725 deletions(-) create mode 100644 AlloVoisinsSwiftUI/Core/Managers/BaseNavigationManager.swift create mode 100644 AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQDatePicker.swift delete mode 100644 AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTabBar.swift delete mode 100644 AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQText.swift create mode 100644 AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQText/SQText.swift create mode 100644 AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQText/SQTextConfiguration.swift create mode 100644 AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQText/SQTextModifiers.swift delete mode 100644 AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextField.swift create mode 100644 AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextFields/SQPasswordField.swift create mode 100644 AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextFields/SQPhoneField.swift create mode 100644 AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextFields/SQTextField.swift create mode 100644 AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextFields/SQTextFieldBase.swift create mode 100644 AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextFields/SQTextFieldConfiguration.swift create mode 100644 AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextFields/SQTextFieldModifiers.swift create mode 100644 AlloVoisinsSwiftUI/Features/Account/Views/Components/ChangeLoginView.swift create mode 100644 AlloVoisinsSwiftUI/Features/Account/Views/Components/PasswordRecommendations.swift create mode 100644 AlloVoisinsSwiftUI/Features/Account/Views/ConfirmAuthView.swift create mode 100644 AlloVoisinsSwiftUI/Features/Account/Views/LoginAndPasswordView.swift create mode 100644 AlloVoisinsSwiftUI/Features/DebugLand/Views/Components/DebugActionView.swift create mode 100644 AlloVoisinsSwiftUI/Features/Identity/ViewModels/IdentityValidationNavigationManager.swift create mode 100644 AlloVoisinsSwiftUI/Features/Identity/ViewModels/IdentityValidationViewModel.swift create mode 100644 AlloVoisinsSwiftUI/Features/Identity/Views/IdentityDocumentsValidationLoaderView.swift create mode 100644 AlloVoisinsSwiftUI/Features/Identity/Views/IdentityDocumentsValidationResultView.swift create mode 100644 AlloVoisinsSwiftUI/Features/Identity/Views/IdentityInformationsValidationView.swift create mode 100644 AlloVoisinsSwiftUI/Features/Identity/Views/IdentityValidationNavigationView.swift create mode 100644 AlloVoisinsSwiftUI/Features/Identity/Views/LivenessErrorView.swift create mode 100644 AlloVoisinsSwiftUI/Features/Identity/Views/LivenessSuccessView.swift create mode 100644 AlloVoisinsSwiftUI/Features/Identity/Views/LivenessTutorialView.swift create mode 100644 AlloVoisinsSwiftUI/Features/Identity/Views/LivenessView.swift create mode 100644 AlloVoisinsSwiftUI/Features/Messenger/Views/CreateMeetingView.swift create mode 100644 AlloVoisinsSwiftUI/Features/Messenger/Views/MessagingBlurView.swift create mode 100644 AlloVoisinsSwiftUI/Features/Profile/Views/AssurancesView.swift create mode 100644 AlloVoisinsSwiftUI/Features/Profile/Views/Components/ProfileAnswerToCell.swift create mode 100644 AlloVoisinsSwiftUI/Features/Profile/Views/Components/ProfileDisponibilityCell.swift create mode 100644 AlloVoisinsSwiftUI/Features/Profile/Views/Components/ProfileInformationsCell.swift create mode 100644 AlloVoisinsSwiftUI/Features/Profile/Views/Components/ProfilePresentationCell.swift create mode 100644 AlloVoisinsSwiftUI/Features/Profile/Views/Components/ProfileStatisticsCell.swift create mode 100644 AlloVoisinsSwiftUI/Features/Profile/Views/WelcomeProModal.swift create mode 100644 AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/messaging.imageset/Contents.json create mode 100644 AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/messaging.imageset/Détails conversation - Utilisateur suspendu.png create mode 100644 AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/portrait_accessories.imageset/Contents.json create mode 100644 AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/portrait_accessories.imageset/Visuel accessoires.png create mode 100644 AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/portrait_angle.imageset/Contents.json create mode 100644 AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/portrait_angle.imageset/Visuel bon angle.png create mode 100644 AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/portrait_reflection.imageset/Contents.json create mode 100644 AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/portrait_reflection.imageset/Visuel reflet.png create mode 100644 AlloVoisinsSwiftUI/Resources/Assets.xcassets/logo-pro-color.imageset/Contents.json create mode 100644 AlloVoisinsSwiftUI/Resources/Assets.xcassets/logo-pro-color.imageset/allovoisinspro.pdf diff --git a/AlloVoisinsSwiftUI.xcodeproj/project.pbxproj b/AlloVoisinsSwiftUI.xcodeproj/project.pbxproj index 8859b8d..446dbc0 100644 --- a/AlloVoisinsSwiftUI.xcodeproj/project.pbxproj +++ b/AlloVoisinsSwiftUI.xcodeproj/project.pbxproj @@ -199,7 +199,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.6; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -256,7 +256,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.6; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; diff --git a/AlloVoisinsSwiftUI/AlloVoisinsSwiftUIApp.swift b/AlloVoisinsSwiftUI/AlloVoisinsSwiftUIApp.swift index 33e7031..5ea2766 100644 --- a/AlloVoisinsSwiftUI/AlloVoisinsSwiftUIApp.swift +++ b/AlloVoisinsSwiftUI/AlloVoisinsSwiftUIApp.swift @@ -12,7 +12,7 @@ struct AlloVoisinsSwiftUIApp: App { var body: some Scene { WindowGroup { NavigationStack { - EmptyView() + DebugLandView() } } } diff --git a/AlloVoisinsSwiftUI/Core/Managers/BaseNavigationManager.swift b/AlloVoisinsSwiftUI/Core/Managers/BaseNavigationManager.swift new file mode 100644 index 0000000..a6b73f3 --- /dev/null +++ b/AlloVoisinsSwiftUI/Core/Managers/BaseNavigationManager.swift @@ -0,0 +1,49 @@ +// +// BaseNavigationManager.swift +// Allovoisins +// +// Created by Victor on 06/12/2024. +// Copyright © 2024 AlloVoisins. All rights reserved. +// + +import SwiftUI + +protocol NavigationRoute: Hashable {} + +class BaseNavigationManager: ObservableObject { + @Published var navigationPath = NavigationPath() + @Published private var routeStack: [Route] = [] + @Published var shouldDismissToRoot: Bool = false + @Published var shouldDismiss: Bool = false + + var currentRoute: Route? { + routeStack.last + } + + var canGoBack: Bool { + !routeStack.isEmpty && !navigationPath.isEmpty + } + + func navigateTo(_ route: Route) { + navigationPath.append(route) + routeStack.append(route) + } + + func goBack() { + if canGoBack { + navigationPath.removeLast() + routeStack.removeLast() + } else { + shouldDismiss = true + } + } + + func resetNavigation() { + navigationPath = NavigationPath() + routeStack = [] + } + + func dismissToRoot() { + shouldDismissToRoot = true + } +} diff --git a/AlloVoisinsSwiftUI/Core/Views/MessageNotificationView.swift b/AlloVoisinsSwiftUI/Core/Views/MessageNotificationView.swift index 27d45aa..98047cb 100644 --- a/AlloVoisinsSwiftUI/Core/Views/MessageNotificationView.swift +++ b/AlloVoisinsSwiftUI/Core/Views/MessageNotificationView.swift @@ -33,8 +33,11 @@ struct MessageNotificationView: View { AVAvatar(model: AVAvatarModel(avatarString: notification.avatarUrl, onlineStatus: .online(), sizing: .M)) .frame(width: 48, height: 48) VStack(alignment: .leading) { - SQText(notification.displayName, font: .demiBold) - SQText(notification.message, size: 14, textColor: .sqNeutral(70)) + SQText(notification.displayName) + .sqFont(.semiBold) + SQText(notification.message) + .sqSize(14) + .sqColor(.sqNeutral(70)) .lineLimit(1) } Spacer() diff --git a/AlloVoisinsSwiftUI/Core/Views/Modals/Components/OnlyForPremierView.swift b/AlloVoisinsSwiftUI/Core/Views/Modals/Components/OnlyForPremierView.swift index f1944f8..61c5a27 100644 --- a/AlloVoisinsSwiftUI/Core/Views/Modals/Components/OnlyForPremierView.swift +++ b/AlloVoisinsSwiftUI/Core/Views/Modals/Components/OnlyForPremierView.swift @@ -12,13 +12,15 @@ struct OnlyForPremierView: View { var body: some View { VStack(spacing: 8) { VStack(alignment: .leading, spacing: 8) { - SQText("Réservé aux abonnés Premier", size: 18, font: .bold) + SQText("Réservé aux abonnés Premier") + .sqSize(18) + .sqFont(.bold) SQText("Seuls les abonnés Premier peuvent profiter de cette fonctionnalité.") } SQButton( "Découvrir l’abonnement Premier") { } - .colorScheme(.orange) + .sqColorScheme(.orange) } .frame(maxWidth: .infinity) } diff --git a/AlloVoisinsSwiftUI/Core/Views/Modals/OnlyForPremierModal.swift b/AlloVoisinsSwiftUI/Core/Views/Modals/OnlyForPremierModal.swift index 4903c9b..ca22d7b 100644 --- a/AlloVoisinsSwiftUI/Core/Views/Modals/OnlyForPremierModal.swift +++ b/AlloVoisinsSwiftUI/Core/Views/Modals/OnlyForPremierModal.swift @@ -27,26 +27,33 @@ struct OnlyForPremierModal: View { Color.white VStack(spacing: 48) { VStack(spacing: 24) { - SQText("Vous avez déjà répondu à 4 demandes de services ce mois-ci", size: 24, font: .bold) + SQText("Vous avez déjà répondu à 4 demandes de services ce mois-ci") + .sqSize(24) + .sqFont(.bold) .multilineTextAlignment(.center) VStack(alignment: .leading, spacing: 16) { - SQText("Augmentez votre chiffre d’affaires en bénéficiant de tous les avantages inclus dans l’abonnement Premier.", font: .bold) + SQText("Augmentez votre chiffre d’affaires en bénéficiant de tous les avantages inclus dans l’abonnement Premier.") + .sqFont(.bold) VStack(alignment: .leading) { HStack { SQIcon(.check, size: .s, type: .solid, color: .sqOrange(50)) - SQText("Répondez aux demandes en illimité", font: .demiBold) + SQText("Répondez aux demandes en illimité") + .sqFont(.semiBold) } HStack { SQIcon(.check, size: .s, type: .solid, color: .sqOrange(50)) - SQText("Augmentez votre visibilité sur Google", font: .demiBold) + SQText("Augmentez votre visibilité sur Google") + .sqFont(.semiBold) } HStack { SQIcon(.check, size: .s, type: .solid, color: .sqOrange(50)) - SQText("Affichez votre numéro de téléphone sur votre profil", font: .demiBold) + SQText("Affichez votre numéro de téléphone sur votre profil") + .sqFont(.semiBold) } HStack { SQIcon(.check, size: .s, type: .solid, color: .sqOrange(50)) - SQText("Gérez vos devis, factures, encaissements...", font: .demiBold) + SQText("Gérez vos devis, factures, encaissements...") + .sqFont(.semiBold) } } } @@ -55,13 +62,15 @@ struct OnlyForPremierModal: View { Spacer() VStack(spacing: 16) { VStack { - SQText("Essai gratuit de 14 jours", size: 18, font: .bold) + SQText("Essai gratuit de 14 jours") + .sqSize(18) + .sqFont(.bold) SQText("à partir de 29,99 € / mois") } SQButton("Je m'abonne") { } - .colorScheme(.orange) + .sqColorScheme(.orange) } .padding(.horizontal, 16) .padding(.vertical, 24) diff --git a/AlloVoisinsSwiftUI/Core/Views/Modals/RegulatedProfessionEditProfilModal.swift b/AlloVoisinsSwiftUI/Core/Views/Modals/RegulatedProfessionEditProfilModal.swift index 37bb70f..3b59e0e 100644 --- a/AlloVoisinsSwiftUI/Core/Views/Modals/RegulatedProfessionEditProfilModal.swift +++ b/AlloVoisinsSwiftUI/Core/Views/Modals/RegulatedProfessionEditProfilModal.swift @@ -12,34 +12,38 @@ struct RegulatedProfessionEditProfilModal: View { var body: some View { VStack { VStack(spacing: 24) { - SQText("Profession réglementée", size: 32, font: .bold) + SQText("Profession réglementée") + .sqSize(32) + .sqFont(.bold) VStack(alignment: .leading, spacing: 16) { - SQText("Vous ne pouvez pas enregistrer votre profil car des éléments indiquent que vous proposez vos services en déménagement avec véhicule. L’activité de déménageur avec véhicule est juridiquement réglementée et seuls les professionnels agréés peuvent l’exercer.", font: .medium) - SQText("Aussi, vous n’êtes pas autorisé à proposer vos services de déménageur avec véhicule, ni à en faire mention sur votre profil.", font: .medium) + SQText("Vous ne pouvez pas enregistrer votre profil car des éléments indiquent que vous proposez vos services en déménagement avec véhicule. L’activité de déménageur avec véhicule est juridiquement réglementée et seuls les professionnels agréés peuvent l’exercer.") + SQText("Aussi, vous n’êtes pas autorisé à proposer vos services de déménageur avec véhicule, ni à en faire mention sur votre profil.") VStack(alignment: .leading, spacing: 8) { - SQText("Nous vous invitons à corriger les éléments suivants :", size: 16, font: .demiBold) + SQText("Nous vous invitons à corriger les éléments suivants :") + .sqFont(.semiBold) HStack { SQIcon(.xmark, size: .m, type: .solid, color: .red) - SQText("Titre du profil", font: .medium) + SQText("Titre du profil") } HStack { SQIcon(.xmark, size: .m, type: .solid, color: .red) - SQText("Présentation profil", font: .medium) + SQText("Présentation profil") } HStack { SQIcon(.xmark, size: .m, type: .solid, color: .red) - SQText("Photo de couverture", font: .medium) + SQText("Photo de couverture") } HStack { SQIcon(.xmark, size: .m, type: .solid, color: .red) - SQText("Photo de profil", font: .medium) + SQText("Photo de profil") } HStack { SQIcon(.xmark, size: .m, type: .solid, color: .red) - SQText("Photos de réalisation", font: .medium) + SQText("Photos de réalisation") } } - SQText("À défaut, ces éléments seront supprimés de votre profil en septembre.", font: .demiBold) + SQText("À défaut, ces éléments seront supprimés de votre profil en septembre.") + .sqFont(.semiBold) Text("[Retrouvez ici](https://www.codingwithrashid.com) les informations utiles pour devenir déménageur agréé.") // SQText("[Retrouvez ici](https://www.codingwithrashid.com) les informations utiles pour devenir déménageur agréé.", font: .medium) } diff --git a/AlloVoisinsSwiftUI/Core/Views/Modals/RegulatedProfessionModal.swift b/AlloVoisinsSwiftUI/Core/Views/Modals/RegulatedProfessionModal.swift index 7da9032..f96396c 100644 --- a/AlloVoisinsSwiftUI/Core/Views/Modals/RegulatedProfessionModal.swift +++ b/AlloVoisinsSwiftUI/Core/Views/Modals/RegulatedProfessionModal.swift @@ -12,12 +12,15 @@ struct RegulatedProfessionModal: View { var body: some View { VStack() { VStack(spacing: 16) { - SQText("Attention au cadre juridique", size: 24, font: .bold) + SQText("Attention au cadre juridique") + .sqSize(24) + .sqFont(.bold) VStack(alignment: .leading, spacing: 16) { SQText("L’activité de déménagement avec véhicule est juridiquement réglementée et seuls les professionnels agréés peuvent l’exercer.") SQText("Vous pouvez proposer vos services pour participer à un déménagement, mais vous n’êtes pas autorisé à mettre à disposition un véhicule.") SQText("Vous trouverez plus d'informations sur les activités réglementées dans la FAQ.") - SQText("Le non-respect de ce cadre légal pourra entraîner la suspension de votre compte.", font: .demiBold) + SQText("Le non-respect de ce cadre légal pourra entraîner la suspension de votre compte.") + .sqFont(.semiBold) } .padding(.horizontal, 16) } diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/NeighborBanner.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/NeighborBanner.swift index b68b297..7e89def 100644 --- a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/NeighborBanner.swift +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/NeighborBanner.swift @@ -12,7 +12,8 @@ struct NeighborBanner: View { var body: some View { HStack(spacing: 8) { SQIcon(.lightbulb_on, size: .xl, color: .sqGreen(80)) - SQText("Nous vous recommandons de sélectionner 3 offreurs.", size: 16, font: .demiBold) + SQText("Nous vous recommandons de sélectionner 3 offreurs.") + .sqFont(.semiBold) } .foregroundColor(.sqGreen(80)) .padding(16) diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQAddressModifyLine.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQAddressModifyLine.swift index 0a82d72..49816bc 100644 --- a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQAddressModifyLine.swift +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQAddressModifyLine.swift @@ -12,12 +12,15 @@ struct SQAddressModifyLine: View { let attributedText = NSAttributedString("Modifier") HStack { - SQText("à 8 allée Baco, 44000 Nantes", size: 14) + SQText("à 8 allée Baco, 44000 Nantes") + .sqSize(14) Spacer() Button { print("Modifier") } label: { - SQText("Modifier", size: 14, font: .bold) + SQText("Modifier") + .sqSize(14) + .sqFont(.bold) .underline(true, pattern: .solid) } } diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQButton.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQButton.swift index 613bda7..fd08937 100644 --- a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQButton.swift +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQButton.swift @@ -15,10 +15,14 @@ struct SQButton: View { @State private var type: SQButtonType = .solid @State private var colorScheme: SQButtonColorScheme = .neutral @State private var textSize: CGFloat = 16 - @State private var font: SQTextFont = .demiBold + @State private var font: SQTextFont = .semiBold @State private var icon: SQIcon? = nil - @State private var isLoading: Bool = false - @State private var isDisabled: Bool = false + @State private var isPressed: Bool = false + @State private var isLarge: Bool = false + + @Binding var isLoading: Bool + + @Environment(\.isEnabled) private var isEnabled private var textWidth: CGFloat { let font = UIFont.systemFont(ofSize: textSize) @@ -27,8 +31,12 @@ struct SQButton: View { return size.width + (icon != nil ? 24 : 0) } - init(_ title: String, action: @escaping () -> Void) { + init(_ title: String, + isLoading: Binding = .constant(false), + action: @escaping () -> Void) + { self.title = title + self._isLoading = isLoading self.action = action } @@ -37,13 +45,14 @@ struct SQButton: View { type: type, colorScheme: colorScheme, isLoading: isLoading, - isDisabled: isDisabled + isDisabled: !isEnabled, + isPressed: isPressed ) } var body: some View { Button(action: { - if !isLoading && !isDisabled { + if !isLoading, !isEnabled { action() } }, label: { @@ -58,10 +67,14 @@ struct SQButton: View { .foregroundColor(buttonStyle.textColor) } - SQText(title, size: textSize, font: font, textColor: buttonStyle.textColor) + SQText(title) + .sqSize(textSize) + .sqFont(font) + .sqColor(buttonStyle.textColor) } } .frame(minWidth: textWidth) + .frame(maxWidth: isLarge ? .infinity : nil, alignment: .center) .padding(.horizontal, 30) .padding(.vertical, 12) .frame(height: 40, alignment: .center) @@ -73,51 +86,57 @@ struct SQButton: View { .stroke(buttonStyle.borderColor, lineWidth: 1) ) }) - .disabled(isLoading || isDisabled) + .buttonStyle(PressedButtonStyle(isPressed: $isPressed)) + } +} + +struct PressedButtonStyle: ButtonStyle { + @Binding var isPressed: Bool + + func makeBody(configuration: Configuration) -> some View { + configuration.label + .onChange(of: configuration.isPressed) { newValue in + isPressed = newValue + } } } // MARK: - Modifiers + extension SQButton { - func buttonType(_ type: SQButtonType) -> SQButton { + func sqButtonType(_ type: SQButtonType) -> SQButton { var copy = self copy._type = State(initialValue: type) return copy } - func colorScheme(_ scheme: SQButtonColorScheme) -> SQButton { + func sqColorScheme(_ scheme: SQButtonColorScheme) -> SQButton { var copy = self copy._colorScheme = State(initialValue: scheme) return copy } - func textSize(_ size: CGFloat) -> SQButton { + func sqTextSize(_ size: CGFloat) -> SQButton { var copy = self copy._textSize = State(initialValue: size) return copy } - func font(_ font: SQTextFont) -> SQButton { + func sqFont(_ font: SQTextFont) -> SQButton { var copy = self copy._font = State(initialValue: font) return copy } - func icon(_ icon: SQIcon?) -> SQButton { + func sqIcon(_ icon: SQIcon?) -> SQButton { var copy = self copy._icon = State(initialValue: icon) return copy } - func loading(_ isLoading: Bool) -> SQButton { + func sqLarge(_ isLarge: Bool = true) -> SQButton { var copy = self - copy._isLoading = State(initialValue: isLoading) - return copy - } - - func disabled(_ isDisabled: Bool) -> SQButton { - var copy = self - copy._isDisabled = State(initialValue: isDisabled) + copy._isLarge = State(initialValue: isLarge) return copy } } @@ -143,6 +162,7 @@ enum SQButtonColorScheme: String, CaseIterable { case grape case forest case royal + case white var baseColor: Color { switch self { @@ -168,6 +188,8 @@ enum SQButtonColorScheme: String, CaseIterable { return .sqForest(100) case .royal: return .sqRoyal(60) + case .white: + return .white } } @@ -195,6 +217,8 @@ enum SQButtonColorScheme: String, CaseIterable { return .sqForest(50) case .royal: return .sqRoyal(30) + case .white: + return .white } } @@ -222,6 +246,8 @@ enum SQButtonColorScheme: String, CaseIterable { return .sqForest(10) case .royal: return .sqRoyal(10) + case .white: + return .white } } } @@ -231,8 +257,22 @@ struct SQButtonStyle { let colorScheme: SQButtonColorScheme let isLoading: Bool let isDisabled: Bool + let isPressed: Bool var backgroundColor: Color { + if isPressed && !isDisabled && !isLoading { + switch type { + case .solid: + return colorScheme.baseColor.opacity(0.8) + case .line: + return .clear + case .light: + return colorScheme.lightColor + case .glass: + return .clear + } + } + if isDisabled { switch type { case .solid: @@ -273,8 +313,11 @@ struct SQButtonStyle { var borderColor: Color { switch type { - case .solid: + if isPressed && !isDisabled && !isLoading { + return colorScheme.baseColor.opacity(0.8) + } + if isDisabled { return colorScheme.lightColor.opacity(0.5) } @@ -282,7 +325,12 @@ struct SQButtonStyle { if isLoading { return colorScheme.lightColor.opacity(0.8) } + case .line: + if isPressed && !isDisabled && !isLoading { + return colorScheme.baseColor.opacity(0.8) + } + if isDisabled { return colorScheme.mediumColor } @@ -294,6 +342,10 @@ struct SQButtonStyle { return colorScheme.baseColor case .light: + if isPressed && !isDisabled && !isLoading { + return colorScheme.lightColor.opacity(0.8) + } + if isDisabled { return colorScheme.lightColor.opacity(0.5) } @@ -312,6 +364,15 @@ struct SQButtonStyle { } var textColor: Color { + if isPressed && !isDisabled && !isLoading { + switch type { + case .solid: + return .white + case .line, .light, .glass: + return colorScheme.baseColor.opacity(0.8) + } + } + if isDisabled { switch type { case .solid: @@ -335,67 +396,37 @@ struct SQButtonStyle { HStack { VStack(spacing: 16) { SQButton("C'est parti !") {} - .colorScheme(.royal) + .sqColorScheme(.royal) SQButton("C'est parti !") {} - .colorScheme(.royal) - .buttonType(.line) + .sqColorScheme(.royal) + .sqButtonType(.line) SQButton("C'est parti !") {} - .colorScheme(.royal) - .buttonType(.light) + .sqColorScheme(.royal) + .sqButtonType(.light) SQButton("C'est parti !") {} - .colorScheme(.royal) - .buttonType(.glass) + .sqColorScheme(.royal) + .sqButtonType(.glass) } VStack(spacing: 16) { - SQButton("C'est parti !") {} - .colorScheme(.royal) - .loading(true) - SQButton("C'est parti !") {} - .colorScheme(.royal) - .buttonType(.line) - .loading(true) - SQButton("C'est parti !") {} - .colorScheme(.royal) - .buttonType(.light) - .loading(true) - SQButton("C'est parti !") {} - .colorScheme(.royal) - .buttonType(.glass) - .loading(true) + SQButton("C'est parti !", isLoading: .constant(true)) {} + .sqColorScheme(.royal) + SQButton("C'est parti !", isLoading: .constant(true)) {} + .sqColorScheme(.royal) + .sqButtonType(.line) + SQButton("C'est parti !", isLoading: .constant(true)) {} + .sqColorScheme(.royal) + .sqButtonType(.light) + SQButton("C'est parti !", isLoading: .constant(true)) {} + .sqColorScheme(.royal) + .sqButtonType(.glass) } } - HStack { - VStack(spacing: 16) { - SQButton("C'est parti !") {} - .colorScheme(.royal) - SQButton("C'est parti !") {} - .colorScheme(.royal) - .buttonType(.line) - SQButton("C'est parti !") {} - .colorScheme(.royal) - .buttonType(.light) - SQButton("C'est parti !") {} - .colorScheme(.royal) - .buttonType(.glass) - } - VStack(spacing: 16) { - SQButton("C'est parti !") {} - .colorScheme(.royal) - .disabled(true) - SQButton("C'est parti !") {} - .colorScheme(.royal) - .buttonType(.line) - .disabled(true) - SQButton("C'est parti !") {} - .colorScheme(.royal) - .buttonType(.light) - .disabled(true) - SQButton("C'est parti !") {} - .colorScheme(.royal) - .buttonType(.glass) - .disabled(true) - } - } + SQButton("Continuer avec Apple") {} + .sqButtonType(.line) + .sqIcon(SQIcon(.apple_brand)) + .sqLarge() } + .padding() } + diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQCheckbox.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQCheckbox.swift index 306c3ca..42e298c 100644 --- a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQCheckbox.swift +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQCheckbox.swift @@ -42,7 +42,10 @@ struct SQCheckbox: View { HStack { SQIcon(.circle_exclamation, customSize: 13, type: .solid, color: .sqSemanticRed) - SQText(error.wrappedValue.message, size: 12, font: .demiBold, textColor: .sqSemanticRed) + SQText(error.wrappedValue.message) + .sqSize(12) + .sqFont(.semiBold) + .sqColor(.sqSemanticRed) } .isHidden(hidden: !error.wrappedValue.isInError) } diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQChips.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQChips.swift index 815048a..aaecb8e 100644 --- a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQChips.swift +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQChips.swift @@ -37,7 +37,10 @@ fileprivate struct SQChip: View { } var body: some View { - SQText(option.label, size: 14, font: .demiBold, textColor: isSelected ? .white : Color.sqNeutral(100)) + SQText(option.label) + .sqSize(14) + .sqFont(.semiBold) + .sqColor(isSelected ? .white : Color.sqNeutral(100)) .padding(.horizontal, 12) .padding(.vertical, 8) .background(isSelected ? option.bgColor : Color.clear) diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQCircleButton.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQCircleButton.swift index 64be373..cdfc7c8 100644 --- a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQCircleButton.swift +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQCircleButton.swift @@ -72,7 +72,9 @@ struct SQCircleButton: View { .shadow(color: Color.black.opacity(0.15), radius: 8, x: 0, y: 4) if !text.isEmpty { - SQText(text, font: .demiBold, textColor: .sqNeutral(80)) + SQText(text) + .sqFont(.semiBold) + .sqColor(.sqNeutral(80)) } } } diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQColorPicker.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQColorPicker.swift index 0d86426..5c2fabd 100644 --- a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQColorPicker.swift +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQColorPicker.swift @@ -35,7 +35,9 @@ struct SQColorPicker: View { var body: some View { VStack(alignment: .leading) { VStack(alignment: .leading) { - SQText(title, size: 18, font: .bold) + SQText(title) + .sqSize(18) + .sqFont(.bold) SQText(subtitle ?? "") .lineLimit(2) .lineSpacing(1) diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQDatePicker.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQDatePicker.swift new file mode 100644 index 0000000..8a2433f --- /dev/null +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQDatePicker.swift @@ -0,0 +1,143 @@ +// +// SQDatePicker.swift +// Allovoisins +// +// Created by gabin Warnier de wailly on 13/12/2024. +// Copyright © 2024 AlloVoisins. All rights reserved. +// + +import SwiftUI + +struct SQDatePickerView: View { + var label: String + var placeholder: String + var error: Binding? + var isDisabled: Bool + var dateRange: ClosedRange? + @Binding private var selectedDate: Date? + @State private var showDatePicker: Bool = false + + init(_ label: String, + placeholder: String, + selectedDate: Binding = .constant(Date()), + error: Binding? = nil, + isDisabled: Bool = false, + dateRange: ClosedRange? = nil) + { + self.label = label + self.placeholder = placeholder + self._selectedDate = selectedDate + self.error = error + self.isDisabled = isDisabled + self.dateRange = dateRange + } + + var body: some View { + ZStack { + VStack(alignment: .leading, spacing: 4) { + SQText(label) + .sqFont(.semiBold) + .sqColor(.sqNeutral(80)) + // TextField + HStack(spacing: 4) { + TextField("", text: .constant(formattedDate(selectedDate))) + .font(.sq(.medium)) + .foregroundStyle(Color.sqNeutral(isDisabled ? 60 : 100)) + .tint(.sqNeutral(80)) + .placeholder(when: selectedDate == nil) { + SQText(placeholder) + .sqColor(.sqNeutral(50)) + } + .onChange(of: self.selectedDate) { _ in + error?.wrappedValue = .none + } + .submitLabel(.done) + .disabled(true) + SQIcon(.calendar, color: isDisabled ? .sqNeutral(40) : .sqNeutral(100)) + } + .padding(16) + .foregroundStyle(Color.sqNeutral()) + .background(isDisabled ? Color.sqNeutral(10) : .clear) + .overlay( + RoundedRectangle(cornerRadius: 8) + .inset(by: 0.5) + .stroke(error?.wrappedValue.isInError ?? false ? .sqSemanticRed : isDisabled ? Color.sqNeutral(20) : Color.sqNeutral(30), lineWidth: 1) + ) + HStack(spacing: 16) { + if let error = error?.wrappedValue, error.isInError { + HStack(spacing: 4) { + SQIcon(.circle_exclamation, customSize: 12, type: .solid, color: .sqSemanticRed) + SQText(error.message) + .sqSize(12) + .sqFont(.semiBold) + .sqColor(.sqSemanticRed) + } + } + } + } + } + .contentShape(.rect) + .onTapGesture { + if isDisabled == false { + showDatePicker.toggle() + } + } + .sheet(isPresented: $showDatePicker) { + BottomSheetDatePicker(label: label, selectedDate: $selectedDate, dateRange: dateRange) + } + } + + private func formattedDate(_ date: Date?) -> String { + let formatter = DateFormatter() + formatter.dateStyle = .short + guard let dateNotNil = date else { return "" } + return formatter.string(from: dateNotNil) + } +} + +struct BottomSheetDatePicker: View { + var label: String + @Binding var selectedDate: Date? + var dateRange: ClosedRange? = nil + @Environment(\.presentationMode) var presentationMode + + var body: some View { + VStack { + SQText(label) + .sqFont(.semiBold) + .padding() + + DatePicker( + "", + selection: Binding( + get: { selectedDate ?? Date() }, + set: { selectedDate = $0 } + ), + in: dateRange ?? Date.distantPast ... Date.distantFuture, + displayedComponents: .date + ) + .datePickerStyle(GraphicalDatePickerStyle()) + .labelsHidden() + + Spacer() + + SQButton("Valider") { + if selectedDate == nil { + if let range = dateRange { + // Utiliser la borne inférieur (hier) qui est la date la plus récente autorisée + selectedDate = range.lowerBound + } else { + selectedDate = Date() + } + } + presentationMode.wrappedValue.dismiss() + } + } + .padding() + .presentationDetents([.medium]) + } +} + +#Preview { + SQDatePickerView("Date de naissance", placeholder: "01/01/1970", selectedDate: .constant(Date())) +} diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQImagePicker.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQImagePicker.swift index 3d9f57d..01fc210 100644 --- a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQImagePicker.swift +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQImagePicker.swift @@ -21,7 +21,8 @@ struct SQImagePicker: View { var body: some View { VStack(alignment: .leading) { - SQText("Images", font: .demiBold) + SQText("Images") + .sqFont(.semiBold) LazyVGrid(columns: columns, alignment: .leading) { ForEach(0 ..< nbOfImages) { _ in @@ -98,7 +99,9 @@ struct SQImagePickerSelectorSheet: View { SQIcon(.xmark, size: .l) } - SQText("Sélectionner une photo", size: 19, font: .bold) + SQText("Sélectionner une photo") + .sqSize(19) + .sqFont(.bold) .frame(maxWidth: .infinity) Spacer() } diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQNavigationBar.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQNavigationBar.swift index c2ba03d..4b73235 100644 --- a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQNavigationBar.swift +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQNavigationBar.swift @@ -19,7 +19,9 @@ struct SQNavigationBar: ViewModifier { .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .principal) { - SQText(title, size: 18, font: .bold) + SQText(title) + .sqSize(18) + .sqFont(.bold) .foregroundColor(style.foregroundColor) } if backAction != nil { diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQPicker.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQPicker.swift index 7a4763e..52aa90f 100644 --- a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQPicker.swift +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQPicker.swift @@ -59,14 +59,14 @@ struct MenuPicker: UIViewRepresentable { let options: [PickerOption] @Binding var isPresenting: Bool - func makeUIView(context: Context) -> UIButton { + func makeUIView(context _: Context) -> UIButton { let button = UIButton(type: .custom) button.setTitle("", for: .normal) updateMenu(button) return button } - func updateUIView(_ uiView: UIButton, context: Context) { + func updateUIView(_ uiView: UIButton, context _: Context) { updateMenu(uiView) if isPresenting { uiView.sendActions(for: .menuActionTriggered) @@ -106,7 +106,7 @@ struct SQPickerPreview: View { let options = [ PickerOption(text: "1 mois"), PickerOption(text: "2 mois"), - PickerOption(text: "3 mois") + PickerOption(text: "3 mois"), ] _selectedDuration = State(initialValue: options[0]) durationOptions = options @@ -119,8 +119,5 @@ struct SQPickerPreview: View { } #Preview { - ZStack { - Color.black - SQPickerPreview() - } + SQPickerPreview() } diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQProgressIndicator.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQProgressIndicator.swift index d041744..87b65ba 100644 --- a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQProgressIndicator.swift +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQProgressIndicator.swift @@ -10,19 +10,62 @@ import SwiftUI struct SQProgressIndicator: View { @Binding var isActivated: Bool + @State private var colorScheme: SQProgressIndicatorColorScheme = .neutral + init(_ isActivated: Binding) { self._isActivated = isActivated } var body: some View { Rectangle() - .foregroundColor(.clear) - .frame(maxWidth: .infinity, minHeight: 4, maxHeight: 4) - .background(isActivated ? Color.sqSemanticGreen : Color.sqNeutral(20)) - .cornerRadius(8) + .foregroundColor(.clear) + .frame(maxWidth: .infinity, minHeight: 4, maxHeight: 4) + .background(isActivated ? colorScheme.baseColor : Color.sqNeutral(20)) + .cornerRadius(8) + } +} + +// MARK: - Modifiers + +extension SQProgressIndicator { + func colorScheme(_ scheme: SQProgressIndicatorColorScheme) -> SQProgressIndicator { + var copy = self + copy._colorScheme = State(initialValue: scheme) + return copy + } +} + +enum SQProgressIndicatorColorScheme: String, CaseIterable { + case neutral + case green + + var baseColor: Color { + switch self { + case .neutral: + return .sqNeutral(80) + case .green: + return .sqSemanticGreen + } } } #Preview { - SQProgressIndicator(.constant(true)) + VStack { + HStack { + SQProgressIndicator(.constant(true)) + SQProgressIndicator(.constant(false)) + SQProgressIndicator(.constant(false)) + } + HStack { + SQProgressIndicator(.constant(true)) + .colorScheme(.green) + SQProgressIndicator(.constant(true)) + .colorScheme(.green) + SQProgressIndicator(.constant(false)) + .colorScheme(.green) + SQProgressIndicator(.constant(false)) + .colorScheme(.green) + } + } + .padding() } diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQRadio.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQRadio.swift index 727a5ea..720f35c 100644 --- a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQRadio.swift +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQRadio.swift @@ -45,7 +45,9 @@ struct SQRadio: View { var body: some View { VStack(alignment: orientation == .vertical ? .leading : .center, spacing: 16) { if let title = title { - SQText(title, size: titleSize, font: .bold) + SQText(title) + .sqSize(titleSize) + .sqFont(.bold) .multilineTextAlignment(.center) } Group { @@ -61,7 +63,10 @@ struct SQRadio: View { } if error.wrappedValue != .none { - SQText(error.wrappedValue.message, size: 12, font: .demiBold, textColor: Color.sqSemanticRed) + SQText(error.wrappedValue.message) + .sqSize(12) + .sqFont(.semiBold) + .sqColor(.sqSemanticRed) } } } @@ -137,7 +142,8 @@ struct SQRadio: View { } if let desc = desc { - SQText(desc, size: 12) + SQText(desc) + .sqSize(12) } } } diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTabBar.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTabBar.swift deleted file mode 100644 index 8a48c15..0000000 --- a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTabBar.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// SQTabBar.swift -// -// -// Created by Victor on 18/07/2024. -// - -import SwiftUI - -struct SQTabBar: View { - @Binding var selection: Int - - var body: some View { - HStack { - TabBarButton(imageName: "house", text: "Accueil") - } - } -} - -private struct TabBarButton: View { - let imageName: String - let text: String - - var body: some View { - VStack { - Image(systemName: imageName) - .renderingMode(.template) - .tint(.black) - .fontWeight(.bold) - Text(text) - } - } -} - -#Preview { - SQTabBar(selection: .constant(1)) -} diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQText.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQText.swift deleted file mode 100644 index 5ac8f2e..0000000 --- a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQText.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// SQText.swift -// -// -// Created by Victor on 12/06/2024. -// - -import SwiftUI - -enum SQTextFont: String { - case medium = "TTChocolates-Medium" - case mediumItalic = "TTChocolates-MediumIt" - case demiBold = "TTChocolates-DemiBold" - case bold = "TTChocolates-Bold" - case boldItalic = "TTChocolates-Bold-Italic" -} - -struct SQText: View { - var text: String - var attributedText: AttributedString? - var size: CGFloat - var font: SQTextFont - var textColor: Color - - init(_ text: String, size: CGFloat = 16, font: SQTextFont = .medium, textColor: Color = .sqNeutral(90)) { - self.text = text - self.attributedText = nil - self.size = size - self.font = font - self.textColor = textColor - } - - init(_ attributedText: AttributedString, size: CGFloat = 16, textColor: Color = .sqNeutral(90)) { - self.text = "" - self.attributedText = attributedText - self.size = size - self.font = .medium - self.textColor = textColor - } - - var body: some View { - if let attributedText = attributedText { - Text(attributedText) - .foregroundStyle(textColor) - } else { - Text(text) - .font(.custom(font.rawValue, size: size)) - .foregroundStyle(textColor) - } - } -} - -#Preview { - VStack(spacing: 10) { - SQText("Hello world!", font: .medium) - SQText("Hello world!", font: .mediumItalic) - SQText("Hello world!", size: 18, font: .demiBold) - SQText("Hello world!", font: .bold) - SQText("Hello world!", size: 18, font: .boldItalic) - } -} diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQText/SQText.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQText/SQText.swift new file mode 100644 index 0000000..2cb4335 --- /dev/null +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQText/SQText.swift @@ -0,0 +1,88 @@ +// +// SQText.swift +// Allovoisins +// +// Created by Victor on 13/06/2024. +// Copyright © 2024 AlloVoisins. All rights reserved. +// + +import SwiftUI + +// MARK: - SQTextFont + +enum SQTextFont: String { + case medium = "TTChocolates-Medium" + case mediumItalic = "TTChocolates-MediumIt" + case semiBold = "TTChocolates-DemiBold" + case bold = "TTChocolates-Bold" + case boldItalic = "TTChocolates-Bold-Italic" +} + +// MARK: - SQText + +/// Text component with custom Sequoia typography. +/// +/// Use View Modifiers to configure the text: +/// ```swift +/// SQText("Hello world!") +/// .sqSize(18) +/// .sqFont(.bold) +/// .sqColor(.sqNeutral(100)) +/// ``` +struct SQText: View { + private let text: String + private let attributedText: AttributedString? + + @Environment(\.sqTextConfiguration) private var config + + /// Creates a text view with plain string. + /// + /// - Parameter text: The string to display + init(_ text: String) { + self.text = text + self.attributedText = nil + } + + /// Creates a text view with attributed string. + /// + /// - Parameter attributedText: The attributed string to display + init(_ attributedText: AttributedString) { + self.text = "" + self.attributedText = attributedText + } + + var body: some View { + if let attributedText = attributedText { + Text(attributedText) + .font(.custom(config.font.rawValue, fixedSize: config.size)) + .foregroundStyle(config.textColor) + } else { + Text(text) + .font(.custom(config.font.rawValue, fixedSize: config.size)) + .foregroundStyle(config.textColor) + } + } +} + +// MARK: - Preview + +#Preview { + VStack(spacing: 10) { + SQText("Hello world!") + .sqFont(.medium) + + SQText("Hello world!") + .sqFont(.mediumItalic) + + SQText("Hello world!") + .sqSize(18) + .sqFont(.semiBold) + + SQText("Hello world!") + .sqFont(.bold) + + SQText("Hello world!") + .sqSize(18) + .sqFont(.boldItalic) + } +} diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQText/SQTextConfiguration.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQText/SQTextConfiguration.swift new file mode 100644 index 0000000..b28698c --- /dev/null +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQText/SQTextConfiguration.swift @@ -0,0 +1,30 @@ +// +// SQTextConfiguration.swift +// Allovoisins +// +// Created by Claude on 22/10/2025. +// Copyright © 2025 AlloVoisins. All rights reserved. +// + +import SwiftUI + +/// Configuration structure for Sequoia Text components. +/// Contains all customizable options that can be set via View Modifiers. +struct SQTextConfiguration { + var size: CGFloat = 16 + var font: SQTextFont = .medium + var textColor: Color = .sqNeutral(90) +} + +// MARK: - Environment Key + +private struct SQTextConfigurationKey: EnvironmentKey { + static let defaultValue = SQTextConfiguration() +} + +extension EnvironmentValues { + var sqTextConfiguration: SQTextConfiguration { + get { self[SQTextConfigurationKey.self] } + set { self[SQTextConfigurationKey.self] = newValue } + } +} diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQText/SQTextModifiers.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQText/SQTextModifiers.swift new file mode 100644 index 0000000..9950873 --- /dev/null +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQText/SQTextModifiers.swift @@ -0,0 +1,78 @@ +// +// SQTextModifiers.swift +// Allovoisins +// +// Created by Claude on 22/10/2025. +// Copyright © 2025 AlloVoisins. All rights reserved. +// + +import SwiftUI + +// MARK: - Font Modifier + +private struct SQFontModifier: ViewModifier { + let font: SQTextFont + @Environment(\.sqTextConfiguration) var config + + func body(content: Content) -> some View { + var newConfig = config + newConfig.font = font + return content.environment(\.sqTextConfiguration, newConfig) + } +} + +extension View { + /// Sets the font weight for the text. + /// + /// - Parameter font: The font weight to apply (.regular, .medium, .semiBold, .bold) + /// - Returns: A view with the font configuration applied + func sqFont(_ font: SQTextFont) -> some View { + modifier(SQFontModifier(font: font)) + } +} + +// MARK: - Size Modifier + +private struct SQSizeModifier: ViewModifier { + let size: CGFloat + @Environment(\.sqTextConfiguration) var config + + func body(content: Content) -> some View { + var newConfig = config + newConfig.size = size + return content.environment(\.sqTextConfiguration, newConfig) + } +} + +extension View { + /// Sets the font size for the text. + /// + /// - Parameter size: The font size in points + /// - Returns: A view with the size configuration applied + func sqSize(_ size: CGFloat) -> some View { + modifier(SQSizeModifier(size: size)) + } +} + +// MARK: - Color Modifier + +private struct SQColorModifier: ViewModifier { + let textColor: Color + @Environment(\.sqTextConfiguration) var config + + func body(content: Content) -> some View { + var newConfig = config + newConfig.textColor = textColor + return content.environment(\.sqTextConfiguration, newConfig) + } +} + +extension View { + /// Sets the text color. + /// + /// - Parameter textColor: The color to apply to the text + /// - Returns: A view with the color configuration applied + func sqColor(_ textColor: Color) -> some View { + modifier(SQColorModifier(textColor: textColor)) + } +} diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextEditor.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextEditor.swift index 59930ae..8446f6b 100644 --- a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextEditor.swift +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextEditor.swift @@ -10,7 +10,6 @@ import SwiftUI struct SQTextEditor: View { var label: String var placeholder: String - var type: SQTextFieldType = .text var error: Binding var icon: SQIcon? var isDisabled: Bool = false @@ -28,7 +27,6 @@ struct SQTextEditor: View { init(_ label: String, placeholder: String, - type: SQTextFieldType = .text, error: Binding = .constant(.none), text: Binding, icon: SQIcon? = nil, @@ -42,7 +40,6 @@ struct SQTextEditor: View { { self.label = label self.placeholder = placeholder - self.type = type self.error = error self._text = text self.icon = icon @@ -61,7 +58,9 @@ struct SQTextEditor: View { SQText(label) Spacer() if isOptional { - SQText("Optionnel", size: 12, textColor: .sqNeutral(50)) + SQText("Optionnel") + .sqSize(12) + .sqColor(.sqNeutral(50)) } } TextEditor(text: Binding( @@ -87,12 +86,17 @@ struct SQTextEditor: View { if error.wrappedValue.isInError { HStack(spacing: 8) { SQIcon(.circle_exclamation, customSize: 12, type: .solid, color: .sqSemanticRed) - SQText(error.wrappedValue.message, size: 12, font: .demiBold, textColor: .sqSemanticRed) + SQText(error.wrappedValue.message) + .sqSize(12) + .sqFont(.semiBold) + .sqColor(.sqSemanticRed) } } Spacer() if !characterCountText.isEmpty { - SQText(characterCountText, size: 12, textColor: .sqNeutral(50)) + SQText(characterCountText) + .sqSize(12) + .sqColor(.sqNeutral(50)) } } } diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextField.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextField.swift deleted file mode 100644 index 7f36865..0000000 --- a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextField.swift +++ /dev/null @@ -1,222 +0,0 @@ -// -// SQTextField.swift -// Sequoia -// -// Created by Victor on 10/10/2024. -// - -import SwiftUI - -enum SQTextFieldType { - case text - case phoneNumber -} - -struct SQTextField: View { - var label: String - var placeholder: String - var type: SQTextFieldType = .text - var icon: SQIcon? - var isDisabled: Bool = false - var isOptional: Bool = false - var tooltipText: String? - var helperText: String? - var error: Binding - 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, - text: Binding, - icon: SQIcon? = nil, - isDisabled: Bool = false, - isOptional: Bool = false, - tooltipText: String? = nil, - helperText: String? = nil, - error: Binding = .constant(.none), - minCharacters: Int? = nil, - maxCharacters: Int? = nil, - infoAction: (() -> Void)? = nil, - iconAction: (() -> Void)? = nil) - { - self.label = label - self.placeholder = placeholder - self.type = type - self._text = text - self.icon = icon - self.isDisabled = isDisabled - self.isOptional = isOptional - self.tooltipText = tooltipText - self.helperText = helperText - self.error = error - 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) - 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: { - SQIcon(.circle_info, color: .sqNeutral(80)) - } - } - Spacer() - if isOptional { - SQText("Optionnel", size: 12, textColor: .sqNeutral(50)) - } - } - // TextField - HStack(spacing: 4) { - if type == .phoneNumber { - TextField("", text: $text) - .placeholder(when: text.isEmpty) { - SQText(placeholder, textColor: .sqNeutral(50)) - } - .onReceive(self.text.publisher.collect()) { - let formatted = formatPhoneNumber(String($0.prefix(15))) - self.text = formatted - } - .keyboardType(.numberPad) - .font(.sq(.medium)) - .foregroundStyle(Color.sqNeutral(100)) - .tint(accentColor) - } else { - TextField(placeholder, text: Binding( - get: { self.text }, - set: { self.text = String($0.prefix(self.maxCharacters ?? Int.max)) } - )) - .onChange(of: self.text, perform: { _ in - self.error.wrappedValue = .none - }) - .font(.sq(.medium)) - .foregroundStyle(Color.sqNeutral(100)) - .tint(accentColor) - if let icon = icon { - Button { - iconAction?() - } label: { - icon - } - } - } - } - .padding(16) - .foregroundStyle(Color.sqNeutral()) - .background(isDisabled ? Color.sqNeutral(10) : .clear) - .overlay( - RoundedRectangle(cornerRadius: 8) - .inset(by: 0.5) - .stroke(error.wrappedValue.isInError ? .sqSemanticRed : isFocused ? accentColor : isDisabled ? Color.sqNeutral(20) : Color.sqNeutral(30), lineWidth: 1) - ) - .focused($isFocused) - .disabled(isDisabled) - HStack(spacing: 16) { - // HStack error - if error.wrappedValue.isInError { - HStack(spacing: 4) { - SQIcon(.circle_exclamation, customSize: 12, type: .solid, color: .sqSemanticRed) - SQText(error.wrappedValue.message, size: 12, textColor: .sqSemanticRed) - } - } else if helperText != nil { - SQText(helperText!, size: 12, textColor: .sqNeutral(50)) - } - Spacer() - if !characterCountText.isEmpty { - SQText(characterCountText, size: 12, textColor: .sqNeutral(50)) - } - } - } - .popover(isPresented: $showTooltip, content: { - SQText(tooltipText ?? "") - }) -// .overlay( -// Group { -// if showTooltip { -// Color.black.opacity(0.001) -// .onTapGesture { -// withAnimation { -// showTooltip = false -// } -// } -// } -// } -// ) - } - - private var characterCountText: String { - let count = text.count - if let min = minCharacters, count < min { - return "\(count)/\(min)" - } else if let max = maxCharacters { - return count >= (max * 4 / 5) ? "\(count)/\(max)" : "\(count)" - } - return "" - } - - private func formatPhoneNumber(_ number: String) -> String { - let numbers = number.filter { $0.isNumber } - var result = "" - - for (index, char) in numbers.enumerated() { - if index > 0 && index % 2 == 0 { - result += " " - } - result += String(char) - } - - return result - } - - private func isValidPhoneNumber(_ number: String) -> Bool { - let numbers = number.filter { $0.isNumber } - return numbers.count == 10 && numbers.first == "0" - } -} - -#Preview { - VStack(spacing: 32) { - SQTextField("Label name", placeholder: "Placeholder", text: .constant("Bonjour, "), minCharacters: 20) - SQTextField("Label name", placeholder: "Placeholder", text: .constant(""), isOptional: true) - SQTextField("Label name", placeholder: "Placeholder", text: .constant(""), isDisabled: true) - SQTextField("Label name", placeholder: "Placeholder", text: .constant(""), error: .constant(.empty)) - 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() -} diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextFields/SQPasswordField.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextFields/SQPasswordField.swift new file mode 100644 index 0000000..6c9746e --- /dev/null +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextFields/SQPasswordField.swift @@ -0,0 +1,112 @@ +// +// SQPasswordField.swift +// Allovoisins +// +// Created by Claude on 22/10/2025. +// Copyright © 2025 AlloVoisins. All rights reserved. +// + +import SwiftUI + +// MARK: - SQPasswordField + +/// Specialized text field for password input with visibility toggle. +/// +/// Use View Modifiers to configure the password field: +/// ```swift +/// SQPasswordField("Mot de passe", text: $password) +/// .sqPlaceholder("Entrez votre mot de passe") +/// .sqMinCharacters(8) +/// .sqHelperText("Minimum 8 caractères") +/// .sqError($passwordError) +/// ``` +struct SQPasswordField: View { + private let label: String + @Binding var text: String + + @FocusState private var isFocused: Bool + @State private var isPasswordVisible = false + @Environment(\.sqTextFieldConfiguration) private var config + + private var accentColor: Color { .sqNeutral(80) } + + /// Creates a password field with visibility toggle. + /// + /// - Parameters: + /// - label: The label text displayed above the field + /// - text: Binding to the password text value + init(_ label: String, text: Binding) { + self.label = label + self._text = text + } + + var body: some View { + SQTextFieldBase(label, isFocused: Binding(get: { isFocused }, set: { isFocused = $0 }), textCount: text.count) { + HStack(spacing: 4) { + Group { + if isPasswordVisible { + TextField("", text: $text) + } else { + SecureField("", text: $text) + } + } + .placeholder(when: text.isEmpty) { + SQText(config.placeholder) + .sqColor(.sqNeutral(50)) + } + .onChange(of: text) { newValue in + // Character limit + if let max = config.maxCharacters, newValue.count > max { + text = String(newValue.prefix(max)) + } + // Reset error + config.error.wrappedValue = .none + } + .keyboardType(config.keyboardType) + .foregroundStyle(Color.sqNeutral(100)) + .tint(accentColor) + .submitLabel(.done) + .focused($isFocused) + .accessibilityLabel(label) + .accessibilityValue(text.isEmpty ? "Vide" : "Rempli") + + Button { + isPasswordVisible.toggle() + } label: { + SQIcon(isPasswordVisible ? .eye_slash : .eye, color: .sqNeutral(80)) + } + .accessibilityLabel(isPasswordVisible ? "Masquer le mot de passe" : "Afficher le mot de passe") + } + } + } +} + +// MARK: - Preview + +#Preview { + VStack(spacing: 32) { + SQPasswordField("Nouveau mot de passe", text: .constant("")) + .sqPlaceholder("Mot de passe") + + SQPasswordField("Mot de passe", text: .constant("MonMotDePasse123!")) + .sqPlaceholder("Mot de passe") + + SQPasswordField("Mot de passe", text: .constant("")) + .sqPlaceholder("Mot de passe") + .sqOptional() + + SQPasswordField("Mot de passe", text: .constant("")) + .sqPlaceholder("Mot de passe") + .disabled(true) + + SQPasswordField("Nouveau mot de passe", text: .constant("")) + .sqPlaceholder("Mot de passe") + .sqMinCharacters(8) + .sqHelperText("Minimum 8 caractères") + + SQPasswordField("Confirmer le mot de passe", text: .constant("")) + .sqPlaceholder("Mot de passe") + .sqTooltipText("Le mot de passe doit contenir au moins 8 caractères, une majuscule, une minuscule et un chiffre.") + } + .padding() +} diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextFields/SQPhoneField.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextFields/SQPhoneField.swift new file mode 100644 index 0000000..ce58cb0 --- /dev/null +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextFields/SQPhoneField.swift @@ -0,0 +1,106 @@ +// +// SQPhoneField.swift +// Allovoisins +// +// Created by Claude on 22/10/2025. +// Copyright © 2025 AlloVoisins. All rights reserved. +// + +import SwiftUI + +// MARK: - SQPhoneField + +/// Specialized text field for phone number input with automatic formatting. +/// +/// Use View Modifiers to configure the phone field: +/// ```swift +/// SQPhoneField("Téléphone", text: $phone) +/// .sqPlaceholder("01 23 45 67 89") +/// .sqHelperText("Format : 01 23 45 67 89") +/// .sqError($phoneError) +/// ``` +struct SQPhoneField: View { + private let label: String + @Binding var text: String + + @FocusState private var isFocused: Bool + @Environment(\.sqTextFieldConfiguration) private var config + + private var accentColor: Color { .sqNeutral(80) } + + /// Creates a phone number field with automatic formatting. + /// + /// - Parameters: + /// - label: The label text displayed above the field + /// - text: Binding to the phone number text value + init(_ label: String, text: Binding) { + self.label = label + self._text = text + } + + var body: some View { + SQTextFieldBase(label, isFocused: Binding(get: { isFocused }, set: { isFocused = $0 }), textCount: text.count) { + TextField("", text: $text) + .placeholder(when: text.isEmpty) { + SQText(config.placeholder) + .sqColor(.sqNeutral(50)) + } + .onChange(of: text) { newValue in + // Format and limit phone number + let formatted = formatPhoneNumber(String(newValue.prefix(15))) + if formatted != newValue { + text = formatted + } + // Reset error + config.error.wrappedValue = .none + } + .keyboardType(.numberPad) + .foregroundStyle(Color.sqNeutral(100)) + .tint(accentColor) + .focused($isFocused) + .accessibilityLabel(label) + .accessibilityValue(text.isEmpty ? "Vide" : text) + } + } + + // MARK: - Helper Methods + + private func formatPhoneNumber(_ number: String) -> String { + let numbers = number.filter { $0.isNumber } + var result = "" + + for (index, char) in numbers.enumerated() { + if index > 0 && index % 2 == 0 { + result += " " + } + result += String(char) + } + + return result + } +} + +// MARK: - Preview + +#Preview { + VStack(spacing: 32) { + SQPhoneField("Téléphone", text: .constant("")) + .sqPlaceholder("01 23 45 67 89") + + SQPhoneField("Téléphone", text: .constant("06 12 34 56 78")) + .sqPlaceholder("01 23 45 67 89") + + SQPhoneField("Téléphone", text: .constant("")) + .sqPlaceholder("01 23 45 67 89") + .sqOptional() + + SQPhoneField("Téléphone", text: .constant("")) + .sqPlaceholder("01 23 45 67 89") + .disabled(true) + + SQPhoneField("Téléphone portable", text: .constant("")) + .sqPlaceholder("01 23 45 67 89") + .sqHelperText("Format : 01 23 45 67 89") + } + .padding() +} diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextFields/SQTextField.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextFields/SQTextField.swift new file mode 100644 index 0000000..5eae53b --- /dev/null +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextFields/SQTextField.swift @@ -0,0 +1,138 @@ +// +// SQTextField.swift +// Sequoia +// +// Created by Victor on 10/10/2024. +// + +import Lottie +import SwiftUI + +// MARK: - SQTextField + +/// Standard text field component with optional icon. +/// +/// Use View Modifiers to configure the text field: +/// ```swift +/// SQTextField("Name", text: $name) +/// .sqPlaceholder("Enter your name") +/// .sqMinCharacters(2) +/// .sqMaxCharacters(50) +/// .sqError($nameError) +/// ``` +struct SQTextField: View { + private let label: String + @Binding var text: String + private let icon: SQIcon? + private let iconAction: (() -> Void)? + + @FocusState private var isFocused: Bool + @Environment(\.sqTextFieldConfiguration) private var config + + private var accentColor: Color { .sqNeutral(80) } + + /// Creates a standard text field. + /// + /// - Parameters: + /// - label: The label text displayed above the field + /// - text: Binding to the text value + init(_ label: String, text: Binding, icon: SQIcon? = nil, iconAction: (() -> Void)? = nil) { + self.label = label + self._text = text + self.icon = icon + self.iconAction = iconAction + } + + var body: some View { + SQTextFieldBase(label, isFocused: Binding(get: { isFocused }, set: { isFocused = $0 }), textCount: text.count) { + HStack(spacing: 4) { + TextField("", text: $text) + .placeholder(when: text.isEmpty) { + SQText(config.placeholder) + .sqColor(.sqNeutral(50)) + } + .onChange(of: text) { newValue in + // Character limit + if let max = config.maxCharacters, newValue.count > max { + text = String(newValue.prefix(max)) + } + // Reset error + config.error.wrappedValue = .none + } + .keyboardType(config.keyboardType) + .foregroundStyle(Color.sqNeutral(100)) + .tint(accentColor) + .submitLabel(.done) + .focused($isFocused) + .toolbar { + ToolbarItemGroup(placement: .keyboard) { + if config.keyboardType == .numberPad || config.keyboardType == .decimalPad || config.keyboardType == .phonePad { + Spacer() + Button("Done") { + isFocused = false + } + } + } + } + .accessibilityLabel(label) + .accessibilityValue(text.isEmpty ? "Vide" : text) + + if config.isLoading.wrappedValue { + LottieView(animation: .named("av_loader")) + .looping() + .frame(width: 20, height: 20) + } else if let icon = icon { + Button(action: iconAction ?? {}) { + icon + } + } + } + } + } +} + +// MARK: - Icon Modifier + +extension SQTextField { + /// Adds an icon button on the right side of the text field. + /// + /// - Parameters: + /// - icon: The icon to display + /// - action: Action to perform when the icon is tapped + /// - Returns: A text field with the icon + func sqIcon(_ icon: SQIcon, action: @escaping () -> Void = {}) -> SQTextField { + SQTextField(label, text: $text, icon: icon, iconAction: action) + } +} + +// MARK: - Preview + +#Preview { + VStack(spacing: 32) { + SQTextField("Label name", text: .constant("Bonjour")) + .sqPlaceholder("Placeholder") + .sqMinCharacters(20) + + SQTextField("Label name", text: .constant("")) + .sqPlaceholder("Placeholder") + .sqOptional() + + SQTextField("Label name", text: .constant("")) + .sqPlaceholder("Placeholder") + .disabled(true) + + SQTextField("Label name", text: .constant("")) + .sqPlaceholder("Placeholder") + + // Note: .sqIcon() must be called BEFORE other modifiers to work correctly + SQTextField("Label with icon", text: .constant("")) + .sqIcon(SQIcon(.magnifying_glass, color: .sqNeutral(80))) + .sqPlaceholder("Placeholder") + + SQTextField("Numéro de RCS", text: .constant("")) + .sqPlaceholder("RCS VILLE B 123456789") + .sqOptional() + .sqTooltipText("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() +} diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextFields/SQTextFieldBase.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextFields/SQTextFieldBase.swift new file mode 100644 index 0000000..745d1b3 --- /dev/null +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextFields/SQTextFieldBase.swift @@ -0,0 +1,170 @@ +// +// SQTextFieldBase.swift +// Allovoisins +// +// Created by Claude on 22/10/2025. +// Copyright © 2025 AlloVoisins. All rights reserved. +// + +import SwiftUI + +// MARK: - SQTextFieldBase + +/// Base component for all Sequoia text field variants. +/// Provides common functionality: label, error display, character count, tooltip, styling, etc. +/// +/// This component uses `@Environment` to read configuration set via View Modifiers, +/// following the same pattern as native SwiftUI components. +struct SQTextFieldBase: View { + private let label: String + @Binding var isFocused: Bool + var textCount: Int + let content: Content + + @Environment(\.sqTextFieldConfiguration) private var config + @State private var showTooltip = false + + private var accentColor: Color { .sqNeutral(80) } + + init( + _ label: String, + isFocused: Binding, + textCount: Int, + @ViewBuilder content: () -> Content + ) { + self.label = label + self._isFocused = isFocused + self.textCount = textCount + self.content = content() + } + + var body: some View { + VStack(alignment: .leading, spacing: 4) { + // Label with tooltip/info + HStack(spacing: 8) { + SQText(label) + .sqFont(.semiBold) + .sqColor(.sqNeutral(80)) + + if let tooltipText = config.tooltipText { + Button { + withAnimation { + showTooltip.toggle() + } + } label: { + SQIcon(.circle_info, color: .sqNeutral(80)) + } + .overlay( + Group { + if showTooltip { + SQTooltip(text: tooltipText, isVisible: $showTooltip) + .fixedSize() + } + } + ) + .accessibilityLabel("Information") + .accessibilityHint("Appuyez deux fois pour afficher plus d'informations") + } else if let infoAction = config.infoAction { + Button { + infoAction() + } label: { + SQIcon(.circle_info, color: .sqNeutral(80)) + } + .accessibilityLabel("Information") + .accessibilityHint("Appuyez deux fois pour afficher plus d'informations") + } + + Spacer() + + if config.isOptional { + SQText("Optionnel") + .sqSize(12) + .sqColor(.sqNeutral(50)) + } + } + + // Content (TextField/SecureField/custom) + content + .padding(16) + .foregroundStyle(Color.sqNeutral()) + .background { + RoundedRectangle(cornerRadius: 8) + .fill(config.isDisabled ? Color.sqNeutral(10) : config.backgroundColor) + } + .overlay( + RoundedRectangle(cornerRadius: 8) + .inset(by: 0.5) + .stroke( + config.error.wrappedValue.isInError ? .sqSemanticRed : + isFocused ? accentColor : + config.isDisabled ? Color.sqNeutral(20) : Color.sqNeutral(30), + lineWidth: 1 + ) + ) + + // Error / Helper / Character count + HStack(spacing: 16) { + if config.error.wrappedValue.isInError { + HStack(spacing: 4) { + SQIcon(.circle_exclamation, customSize: 12, type: .solid, color: .sqSemanticRed) + SQText(config.error.wrappedValue.message) + .sqSize(12) + .sqColor(.sqSemanticRed) + } + } else if let helperText = config.helperText { + SQText(helperText) + .sqSize(12) + .sqColor(.sqNeutral(50)) + } + + Spacer() + + if !characterCountText.isEmpty && config.showCharacterCount { + SQText(characterCountText) + .sqSize(12) + .sqColor(.sqNeutral(50)) + } + } + } + .disabled(config.isDisabled) + .overlay( + Group { + if showTooltip { + Color.black.opacity(0.001) + .onTapGesture { + withAnimation { + showTooltip = false + } + } + } + } + ) + } + + // MARK: - Helper Properties + + private var characterCountText: String { + let count = textCount + if let min = config.minCharacters, count < min { + return "\(count)/\(min) min." + } else if let max = config.maxCharacters { + return count >= (max * 4 / 5) ? "\(count)/\(max) max." : "\(count)" + } + return "" + } +} + +// MARK: - Placeholder Extension + +extension View { + func placeholder( + when shouldShow: Bool, + alignment: Alignment = .leading, + @ViewBuilder placeholder: () -> Content + ) -> some View { + ZStack(alignment: alignment) { + placeholder().opacity(shouldShow ? 1 : 0) + self + } + } +} diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextFields/SQTextFieldConfiguration.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextFields/SQTextFieldConfiguration.swift new file mode 100644 index 0000000..ea5ce8c --- /dev/null +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextFields/SQTextFieldConfiguration.swift @@ -0,0 +1,40 @@ +// +// SQTextFieldConfiguration.swift +// Allovoisins +// +// Created by Claude on 22/10/2025. +// Copyright © 2025 AlloVoisins. All rights reserved. +// + +import SwiftUI + +/// Configuration structure for Sequoia TextField components. +/// Contains all customizable options that can be set via View Modifiers. +struct SQTextFieldConfiguration { + var placeholder: String = "" + var isDisabled: Bool = false + var isOptional: Bool = false + var isLoading: Binding = .constant(false) + var tooltipText: String? = nil + var helperText: String? = nil + var error: Binding = .constant(.none) + var minCharacters: Int? = nil + var maxCharacters: Int? = nil + var showCharacterCount: Bool = true + var backgroundColor: Color = .clear + var keyboardType: UIKeyboardType = .default + var infoAction: (() -> Void)? = nil +} + +// MARK: - Environment Key + +private struct SQTextFieldConfigurationKey: EnvironmentKey { + static let defaultValue = SQTextFieldConfiguration() +} + +extension EnvironmentValues { + var sqTextFieldConfiguration: SQTextFieldConfiguration { + get { self[SQTextFieldConfigurationKey.self] } + set { self[SQTextFieldConfigurationKey.self] = newValue } + } +} diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextFields/SQTextFieldModifiers.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextFields/SQTextFieldModifiers.swift new file mode 100644 index 0000000..573027d --- /dev/null +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTextFields/SQTextFieldModifiers.swift @@ -0,0 +1,242 @@ +// +// SQTextFieldModifiers.swift +// Allovoisins +// +// Created by Claude on 22/10/2025. +// Copyright © 2025 AlloVoisins. All rights reserved. +// + +import SwiftUI + +// MARK: - View Modifiers + +/// View modifiers for Sequoia TextField components. +/// These modifiers allow chainable configuration, similar to native SwiftUI components. +/// +/// Example usage: +/// ```swift +/// SQTextField("Name", text: $name) +/// .sqPlaceholder("Enter your name") +/// .sqMinCharacters(2) +/// .sqMaxCharacters(50) +/// .sqError($nameError) +/// ``` + +// MARK: - Individual Modifier Structs + +private struct SQPlaceholderModifier: ViewModifier { + let placeholder: String + @Environment(\.sqTextFieldConfiguration) var config + + func body(content: Content) -> some View { + var newConfig = config + newConfig.placeholder = placeholder + return content.environment(\.sqTextFieldConfiguration, newConfig) + } +} + +private struct SQMinCharactersModifier: ViewModifier { + let count: Int + @Environment(\.sqTextFieldConfiguration) var config + + func body(content: Content) -> some View { + var newConfig = config + newConfig.minCharacters = count + return content.environment(\.sqTextFieldConfiguration, newConfig) + } +} + +private struct SQMaxCharactersModifier: ViewModifier { + let count: Int + @Environment(\.sqTextFieldConfiguration) var config + + func body(content: Content) -> some View { + var newConfig = config + newConfig.maxCharacters = count + return content.environment(\.sqTextFieldConfiguration, newConfig) + } +} + +private struct SQHelperTextModifier: ViewModifier { + let text: String + @Environment(\.sqTextFieldConfiguration) var config + + func body(content: Content) -> some View { + var newConfig = config + newConfig.helperText = text + return content.environment(\.sqTextFieldConfiguration, newConfig) + } +} + +private struct SQTooltipTextModifier: ViewModifier { + let text: String + @Environment(\.sqTextFieldConfiguration) var config + + func body(content: Content) -> some View { + var newConfig = config + newConfig.tooltipText = text + return content.environment(\.sqTextFieldConfiguration, newConfig) + } +} + +private struct SQErrorModifier: ViewModifier { + let error: Binding + @Environment(\.sqTextFieldConfiguration) var config + + func body(content: Content) -> some View { + var newConfig = config + newConfig.error = error + return content.environment(\.sqTextFieldConfiguration, newConfig) + } +} + +private struct SQOptionalModifier: ViewModifier { + let isOptional: Bool + @Environment(\.sqTextFieldConfiguration) var config + + func body(content: Content) -> some View { + var newConfig = config + newConfig.isOptional = isOptional + return content.environment(\.sqTextFieldConfiguration, newConfig) + } +} + +private struct SQShowCharacterCountModifier: ViewModifier { + let show: Bool + @Environment(\.sqTextFieldConfiguration) var config + + func body(content: Content) -> some View { + var newConfig = config + newConfig.showCharacterCount = show + return content.environment(\.sqTextFieldConfiguration, newConfig) + } +} + +private struct SQBackgroundColorModifier: ViewModifier { + let color: Color + @Environment(\.sqTextFieldConfiguration) var config + + func body(content: Content) -> some View { + var newConfig = config + newConfig.backgroundColor = color + return content.environment(\.sqTextFieldConfiguration, newConfig) + } +} + +private struct SQKeyboardTypeModifier: ViewModifier { + let type: UIKeyboardType + @Environment(\.sqTextFieldConfiguration) var config + + func body(content: Content) -> some View { + var newConfig = config + newConfig.keyboardType = type + return content.environment(\.sqTextFieldConfiguration, newConfig) + } +} + +private struct SQInfoActionModifier: ViewModifier { + let action: () -> Void + @Environment(\.sqTextFieldConfiguration) var config + + func body(content: Content) -> some View { + var newConfig = config + newConfig.infoAction = action + return content.environment(\.sqTextFieldConfiguration, newConfig) + } +} + +private struct SQDisabledModifier: ViewModifier { + let isDisabled: Bool + @Environment(\.sqTextFieldConfiguration) var config + + func body(content: Content) -> some View { + var newConfig = config + newConfig.isDisabled = isDisabled + return content.environment(\.sqTextFieldConfiguration, newConfig) + } +} + +private struct SQIsLoadingModifier: ViewModifier { + let isLoading: Binding + @Environment(\.sqTextFieldConfiguration) var config + + func body(content: Content) -> some View { + var newConfig = config + newConfig.isLoading = isLoading + return content.environment(\.sqTextFieldConfiguration, newConfig) + } +} + +// MARK: - Public Extension + +extension View { + /// Sets the placeholder text for the text field. + func sqPlaceholder(_ text: String) -> some View { + modifier(SQPlaceholderModifier(placeholder: text)) + } + + /// Sets the minimum number of characters required. + func sqMinCharacters(_ count: Int) -> some View { + modifier(SQMinCharactersModifier(count: count)) + } + + /// Sets the maximum number of characters allowed. + func sqMaxCharacters(_ count: Int) -> some View { + modifier(SQMaxCharactersModifier(count: count)) + } + + /// Sets helper text to display below the text field. + func sqHelperText(_ text: String) -> some View { + modifier(SQHelperTextModifier(text: text)) + } + + /// Sets tooltip text with an info icon. + func sqTooltipText(_ text: String) -> some View { + modifier(SQTooltipTextModifier(text: text)) + } + + /// Binds an error to the text field for validation display. + func sqError(_ error: Binding) -> some View { + modifier(SQErrorModifier(error: error)) + } + + /// Marks the field as optional with an "Optionnel" badge. + func sqOptional(_ isOptional: Bool = true) -> some View { + modifier(SQOptionalModifier(isOptional: isOptional)) + } + + /// Marks the field as optional with an "Optionnel" badge (alias for sqOptional). + func sqIsOptional(_ isOptional: Bool = true) -> some View { + modifier(SQOptionalModifier(isOptional: isOptional)) + } + + /// Controls the visibility of the character counter. + func sqShowCharacterCount(_ show: Bool = true) -> some View { + modifier(SQShowCharacterCountModifier(show: show)) + } + + /// Sets the background color of the text field. + func sqBackgroundColor(_ color: Color) -> some View { + modifier(SQBackgroundColorModifier(color: color)) + } + + /// Sets the keyboard type for the text field. + func sqKeyboardType(_ type: UIKeyboardType) -> some View { + modifier(SQKeyboardTypeModifier(type: type)) + } + + /// Sets an action to execute when the info icon is tapped. + func sqInfoAction(_ action: @escaping () -> Void) -> some View { + modifier(SQInfoActionModifier(action: action)) + } + + /// Disables the text field. + func sqDisabled(_ isDisabled: Bool = true) -> some View { + modifier(SQDisabledModifier(isDisabled: isDisabled)) + } + + /// Shows a loading indicator in the text field. + func sqIsLoading(_ isLoading: Binding) -> some View { + modifier(SQIsLoadingModifier(isLoading: isLoading)) + } +} diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQToast.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQToast.swift index db7093d..159ecec 100644 --- a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQToast.swift +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQToast.swift @@ -38,9 +38,12 @@ struct SQToast: View { HStack(spacing: 16) { VStack(alignment: .leading) { if !title.isEmpty { - SQText(title, font: .bold) + SQText(title) + .sqFont(.bold) } - SQText(content, size: 14, font: .demiBold) + SQText(content) + .sqSize(14) + .sqFont(.semiBold) } Spacer() if hasClose { diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTooltip.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTooltip.swift index 55272b5..cb95bd9 100644 --- a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTooltip.swift +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SQTooltip.swift @@ -21,7 +21,8 @@ struct SQTooltip: View { var body: some View { VStack(alignment: .leading, spacing: 0) { - SQText(text, textColor: .white) + SQText(text) + .sqColor(.white) .fixedSize(horizontal: false, vertical: true) .padding(Constants.padding) .background( diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SqSearchBar/SQSearchBar.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SqSearchBar/SQSearchBar.swift index b23c37e..2278d71 100644 --- a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SqSearchBar/SQSearchBar.swift +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SqSearchBar/SQSearchBar.swift @@ -16,7 +16,8 @@ struct SQSearchBar: View { SQIcon(.magnifying_glass, type: .solid) TextField("", text: $text) .placeholder(when: text.isEmpty) { - SQText(placeholder, textColor: .sqNeutral(50)) + SQText(placeholder) + .sqColor(.sqNeutral(50)) } .tint(Color.sqNeutral(100)) } @@ -29,16 +30,3 @@ struct SQSearchBar: View { #Preview { SQSearchBar(text: .constant(""), placeholder: "Rechercher") } - -extension View { - func placeholder( - when shouldShow: Bool, - alignment: Alignment = .leading, - @ViewBuilder placeholder: () -> Content - ) -> some View { - ZStack(alignment: alignment) { - placeholder().opacity(shouldShow ? 1 : 0) - self - } - } -} diff --git a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SqSearchBar/SQSearchBarButton.swift b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SqSearchBar/SQSearchBarButton.swift index 06f8cb7..fc827c5 100644 --- a/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SqSearchBar/SQSearchBarButton.swift +++ b/AlloVoisinsSwiftUI/Core/Views/Sequoia/Components/SqSearchBar/SQSearchBarButton.swift @@ -14,8 +14,8 @@ struct SQSearchBarButton: View { var body: some View { HStack(spacing: 16) { SQIcon(.magnifying_glass, type: .solid) - - SQText(placeholder, textColor: .sqNeutral(50)) + SQText(placeholder) + .sqColor(.sqNeutral(50)) } .frame(maxWidth: .infinity, alignment: .leading) .padding() diff --git a/AlloVoisinsSwiftUI/Features/Account/Views/Components/ChangeLoginView.swift b/AlloVoisinsSwiftUI/Features/Account/Views/Components/ChangeLoginView.swift new file mode 100644 index 0000000..2a2fc6a --- /dev/null +++ b/AlloVoisinsSwiftUI/Features/Account/Views/Components/ChangeLoginView.swift @@ -0,0 +1,39 @@ +// +// ChangeLoginView.swift +// AlloVoisinsSwiftUI +// +// Created by Victor on 23/10/2025. +// + +import SwiftUI + +enum ChangeLoginType { + case phone + case email +} + +struct ChangeLoginView: View { + let type: ChangeLoginType = .phone + @State var phone: String = "" + @State var email: String = "" + + var body: some View { + VStack(spacing: 32) { + switch type { + case .email: + SQTextField("Nouvelle adresse e-mail", text: $email) + .sqPlaceholder("Ex : pierredupont@domaine.com") + case .phone: + SQPhoneField("Nouveau numéro de téléphone", text: $phone) + .sqPlaceholder("Ex : 0600000009") + } + SQButton("Confirmer") {} + Spacer() + } + .padding() + } +} + +#Preview { + ChangeLoginView() +} diff --git a/AlloVoisinsSwiftUI/Features/Account/Views/Components/PasswordRecommendations.swift b/AlloVoisinsSwiftUI/Features/Account/Views/Components/PasswordRecommendations.swift new file mode 100644 index 0000000..1286660 --- /dev/null +++ b/AlloVoisinsSwiftUI/Features/Account/Views/Components/PasswordRecommendations.swift @@ -0,0 +1,53 @@ +// +// PasswordRecommendations.swift +// AlloVoisinsSwiftUI +// +// Created by Victor on 22/10/2025. +// + +import SwiftUI + +struct PasswordRecommendations: View { + var body: some View { + VStack(alignment: .leading, spacing: 2) { + ForEach(PasswordRequirements.allCases, id: \.self) { requirement in + if requirement == .minChar { + requirementView(for: requirement) + + SQText("Au moins 3 des 4 catégories suivantes :") + .padding(.top, 8) + .sqSize(12) + } else { + requirementView(for: requirement) + } + + } + } + .padding(8) + .frame(maxWidth: .infinity, alignment: .leading) + .background(Color(red: 0.95, green: 0.97, blue: 1)) + .cornerRadius(8) + } + + func requirementView(for requirement: PasswordRequirements, isValid: Bool = false) -> some View { + HStack(spacing: 4) { + SQIcon(.check, size: .xs, color: isValid ? .sqSemanticPositive : .sqNeutral(50)) + SQText(requirement.rawValue) + .sqSize(12) + .sqColor(isValid ? .sqNeutral(80) : .sqNeutral(50)) + } + } + + enum PasswordRequirements: String, CaseIterable { + case minChar = "8 caractères minimum" + case oneMaj = "Une lettre majuscule" + case oneMin = "Une lettre minuscule" + case oneNum = "Un chiffre" + case oneSpe = "Un caractère spécial" + } +} + +#Preview { + PasswordRecommendations() + .padding(16) +} diff --git a/AlloVoisinsSwiftUI/Features/Account/Views/ConfirmAuthView.swift b/AlloVoisinsSwiftUI/Features/Account/Views/ConfirmAuthView.swift new file mode 100644 index 0000000..5fd9104 --- /dev/null +++ b/AlloVoisinsSwiftUI/Features/Account/Views/ConfirmAuthView.swift @@ -0,0 +1,63 @@ +// +// ConfirmAuthView.swift +// AlloVoisinsSwiftUI +// +// Created by Victor on 22/10/2025. +// + +import SwiftUI + +struct ConfirmAuthView: View { + @State var password: String = "" + + var body: some View { + VStack(spacing: 16) { + VStack(alignment: .leading, spacing: 16) { + SQText("Par mesure de sécurité, nous vous demandons votre mot de passe avant de modifier votre numéro de téléphone.") + .sqFont(.semiBold) + SQPasswordField("Mot de passe", text: $password) + } + SQButton("Confirmer") { + + } + HStack { + Rectangle() + .frame(height: 1) + .foregroundStyle(Color.sqNeutral(20)) + SQText("ou") + .sqColor(.sqNeutral(70)) + Rectangle() + .frame(height: 1) + .foregroundStyle(Color.sqNeutral(20)) + } + + SQButton("Continuer avec Apple") { + + } + .sqButtonType(.line) + .sqIcon(SQIcon(.apple_brand)) + .sqLarge() + + SQButton("Continuer avec Google") { + + } + .sqButtonType(.line) + .sqIcon(SQIcon(.google_brand)) + .sqLarge() + + SQButton("Continuer avec Facebook") { + + } + .sqButtonType(.line) + .sqIcon(SQIcon(.facebook_f_brand)) + .sqLarge() + + Spacer() + } + .padding() + } +} + +#Preview { + ChangeLoginView() +} diff --git a/AlloVoisinsSwiftUI/Features/Account/Views/LoginAndPasswordView.swift b/AlloVoisinsSwiftUI/Features/Account/Views/LoginAndPasswordView.swift new file mode 100644 index 0000000..52c6d5d --- /dev/null +++ b/AlloVoisinsSwiftUI/Features/Account/Views/LoginAndPasswordView.swift @@ -0,0 +1,108 @@ +// +// LoginAndPasswordView.swift +// AlloVoisinsSwiftUI +// +// Created by Victor on 21/10/2025. +// + +import SwiftUI + +struct LoginAndPasswordView: View { + @State var email: String = "" + @State var phone: String = "" + @State var currentPassword: String = "" + @State var newPassword: String = "" + @State var newPasswordRepeat: String = "" + + var body: some View { + ScrollView { + VStack(spacing: 0) { + VStack(alignment: .leading, spacing: 16) { + VStack(alignment: .leading, spacing: 8) { + SQText("Identifiants") + .sqSize(18) + .sqFont(.bold) + SQText("Vos identifiants ne sont pas communiqués aux autres membres.") + .sqFont(.semiBold) + } + VStack(spacing: 4) { + SQTextField("Adresse e-mail", text: $email) + .sqPlaceholder("pie**@d**********.com") + .sqDisabled(true) + HStack { + Spacer() + Button { + + } label: { + SQText("Modifier") + .sqSize(13) + .underline() + } + } + } + VStack(spacing: 4) { + SQTextField("Numéro de téléphone", text: $phone) + .sqPlaceholder("06******09") + .sqDisabled(true) + HStack { + Spacer() + Button { + + } label: { + SQText("Modifier") + .sqSize(13) + .underline() + } + } + } + } + .padding(.horizontal) + .padding(.bottom, 16) + + Rectangle() + .frame(height: 16) + .foregroundColor(Color.sqNeutral(10)) + + VStack(alignment: .center, spacing: 16) { + VStack(alignment: .leading, spacing: 16) { + SQText("Modifier mon mot de passe") + .sqSize(18) + .sqFont(.bold) + + SQPasswordField("Mot de passe actuel", text: $currentPassword) + .sqPlaceholder("*********") + SQPasswordField("Nouveau mot de passe", text: $newPassword) + .sqPlaceholder("*********") + PasswordRecommendations() + SQPasswordField("Confirmez votre mot de passe", text: $newPasswordRepeat) + .sqPlaceholder("*********") + } + + VStack(alignment: .center, spacing: 16) { + SQButton("Modifier mon mot de passe") { + + } + .sqButtonType(.line) + + Button { + + } label: { + SQText("Mot de passe oublié") + .sqSize(13) + .sqFont(.semiBold) + .underline() + } + } + .padding(.top, 16) + .padding(.horizontal) + } + .padding() + } + } + .sqNavigationBar(title: "Identifiants et mot de passe") + } +} + +#Preview { + LoginAndPasswordView() +} diff --git a/AlloVoisinsSwiftUI/Features/Booster/Views/BoosterConfirmationScreen.swift b/AlloVoisinsSwiftUI/Features/Booster/Views/BoosterConfirmationScreen.swift index 8bf62e9..756afc4 100644 --- a/AlloVoisinsSwiftUI/Features/Booster/Views/BoosterConfirmationScreen.swift +++ b/AlloVoisinsSwiftUI/Features/Booster/Views/BoosterConfirmationScreen.swift @@ -18,9 +18,13 @@ struct BoosterConfirmationScreen: View { Image("booster_logo") .resizable() .frame(width: 210, height: 180) - SQText("C’est confirmé !", size: 18, font: .bold) + SQText("C’est confirmé !") + .sqSize(18) + .sqFont(.bold) .foregroundColor(.white) - SQText("Votre profil sera boosté dès demain.", size: 32, font: .bold) + SQText("Votre profil sera boosté dès demain.") + .sqSize(32) + .sqFont(.bold) .foregroundColor(.white) .multilineTextAlignment(.center) }.padding() @@ -29,7 +33,8 @@ struct BoosterConfirmationScreen: View { SQButton("Accéder à mon option") { } - SQText("XX,XX € / mois, sans engagement.", size: 13) + SQText("XX,XX € / mois, sans engagement.") + .sqSize(13) .foregroundColor(.white) } } diff --git a/AlloVoisinsSwiftUI/Features/Booster/Views/BoosterKnowAboutScreen.swift b/AlloVoisinsSwiftUI/Features/Booster/Views/BoosterKnowAboutScreen.swift index 21e68d3..0bdbebb 100644 --- a/AlloVoisinsSwiftUI/Features/Booster/Views/BoosterKnowAboutScreen.swift +++ b/AlloVoisinsSwiftUI/Features/Booster/Views/BoosterKnowAboutScreen.swift @@ -13,30 +13,40 @@ struct BoosterKnowAboutScreen: View { ScrollView(showsIndicators: false) { VStack(spacing: 16) { VStack(alignment: .leading, spacing: 8) { - SQText("Comment ça marche ?", size: 18, font: .bold) + SQText("Comment ça marche ?") + .sqSize(18) + .sqFont(.bold) SQText("L’option Booster s’active sur les catégories de votre abonnement Premier. Par exemple, vous êtes abonné Premier sur les catégories « Plomberie - Installation Sanitaire » et « Carrelage », votre profil remontera en tête de liste des résultats en lien avec la plomberie et le carrelage.") } VStack(alignment: .leading, spacing: 8) { - SQText("Vous pouvez choisir de remonter votre profil :", size: 18, font: .bold) + SQText("Vous pouvez choisir de remonter votre profil :") + .sqSize(18) + .sqFont(.bold) VStack(alignment: .leading, spacing: 16) { SQText("• Tous les jours\n• 3 jours par semaine\n• 1 jour par semaine") - SQText("Tous les jours", font: .demiBold) + SQText("Tous les jours") + .sqFont(.semiBold) VStack(spacing: 8) { SQText("Cette option permet de remonter votre profil chaque jour dans les listes de résultats, à compter du lendemain de la souscription à l’option Booster, jusqu’à la résiliation de votre option.") - SQText("Exemple : vous souscrivez un dimanche à 15h00, votre option sera activée dès lundi à 00h00.", font: .mediumItalic) + SQText("Exemple : vous souscrivez un dimanche à 15h00, votre option sera activée dès lundi à 00h00.") + .sqFont(.mediumItalic) } .padding(.leading) - SQText("3 jours par semaine", font: .demiBold) + SQText("3 jours par semaine") + .sqFont(.semiBold) VStack(spacing: 8) { SQText("Cette option permet de remonter votre profil 3 jours par semaine dans les listes de résultats, à compter du lendemain de la souscription à l’option Booster, jusqu’à la résiliation de votre option.") - SQText("Exemple : vous souscrivez un dimanche à 15h00, votre profil sera boosté le lundi, puis tous les deux jours.", font: .mediumItalic) + SQText("Exemple : vous souscrivez un dimanche à 15h00, votre profil sera boosté le lundi, puis tous les deux jours.") + .sqFont(.mediumItalic) } .padding(.leading) - SQText("1 jour par semaine", font: .demiBold) + SQText("1 jour par semaine") + .sqFont(.semiBold) VStack(spacing: 8) { SQText("Cette option permet de remonter votre profil 1 jour par semaine dans les listes de résultats, à compter du lendemain de la souscription à l’option Booster, jusqu’à la résiliation de votre option.") - SQText("Exemple : vous souscrivez un dimanche à 15h00, votre profil sera boosté le lundi, puis tous les six jours.", font: .mediumItalic) + SQText("Exemple : vous souscrivez un dimanche à 15h00, votre profil sera boosté le lundi, puis tous les six jours.") + .sqFont(.mediumItalic) } .padding(.leading) } @@ -52,7 +62,9 @@ struct BoosterKnowAboutScreen: View { }) } ToolbarItem(placement: .principal) { - SQText("En savoir plus", size: 18, font: .bold) + SQText("En savoir plus") + .sqSize(18) + .sqFont(.bold) } } .padding(.horizontal) diff --git a/AlloVoisinsSwiftUI/Features/Booster/Views/BoosterSubscriptionManagementScreen.swift b/AlloVoisinsSwiftUI/Features/Booster/Views/BoosterSubscriptionManagementScreen.swift index e6aa01b..c5700cc 100644 --- a/AlloVoisinsSwiftUI/Features/Booster/Views/BoosterSubscriptionManagementScreen.swift +++ b/AlloVoisinsSwiftUI/Features/Booster/Views/BoosterSubscriptionManagementScreen.swift @@ -17,8 +17,10 @@ struct BoosterSubscriptionManagementScreen: View { VStack(alignment: .leading, spacing: 16) { BoosterActiveHeaderView() - SQText("Mes boosters", size: 20, font: .bold) - + SQText("Mes boosters") + .sqSize(20) + .sqFont(.bold) + Picker("", selection: $selectedValue) { SQText("À venir").tag(0) SQText("Passées").tag(1) diff --git a/AlloVoisinsSwiftUI/Features/Booster/Views/BoosterSubscriptionSelectionScreen.swift b/AlloVoisinsSwiftUI/Features/Booster/Views/BoosterSubscriptionSelectionScreen.swift index 8a28ab8..1491338 100644 --- a/AlloVoisinsSwiftUI/Features/Booster/Views/BoosterSubscriptionSelectionScreen.swift +++ b/AlloVoisinsSwiftUI/Features/Booster/Views/BoosterSubscriptionSelectionScreen.swift @@ -82,7 +82,8 @@ struct BoosterSubscriptionSelectionScreen: View { SQButton("C'est parti !") {} if cancellation { Button(action: /*@START_MENU_TOKEN@*/ {}/*@END_MENU_TOKEN@*/, label: { - SQText("Non merci, je souhaite résilier", size: 13) + SQText("Non merci, je souhaite résilier") + .sqSize(12) .foregroundColor(.black) }) .padding() diff --git a/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterActiveHeaderView.swift b/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterActiveHeaderView.swift index 0a02d09..dd94fa5 100644 --- a/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterActiveHeaderView.swift +++ b/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterActiveHeaderView.swift @@ -15,9 +15,12 @@ struct BoosterActiveHeaderView: View { .resizable() .frame(width: 93, height: 80) VStack { - SQText("Booster en cours", size: 18, font: .bold) + SQText("Booster en cours") + .sqSize(18) + .sqFont(.bold) .foregroundColor(.sqRoyal()) - SQText("Aujourd’hui", size: 14) + SQText("Aujourd’hui") + .sqSize(14) } } .padding() diff --git a/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterFeaturesView.swift b/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterFeaturesView.swift index 716b0e5..42188e1 100644 --- a/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterFeaturesView.swift +++ b/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterFeaturesView.swift @@ -10,25 +10,31 @@ import SwiftUI struct BoosterFeaturesView: View { var body: some View { VStack(alignment: .center, spacing: 24) { - SQText("Boostez votre activité !", size: 32, font: .bold) + SQText("Boostez votre activité !") + .sqSize(32) + .sqFont(.bold) .foregroundColor(.white) VStack(alignment: .center) { VStack(alignment: .leading) { - SQText("Votre profil est mis en avant:", font: .bold) + SQText("Votre profil est mis en avant:") + .sqFont(.bold) .foregroundColor(.white) HStack { SQIcon(.check, type: .solid, color: .white) - SQText("Affichage en tête de liste des résultats", font: .bold) + SQText("Affichage en tête de liste des résultats") + .sqFont(.bold) .foregroundColor(.white) } HStack { SQIcon(.check, type: .solid, color: .white) - SQText("Badge “Profil boosté” à chaque affichage de votre profil", font: .bold) + SQText("Badge “Profil boosté” à chaque affichage de votre profil") + .sqFont(.bold) .foregroundColor(.white) } HStack { SQIcon(.check, type: .solid, color: .white) - SQText("Référencement boosté sur les moteurs de recherche", font: .bold) + SQText("Référencement boosté sur les moteurs de recherche") + .sqFont(.bold) .foregroundColor(.white) } } diff --git a/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterHistoryCellView.swift b/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterHistoryCellView.swift index 36659a9..eaf60d1 100644 --- a/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterHistoryCellView.swift +++ b/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterHistoryCellView.swift @@ -14,7 +14,8 @@ struct BoosterHistoryCellView: View { ZStack { HStack(alignment: .center) { SQIcon(.calendar, color: .sqRoyal()) - SQText("Samedi 20 avril", font: .bold) + SQText("Samedi 20 avril") + .sqFont(.bold) .foregroundColor(.sqRoyal()) } .frame(maxWidth: .infinity) diff --git a/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterLockedToPremierView.swift b/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterLockedToPremierView.swift index bae6845..6108f6f 100644 --- a/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterLockedToPremierView.swift +++ b/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterLockedToPremierView.swift @@ -12,7 +12,8 @@ struct BoosterLockedToPremierView: View { var body: some View { HStack { SQIcon(.lock_keyhole, size: .xs, type: .solid, color: .white) - SQText("Abonnés Premier", size: 13) + SQText("Abonnés Premier") + .sqSize(13) .foregroundColor(.white) } } diff --git a/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterPromotionView.swift b/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterPromotionView.swift index 87d519c..382d54d 100644 --- a/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterPromotionView.swift +++ b/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterPromotionView.swift @@ -12,15 +12,17 @@ struct BoosterPromotionView: View { VStack(spacing: 0) { VStack(spacing: 16) { VStack { - SQText("Envie de booster votre activité ?", font: .bold) - SQText("Activez l’option Booster !", font: .demiBold) + SQText("Envie de booster votre activité ?") + .sqFont(.bold) + SQText("Activez l’option Booster !") + .sqFont(.semiBold) } BoosterStatsView() SQButton("Activer l’option Booster") { } - .colorScheme(.royal) + .sqColorScheme(.royal) } .padding() .foregroundColor(.sqRoyal()) diff --git a/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterSelectionView.swift b/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterSelectionView.swift index 64eeb40..06f6392 100644 --- a/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterSelectionView.swift +++ b/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterSelectionView.swift @@ -26,7 +26,8 @@ struct BoosterSelectionView: View { } } } - SQText("* 1 mois gratuit valable uniquement sur la formule “3 jours par semaine.”", size: 14) + SQText("* 1 mois gratuit valable uniquement sur la formule “3 jours par semaine.”") + .sqSize(14) .multilineTextAlignment(.center) .foregroundColor(.sqRoyal()) } diff --git a/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterStatsView.swift b/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterStatsView.swift index 35a4167..8e52ddd 100644 --- a/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterStatsView.swift +++ b/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterStatsView.swift @@ -14,10 +14,16 @@ struct BoosterStatsView: View { HStack(alignment: .top, spacing: 8) { VStack { HStack(spacing: 0) { - SQText("x", size: 24, font: .bold) - SQText("3", size: 32, font: .bold) + SQText("x") + .sqSize(24) + .sqFont(.bold) + SQText("3") + .sqSize(32) + .sqFont(.bold) } - SQText("Demandes privées*", size: 14, font: .demiBold) + SQText("Demandes privées*") + .sqSize(14) + .sqFont(.semiBold) .lineLimit(2) .multilineTextAlignment(.center) } @@ -29,10 +35,16 @@ struct BoosterStatsView: View { .frame(height: 72) VStack { HStack(spacing: 0) { - SQText("x", size: 24, font: .bold) - SQText("3", size: 32, font: .bold) + SQText("x") + .sqSize(24) + .sqFont(.bold) + SQText("3") + .sqSize(32) + .sqFont(.bold) } - SQText("Évaluations*", size: 14, font: .demiBold) + SQText("Évaluations*") + .sqSize(14) + .sqFont(.semiBold) .lineLimit(2) .multilineTextAlignment(.center) } @@ -44,17 +56,24 @@ struct BoosterStatsView: View { .frame(height: 72) VStack { HStack(spacing: 0) { - SQText("x", size: 24, font: .bold) - SQText("2.9", size: 32, font: .bold) + SQText("x") + .sqSize(24) + .sqFont(.bold) + SQText("2.9") + .sqSize(32) + .sqFont(.bold) } - SQText("Mise en favori*", size: 14, font: .demiBold) + SQText("Mise en favori*") + .sqSize(14) + .sqFont(.semiBold) .lineLimit(2) .multilineTextAlignment(.center) } .frame(minWidth: 100, maxWidth: .infinity) } .foregroundColor(.sqRoyal()) - SQText("*Performances moyennes obtenues par les profils boostés, par rapport aux Abonnés Premier.", size: 11, font: .medium) + SQText("*Performances moyennes obtenues par les profils boostés, par rapport aux Abonnés Premier.") + .sqSize(11) .lineLimit(2) } .foregroundColor(.sqRoyal()) diff --git a/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterSubscriptionCardView.swift b/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterSubscriptionCardView.swift index 267c0c2..2beb0ac 100644 --- a/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterSubscriptionCardView.swift +++ b/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterSubscriptionCardView.swift @@ -27,16 +27,20 @@ struct BoosterSubscriptionCardView: View { var body: some View { VStack { if currentOption { - SQText("Option actuelle", font: .bold) + SQText("Option actuelle") + .sqFont(.bold) .foregroundColor(.sqRoyal()) } ZStack(alignment: .topLeading) { VStack { - SQText("3 jours", size: 32, font: .bold) + SQText("3 jours") + .sqSize(32) + .sqFont(.bold) .minimumScaleFactor(0.5) .lineLimit(1) .foregroundColor(.white) - SQText("par semaine", size: 14) + SQText("par semaine") + .sqSize(14) .minimumScaleFactor(0.5) .lineLimit(1) .foregroundColor(.white) @@ -47,7 +51,8 @@ struct BoosterSubscriptionCardView: View { // .minimumScaleFactor(0.5) // .lineLimit(1) // .foregroundColor(.white) - SQText("Sans engagement", size: 12) + SQText("Sans engagement") + .sqSize(12) .minimumScaleFactor(0.5) .lineLimit(1) .foregroundColor(.white) @@ -73,7 +78,8 @@ struct BoosterSubscriptionCardView: View { startPoint: .top, endPoint: .bottom)) ) if isFree { - SQText("1 mois gratuit *", font: .bold) + SQText("1 mois gratuit *") + .sqFont(.bold) .foregroundColor(.sqRoyal()) } } diff --git a/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterSubscriptionOptionsView.swift b/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterSubscriptionOptionsView.swift index d17a901..6054f41 100644 --- a/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterSubscriptionOptionsView.swift +++ b/AlloVoisinsSwiftUI/Features/Booster/Views/Components/BoosterSubscriptionOptionsView.swift @@ -12,7 +12,9 @@ struct BoosterSubscriptionOptionsView: View { var body: some View { VStack { HStack { - SQText("Booster", size: 18, font: .bold) + SQText("Booster") + .sqSize(18) + .sqFont(.bold) Spacer() SQIcon(.xmark, color: .white) } diff --git a/AlloVoisinsSwiftUI/Features/Compliments/ProfileComplimentListView.swift b/AlloVoisinsSwiftUI/Features/Compliments/ProfileComplimentListView.swift index d9e53ea..3740a40 100644 --- a/AlloVoisinsSwiftUI/Features/Compliments/ProfileComplimentListView.swift +++ b/AlloVoisinsSwiftUI/Features/Compliments/ProfileComplimentListView.swift @@ -22,7 +22,9 @@ struct ProfileComplimentListView: View { var body: some View { VStack(alignment: .leading) { - SQText("Compliments reçus", size: 18, font: .bold) + SQText("Compliments reçus") + .sqSize(18) + .sqFont(.bold) .padding(.horizontal) VStack(alignment: .center, spacing: 16) { VStack(alignment: .leading) { diff --git a/AlloVoisinsSwiftUI/Features/Compliments/Views/Components/ComplimentPillView.swift b/AlloVoisinsSwiftUI/Features/Compliments/Views/Components/ComplimentPillView.swift index c28d58d..9f1c8b6 100644 --- a/AlloVoisinsSwiftUI/Features/Compliments/Views/Components/ComplimentPillView.swift +++ b/AlloVoisinsSwiftUI/Features/Compliments/Views/Components/ComplimentPillView.swift @@ -17,7 +17,9 @@ struct ComplimentPillView: View { .frame(width: 32, height: 32, alignment: .center) .background(Color.sqGreen(10)) .cornerRadius(32) - SQText("Excellent rapport qualité/prix", size: 12, font: .demiBold) + SQText("Excellent rapport qualité/prix") + .sqSize(12) + .sqFont(.semiBold) } .padding(.leading, 1) .padding(.trailing, 16) diff --git a/AlloVoisinsSwiftUI/Features/Compliments/Views/Components/ComplimentView.swift b/AlloVoisinsSwiftUI/Features/Compliments/Views/Components/ComplimentView.swift index 6e46457..7f40552 100644 --- a/AlloVoisinsSwiftUI/Features/Compliments/Views/Components/ComplimentView.swift +++ b/AlloVoisinsSwiftUI/Features/Compliments/Views/Components/ComplimentView.swift @@ -17,7 +17,9 @@ struct ComplimentView: View { .frame(width: 56, height: 56, alignment: .center) .background(Color.sqGreen(10)) .cornerRadius(100) - SQText("Excellent rapport qualité/prix", size: 11, font: .demiBold) + SQText("Excellent rapport qualité/prix") + .sqSize(11) + .sqFont(.semiBold) .multilineTextAlignment(.center) .foregroundColor(Color.sqNeutral(90)) } @@ -25,7 +27,9 @@ struct ComplimentView: View { .frame(width: 87) HStack { - SQText("1", size: 11, font: .bold) + SQText("1") + .sqSize(11) + .sqFont(.bold) .foregroundColor(Color.sqNeutral()) .padding(.horizontal, 6) .frame(height: 16) diff --git a/AlloVoisinsSwiftUI/Features/DebugLand/Views/Components/CurrentDebugUser.swift b/AlloVoisinsSwiftUI/Features/DebugLand/Views/Components/CurrentDebugUser.swift index cf71085..e5f4690 100644 --- a/AlloVoisinsSwiftUI/Features/DebugLand/Views/Components/CurrentDebugUser.swift +++ b/AlloVoisinsSwiftUI/Features/DebugLand/Views/Components/CurrentDebugUser.swift @@ -14,7 +14,9 @@ struct CurrentDebugUser: View { VStack { SQText("Harbin Leduc") SQText("Torphy LLC") - SQText("Auto-entrepreneur", size: 12, font: .demiBold) + SQText("Auto-entrepreneur") + .sqSize(12) + .sqFont(.semiBold) .padding(.vertical, 4) .padding(.horizontal, 8) .background { @@ -24,7 +26,8 @@ struct CurrentDebugUser: View { } HStack { - SQText("UserID:", font: .demiBold) + SQText("UserID:") + .sqFont(.semiBold) Spacer() SQText("103135") Button {} label: { @@ -37,7 +40,8 @@ struct CurrentDebugUser: View { } } HStack { - SQText("Email:", font: .demiBold) + SQText("Email:") + .sqFont(.semiBold) Spacer() SQText("test@test.com") Button {} label: { @@ -50,7 +54,8 @@ struct CurrentDebugUser: View { } } HStack { - SQText("Téléphone:", font: .demiBold) + SQText("Téléphone:") + .sqFont(.semiBold) Spacer() SQText("0612345678") Button {} label: { diff --git a/AlloVoisinsSwiftUI/Features/DebugLand/Views/Components/DebugActionView.swift b/AlloVoisinsSwiftUI/Features/DebugLand/Views/Components/DebugActionView.swift new file mode 100644 index 0000000..8d87be0 --- /dev/null +++ b/AlloVoisinsSwiftUI/Features/DebugLand/Views/Components/DebugActionView.swift @@ -0,0 +1,31 @@ +// +// DebugActionView.swift +// AlloVoisinsSwiftUI +// +// Created by Victor on 14/08/2025. +// + +import SwiftUI + +struct DebugActionView: View { + let title: String + @State var isOn = false + + var body: some View { + VStack(spacing: 16) { + SQText(title) + .sqFont(.semiBold) + Toggle("", isOn: $isOn) + } + .labelsHidden() + .frame(maxWidth: .infinity, maxHeight: 128) + .background { + RoundedRectangle(cornerRadius: 10) + .fill(Color.sqNeutral(10)) + } + } +} + +#Preview { + DebugActionView(title: "Anglais") +} diff --git a/AlloVoisinsSwiftUI/Features/DebugLand/Views/ConfigPrestationSearchView.swift b/AlloVoisinsSwiftUI/Features/DebugLand/Views/ConfigPrestationSearchView.swift index e335076..9950ce0 100644 --- a/AlloVoisinsSwiftUI/Features/DebugLand/Views/ConfigPrestationSearchView.swift +++ b/AlloVoisinsSwiftUI/Features/DebugLand/Views/ConfigPrestationSearchView.swift @@ -23,16 +23,21 @@ struct ConfigPrestationSearchView: View { VStack { Spacer() VStack(spacing: 16) { - SQText("Configuration de la recherche des prestations", size: 18, font: .bold) + SQText("Configuration de la recherche des prestations") + .sqSize(18) + .sqFont(.bold) .multilineTextAlignment(.center) Divider() - SQText("Types à rechercher :", font: .demiBold) + SQText("Types à rechercher :") + .sqFont(.semiBold) SQPicker(selection: $selectedSearchType, options: searchTypes) Toggle(isOn: $showSuggested) { - SQText("Afficher une suggestion", font: .demiBold) + SQText("Afficher une suggestion") + .sqFont(.semiBold) } Toggle(isOn: $showAllCategories) { - SQText("Afficher \"Toutes les catégories\"", font: .demiBold) + SQText("Afficher \"Toutes les catégories\"") + .sqFont(.semiBold) } } .padding() diff --git a/AlloVoisinsSwiftUI/Features/DebugLand/Views/DebugLandView.swift b/AlloVoisinsSwiftUI/Features/DebugLand/Views/DebugLandView.swift index 2005786..3c5f196 100644 --- a/AlloVoisinsSwiftUI/Features/DebugLand/Views/DebugLandView.swift +++ b/AlloVoisinsSwiftUI/Features/DebugLand/Views/DebugLandView.swift @@ -43,9 +43,21 @@ struct DebugLandView: View { CurrentDebugUser() VStack { - SQText("Environnement :", font: .demiBold) - SQPicker(selection: $currentEnv, options: pickerOptions) + SQText("Environnement :") + .sqFont(.semiBold) + SQText("Prod") + .sqFont(.bold) + .padding(.vertical, 4) + .padding(.horizontal, 8) + .background { + RoundedRectangle(cornerRadius: 8) + .fill(Color.sqRed()) + } + SQButton("Changer d'environnement") { + + } } + .frame(maxWidth: .infinity) .padding() .background { RoundedRectangle(cornerRadius: 10) @@ -54,16 +66,19 @@ struct DebugLandView: View { .padding(.horizontal) VStack(alignment: .leading, spacing: 8) { - SQText("User ID :", font: .demiBold) + SQText("User ID :") + .sqFont(.semiBold) HStack(spacing: 8) { VStack { - SQTextField("Visiter le profil :", placeholder: "UserID", text: $userId) + SQTextField("Visiter le profil :", text: $userId) + .sqPlaceholder("UserID") SQButton("Visiter le profil") { } } VStack { - SQTextField("Se connecter sur :", placeholder: "UserID", text: $userId) + SQTextField("Se connecter sur :", text: $userId) + .sqPlaceholder("UserID") .keyboardType(.numberPad) SQButton("Se connecter") { @@ -77,11 +92,24 @@ struct DebugLandView: View { .fill(Color.sqNeutral(10)) } .padding(.horizontal) + + VStack { + LazyHGrid(rows: [ + GridItem.init(), + GridItem.init() + ]) { + DebugActionView(title: "") + DebugActionView(title: "") + DebugActionView(title: "") + DebugActionView(title: "") + } + } } } .toolbar { ToolbarItem(placement: .principal) { - SQText("🦄 Debug Land 🦄", font: .bold) + SQText("🦄 Debug Land 🦄") + .sqFont(.bold) } ToolbarItem(placement: .primaryAction) { @@ -89,6 +117,11 @@ struct DebugLandView: View { SQIcon(.user_group) } } + ToolbarItem(placement: .topBarTrailing) { + Button {} label: { + SQIcon(.ballot_check) + } + } } } } diff --git a/AlloVoisinsSwiftUI/Features/Identity/ViewModels/IdentityValidationNavigationManager.swift b/AlloVoisinsSwiftUI/Features/Identity/ViewModels/IdentityValidationNavigationManager.swift new file mode 100644 index 0000000..c9a12ad --- /dev/null +++ b/AlloVoisinsSwiftUI/Features/Identity/ViewModels/IdentityValidationNavigationManager.swift @@ -0,0 +1,20 @@ +// +// IdentityValidationNavigationManager.swift +// AlloVoisinsSwiftUI +// +// Created by Victor on 30/06/2025. +// + +import Foundation + +enum IdentityValidationRoute: NavigationRoute { + case informationsValidation + case livenessTutorial + case liveness + case livenessSucess + case livenessError + case identityDocumentsList + case identityDocumentsValidation +} + +class IdentityValidationNavigationManager: BaseNavigationManager {} diff --git a/AlloVoisinsSwiftUI/Features/Identity/ViewModels/IdentityValidationViewModel.swift b/AlloVoisinsSwiftUI/Features/Identity/ViewModels/IdentityValidationViewModel.swift new file mode 100644 index 0000000..a47c084 --- /dev/null +++ b/AlloVoisinsSwiftUI/Features/Identity/ViewModels/IdentityValidationViewModel.swift @@ -0,0 +1,13 @@ +// +// IdentityValidationViewModel.swift +// AlloVoisinsSwiftUI +// +// Created by Victor on 30/06/2025. +// + +import SwiftUI + +@MainActor +class IdentityValidationViewModel: ObservableObject { + @Published var isLoading: Bool = false +} diff --git a/AlloVoisinsSwiftUI/Features/Identity/Views/IdentityDocumentsValidationLoaderView.swift b/AlloVoisinsSwiftUI/Features/Identity/Views/IdentityDocumentsValidationLoaderView.swift new file mode 100644 index 0000000..e29a782 --- /dev/null +++ b/AlloVoisinsSwiftUI/Features/Identity/Views/IdentityDocumentsValidationLoaderView.swift @@ -0,0 +1,36 @@ +// +// IdentityDocumentsValidationLoaderView.swift +// AlloVoisinsSwiftUI +// +// Created by Victor on 01/07/2025. +// + +import Lottie +import SwiftUI + +struct IdentityDocumentsValidationLoaderView: View { + var body: some View { + VStack { + HStack { + SQProgressIndicator(.constant(true)) + SQProgressIndicator(.constant(false)) + SQProgressIndicator(.constant(false)) + } + Spacer() + VStack(spacing: 16) { + LottieView(animation: .named("av_loader")) + .looping() + .frame(width: 60, height: 60) + SQText("Vérification en cours...") + .sqSize(20) + .sqFont(.semiBold) + } + Spacer() + } + .padding(.horizontal) + } +} + +#Preview { + IdentityDocumentsValidationLoaderView() +} diff --git a/AlloVoisinsSwiftUI/Features/Identity/Views/IdentityDocumentsValidationResultView.swift b/AlloVoisinsSwiftUI/Features/Identity/Views/IdentityDocumentsValidationResultView.swift new file mode 100644 index 0000000..21f4bb7 --- /dev/null +++ b/AlloVoisinsSwiftUI/Features/Identity/Views/IdentityDocumentsValidationResultView.swift @@ -0,0 +1,74 @@ +// +// IdentityDocumentsValidationResultView.swift +// AlloVoisinsSwiftUI +// +// Created by Victor on 01/07/2025. +// + +import SwiftUI + +struct IdentityDocumentsValidationResultView: View { + var body: some View { + VStack(spacing: 32) { + HStack { + SQProgressIndicator(.constant(true)) + SQProgressIndicator(.constant(false)) + SQProgressIndicator(.constant(false)) + } + if true { + VStack(spacing: 32) { + VStack(spacing: 16) { + SQIcon(.xmark, customSize: 60, color: .sqSemanticRed) + SQText("Votre identité n’a pas été validée.") + .sqSize(20) + .sqFont(.semiBold) + } + VStack(alignment: .leading, spacing: 8) { + SQText("Assurez-vous que :") + .sqSize(18) + .sqFont(.bold) + HStack(alignment: .top) { + SQText("1.") + SQText("Le document est valable et en cours de validité.") + } + HStack(alignment: .top) { + SQText("2.") + SQText("Les informations renseignées sur le document transmis sont strictement identiques aux informations indiquées sur votre compte AlloVoisins.") + } + HStack(alignment: .top) { + SQText("3.") + SQText("Vous avez plus de 18 ans.") + } + HStack(alignment: .top) { + SQText("4.") + SQText("Le document vous appartient.") + } + } + } + } else { + Spacer() + VStack(spacing: 16) { + SQIcon(.check, customSize: 60, color: .sqSemanticGreen) + SQText("Votre identité a été vérifiée.") + .sqSize(20) + .sqFont(.semiBold) + } + } + Spacer() + if true { + SQButton("Recommencer") { + + } + } else { + SQButton("Terminer") { + + } + } + } + .padding(.horizontal) + } +} + +#Preview { + IdentityDocumentsValidationResultView() +} diff --git a/AlloVoisinsSwiftUI/Features/Identity/Views/IdentityInformationsValidationView.swift b/AlloVoisinsSwiftUI/Features/Identity/Views/IdentityInformationsValidationView.swift new file mode 100644 index 0000000..11672fc --- /dev/null +++ b/AlloVoisinsSwiftUI/Features/Identity/Views/IdentityInformationsValidationView.swift @@ -0,0 +1,52 @@ +// +// IdentityInformationsValidationView.swift +// AlloVoisinsSwiftUI +// +// Created by Victor on 30/06/2025. +// + +import SwiftUI + +struct IdentityInformationsValidationView: View { + var body: some View { + VStack(spacing: 16) { + VStack(alignment: .leading, spacing: 24) { + SQText("Les étapes de vérification d’identité :") + .sqSize(20) + .sqFont(.bold) + VStack(alignment: .leading, spacing: 8) { + SQText("1. Vous allez prendre une vidéo de votre visage") + SQText("2. Vous allez prendre une photo de votre pièce d’identité.") + SQText("3. Nous allons comparer la vidéo de votre visage et les informations de votre compte AlloVoisins avec les éléments figurant sur votre pièce d’identité.") + } + VStack(alignment: .leading, spacing: 8) { + HStack { + SQIcon(.circle_exclamation) + SQText("Vérifiez que :") + .sqFont(.semiBold) + } + SQText("Les informations renseignées sur le document d’identité transmis doivent être strictement identiques aux informations ci-dessous, indiquées sur votre compte AlloVoisins :") + VStack(alignment: .leading) { + SQText(" ‧ Prénom : Maëlys-Gaëlle") + SQText(" ‧ Nom : Martin") + SQText(" ‧ Date de naissance : 13/07/1990") + } + } + } + SQButton("Mettre à jour mes informations") { + + } + .sqButtonType(.line) + Spacer() + SQButton("Commencer") { + + } + } + .padding([.trailing, .leading, .top]) + .sqNavigationBar(title: "Vérification de mon identité") + } +} + +#Preview { + IdentityInformationsValidationView() +} diff --git a/AlloVoisinsSwiftUI/Features/Identity/Views/IdentityValidationNavigationView.swift b/AlloVoisinsSwiftUI/Features/Identity/Views/IdentityValidationNavigationView.swift new file mode 100644 index 0000000..62f0d9f --- /dev/null +++ b/AlloVoisinsSwiftUI/Features/Identity/Views/IdentityValidationNavigationView.swift @@ -0,0 +1,48 @@ +// +// IdentityValidationNavigationView.swift +// AlloVoisinsSwiftUI +// +// Created by Victor on 30/06/2025. +// + +import SwiftUI + +struct IdentityValidationNavigationView: View { + @ObservedObject var navigationManager: IdentityValidationNavigationManager + @ObservedObject var viewModel: IdentityValidationViewModel + + init(navigationManager: IdentityValidationNavigationManager) { + self.navigationManager = navigationManager + self.viewModel = .init() + } + + var body: some View { + NavigationStack(path: $navigationManager.navigationPath) { + IdentityInformationsValidationView() + .navigationDestination(for: IdentityValidationRoute.self) { route in + switch route { + case .livenessTutorial: + LivenessTutorialView() + case .liveness: + LivenessView() + case .livenessSucess: + LivenessView() + case .livenessError: + LivenessView() + case .informationsValidation: + IdentityInformationsValidationView() + case .identityDocumentsList: + IdentityInformationsValidationView() + case .identityDocumentsValidation: + IdentityInformationsValidationView() + } + } + } + .environmentObject(navigationManager) + .environmentObject(viewModel) + } +} + +#Preview { + IdentityValidationNavigationView(navigationManager: IdentityValidationNavigationManager()) +} diff --git a/AlloVoisinsSwiftUI/Features/Identity/Views/LivenessErrorView.swift b/AlloVoisinsSwiftUI/Features/Identity/Views/LivenessErrorView.swift new file mode 100644 index 0000000..bd02a36 --- /dev/null +++ b/AlloVoisinsSwiftUI/Features/Identity/Views/LivenessErrorView.swift @@ -0,0 +1,34 @@ +// +// LivenessErrorView.swift +// AlloVoisinsSwiftUI +// +// Created by Victor on 30/06/2025. +// + +import SwiftUI + +struct LivenessErrorView: View { + var body: some View { + VStack(spacing: 24) { + HStack { + SQProgressIndicator(.constant(true)) + SQProgressIndicator(.constant(false)) + SQProgressIndicator(.constant(false)) + } + SQIcon(.xmark, customSize: 60, color: .sqSemanticRed) + SQText("Nous n’avons pas réussi à détecter votre visage.") + .sqSize(20) + .sqFont(.semiBold) + SQText("Assurez-vous que les conditions d’éclairage sont bonnes et que votre téléphone est bien en face de vous.\nL’image ne doit pas être floue, coupée, ou présenter des reflets.") + Spacer() + SQButton("Recommencer") { + + } + } + .padding() + } +} + +#Preview { + LivenessErrorView() +} diff --git a/AlloVoisinsSwiftUI/Features/Identity/Views/LivenessSuccessView.swift b/AlloVoisinsSwiftUI/Features/Identity/Views/LivenessSuccessView.swift new file mode 100644 index 0000000..cf1433f --- /dev/null +++ b/AlloVoisinsSwiftUI/Features/Identity/Views/LivenessSuccessView.swift @@ -0,0 +1,53 @@ +// +// LivenessSuccessView.swift +// AlloVoisinsSwiftUI +// +// Created by Victor on 30/06/2025. +// + +import SwiftUI + +struct LivenessSuccessView: View { + var body: some View { + VStack(spacing: 24) { + HStack { + SQProgressIndicator(.constant(true)) + SQProgressIndicator(.constant(false)) + SQProgressIndicator(.constant(false)) + } + SQImage("lemonsieur", height: 365) + .clipShape(Ellipse()) + .overlay( + Ellipse() + .stroke(Color.gray, lineWidth: 0) + ) + + VStack(alignment: .leading) { + HStack { + SQIcon(.check, color: .sqSemanticPositive) + SQText("Votre visage est bien dans le cadre.") + Spacer() + } + HStack { + SQIcon(.check, color: .sqSemanticPositive) + SQText("L’image n’est pas floue et n’a pas de reflet.") + Spacer() + } + HStack { + SQIcon(.check, color: .sqSemanticPositive) + SQText("Vous ne portez pas d’accessoires couvrant.") + Spacer() + } + } + Spacer() + SQButton("Continuer") { + + } + } + .padding() + } +} + +#Preview { + LivenessSuccessView() +} diff --git a/AlloVoisinsSwiftUI/Features/Identity/Views/LivenessTutorialView.swift b/AlloVoisinsSwiftUI/Features/Identity/Views/LivenessTutorialView.swift new file mode 100644 index 0000000..55d41bc --- /dev/null +++ b/AlloVoisinsSwiftUI/Features/Identity/Views/LivenessTutorialView.swift @@ -0,0 +1,54 @@ +// +// LivenessTutorialView.swift +// AlloVoisinsSwiftUI +// +// Created by Victor on 30/06/2025. +// + +import SwiftUI + +struct LivenessTutorialView: View { + var body: some View { + VStack(spacing: 0) { + HStack { + SQProgressIndicator(.constant(true)) + SQProgressIndicator(.constant(false)) + SQProgressIndicator(.constant(false)) + } + VStack(spacing: 32) { + VStack(alignment: .leading, spacing: 8) { + SQText("Pour vérifier votre identité, vous allez devoir réaliser une courte vidéo de votre visage, nous permettant de valider que vous êtes une personne réelle.") + .sqFont(.semiBold) + SQText("Cette vidéo ne sera pas stockée par AlloVoisins.\nElle est éphémère et n’est utilisée que dans le cadre du processus de vérification d’identité.") + } + .padding(.vertical, 16) + VStack(alignment: .leading, spacing: 8) { + SQText("Pour garantir le bon déroulement de la vérification d’identité :") + .sqSize(18) + .sqFont(.bold) + HStack { + SQImage("portrait_reflection", height: 80) + SQText("Trouvez un endroit bien éclairé.\nUne bonne lumière est essentielle, mais évitez la lumière directe du soleil sur votre visage.") + } + HStack { + SQImage("portrait_accessories", height: 80) + SQText("Veuillez retirer vos lunettes de soleil, chapeau, masque ou tout autre accessoire. Ceux-ci rendent votre identification plus difficile.") + } + HStack { + SQImage("portrait_angle", height: 80) + SQText("Positionnez votre visage dans le cadre de l'écran et suivez les instructions.") + } + } + Spacer() + SQButton("Continuer") { + + } + } + } + .padding([.horizontal, .top]) + } +} + +#Preview { + LivenessTutorialView() +} diff --git a/AlloVoisinsSwiftUI/Features/Identity/Views/LivenessView.swift b/AlloVoisinsSwiftUI/Features/Identity/Views/LivenessView.swift new file mode 100644 index 0000000..5975a14 --- /dev/null +++ b/AlloVoisinsSwiftUI/Features/Identity/Views/LivenessView.swift @@ -0,0 +1,31 @@ +// +// LivenessView.swift +// AlloVoisinsSwiftUI +// +// Created by Victor on 30/06/2025. +// + +import SwiftUI + +struct LivenessView: View { + @Environment(\.presentationMode) var presentationMode + @State private var isPresentingLiveness = true + + var body: some View { +// FaceLivenessDetectorView( +// sessionID: "", +// region: "", +// isPresented: $isPresentingLiveness, +// onCompletion: { result in +// switch result { +// case .success: break +// case .failure(_): break +// } +// } +// ) + } +} + +#Preview { + LivenessView() +} diff --git a/AlloVoisinsSwiftUI/Features/KYC/Views/Components/DocumentPreviewView.swift b/AlloVoisinsSwiftUI/Features/KYC/Views/Components/DocumentPreviewView.swift index 1be984a..293525c 100644 --- a/AlloVoisinsSwiftUI/Features/KYC/Views/Components/DocumentPreviewView.swift +++ b/AlloVoisinsSwiftUI/Features/KYC/Views/Components/DocumentPreviewView.swift @@ -5,6 +5,7 @@ // Created by Victor on 16/12/2024. // +import Lottie import SwiftUI struct DocumentPreviewView: View { @@ -19,37 +20,35 @@ struct DocumentPreviewView: View { SQImage("visit_card_new", height: 160) } else { SQIcon(.file_pdf, size: .l, color: .sqNeutral(80)) - SQText("PDF", size: 16, font: .demiBold) + SQText("PDF") + .sqFont(.semiBold) } } .frame(width: 160, height: 160, alignment: .center) - .background(Color.sqNeutral(10)) .overlay( RoundedRectangle(cornerRadius: 8) .inset(by: 0.5) .stroke(Color.sqNeutral(20), lineWidth: 1) ) - Button { - - } label: { - SQIcon(.trash_can, size: .s, color: .white) + Button {} label: { + SQIcon(.xmark, size: .s, color: .white) .padding() .background { Circle() .foregroundColor(Color.sqSemanticRed) - .frame(height: 24) + .frame(height: 32) } + .padding(-16) } } - SQText(fileName, size: 13) + SQText(fileName) + .sqSize(13) } } } #Preview { - DocumentPreviewView(fileName: "le-nom-de-mon-fichier.pdf") { - - } + DocumentPreviewView(fileName: "le-nom-de-mon-fichier.pdf") {} } diff --git a/AlloVoisinsSwiftUI/Features/KYC/Views/Components/IdentityDocumentButton.swift b/AlloVoisinsSwiftUI/Features/KYC/Views/Components/IdentityDocumentButton.swift index 8c79229..5ad779d 100644 --- a/AlloVoisinsSwiftUI/Features/KYC/Views/Components/IdentityDocumentButton.swift +++ b/AlloVoisinsSwiftUI/Features/KYC/Views/Components/IdentityDocumentButton.swift @@ -67,7 +67,9 @@ struct IdentityDocumentButton: View { VStack(alignment: .leading) { SQText(type.title) if !type.subtitle.isEmpty { - SQText(type.subtitle, size: 13, textColor: .sqNeutral(70)) + SQText(type.subtitle) + .sqSize(13) + .sqColor(.sqNeutral(70)) } } Spacer() diff --git a/AlloVoisinsSwiftUI/Features/KYC/Views/Components/KYCDocumentButton.swift b/AlloVoisinsSwiftUI/Features/KYC/Views/Components/KYCDocumentButton.swift index 7871f57..55e3689 100644 --- a/AlloVoisinsSwiftUI/Features/KYC/Views/Components/KYCDocumentButton.swift +++ b/AlloVoisinsSwiftUI/Features/KYC/Views/Components/KYCDocumentButton.swift @@ -69,15 +69,20 @@ struct KYCDocumentButton: View { var body: some View { VStack(alignment: .leading) { HStack { - SQText(name, font: .bold) + SQText(name) + .sqFont(.bold) Spacer() HStack(spacing: 4) { - SQText(status.text, size: 13, font: .demiBold, textColor: status.color) + SQText(status.text) + .sqSize(13) + .sqFont(.semiBold) + .sqColor(status.color) status.icon } } HStack { - SQText(desc, size: 14) + SQText(desc) + .sqSize(14) Spacer() if [.missing, .expired, .refused].contains(status) { SQIcon(.chevron_right, size: .l) diff --git a/AlloVoisinsSwiftUI/Features/KYC/Views/Components/KYCInformationAlertView.swift b/AlloVoisinsSwiftUI/Features/KYC/Views/Components/KYCInformationAlertView.swift index 3f4155d..7c629d0 100644 --- a/AlloVoisinsSwiftUI/Features/KYC/Views/Components/KYCInformationAlertView.swift +++ b/AlloVoisinsSwiftUI/Features/KYC/Views/Components/KYCInformationAlertView.swift @@ -12,7 +12,10 @@ struct KYCInformationAlertView: View { HStack { SQIcon(.triangle_exclamation, size: .xl, color: .sqRed(80)) VStack(alignment: .leading) { - SQText("Mangopay facturera le montant de la vérification dès lors que vous aurez soumis un document à vérification.", size: 14, font: .demiBold, textColor: .sqRed(80)) + SQText("Mangopay facturera le montant de la vérification dès lors que vous aurez soumis un document à vérification.") + .sqSize(14) + .sqFont(.semiBold) + .sqColor(.sqRed(80)) } } .frame(maxWidth: .infinity) diff --git a/AlloVoisinsSwiftUI/Features/Marketing/Views/Cards/CardColorSelectionView.swift b/AlloVoisinsSwiftUI/Features/Marketing/Views/Cards/CardColorSelectionView.swift index 246e4c7..d359592 100644 --- a/AlloVoisinsSwiftUI/Features/Marketing/Views/Cards/CardColorSelectionView.swift +++ b/AlloVoisinsSwiftUI/Features/Marketing/Views/Cards/CardColorSelectionView.swift @@ -18,7 +18,9 @@ struct CardColorSelectionView: View { 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) + SQText("Aperçu") + .sqSize(18) + .sqFont(.bold) selectedTemplate.imageColorTemplate(isLight: selectedColor.isLight) .background(selectedColor) .frame(maxWidth: .infinity) @@ -28,7 +30,8 @@ struct CardColorSelectionView: View { SQText("Appliquer cette couleur sur mes prospectus, 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) + SQText("Nous vous recommandons d’appliquer la même couleur sur tous vos documents.") + .sqSize(12) } Spacer() } diff --git a/AlloVoisinsSwiftUI/Features/Marketing/Views/Cards/CardFormView.swift b/AlloVoisinsSwiftUI/Features/Marketing/Views/Cards/CardFormView.swift index 93b742c..eae5369 100644 --- a/AlloVoisinsSwiftUI/Features/Marketing/Views/Cards/CardFormView.swift +++ b/AlloVoisinsSwiftUI/Features/Marketing/Views/Cards/CardFormView.swift @@ -28,11 +28,14 @@ struct CardFormView: View { VStack { VStack(alignment: .leading, spacing: 16) { VStack(alignment: .leading) { - SQText("Informations", size: 24, font: .bold) + SQText("Informations") + .sqSize(24) + .sqFont(.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) + SQText("Image") + .sqFont(.semiBold) ZStack(alignment: .bottomTrailing) { Button(action: { showActionSheet = true }) { @@ -60,17 +63,25 @@ struct CardFormView: View { } .frame(maxWidth: .infinity) } - SQTextField("Titre", placeholder: "Ex : Pro Solutions", text: $title) - SQTextField("Sous-titre", placeholder: "Ex : Martin Dupont", text: $subtitle, isOptional: true) - SQTextField("Métier", placeholder: "Ex : Dépannage électroménager", text: $job, isOptional: true) + SQTextField("Titre", text: $title) + .sqPlaceholder("Ex : Pro Solutions") + SQTextField("Sous-titre", text: $subtitle) + .sqPlaceholder("Ex : Martin Dupont") + .sqOptional() + SQTextField("Métier", text: $job) + .sqPlaceholder("Ex : Dépannage électroménager") + .sqOptional() VStack(alignment: .leading, spacing: 0) { Toggle(isOn: $showRating) { - SQText("Afficher ma note AlloVoisins", font: .demiBold) + SQText("Afficher ma note AlloVoisins") + .sqFont(.semiBold) } .tint(Color.sqNeutral(100)) HStack(spacing: 4) { SQIcon(.star, type: .solid, color: .sqGold(50)) - SQText("4,7/5 sur", size: 12, font: .demiBold) + SQText("4,7/5 sur") + .sqSize(12) + .sqFont(.semiBold) SQImage("logo", height: 14) } } @@ -83,18 +94,20 @@ struct CardFormView: View { .foregroundColor(Color.sqNeutral(10)) VStack(alignment: .leading, spacing: 16) { - SQTextField("Numéro de téléphone", placeholder: "Ex : 06 12 34 56 78", text: $phoneNumber) - SQTextField("Adresse complète", placeholder: "Ex : 1 rue de la gare, 67000 Strasbourg", text: $address) + SQTextField("Numéro de téléphone", text: $phoneNumber) + .sqPlaceholder("Ex : 06 12 34 56 78") + SQTextField("Adresse complète", text: $address) + .sqPlaceholder("Ex : 1 rue de la gare, 67000 Strasbourg") } .padding() } } SQFooter { SQButton("Aperçu") {} - .icon(SQIcon(.eye, color: .sqNeutral(100))) - .buttonType(.line) + .sqIcon(SQIcon(.eye, color: .sqNeutral(100))) + .sqButtonType(.line) SQButton("Imprimer") {} - .icon(SQIcon(.print, color: .white)) + .sqIcon(SQIcon(.print, color: .white)) } } .confirmationDialog("Choisir une image", isPresented: $showActionSheet, actions: { diff --git a/AlloVoisinsSwiftUI/Features/Marketing/Views/Cards/CardPrintView.swift b/AlloVoisinsSwiftUI/Features/Marketing/Views/Cards/CardPrintView.swift index 42300ce..20c3f82 100644 --- a/AlloVoisinsSwiftUI/Features/Marketing/Views/Cards/CardPrintView.swift +++ b/AlloVoisinsSwiftUI/Features/Marketing/Views/Cards/CardPrintView.swift @@ -10,7 +10,8 @@ import SwiftUI struct CardPrintView: View { var body: some View { VStack(alignment: .leading, spacing: 16) { - SQText("Génération d’une planche au format A4 comprenant 8 cartes de visite.", font: .demiBold) + SQText("Génération d’une planche au format A4 comprenant 8 cartes de visite.") + .sqFont(.semiBold) Image("visit_card_print_template") .resizable() .scaledToFit() diff --git a/AlloVoisinsSwiftUI/Features/Marketing/Views/Flyers/FlyerColorSelectionView.swift b/AlloVoisinsSwiftUI/Features/Marketing/Views/Flyers/FlyerColorSelectionView.swift index d72a429..a80cd12 100644 --- a/AlloVoisinsSwiftUI/Features/Marketing/Views/Flyers/FlyerColorSelectionView.swift +++ b/AlloVoisinsSwiftUI/Features/Marketing/Views/Flyers/FlyerColorSelectionView.swift @@ -18,7 +18,9 @@ struct FlyerColorSelectionView: View { 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) + SQText("Aperçu") + .sqSize(18) + .sqFont(.bold) selectedTemplate.imageColorTemplate(isLight: selectedColor.isLight) .background(selectedColor) .frame(maxWidth: .infinity) @@ -28,7 +30,8 @@ struct FlyerColorSelectionView: View { 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) + SQText("Nous vous recommandons d’appliquer la même couleur sur tous vos documents.") + .sqSize(12) } Spacer() } diff --git a/AlloVoisinsSwiftUI/Features/Marketing/Views/Flyers/FlyerFormView.swift b/AlloVoisinsSwiftUI/Features/Marketing/Views/Flyers/FlyerFormView.swift index 1d2a74b..4257d80 100644 --- a/AlloVoisinsSwiftUI/Features/Marketing/Views/Flyers/FlyerFormView.swift +++ b/AlloVoisinsSwiftUI/Features/Marketing/Views/Flyers/FlyerFormView.swift @@ -30,11 +30,14 @@ struct FlyerFormView: View { VStack { VStack(alignment: .leading, spacing: 16) { VStack(alignment: .leading) { - SQText("Informations", size: 24, font: .bold) + SQText("Informations") + .sqSize(24) + .sqFont(.bold) SQText("Les modifications apportées sur votre prospectus ne seront pas reportées sur votre profil.") } VStack(alignment: .leading) { - SQText("Image", font: .demiBold) + SQText("Image") + .sqFont(.semiBold) ZStack(alignment: .bottomTrailing) { Button(action: { showActionSheet = true }) { @@ -62,17 +65,25 @@ struct FlyerFormView: View { } .frame(maxWidth: .infinity) } - SQTextField("Titre", placeholder: "Ex : Pro Solutions", text: $title) - SQTextField("Sous-titre", placeholder: "Ex : Martin Dupont", text: $subtitle, isOptional: true) - SQTextField("Métier", placeholder: "Ex : Dépannage électroménager", text: $job, isOptional: true) + SQTextField("Titre", text: $title) + .sqPlaceholder("Ex : Pro Solutions") + SQTextField("Sous-titre", text: $subtitle) + .sqPlaceholder("Ex : Martin Dupont") + .sqOptional() + SQTextField("Métier", text: $job) + .sqPlaceholder("Ex : Dépannage électroménager") + .sqOptional() VStack(alignment: .leading, spacing: 0) { Toggle(isOn: $showRating) { - SQText("Afficher ma note AlloVoisins", font: .demiBold) + SQText("Afficher ma note AlloVoisins") + .sqFont(.semiBold) } .tint(Color.sqNeutral(100)) HStack(spacing: 4) { SQIcon(.star, type: .solid, color: .sqGold(50)) - SQText("4,7/5 sur", size: 12, font: .demiBold) + SQText("4,7/5 sur") + .sqSize(12) + .sqFont(.semiBold) SQImage("logo", height: 14) } } @@ -87,11 +98,21 @@ struct FlyerFormView: View { 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", text: $job, isOptional: true) - SQTextField("Prestation 2", placeholder: "Ex : Réparation machine à laver", text: $job, isOptional: true) - SQTextField("Prestation 3", placeholder: "Ex : Réparation four", text: $job, isOptional: true) - SQTextField("Prestation 4", placeholder: "Ex : Réparation outillage", text: $job, isOptional: true) - SQTextField("Prestation 5", placeholder: "Ex : Dépannage électroménager", text: $job, isOptional: true) + SQTextField("Prestation 1", text: $job) + .sqPlaceholder("Ex : Réparation lave-vaisselle") + .sqOptional() + SQTextField("Prestation 2", text: $job) + .sqPlaceholder("Ex : Réparation machine à laver") + .sqOptional() + SQTextField("Prestation 3", text: $job) + .sqPlaceholder("Ex : Réparation four") + .sqOptional() + SQTextField("Prestation 4", text: $job) + .sqPlaceholder("Ex : Réparation outillage") + .sqOptional() + SQTextField("Prestation 5", text: $job) + .sqPlaceholder("Ex : Dépannage électroménager") + .sqOptional() } .padding() @@ -100,8 +121,10 @@ struct FlyerFormView: View { .foregroundColor(Color.sqNeutral(10)) VStack(alignment: .leading, spacing: 16) { - SQTextField("Numéro de téléphone", placeholder: "Ex : 06 12 34 56 78", text: $phoneNumber) - SQTextField("Adresse complète", placeholder: "Ex : 1 rue de la gare, 67000 Strasbourg", text: $address) + SQTextField("Numéro de téléphone", text: $phoneNumber) + .sqPlaceholder("Ex : 06 12 34 56 78") + SQTextField("Adresse complète", text: $address) + .sqPlaceholder("Ex : 1 rue de la gare, 67000 Strasbourg") } .padding() @@ -110,18 +133,36 @@ struct FlyerFormView: View { .foregroundColor(Color.sqNeutral(10)) VStack(alignment: .leading, spacing: 16) { - SQText("Mentions légales obligatoires", size: 18, font: .bold) + SQText("Mentions légales obligatoires") + .sqSize(18) + .sqFont(.bold) - SQTextField("Dénomination sociale", placeholder: "Ex : Pro solutions", text: $job, isOptional: true) - SQTextField("Adresse du siège social", placeholder: "Ex : 16 rue de la Redoute, 67500 Haguenau", text: $job, isOptional: true) + SQTextField("Dénomination sociale", text: $job) + .sqPlaceholder("Ex : Pro solutions") + .sqOptional() + SQTextField("Adresse du siège social", text: $job) + .sqPlaceholder("Ex : 16 rue de la Redoute, 67500 Haguenau") + .sqOptional() VStack(spacing: 0) { - SQTextField("Numéro SIRET", placeholder: "Ex : 12345678901234", 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 SIRET", text: $job) + .sqPlaceholder("Ex : 12345678901234") + .sqOptional() + SQText("Le numéro SIRET se compose de 14 chiffres : les 9 chiffres du SIREN + 5 chiffres propres à chaque établissement (NIC).") + .sqSize(12) + .sqColor(.sqNeutral(50)) } - SQTextField("Numéro de RCS", placeholder: "Ex : RCS STRASBOURG B 123456789", text: $job, isOptional: true) - SQTextField("Statut juridique", placeholder: "Ex : SARL", text: $job, isOptional: true) - SQTextField("Montant du capital social (€)", placeholder: "Ex : 1 000,00", text: $job, isOptional: true) - SQTextField("Autre champ relatif à votre activité", placeholder: "Ex : Pour votre santé, mangez 5 fruits et légumes par jour", text: $job, isOptional: true) + SQTextField("Numéro de RCS", text: $job) + .sqPlaceholder("Ex : RCS STRASBOURG B 123456789") + .sqOptional() + SQTextField("Statut juridique", text: $job) + .sqPlaceholder("Ex : SARL") + .sqOptional() + SQTextField("Montant du capital social (€)", text: $job) + .sqPlaceholder("Ex : 1 000,00") + .sqOptional() + SQTextField("Autre champ relatif à votre activité", text: $job) + .sqPlaceholder("Ex : Pour votre santé, mangez 5 fruits et légumes par jour") + .sqOptional() SQCheckbox("Je comprends que j’engage ma responsabilité sur l’exhaustivité et l’authenticité des informations renseignées ci-dessus.", isChecked: $authentConfirm, error: .constant(.none)) } .padding() @@ -129,14 +170,14 @@ struct FlyerFormView: View { } SQFooter { SQButton("Aperçu") {} - .icon(SQIcon(.eye, color: .sqNeutral(100))) - .buttonType(.line) + .sqIcon(SQIcon(.eye, color: .sqNeutral(100))) + .sqButtonType(.line) SQButton("Imprimer") { if authentConfirm == false { self.confirmIsInError = true } } - .icon(SQIcon(.print, color: .white)) + .sqIcon(SQIcon(.print, color: .white)) } } .confirmationDialog("Choisir une image", isPresented: $showActionSheet, actions: { diff --git a/AlloVoisinsSwiftUI/Features/Marketing/Views/MarketingSupportSelectionView.swift b/AlloVoisinsSwiftUI/Features/Marketing/Views/MarketingSupportSelectionView.swift index 7d3d0d2..e731fd6 100644 --- a/AlloVoisinsSwiftUI/Features/Marketing/Views/MarketingSupportSelectionView.swift +++ b/AlloVoisinsSwiftUI/Features/Marketing/Views/MarketingSupportSelectionView.swift @@ -20,7 +20,9 @@ struct MarketingSupportSelectionView: View { SQImage("visit_card_new", height: 200) .cornerRadius(8, corners: [.topLeft, .topRight]) HStack { - SQText("Mes cartes de visite", size: 18, font: .bold) + SQText("Mes cartes de visite") + .sqSize(18) + .sqFont(.bold) Spacer() SQImage("for_pro_icon", height: 13) } @@ -41,7 +43,9 @@ struct MarketingSupportSelectionView: View { SQImage("flyers", height: 200) .cornerRadius(8, corners: [.topLeft, .topRight]) HStack { - SQText("Mes prospetus", size: 18, font: .bold) + SQText("Mes prospetus") + .sqSize(18) + .sqFont(.bold) Spacer() SQImage("for_pro_icon", height: 13) } @@ -59,7 +63,9 @@ struct MarketingSupportSelectionView: View { SQImage("quotes", height: 200) .cornerRadius(8, corners: [.topLeft, .topRight]) HStack { - SQText("Mes prospetus", size: 18, font: .bold) + SQText("Mes prospetus") + .sqSize(18) + .sqFont(.bold) Spacer() SQImage("for_pro_icon", height: 13) } diff --git a/AlloVoisinsSwiftUI/Features/Messenger/Views/CreateMeetingView.swift b/AlloVoisinsSwiftUI/Features/Messenger/Views/CreateMeetingView.swift new file mode 100644 index 0000000..25df51e --- /dev/null +++ b/AlloVoisinsSwiftUI/Features/Messenger/Views/CreateMeetingView.swift @@ -0,0 +1,78 @@ +// +// CreateMeetingView.swift +// AlloVoisinsSwiftUI +// +// Created by Victor on 25/09/2025. +// + +import SwiftUI + +struct CreateMeetingView: View { + @State var date: Date = .init() + @State var selectedOption: Int? = nil + + var body: some View { + ScrollView { + VStack { + VStack(alignment: .leading) { + SQText("Les avantages de la prise de “rendez-vous” :") + .sqSize(14) + .sqFont(.bold) + VStack(alignment: .leading) { + HStack { + SQText(" . ") + SQText("Engagement mutuel") + } + HStack { + SQText(" . ") + SQText("Ajout à l’agenda personnel") + } + HStack { + SQText(" . ") + SQText("Rappels SMS avant le rendez-vous") + } + HStack { + SQText(" . ") + SQText("Lancement de la navigation GPS") + } + HStack { + SQText(" . ") + SQText("Rappel “Laisser un avis” après le rendez-vous") + } + } + } + .padding(16) + .frame(maxWidth: .infinity, alignment: .leading) + .background(Color.sqNeutral(10)) + .cornerRadius(8) + + VStack(alignment: .leading) { + SQDatePickerView("Date", placeholder: "") + HStack(spacing: 16) { + SQDatePickerView("Date", placeholder: "") + SQDatePickerView("Date", placeholder: "") + } + SQRadio(title: "Modalités du rendez-vous", options: [ + SQRadioOption(title: "Appel vocal ou vidéo"), + SQRadioOption(title: "Adresse de la demande"), + SQRadioOption(title: "Chez {display_name_offreur}", desc: "2 rue de Rivoli, 75004 Paris"), + SQRadioOption(title: "À une autre adresse"), + ], selectedIndex: $selectedOption) + SQTextEditor("Informations pratiques", placeholder: "Ex : Itinéraire, étage, digicode, interphone ...", text: .constant(""), isOptional: true, maxCharacters: 500) + SQCheckbox("Je comprends que l'adresse et les informations pratiques seront communiquées lorsque le rendez-vous sera accepté par {display_name_offreur}.", isChecked: .constant(true)) + } + SQButton("Proposer le rendez-vous") { + + } + } + .padding(.horizontal) + } + .sqNavigationBar(title: "Proposer un rendez-vous", style: .modal) + } +} + +#Preview { + NavigationView { + CreateMeetingView() + } +} diff --git a/AlloVoisinsSwiftUI/Features/Messenger/Views/MessagingBlurView.swift b/AlloVoisinsSwiftUI/Features/Messenger/Views/MessagingBlurView.swift new file mode 100644 index 0000000..95cfe1c --- /dev/null +++ b/AlloVoisinsSwiftUI/Features/Messenger/Views/MessagingBlurView.swift @@ -0,0 +1,40 @@ +// +// MessagingBlurView.swift +// AlloVoisinsSwiftUI +// +// Created by Victor on 23/07/2025. +// + +import SwiftUI + +struct MessagingBlurView: View { + var body: some View { + ZStack { + Image("messaging") + VStack(spacing: 4) { + Spacer() + SQIcon(.eye_slash, size: .xl, color: .white) + SQText("Par mesure de sécurité, nous avons suspendu le compte de cet utilisateur.") + .sqSize(18) + .sqFont(.semiBold) + .sqColor(.white) + .multilineTextAlignment(.center) + .padding(20) + SQButton("Afficher") {} + .sqButtonType(.line) + .sqColorScheme(.white) + Spacer() + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background( + Color.sqNeutral(100) + .opacity(0.5) + ) + .background(.ultraThinMaterial) + } + } +} + +#Preview { + MessagingBlurView() +} diff --git a/AlloVoisinsSwiftUI/Features/Neighbors/Views/Components/MoreNeighborsSelectedBadge.swift b/AlloVoisinsSwiftUI/Features/Neighbors/Views/Components/MoreNeighborsSelectedBadge.swift index 44ce600..534bc6a 100644 --- a/AlloVoisinsSwiftUI/Features/Neighbors/Views/Components/MoreNeighborsSelectedBadge.swift +++ b/AlloVoisinsSwiftUI/Features/Neighbors/Views/Components/MoreNeighborsSelectedBadge.swift @@ -12,7 +12,9 @@ struct MoreNeighborsSelectedBadge: View { var body: some View { HStack(alignment: .center, spacing: 10) { - SQText("+\(selectedNeighbors)", font: .bold, textColor: .sqNeutral(50)) + SQText("+\(selectedNeighbors)") + .sqFont(.bold) + .sqColor(.sqNeutral(50)) } .frame(width: 32, height: 32, alignment: .center) .background(Color.sqNeutral(20)) diff --git a/AlloVoisinsSwiftUI/Features/Neighbors/Views/Components/NeighborsSelectionFooter.swift b/AlloVoisinsSwiftUI/Features/Neighbors/Views/Components/NeighborsSelectionFooter.swift index 363dabf..404e337 100644 --- a/AlloVoisinsSwiftUI/Features/Neighbors/Views/Components/NeighborsSelectionFooter.swift +++ b/AlloVoisinsSwiftUI/Features/Neighbors/Views/Components/NeighborsSelectionFooter.swift @@ -26,7 +26,8 @@ struct NeighborsSelectionFooter: View { size: .l, isFull: false ) {} - SQText("Sélectionner", font: .demiBold) + SQText("Sélectionner") + .sqFont(.semiBold) } Spacer() SQCircleButton( @@ -51,7 +52,9 @@ struct NeighborsSelectionFooter: View { } VStack(alignment: .leading, spacing: 8) { - SQText("Nous vous recommandons de sélectionner 3 offreurs :", size: 13, font: .bold) + SQText("Nous vous recommandons de sélectionner 3 offreurs :") + .sqSize(13) + .sqFont(.bold) VStack(alignment: .leading, spacing: 8) { HStack(spacing: 8) { SQImage("neighbor_avatar", height: 32) diff --git a/AlloVoisinsSwiftUI/Features/Neighbors/Views/Components/NeihborsCard.swift b/AlloVoisinsSwiftUI/Features/Neighbors/Views/Components/NeihborsCard.swift index 0f7daa8..e914bb0 100644 --- a/AlloVoisinsSwiftUI/Features/Neighbors/Views/Components/NeihborsCard.swift +++ b/AlloVoisinsSwiftUI/Features/Neighbors/Views/Components/NeihborsCard.swift @@ -13,10 +13,17 @@ struct NeihborsCard: View { HStack(spacing: 8) { AVAvatar(model: AVAvatarModel(avatarString: "https://preprod.allovoisins.com//uploads/u/avatars/9/b/9/9b93e04d6d_4681618_l.jpg", onlineStatus: .online(), sizing: .L)) VStack(alignment: .leading) { - SQText("Julien L.", font: .demiBold) - SQText("Julien, Petits Jobs À Nantes", size: 12, font: .demiBold) - SQText("Nantes (Gloriette-Feydeaux) - 3,1 km", size: 12, textColor: .sqNeutral(80)) - SQText("En ligne", size: 12, textColor: .sqNeutral(80)) + SQText("Julien L.") + .sqFont(.semiBold) + SQText("Julien, Petits Jobs À Nantes") + .sqSize(12) + .sqFont(.semiBold) + SQText("Nantes (Gloriette-Feydeaux) - 3,1 km") + .sqSize(12) + .sqColor(.sqNeutral(80)) + SQText("En ligne") + .sqSize(12) + .sqColor(.sqNeutral(80)) } Spacer() } @@ -28,7 +35,8 @@ struct NeihborsCard: View { HStack { SQIcon(.star, type: .solid, color: .sqGold(50)) - SQText("4,5/5", font: .bold) + SQText("4,5/5") + .sqFont(.bold) SQText("(35 avis)") } } diff --git a/AlloVoisinsSwiftUI/Features/Prestations/Views/CategorySelectorView.swift b/AlloVoisinsSwiftUI/Features/Prestations/Views/CategorySelectorView.swift index 6f72e41..bda6863 100644 --- a/AlloVoisinsSwiftUI/Features/Prestations/Views/CategorySelectorView.swift +++ b/AlloVoisinsSwiftUI/Features/Prestations/Views/CategorySelectorView.swift @@ -14,7 +14,8 @@ struct CategorySelectorView: View { SQText("Zone de texte sur la partie haute de l’écran, pouvant être sur plusieurs lignes.") HStack(alignment: .top, spacing: 0) { VStack { - SQText("Services", font: .demiBold) + SQText("Services") + .sqFont(.semiBold) .frame(maxWidth: .infinity) Rectangle() .fill(Color.sqNeutral(100)) @@ -22,7 +23,8 @@ struct CategorySelectorView: View { .padding(.top, 1) } VStack { - SQText("Objets", font: .demiBold) + SQText("Objets") + .sqFont(.semiBold) .frame(maxWidth: .infinity) Rectangle() .fill(Color.sqNeutral(20)) diff --git a/AlloVoisinsSwiftUI/Features/Prestations/Views/Components/AllCategoriesCell.swift b/AlloVoisinsSwiftUI/Features/Prestations/Views/Components/AllCategoriesCell.swift index 228d668..5b2f47d 100644 --- a/AlloVoisinsSwiftUI/Features/Prestations/Views/Components/AllCategoriesCell.swift +++ b/AlloVoisinsSwiftUI/Features/Prestations/Views/Components/AllCategoriesCell.swift @@ -10,7 +10,8 @@ import SwiftUI struct AllCategoriesCell: View { var body: some View { HStack { - SQText("Voir toutes les catégories", font: .demiBold) + SQText("Voir toutes les catégories") + .sqFont(.semiBold) Spacer() SQIcon(.chevron_right) } diff --git a/AlloVoisinsSwiftUI/Features/Prestations/Views/Components/PrestationCell.swift b/AlloVoisinsSwiftUI/Features/Prestations/Views/Components/PrestationCell.swift index a402582..796d56a 100644 --- a/AlloVoisinsSwiftUI/Features/Prestations/Views/Components/PrestationCell.swift +++ b/AlloVoisinsSwiftUI/Features/Prestations/Views/Components/PrestationCell.swift @@ -13,7 +13,9 @@ struct PrestationCell: View { SQIcon(.magnifying_glass) VStack(alignment: .leading) { SQText("remplacement de vitre") - SQText("dans Menuiserie - Huisserie - Agencement", size: 12, textColor: .sqNeutral(60)) + SQText("dans Menuiserie - Huisserie - Agencement") + .sqSize(12) + .sqColor(.sqNeutral(60)) } } } diff --git a/AlloVoisinsSwiftUI/Features/Prestations/Views/Components/PrestationHistorySearchCell.swift b/AlloVoisinsSwiftUI/Features/Prestations/Views/Components/PrestationHistorySearchCell.swift index 1ca546f..c20ca4e 100644 --- a/AlloVoisinsSwiftUI/Features/Prestations/Views/Components/PrestationHistorySearchCell.swift +++ b/AlloVoisinsSwiftUI/Features/Prestations/Views/Components/PrestationHistorySearchCell.swift @@ -13,7 +13,9 @@ struct PrestationHistorySearchCell: View { SQIcon(.clock) VStack(alignment: .leading) { SQText("remplacement de vitre") - SQText("dans Menuiserie - Huisserie - Agencement", size: 12, textColor: .sqNeutral(60)) + SQText("dans Menuiserie - Huisserie - Agencement") + .sqSize(12) + .sqColor(.sqNeutral(60)) } Spacer() SQIcon(.xmark) diff --git a/AlloVoisinsSwiftUI/Features/Prestations/Views/PrestationSearchView.swift b/AlloVoisinsSwiftUI/Features/Prestations/Views/PrestationSearchView.swift index eb05f37..7697cde 100644 --- a/AlloVoisinsSwiftUI/Features/Prestations/Views/PrestationSearchView.swift +++ b/AlloVoisinsSwiftUI/Features/Prestations/Views/PrestationSearchView.swift @@ -35,14 +35,18 @@ struct PrestationSearchView: View { Divider() VStack(alignment: .leading, spacing: 16) { - SQText("Mes dernières recherches", size: 18, font: .bold) + SQText("Mes dernières recherches") + .sqSize(18) + .sqFont(.bold) ForEach(0..<3) { _ in PrestationHistorySearchCell() } } VStack(alignment: .leading, spacing: 16) { - SQText("Recherches les plus populaires", size: 18, font: .bold) + SQText("Recherches les plus populaires") + .sqSize(18) + .sqFont(.bold) ForEach(0..<3) { _ in PrestationCell() } diff --git a/AlloVoisinsSwiftUI/Features/Profile/Views/AssurancesView.swift b/AlloVoisinsSwiftUI/Features/Profile/Views/AssurancesView.swift new file mode 100644 index 0000000..a8ed1aa --- /dev/null +++ b/AlloVoisinsSwiftUI/Features/Profile/Views/AssurancesView.swift @@ -0,0 +1,59 @@ +// +// AssurancesView.swift +// AlloVoisinsSwiftUI +// +// Created by Victor on 20/11/2025. +// + +import SwiftUI + +struct AssurancesView: View { + @State private var selectedOption = PickerOption(text: "Responsabilité Civile Professionnelle") + + private let options: [PickerOption] = [ + PickerOption(text: "Responsabilité Civile Professionnelle"), + PickerOption(text: "Garantie décennale"), + PickerOption(text: "Autre"), + ] + + var body: some View { + VStack(spacing: 16) { + SQText("Ajouter une assurance") + .sqFont(.bold) + .sqSize(18) + + VStack(alignment: .leading, spacing: 4) { + SQText("Type d’assurance") + .sqFont(.semiBold) + .sqColor(.sqNeutral(80)) + SQPicker(selection: $selectedOption, options: options) + } + + SQDatePickerView("Date d'expiration", placeholder: "JJ/MM/AAAA") + + if true { + DocumentPreviewView(fileName: "garantie-decennale.pdf") {} + } else { + SQButton("Importer une attestation") {} + .sqIcon(.init(.inbox_in)) + .sqButtonType(.line) + } + + SQText("Formats de fichier acceptés : jpg, pdf. Votre fichier ne doit pas dépasser un poids maximum de 7Mo.") + .sqSize(14) + .sqColor(.sqNeutral(80)) + + SQCheckbox("Je certifie l’exactitude des éléments renseignés et je m’engage à fournir mes attestations d’assurance à première demande.", isChecked: .constant(false), error: .constant(SQFormFieldError.none)) + .padding(.top, 16) + SQButton("Enregistrer") {} + Spacer() + } + .padding() + } +} + +#Preview { + NavigationView { + AssurancesView() + } +} diff --git a/AlloVoisinsSwiftUI/Features/Profile/Views/Components/ProfileAnswerToCell.swift b/AlloVoisinsSwiftUI/Features/Profile/Views/Components/ProfileAnswerToCell.swift new file mode 100644 index 0000000..c790d21 --- /dev/null +++ b/AlloVoisinsSwiftUI/Features/Profile/Views/Components/ProfileAnswerToCell.swift @@ -0,0 +1,66 @@ +// +// ProfileAnswerToCell.swift +// AlloVoisinsSwiftUI +// +// Created by Victor on 18/08/2025. +// + +import SwiftUI + +struct ProfileAnswerToCell: View { + var body: some View { + VStack(spacing: 8) { + VStack(alignment: .leading, spacing: 8) { + HStack { + SQText("Répond aux demandes de :") + .sqFont(.semiBold) + Spacer() + SQText("Abonné Premier") + .sqSize(13) + .sqFont(.semiBold) + .sqColor(.sqOrange(70)) + .padding(.horizontal, 8) + .padding(.vertical, 2) + .background(Color.sqOrange(20)) + .cornerRadius(4) + } + VStack(alignment: .leading, spacing: 4) { + HStack { + SQText("•") + SQText("Peinture - Tapisserie") + .sqColor(.sqNeutral(80)) + SQText("(43 réponses)") + .sqSize(13) + .sqColor(.sqNeutral(80)) + } + HStack { + SQText("•") + SQText("Aide à domicile") + .sqColor(.sqNeutral(80)) + SQText("(4 réponses)") + .sqSize(13) + .sqColor(.sqNeutral(80)) + } + HStack { + SQText("•") + SQText("Livraison de courses") + .sqColor(.sqNeutral(80)) + SQText("(2 réponses)") + .sqSize(13) + .sqColor(.sqNeutral(80)) + } + } + } + SQButton("Voir tout") { + + } + .sqTextSize(13) + .sqButtonType(.line) + } + } +} + +#Preview { + ProfileAnswerToCell() + .padding() +} diff --git a/AlloVoisinsSwiftUI/Features/Profile/Views/Components/ProfileDisponibilityCell.swift b/AlloVoisinsSwiftUI/Features/Profile/Views/Components/ProfileDisponibilityCell.swift new file mode 100644 index 0000000..61abeaf --- /dev/null +++ b/AlloVoisinsSwiftUI/Features/Profile/Views/Components/ProfileDisponibilityCell.swift @@ -0,0 +1,53 @@ +// +// ProfileDisponibilityCell.swift +// AlloVoisinsSwiftUI +// +// Created by Victor on 18/08/2025. +// + +import SwiftUI + +struct ProfileDisponibilityCell: View { + var body: some View { + VStack(alignment: .leading, spacing: 16) { + SQText("Informations pratiques") + .sqSize(18) + .sqFont(.semiBold) + VStack(alignment: .leading, spacing: 8) { + SQText("Informations tarifaires et promotionnelles") + .sqFont(.semiBold) + SQText("Mes devis sont toujours gratuits, et j’offre 5% à ma clientèle refaisant appel à moi pour leur futur projet.") + } + SQDivider() + VStack(alignment: .leading, spacing: 8) { + SQText("Disponiblités") + .sqFont(.semiBold) + HStack(spacing: 24) { + VStack(alignment: .leading, spacing: 8) { + SQText("Lundi :") + SQText("Mardi :") + SQText("Mercredi :") + SQText("Jeudi :") + SQText("Vendredi :") + SQText("Samedi :") + SQText("Dimanche :") + } + VStack(alignment: .leading, spacing: 8) { + SQText("Non renseigné") + SQText("Non renseigné") + SQText("Non renseigné") + SQText("Non renseigné") + SQText("Non renseigné") + SQText("Non renseigné") + SQText("Non renseigné") + } + } + } + } + } +} + +#Preview { + ProfileDisponibilityCell() + .padding() +} diff --git a/AlloVoisinsSwiftUI/Features/Profile/Views/Components/ProfileInformationsCell.swift b/AlloVoisinsSwiftUI/Features/Profile/Views/Components/ProfileInformationsCell.swift new file mode 100644 index 0000000..a4281b8 --- /dev/null +++ b/AlloVoisinsSwiftUI/Features/Profile/Views/Components/ProfileInformationsCell.swift @@ -0,0 +1,112 @@ +// +// ProfileInformationsCell.swift +// AlloVoisinsSwiftUI +// +// Created by Victor on 18/08/2025. +// + +import SwiftUI + +struct PopoverModel: Identifiable { + var id: String { message } + let message: String +} + +struct ProfileInformationsCell: View { + @State private var popover: PopoverModel? + + var body: some View { + VStack(alignment: .leading, spacing: 16) { + SQText("Informations administratives et vérifications") + .sqSize(18) + .sqFont(.semiBold) + VStack(alignment: .leading) { + SQText("Vérifications") + .sqFont(.semiBold) + HStack(spacing: 8) { + SQIcon(.circle_check, color: .sqGreen(50)) + SQText("Adresse e-mail") + } + HStack(spacing: 8) { + SQIcon(.circle_check, color: .sqGreen(50)) + SQText("Téléphone") + } + HStack(spacing: 8) { + SQIcon(.circle_check, color: .sqGreen(50)) + SQText("Identité") + } + HStack { + Spacer() + SQButton("Appeler") {} + .sqIcon(SQIcon(.phone)) + .sqButtonType(.line) + .sqTextSize(13) + Spacer() + } + } + SQDivider() + VStack(alignment: .leading, spacing: 8) { + SQText("Informations légales") + .sqFont(.semiBold) + HStack(alignment: .top) { + SQText("•") + SQText("Numéro SIRET : 869 322 037 - 0043") + } + HStack(alignment: .top) { + SQText("•") + SQText("Forme juridique : Société à responsabilité limitée (SARL)") + } + HStack(alignment: .top) { + SQText("•") + SQText("Entreprise créée en septembre 2022") + } + } + SQDivider() + VStack(alignment: .leading, spacing: 8) { + HStack { + SQText("Assurances") + .sqFont(.semiBold) + Button() { + popover = PopoverModel(message: "Cet utilisateur a renseigné ses attestations d’assurance et s’engage à vous les fournir à tout moment.") + } label: { + SQIcon(.circle_info) + } + .popover(item: $popover, arrowEdge: .bottom) { detail in + Text("\(detail.message)") + .padding() + } + } + VStack(alignment: .leading, spacing: 8) { + HStack(alignment: .top) { + SQText("•") + SQText("Responsabilité Civile Professionnelle") + } + HStack(alignment: .top) { + SQText("•") + SQText("Garantie décennale") + } + } + } + SQDivider() + VStack(alignment: .leading, spacing: 8) { + SQText("Certifications / Diplômes") + .sqFont(.semiBold) + VStack(alignment: .leading) { + SQText("BEP de plombier chauffagiste") + SQText("Date de délivrance : mars 2024") + .sqSize(12) + } + VStack(alignment: .leading) { + SQText("Attestation fluidique cat:1") + SQText("Date de délivrance : février 2020") + .sqSize(12) + } + } + } + } +} + +#Preview { + ProfileInformationsCell() + .padding() +} diff --git a/AlloVoisinsSwiftUI/Features/Profile/Views/Components/ProfilePresentationCell.swift b/AlloVoisinsSwiftUI/Features/Profile/Views/Components/ProfilePresentationCell.swift new file mode 100644 index 0000000..57b95e1 --- /dev/null +++ b/AlloVoisinsSwiftUI/Features/Profile/Views/Components/ProfilePresentationCell.swift @@ -0,0 +1,37 @@ +// +// ProfilePresentationCell.swift +// AlloVoisinsSwiftUI +// +// Created by Victor on 18/08/2025. +// + +import SwiftUI + +struct ProfilePresentationCell: View { + var presentationText: String + + var body: some View { + VStack(alignment: .leading) { + HStack { + SQText("« \(presentationText) »") + .sqSize(18) + .multilineTextAlignment(.leading) + Spacer() + } + .frame(maxWidth: .infinity) + .padding(16) + .background { + Rectangle() + .fill(Color.sqNeutral(10)) + } + .cornerRadius(8, corners: [.topLeft, .topRight, .bottomLeft]) + } + .padding(16) + .background(Color.white) + } +} + +#Preview { + ProfilePresentationCell(presentationText: "Hello zfopze greozj giorezjgogiijrzg iezjg ooijfzgio dfiozjg oirezj jeoizkg ok,ezkog, oeizg,o o,zojrezov oez,vov eziov ezio, ezoi,vio ez,vio,e ozv,oi,zirziii") + .padding() +} diff --git a/AlloVoisinsSwiftUI/Features/Profile/Views/Components/ProfileStatisticsCell.swift b/AlloVoisinsSwiftUI/Features/Profile/Views/Components/ProfileStatisticsCell.swift new file mode 100644 index 0000000..71f5663 --- /dev/null +++ b/AlloVoisinsSwiftUI/Features/Profile/Views/Components/ProfileStatisticsCell.swift @@ -0,0 +1,44 @@ +// +// ProfileStatisticsCell.swift +// AlloVoisinsSwiftUI +// +// Created by Victor on 18/08/2025. +// + +import SwiftUI + +struct ProfileStatisticsCell: View { + var body: some View { + HStack { + Spacer() + VStack(spacing: 4) { + HStack(spacing: 8) { + SQIcon(.user) + SQText("18 mars 2017") + .sqFont(.bold) + } + SQText("date d’inscription") + .sqSize(13) + } + Spacer() + SQDivider() + .frame(height: 55) + Spacer() + VStack(spacing: 4) { + HStack(spacing: 8) { + SQIcon(.users) + SQText("-") + .sqFont(.bold) + } + SQText("mises en relation") + .sqSize(13) + } + Spacer() + } + } +} + +#Preview { + ProfileStatisticsCell() + .padding() +} diff --git a/AlloVoisinsSwiftUI/Features/Profile/Views/WelcomeProModal.swift b/AlloVoisinsSwiftUI/Features/Profile/Views/WelcomeProModal.swift new file mode 100644 index 0000000..543d6f9 --- /dev/null +++ b/AlloVoisinsSwiftUI/Features/Profile/Views/WelcomeProModal.swift @@ -0,0 +1,58 @@ +// +// WelcomeProModal.swift +// AlloVoisinsSwiftUI +// +// Created by Victor on 13/10/2025. +// + +import SwiftUI + +struct WelcomeProModal: View { + var body: some View { + VStack(spacing: 48) { + VStack(spacing: 12) { + SQText("Bienvenue sur") + .sqSize(32) + .sqFont(.bold) + SQImage("logo-pro-color", height: 47) + } + HStack { + VStack(alignment: .leading, spacing: 16) { + SQText("Vous pouvez désormais :") + .sqSize(18) + .sqFont(.semiBold) + HStack(alignment: .top, spacing: 8) { + SQIcon(.check, color: .sqGrape()) + SQText("Répondre à toutes les demandes, dont celles réservées aux pros") + } + HStack(alignment: .top, spacing: 8) { + SQIcon(.check, color: .sqGrape()) + SQText("Créer vos supports de communication personnalisés (cartes de visite et prospectus)") + } + HStack(alignment: .top, spacing: 8) { + SQIcon(.check, color: .sqGrape()) + SQText("Utiliser notre logiciel de facturation") + } + } + Spacer() + } + .padding(16) + .cornerRadius(8) + .overlay( + RoundedRectangle(cornerRadius: 8) + .inset(by: 0.5) + .stroke(Color.sqNeutral(20), lineWidth: 1) + ) + + Spacer() + SQButton("C’est parti !") {} + .sqColorScheme(.grape) + } + .sqNavigationBar(title: "") + .padding(16) + } +} + +#Preview { + WelcomeProModal() +} diff --git a/AlloVoisinsSwiftUI/Features/Reporting/Views/ReportConfirmationView.swift b/AlloVoisinsSwiftUI/Features/Reporting/Views/ReportConfirmationView.swift index cf79bbb..3e2d37a 100644 --- a/AlloVoisinsSwiftUI/Features/Reporting/Views/ReportConfirmationView.swift +++ b/AlloVoisinsSwiftUI/Features/Reporting/Views/ReportConfirmationView.swift @@ -12,13 +12,17 @@ struct ReportConfirmationView: View { VStack(spacing: 16) { VStack { SQIcon(.check, customSize: 64, type: .solid, color: .sqSemanticGreen) - SQText("Votre signalement a bien été pris en compte.", size: 18, font: .bold) + SQText("Votre signalement a bien été pris en compte.") + .sqSize(18) + .sqFont(.bold) } VStack(alignment: .leading, spacing: 16) { - SQText("Merci de participer à améliorer la communauté. Nous nous efforçons de traiter votre signalement dans les plus brefs délais et nous vous informerons de la suite qui sera donnée.", font: .demiBold) + SQText("Merci de participer à améliorer la communauté. Nous nous efforçons de traiter votre signalement dans les plus brefs délais et nous vous informerons de la suite qui sera donnée.") + .sqFont(.semiBold) VStack(alignment: .leading, spacing: 4) { - SQText("Souhaitez-vous bloquer cet utilisateur ?", font: .demiBold) + SQText("Souhaitez-vous bloquer cet utilisateur ?") + .sqFont(.semiBold) HStack { Toggle(isOn: .constant(true)) { SQText("En bloquant cet utilisateur, il ne pourra plus vous contacter.") diff --git a/AlloVoisinsSwiftUI/Features/Reporting/Views/ReportFinalizationView.swift b/AlloVoisinsSwiftUI/Features/Reporting/Views/ReportFinalizationView.swift index 346352b..b7b7235 100644 --- a/AlloVoisinsSwiftUI/Features/Reporting/Views/ReportFinalizationView.swift +++ b/AlloVoisinsSwiftUI/Features/Reporting/Views/ReportFinalizationView.swift @@ -11,14 +11,17 @@ struct ReportFinalizationView: View { var body: some View { ScrollView { VStack(alignment: .leading, spacing: 16) { - SQText("Votre signalement", font: .bold) + SQText("Votre signalement") + .sqFont(.bold) HStack(alignment: .top) { - SQText("Motif :", font: .demiBold) + SQText("Motif :") + .sqFont(.semiBold) SQText("Image non conforme") } Divider() HStack(alignment: .top) { - SQText("Sous-motif :", font: .demiBold) + SQText("Sous-motif :") + .sqFont(.semiBold) SQText("Photo de profil ou couverture inappropriée") } Divider() @@ -30,7 +33,7 @@ struct ReportFinalizationView: View { SQCheckbox("Je certifie que toutes les informations renseignées sont exactes et complètes et que je les ai fournies en toute bonne foi.", isChecked: .constant(false), error: .constant(.none), alignment: .center) SQCheckbox("Je comprends que tout signalement abusif pourra faire l’objet de sanctions.", isChecked: .constant(true), error: .constant(.none), alignment: .center) SQButton("Signaler") {} - .colorScheme(.red) + .sqColorScheme(.red) } } } diff --git a/AlloVoisinsSwiftUI/Features/Resiliation/ViewModels/PromotionalScreenViewModel.swift b/AlloVoisinsSwiftUI/Features/Resiliation/ViewModels/PromotionalScreenViewModel.swift index 27d1069..e169afc 100644 --- a/AlloVoisinsSwiftUI/Features/Resiliation/ViewModels/PromotionalScreenViewModel.swift +++ b/AlloVoisinsSwiftUI/Features/Resiliation/ViewModels/PromotionalScreenViewModel.swift @@ -6,7 +6,7 @@ // import Foundation -import SwiftUICore +import SwiftUI class PromotionalScreenViewModel: ObservableObject { @Published var nextPromotionalScreen: ResiliationPromotionalScreen? diff --git a/AlloVoisinsSwiftUI/Features/Resiliation/ViewModels/ResiliationViewModel.swift b/AlloVoisinsSwiftUI/Features/Resiliation/ViewModels/ResiliationViewModel.swift index 6a52738..be62ec5 100644 --- a/AlloVoisinsSwiftUI/Features/Resiliation/ViewModels/ResiliationViewModel.swift +++ b/AlloVoisinsSwiftUI/Features/Resiliation/ViewModels/ResiliationViewModel.swift @@ -6,7 +6,7 @@ // import Foundation -import SwiftUICore +import SwiftUI class ResiliationViewModel: ObservableObject { @Published var resiliationType: ResiliationType diff --git a/AlloVoisinsSwiftUI/Features/Resiliation/Views/AlloVoisinReputationScreen.swift b/AlloVoisinsSwiftUI/Features/Resiliation/Views/AlloVoisinReputationScreen.swift index 3490336..37ac283 100644 --- a/AlloVoisinsSwiftUI/Features/Resiliation/Views/AlloVoisinReputationScreen.swift +++ b/AlloVoisinsSwiftUI/Features/Resiliation/Views/AlloVoisinReputationScreen.swift @@ -15,11 +15,17 @@ struct AlloVoisinReputationScreen: View { VStack(spacing: 16) { VStack(spacing: 32) { VStack { - SQText("Le saviez-vous ?", size: 20, font: .bold) - SQText("AlloVoisins en France, c’est :", size: 20, font: .bold) + SQText("Le saviez-vous ?") + .sqSize(20) + .sqFont(.bold) + SQText("AlloVoisins en France, c’est :") + .sqSize(20) + .sqFont(.bold) } VStack { - SQText("4,5 millions", size: 32, font: .bold) + SQText("4,5 millions") + .sqSize(32) + .sqFont(.bold) SQText("de membres, partout en France") } .padding(16) @@ -29,22 +35,27 @@ struct AlloVoisinReputationScreen: View { VStack { HStack { SQIcon(.star, size: .xxl, type: .solid, color: .sqGold(50)) - SQText("4,6/5", size: 32, font: .bold) + SQText("4,6/5") + .sqSize(32) + .sqFont(.bold) } SQText("Calculé à partir de 107,1 k avis") } HStack(spacing: 16) { HStack(spacing: 2) { SQIcon(.apple_brand, size: .s) - SQText("App Store", size: 10) + SQText("App Store") + .sqSize(10) } HStack(spacing: 2) { SQIcon(.play_store_brand, size: .s) - SQText("Google Play", size: 10) + SQText("Google Play") + .sqSize(10) } HStack(spacing: 2) { SQIcon(.star_trustpilot_brand, size: .s) - SQText("Trustpilot", size: 10) + SQText("Trustpilot") + .sqSize(10) } } } @@ -52,7 +63,9 @@ struct AlloVoisinReputationScreen: View { .background(Color.white) .cornerRadius(8) VStack { - SQText("La presse en parle", size: 24, font: .bold) + SQText("La presse en parle") + .sqSize(24) + .sqFont(.bold) HStack(spacing: 16) { Image("m6") .resizable() @@ -86,8 +99,8 @@ struct AlloVoisinReputationScreen: View { SQButton("J’ai compris, mais je souhaite résilier") { navigateToNext = true } - .buttonType(.glass) - .textSize(13) + .sqButtonType(.glass) + .sqTextSize(13) } .navigationDestination(isPresented: $navigateToNext) { if let screen = viewModel.nextPromotionalScreen { diff --git a/AlloVoisinsSwiftUI/Features/Resiliation/Views/Components/TrialWarningCellView.swift b/AlloVoisinsSwiftUI/Features/Resiliation/Views/Components/TrialWarningCellView.swift index c27d404..4a67250 100644 --- a/AlloVoisinsSwiftUI/Features/Resiliation/Views/Components/TrialWarningCellView.swift +++ b/AlloVoisinsSwiftUI/Features/Resiliation/Views/Components/TrialWarningCellView.swift @@ -11,9 +11,11 @@ struct TrialWarningCellView: View { var body: some View { VStack(spacing: 8) { SQIcon(.triangle_exclamation, size: .xxl, type: .solid, color: .SEMANTIC_RED) - SQText("Votre période d’essai gratuite* se termine le 14 mars 2024. Toute résiliation avant cette date entraînera la coupure immédiate du service.", font: .demiBold) + SQText("Votre période d’essai gratuite* se termine le 14 mars 2024. Toute résiliation avant cette date entraînera la coupure immédiate du service.") + .sqFont(.semiBold) .multilineTextAlignment(.center) - SQText("* Offre d’essai valable une seule fois par utilisateur.", size: 13) + SQText("* Offre d’essai valable une seule fois par utilisateur.") + .sqSize(13) } } } diff --git a/AlloVoisinsSwiftUI/Features/Resiliation/Views/ContinueAsParticularScreen.swift b/AlloVoisinsSwiftUI/Features/Resiliation/Views/ContinueAsParticularScreen.swift index cb261d2..7d99c5d 100644 --- a/AlloVoisinsSwiftUI/Features/Resiliation/Views/ContinueAsParticularScreen.swift +++ b/AlloVoisinsSwiftUI/Features/Resiliation/Views/ContinueAsParticularScreen.swift @@ -13,9 +13,12 @@ struct ContinueAsParticularScreen: View { var body: some View { VStack(spacing: 32) { - SQText("Souhaitez-vous poursuivre en tant que particulier ?", size: 18, font: .bold) + SQText("Souhaitez-vous poursuivre en tant que particulier ?") + .sqSize(18) + .sqFont(.bold) .multilineTextAlignment(.center) - SQText("Continuez de proposer vos services en tant que particulier pour arrondir vos fins de mois. À partir de 9,99 € / mois (sans engagement).", font: .demiBold) + SQText("Continuez de proposer vos services en tant que particulier pour arrondir vos fins de mois. À partir de 9,99 € / mois (sans engagement).") + .sqFont(.semiBold) .multilineTextAlignment(.center) VStack { SQButton("Changer de statut") { @@ -25,7 +28,7 @@ struct ContinueAsParticularScreen: View { { navigateToNext = true } - .buttonType(.glass) + .sqButtonType(.glass) } } .navigationDestination(isPresented: $navigateToNext) { diff --git a/AlloVoisinsSwiftUI/Features/Resiliation/Views/GenericResiliationScreen.swift b/AlloVoisinsSwiftUI/Features/Resiliation/Views/GenericResiliationScreen.swift index 5991a51..daf8e1a 100644 --- a/AlloVoisinsSwiftUI/Features/Resiliation/Views/GenericResiliationScreen.swift +++ b/AlloVoisinsSwiftUI/Features/Resiliation/Views/GenericResiliationScreen.swift @@ -98,15 +98,15 @@ struct GenericResiliationScreen: View { .cornerRadius(8) SQButton(content.screenType.buttonTitle, action: buttonAction) - .colorScheme(content.screenType.buttonColor) + .sqColorScheme(content.screenType.buttonColor) } if content.screenType != .statusChange { SQButton(content.screenType.cancelButtonTitle) { navigateToNext = true } - .buttonType(.glass) - .textSize(13) + .sqButtonType(.glass) + .sqTextSize(13) } } .navigationDestination(isPresented: $navigateToNext) { @@ -150,11 +150,17 @@ struct AlloVoisinReputationContent: ResiliationContentView { var body: some View { VStack(spacing: 32) { VStack { - SQText("Le saviez-vous ?", size: 20, font: .bold) - SQText("AlloVoisins en France, c'est :", size: 20, font: .bold) + SQText("Le saviez-vous ?") + .sqSize(20) + .sqFont(.bold) + SQText("AlloVoisins en France, c'est :") + .sqSize(20) + .sqFont(.bold) } VStack { - SQText("4,5 millions", size: 32, font: .bold) + SQText("4,5 millions") + .sqSize(32) + .sqFont(.bold) SQText("de membres, partout en France") } .padding(16) diff --git a/AlloVoisinsSwiftUI/Features/Resiliation/Views/GetMoreRatingsScreen.swift b/AlloVoisinsSwiftUI/Features/Resiliation/Views/GetMoreRatingsScreen.swift index 297649c..109314a 100644 --- a/AlloVoisinsSwiftUI/Features/Resiliation/Views/GetMoreRatingsScreen.swift +++ b/AlloVoisinsSwiftUI/Features/Resiliation/Views/GetMoreRatingsScreen.swift @@ -15,25 +15,30 @@ struct GetMoreRatingsScreen: View { VStack(spacing: 16) { VStack(spacing: 32) { // Vous ne recevez pas assez de demandes car vous n’avez pas assez d’avis. - SQText("Vous n’obtenez pas de réponses positives car vous n’avez pas assez d’avis.", size: 20, font: .bold) + SQText("Vous n’obtenez pas de réponses positives car vous n’avez pas assez d’avis.") + .sqSize(20) + .sqFont(.bold) .multilineTextAlignment(.center) RatingStarsView() VStack { SQText("À partir de") - SQText("4 avis", size: 32, font: .bold) + SQText("4 avis") + .sqSize(32) + .sqFont(.bold) .foregroundStyle(Color.sqGold()) SQText("les chances d’être sélectionné augmentent considérablement.") .multilineTextAlignment(.center) } VStack { - SQText("Le saviez-vous ?", font: .demiBold) + SQText("Le saviez-vous ?") + .sqFont(.semiBold) SQText("Vous pouvez recueillir des avis auprès de vos clients hors AlloVoisins pour faire décoller votre activité.") .multilineTextAlignment(.center) } SQButton("Recueillir des avis") { navigateToNext = true } - .colorScheme(.gold) + .sqColorScheme(.gold) } .padding(16) .background(Color.sqGold(10)) @@ -42,8 +47,8 @@ struct GetMoreRatingsScreen: View { SQButton("J’ai compris, mais je souhaite résilier") { navigateToNext = true } - .buttonType(.glass) - .textSize(13) + .sqButtonType(.glass) + .sqTextSize(13) } .navigationDestination(isPresented: $navigateToNext) { if let screen = viewModel.nextPromotionalScreen { diff --git a/AlloVoisinsSwiftUI/Features/Resiliation/Views/MoreTimeScreen.swift b/AlloVoisinsSwiftUI/Features/Resiliation/Views/MoreTimeScreen.swift index 3c15dda..ea7986d 100644 --- a/AlloVoisinsSwiftUI/Features/Resiliation/Views/MoreTimeScreen.swift +++ b/AlloVoisinsSwiftUI/Features/Resiliation/Views/MoreTimeScreen.swift @@ -14,10 +14,14 @@ struct MoreTimeScreen: View { var body: some View { VStack(spacing: 16) { VStack(spacing: 32) { - SQText("Profitez de plus de temps pour vous faire votre opinion.", size: 20, font: .bold) + SQText("Profitez de plus de temps pour vous faire votre opinion.") + .sqSize(20) + .sqFont(.bold) .multilineTextAlignment(.center) VStack { - SQText("+ X jours", size: 32, font: .bold) + SQText("+ X jours") + .sqSize(32) + .sqFont(.bold) .foregroundStyle(Color.sqBlue(50)) SQText("d’essai supplémentaires pour découvrir toutes les fonctionnalités de l’abonnement Premier.") .multilineTextAlignment(.center) @@ -25,7 +29,7 @@ struct MoreTimeScreen: View { SQButton("Je prolonge ma période d’essai") { navigateToNext = true } - .colorScheme(.blue) + .sqColorScheme(.blue) } .padding(16) .background(Color.sqBlue(10)) @@ -34,8 +38,8 @@ struct MoreTimeScreen: View { SQButton("Non merci, je souhaite résilier") { navigateToNext = true } - .buttonType(.glass) - .textSize(13) + .sqButtonType(.glass) + .sqTextSize(13) } .navigationDestination(isPresented: $navigateToNext) { if let screen = viewModel.nextPromotionalScreen { diff --git a/AlloVoisinsSwiftUI/Features/Resiliation/Views/OnlyProRequestsScreen.swift b/AlloVoisinsSwiftUI/Features/Resiliation/Views/OnlyProRequestsScreen.swift index bb8eb17..64eb094 100644 --- a/AlloVoisinsSwiftUI/Features/Resiliation/Views/OnlyProRequestsScreen.swift +++ b/AlloVoisinsSwiftUI/Features/Resiliation/Views/OnlyProRequestsScreen.swift @@ -14,26 +14,32 @@ struct OnlyProRequestsScreen: View { var body: some View { VStack(spacing: 16) { VStack(spacing: 32) { - SQText("Demandes réservées aux pros", size: 20, font: .bold) + SQText("Demandes réservées aux pros") + .sqSize(20) + .sqFont(.bold) .multilineTextAlignment(.center) Image("only_for_pro") .resizable() .scaledToFit() .frame(height: 60) VStack { - SQText("4 fois plus", size: 32, font: .bold) + SQText("4 fois plus") + .sqSize(32) + .sqFont(.bold) SQText("de demandes publiées réservées aux pros") - SQText("*entre 2023 et 2024.", size: 11) + SQText("*entre 2023 et 2024.") + .sqSize(11) } VStack { - SQText("Le saviez-vous ?", font: .demiBold) + SQText("Le saviez-vous ?") + .sqFont(.semiBold) SQText("Sur le menu abonnement, vous pouvez filtrer les demandes réservées aux pros.") .multilineTextAlignment(.center) } SQButton("Voir les demandes") { navigateToNext = true } - .colorScheme(.grape) + .sqColorScheme(.grape) } .padding(16) .background(Color.sqGrape(10)) @@ -42,8 +48,8 @@ struct OnlyProRequestsScreen: View { SQButton("J’ai compris, mais je souhaite résilier") { navigateToNext = true } - .buttonType(.glass) - .textSize(13) + .sqButtonType(.glass) + .sqTextSize(13) } .navigationDestination(isPresented: $navigateToNext) { if let screen = viewModel.nextPromotionalScreen { diff --git a/AlloVoisinsSwiftUI/Features/Resiliation/Views/PersonalizedSupportScreen.swift b/AlloVoisinsSwiftUI/Features/Resiliation/Views/PersonalizedSupportScreen.swift index 7596f09..2fc4ab0 100644 --- a/AlloVoisinsSwiftUI/Features/Resiliation/Views/PersonalizedSupportScreen.swift +++ b/AlloVoisinsSwiftUI/Features/Resiliation/Views/PersonalizedSupportScreen.swift @@ -14,21 +14,24 @@ struct PersonalizedSupportScreen: View { var body: some View { VStack(spacing: 16) { VStack(spacing: 32) { - SQText("Bénéficiez d’un accompagnement personnalisé.", size: 20, font: .bold) + SQText("Bénéficiez d’un accompagnement personnalisé.") + .sqSize(20) + .sqFont(.bold) .multilineTextAlignment(.center) Image("assistance") .resizable() .scaledToFit() .frame(height: 100) VStack { - SQText("Le saviez-vous ?", font: .demiBold) + SQText("Le saviez-vous ?") + .sqFont(.semiBold) SQText("Notre équipe dédiée aux professionnels est disponible pour répondre à toutes vos questions par téléphone.") .multilineTextAlignment(.center) } SQButton("Je souhaite être appelé") { navigateToNext = true } - .colorScheme(.grape) + .sqColorScheme(.grape) } .padding(16) .background(Color.sqGrape(10)) @@ -37,8 +40,8 @@ struct PersonalizedSupportScreen: View { SQButton("Non merci, je souhaite résilier") { navigateToNext = true } - .buttonType(.glass) - .textSize(13) + .sqButtonType(.glass) + .sqTextSize(13) } .navigationDestination(isPresented: $navigateToNext) { if let screen = viewModel.nextPromotionalScreen { diff --git a/AlloVoisinsSwiftUI/Features/Resiliation/Views/ProfileCompletionScreen.swift b/AlloVoisinsSwiftUI/Features/Resiliation/Views/ProfileCompletionScreen.swift index 0ee2bfd..2e15bbb 100644 --- a/AlloVoisinsSwiftUI/Features/Resiliation/Views/ProfileCompletionScreen.swift +++ b/AlloVoisinsSwiftUI/Features/Resiliation/Views/ProfileCompletionScreen.swift @@ -14,13 +14,17 @@ struct ProfileCompletionScreen: View { var body: some View { VStack(spacing: 16) { VStack(spacing: 32) { - SQText("Vous n’obtenez pas de réponses positives car votre profil est incomplet.", size: 20, font: .bold) + SQText("Vous n’obtenez pas de réponses positives car votre profil est incomplet.") + .sqSize(20) + .sqFont(.bold) .multilineTextAlignment(.center) VStack(spacing: 8) { HStack(spacing: 4) { SQIcon(.circle_exclamation, type: .solid, color: .sqSemanticNegative) SQText("Profil complété à :") - SQText("40%", size: 18, font: .bold) + SQText("40%") + .sqSize(18) + .sqFont(.bold) } VStack { Rectangle() @@ -33,25 +37,37 @@ struct ProfileCompletionScreen: View { .background(Color.sqNeutral(20)) .cornerRadius(12) HStack { - SQText("0%", size: 13) + SQText("0%") + .sqSize(13) Spacer() - SQText("100%", size: 13) + SQText("100%") + .sqSize(13) } VStack(spacing: 8) { SQText("Attractivité de votre profil :") HStack(spacing: 16) { - SQText("Faible", size: 18, font: .bold) + SQText("Faible") + .sqSize(18) + .sqFont(.bold) SQIcon(.chevron_right) - SQText("Modérée", size: 18, font: .bold, textColor: .sqNeutral(30)) + SQText("Modérée") + .sqSize(18) + .sqFont(.bold) + .sqColor(.sqNeutral(30)) SQIcon(.chevron_right) - SQText("Élevée", size: 18, font: .bold, textColor: .sqNeutral(30)) + SQText("Élevée") + .sqSize(18) + .sqFont(.bold) + .sqColor(.sqNeutral(30)) } } } .padding(16) .background(Color.white) .cornerRadius(8) - SQText("90%", size: 32, font: .bold) + SQText("90%") + .sqSize(32) + .sqFont(.bold) SQText("des demandeurs comparent systématiquement les profils des offreurs pour faire leur choix.") .multilineTextAlignment(.center) SQButton("Je complète mon profil") { @@ -65,8 +81,8 @@ struct ProfileCompletionScreen: View { SQButton("J’ai compris, mais je souhaite résilier") { navigateToNext = true } - .buttonType(.glass) - .textSize(13) + .sqButtonType(.glass) + .sqTextSize(13) } .navigationDestination(isPresented: $navigateToNext) { if let screen = viewModel.nextPromotionalScreen { diff --git a/AlloVoisinsSwiftUI/Features/Resiliation/Views/ResiliationCheckStepsScreen.swift b/AlloVoisinsSwiftUI/Features/Resiliation/Views/ResiliationCheckStepsScreen.swift index c5f5da6..4dbce29 100644 --- a/AlloVoisinsSwiftUI/Features/Resiliation/Views/ResiliationCheckStepsScreen.swift +++ b/AlloVoisinsSwiftUI/Features/Resiliation/Views/ResiliationCheckStepsScreen.swift @@ -19,7 +19,9 @@ struct ResiliationCheckStepsScreen: View { var body: some View { ScrollView { VStack(spacing: 16) { - SQText("En résiliant votre abonnement vous renoncez à :", size: 18, font: .bold) + SQText("En résiliant votre abonnement vous renoncez à :") + .sqSize(18) + .sqFont(.bold) .multilineTextAlignment(.center) ForEach(viewModel.resiliationCheckSteps, id: \.self) { step in ResiliationCheckStepView( @@ -43,7 +45,7 @@ struct ResiliationCheckStepsScreen: View { HStack { SQButton("Annuler") {} - .buttonType(.glass) + .sqButtonType(.glass) SQButton("Continuer") { navigateToReasonScreen = true } diff --git a/AlloVoisinsSwiftUI/Features/Resiliation/Views/ResiliationConfirmationScreen.swift b/AlloVoisinsSwiftUI/Features/Resiliation/Views/ResiliationConfirmationScreen.swift index cdb5f9f..ed3aa68 100644 --- a/AlloVoisinsSwiftUI/Features/Resiliation/Views/ResiliationConfirmationScreen.swift +++ b/AlloVoisinsSwiftUI/Features/Resiliation/Views/ResiliationConfirmationScreen.swift @@ -16,7 +16,9 @@ struct ResiliationConfirmationScreen: View { .resizable() .scaledToFit() .frame(height: 200) - SQText("Votre résiliation a été prise en compte. Elle sera effective le JJ/MM/AAAA.", size: 18, font: .bold) + SQText("Votre résiliation a été prise en compte. Elle sera effective le JJ/MM/AAAA.") + .sqSize(18) + .sqFont(.bold) SQButton("Terminer") { } diff --git a/AlloVoisinsSwiftUI/Features/Resiliation/Views/ResiliationReasonScreen.swift b/AlloVoisinsSwiftUI/Features/Resiliation/Views/ResiliationReasonScreen.swift index ade38fd..9ac397d 100644 --- a/AlloVoisinsSwiftUI/Features/Resiliation/Views/ResiliationReasonScreen.swift +++ b/AlloVoisinsSwiftUI/Features/Resiliation/Views/ResiliationReasonScreen.swift @@ -21,14 +21,15 @@ struct ResiliationReasonScreen: View { options: viewModel.resiliationReasons.map { SQRadioOption(title: $0.text) }, selectedIndex: $selectedIndex) if selectedIndex == viewModel.resiliationReasons.count - 1 { - SQTextField("", placeholder: "Précisez-nous le motif de votre résiliation", text: $resiliationOtherMotif) + SQTextField("", text: $resiliationOtherMotif) + .sqPlaceholder("Précisez-nous le motif de votre résiliation") } HStack { SQButton("Annuler") { dismiss() } - .buttonType(.glass) + .sqButtonType(.glass) SQButton("Continuer") { if let index = selectedIndex { let selectedReason = viewModel.resiliationReasons[index] diff --git a/AlloVoisinsSwiftUI/Features/Resiliation/Views/ResizePerimeterScreen.swift b/AlloVoisinsSwiftUI/Features/Resiliation/Views/ResizePerimeterScreen.swift index 7115e8b..28993f5 100644 --- a/AlloVoisinsSwiftUI/Features/Resiliation/Views/ResizePerimeterScreen.swift +++ b/AlloVoisinsSwiftUI/Features/Resiliation/Views/ResizePerimeterScreen.swift @@ -14,21 +14,24 @@ struct ResizePerimeterScreen: View { var body: some View { VStack(spacing: 16) { VStack(spacing: 32) { - SQText("Augmentez votre périmètre afin de recevoir plus de demandes", size: 20, font: .bold) + SQText("Augmentez votre périmètre afin de recevoir plus de demandes") + .sqSize(20) + .sqFont(.bold) .multilineTextAlignment(.center) Image("perimeter") .resizable() .scaledToFit() .frame(height: 140) VStack { - SQText("Le saviez-vous ?", font: .demiBold) + SQText("Le saviez-vous ?") + .sqFont(.semiBold) SQText("Si vous le souhaitez, vous pouvez modifier à tout moment votre abonnement en élargissant votre périmètre d’intervention.") .multilineTextAlignment(.center) } SQButton("Modifier mon périmètre") { navigateToNext = true } - .colorScheme(.orange) + .sqColorScheme(.orange) } .padding(16) .background(Color.sqOrange(10)) @@ -37,8 +40,8 @@ struct ResizePerimeterScreen: View { SQButton("J’ai compris, mais je souhaite résilier") { navigateToNext = true } - .buttonType(.glass) - .textSize(13) + .sqButtonType(.glass) + .sqTextSize(13) } .navigationDestination(isPresented: $navigateToNext) { if let screen = viewModel.nextPromotionalScreen { diff --git a/AlloVoisinsSwiftUI/Features/Resiliation/Views/SoftwarePresentationScreen.swift b/AlloVoisinsSwiftUI/Features/Resiliation/Views/SoftwarePresentationScreen.swift index ded51e8..ddffa8f 100644 --- a/AlloVoisinsSwiftUI/Features/Resiliation/Views/SoftwarePresentationScreen.swift +++ b/AlloVoisinsSwiftUI/Features/Resiliation/Views/SoftwarePresentationScreen.swift @@ -13,30 +13,39 @@ struct SoftwarePresentationScreen: View { var body: some View { VStack(spacing: 32) { - SQText("Saviez-vous que votre abonnement peut vous offrir bien plus que de nouveaux clients ?", size: 18, font: .bold) + SQText("Saviez-vous que votre abonnement peut vous offrir bien plus que de nouveaux clients ?") + .sqSize(18) + .sqFont(.bold) .multilineTextAlignment(.center) - SQText("Bénéficiez d’un accompagnement personnalisé par téléphone pour créer votre structure.", font: .demiBold) + SQText("Bénéficiez d’un accompagnement personnalisé par téléphone pour créer votre structure.") + .sqFont(.semiBold) .multilineTextAlignment(.center) VStack(spacing: 16) { VStack(alignment: .leading, spacing: 16) { HStack { SQIcon(.file_signature, size: .m) - SQText("Créez et envoyez vos devis et factures à tous vos clients", font: .demiBold) + SQText("Créez et envoyez vos devis et factures à tous vos clients") + .sqFont(.semiBold) } HStack { SQIcon(.address_card, size: .m) - SQText("Créez vos cartes de visite et prospectus personnalisés", font: .demiBold) + SQText("Créez vos cartes de visite et prospectus personnalisés") + .sqFont(.semiBold) } HStack { SQIcon(.chart_line_up, size: .m) - SQText("Suivez votre chiffre d’affaires", font: .demiBold) + SQText("Suivez votre chiffre d’affaires") + .sqFont(.semiBold) } HStack { SQIcon(.users, size: .m) - SQText("Gérez votre répertoire client regroupant vos clients AlloVoisins et hors AlloVoisins", font: .demiBold) + SQText("Gérez votre répertoire client regroupant vos clients AlloVoisins et hors AlloVoisins") + .sqFont(.semiBold) } } - SQText("Inclus, sans surcoût", size: 13, font: .demiBold) + SQText("Inclus, sans surcoût") + .sqSize(13) + .sqFont(.semiBold) } VStack { SQButton("Découvrir le logiciel") { @@ -45,8 +54,8 @@ struct SoftwarePresentationScreen: View { SQButton("J’ai compris, mais je souhaite résilier") { navigateToNext = true } - .buttonType(.glass) - .textSize(13) + .sqButtonType(.glass) + .sqTextSize(13) } } .navigationDestination(isPresented: $navigateToNext) { diff --git a/AlloVoisinsSwiftUI/Features/Resiliation/Views/StatusChangeScreen.swift b/AlloVoisinsSwiftUI/Features/Resiliation/Views/StatusChangeScreen.swift index e40da24..972d847 100644 --- a/AlloVoisinsSwiftUI/Features/Resiliation/Views/StatusChangeScreen.swift +++ b/AlloVoisinsSwiftUI/Features/Resiliation/Views/StatusChangeScreen.swift @@ -17,14 +17,15 @@ struct StatusChangeScreen: View { SQText("En continuant, votre abonnement sera résilié et votre demande de changement de statut sera étudiée par nos équipes.") .multilineTextAlignment(.center) VStack { - SQText("Le saviez-vous ?", font: .demiBold) + SQText("Le saviez-vous ?") + .sqFont(.semiBold) SQText("Si vous le souhaitez, vous pourrez vous abonner à l’abonnement Premier en tant que particulier.") .multilineTextAlignment(.center) } SQButton("Résilier et changer de statut") { navigateToNext = true } - .colorScheme(.orange) + .sqColorScheme(.orange) } .padding(16) .background(Color.sqOrange(10)) diff --git a/AlloVoisinsSwiftUI/Features/Resiliation/Views/WebinaireScreen.swift b/AlloVoisinsSwiftUI/Features/Resiliation/Views/WebinaireScreen.swift index 96372a2..41df9cb 100644 --- a/AlloVoisinsSwiftUI/Features/Resiliation/Views/WebinaireScreen.swift +++ b/AlloVoisinsSwiftUI/Features/Resiliation/Views/WebinaireScreen.swift @@ -14,17 +14,23 @@ struct WebinaireScreen: View { var body: some View { VStack(spacing: 16) { VStack(spacing: 32) { - SQText("Webinaire spécial Pro", size: 20, font: .bold) + SQText("Webinaire spécial Pro") + .sqSize(20) + .sqFont(.bold) .multilineTextAlignment(.center) Image("webinaire") .resizable() .scaledToFit() .frame(height: 140) - SQText("30 minutes", size: 32, font: .bold) + SQText("30 minutes") + .sqSize(32) + .sqFont(.bold) VStack { SQText("Inscrivez-vous dès maintenant au webinaire gratuit sur le thème") .multilineTextAlignment(.center) - SQText("“Augmenter mon chiffre d'affaires.“", size: 18, font: .bold) + SQText("“Augmenter mon chiffre d'affaires.“") + .sqSize(18) + .sqFont(.bold) } NavigationLink { if let screen = viewModel.nextPromotionalScreen { @@ -35,7 +41,7 @@ struct WebinaireScreen: View { } label: { SQButton("Je m’inscris") { } - .colorScheme(.grape) + .sqColorScheme(.grape) } } .padding(16) @@ -51,8 +57,8 @@ struct WebinaireScreen: View { } label: { SQButton("Non merci, je souhaite résilier") { } - .buttonType(.glass) - .textSize(13) + .sqButtonType(.glass) + .sqTextSize(13) } } .navigationDestination(isPresented: $navigateToNext) { diff --git a/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/NewPerimeterCellView.swift b/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/NewPerimeterCellView.swift index 1749a70..9c4ba91 100644 --- a/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/NewPerimeterCellView.swift +++ b/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/NewPerimeterCellView.swift @@ -11,13 +11,16 @@ struct NewPerimeterCellView: View { var body: some View { VStack(spacing: 16) { - SQText("Vous souhaitez ajouter un nouveau périmètre ?", size: 24, font: .bold) + SQText("Vous souhaitez ajouter un nouveau périmètre ?") + .sqSize(24) + .sqFont(.bold) .multilineTextAlignment(.center) SQText("Si vous souhaitez couvrir une zone géographique différente de votre périmètre actuel, souscrivez à un nouvel abonnement pour proposer vos services dans ce nouveau secteur.") SQButton("Souscrire à un nouveau périmètre") {} - .buttonType(.line) - .colorScheme(.orange) - SQText("À partir de 9,99€ / mois, sans engagement", size: 13) + .sqButtonType(.line) + .sqColorScheme(.orange) + SQText("À partir de 9,99€ / mois, sans engagement") + .sqSize(13) } .padding(.horizontal, 32) .padding(.vertical, 16) diff --git a/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/FAQRow.swift b/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/FAQRow.swift index b383ba8..7b7965c 100644 --- a/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/FAQRow.swift +++ b/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/FAQRow.swift @@ -19,7 +19,9 @@ struct FAQRow: View { } }) { HStack { - SQText(item.question, size: 13, font: .demiBold) + SQText(item.question) + .sqSize(13) + .sqFont(.semiBold) Spacer() SQIcon(isExpanded ? .chevron_up : .chevron_down) } @@ -27,7 +29,8 @@ struct FAQRow: View { } if isExpanded { - SQText(item.answer, size: 14) + SQText(item.answer) + .sqSize(14) .padding(.bottom, 16) } diff --git a/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/FAQSection.swift b/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/FAQSection.swift index fb0583e..615ecdb 100644 --- a/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/FAQSection.swift +++ b/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/FAQSection.swift @@ -12,7 +12,8 @@ struct FAQSection: View { var body: some View { VStack(alignment: .leading, spacing: 0) { - SQText("Questions fréquentes", size: 16, font: .demiBold) + SQText("Questions fréquentes") + .sqFont(.semiBold) .padding(.vertical, 8) ForEach(items) { item in diff --git a/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/FeatureRow.swift b/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/FeatureRow.swift index 695ca5f..3c7017f 100644 --- a/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/FeatureRow.swift +++ b/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/FeatureRow.swift @@ -12,8 +12,10 @@ struct FeatureRow: View { var body: some View { HStack(alignment: .top) { - SQText(" • ", size: 14) - SQText("\(feature.description)", size: 14) + SQText(" • ") + .sqSize(14) + SQText("\(feature.description)") + .sqSize(14) Spacer() switch feature.value { case .boolean(let isIncluded): @@ -22,7 +24,9 @@ struct FeatureRow: View { type: .solid, color: isIncluded ? .sqSemanticGreen : .sqSemanticRed) case .text(let text): - SQText(text, size: 14, font: .demiBold) + SQText(text) + .sqSize(14) + .sqFont(.semiBold) } } } diff --git a/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/FeatureSection.swift b/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/FeatureSection.swift index 38b4e72..fe9ba34 100644 --- a/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/FeatureSection.swift +++ b/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/FeatureSection.swift @@ -12,9 +12,13 @@ struct FeatureSection: View { var body: some View { VStack(alignment: .leading, spacing: 8) { - SQText(section.title, size: 18, font: .bold) + SQText(section.title) + .sqSize(18) + .sqFont(.bold) if let subtitle = section.subtitle { - SQText(subtitle, size: 13, font: .demiBold) + SQText(subtitle) + .sqSize(13) + .sqFont(.semiBold) } ForEach(section.features, id: \.description) { feature in FeatureRow(feature: feature) diff --git a/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/PricingSubscribeFooter.swift b/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/PricingSubscribeFooter.swift index af515d2..ebb7e6c 100644 --- a/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/PricingSubscribeFooter.swift +++ b/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/PricingSubscribeFooter.swift @@ -12,17 +12,24 @@ struct PricingSubscribeFooter: View { var body: some View { VStack { - SQText(pricing.footerPrimaryText, size: 18, font: .bold, textColor: pricing.footerTextColor) + SQText(pricing.footerPrimaryText) + .sqSize(18) + .sqFont(.bold) + .sqColor(pricing.footerTextColor) if !pricing.footerSecondaryText.isEmpty { - SQText(pricing.footerSecondaryText, size: 13, textColor: pricing.footerTextColor) + SQText(pricing.footerSecondaryText) + .sqSize(13) + .sqColor(pricing.footerTextColor) } SQButton("Continuer") { } - .colorScheme(pricing.footerButtonColor) + .sqColorScheme(pricing.footerButtonColor) if !pricing.footerTertiaryText.isEmpty { - SQText(pricing.footerTertiaryText, size: 12, textColor: .sqOrange(70)) + SQText(pricing.footerTertiaryText) + .sqSize(12) + .sqColor(.sqOrange(70)) } } .padding() diff --git a/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/PricingSubscribeHeader.swift b/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/PricingSubscribeHeader.swift index 6c29723..48569cd 100644 --- a/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/PricingSubscribeHeader.swift +++ b/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/PricingSubscribeHeader.swift @@ -18,7 +18,9 @@ struct PricingSubscribeHeader: View { SQIcon(.xmark, size: .l) } Spacer() - SQText(pricing.headerTitle, size: 18, font: .bold) + SQText(pricing.headerTitle) + .sqSize(18) + .sqFont(.bold) Spacer() if pricing == .premierPro { Button { diff --git a/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/ReassuranceIndicatorSection.swift b/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/ReassuranceIndicatorSection.swift index 7095cf1..0f49914 100644 --- a/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/ReassuranceIndicatorSection.swift +++ b/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/ReassuranceIndicatorSection.swift @@ -20,7 +20,8 @@ struct ReassuranceIndicatorSection: View { SQIcon(indicator.icon, size: .s, type: .solid) } - SQText(indicator.text, size: 14) + SQText(indicator.text) + .sqSize(14) .multilineTextAlignment(.center) } } diff --git a/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/SwitchAndInfo.swift b/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/SwitchAndInfo.swift index 0c4f742..d81dd62 100644 --- a/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/SwitchAndInfo.swift +++ b/AlloVoisinsSwiftUI/Features/Subscriptions/Views/Components/Pricing/SwitchAndInfo.swift @@ -15,7 +15,8 @@ struct SwitchAndInfo: View { if pricing == .premierPro { HStack { SQIcon(.chart_line_up, size: .l) - SQText("L’abonnement conçu pour augmenter votre chiffre d’affaires et développer votre entreprise.", font: .demiBold) + SQText("L’abonnement conçu pour augmenter votre chiffre d’affaires et développer votre entreprise.") + .sqFont(.semiBold) } .padding() .background { @@ -29,9 +30,11 @@ struct SwitchAndInfo: View { } .pickerStyle(.segmented) if pricing == .standard { - SQText("Idéal pour proposer vos services ponctuellement, sans objectif de complément de revenus.", font: .demiBold) + SQText("Idéal pour proposer vos services ponctuellement, sans objectif de complément de revenus.") + .sqFont(.semiBold) } else { - SQText("Idéal pour vous générer un complément de revenus régulier.", font: .demiBold) + SQText("Idéal pour vous générer un complément de revenus régulier.") + .sqFont(.semiBold) } } } diff --git a/AlloVoisinsSwiftUI/Features/Visio/Views/VisioPermissionsModal.swift b/AlloVoisinsSwiftUI/Features/Visio/Views/VisioPermissionsModal.swift index 5f89d86..10c5785 100644 --- a/AlloVoisinsSwiftUI/Features/Visio/Views/VisioPermissionsModal.swift +++ b/AlloVoisinsSwiftUI/Features/Visio/Views/VisioPermissionsModal.swift @@ -11,10 +11,15 @@ struct VisioPermissionsModal: View { var body: some View { VStack(spacing: 0) { HStack(spacing: 16) { - SQText("Appels vocaux et vidéo", size: 18, font: .bold) + SQText("Appels vocaux et vidéo") + .sqSize(18) + .sqFont(.bold) Spacer() Image("new") - SQText("NOUVEAU", size: 9, font: .bold, textColor: .white) + SQText("NOUVEAU") + .sqSize(9) + .sqFont(.bold) + .sqColor(.white) .padding(.horizontal, 4) .padding(.vertical, 2) .background { diff --git a/AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/messaging.imageset/Contents.json b/AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/messaging.imageset/Contents.json new file mode 100644 index 0000000..c88d241 --- /dev/null +++ b/AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/messaging.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Détails conversation - Utilisateur suspendu.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/messaging.imageset/Détails conversation - Utilisateur suspendu.png b/AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/messaging.imageset/Détails conversation - Utilisateur suspendu.png new file mode 100644 index 0000000000000000000000000000000000000000..40066d90b6bfa6bcd7c4229875c4b1c2626c7edf GIT binary patch literal 150127 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 I1=)uGFHS{dxc~qF literal 0 HcmV?d00001 diff --git a/AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/portrait_accessories.imageset/Contents.json b/AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/portrait_accessories.imageset/Contents.json new file mode 100644 index 0000000..415f5ef --- /dev/null +++ b/AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/portrait_accessories.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Visuel accessoires.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/portrait_accessories.imageset/Visuel accessoires.png b/AlloVoisinsSwiftUI/Resources/Assets.xcassets/Images/portrait_accessories.imageset/Visuel accessoires.png new file mode 100644 index 0000000000000000000000000000000000000000..1fe0b00ef99ec4290fb2a7b6dd5d6efcb3fbea0c GIT binary patch literal 2410 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 P00000NkvXXu0mjf)Kn7O literal 0 HcmV?d00001 diff --git a/AlloVoisinsSwiftUI/Resources/Assets.xcassets/logo-pro-color.imageset/Contents.json b/AlloVoisinsSwiftUI/Resources/Assets.xcassets/logo-pro-color.imageset/Contents.json new file mode 100644 index 0000000..902f1e6 --- /dev/null +++ b/AlloVoisinsSwiftUI/Resources/Assets.xcassets/logo-pro-color.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "allovoisinspro.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/AlloVoisinsSwiftUI/Resources/Assets.xcassets/logo-pro-color.imageset/allovoisinspro.pdf b/AlloVoisinsSwiftUI/Resources/Assets.xcassets/logo-pro-color.imageset/allovoisinspro.pdf new file mode 100644 index 0000000..255fb3d --- /dev/null +++ b/AlloVoisinsSwiftUI/Resources/Assets.xcassets/logo-pro-color.imageset/allovoisinspro.pdf @@ -0,0 +1,379 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 40.181030 cm +0.529412 0.858824 0.470588 scn +16.280462 -17.412254 m +23.332426 -17.412254 28.120798 -14.452168 31.255003 -10.708534 c +31.255003 -12.449760 l +31.255003 -14.800415 33.344475 -16.889885 36.391621 -16.889885 c +39.264641 -16.889885 41.528236 -14.713356 41.528236 -11.840332 c +41.528236 10.795601 l +41.528236 16.802832 39.961128 21.678263 36.565742 24.986589 c +33.344475 28.294918 28.207857 30.123207 21.330017 30.123207 c +15.322787 30.123207 10.882661 29.078468 6.529596 27.424305 c +4.962493 26.814877 3.656574 25.160711 3.656574 23.245365 c +3.656574 20.807650 5.658984 18.892302 8.096700 18.892302 c +8.619067 18.892302 9.141437 18.979362 9.750865 19.153486 c +12.623887 20.285282 15.932216 20.981770 19.937035 20.981770 c +27.337246 20.981770 31.342064 17.499321 31.342064 10.969727 c +31.342064 9.837929 l +27.772551 10.969727 24.115978 11.753277 18.979361 11.753277 c +7.748455 11.753277 0.000000 6.877842 0.000000 -3.047142 c +0.000000 -3.221264 l +0.000000 -12.449760 7.661394 -17.412254 16.280462 -17.412254 c +h +19.240545 -9.750858 m +14.190990 -9.750858 10.273232 -7.226082 10.273232 -2.785957 c +10.273232 -2.611835 l +10.273232 2.176533 14.278052 5.049557 21.068832 5.049557 c +25.247776 5.049557 28.817286 4.266003 31.516188 3.221268 c +31.516188 0.087063 l +31.516188 -5.746044 26.205448 -9.750858 19.240545 -9.750858 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 0.000000 23.291138 cm +0.529412 0.858824 0.470588 scn +52.142231 5.223686 m +52.142231 2.263603 54.492882 0.000008 57.365906 0.000008 c +60.325989 0.000008 62.676643 2.263603 62.676643 5.223686 c +62.676643 59.201675 l +62.676643 62.074699 60.325989 64.425354 57.365906 64.425354 c +54.405823 64.425354 52.142231 62.074699 52.142231 59.201675 c +52.142231 5.223686 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 0.000000 23.291138 cm +0.529412 0.858824 0.470588 scn +74.441483 5.223686 m +74.441483 2.263603 76.792137 0.000008 79.665161 0.000008 c +82.625244 0.000008 84.975899 2.263603 84.975899 5.223686 c +84.975899 59.201675 l +84.975899 62.074699 82.625244 64.425354 79.665161 64.425354 c +76.705078 64.425354 74.441483 62.074699 74.441483 59.201675 c +74.441483 5.223686 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 0.000000 39.745728 cm +0.529412 0.858824 0.470588 scn +117.983688 -17.064011 m +132.174683 -17.064011 142.622025 -6.181351 142.622025 6.877842 c +142.622025 7.051964 l +142.622025 20.198214 132.261734 30.906755 118.157806 30.906755 c +104.053871 30.906755 93.606522 20.024096 93.606522 6.877842 c +93.606522 6.703716 l +93.606522 -6.355476 103.966820 -17.064011 117.983688 -17.064011 c +h +118.157806 -7.835514 m +109.974045 -7.835514 104.140945 -1.131798 104.140945 6.877842 c +104.140945 7.051964 l +104.140945 15.061600 109.538742 21.678259 117.983688 21.678259 c +126.254509 21.678259 132.087616 14.974541 132.087616 6.877842 c +132.087616 6.703716 l +132.087616 -1.218861 126.689812 -7.835514 118.157806 -7.835514 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 150.645142 39.832581 cm +1.000000 0.235294 0.454902 scn +28.469042 -12.362652 m +43.791828 22.810108 l +44.053013 23.332474 44.401260 24.377213 44.401260 25.334885 c +44.401260 28.207905 42.137665 30.384439 39.264641 30.384439 c +36.478680 30.384439 35.085701 28.556150 34.302147 26.727865 c +22.287689 -3.917706 l +10.447354 26.553741 l +9.576741 28.730276 8.096701 30.384439 5.223677 30.384439 c +2.350655 30.384439 0.000000 27.946724 0.000000 25.334885 c +0.000000 24.377213 0.348245 23.506598 0.609429 22.723045 c +15.932216 -12.362652 l +17.151073 -15.148613 19.066422 -16.976898 21.939445 -16.976898 c +22.461813 -16.976898 l +25.334837 -16.976898 27.250183 -15.061554 28.469042 -12.362652 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 150.645142 39.223206 cm +1.000000 0.235294 0.454902 scn +71.860931 -16.541649 m +86.051918 -16.541649 96.499283 -5.658985 96.499283 7.400208 c +96.499283 7.574329 l +96.499283 20.720583 86.138985 31.429121 72.035057 31.429121 c +57.931126 31.429121 47.483772 20.546461 47.483772 7.400208 c +47.483772 7.226082 l +47.483772 -5.833107 57.844063 -16.541649 71.860931 -16.541649 c +h +72.035057 -7.313152 m +63.851295 -7.313152 58.018185 -0.609432 58.018185 7.400208 c +58.018185 7.574329 l +58.018185 15.583969 63.415985 22.200624 71.860931 22.200624 c +80.131752 22.200624 85.964859 15.496906 85.964859 7.400208 c +85.964859 7.226082 l +85.964859 -0.696495 80.567062 -7.313152 72.035057 -7.313152 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 150.645142 23.291016 cm +1.000000 0.235294 0.454902 scn +104.441582 58.243973 m +104.441582 55.109764 107.053421 53.020294 110.448814 53.020294 c +113.844200 53.020294 116.456039 55.109764 116.456039 58.243973 c +116.456039 58.766338 l +116.456039 61.900543 113.844200 63.902954 110.448814 63.902954 c +107.053421 63.902954 104.441582 61.900543 104.441582 58.766338 c +104.441582 58.243973 l +h +105.225128 5.223648 m +105.225128 2.263565 107.575790 -0.000027 110.448814 -0.000027 c +113.408897 -0.000027 115.759544 2.263565 115.759544 5.223648 c +115.759544 41.615265 l +115.759544 44.575348 113.408897 46.926003 110.448814 46.926003 c +107.488724 46.926003 105.225128 44.575348 105.225128 41.615265 c +105.225128 5.223648 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 150.645142 39.571411 cm +1.000000 0.235294 0.454902 scn +142.673035 -16.715729 m +152.249786 -16.715729 159.737061 -11.927357 159.737061 -2.263557 c +159.737061 -2.089436 l +159.737061 6.181389 152.162720 9.228531 145.459000 11.318001 c +140.061203 13.059227 135.098709 14.452209 135.098709 17.847601 c +135.098709 18.021725 l +135.098709 20.633562 137.449356 22.548910 141.541245 22.548910 c +144.762512 22.548910 148.593216 21.330050 152.336853 19.414703 c +153.033340 19.066458 153.555695 18.892334 154.426315 18.892334 c +156.864029 18.892334 158.779388 20.720623 158.779388 23.158339 c +158.779388 24.986626 157.734650 26.379606 156.254608 27.163158 c +151.814468 29.513813 146.677856 30.906790 141.802429 30.906790 c +132.660995 30.906790 125.434906 25.596054 125.434906 16.889927 c +125.434906 16.715803 l +125.434906 7.922615 133.009247 5.136654 139.800018 3.221306 c +145.197830 1.567142 150.073242 0.348282 150.073242 -3.221230 c +150.073242 -3.395351 l +150.073242 -6.442497 147.461411 -8.357845 142.934219 -8.357845 c +138.755280 -8.357845 134.141037 -6.877804 129.700897 -3.917721 c +129.091476 -3.569473 128.307922 -3.308292 127.437317 -3.308292 c +124.999596 -3.308292 123.171310 -5.223640 123.171310 -7.574295 c +123.171310 -9.315517 124.041931 -10.621441 125.173721 -11.230869 c +130.484467 -14.887444 136.839935 -16.715729 142.673035 -16.715729 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 150.645142 23.291016 cm +1.000000 0.235294 0.454902 scn +167.744659 58.243973 m +167.744659 55.109764 170.356506 53.020294 173.751892 53.020294 c +177.147278 53.020294 179.759125 55.109764 179.759125 58.243973 c +179.759125 58.766338 l +179.759125 61.900543 177.147278 63.902954 173.751892 63.902954 c +170.356506 63.902954 167.744659 61.900543 167.744659 58.766338 c +167.744659 58.243973 l +h +168.528198 5.223648 m +168.528198 2.263565 170.878860 -0.000027 173.751892 -0.000027 c +176.711975 -0.000027 179.062622 2.263565 179.062622 5.223648 c +179.062622 41.615265 l +179.062622 44.575348 176.711975 46.926003 173.751892 46.926003 c +170.791809 46.926003 168.528198 44.575348 168.528198 41.615265 c +168.528198 5.223648 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 150.645142 39.832642 cm +1.000000 0.235294 0.454902 scn +190.218033 -11.317978 m +190.218033 -14.278061 192.568695 -16.541653 195.441711 -16.541653 c +198.401794 -16.541653 200.752426 -14.278061 200.752426 -11.317978 c +200.752426 9.837917 l +200.752426 16.976942 205.018463 21.242945 211.112747 21.242945 c +217.381165 21.242945 220.950668 17.151064 220.950668 10.012035 c +220.950668 -11.317978 l +220.950668 -14.278061 223.301315 -16.541653 226.261398 -16.541653 c +229.221481 -16.541653 231.485077 -14.278061 231.485077 -11.317978 c +231.485077 13.233307 l +231.485077 23.854784 225.477859 30.819685 215.117554 30.819685 c +207.978531 30.819685 203.712509 27.076050 200.752426 22.810047 c +200.752426 25.073639 l +200.752426 28.033724 198.401794 30.384378 195.441711 30.384378 c +192.481628 30.384378 190.218033 28.033724 190.218033 25.073639 c +190.218033 -11.317978 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 150.645142 39.571411 cm +1.000000 0.235294 0.454902 scn +257.352478 -16.715729 m +266.929230 -16.715729 274.416473 -11.927357 274.416473 -2.263557 c +274.416473 -2.089436 l +274.416473 6.181389 266.842163 9.228531 260.138428 11.318001 c +254.740631 13.059227 249.778137 14.452209 249.778137 17.847601 c +249.778137 18.021725 l +249.778137 20.633562 252.128799 22.548910 256.220673 22.548910 c +259.441956 22.548910 263.272644 21.330050 267.016296 19.414703 c +267.712769 19.066458 268.235138 18.892334 269.105743 18.892334 c +271.543488 18.892334 273.458832 20.720623 273.458832 23.158339 c +273.458832 24.986626 272.414093 26.379606 270.934052 27.163158 c +266.493927 29.513813 261.357300 30.906790 256.481842 30.906790 c +247.340424 30.906790 240.114334 25.596054 240.114334 16.889927 c +240.114334 16.715803 l +240.114334 7.922615 247.688675 5.136654 254.479462 3.221306 c +259.877258 1.567142 264.752686 0.348282 264.752686 -3.221230 c +264.752686 -3.395351 l +264.752686 -6.442497 262.140869 -8.357845 257.613678 -8.357845 c +253.434723 -8.357845 248.820465 -6.877804 244.380341 -3.917721 c +243.770905 -3.569473 242.987366 -3.308292 242.116745 -3.308292 c +239.679031 -3.308292 237.850739 -5.223640 237.850739 -7.574295 c +237.850739 -9.315517 238.721359 -10.621441 239.853165 -11.230869 c +245.163895 -14.887444 251.519379 -16.715729 257.352478 -16.715729 c +h +f +n +Q +q +0.998630 0.052336 -0.052336 0.998630 437.630890 -8.412956 cm +0.435294 0.133333 0.552941 scn +0.000000 81.448296 m +0.000000 84.653801 2.598578 87.252380 5.804086 87.252380 c +157.229233 87.252380 l +160.434738 87.252380 163.033325 84.653801 163.033325 81.448296 c +163.033325 14.228600 l +163.033325 11.023094 160.434753 8.424515 157.229233 8.424515 c +5.804094 8.424515 l +2.598586 8.424515 0.000000 11.023094 0.000000 14.228600 c +0.000000 81.448296 l +h +f +n +Q +q +1.000000 -0.000000 0.000000 1.000000 445.417725 9.358032 cm +1.000000 1.000000 1.000000 scn +34.287521 52.236763 m +40.817120 52.236763 45.170181 47.883698 45.170181 40.918793 c +45.170181 30.645563 37.944099 22.461800 29.325031 22.461800 c +22.534252 22.461800 17.745880 27.337234 17.745880 34.040955 c +17.745880 43.791821 25.842577 52.236763 34.287521 52.236763 c +h +19.400043 60.855831 m +16.439960 60.855831 14.263427 58.940483 13.392814 55.545090 c +0.333622 6.790771 l +-0.798175 2.524769 1.030112 -0.000011 4.599625 -0.000011 c +7.385586 -0.000011 9.562119 1.915337 10.432732 5.223663 c +15.046979 22.548862 l +17.832941 17.412247 22.534248 13.407425 30.630947 13.407425 c +43.777203 13.407425 56.139900 25.334824 56.139900 41.615288 c +56.139900 53.803867 48.478512 61.291138 38.205280 61.291138 c +32.198051 61.291138 27.670864 58.418118 23.491922 54.239174 c +24.014292 56.154522 l +24.101355 58.940483 22.534250 60.855831 19.400043 60.855831 c +h +79.121361 54.587418 m +79.991974 58.243992 78.250748 60.855831 74.768295 60.855831 c +71.982338 60.855831 69.805801 58.940483 68.935188 55.545090 c +59.532574 20.720577 l +58.400776 16.454575 60.316124 13.929794 63.798576 13.929794 c +66.584534 13.929794 68.761070 15.845146 69.718742 19.153473 c +73.027069 31.429113 l +76.074219 42.747078 82.690872 48.667252 91.309944 50.582596 c +94.792397 51.453213 96.272430 53.281498 96.272430 56.154522 c +96.272430 59.114601 94.531212 60.942890 91.658188 60.942890 c +86.869820 60.942890 81.907318 57.286320 78.076622 50.669659 c +79.121361 54.587418 l +h +115.793884 13.320366 m +130.420181 13.320366 142.608765 26.205437 142.608765 40.222305 c +142.608765 52.410885 134.163818 61.291138 121.017563 61.291138 c +106.217140 61.291138 94.202690 48.319004 94.202690 34.302139 c +94.202690 22.200619 102.647629 13.320366 115.793884 13.320366 c +h +116.403313 22.461800 m +109.438408 22.461800 104.650040 26.901928 104.650040 34.650383 c +104.650040 42.834145 111.353760 52.149700 120.408134 52.149700 c +127.373039 52.149700 132.161407 47.709576 132.161407 39.961121 c +132.161407 31.690300 125.283562 22.461800 116.403313 22.461800 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 12184 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 600.000000 87.716492 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000012274 00000 n +0000012298 00000 n +0000012472 00000 n +0000012546 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +12605 +%%EOF \ No newline at end of file