Преглед изворни кода

refact page contact et news/id

Olivier Massot пре 1 година
родитељ
комит
ed95e31585

+ 4 - 1
components/About/Agenda.vue → components/Common/Agenda.vue

@@ -1,3 +1,6 @@
+<!--
+Section "Agenda des évènements"
+-->
 <template>
   <AnchoredSection id="agenda">
     <LayoutContainer class="mb-12" >
@@ -9,7 +12,7 @@
         </v-col>
 
         <v-col cols="4">
-          <!-- Factoriser les controles du carousel dans un component -->
+          <!-- Factoriser les contrôles du carousel dans un component -->
           <div class="carousel-controls">
             <v-btn
               icon="fas fa-chevron-left"

+ 2 - 2
components/Common/Share.vue

@@ -38,7 +38,7 @@
         name: "Messenger",
         icon: "fa-brands fa-facebook-messenger",
         colorOnHover: "#0085ff",
-        url: `https://www.facebook.com/dialog/send?link=${encodeURIComponent(url.value)}`
+        url: `https://messenger.com`
       },
       {
         name: "Instagram",
@@ -74,7 +74,7 @@
   line-height: 28px;
 
   i {
-    font-size: 1.9em;
+    font-size: 32px;
     margin: 0.5rem 0.3rem;
     color: #808080;
   }

+ 12 - 343
components/Contact/Banner.vue

@@ -1,358 +1,27 @@
 <template>
   <LayoutContainer>
-    <LayoutUITitlePage
-      title="Besoin d'aide ?"
-      subtitle="Notre équipe est là pour vous. contactez-nous!. "
-    />
+    <LayoutUITitlePage>
+      Besoin d'aide ?
+      <template #subtitle>Notre équipe est là pour vous. contactez-nous!. </template>
+    </LayoutUITitlePage>
+
     <v-row>
-      <v-col cols="12" style="position: relative">
-        <img
+      <v-col cols="12" class="position-relative">
+        <v-img
           src="/images/formation/banner.jpg"
-          alt="line"
-          class="cover-image"
+          alt="banner"
+          cover
+          class="banner"
         />
       </v-col>
     </v-row>
-    <div class="container-contact">
-      <v-form ref="form" v-model="valid" lazy-validation>
-        <v-container>
-          <h4 class="title-h4">Veuillez remplir le formulaire ci-dessous</h4>
-
-          <h6 class="infos">Vos coordonnées</h6>
-
-          <!-- Gender selection -->
-          <v-row>
-            <v-col cols="12">
-              <v-radio-group v-model="gender" row mandatory inline>
-                <v-radio label="Madame" value="Madame"></v-radio>
-                <v-radio label="Monsieur" value="Monsieur"></v-radio>
-              </v-radio-group>
-            </v-col>
-          </v-row>
-
-          <!-- Name and Surname -->
-          <v-row>
-            <v-col cols="12" md="6">
-              <v-text-field
-                v-model="name"
-                :rules="nameRules"
-                label="Nom*"
-                required
-              ></v-text-field>
-            </v-col>
-            <v-col cols="12" md="6">
-              <v-text-field
-                v-model="surname"
-                :rules="surnameRules"
-                label="Prénom*"
-                required
-              ></v-text-field>
-            </v-col>
-          </v-row>
-
-          <!-- Postal code and city -->
-          <v-row>
-            <v-col cols="12" md="6">
-              <v-text-field
-                v-model="postalCode"
-                label="Code postal*"
-                type="number"
-              ></v-text-field>
-            </v-col>
-            <v-col cols="12" md="6">
-              <v-text-field v-model="city" label="Ville"></v-text-field>
-            </v-col>
-          </v-row>
-
-          <!-- Email and phone on the same line -->
-          <v-row>
-            <v-col cols="12" md="6">
-              <v-text-field
-                v-model="email"
-                :rules="emailRules"
-                label="Email*"
-                required
-                type="email"
-              ></v-text-field>
-            </v-col>
-            <v-col cols="12" md="6">
-              <v-text-field
-                v-model="phone"
-                label="Téléphone*"
-                type="tel"
-              ></v-text-field>
-            </v-col>
-          </v-row>
-
-          <!-- Structure name  -->
-          <v-row>
-            <v-col cols="12">
-              <v-text-field
-                v-model="structureName"
-                :rules="structureNameRules"
-                label="Nom de la structure*"
-                required
-              ></v-text-field>
-            </v-col>
-          </v-row>
-          <h6 class="infos">Votre demande concerne</h6>
-
-          <!-- Request type and product concerned  -->
-          <v-row>
-            <v-col cols="12" md="6">
-              <v-select
-                v-model="requestType"
-                :items="requestTypes"
-                label="Votre demande concerne*"
-                outlined
-                dense
-              ></v-select>
-            </v-col>
-            <v-col cols="12" md="6">
-              <v-text-field
-                v-model="concernedProduct"
-                label="Le produit concerné"
-                outlined
-                dense
-              ></v-text-field>
-            </v-col>
-          </v-row>
-          <h6 class="infos">Votre message</h6>
-
-          <!-- Message  -->
-          <v-row>
-            <v-col cols="12">
-              <v-textarea
-                v-model="message"
-                :rules="messageRules"
-                label="Votre message*"
-                required
-                outlined
-                dense
-                maxlength="400"
-              ></v-textarea>
-            </v-col>
-          </v-row>
-
-          <!-- Policy and  checkboxes -->
-              <v-checkbox
-                v-model="privacyPolicy"
-                :rules="[(v) => !!v || 'You must accept the privacy policy']"
-                label="J'ai pris connaissance de la politique de confidentialité et j'accepte le traitement de mes données personnelles par Opentalent."
-              ></v-checkbox>
-
-              <v-checkbox
-                v-model="newsletterSubscription"
-                label="Je souhaite recevoir des communications d'Opentalent par email (promotions, informations logiciel…). Je pourrai me désinscrire à tout moment."
-              ></v-checkbox>
-
-              <v-checkbox
-                v-model="captchaChecked"
-                :rules="[(v) => !!v || 'You must pass the captcha']"
-                label="Captcha"
-              ></v-checkbox>
-
-          <!-- Submit Button -->
-          <v-row>
-            <v-col cols="12">
-              <v-btn :disabled="!valid" @click="submitForm">Envoyer</v-btn>
-            </v-col>
-          </v-row>
-        </v-container>
-      </v-form>
-
-      <div v-if="submissionStatus">
-        {{ submissionStatus }}
-      </div>
-    </div>
-
-    <div id="map" style="height: 500px"></div>
   </LayoutContainer>
 </template>
 
-<script setup>
-import { ref, reactive, computed, toRefs } from "vue";
-import { useRoute } from "vue-router";
-import { onMounted } from "vue";
-import L from "leaflet";
-import "leaflet/dist/leaflet.css";
-
-onMounted(() => {
-  const map = L.map("map").setView([46.075245, 6.570162], 16);
-
-  L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
-    maxZoom: 19,
-    attribution: "© OpenStreetMap contributors",
-  }).addTo(map);
-
-  const marker = L.marker([46.075245, 6.570162]).addTo(map);
-});
-const route = useRoute();
-const defaultRequestType = route.query.request;
-const captchaChecked = ref(false);
-
-const name = ref("");
-const surname = ref("");
-const email = ref("");
-const structureName = ref("");
-const message = ref("");
-const privacyPolicy = ref(false);
-const gender = ref(null);
-const postalCode = ref(null);
-const city = ref("");
-const phone = ref(null);
-const concernedProduct = ref("");
-const newsletterSubscription = ref(false);
-const submissionStatus = ref("");
-
-const validateName = (name) => !!name || "Name is required";
-const validateSurname = (surname) => !!surname || "Surname is required";
-const validateEmail = (email) =>
-  (!!email && /.+@.+\..+/.test(email)) || "E-mail must be valid";
-const validateStructureName = (structureName) =>
-  !!structureName || "Structure name is required";
-const validateMessage = (message) =>
-  (!!message && message.length <= 400) ||
-  "Message cannot exceed 400 characters";
-
-const valid = computed(() => {
-  return (
-    validateName(name.value) === true &&
-    validateSurname(surname.value) === true &&
-    validateEmail(email.value) === true &&
-    validateStructureName(structureName.value) === true &&
-    validateMessage(message.value) === true &&
-    privacyPolicy.value === true &&
-    captchaChecked.value === true
-  );
-});
-
-const requestTypes = [
-  "Demande d'information",
-  "Demande de devis",
-  "Demande de démonstration",
-  "Demande d'option supplémentaire",
-  "Autre",
-];
-const requestType = ref(
-  defaultRequestType === "demo" ? "Demande de démonstration" : null
-);
-
-const formData = reactive({
-  gender: null,
-  postalCode: null,
-  city: "",
-  phone: null,
-  requestType: null,
-  concernedProduct: "",
-  newsletterSubscription,
-});
-
-// Methods
-const submitForm = () => {
-  if (valid.value) {
-    // Logique d'envoi du formulaire
-    submissionStatus.value = "Mail envoyé à contact@opentalent.fr";
-  } else {
-    console.log("Validation failed!");
-    submissionStatus.value = "";
-  }
-};
-
-const formDataRefs = toRefs(formData);
-const formRefs = {
-  ...formDataRefs,
-  name,
-  surname,
-  email,
-  structureName,
-  message,
-  privacyPolicy,
-  valid,
-};
-</script>
-
-<style scoped>
-#map {
-  height: 100%;
-  width: 70%;
-  margin-left: auto;
-  margin-right: auto;
-}
-.infos {
-  font-size: 20px;
-  color: #000000;
-  margin-bottom: 1rem;
-}
-
-.container-contact {
-  max-width: 1400px;
-  margin: 0 auto;
-}
-.title-h4 {
-  font-size: 40px;
-  line-height: 95px;
-  color: #000000;
-  margin-bottom: 1rem;
-  color: rgba(0, 0, 0, 0.3);
-}
-.image-text {
-  position: absolute;
-  top: 40px;
-  left: 20px;
-  font-family: "Barlow";
-  color: white;
-  font-size: 3rem;
-  width: 30rem;
-  font-style: italic;
-  font-weight: 300;
-  line-height: 40px;
-}
-
-:deep().subtitle {
-  font-size: 1.5rem;
-  line-height: 2rem;
-  letter-spacing: 0.1rem;
-  margin-bottom: 1rem;
-  text-transform: uppercase;
-}
-
-.formation {
-  font-family: "Barlow";
-  font-style: normal;
-  font-weight: 600;
-  font-size: 90px;
-  line-height: 85px;
-  text-align: center;
-  color: #000000;
-  margin-bottom: 1rem;
-}
-
-.menu-container {
-  display: flex;
-  justify-content: space-around;
-  padding: 1rem 10rem;
-  background: white;
-  color: #bbb8b8;
-  font-family: "Barlow";
-  font-size: 12px;
-  line-height: 16px;
-  display: flex;
-  align-items: center;
-  text-align: center;
-  letter-spacing: 0.18em;
-  text-transform: uppercase;
-}
-
-.v-chip.active-menu {
-  background: black;
-  color: white;
-}
-
-.cover-image {
+<style scoped lang="scss">
+.banner {
   width: 100%;
   height: 15rem;
-  object-fit: cover;
   object-position: center 30%;
   margin: 0 auto;
   transform: scaleX(-1);

+ 331 - 0
components/Contact/Form.vue

@@ -0,0 +1,331 @@
+<template>
+  <LayoutContainer>
+    <v-form
+      ref="form"
+      v-model="valid"
+      lazy-validation
+    >
+      <v-container>
+        <h4>
+          Veuillez remplir le formulaire ci-dessous
+        </h4>
+
+        <i>Les champs dont le nom est suivi d'une astérisque (*) sont obligatoires</i>
+
+        <h6>
+          Vos coordonnées
+        </h6>
+
+        <!-- Gender selection -->
+        <v-row>
+          <v-col cols="12">
+            <v-radio-group
+              v-model="gender"
+              row
+              mandatory
+              inline
+            >
+              <v-radio label="Madame" value="Madame" />
+              <v-radio label="Monsieur" value="Monsieur" />
+            </v-radio-group>
+          </v-col>
+        </v-row>
+
+        <!-- Name and Surname -->
+        <v-row>
+          <v-col cols="12" md="6">
+            <v-text-field
+              v-model="name"
+              :rules="[validateName]"
+              label="Nom*"
+              required
+            />
+          </v-col>
+
+          <v-col cols="12" md="6">
+            <v-text-field
+              v-model="surname"
+              :rules="[validateSurname]"
+              label="Prénom*"
+              required
+            />
+          </v-col>
+        </v-row>
+
+        <!-- Postal code and city -->
+        <v-row>
+          <v-col cols="12" md="6">
+            <v-text-field
+              v-model="postalCode"
+              label="Code postal*"
+              :rules="[validatePostalCode]"
+            />
+          </v-col>
+
+          <v-col cols="12" md="6">
+            <v-text-field
+              v-model="city"
+              label="Ville"
+            />
+          </v-col>
+        </v-row>
+
+        <!-- Email and phone on the same line -->
+        <v-row>
+          <v-col cols="12" md="6">
+            <v-text-field
+              v-model="email"
+              :rules="[validateEmail]"
+              label="Email*"
+              required
+              type="email"
+            />
+          </v-col>
+
+          <v-col cols="12" md="6">
+            <!-- TODO: remplacer par un input dédié aux nums -->
+            <v-text-field
+              v-model="phone"
+              label="Téléphone*"
+              type="tel"
+            />
+          </v-col>
+        </v-row>
+
+        <!-- Structure name  -->
+        <v-row>
+          <v-col cols="12">
+            <v-text-field
+              v-model="structureName"
+              :rules="[validateStructureName]"
+              label="Nom de la structure*"
+              required
+            />
+          </v-col>
+        </v-row>
+
+        <h6>
+          Votre demande concerne
+        </h6>
+
+        <!-- Request type and product concerned  -->
+        <v-row>
+          <v-col cols="12" md="6">
+            <v-select
+              v-model="requestType"
+              :items="requestTypes"
+              label="Votre demande concerne*"
+              outlined
+              dense
+            />
+          </v-col>
+
+          <v-col cols="12" md="6">
+            <v-text-field
+              v-model="concernedProduct"
+              label="Le produit concerné"
+              outlined
+              dense
+            />
+          </v-col>
+        </v-row>
+
+        <h6>
+          Votre message
+        </h6>
+
+        <!-- Message  -->
+        <v-row>
+          <v-col cols="12">
+            <v-textarea
+              v-model="message"
+              :rules="[validateMessageLength]"
+              label="Votre message*"
+              required
+              outlined
+              dense
+              maxlength="400"
+            />
+          </v-col>
+        </v-row>
+
+        <!-- Policy and  checkboxes -->
+        <v-checkbox
+          v-model="privacyPolicy"
+          :rules="[(v) => !!v || 'You must accept the privacy policy']"
+          label="J'ai pris connaissance de la politique de confidentialité et j'accepte le traitement de mes données personnelles par Opentalent."
+        />
+
+        <v-checkbox
+          v-model="newsletterSubscription"
+          label="Je souhaite recevoir des communications d'Opentalent par email (promotions, informations logiciel…). Je pourrai me désinscrire à tout moment."
+        />
+
+        <!-- TODO: Remplacer par un vrai captcha
+        voir:
+          - https://nuxt.com/modules/recaptcha
+          - https://nuxt.com/modules/turnstile
+        -->
+        <v-checkbox
+          v-model="captchaChecked"
+          :rules="[(v) => !!v || 'You must pass the captcha']"
+          label="Captcha"
+        />
+
+        <!-- Submit Button -->
+        <v-row>
+          <v-col cols="12">
+            <v-btn
+              :disabled="!valid"
+              @click="submitForm"
+            >
+              Envoyer
+            </v-btn>
+          </v-col>
+        </v-row>
+      </v-container>
+    </v-form>
+
+    <div v-if="submissionStatus">
+      {{ submissionStatus }}
+    </div>
+
+  </LayoutContainer>
+</template>
+
+<script setup lang="ts">
+import { ComputedRef } from "vue";
+import { ContactFormData } from "~/types/interface";
+
+const route = useRoute();
+const defaultRequestType = route.query.request;
+
+// --- Constants ---
+const requestTypes: Array<string> = [
+  "Demande d'information",
+  "Demande de devis",
+  "Demande de démonstration",
+  "Demande d'option supplémentaire",
+  "Autre",
+];
+
+
+// --- Refs ---
+const name: Ref<string | null> = ref(null);
+
+const surname: Ref<string | null> = ref(null);
+
+const email: Ref<string | null> = ref(null);
+
+const structureName: Ref<string | null> = ref(null);
+
+const message: Ref<string | null> = ref(null);
+
+const privacyPolicy: Ref<boolean> = ref(false);
+
+const gender: Ref<string | null> = ref(null);
+
+const postalCode: Ref<string | null> = ref(null);
+
+const city: Ref<string | null> = ref(null);
+
+const phone: Ref<string | null> = ref(null);
+
+const concernedProduct: Ref<string | null> = ref(null);
+
+const newsletterSubscription: Ref<boolean> = ref(false);
+
+const captchaChecked: Ref<boolean> = ref(false);
+
+const submissionStatus: Ref<string | null> = ref(null);
+
+
+// --- Validation ---
+const validateName = (name: string | null) => !!name || "Le nom est obligatoire";
+
+const validateSurname = (surname: string | null) => !!surname || "Le prénom est obligatoire";
+
+const validatePostalCode = (postalCode: string | null) =>
+  (!!postalCode && /^\d{5}$/.test(postalCode)) || "Le code postal doit être valide";
+
+const validateEmail = (email: string | null) =>
+  (!!email && /.+@.+\..+/.test(email)) || "L'adresse e-mail doit être valide";
+
+const validateStructureName = (structureName: string | null) =>
+  !!structureName || "Le nom de la structure est requis";
+
+const validateMessageLength = (message: string | null) =>
+  (!!message && message.length <= 400) ||
+  "Le message ne doit pas dépasser 400 caractères";
+
+// TODO: revoir la validation, ça devrait être géré directement dans le v-form
+const valid: ComputedRef<boolean> = computed(() => {
+  return (
+    validateName(name.value) === true &&
+    validateSurname(surname.value) === true &&
+    validatePostalCode(postalCode.value) === true &&
+    validateEmail(email.value) === true &&
+    validateStructureName(structureName.value) === true &&
+    validateMessageLength(message.value) === true &&
+    privacyPolicy.value === true &&
+    captchaChecked.value === true
+  );
+});
+
+// --- Form data (voir si utile) ---
+const formData: ContactFormData = reactive({
+  gender: null,
+  postalCode: null,
+  city: "",
+  phone: null,
+  requestType: null,
+  concernedProduct: "",
+  newsletterSubscription,
+});
+
+const formRefs = {
+  ...toRefs(formData),
+  name,
+  surname,
+  email,
+  structureName,
+  message,
+  privacyPolicy,
+  valid,
+};
+
+// --- Methods ---
+const submitForm = () => {
+  if (valid.value) {
+    // Logique d'envoi du formulaire
+    // TODO: implémenter
+    submissionStatus.value = "Mail envoyé à contact@opentalent.fr";
+  } else {
+    console.log("Validation failed!");
+    submissionStatus.value = "";
+  }
+};
+
+const requestType: Ref<string | null> = ref(
+  defaultRequestType === "demo" ? "Demande de démonstration" : null
+);
+</script>
+
+<style scoped lang="scss">
+.v-form {
+  max-width: 1400px;
+  margin: 0 auto;
+
+  h4 {
+    font-size: 40px;
+    line-height: 95px;
+    margin-bottom: 1rem;
+  }
+
+  h6 {
+    margin-top: 32px;
+    font-size: 20px;
+    margin-bottom: 1rem;
+  }
+}
+</style>

+ 28 - 0
components/Contact/Map.vue

@@ -0,0 +1,28 @@
+<template>
+  <div id="map" style="height: 500px" />
+</template>
+
+<script setup lang="ts">
+import L from "leaflet";
+import "leaflet/dist/leaflet.css";
+
+onMounted(() => {
+  const map = L.map("map").setView([46.075245, 6.570162], 16);
+
+  L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
+    maxZoom: 19,
+    attribution: "© OpenStreetMap contributors",
+  }).addTo(map);
+
+  const marker = L.marker([46.075245, 6.570162]).addTo(map);
+});
+</script>
+
+<style scoped lang="scss">
+#map {
+  height: 100%;
+  width: 70%;
+  margin-left: auto;
+  margin-right: auto;
+}
+</style>

