Преглед на файлове

add the news index page

Olivier Massot преди 1 година
родител
ревизия
9959d89f51
променени са 4 файла, в които са добавени 305 реда и са изтрити 325 реда
  1. 5 4
      components/JoinUs/Missions.vue
  2. 297 0
      components/News/List.vue
  3. 1 0
      models/Maestro/News.ts
  4. 2 321
      pages/actualites/index.vue

+ 5 - 4
components/JoinUs/Missions.vue

@@ -52,7 +52,6 @@
           :length="lastPage"
           rounded="circle"
           max="10"
-          class="pagination"
           @update:model-value="pageUpdated"
         />
       </v-col>
@@ -170,6 +169,7 @@ const { data: jobCollection, pending, refresh } = fetchCollection(
   query
 )
 
+// TODO: voir pourquoi on se retrouve obligé de passer par ce computed pour avoir le type TS correct?
 const jobs: ComputedRef<JobPosting[]> = computed(() => {
   return jobCollection.value !== null ?
          jobCollection.value.items as JobPosting[] :
@@ -186,11 +186,12 @@ const pageUpdated = async (newVal: number): Promise<void> => {
   page.value = newVal
 
   pending.value = true
-  await navigateTo({ path: '', hash: '#join-us-anchor' })
+  await refresh()
 
+  // TODO: remplacer par un watcher sur pending?
   setTimeout(
-    async () => await refresh(),
-    100
+    async () => await navigateTo({ path: '', hash: '#join-us-anchor' }),
+    200
   )
 }
 

+ 297 - 0
components/News/List.vue

@@ -0,0 +1,297 @@
+<template>
+  <h1 id="news-anchor" class="title mt-12 mb-12">
+    Toutes les news
+  </h1>
+
+  <div v-if="pending">
+    <v-row class="justify-center progress">
+      <v-progress-circular
+        indeterminate
+        color="grey"
+      />
+    </v-row>
+  </div>
+
+  <div v-else-if="newsItems !== null">
+    <v-row
+      v-for="(newsItem, index) in newsItems"
+      :key="index"
+      class="news d-flex align-center mr-10"
+    >
+      <v-card class="alt-theme">
+        <v-card-item>
+          <v-container fluid>
+            <v-row align="center">
+              <v-col cols="3">
+                <v-img
+                  v-if="newsItem.attachment"
+                  :src="getImageUrl(newsItem.attachment)"
+                  alt="poster"
+                  height="200"
+                  width="400"
+                />
+              </v-col>
+
+              <v-col cols="9">
+                <div class="details">
+                  <NuxtLink
+                    :to="`/actualites/${newsItem.id}`"
+                    class="text-decoration-none"
+                  >
+                    <v-card-title>
+                      {{ newsItem.title }}
+
+                      <v-icon
+                        v-if="newsItem.featured"
+                        size="16"
+                        icon="star fas fa-star"
+                      />
+                    </v-card-title>
+                  </NuxtLink>
+
+                  <v-card-text>
+                    <div class="flex-container">
+                      <div class="text-container">
+                        <table>
+                          <tr>
+                            <td>
+                              {{ newsItem.leadText }}
+                            </td>
+                          </tr>
+                        </table>
+                      </div>
+
+                      <div class="button-container">
+                        <v-card-actions class="justify-end">
+                          <NuxtLink
+                            :to="`/actualites/${newsItem.id}`"
+                            class="text-decoration-none"
+                          >
+                            <v-btn class="btn" text>
+                              <v-icon icon="fas fa-info" class="mr-2" />
+                              En
+                              savoir plus
+                            </v-btn>
+                          </NuxtLink>
+                        </v-card-actions>
+                      </div>
+                    </div>
+                  </v-card-text>
+                </div>
+              </v-col>
+            </v-row>
+          </v-container>
+        </v-card-item>
+      </v-card>
+    </v-row>
+
+    <v-row>
+      <v-col cols="12">
+        <v-pagination
+          v-if="lastPage"
+          :model-value="page"
+          :length="lastPage"
+          rounded="circle"
+          max="10"
+          class="mt-4"
+          @update:model-value="pageUpdated"
+        />
+      </v-col>
+    </v-row>
+  </div>
+</template>
+
+<script setup lang="ts">
+
+import { ComputedRef } from "vue";
+import { useEntityFetch } from "~/composables/data/useEntityFetch";
+import News from "~/models/Maestro/News";
+
+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}`;
+
+const page: Ref<number> = ref(1);
+
+const updatePage = (newPage: number) => {
+  console.log("updatePage", newPage);
+  page.value = newPage;
+};
+
+const query: ComputedRef<Record<string, string | number>> = computed(() => {
+  return {
+    page: page.value,
+    type: "ENTREPRISE",
+  };
+});
+
+const { data: newsCollection, pending, refresh } = fetchCollection(News, null, query)
+
+const filterNews = (items: News[]): News[] => {
+  const results = []
+  const currentDate = new Date();
+
+  for (const item of items) {
+    if (
+      item.startPublication &&
+      currentDate < new Date(item.startPublication)
+    ) {
+      continue
+    }
+    if (
+      item.endPublication &&
+      currentDate > new Date(item.endPublication)
+    ) {
+      continue
+    }
+
+    if (item.type === "ENTREPRISE") {
+      results.push(item)
+    }
+  }
+
+  return results
+}
+
+const sortNews = (items: News[]): News[] => {
+  return items.sort((a, b) => {
+    if (a.featured !== b.featured) {
+      return a.featured ? -1 : 1; // placer les objets "featured" en premier
+    } else {
+      //@ts-ignore
+      return b.id - a.id;
+    }
+  })
+}
+
+// TODO: voir pourquoi on se retrouve obligé de passer par ce computed pour avoir le type TS correct?
+const newsItems: ComputedRef<News[]> = computed(() => {
+  if (newsCollection.value === null) {
+    return [];
+  }
+
+  const items = newsCollection.value.items as News[]
+
+  // TODO: ça va pas, faut faire le tri et le filtre côté api (sinon ça trie et filtre que la page en cours...)
+  return sortNews(filterNews(items))
+})
+
+const lastPage: ComputedRef<number | null> = computed(() => {
+  return newsCollection.value !== null && newsCollection.value.pagination ?
+    newsCollection.value.pagination.last ?? null :
+    null
+})
+
+const pageUpdated = async (newVal: number): Promise<void> => {
+  // TODO: factoriser, puisque on retrouve la même logique dans le component JoinUsMissions
+  page.value = newVal
+
+  pending.value = true
+  await refresh()
+
+  setTimeout(
+    async () => await navigateTo({ path: '', hash: '#news-anchor' }),
+    200
+  )
+}
+</script>
+
+<style scoped lang="scss">
+.v-container {
+  padding: 0 !important;
+}
+
+h1 {
+  color: #d1cdc7; /* TODO: pqoi cette couleur ici? */
+  margin-left: 3rem;
+  margin-top: 2rem;
+  font-family: Barlow, serif;
+  font-size: 4rem;
+  font-style: normal;
+  font-weight: 600;
+  line-height: 42px;
+}
+
+.news {
+  .v-card {
+    border-radius: 10px;
+    min-width: 100%;
+    margin-bottom: 1rem;
+    margin-left: 2rem;
+    margin-right: 2rem;
+    padding-top: 0.2rem;
+    padding-bottom: 0.2rem;
+    height: 60%;
+
+    .v-card-text {
+      letter-spacing: 0 !important;
+      padding: 0 !important;
+    }
+
+    .v-card-item {
+      padding: 0 !important;
+    }
+  }
+
+  .v-img {
+    width: 80%;
+    margin-left: auto;
+    margin-right: auto;
+  }
+
+  .details {
+    border: 1px solid white !important;
+    padding: 9px;
+    border-radius: 20px;
+    width: 99%;
+
+    .v-card-title {
+      color: #fff;
+      font-family: Barlow, serif;
+      font-size: 36px;
+      font-style: normal;
+      font-weight: 600;
+      line-height: 39px;
+    }
+
+    .star {
+      color: yellow !important;
+    }
+
+    .v-card-text {
+      td {
+        color: #fff;
+        font-family: Barlow, serif;
+        font-size: 22px;
+        font-style: normal;
+        font-weight: 500;
+        line-height: 26px;
+        margin-left: 1rem;
+      }
+    }
+
+    .v-btn {
+      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;
+    }
+  }
+}
+</style>

+ 1 - 0
models/Maestro/News.ts

@@ -24,6 +24,7 @@ export default class News extends ApiModel {
   @Str(null)
   declare bodyText: string | null
 
+  @Bool(false)
   declare featured: boolean
 
   @Bool(false)

+ 2 - 321
pages/actualites/index.vue

@@ -1,328 +1,9 @@
 <template>
   <LayoutNavigation />
 
-  <h1 class="title mt-12 mb-12">Toutes les news</h1>
+  <NewsList />
 
-  <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 à l'accueil
-          </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">
-                      <!-- Icône étoile pour les actualités 'featured' -->
-
-                      <!-- Titre de l'actualité -->
-                      {{ news.title }}
-                      <v-icon
-                        size="16"
-                        v-if="news.featured"
-                        class="fas fa-star"
-                        style="color: yellow"
-                      ></v-icon>
-                    </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">
-                          <NuxtLink
-                            :to="`/actualites/${news.id}`"
-                            class="text-decoration-none"
-                          >
-                            <v-btn class="btn" text>
-                              <v-icon class="fas fa-info mr-2"></v-icon>En
-                              savoir plus
-                            </v-btn>
-                          </NuxtLink>
-                        </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>
   <LayoutFooterPrefooter />
+
   <LayoutFooter />
 </template>
-
-<script setup lang="ts">
-import { ref, watch } from "vue";
-import { useMaestroRequestService } from "~/composables/data/useMaestroRequestService";
-
-// Base URL for API requests
-const config = useRuntimeConfig();
-const baseUrl = `${config.public.apiBaseUrl}/api/news`;
-const getImageUrl = (attachment) =>
-  `${config.public.apiBaseUrl}/uploads/news/${attachment}`;
-
-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;
-};
-
-const query = computed(() => {
-  const queryParams: {
-    page: number;
-    type?: string;
-    [key: string]: number | string;
-  } = {
-    page: page.value,
-    type: "ENTREPRISE",
-  };
-
-  return queryParams;
-});
-
-const totalItems = ref(0);
-
-const {
-  data: news = [],
-  pending,
-  refresh,
-} = useLazyAsyncData("files", async () => {
-  const response = await apiRequestService.get(baseUrl, query.value);
-  const collection = response["hydra:member"];
-  const currentDate = new Date();
-
-  // Séparer les actualités en fonction de la propriété 'featured'
-  const featuredNews = collection
-    .filter((item) => item.featured)
-    .sort((a, b) => new Date(b.startPublication) - new Date(a.startPublication))
-    .slice(0, 3); // Prendre les trois dernières actualités 'featured'
-
-  const regularNews = collection.filter((item) => {
-    const startPublicationDate = new Date(item.startPublication);
-    const endPublicationDate = new Date(item.endPublication);
-
-    return (
-      !item.featured &&
-      item.type === "ENTREPRISE" &&
-      endPublicationDate >= currentDate &&
-      startPublicationDate <= currentDate
-    );
-  });
-
-  // Combinez les actualités 'featured' et les autres actualités
-  const combinedNews = [...featuredNews, ...regularNews];
-
-  totalItems.value = combinedNews.length;
-  console.log(combinedNews);
-  return combinedNews;
-});
-
-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>