Forráskód Böngészése

refact BannerTitle, MenuScroll component

Olivier Massot 1 éve
szülő
commit
58cd799b1c

+ 48 - 39
components/Common/Banner/BannerTitle.vue

@@ -1,26 +1,42 @@
 
 <template>
   <LayoutContainer>
-  <div class="container-title">
-    <v-col cols="4" class="text-left">
-      <h1>{{ leftText }}</h1>
-    </v-col>
-    <v-col cols="4" class="logiciel">
-      <h1>{{ centerText }}</h1>
-    </v-col>
-    <v-col cols="4" class="text-right">
-      <h1>{{ rightText }}</h1>
-    </v-col>
-  </div>
-</LayoutContainer>
+    <div class="container-title">
+      <v-col cols="3" class="lateral-text">
+        <span>
+          {{ leftText }}
+        </span>
+      </v-col>
+      <v-col cols="6">
+        <h1>
+          {{ title }}
+        </h1>
+      </v-col>
+      <v-col cols="3" class="lateral-text">
+        <span>
+          {{ rightText }}
+        </span>
+      </v-col>
+    </div>
+  </LayoutContainer>
 </template>
 
-<script setup>
-
+<script setup lang="ts">
 const props = defineProps({
-  leftText: String,
-  centerText: String,
-  rightText: String,
+  title: {
+    type: String,
+    required: true
+  },
+  leftText: {
+    type: String,
+    required: false,
+    default: ""
+  },
+  rightText: {
+    type: String,
+    required: false,
+    default: ""
+  },
 });
 </script>
 