+ 1 - 0
components/JoinUs/MissionDetail.vue

@@ -15,6 +15,7 @@
           <v-btn
             to="/nous-rejoindre"
             prepend-icon="fas fa-arrow-left"
+            variant="outlined"
             class="back-button"
           >
             Retour aux annonces

+ 0 - 61
components/Logiciels/Manager/Agenda.vue

@@ -1,61 +0,0 @@
-<template>
-  <LayoutContainer>
-    <v-row>
-      <v-col cols="4">
-        <h3>L'agenda Opentalent</h3>
-      </v-col>
-
-      <v-col cols="4" />
-
-      <v-col cols="4">
-        <v-btn>Tous les évèvenements</v-btn>
-      </v-col>
-    </v-row>
-
-    <v-row>
-      <!-- slide card-->
-      <v-col cols="4">
-        <v-card>
-          <v-img
-            src="/images/logiciels/school/enseignement.jpg"
-            class="enseignement"
-          >
-            <v-card-title class="title">
-              Enseignement
-            </v-card-title>
-          </v-img>
-        </v-card>
-      </v-col>
-
-      <v-col cols="4">
-        <v-card>
-          <v-img
-            src="/images/logiciels/school/enseignement.jpg"
-            class="enseignement"
-          >
-            <v-card-title class="title">
-              Enseignement
-            </v-card-title>
-          </v-img>
-        </v-card>
-      </v-col>
-
-      <v-col cols="4">
-        <v-card>
-          <v-img
-            src="/images/logiciels/school/enseignement.jpg"
-            class="enseignement"
-          >
-            <v-card-title class="title">
-              Enseignement
-            </v-card-title>
-          </v-img>
-        </v-card>
-      </v-col>
-    </v-row>
-  </LayoutContainer>
-</template>
-
-<script setup></script>
-
-<style scoped></style>

