Contact.vue 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. <template>
  2. <div>
  3. <div id="anchor" />
  4. <v-form
  5. v-if="!contactRequestSent"
  6. ref="form"
  7. validate-on="submit lazy"
  8. @submit.prevent="submit"
  9. >
  10. <v-container>
  11. <v-row>
  12. <v-col cols="12" md="6">
  13. <v-text-field
  14. v-model="email"
  15. :rules="[validateEmail]"
  16. :label="$t('contact_email')"
  17. required
  18. type="email"
  19. />
  20. </v-col>
  21. <v-col cols="12" md="6">
  22. <v-text-field
  23. v-model="name"
  24. :label="$t('contact_name')"
  25. required
  26. />
  27. </v-col>
  28. </v-row>
  29. <v-row>
  30. <v-col cols="12">
  31. <v-textarea
  32. v-model="message"
  33. :rules="[validateNonEmptyMessage]"
  34. :label="$t('contact_message')"
  35. required
  36. maxlength="400"
  37. />
  38. </v-col>
  39. </v-row>
  40. <v-row>
  41. <v-col cols="12" class="captcha-container">
  42. <AltchaValidation v-if="!appStore.altchaPayload"/>
  43. <v-card v-else class="pa-2">{{ $t('captcha_already_verified') }}</v-card>
  44. <v-checkbox
  45. v-model="honeyPotChecked"
  46. :rules="[validateCaptcha]"
  47. class="hidden-ctrl"
  48. />
  49. </v-col>
  50. </v-row>
  51. <!-- Submit Button -->
  52. <div class="d-flex flex-row justify-center">
  53. <v-btn
  54. type="submit"
  55. variant="outlined"
  56. :height="54"
  57. :width="180"
  58. class="submit-btn"
  59. >
  60. {{ $t('contact_submit') }}
  61. </v-btn>
  62. </div>
  63. <div v-if="errorMsg" class="error">
  64. {{ errorMsg }}
  65. </div>
  66. </v-container>
  67. </v-form>
  68. <div v-else class="confirmation-message d-flex flex-row justify-center">
  69. <v-card>
  70. <v-icon icon="fas fa-check mr-1" />
  71. {{ $t('contact_confirmation') }}
  72. </v-card>
  73. </div>
  74. </div>
  75. </template>
  76. <script setup lang="ts">
  77. import type { Ref } from '@vue/reactivity'
  78. import { ref } from 'vue'
  79. import { useI18n } from 'vue-i18n'
  80. const appStore = useAppStore()
  81. const form: Ref<HTMLElement | null> = ref(null)
  82. const contactRequestSent: Ref<boolean> = ref(false)
  83. const errorMsg: Ref<string | null> = ref(null)
  84. const i18n = useI18n()
  85. const email: Ref<string | null> = ref(null)
  86. const name: Ref<string | null> = ref(null)
  87. const message: Ref<string | null> = ref(null)
  88. // Honeypot checkbox (if checked: it's probably a bot)
  89. const honeyPotChecked: Ref<boolean> = ref(false)
  90. const validateEmail = (email: string | null) =>
  91. (!!email && /.+@.+\..+/.test(email)) || i18n.t("email_must_be_valid")
  92. const validateNonEmptyMessage = (message: string | null) =>
  93. (!!message && message.length > 10) || i18n.t("message_must_be_valid")
  94. const validateCaptcha = () =>
  95. !honeyPotChecked.value && appStore.altchaPayload !== null || i18n.t("captcha_must_be_validated")
  96. /**
  97. * Submits the contact form.
  98. *
  99. * This function validates the form and sets the value of a variable to indicate whether the form submission was successful.
  100. *
  101. * @function
  102. *
  103. * @returns {void}
  104. */
  105. const submit = async (): Promise<void> => {
  106. const { valid } = await form.value!.validate()
  107. if (!valid) {
  108. contactRequestSent.value = false
  109. return
  110. }
  111. const url = 'https://api.ogene.fr/api/contact?recipient=sauvagerie';
  112. const headers = {
  113. 'Content-Type': 'application/ld+json'
  114. };
  115. const body = {
  116. "email": email.value,
  117. "name": name.value ?? '-',
  118. "message": message.value,
  119. "altchaPayload": appStore.altchaPayload
  120. };
  121. try {
  122. const response = await fetch(url, {
  123. method: 'POST',
  124. headers: headers,
  125. body: JSON.stringify(body)
  126. });
  127. if (!response.ok) {
  128. throw new Error(`HTTP error! status: ${response.status}`);
  129. }
  130. await response.json();
  131. } catch (error) {
  132. console.error('There was a problem with the fetch operation: ', error);
  133. }
  134. contactRequestSent.value = true
  135. errorMsg.value = null
  136. }
  137. </script>
  138. <style scoped lang="scss">
  139. .captcha-container {
  140. display: flex;
  141. flex-direction: column;
  142. align-items: center;
  143. margin: 24px 0;
  144. :deep(altcha-widget) {
  145. min-width: 280px;
  146. @media (max-width: 600px) {
  147. .altcha {
  148. margin: 0 auto;
  149. }
  150. }
  151. }
  152. }
  153. .confirmation-message .v-card {
  154. padding: 14px;
  155. }
  156. .hidden-ctrl {
  157. :deep(.v-input__control) {
  158. display: none;
  159. }
  160. }
  161. </style>