Form.vue 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. <template>
  2. <LayoutContainer>
  3. <v-form
  4. ref="form"
  5. v-model="valid"
  6. lazy-validation
  7. >
  8. <v-container>
  9. <h4>
  10. Veuillez remplir le formulaire ci-dessous
  11. </h4>
  12. <i>Les champs dont le nom est suivi d'une astérisque (*) sont obligatoires</i>
  13. <h6>
  14. Vos coordonnées
  15. </h6>
  16. <!-- Gender selection -->
  17. <v-row>
  18. <v-col cols="12">
  19. <v-radio-group
  20. v-model="gender"
  21. row
  22. mandatory
  23. inline
  24. >
  25. <v-radio label="Madame" value="Madame" />
  26. <v-radio label="Monsieur" value="Monsieur" />
  27. </v-radio-group>
  28. </v-col>
  29. </v-row>
  30. <!-- Name and Surname -->
  31. <v-row>
  32. <v-col cols="12" md="6">
  33. <v-text-field
  34. v-model="name"
  35. :rules="[validateName]"
  36. label="Nom*"
  37. required
  38. />
  39. </v-col>
  40. <v-col cols="12" md="6">
  41. <v-text-field
  42. v-model="surname"
  43. :rules="[validateSurname]"
  44. label="Prénom*"
  45. required
  46. />
  47. </v-col>
  48. </v-row>
  49. <!-- Postal code and city -->
  50. <v-row>
  51. <v-col cols="12" md="6">
  52. <v-text-field
  53. v-model="postalCode"
  54. label="Code postal*"
  55. :rules="[validatePostalCode]"
  56. />
  57. </v-col>
  58. <v-col cols="12" md="6">
  59. <v-text-field
  60. v-model="city"
  61. label="Ville"
  62. />
  63. </v-col>
  64. </v-row>
  65. <!-- Email and phone on the same line -->
  66. <v-row>
  67. <v-col cols="12" md="6">
  68. <v-text-field
  69. v-model="email"
  70. :rules="[validateEmail]"
  71. label="Email*"
  72. required
  73. type="email"
  74. />
  75. </v-col>
  76. <v-col cols="12" md="6">
  77. <v-text-field
  78. v-model="phone"
  79. :rules="[validatePhone]"
  80. label="Téléphone*"
  81. type="tel"
  82. />
  83. </v-col>
  84. </v-row>
  85. <!-- Structure name -->
  86. <v-row>
  87. <v-col cols="12">
  88. <v-text-field
  89. v-model="structureName"
  90. :rules="[validateStructureName]"
  91. label="Nom de la structure*"
  92. required
  93. />
  94. </v-col>
  95. </v-row>
  96. <h6>
  97. Votre demande concerne
  98. </h6>
  99. <!-- Request type and product concerned -->
  100. <v-row>
  101. <v-col cols="12" md="6">
  102. <v-select
  103. v-model="requestType"
  104. :items="requestTypes"
  105. label="Votre demande concerne*"
  106. outlined
  107. dense
  108. />
  109. </v-col>
  110. <v-col cols="12" md="6">
  111. <v-text-field
  112. v-model="concernedProduct"
  113. label="Le produit concerné"
  114. outlined
  115. dense
  116. />
  117. </v-col>
  118. </v-row>
  119. <h6>
  120. Votre message
  121. </h6>
  122. <!-- Message -->
  123. <v-row>
  124. <v-col cols="12">
  125. <v-textarea
  126. v-model="message"
  127. :rules="[validateMessageLength]"
  128. label="Votre message*"
  129. required
  130. outlined
  131. dense
  132. maxlength="400"
  133. />
  134. </v-col>
  135. </v-row>
  136. <!-- Policy and checkboxes -->
  137. <v-checkbox
  138. v-model="privacyPolicy"
  139. :rules="[(v) => !!v || 'You must accept the privacy policy']"
  140. label="J'ai pris connaissance de la politique de confidentialité et j'accepte le traitement de mes données personnelles par Opentalent."
  141. />
  142. <v-checkbox
  143. v-model="newsletterSubscription"
  144. label="Je souhaite recevoir des communications d'Opentalent par email (promotions, informations logiciel…). Je pourrai me désinscrire à tout moment."
  145. />
  146. <!-- TODO: Remplacer par un vrai captcha
  147. voir:
  148. - https://nuxt.com/modules/recaptcha
  149. - https://nuxt.com/modules/turnstile
  150. -->
  151. <v-checkbox
  152. v-model="captchaChecked"
  153. :rules="[(v) => !!v || 'You must pass the captcha']"
  154. label="Captcha"
  155. />
  156. <!-- @see https://github.com/hCaptcha/vue-hcaptcha -->
  157. <vue-hcaptcha :sitekey="runtimeConfig.hCaptchaSiteKey" />
  158. <!-- Submit Button -->
  159. <v-row>
  160. <v-col cols="12">
  161. <v-btn
  162. :disabled="!valid"
  163. @click="submitForm"
  164. >
  165. Envoyer
  166. </v-btn>
  167. </v-col>
  168. </v-row>
  169. </v-container>
  170. </v-form>
  171. <div v-if="submissionStatus">
  172. {{ submissionStatus }}
  173. </div>
  174. </LayoutContainer>
  175. </template>
  176. <script setup lang="ts">
  177. import { ContactFormData } from "~/types/interface";
  178. import VueHcaptcha from '@hcaptcha/vue3-hcaptcha';
  179. const route = useRoute();
  180. const defaultRequestType = route.query.request;
  181. const runtimeConfig = useRuntimeConfig()
  182. // --- Constants ---
  183. const requestTypes: Array<string> = [
  184. "Demande d'information",
  185. "Demande de devis",
  186. "Demande de démonstration",
  187. "Demande d'option supplémentaire",
  188. "Autre",
  189. ];
  190. // --- Refs ---
  191. const name: Ref<string | null> = ref(null);
  192. const surname: Ref<string | null> = ref(null);
  193. const email: Ref<string | null> = ref(null);
  194. const structureName: Ref<string | null> = ref(null);
  195. const message: Ref<string | null> = ref(null);
  196. const privacyPolicy: Ref<boolean> = ref(false);
  197. const gender: Ref<string | null> = ref(null);
  198. const postalCode: Ref<string | null> = ref(null);
  199. const city: Ref<string | null> = ref(null);
  200. const phone: Ref<string | null> = ref(null);
  201. const concernedProduct: Ref<string | null> = ref(null);
  202. const newsletterSubscription: Ref<boolean> = ref(false);
  203. const captchaChecked: Ref<boolean> = ref(false);
  204. const submissionStatus: Ref<string | null> = ref(null);
  205. // --- Validation ---
  206. const validateName = (name: string | null) => !!name || "Le nom est obligatoire";
  207. const validateSurname = (surname: string | null) => !!surname || "Le prénom est obligatoire";
  208. const validatePostalCode = (postalCode: string | null) =>
  209. (!!postalCode && /^\d{5}$/.test(postalCode)) || "Le code postal doit être valide";
  210. const validateEmail = (email: string | null) =>
  211. (!!email && /.+@.+\..+/.test(email)) || "L'adresse e-mail doit être valide";
  212. const validatePhone = (email: string | null) =>
  213. (!!email && /^((\+|00)33\s?|0)[67]([\s.]?\d{2}){4}$/.test(email)) || "Le numéro de téléphone doit être valide";
  214. const validateStructureName = (structureName: string | null) =>
  215. !!structureName || "Le nom de la structure est requis";
  216. const validateMessageLength = (message: string | null) =>
  217. (!!message && message.length <= 400) ||
  218. "Le message ne doit pas dépasser 400 caractères";
  219. // TODO: revoir la validation, ça devrait être géré directement dans le v-form
  220. const valid: ComputedRef<boolean> = computed(() => {
  221. return (
  222. validateName(name.value) === true &&
  223. validateSurname(surname.value) === true &&
  224. validatePostalCode(postalCode.value) === true &&
  225. validateEmail(email.value) === true &&
  226. validatePhone(phone.value) === true &&
  227. validateStructureName(structureName.value) === true &&
  228. validateMessageLength(message.value) === true &&
  229. privacyPolicy.value === true &&
  230. captchaChecked.value === true
  231. );
  232. });
  233. // --- Form data (voir si utile) ---
  234. const formData: ContactFormData = reactive({
  235. gender: null,
  236. postalCode: null,
  237. city: "",
  238. phone: null,
  239. requestType: null,
  240. concernedProduct: "",
  241. newsletterSubscription,
  242. });
  243. const formRefs = {
  244. ...toRefs(formData),
  245. name,
  246. surname,
  247. email,
  248. structureName,
  249. message,
  250. privacyPolicy,
  251. valid,
  252. };
  253. // --- Methods ---
  254. const submitForm = () => {
  255. if (valid.value) {
  256. // Logique d'envoi du formulaire
  257. // TODO: implémenter
  258. submissionStatus.value = "Mail envoyé à contact@opentalent.fr";
  259. } else {
  260. console.log("Validation failed!");
  261. submissionStatus.value = "";
  262. }
  263. };
  264. const requestType: Ref<string | null> = ref(
  265. defaultRequestType === "demo" ? "Demande de démonstration" : null
  266. );
  267. </script>
  268. <style scoped lang="scss">
  269. .v-form {
  270. max-width: 1400px;
  271. margin: 0 auto;
  272. h4 {
  273. font-size: 40px;
  274. line-height: 95px;
  275. margin-bottom: 1rem;
  276. }
  277. h6 {
  278. margin-top: 32px;
  279. font-size: 20px;
  280. margin-bottom: 1rem;
  281. }
  282. }
  283. </style>