+ 0 - 324
components/Logiciels/School/Agenda.vue

@@ -1,324 +0,0 @@
-<template>
-  <LayoutContainer :overflow="false">
-    <v-row>
-      <v-col cols="4">
-        <h2 class="title">
-          L'agenda Opentalent
-        </h2>
-      </v-col>
-
-      <v-col cols="4">
-        <div class="d-flex justify-center align-center">
-          <div
-            class="carousel-button"
-            @click="goPrevious"
-          >
-            <i class="fas fa-chevron-left" />
-          </div>
-          <div
-            class="carousel-button"
-            @click="goNext"
-          >
-            <i class="fas fa-chevron-right" />
-          </div>
-        </div>
-      </v-col>
-
-      <v-col cols="4">
-        <v-btn
-          class="btn-news"
-          text
-        >
-          VOIR TOUTES LES ACTUALITÉS
-        </v-btn>
-      </v-col>
-    </v-row>
-
-    <p class="agenda-details">
-      Retrouvez tous les évènements culturels autour de chez vous.
-    </p>
-
-    <v-row>
-      <v-col cols="12">
-        <Carousel
-          ref="carousel"
-          :items-to-show="3"
-          :items-to-scroll="2"
-        >
-          <Slide
-            v-for="(event, eventIndex) in events"
-            :key="eventIndex"
-            class="slide-card"
-          >
-            <div class="card">
-              <img
-                class="card-img-top"
-                :src="event.img"
-                alt="Card image cap"
-              >
-              <div class="card-body">
-                <small class="card-rdv">{{ event.rdv }}</small>
-                <h5 class="card-title">
-                  {{ event.title }}
-                </h5>
-                <p class="card-localisation">
-                  {{ event.localisation }}
-                </p>
-              </div>
-
-              <div class="card-footer">
-                <v-chip-group
-                  active-class="primary--text"
-                  column
-                >
-                  <v-chip
-                    v-for="(tag, tagIndex) in event.tags"
-                    :key="tagIndex"
-                    class="ma-2 chip-custom"
-                    :color="tagColor(tag)"
-                    label
-                  >
-                    <span :class="tagTextColor(tag)">{{ tag }}</span>
-                  </v-chip>
-                </v-chip-group>
-              </div>
-            </div>
-          </Slide>
-        </Carousel>
-      </v-col>
-    </v-row>
-  </LayoutContainer>
-</template>
-
-<script setup>
-import { ref } from "vue";
-import { Carousel, Slide } from "vue3-carousel";
-import "vue3-carousel/dist/carousel.css";
-const tagColor = (tag) => {
-  switch (tag) {
-    case "Payant":
-      return "red";
-    case "Gratuit":
-      return "green";
-    default:
-      return "primary";
-  }
-};
-
-const tagTextColor = (tag) => {
-  switch (tag) {
-    case "Payant":
-      return "red--text";
-    case "Gratuit":
-      return "green--text";
-    default:
-      return "black--text";
-  }
-};
-const events = ref([
-  {
-    rdv: "20h00",
-    title: "LA NUIT DES RÊVES  ",
-    localisation: "FESTIVALDE musique - LONGCHAMP",
-    date: "21/06/2023",
-    img: "/images/agenda/agenda2.jpg",
-    tags: ["Festival", "Musique", "Tout public", "Payant"],
-  },
-  {
-    rdv: "20h00",
-    title: "LE LAC DES CYGNES",
-    localisation: "SPECTACLE DE DANSE - PARIS 1",
-    date: "22/06/2023",
-    img: "/images/agenda/agenda3.jpg",
-    tags: ["Festival", "Musique", "Tout public", "Gratuit"],
-  },
-  {
-    rdv: "20h00",
-    title: "SOLIDAYS 2023  : 23 > 25 juin",
-    localisation: "ORCHESTRE DE PARIS - PARIS 12",
-    date: "23/06/2023",
-    img: "/images/agenda/agenda4.jpg",
-    tags: ["Festival", "Musique", "Tout public", "Payant"],
-  },
-  {
-    rdv: "20h00",
-    title: "LE LAC DES CYGNES",
-    localisation: "FESTIVALDE musique - LONGCHAMP",
-    date: "24/06/2023",
-    img: "/images/agenda/agenda5.jpg",
-    tags: ["Festival", "Musique", "Tout public", "Payant"],
-  },
-  {
-    rdv: "20h00",
-    title: "SOLIDAYS 2023  : 23 > 25 juin ",
-    localisation: "SPECTACLE DE DANSE - PARIS 1",
-    date: "20/06/2023",
-    img: "/images/agenda/agenda1.jpg",
-    tags: ["Festival", "Musique", "Tout public", "Payant"],
-  },
-]);
-
-let carousel;
-
-const goPrevious = () => carousel.prev();
-const goNext = () => carousel.next();
-</script>
-
-<style scoped>
-.green--text {
-  color: green;
-}
-
-.red--text {
-  color: red;
-}
-.black--text {
-  color: black;
-}
-.btn-news {
-  color: #9edbdd;
-  border-radius: 2rem;
-  font-family: "Barlow";
-  background: transparent;
-  border: 1px solid #9edbdd;
-  border-radius: 6px;
-  font-style: normal;
-  font-weight: 600;
-  text-transform: uppercase;
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  padding: 25px;
-  font-size: 10px;
-  line-height: 15px;
-}
-.chip-detail {
-  color: #000000;
-}
-.chip-custom {
-  color: white;
-  border: 1px solid #0e2d32;
-  border-radius: 3rem;
-  text-transform: uppercase;
-  font-family: "Barlow";
-  font-style: normal;
-  display: flex;
-  align-items: center;
-  text-align: center;
-}
-
-.card-localisation {
-  letter-spacing: 0.18em;
-  text-transform: uppercase;
-  font-size: 10px;
-  color: #112528;
-}
-.card {
-  border-radius: 15px 15px 0 0;
-  margin-bottom: 2rem;
-  width: 90%;
-}
-
-.icon-title {
-  color: #64afb7;
-  margin-top: 4.5rem;
-}
-.container-title {
-  display: flex;
-  align-items: center;
-  margin-left: 2rem;
-  margin-top: 4.5rem;
-}
-
-.carousel-button i {
-  color: #000000;
-}
-.card-text {
-  font-family: "Barlow";
-  font-style: normal;
-  font-weight: 500;
-  font-size: 16px;
-  line-height: 18px;
-  margin-bottom: 1rem;
-  color: #091d20;
-}
-.card-title {
-  font-family: "Barlow";
-  font-style: normal;
-  font-weight: 500;
-  font-size: 20px;
-  line-height: 24px;
-  display: flex;
-  align-items: center;
-  letter-spacing: 0.18em;
-  text-transform: uppercase;
-}
-.card-date {
-  font-size: 0.8em;
-  color: #888;
-  margin-left: 1rem;
-}
-
-.card-footer {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-}
-
-.card-body {
-  text-align: left;
-  margin-bottom: 1rem;
-  margin-left: 1rem;
-  font-family: "Barlow";
-  font-style: normal;
-  font-weight: 500;
-  line-height: 24px;
-  color: #112528;
-}
-
-.card-img-top {
-  border-radius: 9px 9px 0 0;
-  width: 100%;
-  object-fit: cover;
-  object-position: center;
-}
-
-.title,
-.carousel-button,
-.btn-news {
-  margin-top: 2rem;
-  margin-bottom: 2rem;
-}
-.agenda-details {
-  font-family: "Barlow";
-  font-style: normal;
-  font-weight: 300;
-  font-size: 16px;
-  line-height: 20px;
-  margin-left: 2rem;
-  color: #091d20;
-  margin-bottom: 3rem;
-  width: 15rem;
-}
-.title {
-  font-family: "Barlow";
-  font-style: normal;
-  font-weight: 600;
-  font-size: 42px;
-  line-height: 42px;
-  margin-left: 2rem;
-  color: #071b1f;
-}
-
-.carousel-button {
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  width: 40px;
-  height: 40px;
-  background-color: transparent;
-  border: 2px solid #000000;
-  cursor: pointer;
-  margin-right: 1rem;
-}
-</style>

