Browse Source

page actualité

Maha Bouchiba 2 năm trước cách đây
mục cha
commit
9adc002fc8

+ 6 - 1
.env.docker

@@ -1,4 +1,9 @@
 ## LOCAL ENVIRONMENT FILE
 NUXT_ENV=dev
 NUXT_DEBUG=1
-DEBUG=1
+DEBUG=1
+
+NUXT_API_BASE_URL=http://nginx_maestro/api
+NUXT_PUBLIC_API_BASE_URL=http://nginx_maestro/api
+
+

+ 0 - 4
components/Layout/Prefooter.vue

@@ -151,9 +151,7 @@ const { smAndDown, lgAndUp } = useDisplay();
   line-height: 18px;
   display: flex;
   align-items: center;
-
   color: #ecfbfc;
-
   text-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25), 0px 4px 4px rgba(0, 0, 0, 0.25),
     0px 4px 4px rgba(0, 0, 0, 0.25);
 }
@@ -167,9 +165,7 @@ const { smAndDown, lgAndUp } = useDisplay();
   line-height: 18px;
   display: flex;
   align-items: center;
-
   color: #ecfbfc;
-
   text-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25), 0px 4px 4px rgba(0, 0, 0, 0.25),
     0px 4px 4px rgba(0, 0, 0, 0.25);
 }

+ 6 - 0
composables/useApiFetch.ts

@@ -0,0 +1,6 @@
+import { NitroFetchRequest } from 'nitropack'
+
+export function useApiFetch(request:NitroFetchRequest, options:any = {}) {
+  const config = useRuntimeConfig()
+  return useLazyFetch(request, { baseURL: config.apiBaseURL ?? config.public.apiBaseUrl, ...options })
+}

+ 11 - 0
composables/useDataProvider.ts

@@ -0,0 +1,11 @@
+import dataProvider from "~~/services/data/dataProvider";
+let data= null;
+
+export const useDataProvider = () => {
+  
+  if(dataProvider === null) {
+    const { apiRequestService } = useMaestroRequestService();
+    data = new dataProvider(apiRequestService);
+  }
+return dataProvider;
+}

+ 62 - 0
composables/useMaestroRequestService.ts

@@ -0,0 +1,62 @@
+import {FetchContext, FetchOptions} from "ohmyfetch";
+import ApiRequestService from "~/services/data/apiRequestService";
+import {Ref} from "@vue/reactivity";
+
+/**
+ * Retourne une instance de ApiRequestService configurée pour interroger l'api Ap2i
+ *
+ * @see https://github.com/unjs/ohmyfetch/blob/main/README.md#%EF%B8%8F-create-fetch-with-default-options
+ */
+let apiRequestServiceClass:null|ApiRequestService = null
+export const useMaestroRequestService = () => {
+    const runtimeConfig = useRuntimeConfig()
+
+    const baseURL = runtimeConfig.apiBaseUrl ?? runtimeConfig.public.apiBaseUrl
+
+    const pending: Ref<boolean> = ref(false)
+
+    /**
+     * Peuple les headers avant l'envoi de la requête
+     *
+     * @param request
+     * @param options
+     */
+    const onRequest = async function ({ request, options }: FetchContext) {
+        // @ts-ignore
+        if(options && options.noXaccessId) {
+            return
+        }
+
+        pending.value = true
+        console.log('Request : ' + request + ' (SSR: ' + process.server + ')')
+    }
+
+    const onResponse = async function({ request, options, response }: FetchContext) {
+        pending.value = false
+    }
+
+    /**
+     * Gère les erreurs retournées par l'api
+     *
+     * @param request
+     * @param response
+     * @param error
+     */
+
+
+    const config : FetchOptions = {
+        baseURL,
+        onRequest,
+        onResponse,
+    }
+
+    //Avoid memory leak
+    if (apiRequestServiceClass === null) {
+        // Utilise la fonction `create` d'ohmyfetch pour générer un fetcher dédié à l'interrogation de Ap2i
+        const fetcher = $fetch.create(config)
+        // @ts-ignore
+        apiRequestServiceClass = new ApiRequestService(fetcher)
+    }
+
+    return { apiRequestService: apiRequestServiceClass, pending: pending }
+}