@@ -29,43 +45,36 @@ const props = defineProps({
   display: flex;
   justify-content: space-around;
   line-height: 16px;
-  display: flex;
   align-items: center;
   text-align: center;
   letter-spacing: 0.18em;
   border-bottom: 0.1rem solid #eaeaea;
 }
 
-.text-left {
+h1 {
+  font-size: 72px;
+  line-height: 77px;
+  color: #000000;
+  text-align: center;
+}
+
+.lateral-text {
   position: absolute;
   font-weight: 600;
-  font-size: 2.3rem;
+  font-size: 72px;
   line-height: 85px;
   opacity: 0.08;
   margin-top: 2rem;
-  left:-2rem;
   margin-bottom: 2rem;
-}
-.text-right {
-  position: absolute;
-  right: -2rem;
-  margin-top: 2rem;
   font-style: normal;
-  font-weight: 600;
-  font-size: 2.3rem;
-  line-height: 85px;
-  text-align: center;
-  opacity: 0.08;
-  margin-bottom: 2rem;
 
 }
 
-.logiciel {
-  display: flex;
-  justify-content: space-around;
-  font-size: 2.3rem;
-  line-height: 77px;
-  color: #000000;
-  text-align: center;
+.lateral-text:first-child {
+  left: -10rem;
+}
+
+.lateral-text:last-child {
+  right: -10rem;
 }
 </style>

+ 46 - 64
components/Common/MenuScroll.vue

@@ -1,112 +1,94 @@
+<!-- Menu sticky horizontal -->
+<!-- TODO: review -->
 <template>
   <LayoutContainer>
     <v-row>
       <v-col
         cols="12"
-        class="menu-container"
-        :class="{ 'sticky-menu': isSticky }"
       >
-        <div
-          v-for="menu in menus"
-          :key="menu.id"
-          @click="navigate(menu)"
+        <v-list
+          class="menu-container"
+          density="compact"
+          :class="{ 'sticky-menu': isSticky }"
         >
-          <v-chip
-            v-if="activeMenu === menu.id"
-            class="active-menu"
-          >
-            {{ menu.label }}
-          </v-chip>
-          <span v-else>{{ menu.label }}</span>
-        </div>
+          <div v-for="menu in menus" :key="menu.anchor">
+            <nuxt-link :to="{ path: '', hash: '#' + menu.anchor }">
+              <v-list-item
+                :class="{ active : isSticky && layoutStore.isAnchoredSectionOnScreen[menu.anchor] }"
+              >
+                {{ menu.label }}
+              </v-list-item>
+            </nuxt-link>
+          </div>
+        </v-list>
       </v-col>
     </v-row>
   </LayoutContainer>
 </template>
 
-<script setup>
-import { ref, onMounted, watch } from 'vue';
+<script setup lang="ts">
 
-const props = defineProps({
-  menus: Array
-});
-
-const isSticky = ref(false);
-const activeMenu = ref('');
+import { PropType } from "@vue/runtime-core";
+import { MenuScroll } from "~/types/interface";
+import { useLayoutStore } from "~/stores/layoutStore";
 
-const scrollToElement = (element) => {
-  if (element) {
-    element.scrollIntoView({ behavior: "smooth" });
+const props = defineProps({
+  menus: {
+    type: Array as PropType<Array < MenuScroll >>,
+    required: true
   }
-};
+});
 
-const handleScroll = () => {
-  const scrollPosition = window.scrollY;
-  isSticky.value = scrollPosition > 800; 
+const layoutStore = useLayoutStore()
 
-  for (const menu of props.menus) {
-    const { element } = menu;
-    if (element && scrollPosition >= element.offsetTop && scrollPosition < element.offsetTop + element.offsetHeight) {
-      activeMenu.value = menu.id;
-      break;
-    }
-  }
-};
+const isSticky: Ref<boolean> = ref(false);
 
 onMounted(() => {
-  props.menus.forEach(menu => {
-    menu.element = document.getElementById(menu.id);
-  });
   window.addEventListener('scroll', handleScroll);
-});
+})
 
-watch(() => props.menus, (newMenus) => {
-  newMenus.forEach(menu => {
-    menu.element = document.getElementById(menu.id);
-  });
-}, { deep: true });
-
-const navigate = (menu) => {
-  activeMenu.value = menu.id;
-  scrollToElement(menu.element);
-};
+const handleScroll = () => {
+  isSticky.value = window.scrollY > 800;
+}
 </script>
 
-<style scoped>
-
+<style scoped lang="scss">
 .sticky-menu {
   position: fixed;
   top: 0;
   left: 0;
   right: 0;
   background: white;
-  box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
+  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
 }
+
 .menu-container {
   z-index: 3;
   display: flex;
   justify-content: space-around;
   background: white;
   color: #071b1f;
-  font-family: "Barlow";
+  font-family: "Barlow", serif;
   font-size: 1rem;
   line-height: 19px;
-  display: flex;
   align-items: center;
   text-align: center;
   letter-spacing: 0.18em;
   text-transform: uppercase;
   border-bottom: 0.1rem solid #eaeaea;
-}
-.v-chip.active-menu {
-  background: var(--Vert-100, #091D20);;
-  color: white;
+
+  a {
+    text-decoration: none;
+    color: #071b1f;
+  }
+
+  a:hover {
+    text-decoration: underline;
+  }
 }
 
-.menu-container div:hover {
-  cursor: pointer;
-  text-decoration: underline;
-  z-index: 15;
+.active {
+  font-weight: 800;
 }
 </style>
 

+ 67 - 0
components/Layout/AnchoredSection.vue

@@ -0,0 +1,67 @@
+<template>
+  <!--suppress VueUnrecognizedDirective -->
+  <div
+    :id="id"
+    ref="section"
+    v-intersect="onIntersect"
+  >
+    <slot/>
+  </div>
+
+</template>
+
+<script setup lang="ts">
+  import { useLayoutStore } from "~/stores/layoutStore";
+
+  const layoutStore = useLayoutStore()
+
+  const props = defineProps({
+    id: {
+      type: String,
+      required: true
+    }
+  })
+
+  if (!props.id) {
+    throw new Error("Anchor's id is missing")
+  }
+
+  const section: Ref<HTMLElement | null> = ref(null)
+
+  layoutStore.setIsAnchoredSectionOnScreen(props.id, false)
+
+  const onIntersect = (e: boolean) => {
+    layoutStore.setIsAnchoredSectionOnScreen(props.id, e)
+  }
+
+  // TODO: supprimer si pas nécessaire, je conserve au cas où il faudrait un
+  //       fonctionnement plus fin qu'un simple intersect
+  // const top: Ref<number | null> = ref(null)
+  // const bottom: Ref<number | null> = ref(null)
+  //
+  // onMounted(() => {
+  //   top.value = section.value!.offsetTop
+  //   bottom.value = section.value!.offsetTop + section.value!.offsetHeight
+  // })
+  //
+  // const onScroll = (e: any) => {
+  //   if (top.value === null || bottom.value === null) {
+  //     return
+  //   }
+  //
+  //   const scroll: number = e.target.scrollingElement.scrollTop
+  //
+  //   const active = scroll > top.value && scroll < bottom.value
+  //
+  //   if (active !== layoutStore.isAnchoredSectionOnScreen[props.id]) {
+  //     layoutStore.setIsAnchoredSectionOnScreen(
+  //       props.id,
+  //       active
+  //     )
+  //   }
+  // }
+</script>
+
+<style scoped>
+
+</style>

+ 83 - 79
components/Logiciels/Artist/Abonnement.vue

@@ -1,91 +1,95 @@
 <template>
-  <div id="Abonnement">
-    <LayoutContainer>
-      <v-row class="mt-12">
-        <v-col cols="4">
-          <LayoutUISubTitle
-            :iconSize="6"
-            :iconClasses="iconClasses"
-            :titleText="'S\'abonner dès maintenant'"
-            :iconColor="'#fac20a'"
-          />
-          <div class="profile-circle">
-            <v-img
-              src="/images/logo/logiciels/OT_Artist-BLANC.png"
-              style="top: 0.5rem"
-            ></v-img>
-          </div>
+  <AnchoredSection id="subscription">
+    <div>
+      <LayoutContainer>
+        <v-row class="mt-12">
+          <v-col cols="4">
+            <LayoutUISubTitle
+              :iconSize="6"
+              :iconClasses="iconClasses"
+              :titleText="'S\'abonner dès maintenant'"
+              :iconColor="'#fac20a'"
+            />
+            <div class="profile-circle">
+              <v-img
+                src="/images/logo/logiciels/OT_Artist-BLANC.png"
+                style="top: 0.5rem"
+              ></v-img>
+            </div>
 
-          <div class="subscription-info">
-            <p class="mt-3 mb-6">
-              Pour vous abonner au logiciel, téléchargez et remplissez le
-              formulaire avant de nous le transmettre
-            </p>
-            <a
-              href="https://www.opentalent.fr/fileadmin/stockage/commercial/contrats/BDC_Artist_Public-23.pdf"
-              target="_blank"
-              class="download-button"
-              >Télécharger le formulaire d'abonnement</a
-            >
-          </div>
-          <div class="subscription-steps">
-            <ol>
-              <li class="mt-6">Téléchargez le formulaire</li>
-              <li>Complétez le formulaire</li>
-              <li>
-                Joignez le règlement par chèque avec le formulaire à <br />
+            <div class="subscription-info">
+              <p class="mt-3 mb-6">
+                Pour vous abonner au logiciel, téléchargez et remplissez le
+                formulaire avant de nous le transmettre
+              </p>
+              <a
+                href="https://www.opentalent.fr/fileadmin/stockage/commercial/contrats/BDC_Artist_Public-23.pdf"
+                target="_blank"
+                class="download-button"
+                >Télécharger le formulaire d'abonnement</a
+              >
+            </div>
+            <div class="subscription-steps">
+              <ol>
+                <li class="mt-6">Téléchargez le formulaire</li>
+                <li>Complétez le formulaire</li>
+                <li>
+                  Joignez le règlement par chèque avec le formulaire à <br />
+                  <br />
+                  2iOPENservice <br />
+                  217 rue Raoul Follereau <br />
+                  74300 CLUSES
+                </li>
                 <br />
-                2iOPENservice <br />
-                217 rue Raoul Follereau <br />
-                74300 CLUSES
-              </li>
-              <br />
-              <li>
-                Après réception de votre formulaire d'adhésion et de votre
-                règlement, nous vous ouvrons le service choisi. Vous recevrez
-                alors un mail avec votre identifiant de connexion, votre mot de
-                passe, ainsi que l'URL de votre site internet.
-              </li>
-            </ol>
-          </div>
-        </v-col>
+                <li>
+                  Après réception de votre formulaire d'adhésion et de votre
+                  règlement, nous vous ouvrons le service choisi. Vous recevrez
+                  alors un mail avec votre identifiant de connexion, votre mot de
+                  passe, ainsi que l'URL de votre site internet.
+                </li>
+              </ol>
+            </div>
+          </v-col>
 
-        <v-col cols="8">
-          <h5 class="title">
-            Opentalent Artist, <br> la solution que vous attendiez...
-          </h5>
-          <p class="solution" style="text-align: justify">
-            Conçu pour les structures artistiques telles que chorales,
-            orchestres (fanfares, orchestres d'harmonie, symphoniques, musiques
-            actuelles, petites formations musicales...), compagnies de danse,
-            troupes de théâtre, cirque, arts de la rue… Le logiciel s'adapte à
-            vos besoins d'évolution !
-          </p>
-          <h3 class="cmf">
-            Adhérents CMF ? <br> Et si on vous disait que vous l’aviez déjà ...
-          </h3>
-          <div class="border-row">
+          <v-col cols="8">
+            <h5 class="title">
+              Opentalent Artist, <br> la solution que vous attendiez...
+            </h5>
+            <p class="solution" style="text-align: justify">
+              Conçu pour les structures artistiques telles que chorales,
+              orchestres (fanfares, orchestres d'harmonie, symphoniques, musiques
+              actuelles, petites formations musicales...), compagnies de danse,
+              troupes de théâtre, cirque, arts de la rue… Le logiciel s'adapte à
+              vos besoins d'évolution !
+            </p>
+            <h3 class="cmf">
+              Adhérents CMF ? <br> Et si on vous disait que vous l’aviez déjà ...
+            </h3>
+            <div class="border-row">
 
 
-            <div class="logo-cmf"></div>
-            <div class="cmf-container">
-              <p class="cmf-text">
-                Attention si vous êtes adhérent à la Confédération Musicale de
-                France (CMF), vous bénéficiez gratuitement, dans le cadre de
-                votre adhésion, de la version Opentalent Artist Standard, et de
-                conditions privilégiées pour la version Artist Premium.
-                Contactez nous ou contactez votre fédération pour obtenir vos
-                codes d'accès.
-              </p>
+              <div class="logo-cmf"></div>
+              <div class="cmf-container">
+                <p class="cmf-text">
+                  Attention si vous êtes adhérent à la Confédération Musicale de
+                  France (CMF), vous bénéficiez gratuitement, dans le cadre de
+                  votre adhésion, de la version Opentalent Artist Standard, et de
+                  conditions privilégiées pour la version Artist Premium.
+                  Contactez nous ou contactez votre fédération pour obtenir vos
+                  codes d'accès.
+                </p>
+              </div>
             </div>
-          </div>
-        </v-col>
-      </v-row>
-    </LayoutContainer>
-  </div>
+          </v-col>
+        </v-row>
+      </LayoutContainer>
+    </div>
+  </AnchoredSection>
 </template>
 
-<script setup></script>
+<script setup>
+import AnchoredSection from "~/components/Layout/AnchoredSection.vue";
+</script>
 
 <style scoped>
 .v-container {

+ 36 - 33
components/Logiciels/Artist/Avantages.vue

@@ -1,44 +1,47 @@
 
   <template>
-    <div id="Avantages">
-      <LayoutContainer>
-        <v-row class="row-custom">
-          <LayoutUISubTitle
-            :iconSize="6"
-            :iconClasses="iconClasses"
-            :titleText="'Découvrez les avantages de la solution'"
-            :iconColor="'#fac20a'"
+    <AnchoredSection id="benefits">
+      <div>
+        <LayoutContainer>
+          <v-row class="row-custom">
+            <LayoutUISubTitle
+              :iconSize="6"
+              :iconClasses="iconClasses"
+              :titleText="'Découvrez les avantages de la solution'"
+              :iconColor="'#fac20a'"
 
-          />
-          <LayoutUITitle title="Des avantages concrets" />
-        </v-row>
+            />
+            <LayoutUITitle title="Des avantages concrets" />
+          </v-row>
 
-        <v-row class="row-custom">
-          <v-col
-            cols="12"
-            md="4"
-            offset-md="1"
-            v-for="(card, index) in cards"
-            :key="index"
-          >
-            <CommonCardAvantageCard
-              :title="card.title"
-              :number="card.number"
-              :description="card.description"
-              :image="card.image"
-              :isMemberCMF="card.isMemberCMF"
-              :numberColor="card.numberColor"
-              :dynamicNumberColor="'#fac20a'"
+          <v-row class="row-custom">
+            <v-col
+              cols="12"
+              md="4"
+              offset-md="1"
+              v-for="(card, index) in cards"
+              :key="index"
+            >
+              <CommonCardAvantageCard
+                :title="card.title"
+                :number="card.number"
+                :description="card.description"
+                :image="card.image"
+                :isMemberCMF="card.isMemberCMF"
+                :numberColor="card.numberColor"
+                :dynamicNumberColor="'#fac20a'"
 
-            />
-          </v-col>
-        </v-row>
-      </LayoutContainer>
-    </div>
+              />
+            </v-col>
+          </v-row>
+        </LayoutContainer>
+      </div>
+    </AnchoredSection>
   </template>
 
 <script setup>
 import { ref } from "vue";
+import AnchoredSection from "~/components/Layout/AnchoredSection.vue";
 
 const cards = ref([
   {
@@ -79,4 +82,4 @@ const cards = ref([
   margin-right: auto;
 }
 
-</style>
+</style>

+ 8 - 15
components/Logiciels/Artist/Banner.vue

@@ -1,29 +1,22 @@
 <template>
   <LayoutContainer>
-    <v-row>
-      <CommonBannerTitle
-        :leftText="'School'"
-        :centerText="'Opentalent Artist'"
-        :rightText="'Manager'"
-      />
-    </v-row>
-
     <v-row>
       <v-col cols="12">
         <CommonBanner
-          :imageSrc="'/images/logiciels/artist/banner.jpg'"
-          imageAlt="'line'"
-          :squareText="'Orchestres, chorales, compagnies de danse, de cirque et de théâtre  '"
-          :logoSrc="'/images/logo/logiciels/Artist-noir.png'"
-          :squareColor="'#fac20a'"
-          :blueSquareColor="'#FFF'"
+          imageSrc="/images/logiciels/artist/banner.jpg"
+          imageAlt="line"
+          squareText="Orchestres, chorales, compagnies de danse, de cirque et de théâtre"
+          logoSrc="/images/logo/logiciels/Artist-noir.png"
+          squareColor="#fac20a"
+          blueSquareColor="#FFF"
         />
       </v-col>
     </v-row>
   </LayoutContainer>
 </template>
 
-<script setup></script>
+<script setup>
+</script>
 
 <style scoped>
 </style>

+ 23 - 19
components/Logiciels/Artist/Comparatif.vue

@@ -1,27 +1,31 @@
 <template>
-  <div id="Comparatif">
-    <LayoutContainer>
-      <LayoutUISubTitle
-          :iconSize="6"
-          :iconClasses="iconClasses"
-          :titleText="'Comparatif de nos solutions'"
-          :iconColor="'#fac20a'"
+  <AnchoredSection id="comparative">
+    <div>
+      <LayoutContainer>
+        <LayoutUISubTitle
+            :iconSize="6"
+            :iconClasses="iconClasses"
+            :titleText="'Comparatif de nos solutions'"
+            :iconColor="'#fac20a'"
+          />
+          <LayoutUITitle
+            title="Choisissez la version qui vous convient !"
+          />
+          <CommonTableComparatif
+          :standardPrice="'14€'"
+          :premiumPrice="'18€'"
+          :color="'#0e2d32'"
+          :stripeColor="'#fac20a33'"
+          :tableData="tableData"
         />
-        <LayoutUITitle
-          title="Choisissez la version qui vous convient !"
-        />
-        <CommonTableComparatif
-        :standardPrice="'14€'"
-        :premiumPrice="'18€'"
-        :color="'#0e2d32'"
-        :stripeColor="'#fac20a33'"
-        :tableData="tableData"
-      />
-    </LayoutContainer>
-  </div>
+      </LayoutContainer>
+    </div>
+  </AnchoredSection>
 </template>
 
 <script setup>
+import AnchoredSection from "~/components/Layout/AnchoredSection.vue";
+
 const tableData = [
   {
     id: 1,

+ 20 - 17
components/Logiciels/Artist/Fonctionnalites.vue

@@ -1,25 +1,28 @@
 <template>
-  <div id="Fonctionnalites">
-    <LayoutContainer>
-      <div class="container-green">
-          <v-row>
-            <v-col cols="12">
-              <CommonCarouselFonctionnalite
-                :cards="cards"
-                :functionCarousel="functionCarousel"
-                :itemsToShow="itemsToShow"
-                pricingFromText="à partir de" 
-              />
-            </v-col>
-          </v-row>
-      </div>
-    </LayoutContainer>
-  </div>
+  <AnchoredSection id="functionalities">
+    <div>
+      <LayoutContainer>
+        <div class="container-green">
+            <v-row>
+              <v-col cols="12">
+                <CommonCarouselFonctionnalite
+                  :cards="cards"
+                  :functionCarousel="functionCarousel"
+                  :itemsToShow="itemsToShow"
+                  pricingFromText="à partir de"
+                />
+              </v-col>
+            </v-row>
+        </div>
+      </LayoutContainer>
+    </div>
+  </AnchoredSection>
 </template>
 
 <script setup>
 import { ref } from "vue";
 import "vue3-carousel/dist/carousel.css";
+import AnchoredSection from "~/components/Layout/AnchoredSection.vue";
 
 const functionCarousel = ref(null);
 
@@ -114,4 +117,4 @@ const cards = [
 .container-green {
   background-color: #0e2d32;
 }
-</style>
+</style>

+ 90 - 86
components/Logiciels/Artist/Formations.vue

@@ -1,101 +1,105 @@
 <template>
-  <div id="Webinaires">
-    <LayoutContainer>
-      <div class="container-green 2">
-        <v-container>
-          <v-row class="custom-row">
-            <LayoutUISubTitle
-              class="mt-12"
-              title-color="#fff"
-              :iconSize="6"
-              :iconClasses="iconClasses"
-              :titleText="'Pour aller plus loin'"
-              :iconColor="'#fac20a'"
-            />
-          </v-row>
+  <AnchoredSection id="webinars">
+    <div>
+      <LayoutContainer>
+        <div class="container-green 2">
+          <v-container>
+            <v-row class="custom-row">
+              <LayoutUISubTitle
+                class="mt-12"
+                title-color="#fff"
+                :iconSize="6"
+                :iconClasses="iconClasses"
+                :titleText="'Pour aller plus loin'"
+                :iconColor="'#fac20a'"
+              />
+            </v-row>
 
-          <v-row class="mt-12 align-center" no-gutters>
-            <v-col cols="12" lg="6" md="6" sm="6">
-              <div class="reunion-img mb-12"></div>
-            </v-col>
+            <v-row class="mt-12 align-center" no-gutters>
+              <v-col cols="12" lg="6" md="6" sm="6">
+                <div class="reunion-img mb-12"></div>
+              </v-col>
 
-            <v-col cols="12" lg="6" md="6" sm="6">
-              <h3 class="formation-title ml-6 mr-12">
-                Webinaire - Partez à la découverte du logiciel Opentalent Artist
-              </h3>
-              <p
-                class="formation-details ml-6 mr-12"
-                style="text-align: justify"
-              >
-                Rejoignez notre webinaire, spécialement conçu pour les
-                professionnels du secteur culturel, orchestres, chorales,
-                compagnies de danse, ainsi que les troupes de théâtre et de
-                cirque. Cette session interactive vous offre une occasion unique
-                de vous immerger dans les fonctionnalités de notre logiciel, de
-                comprendre ses avantages distinctifs et d'explorer les diverses
-                versions disponibles. Ne manquez pas cette chance de simplifiez
-                votre gestion et de faire évoluer votre pratique artistique avec
-                nos solutions technologiques innovantes !
-              </p>
-              <nuxt-link to="/webinaires">
-                <v-btn class="formation-btn mt-12 ml-6">
-                  S'inscrire à nos webinaires</v-btn
+              <v-col cols="12" lg="6" md="6" sm="6">
+                <h3 class="formation-title ml-6 mr-12">
+                  Webinaire - Partez à la découverte du logiciel Opentalent Artist
+                </h3>
+                <p
+                  class="formation-details ml-6 mr-12"
+                  style="text-align: justify"
                 >
-              </nuxt-link>
+                  Rejoignez notre webinaire, spécialement conçu pour les
+                  professionnels du secteur culturel, orchestres, chorales,
+                  compagnies de danse, ainsi que les troupes de théâtre et de
+                  cirque. Cette session interactive vous offre une occasion unique
+                  de vous immerger dans les fonctionnalités de notre logiciel, de
+                  comprendre ses avantages distinctifs et d'explorer les diverses
+                  versions disponibles. Ne manquez pas cette chance de simplifiez
+                  votre gestion et de faire évoluer votre pratique artistique avec
+                  nos solutions technologiques innovantes !
+                </p>
+                <nuxt-link to="/webinaires">
+                  <v-btn class="formation-btn mt-12 ml-6">
+                    S'inscrire à nos webinaires</v-btn
+                  >
+                </nuxt-link>
+              </v-col>
+            </v-row>
+          </v-container>
+        </div>
+        <v-row class="custom-row">
+          <LayoutUISubTitle
+            :iconSize="6"
+            :iconClasses="iconClasses"
+            :titleText="' Quelques chiffres'"
+            class="mb-12"
+          />
+        </v-row>
+        <v-container>
+          <v-row class="card-container mb-12">
+            <v-col
+              cols="3"
+              class="d-flex justify-center align-center small-padding"
+            >
+              <CommonCardStat
+                :chiffre="'184 634'"
+                text="Utilisateurs"
+                backgroundColor="#fac20a"
+              />
+            </v-col>
+            <v-col cols="3" class="d-flex justify-center align-center">
+              <CommonCardStat
+                :chiffre="'3 424'"
+                text="Structures"
+                backgroundColor="#fac20a"
+              />
+            </v-col>
+            <v-col cols="3" class="d-flex justify-center align-center">
+              <CommonCardStat
+                :chiffre="13"
+                text="Années d'expérience"
+                backgroundColor="#fac20a"
+              />
             </v-col>
           </v-row>
         </v-container>
-      </div>
-      <v-row class="custom-row">
-        <LayoutUISubTitle
-          :iconSize="6"
-          :iconClasses="iconClasses"
-          :titleText="' Quelques chiffres'"
-          class="mb-12"
-        />
-      </v-row>
-      <v-container>
-        <v-row class="card-container mb-12">
-          <v-col
-            cols="3"
-            class="d-flex justify-center align-center small-padding"
-          >
-            <CommonCardStat
-              :chiffre="'184 634'"
-              text="Utilisateurs"
-              backgroundColor="#fac20a"
-            />
-          </v-col>
-          <v-col cols="3" class="d-flex justify-center align-center">
-            <CommonCardStat
-              :chiffre="'3 424'"
-              text="Structures"
-              backgroundColor="#fac20a"
-            />
-          </v-col>
-          <v-col cols="3" class="d-flex justify-center align-center">
-            <CommonCardStat
-              :chiffre="13"
-              text="Années d'expérience"
-              backgroundColor="#fac20a"
-            />
-          </v-col>
-        </v-row>
-      </v-container>
-      <v-row />
+        <v-row />
 
-      <v-row />
+        <v-row />
 
-      <CommonCarouselClients :items="items" >
-        <template v-slot:title>
-          Plus de <span class="alt-color">3400 structures</span> nous ont déjà adoptées
-        </template>
-      </CommonCarouselClients>
-    </LayoutContainer>
-  </div>
+        <CommonCarouselClients :items="items" >
+          <template v-slot:title>
+            Plus de <span class="alt-color">3400 structures</span> nous ont déjà adoptées
+          </template>
+        </CommonCarouselClients>
+      </LayoutContainer>
+    </div>
+  </AnchoredSection>
 </template>
 
 <script setup lang="ts">
+import AnchoredSection from "~/components/Layout/AnchoredSection.vue";
+
 const items: Ref<Array<{ src: string }>> = ref([
   { src: "/images/reviews/artist/review1.jpeg" },
   { src: "/images/reviews/artist/review2.jpg" },

+ 5 - 3
components/Logiciels/Artist/Presentation.vue

@@ -1,5 +1,5 @@
 <template>
-  <div id="Presentation">
+  <AnchoredSection id="presentation">
     <CommonPresentation
       :pictoImages="pictoData"
       :presentationText="presentationData"
@@ -7,12 +7,14 @@
       pricingPeriodText="/mois"
       pricingAnnouncementText="payable annuellement"
     />
-  </div>
 
-  <CommonContainerVideo image-url="/images/logiciels/school/screen2.png" />
+    <CommonContainerVideo image-url="/images/logiciels/school/screen2.png" />
+  </AnchoredSection>
 </template>
 
 <script setup>
+import AnchoredSection from "~/components/Layout/AnchoredSection.vue";
+
 const pictoData = [
   {
     src: "/images/logiciels/artist/picto1.png",

+ 9 - 5
components/Logiciels/Artist/Reviews.vue

@@ -1,12 +1,16 @@
 <template>
-  <div id="Temoignages">
-    <LayoutContainer>
-      <CommonReviewCard :cards="cards" />
-    </LayoutContainer>
-  </div>
+  <AnchoredSection id="testimonials">
+    <div>
+      <LayoutContainer>
+        <CommonReviewCard :cards="cards" />
+      </LayoutContainer>
+    </div>
+  </AnchoredSection>
 </template>
 
 <script setup>
+import AnchoredSection from "~/components/Layout/AnchoredSection.vue";
+
 const cards = [
   {
     description:

+ 1 - 1
components/Logiciels/Manager/Banner.vue

@@ -3,7 +3,7 @@
     <v-row>
       <CommonBannerTitle
         :leftText="'School'"
-        :centerText="'Opentalent Manager'"
+        :title="'Opentalent Manager'"
         :rightText="'Artist'"
       />
     </v-row>

+ 1 - 1
components/Logiciels/School/Banner.vue

@@ -3,7 +3,7 @@
     <v-row>
       <CommonBannerTitle
         :leftText="'Artist'"
-        :centerText="'Opentalent School'"
+        :title="'Opentalent School'"
         :rightText="'Manager'"
       />
     </v-row>

+ 23 - 16
pages/opentalent_artist.vue

@@ -1,11 +1,19 @@
 <template>
-  <div class="theme-artist">
+  <div class="theme-artist" >
     <LayoutNavigation />
-    <LogicielsArtistBanner />
-    <CommonMenuScroll :menus="menus" class="mb-6" />
 
     <CommonStickyMenu :actions="stickyMenuActions" />
 
+    <CommonBannerTitle
+      title="Opentalent Artist"
+      leftText="School"
+      rightText="Manager"
+    />
+
+    <LogicielsArtistBanner />
+
+    <CommonMenuScroll :menus="menus" class="mb-6" />
+
     <LogicielsArtistPresentation />
 
     <LogicielsArtistAvantages />
@@ -16,7 +24,7 @@
 
     <LogicielsArtistAbonnement />
 
-    <LogicielsArtistFormations />
+    <LogicielsArtistFormations/>
 
     <LogicielsArtistReviews />
 
@@ -29,19 +37,18 @@
 </template>
 
 <script setup lang="ts">
-import { ref } from "vue";
 import { StickyMenuActionType } from "~/types/enum/layout";
-import { StickyMenuAction } from "~/types/interface";
-
-const menus = ref([
-  { id: "Presentation", label: "Présentation", element: null },
-  { id: "Avantages", label: "Avantages", element: null },
-  { id: "Fonctionnalites", label: "Fonctionnalités", element: null },
-  { id: "Comparatif", label: "Comparatif", element: null },
-  { id: "Abonnement", label: "Abonnement", element: null },
-  { id: "Webinaires", label: "Wébinaires", element: null },
-  { id: "Temoignages", label: "Témoignages", element: null },
-]).value;
+import { MenuScroll, StickyMenuAction } from "~/types/interface";
+
+const menus = [
+  { anchor: "presentation", label: "Présentation" },
+  { anchor: "benefits", label: "Avantages" },
+  { anchor: "functionalities", label: "Fonctionnalités" },
+  { anchor: "comparative", label: "Comparatif" },
+  { anchor: "subscription", label: "Abonnement" },
+  { anchor: "webinars", label: "Wébinaires" },
+  { anchor: "testimonials", label: "Témoignages" },
+];
 
 const stickyMenuActions: Array<StickyMenuAction> = [
   {

+ 14 - 0
plugins/router.ts

@@ -0,0 +1,14 @@
+/**
+ * Redéfinit le comportement du router quand on navigue vers une ancre
+ */
+export default defineNuxtPlugin(({ $router }) => {
+  $router.options.scrollBehavior = (to: { hash: any}, from: any, savedPosition: any) => {
+    if (to.hash) {
+      return {
+        el: to.hash,
+        top: 30,
+        behavior: 'smooth'
+      }
+    }
+  }
+})

+ 8 - 1
stores/layoutStore.ts

@@ -8,9 +8,16 @@ export const useLayoutStore = defineStore('layout', () => {
     isFooterVisible.value = value
   }
 
+  const isAnchoredSectionOnScreen: Ref<Record<string, boolean>> = ref({})
+
+  const setIsAnchoredSectionOnScreen = (sectionId: string, value: boolean) => {
+    isAnchoredSectionOnScreen.value[sectionId] = value
+  }
 
   return {
     isFooterVisible,
-    setIsFooterVisible
+    setIsFooterVisible,
+    isAnchoredSectionOnScreen,
+    setIsAnchoredSectionOnScreen
   }
 })

+ 5 - 0
types/interface.d.ts

@@ -46,3 +46,8 @@ interface Review {
   status: string,
   structure: string,
 }
+
+interface MenuScroll {
+  label: string,
+  anchor: string
+}