+ 187 - 0
components/News/Details.vue

@@ -0,0 +1,187 @@
+<template>
+  <LayoutContainer>
+    <v-row>
+      <v-col cols="12">
+        <CommonBanner
+          imageSrc="/images/actu/pub.png"
+          imageAlt="banner"
+        />
+      </v-col>
+    </v-row>
+
+    <div class="news-section">
+      <v-row class="mb-6 custom-row">
+        <v-col class="d-flex align-items-center">
+          <v-btn
+            to="/actualites"
+            prepend-icon="fas fa-arrow-left"
+            variant="outlined"
+            class="back-button"
+          >
+            Retour aux actualités
+          </v-btn>
+        </v-col>
+      </v-row>
+
+      <div>
+        <div v-if="pending">
+          <v-row class="justify-center progress">
+            <v-progress-circular
+              indeterminate
+              color="grey"
+            />
+          </v-row>
+        </div>
+
+        <div v-else-if="newsItem !== null" class="news">
+          <v-row class="custom-row">
+            <v-col cols="6">
+              <v-img :src="getImageUrl(newsItem.attachment)"/>
+            </v-col>
+
+            <v-col cols="6">
+              <h4>
+                {{ newsItem.title }}
+              </h4>
+            </v-col>
+          </v-row>
+
+          <v-row class="custom-row">
+            <p class="description">
+              <!-- TODO: idem que pour les annonces -->
+              {{ newsItem.bodyText }}
+            </p>
+          </v-row>
+
+          <v-row class="d-flex justify-center align-center">
+            <v-btn prepend-icon="fas fa-info" class="btn-plus mb-12" text>
+              En savoir plus
+            </v-btn>
+          </v-row>
+
+          <v-row class="d-flex justify-space-between custom-row">
+            <p>
+              MOTS CLÉS
+            </p>
+            <div>
+              <p>PARTAGER</p>
+            </div>
+          </v-row>
+
+          <v-row class="d-flex justify-space-between mb-8 custom-row">
+            <p class="key-word mt-3">
+              <!-- TODO: remplacer par la bonne prop -->
+              ROCK CONCERT FESTIVAL
+            </p>
+
+            <CommonShare />
+          </v-row>
+        </div>
+      </div>
+    </div>
+
+    <CommonAgenda />
+  </LayoutContainer>
+</template>
+
+<script setup lang="ts">
+import { useEntityFetch } from "~/composables/data/useEntityFetch";
+import News from "~/models/Maestro/News";
+
+const route = useRoute();
+const { fetch } = useEntityFetch()
+const config = useRuntimeConfig()
+
+const newsId: number = parseInt(route.params.id as string)
+if (!newsId || isNaN(newsId)) {
+  throw new Error('Missing or invalid id')
+}
+
+const { data: newsItem, pending } = fetch(News, newsId)
+
+const getImageUrl = (attachment: string): string | null => {
+  if (!attachment) {
+    return null
+  }
+  return `${config.public.apiBaseUrl}/uploads/news/${attachment}`
+}
+</script>
+
+<style scoped>
+.custom-row {
+  width: 90%;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+.news {
+  .v-img {
+    width: 80%;
+    margin-left: 3.5rem;
+    margin-right: auto;
+  }
+
+  h4 {
+    margin-top: 11rem;
+    text-decoration: none;
+    text-transform: uppercase;
+    font-family: Barlow, serif;
+    font-size: 36px;
+    font-style: normal;
+    font-weight: 600;
+  }
+
+  .description {
+    color: #0e2d32;
+    text-align: justify;
+    font-family: "Barlow", serif;
+    font-size: 30px;
+    font-style: normal;
+    font-weight: 500;
+    line-height: 34px;
+    margin-left: 3.5rem;
+    margin-right: 3.5rem;
+    margin-bottom: 3rem;
+  }
+
+  .btn-plus {
+    background: var(--secondary-color);
+    color: var(--on-secondary-color);
+    display: flex;
+    left: 0;
+    padding: 25px 28px;
+    align-items: center;
+    gap: 9px;
+    font-family: Barlow, serif;
+    font-size: 0.9rem;
+    border-radius: 5px;
+    font-style: normal;
+    font-weight: 700;
+    line-height: 15px;
+    letter-spacing: 1.3px;
+    text-transform: uppercase;
+    margin-bottom: -1rem;
+  }
+}
+
+.back-button {
+  text-decoration: none;
+  color: #000000;
+  font-family: Barlow, serif;
+  font-size: 0.9rem;
+  font-style: normal;
+  font-weight: 700;
+  line-height: 15px;
+  letter-spacing: 1.8px;
+  text-transform: uppercase;
+}
+
+.key-word {
+  color: #000;
+  font-family: Barlow, serif;
+  font-size: 20px;
+  font-style: normal;
+  font-weight: 500;
+  line-height: 24px;
+}
+</style>

+ 6 - 10
components/News/List.vue

@@ -12,7 +12,7 @@
     </v-row>
   </div>
 
-  <div v-else-if="newsItems !== null">
+  <div>
     <v-row
       v-for="(newsItem, index) in newsItems"
       :key="index"
@@ -63,16 +63,13 @@
 
                       <div class="button-container">
                         <v-card-actions class="justify-end">
-                          <NuxtLink
+                          <v-btn
                             :to="`/actualites/${newsItem.id}`"
-                            class="text-decoration-none"
+                            prepend-icon="fas fa-info"
+                            class="btn mr-2 mb-1"
                           >
-                            <v-btn class="btn" text>
-                              <v-icon icon="fas fa-info" class="mr-2" />
-                              En
-                              savoir plus
-                            </v-btn>
-                          </NuxtLink>
+                            En savoir plus
+                          </v-btn>
                         </v-card-actions>
                       </div>
                     </div>
@@ -111,7 +108,6 @@ const i18n = useI18n();
 const config = useRuntimeConfig();
 const { fetchCollection } = useEntityFetch()
 
-const baseUrl = `${config.public.apiBaseUrl}/api/news`;
 const getImageUrl = (attachment: string) =>
   `${config.public.apiBaseUrl}/uploads/news/${attachment}`;
 

+ 2 - 513
pages/actualites/[id].vue

@@ -1,520 +1,9 @@
 <template>
   <LayoutNavigation />
-  <LayoutContainer>
-    <v-row>
-      <v-col cols="12">
-        <CommonBanner :imageSrc="'/images/actu/pub.png'" imageAlt="'line'" />
-      </v-col>
-    </v-row>
-
-    <div v-if="actu">
-      <v-row class="custom-row">
-        <v-col>
-          <div class="d-flex align-items-center">
-            <NuxtLink to="/actualites" style="text-decoration: none !important">
-              <div class="carousel-button">
-                <i class="fa-solid fa-arrow-left" />
-              </div>
-            </NuxtLink>
-            <NuxtLink to="/actualites" class="ml-2 back-button mt-12">
-              Retour aux actualités
-            </NuxtLink>
-          </div>
-        </v-col>
-      </v-row>
-
-      <v-row class="custom-row">
-        <v-col cols="6">
-          <v-img>
-            <img :src="getImageUrl(actu.attachment)" class="image-actu" />
-          </v-img>
-        </v-col>
-
-        <v-col cols="6">
-          <h4 class="title-actu">{{ actu.title }}</h4>
-        </v-col>
-      </v-row>
-
-      <v-row class="custom-row">
-        <p v-html="actu.bodyText" class="description-actu"></p>
-      </v-row>
-
-      <v-row class="d-flex justify-center align-center">
-        <v-btn class="btn mb-12" text>
-          <v-icon class="fas fa-info mr-2"></v-icon>En savoir plus
-        </v-btn>
-      </v-row>
-    </div>
-    <v-row class="d-flex justify-space-between custom-row">
-      <p class="share ml-6">MOTS CLÉS</p>
-      <div>
-        <p class="share mr-6">PARTAGER</p>
-      </div>
-    </v-row>
-
-    <v-row class="d-flex justify-space-between mb-8 custom-row">
-      <p class="key-word mt-3 ml-6">ROCK CONCERT FESTIVAL</p>
-      <div class="social-icons mr-6">
-        <a href="https://facebook.com" target="_blank" class="social-icon">
-          <i class="fa-brands fa-facebook"></i>
-        </a>
-        <a href="https://twitter.com" target="_blank" class="social-icon">
-          <i class="fa-brands fa-twitter"></i>
-        </a>
-        <a href="https://messenger.com" target="_blank" class="social-icon">
-          <i class="fa-brands fa-facebook-messenger"></i>
-        </a>
-        <a href="https://instagram.com" target="_blank" class="social-icon">
-          <i class="fa-brands fa-instagram"></i>
-        </a>
-        <a href="https://linkedin.com" target="_blank" class="social-icon">
-          <i class="fa-brands fa-linkedin"></i>
-        </a>
-        <a href="mailto:example@example.com" class="social-icon">
-          <i class="fa-solid fa-envelope"></i>
-        </a>
-      </div>
-    </v-row>
-
-    <v-row class="container-green">
-      <v-col cols="12">
-        <div class="d-flex justify-center align-center">
-          <div
-            class="carousel-button"
-            @click="goPrevious"
-            style="border: 2px solid #fff !important"
-          >
-            <i class="fas fa-chevron-left" style="color: white" />
-          </div>
-          <div
-            class="carousel-button"
-            @click="goNext"
-            style="border: 2px solid #fff !important"
-          >
-            <i class="fas fa-chevron-right" style="color: white" />
-          </div>
-        </div>
-        <Carousel ref="carousel" :items-to-show="3" :items-to-scroll="2">
-          <Slide
-            v-for="(event, index) in events"
-            :key="index"
-            class="slide-card"
-          >
-            <div class="card">
-              <img class="card-img-top" :src="event.img" alt="Card image cap" />
-              <div class="card-body">
-                <small class="card-rdv">{{ event.rdv }}</small>
-                <h5 class="card-title">
-                  {{ event.title }}
-                </h5>
-                <p class="card-localisation">
-                  {{ event.localisation }}
-                </p>
-              </div>
-
-              <div class="card-footer">
-                <v-chip-group active-class="primary--text" column>
-                  <v-chip
-                    v-for="(tag, indexTag) in event.tags"
-                    :key="indexTag"
-                    class="ma-2 chip-custom"
-                    :color="tagColor(tag)"
-                    label
-                  >
-                    <span :class="tagTextColor(tag)">{{ tag }}</span>
-                  </v-chip>
-                </v-chip-group>
-              </div>
-            </div>
-          </Slide>
-        </Carousel>
-      </v-col>
-    </v-row>
-  </LayoutContainer>
+  <NewsDetails />
   <LayoutFooterPrefooter />
   <LayoutFooter />
 </template>
-
 <script setup lang="ts">
-import { ref } from "vue";
-import { Carousel, Slide } from "vue3-carousel";
-import { useRoute } from "vue-router";
-import "vue3-carousel/dist/carousel.css";
-import { useMaestroRequestService } from "~/composables/data/useMaestroRequestService";
-const { apiRequestService } = useMaestroRequestService();
-
-const actu = ref(null);
-const route = useRoute();
-const newsId = route.params.id;
-const pending = ref(true);
-const config = useRuntimeConfig();
-
-onMounted(async () => {
-  try {
-    actu.value = await apiRequestService.get(
-      `${config.public.apiBaseUrl}/api/news/${newsId}`
-    );
-    pending.value = false;
-  } catch (error) {
-    console.error("Erreur lors de la récupération de l'actualité", error);
-    pending.value = false;
-  }
-});
-
-const getImageUrl = (attachment: string) => {
-  if (attachment) {
-    return `${config.public.apiBaseUrl}/uploads/news/${attachment}`;
-  }
-  console.log("pas d'image");
-};
-
-const tagColor = (tag) => {
-  switch (tag) {
-    case "Payant":
-      return "red";
-    case "Gratuit":
-      return "green";
-    default:
-      return "primary";
-  }
-};
-
-const tagTextColor = (tag) => {
-  switch (tag) {
-    case "Payant":
-      return "red--text";
-    case "Gratuit":
-      return "green--text";
-    default:
-      return "white--text";
-  }
-};
-const events = ref([
-  {
-    rdv: "20h00",
-    title: "LA NUIT DES RÊVES  ",
-    localisation: "FESTIVALDE musique - LONGCHAMP",
-    date: "21/06/2023",
-    img: "/images/agenda/agenda2.jpg",
-    tags: ["Festival", "Musique", "Tout public", "Payant"],
-  },
-  {
-    rdv: "20h00",
-    title: "LE LAC DES CYGNES",
-    localisation: "SPECTACLE DE DANSE - PARIS 1",
-    date: "22/06/2023",
-    img: "/images/agenda/agenda3.jpg",
-    tags: ["Festival", "Musique", "Tout public", "Gratuit"],
-  },
-  {
-    rdv: "20h00",
-    title: "SOLIDAYS 2023  : 23 > 25 juin",
-    localisation: "ORCHESTRE DE PARIS - PARIS 12",
-    date: "23/06/2023",
-    img: "/images/agenda/agenda4.jpg",
-    tags: ["Festival", "Musique", "Tout public", "Payant"],
-  },
-  {
-    rdv: "20h00",
-    title: "LE LAC DES CYGNES",
-    localisation: "FESTIVALDE musique - LONGCHAMP",
-    date: "24/06/2023",
-    img: "/images/agenda/agenda5.jpg",
-    tags: ["Festival", "Musique", "Tout public", "Payant"],
-  },
-  {
-    rdv: "20h00",
-    title: "SOLIDAYS 2023  : 23 > 25 juin ",
-    localisation: "SPECTACLE DE DANSE - PARIS 1",
-    date: "20/06/2023",
-    img: "/images/agenda/agenda1.jpg",
-    tags: ["Festival", "Musique", "Tout public", "Payant"],
-  },
-]);
-
-let carousel;
-
-const goPrevious = () => carousel.prev();
-const goNext = () => carousel.next();
+import News from "~/components/Home/News.vue";
 </script>
-
-<style scoped>
-.custom-row {
-  width: 90%;
-  margin-left: auto;
-  margin-right: auto;
-}
-:deep().text-left,
-:deep().text-right,
-:deep().description,
-:deep().details-square,
-:deep().logo-square {
-  display: none;
-}
-:deep().text-right {
-  display: none;
-}
-
-:deep().cover-image {
-  transform: none;
-}
-
-.btn {
-  background: var(--Vert-60, #64afb7);
-  display: flex;
-  left: 0;
-  padding: 25px 28px;
-  align-items: center;
-  gap: 9px;
-  color: var(--NEUTRAL---BLANC, #fff);
-  font-family: Barlow;
-  font-size: 0.9rem;
-  border-radius: 5px;
-  font-style: normal;
-  font-weight: 700;
-  line-height: 15px;
-  letter-spacing: 1.3px;
-  text-transform: uppercase;
-  margin-bottom: -1rem;
-}
-
-.green--text {
-  color: green;
-}
-
-.red--text {
-  color: red;
-}
-.black--text {
-  color: black;
-}
-.btn-news {
-  color: #9edbdd;
-  border-radius: 2rem;
-  font-family: "Barlow";
-  background: transparent;
-  border: 1px solid #9edbdd;
-  border-radius: 6px;
-  font-style: normal;
-  font-weight: 600;
-  text-transform: uppercase;
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  padding: 25px;
-  font-size: 10px;
-  line-height: 15px;
-}
-.chip-detail {
-  color: #000000;
-}
-.chip-custom {
-  color: white;
-  border: 1px solid white;
-  border-radius: 3rem;
-  text-transform: uppercase;
-  font-family: "Barlow";
-  font-style: normal;
-  display: flex;
-  align-items: center;
-  text-align: center;
-}
-
-.card-localisation {
-  letter-spacing: 0.18em;
-  text-transform: uppercase;
-  font-size: 10px;
-  color: white;
-}
-.card {
-  border-radius: 15px 15px 0 0;
-  margin-bottom: 2rem;
-}
-
-.icon-title {
-  color: #64afb7;
-  margin-top: 4.5rem;
-}
-.container-title {
-  display: flex;
-  align-items: center;
-  margin-left: 2rem;
-  margin-top: 4.5rem;
-}
-
-.carousel-button i {
-  color: #000000;
-}
-.card-text {
-  font-family: "Barlow";
-  font-style: normal;
-  font-weight: 500;
-  font-size: 16px;
-  line-height: 18px;
-  margin-bottom: 1rem;
-  color: #fff !important;
-}
-.card-title {
-  font-family: "Barlow";
-  font-style: normal;
-  font-weight: 500;
-  font-size: 20px;
-  line-height: 24px;
-  display: flex;
-  align-items: center;
-  letter-spacing: 0.18em;
-  text-transform: uppercase;
-  color: #fff !important;
-}
-.card-date {
-  font-size: 0.8em;
-  color: #888;
-  margin-left: 1rem;
-  color: #fff !important;
-}
-
-.card-footer {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-}
-
-.card-body {
-  text-align: left;
-  margin-bottom: 1rem;
-  margin-left: 1rem;
-  font-family: "Barlow";
-  font-style: normal;
-  font-weight: 500;
-  line-height: 24px;
-  color: #fff !important;
-  color: #112528;
-}
-
-.card-img-top {
-  border-radius: 9px 9px 0 0;
-  width: 100%;
-  object-fit: 90%;
-  object-position: center;
-  width: 384px;
-  height: 247.11px;
-}
-
-.title,
-.carousel-button,
-.btn-news {
-  margin-top: 2rem;
-  margin-bottom: 2rem;
-}
-.agenda-details {
-  font-family: "Barlow";
-  font-style: normal;
-  font-weight: 300;
-  font-size: 16px;
-  line-height: 20px;
-  margin-left: 2rem;
-  color: #091d20;
-  margin-bottom: 3rem;
-  width: 15rem;
-}
-.title {
-  font-family: "Barlow";
-  font-style: normal;
-  font-weight: 600;
-  font-size: 42px;
-  line-height: 42px;
-  margin-left: 2rem;
-  color: #071b1f;
-}
-
-.carousel-button {
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  width: 60px;
-  height: 60px;
-  background-color: transparent;
-  border: 2px solid #000000;
-  cursor: pointer;
-  margin-right: 1rem;
-}
-
-.container-green {
-  background-color: #0e2d32;
-}
-
-.back-button {
-  text-decoration: none;
-  color: #000000;
-  font-family: Barlow;
-  font-size: 0.9rem;
-  font-style: normal;
-  font-weight: 700;
-  line-height: 15px;
-  letter-spacing: 1.8px;
-  text-transform: uppercase;
-}
-
-.image-actu {
-  width: 80%;
-  margin-left: 3.5rem;
-  margin-right: auto;
-}
-
-.title-actu {
-  margin-top: 11rem;
-  text-decoration: none;
-  text-transform: uppercase;
-  font-family: Barlow;
-  font-size: 36px;
-  font-style: normal;
-  font-weight: 600;
-}
-
-.description-actu {
-  color: #0e2d32;
-  text-align: justify;
-  font-family: "Barlow";
-  font-size: 30px;
-  font-style: normal;
-  font-weight: 500;
-  line-height: 34px;
-  margin-left: 3.5rem;
-  margin-right: 3.5rem;
-  margin-bottom: 3rem;
-}
-
-.share {
-  color: #000;
-  font-family: Barlow;
-  font-size: 26px;
-  font-style: normal;
-  font-weight: 400;
-  line-height: 28px;
-}
-
-.social-icons {
-  display: flex;
-  gap: 10px;
-}
-
-.social-icon i {
-  font-size: 1.9em;
-  margin-left: 0.5rem;
-  margin-top: 0.5rem;
-  color: #0e2d32;
-}
-
-.key-word {
-  color: #000;
-  font-family: Barlow;
-  font-size: 20px;
-  font-style: normal;
-  font-weight: 500;
-  line-height: 24px;
-}
-
-.title-other {
-  color: #fff;
-}
-</style>

+ 12 - 3
pages/nous-contacter.vue

@@ -1,10 +1,19 @@
 <template>
   <LayoutNavigation />
+
   <ContactBanner />
+
+  <ContactForm />
+
+  <ContactMap class="contact-map" />
+
   <LayoutFooterPrefooter />
+
   <LayoutFooter />
 </template>
 
-<script setup></script>
-
-<style scoped></style>
+<style scoped lang="scss">
+.contact-map {
+  margin-top: 48px;
+}
+</style>

+ 1 - 1
pages/qui-sommes-nous.vue

@@ -17,7 +17,7 @@
 
   <AboutLogiciels />
 
-  <AboutAgenda />
+  <CommonAgenda />
 
   <AboutChronologie v-if="lgAndUp"/>
 

+ 10 - 0
types/interface.d.ts

@@ -143,3 +143,13 @@ interface SocialNetworkShareBtn {
   colorOnHover?: string,
   url: string
 }
+
+interface ContactFormData {
+  gender: string | null,
+  postalCode: string | null,
+  city: string | null,
+  phone: string | null,
+  requestType: string | null,
+  concernedProduct: string | null,
+  newsletterSubscription: boolean
+}