Form.vue 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  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, PropType, 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 props = defineProps({
  88. jobPostingId: {
  89. type: Number as PropType<number | null>,
  90. required: false,
  91. default: null,
  92. },
  93. })
  94. const { em } = useEntityManager()
  95. const form: Ref<HTMLElement | null> = ref(null)
  96. const jobApplicationSent: Ref<boolean> = ref(false)
  97. const emit = defineEmits(['submit'])
  98. // @ts-ignore
  99. const jobApplication: ContactRequest = reactive(em.newInstance(JobApplication))
  100. const resumeUpload = ref(null)
  101. const motivationLetterUpload = ref(null)
  102. // --- Validation ---
  103. const maxMessageLength = 2000
  104. const leftCars: ComputedRef<number> = computed(
  105. () =>
  106. maxMessageLength -
  107. (jobApplication.message ? jobApplication.message.length : 0)
  108. )
  109. // Taille maximum en Mo
  110. const maxFileSize = 5
  111. const validateName = (name: string | null) => !!name || 'Le nom est obligatoire'
  112. const validateSurname = (surname: string | null) =>
  113. !!surname || 'Le prénom est obligatoire'
  114. const validateEmail = (email: string | null) =>
  115. (!!email && /.+@.+\..+/.test(email)) || "L'adresse e-mail doit être valide"
  116. const validatePhone = (email: string | null) =>
  117. (!!email && /^((\+|00)33\s?|0)[1-7]([\s.]?\d{2}){4}$/.test(email)) ||
  118. 'Le numéro de téléphone doit être valide'
  119. const validateResume = () =>
  120. (resumeUpload.value !== null && resumeUpload.value[0] !== null) ||
  121. "Vous devez joindre un CV à l'un des formats suivants : .pdf, .jpeg, .png"
  122. const validateResumeFileSize = () =>
  123. (resumeUpload.value !== null &&
  124. // @ts-ignore
  125. resumeUpload.value.size < maxFileSize * 1024 * 1024) ||
  126. 'La taille du fichier ne doit pas dépasser ' + maxFileSize + ' Mo'
  127. const validateMotivationLetter = () =>
  128. motivationLetterUpload.value === null ||
  129. motivationLetterUpload.value[0] !== null ||
  130. "Vous devez joindre votre lettre de motivation à l'un des formats suivants : .pdf, .jpeg, .png"
  131. const validateMotivationLetterFileSize = () =>
  132. motivationLetterUpload.value === null ||
  133. // @ts-ignore
  134. motivationLetterUpload.value.size < maxFileSize * 1024 * 1024 ||
  135. 'La taille du fichier ne doit pas dépasser ' + maxFileSize + ' Mo'
  136. const validateNonEmptyMessage = (message: string | null) =>
  137. (!!message && message.length > 0) || 'Le message ne peut pas être vide'
  138. const validateMessageLength = (message: string | null) =>
  139. (!!message && message.length <= maxMessageLength) ||
  140. 'Le message ne doit pas dépasser ' + maxMessageLength + ' caractères'
  141. /**
  142. * Soumet le formulaire de candidature (boite de dialogue)
  143. */
  144. const submit = async () => {
  145. jobApplication.jobPostingId = props.jobPostingId
  146. jobApplication.resume =
  147. resumeUpload.value !== null
  148. ? {
  149. // @ts-ignore
  150. name: resumeUpload.value.name,
  151. content: await FileUtils.blobToBase64(resumeUpload.value),
  152. }
  153. : null
  154. jobApplication.motivationLetter =
  155. motivationLetterUpload.value !== null
  156. ? {
  157. // @ts-ignore
  158. name: motivationLetterUpload.value.name,
  159. content: await FileUtils.blobToBase64(motivationLetterUpload.value),
  160. }
  161. : null
  162. // @ts-ignore
  163. const { valid } = await form.value!.validate()
  164. if (!valid) {
  165. jobApplicationSent.value = false
  166. return
  167. }
  168. await em.persist(JobApplication, jobApplication)
  169. jobApplicationSent.value = true
  170. emit('submit')
  171. }
  172. </script>
  173. <style scoped lang="scss">
  174. .submit {
  175. width: 100%;
  176. margin-bottom: 0 !important;
  177. height: 55px;
  178. background: var(--secondary-color);
  179. }
  180. .submit:hover {
  181. background-color: var(--on-neutral-color-extra-light);
  182. }
  183. </style>