|
|
@@ -85,6 +85,7 @@
|
|
|
:rules="[validateRequired]"
|
|
|
label="Nom de la structure*"
|
|
|
required
|
|
|
+ @input="onStructureNameUpdated"
|
|
|
/>
|
|
|
</v-col>
|
|
|
</v-row>
|
|
|
@@ -174,7 +175,43 @@
|
|
|
</v-col>
|
|
|
</v-row>
|
|
|
|
|
|
-
|
|
|
+ <!-- Structure identifier -->
|
|
|
+ <v-row>
|
|
|
+ <v-col cols="12" md="6" class="mx-auto">
|
|
|
+ <v-text-field
|
|
|
+ v-model="trialRequest.structureIdentifier"
|
|
|
+ :rules="[
|
|
|
+ validateRequired,
|
|
|
+ validateSubdomain,
|
|
|
+ validateSubdomainAvailability,
|
|
|
+ ]"
|
|
|
+ label="Identifiant de la structure*"
|
|
|
+ required
|
|
|
+ class="text-center"
|
|
|
+ @input="onStructureIdentifierUpdated"
|
|
|
+ />
|
|
|
+ <div class="validationMessage">
|
|
|
+ <span v-if="validationPending">
|
|
|
+ <v-progress-circular size="16" indeterminate />
|
|
|
+ <i class="ml-2">Vérification en cours</i>
|
|
|
+ </span>
|
|
|
+ <span
|
|
|
+ v-else-if="subdomainAvailable === true"
|
|
|
+ class="text-success"
|
|
|
+ >
|
|
|
+ <v-icon>fa fa-check</v-icon>
|
|
|
+ <i class="ml-2"> Cet identifiant est disponible</i>
|
|
|
+ </span>
|
|
|
+ <span
|
|
|
+ v-else-if="subdomainAvailable === false"
|
|
|
+ class="text-error"
|
|
|
+ >
|
|
|
+ <v-icon>fa fa-x</v-icon>
|
|
|
+ <i class="ml-2">Cet identifiant n'est pas disponible</i>
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </v-col>
|
|
|
+ </v-row>
|
|
|
|
|
|
<h2 class="section-title">Représentée par</h2>
|
|
|
|
|
|
@@ -343,24 +380,34 @@
|
|
|
import { useRouter } from 'vue-router'
|
|
|
import type { Ref } from 'vue'
|
|
|
import { reactive } from 'vue'
|
|
|
+import _ from 'lodash'
|
|
|
import { useRuntimeConfig } from '#app'
|
|
|
import type { TrialRequest } from '~/types/interface'
|
|
|
import { STRUCTURE_TYPE, LEGAL_STATUS } from '~/types/types'
|
|
|
+import { useAp2iRequestService } from '~/composables/data/useAp2iRequestService'
|
|
|
+import { slugify } from '~/utils/string'
|
|
|
|
|
|
const router = useRouter()
|
|
|
|
|
|
const form: Ref<HTMLElement | null> = ref(null)
|
|
|
|
|
|
// Structure types and legal statuses
|
|
|
-const structureTypes = Object.values(STRUCTURE_TYPE).map((item) => ({
|
|
|
- value: item.key,
|
|
|
- title: item.label,
|
|
|
-})).sort((a, b) => (a.title > b.title ? 1 : -1))
|
|
|
-
|
|
|
-const legalStatuses = Object.values(LEGAL_STATUS).map((item) => ({
|
|
|
- value: item.key,
|
|
|
- title: item.label,
|
|
|
-})).sort((a, b) => (a.title > b.title ? 1 : -1))
|
|
|
+const structureTypes = Object.values(STRUCTURE_TYPE)
|
|
|
+ .map((item) => ({
|
|
|
+ value: item.key,
|
|
|
+ title: item.label,
|
|
|
+ }))
|
|
|
+ .sort((a, b) => (a.title > b.title ? 1 : -1))
|
|
|
+
|
|
|
+const legalStatuses = Object.values(LEGAL_STATUS)
|
|
|
+ .map((item) => ({
|
|
|
+ value: item.key,
|
|
|
+ title: item.label,
|
|
|
+ }))
|
|
|
+ .sort((a, b) => (a.title > b.title ? 1 : -1))
|
|
|
+
|
|
|
+// Get apiRequestService for subdomain availability check
|
|
|
+const { ap2iRequestService } = useAp2iRequestService()
|
|
|
|
|
|
// Trial request data
|
|
|
const trialRequest = reactive<TrialRequest>({
|
|
|
@@ -372,6 +419,7 @@ const trialRequest = reactive<TrialRequest>({
|
|
|
structureEmail: '',
|
|
|
structureType: 'ARTISTIC_PRACTICE_ONLY',
|
|
|
legalStatus: 'ASSOCIATION_LAW_1901',
|
|
|
+ structureIdentifier: '',
|
|
|
siren: '',
|
|
|
representativeFirstName: '',
|
|
|
representativeLastName: '',
|
|
|
@@ -383,6 +431,49 @@ const trialRequest = reactive<TrialRequest>({
|
|
|
newsletterSubscription: false,
|
|
|
})
|
|
|
|
|
|
+// Track if structure identifier has been manually modified
|
|
|
+const structureIdentifierModified = ref(false)
|
|
|
+
|
|
|
+// Variables for subdomain validation
|
|
|
+const validationPending = ref(false)
|
|
|
+const subdomainAvailable = ref<boolean | null>(null)
|
|
|
+
|
|
|
+const checkSubdomainAvailability = async (subdomain: string) => {
|
|
|
+ if (!subdomain || validateSubdomain(subdomain) !== true) {
|
|
|
+ subdomainAvailable.value = null
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ validationPending.value = true
|
|
|
+ try {
|
|
|
+ const subdomainAvailability = await ap2iRequestService.get(
|
|
|
+ '/api/public/subdomains/is_available',
|
|
|
+ { subdomain }
|
|
|
+ )
|
|
|
+
|
|
|
+ subdomainAvailable.value =
|
|
|
+ subdomainAvailability && subdomainAvailability.available === true
|
|
|
+ validationPending.value = false
|
|
|
+ return subdomainAvailable.value
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Error checking subdomain availability:', error)
|
|
|
+ subdomainAvailable.value = false
|
|
|
+ validationPending.value = false
|
|
|
+ return false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Version debounced de la fonction checkAvailability
|
|
|
+ * @see https://docs-lodash.com/v4/debounce/
|
|
|
+ */
|
|
|
+const checkSubdomainAvailabilityDebounced: _.DebouncedFunc<() => void> =
|
|
|
+ _.debounce(
|
|
|
+ async () =>
|
|
|
+ await checkSubdomainAvailability(trialRequest.structureIdentifier),
|
|
|
+ 600
|
|
|
+ )
|
|
|
+
|
|
|
// Validation rules
|
|
|
const validateRequired = (value: string) =>
|
|
|
!!value || 'Ce champ est obligatoire'
|
|
|
@@ -403,6 +494,25 @@ const validateSiren = (siren: string) =>
|
|
|
const validateCheckbox = (value: boolean) =>
|
|
|
value || 'Vous devez accepter cette condition'
|
|
|
|
|
|
+const validateSubdomain = (value: string) => {
|
|
|
+ if (!value) return 'Ce champ est obligatoire'
|
|
|
+
|
|
|
+ const regex = /^[a-z0-9][a-z0-9-]{0,28}[a-z0-9]$/
|
|
|
+
|
|
|
+ return (
|
|
|
+ regex.test(value) ||
|
|
|
+ 'Format invalide. Utilisez uniquement des lettres minuscules, des chiffres et des tirets. Doit commencer et finir par une lettre ou un chiffre. Maximum 30 caractères.'
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+const validateSubdomainAvailability = (value: string) => {
|
|
|
+ if (!value) return ''
|
|
|
+
|
|
|
+ return (
|
|
|
+ subdomainAvailable.value === true || "Cet identifiant n'est pas disponible"
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
// Form state
|
|
|
const trialRequestSent: Ref<boolean> = ref(false)
|
|
|
const errorMsg: Ref<string | null> = ref(null)
|
|
|
@@ -419,8 +529,6 @@ const submit = async (): Promise<void> => {
|
|
|
const config = useRuntimeConfig()
|
|
|
const apiUrl = `${config.public.apiBaseUrl}/trial/artists_premium`
|
|
|
|
|
|
- console.log('Sending trial request to:', apiUrl)
|
|
|
-
|
|
|
// Send the data to the API
|
|
|
const response = await fetch(apiUrl, {
|
|
|
method: 'POST',
|
|
|
@@ -448,6 +556,20 @@ const submit = async (): Promise<void> => {
|
|
|
"Une erreur s'est produite lors de l'activation de votre essai. Veuillez réessayer plus tard ou nous contacter directement."
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+// Event handler for structureName updates
|
|
|
+const onStructureNameUpdated = (newName: string) => {
|
|
|
+ if (!structureIdentifierModified.value && newName) {
|
|
|
+ trialRequest.structureIdentifier = slugify(trialRequest.structureName)
|
|
|
+ checkSubdomainAvailabilityDebounced()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Event handler for structureIdentifier updates
|
|
|
+const onStructureIdentifierUpdated = () => {
|
|
|
+ structureIdentifierModified.value = true
|
|
|
+ checkSubdomainAvailabilityDebounced()
|
|
|
+}
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|