+ 0 - 191
pages/actualites.vue

@@ -1,191 +0,0 @@
-<template>
-  <LayoutNavigation />
-
-  <h1 class="title">Toutes les news</h1>
-
-  <v-row>
-    <v-col>
-      <div class="back-container-pagination d-flex justify-space-between">
-        <v-col cols="6">
-          <v-btn class="back-pagination" text @click="goBack">
-            <v-icon>mdi-arrow-left</v-icon>
-            Retour
-          </v-btn>
-        </v-col>
-
-        <v-col cols="6">
-          <div class="d-flex justify-end">
-            <v-pagination
-              v-model="page"
-              :length="pages"
-              @input="onPageChange"
-            ></v-pagination>
-          </div>
-        </v-col>
-      </div>
-    </v-col>
-  </v-row>
-
-  <v-row
-    class="d-flex align-center mb-6"
-    v-for="(actualite, index) in actualites"
-    :key="index"
-  >
-    <v-card class="container-green">
-      <v-card-item>
-        <v-container fluid>
-          <v-row align="center">
-            <v-col cols="3">
-              <v-img
-                :src="actualite.image"
-                alt="poster"
-                class="image-actu mt-12"
-              />
-            </v-col>
-
-            <v-col cols="9">
-              <div class="border">
-                <NuxtLink class="text-decoration-none">
-                  <v-card-title class="card-title">
-                    {{ actualite.titre }}
-                  </v-card-title>
-                </NuxtLink>
-
-                <v-card-text class="infos">
-                  <table>
-                    <tr>
-                      <div class="container-info">
-                        <td>
-                          {{ actualite.description }}
-                        </td>
-                      </div>
-                    </tr>
-                  </table>
-                </v-card-text>
-                <v-col :cols="actionsColumnWidth">
-                  <v-card-actions class="justify-end">
-                    <div class="container-button justify-end">
-                      <v-btn class="btn btn-common btn-subscribe" text>
-                        <v-icon class="fas fa-info mr-2"></v-icon>En savoir plus
-                      </v-btn>
-                    </div>
-                  </v-card-actions>
-                </v-col>
-              </div>
-            </v-col>
-          </v-row>
-        </v-container>
-      </v-card-item>
-    </v-card>
-  </v-row>
-</template>
-
-<script setup>
-import { ref } from "vue";
-
-const actualites = ref([
-  {
-    image: "/images/actualites/actualite1.png",
-    titre: "ANDRÉA BOCCELLI EN CONCERT GRATUIT À LOURDES - JUIL 2023",
-    description:
-      "Lorem ipsum dolor sit amet. At doloribus nostrum non temporibus quidem est ipsa laborum sed internos dolor in pariatur vitae. Et quis nostrum non praesentium impedit 33 doloribus cumque et adipisci velit! Et pariatur nihil quo modi dolorum qui porro corporis est vero alias et eligendi officiis!",
-  },
-  {
-    image: "/images/actualites/actualite3.png",
-    titre: "DÉCOUVREZ L’INCROYABLE HISTOIRE DU VIOLON DU TITANIC",
-    description:
-      "Lorem ipsum dolor sit amet. At doloribus nostrum non temporibus quidem est ipsa laborum sed internos dolor in pariatur vitae. Et quis nostrum non praesentium impedit 33 doloribus cumque et adipisci velit! Et pariatur nihil quo modi dolorum qui porro corporis est vero alias et eligendi officiis!",
-  },
-  {
-    image: "/images/actualites/actualite4.png",
-    titre: "RETOUR SUR LE BRASS BAND 2023",
-    description: "Lorem ipsum dolor sit amet...",
-  },
-  {
-    image: "/images/actualites/actualite2.png",
-    titre: "ÉTÉ CULTUREL : APPEL À CANDIDATURE",
-    description: "Lorem ipsum dolor sit amet...",
-  },
-]);
-</script>
-
-<style scoped>
-.title {
-  color: #d1cdc7;
-  margin-left: 5rem;
-  margin-top: 2rem;
-  font-family: Barlow;
-  font-size: 42px;
-  font-style: normal;
-  font-weight: 600;
-  line-height: 42px;
-}
-.container-info {
-  color: #fff;
-  font-family: Barlow;
-  font-size: 22px;
-  font-style: normal;
-  font-weight: 500;
-  line-height: 26px;
-}
-.border {
-  border: 1px solid white !important;
-  padding: 10px;
-  border-radius: 20px;
-  width: 100%;
-}
-
-.image-actu {
-  width: 80%;
-  margin-left: auto;
-  margin-right: auto;
-  bottom: 2rem;
-}
-
-.btn-common {
-  display: flex;
-  left: 0;
-  padding: 25px 28px;
-  align-items: center;
-  gap: 9px;
-  color: var(--NEUTRAL---BLANC, #fff);
-  font-family: Barlow;
-  font-size: 0.8rem;
-  font-style: normal;
-  font-weight: 700;
-  line-height: 15px;
-  letter-spacing: 1.3px;
-  text-transform: uppercase;
-}
-
-.btn {
-  background: var(--Vert-60, #64afb7);
-}
-
-.card-title {
-  color: var(--NEUTRAL---BLANC, #fff);
-  font-family: Barlow;
-  font-size: 36px;
-  margin-left: 1rem;
-  font-style: normal;
-  font-weight: 600;
-  line-height: 39px;
-}
-
-.container-green {
-  border-radius: 10px;
-  background: #112528;
-  margin-bottom: 2rem;
-  margin-left: 2rem;
-  margin-right: 2rem;
-  color: #fff;
-  color: #fff;
-  font-family: Barlow;
-  font-size: 36px;
-  font-style: normal;
-  font-weight: 600;
-  line-height: 39px;
-  width: 100%;
-  height: auto;
-}
-</style>

+ 507 - 0
pages/actualites/[id].vue

@@ -0,0 +1,507 @@
+<template>
+  <LayoutContainer>
+    <LayoutNavigation />
+    <v-row>
+      <v-col cols="12">
+        <div class="banner-container">
+          <img src="/images/actu/pub.png" alt="line" class="cover-image" />
+        </div>
+      </v-col>
+    </v-row>
+    <div v-if="actu">
+      <v-col>
+        <div class="d-flex align-items-center ml-12">
+          <div class="carousel-button">
+            <i class="fa-solid fa-arrow-left" />
+          </div>
+          <nuxt-link to="/actualites" class="ml-2 back-button mt-12">
+            Retour aux actualités
+          </nuxt-link>
+        </div>
+      </v-col>
+
+      <v-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>
+        <p class="description-actu">
+          {{ actu.bodyText }}
+        </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">
+      <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">
+      <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">
+            <i class="fas fa-chevron-left" />
+          </div>
+          <div class="carousel-button" @click="goNext">
+            <i class="fas fa-chevron-right" />
+          </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>
+  <LayoutPrefooter />
+  <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/useMaestroRequestService";
+const { apiRequestService } = useMaestroRequestService();
+
+const actu = ref(null);
+const route = useRoute();
+const newsId = route.params.id;
+const pending = ref(true);
+// data event list using lazy loading
+onMounted(async () => {
+  try {
+    actu.value = await apiRequestService.get(
+      `https://local.maestro.opentalent.fr/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 `https://local.maestro.opentalent.fr/uploads/news/${attachment}`;
+  }
+  console.log("pas d'image");
+  return "/images/actu/image.png";
+};
+
+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();
+</script>
+
+<style scoped>
+.banner-container {
+  position: relative;
+  overflow: hidden;
+  display: flex;
+  justify-content: center;
+  align-items: flex-start;
+  height: 25rem;
+  width: 100%;
+}
+.cover-image {
+  width: 95%;
+  height: auto;
+  object-fit: cover;
+  margin: 0 auto;
+  display: block;
+}
+
+.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: #112528;
+}
+.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: .5rem;
+  margin-top: .5rem
+}
+
+.key-word{
+  color: #000;
+font-family: Barlow;
+font-size: 20px;
+font-style: normal;
+font-weight: 500;
+line-height: 24px;
+}
+
+.title-other{
+  color: #fff;
+}
+</style>

+ 288 - 0
pages/actualites/index.vue

@@ -0,0 +1,288 @@
+<template>
+  <LayoutNavigation />
+
+  <h1 class="title mt-12 mb-12">Toutes les news</h1>
+
+
+  <div v-if="pending">{{ t("pending") }}</div>
+
+  <div v-else>
+
+    <v-row>
+      <v-col>
+        <div class="d-flex align-items-center ml-12 mt-6 mb-6">
+          <div class="carousel-button">
+            <i class="fa-solid fa-arrow-left" />
+          </div>
+          <nuxt-link to="/actualites" class="ml-2 back-button mt-6">
+            Retour aux actualités
+          </nuxt-link>
+        </div>
+      </v-col>
+
+      <v-pagination
+        v-model="page"
+        :length="Math.ceil(totalItems / itemsPerPage)"
+        total-visible="9"
+        @input="updatePage"
+        rounded="circle"
+        max="10"
+        class="pagination mr-10 mt-6"
+        :prev-icon="page !== 1 ? 'fas fa-arrow-left' : ''"
+        :next-icon="
+          page !== Math.ceil(totalItems / itemsPerPage)
+            ? 'fas fa-arrow-right'
+            : ''
+        "
+      />
+    </v-row>
+
+    <v-row
+      class="d-flex align-center mr-10"
+      v-for="(news, index) in news"
+      :key="index"
+    >
+      <v-card class="container-green">
+        <v-card-item>
+          <v-container fluid>
+            <v-row align="center">
+              <v-col cols="3">
+                <v-img
+                  :src="getImageUrl(news.attachment)"
+                  alt="poster"
+                  class="image-actu"
+                  height="200"
+                  width="400"
+                />
+              </v-col>
+
+              <v-col cols="9">
+                <div class="border">
+                  <NuxtLink
+                    :to="`/actualites/${news.id}`"
+                    class="text-decoration-none"
+                  >
+                    <v-card-title class="card-title">
+                      {{ news.title }}
+                    </v-card-title>
+                  </NuxtLink>
+
+                  <v-card-text class="infos">
+                    <div class="flex-container">
+                      <div class="text-container">
+                        <table>
+                          <tr>
+                            <td class="leadtext">
+                              {{ news.leadText }}
+                            </td>
+                          </tr>
+                        </table>
+                      </div>
+                      <div class="button-container">
+                        <v-card-actions class="justify-end">
+                          <v-btn class="btn" text>
+                            <v-icon class="fas fa-info mr-2"></v-icon>En savoir
+                            plus
+                          </v-btn>
+                        </v-card-actions>
+                      </div>
+                    </div>
+                  </v-card-text>
+                  <v-col :cols="actionsColumnWidth"> </v-col>
+                </div>
+              </v-col>
+            </v-row>
+          </v-container>
+        </v-card-item>
+      </v-card>
+    </v-row>
+
+    <v-pagination
+        v-model="page"
+        :length="Math.ceil(totalItems / itemsPerPage)"
+        total-visible="9"
+        @input="updatePage"
+        rounded="circle"
+        max="10"
+        class="pagination mr-10"
+        :prev-icon="page !== 1 ? 'fas fa-arrow-left' : ''"
+        :next-icon="
+          page !== Math.ceil(totalItems / itemsPerPage)
+            ? 'fas fa-arrow-right'
+            : ''
+        "
+      />
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, onMounted, watch } from "vue";
+import { useMaestroRequestService } from "~/composables/useMaestroRequestService";
+
+const { apiRequestService } = useMaestroRequestService();
+const currentPage = ref(1);
+const totalPages = ref(0);
+const page: Ref<number> = ref(1);
+const itemsPerPage: Ref<number> = ref(10);
+const { t } = useI18n();
+
+const updatePage = (newPage: number) => {
+  console.log("updatePage", newPage);
+  page.value = newPage;
+};
+
+// récupère les événements en fonction des filtres
+const query = computed(() => {
+  const queryParams: { page: number; [key: string]: number | string } = {
+    page: page.value,
+  };
+
+  return queryParams;
+});
+const getImageUrl = (attachment: string) => {
+  return `https://local.maestro.opentalent.fr/uploads/news/${attachment}`;
+};
+const totalItems = ref(0);
+
+// data event list using lazy loading
+const {
+  data: news = [],
+  pending,
+  refresh,
+} = useLazyAsyncData("files", async () => {
+  const response = await apiRequestService.get(
+    "https://local.maestro.opentalent.fr/api/news",
+    query.value
+  );
+  const collection = response["hydra:member"];
+  totalItems.value = response["hydra:totalItems"];
+  console.log(collection);
+  return collection;
+});
+
+watch(page, () => {
+  refresh();
+});
+</script>
+
+<style scoped>
+.v-container {
+  padding: 0 !important;
+}
+.v-card-text {
+  letter-spacing: 0 !important;
+  padding: 0 !important;
+}
+
+.v-card-item {
+  padding: 0 !important;
+}
+.title {
+  color: #d1cdc7;
+  margin-left: 3rem;
+  margin-top: 2rem;
+  font-family: Barlow;
+  font-size: 4rem;
+  font-style: normal;
+  font-weight: 600;
+  line-height: 42px;
+}
+.border {
+  border: 1px solid white !important;
+  padding: 9px;
+  border-radius: 20px;
+  width: 99%;
+}
+
+.image-actu {
+  width: 80%;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+.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;
+}
+
+.card-title {
+  color: #FFF;
+font-family: Barlow;
+font-size: 36px;
+font-style: normal;
+font-weight: 600;
+line-height: 39px
+}
+
+.container-green {
+  border-radius: 10px;
+  min-width: 100%;
+  background: #112528;
+  margin-bottom: 1rem;
+  margin-left: 2rem;
+  margin-right: 2rem;
+  padding-top: 0.2rem;
+  padding-bottom: 0.2rem;
+  height: 60%;
+}
+
+.infos {
+  color: #fff;
+}
+
+.pagination {
+  align-items: center;
+  margin-top: 16px;
+  margin-bottom: 16px;
+  color: #112528;
+}
+
+.leadtext {
+  color: #FFF;
+font-family: Barlow;
+font-size: 22px;
+font-style: normal;
+font-weight: 500;
+line-height: 26px;
+margin-left: 1rem;
+
+}
+
+.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;
+}
+
+.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;
+}
+</style>

