Form.vue 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. <template>
  2. <div>
  3. <v-card v-if="!jobApplicationSent">
  4. <v-card-title class="text-center">
  5. Formulaire de Candidature
  6. </v-card-title>
  7. <v-card-text>
  8. <v-form ref="form" validate-on="submit lazy" @submit.prevent="submit">
  9. <v-text-field
  10. id="jobApplicationName"
  11. v-model="jobApplication.name"
  12. :rules="[validateName]"
  13. label="Nom*"
  14. required
  15. />
  16. <v-text-field
  17. id="jobApplicationSurname"
  18. v-model="jobApplication.surname"
  19. :rules="[validateSurname]"
  20. label="Prénom*"
  21. required
  22. />
  23. <v-text-field
  24. id="jobApplicationPhone"
  25. v-model="jobApplication.phone"
  26. :rules="[validatePhone]"
  27. label="Téléphone*"
  28. required
  29. />
  30. <v-text-field
  31. id="jobApplicationEmail"
  32. v-model="jobApplication.email"
  33. :rules="[validateEmail]"
  34. label="Email*"
  35. required
  36. />
  37. <v-file-input
  38. id="jobApplicationResume"
  39. v-model="resumeUpload"
  40. :rules="[validateResume, validateResumeFileSize]"
  41. label="Dépôt de CV*"
  42. accept=".pdf, .jpeg, .png"
  43. show-size
  44. required
  45. />
  46. <v-file-input
  47. id="jobApplicationMotivationLetter"
  48. v-model="motivationLetterUpload"
  49. :rules="[
  50. validateMotivationLetter,
  51. validateMotivationLetterFileSize,
  52. ]"
  53. label="Dépôt de lettre de motivation"
  54. accept=".pdf, .jpeg, .png"
  55. show-size
  56. />
  57. <v-textarea
  58. id="jobApplicationMessage"
  59. v-model="jobApplication.message"
  60. :rules="[validateNonEmptyMessage, validateMessageLength]"
  61. label="Message*"
  62. required
  63. />
  64. <span class="remaining-cars-notice"
  65. >{{ leftCars }} caractères restants</span
  66. >
  67. <div class="d-flex flex-column align-center mt-4">
  68. <!-- @see https://github.com/hCaptcha/vue-hcaptcha -->
  69. <LayoutCaptcha />
  70. </div>
  71. </v-form>
  72. </v-card-text>
  73. <p class="text-right mr-6">* Champs obligatoires</p>
  74. <v-card-actions class="justify-center">
  75. <v-btn class="btn-more mb-4 submit" @click="submit"> Envoyer </v-btn>
  76. </v-card-actions>
  77. </v-card>
  78. </div>
  79. </template>
  80. <script setup lang="ts">
  81. import type { ComputedRef, Ref } from 'vue'
  82. import { reactive } from 'vue'
  83. import ContactRequest from '~/models/Maestro/ContactRequest'
  84. import { useEntityManager } from '~/composables/data/useEntityManager'
  85. import JobApplication from '~/models/Maestro/JobApplication'
  86. import FileUtils from '~/services/utils/FileUtils'
  87. const { em } = useEntityManager()
  88. const form: Ref<HTMLElement | null> = ref(null)
  89. const jobApplicationSent: Ref<boolean> = ref(false)
  90. const emit = defineEmits(['submit'])
  91. // @ts-ignore
  92. const jobApplication: ContactRequest = reactive(em.newInstance(JobApplication))
  93. const resumeUpload = ref(null)
  94. const motivationLetterUpload = ref(null)
  95. // --- Validation ---
  96. const maxMessageLength = 2000
  97. const leftCars: ComputedRef<number> = computed(
  98. () =>
  99. maxMessageLength -
  100. (jobApplication.message ? jobApplication.message.length : 0)
  101. )
  102. // Taille maximum en Mo
  103. const maxFileSize = 5
  104. const validateName = (name: string | null) => !!name || 'Le nom est obligatoire'
  105. const validateSurname = (surname: string | null) =>
  106. !!surname || 'Le prénom est obligatoire'
  107. const validateEmail = (email: string | null) =>
  108. (!!email && /.+@.+\..+/.test(email)) || "L'adresse e-mail doit être valide"
  109. const validatePhone = (email: string | null) =>
  110. (!!email && /^((\+|00)33\s?|0)[1-7]([\s.]?\d{2}){4}$/.test(email)) ||
  111. 'Le numéro de téléphone doit être valide'
  112. const validateResume = () =>
  113. (resumeUpload.value !== null && resumeUpload.value[0] !== null) ||
  114. "Vous devez joindre un CV à l'un des formats suivants : .pdf, .jpeg, .png"
  115. const validateResumeFileSize = () =>
  116. (resumeUpload.value !== null &&
  117. // @ts-ignore
  118. resumeUpload.value.size < maxFileSize * 1024 * 1024) ||
  119. 'La taille du fichier ne doit pas dépasser ' + maxFileSize + ' Mo'
  120. const validateMotivationLetter = () =>
  121. motivationLetterUpload.value === null ||
  122. motivationLetterUpload.value[0] !== null ||
  123. "Vous devez joindre votre lettre de motivation à l'un des formats suivants : .pdf, .jpeg, .png"
  124. const validateMotivationLetterFileSize = () =>
  125. motivationLetterUpload.value === null ||
  126. // @ts-ignore
  127. motivationLetterUpload.value.size < maxFileSize * 1024 * 1024 ||
  128. 'La taille du fichier ne doit pas dépasser ' + maxFileSize + ' Mo'
  129. const validateNonEmptyMessage = (message: string | null) =>
  130. (!!message && message.length > 0) || 'Le message ne peut pas être vide'
  131. const validateMessageLength = (message: string | null) =>
  132. (!!message && message.length <= maxMessageLength) ||
  133. 'Le message ne doit pas dépasser ' + maxMessageLength + ' caractères'
  134. /**
  135. * Soumet le formulaire de candidature (boite de dialogue)
  136. */
  137. const submit = async () => {
  138. jobApplication.resume =
  139. resumeUpload.value !== null
  140. ? {
  141. // @ts-ignore
  142. name: resumeUpload.value.name,
  143. content: await FileUtils.blobToBase64(resumeUpload.value),
  144. }
  145. : null
  146. jobApplication.motivationLetter =
  147. motivationLetterUpload.value !== null
  148. ? {
  149. // @ts-ignore
  150. name: motivationLetterUpload.value.name,
  151. content: await FileUtils.blobToBase64(motivationLetterUpload.value),
  152. }
  153. : null
  154. const { valid } = await form.value!.validate()
  155. if (!valid) {
  156. jobApplicationSent.value = false
  157. return
  158. }
  159. await em.persist(JobApplication, jobApplication)
  160. jobApplicationSent.value = true
  161. emit('submit')
  162. }
  163. </script>
  164. <style scoped lang="scss">
  165. .submit {
  166. width: 100%;
  167. margin-bottom: 0 !important;
  168. height: 55px;
  169. background: var(--secondary-color);
  170. }
  171. .submit:hover {
  172. background-color: var(--on-neutral-color-extra-light);
  173. }
  174. </style>