new.vue 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. <template>
  2. <div>
  3. <LayoutParametersSection>
  4. <UiForm
  5. ref="form"
  6. v-model="subdomain"
  7. :submit-actions="submitActions"
  8. :validation-pending="validationPending"
  9. :refresh-profile="true"
  10. >
  11. <v-container :fluid="true" class="container">
  12. <v-row>
  13. <v-col cols="12" sm="6">
  14. <div>{{ $t('pleaseEnterYourNewSubdomain') }} :</div>
  15. </v-col>
  16. </v-row>
  17. <v-row>
  18. <v-col cols="12" sm="6">
  19. <UiInputText
  20. v-model="subdomain.subdomain"
  21. field="subdomain"
  22. type="string"
  23. :rules="rules()"
  24. @update:model-value="onSubdomainUpdate"
  25. />
  26. </v-col>
  27. </v-row>
  28. <div class="validationMessage">
  29. <span v-if="validationPending">
  30. <v-progress-circular size="16" indeterminate />
  31. <i class="ml-2">{{ $t('validation_ongoing') }}</i>
  32. </span>
  33. <span v-else-if="subdomainAvailable === true" class="text-success">
  34. <v-icon>fa fa-check</v-icon>
  35. <i class="ml-2">{{ $t('this_subdomain_is_available') }}</i>
  36. </span>
  37. </div>
  38. </v-container>
  39. <template #form.button>
  40. <NuxtLink to="/parameters/website" class="no-decoration">
  41. <v-btn class="mr-4 theme-neutral">
  42. {{ $t('back') }}
  43. </v-btn>
  44. </NuxtLink>
  45. </template>
  46. </UiForm>
  47. </LayoutParametersSection>
  48. </div>
  49. </template>
  50. <script setup lang="ts">
  51. import type { Ref } from 'vue'
  52. import _ from 'lodash'
  53. import { useEntityManager } from '~/composables/data/useEntityManager'
  54. import Subdomain from '~/models/Organization/Subdomain'
  55. import { SUBMIT_TYPE } from '~/types/enum/enums'
  56. import type { AnyJson } from '~/types/data'
  57. import SubdomainValidation from '~/services/validation/subdomainValidation'
  58. import { useSubdomainValidation } from '~/composables/form/validation/useSubdomainValidation'
  59. import type Form from '~/components/Ui/Form.vue'
  60. definePageMeta({
  61. name: 'record_a_new_subdomain',
  62. })
  63. const i18n = useI18n()
  64. const { em } = useEntityManager()
  65. const { subdomainValidation } = useSubdomainValidation()
  66. const organizationProfileStore = useOrganizationProfileStore()
  67. // @ts-expect-error TODO à résoudre quand l'EM pourra gérer les types génériques
  68. const subdomain: Ref<Subdomain> = ref(
  69. em.newInstance(Subdomain, { organization: organizationProfileStore.id }),
  70. )
  71. const submitActions = computed(() => {
  72. const actions: AnyJson = {}
  73. actions[SUBMIT_TYPE.SAVE_AND_BACK] = '/parameters/website'
  74. return actions
  75. })
  76. const form: Ref<typeof Form | null> = ref(null)
  77. const subdomainAvailable: Ref<boolean | null> = ref(null)
  78. const validationPending: Ref<boolean> = ref(false)
  79. /**
  80. * Délai entre le dernier caractère saisi et la requête de vérification de la disponibilité du sous-domaine (en ms)
  81. */
  82. const inputDelay = 600
  83. /**
  84. * Procède à la vérification de disponibilité.
  85. * @param subdomain
  86. */
  87. const checkAvailability = async (subdomain: string) => {
  88. subdomainAvailable.value = await subdomainValidation.isAvailable(subdomain)
  89. validationPending.value = false
  90. form.value!.validate()
  91. }
  92. /**
  93. * Version debounced de la fonction checkAvailability
  94. * @see https://docs-lodash.com/v4/debounce/
  95. */
  96. const checkAvailabilityDebounced: _.DebouncedFunc<() => void> = _.debounce(
  97. async () => {
  98. if (subdomain.value.subdomain === null) {
  99. return
  100. }
  101. await checkAvailability(subdomain.value.subdomain)
  102. },
  103. inputDelay,
  104. )
  105. const onSubdomainUpdate = () => {
  106. subdomainAvailable.value = null
  107. if (
  108. subdomain.value.subdomain !== null &&
  109. SubdomainValidation.isValid(subdomain.value.subdomain)
  110. ) {
  111. // Le sous domaine est valide: on vérifie sa disponibilité
  112. validationPending.value = true
  113. checkAvailabilityDebounced()
  114. } else {
  115. validationPending.value = false
  116. }
  117. }
  118. /**
  119. * Règles de validation
  120. */
  121. const rules = () => [
  122. (subdomain: string | null) =>
  123. (subdomain !== null && subdomain.length > 0) ||
  124. i18n.t('please_enter_a_value_for_the_subdomain'),
  125. (subdomain: string | null) =>
  126. (subdomain !== null && subdomain.length >= 2 && subdomain.length <= 60) ||
  127. i18n.t('subdomain_need_to_have_0_to_60_cars'),
  128. (subdomain: string | null) =>
  129. SubdomainValidation.isValid(subdomain) ||
  130. i18n.t('subdomain_can_not_contain_spaces_caps_or_special_cars'),
  131. () =>
  132. subdomainAvailable.value !== false ||
  133. i18n.t('this_subdomain_is_already_in_use'),
  134. ]
  135. </script>
  136. <style scoped lang="scss">
  137. .validationMessage {
  138. height: 20px;
  139. min-height: 20px;
  140. }
  141. </style>