+ 44 - 0
services/data/apiRequestService.ts

@@ -0,0 +1,44 @@
+/**
+ * A basic api request service
+ *
+ * It will send basic http requests and returns raw results
+ */
+import { $Fetch, FetchOptions } from "ohmyfetch";
+
+class ApiRequestService {
+  private readonly fetch: $Fetch;
+
+  public constructor(fetch: $Fetch) {
+    this.fetch = fetch;
+  }
+
+  /**
+   * Send a GET request
+   *
+   * @param url
+   * @param query
+   */
+  public async get<T>(
+    url: string,
+    query: AssociativeArray | null = null
+  ): Promise<T> {
+    return await this.request<T>("GET", url, null, query);
+  }
+
+  protected async request<T>(
+    method: string,
+    url: string,
+    body: string | null = null,
+    query: AssociativeArray | null = null
+  ): Promise<T> {
+    const config: FetchOptions = { method };
+    if (query) {
+      config.query = query;
+    }
+
+    // @ts-ignore
+    return this.fetch<T>(url, config);
+  }
+}
+
+export default ApiRequestService;

+ 34 - 0
services/data/dataProvider.ts

@@ -0,0 +1,34 @@
+import ApiRequestService from "./apiRequestService";
+
+class dataProvider {
+  private apiRequestService: ApiRequestService;
+  public constructor(apiRequestService: ApiRequestService) {
+    this.apiRequestService = apiRequestService;
+  }
+
+  public async fetchNews(): Promise<{ news }> {
+    const response = await this.apiRequestService.get(
+      "/news"
+    );
+    console.log(response)
+
+    return {
+      news: response,
+    };
+    
+  }
+
+  public async fetchNew(
+    id: number
+  ): Promise<{ new }> {
+    const response = await this.apiRequestService.get(
+      "/news/" + id
+    );
+    console.log(response)
+      return {
+        new: response,
+      };
+  }
+}
+
+export default dataProvider;

+ 3 - 0
types/interface.d.ts

@@ -0,0 +1,3 @@
+interface AssociativeArray {
+  [key: string]: any;
+}