浏览代码

refactor the "nous-rejoindre/[id]" page

Olivier Massot 1 年之前
父节点
当前提交
5e705ffccf

+ 205 - 0
components/JoinUs/MissionDetail.vue

@@ -0,0 +1,205 @@
+<template>
+  <LayoutContainer>
+    <v-row>
+      <v-col cols="12">
+        <CommonBanner
+          imageSrc="/images/actu/pub.png"
+          imageAlt="banner"
+        />
+      </v-col>
+    </v-row>
+
+    <div class="job-section">
+      <v-row class="mb-6">
+        <v-col class="d-flex align-items-center">
+          <v-btn
+            to="/nous-rejoindre"
+            prepend-icon="fas fa-arrow-left"
+            class="back-button"
+          >
+            Retour aux annonces
+          </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="job !== null">
+          <LayoutUITitlePage>
+            {{ job.title }}
+          </LayoutUITitlePage>
+
+          <v-row class="details blue-content my-6">
+            <v-col cols="6">
+              <v-row>
+                <div>
+                  Type de contrat :
+                  <b>{{ job.contractType }} </b>
+                </div>
+              </v-row>
+
+              <v-row>
+                <div>
+                  Location :
+                  <b>{{ job.postalCode }} {{ job.city }}</b>
+                </div>
+              </v-row>
+            </v-col>
+
+            <v-col cols="6">
+              <v-row>
+                <div>
+                  Secteur d'activité : <b>{{ job.sector.join(', ') }}</b>
+                </div>
+              </v-row>
+
+              <v-row>
+                <div>
+                  Date de parution :
+                  <b>{{ formatDate(job.startPublication) }}</b>
+                </div>
+              </v-row>
+            </v-col>
+          </v-row>
+
+          <v-row>
+            <p class="custom-row description mb-12">
+              <!-- TODO: à revoir, en sachant qu'injecter du html ici serait une faille de sécurité sévère :( -->
+              {{ job.content }}
+            </p>
+          </v-row>
+
+          <v-row class="d-flex justify-center align-center">
+            <v-btn
+              prepend-icon="fas fa-info"
+              class="btn-apply mb-12"
+            >
+              Je postule
+            </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>
+
+  </LayoutContainer>
+</template>
+
+<script setup lang="ts">
+import "vue3-carousel/dist/carousel.css";
+import { useEntityFetch } from "~/composables/data/useEntityFetch";
+import JobPosting from "~/models/Maestro/JobPosting";
+import DateUtils from "~/services/utils/dateUtils";
+
+const route = useRoute();
+const { fetch } = useEntityFetch()
+const config = useRuntimeConfig()
+
+const jobId: number = parseInt(route.params.id as string)
+if (!jobId || isNaN(jobId)) {
+  throw new Error('Missing or invalid id')
+}
+
+const { data: job, pending } = fetch(JobPosting, jobId)
+
+const formatDate = (date: string) => {
+  return DateUtils.format(new Date(date), "dd/MM/yyyy")
+};
+</script>
+
+<style scoped lang="scss">
+.custom-row {
+  width: 90%;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+.progress {
+  margin: 32px auto 128px auto;
+}
+
+.job-section {
+  margin: 32px 12%;
+
+  .details {
+    background-color: var(--secondary-color);
+    color: var(--on-secondary-color);
+    height: 10rem;
+
+    .v-row {
+      width: 90%;
+      margin-left: auto;
+      margin-right: auto;
+    }
+
+    div {
+      margin: 12px 0;
+      font-family: "Barlow", serif;
+      font-style: normal;
+      font-weight: 500;
+      font-size: 25px;
+      line-height: 18px;
+    }
+  }
+
+  .description {
+    color: #0e2d32;
+    text-align: justify;
+    font-family: "Barlow", serif;
+    font-size: 1.875rem;
+    font-style: normal;
+    font-weight: 500;
+    line-height: 2.125rem;
+  }
+}
+
+@media (max-width: 600px) {
+  .job-section {
+    margin: 32px 6%;
+  }
+}
+
+.btn-apply {
+  background: var(--secondary-color);
+  color: var(--neutral-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 - 1
nuxt.config.ts

@@ -102,7 +102,7 @@ export default defineNuxtConfig({
   },
   },
   devtools: {
   devtools: {
     // @see https://github.com/nuxt/devtools
     // @see https://github.com/nuxt/devtools
-    enabled: false,
+    enabled: true,
   },
   },
   vite: {
   vite: {
     esbuild: {
     esbuild: {

+ 0 - 1
package.json

@@ -51,7 +51,6 @@
     "typeface-barlow": "^1.1.13",
     "typeface-barlow": "^1.1.13",
     "uuid": "^9.0.1",
     "uuid": "^9.0.1",
     "vite-plugin-vuetify": "^1.0.2",
     "vite-plugin-vuetify": "^1.0.2",
-    "vue-social-sharing": "^3.0.9",
     "vue3-carousel": "^0.3.1",
     "vue3-carousel": "^0.3.1",
     "vue3-datepicker": "^0.3.4",
     "vue3-datepicker": "^0.3.4",
     "vuetify": "^3.1.15"
     "vuetify": "^3.1.15"

+ 1 - 1
pages/actualites/[id].vue

@@ -140,7 +140,7 @@ import { ref } from "vue";
 import { Carousel, Slide } from "vue3-carousel";
 import { Carousel, Slide } from "vue3-carousel";
 import { useRoute } from "vue-router";
 import { useRoute } from "vue-router";
 import "vue3-carousel/dist/carousel.css";
 import "vue3-carousel/dist/carousel.css";
-import { useMaestroRequestService } from "~/composables/useMaestroRequestService";
+import { useMaestroRequestService } from "~/composables/data/useMaestroRequestService";
 const { apiRequestService } = useMaestroRequestService();
 const { apiRequestService } = useMaestroRequestService();
 
 
 const actu = ref(null);
 const actu = ref(null);

+ 1 - 1
pages/actualites/index.vue

@@ -131,7 +131,7 @@
 
 
 <script setup lang="ts">
 <script setup lang="ts">
 import { ref, watch } from "vue";
 import { ref, watch } from "vue";
-import { useMaestroRequestService } from "~/composables/useMaestroRequestService";
+import { useMaestroRequestService } from "~/composables/data/useMaestroRequestService";
 
 
 // Base URL for API requests
 // Base URL for API requests
 const config = useRuntimeConfig();
 const config = useRuntimeConfig();

+ 2 - 451
pages/nous-rejoindre/[id].vue

@@ -1,458 +1,9 @@
 <template>
 <template>
   <LayoutNavigation />
   <LayoutNavigation />
-  <LayoutContainer>
-    <v-row>
-      <v-col cols="12">
-        <CommonBanner :imageSrc="'/images/actu/pub.png'" imageAlt="'line'" />
-      </v-col>
-    </v-row>
 
 
-    <div v-if="job">
-      <v-row class="custom-row">
-        <v-col>
-          <div class="d-flex align-items-center">
-            <NuxtLink
-              to="/nous-rejoindre"
-              style="text-decoration: none !important"
-            >
-              <div class="carousel-button">
-                <i class="fa-solid fa-arrow-left" />
-              </div>
-            </NuxtLink>
-            <NuxtLink to="/nous-rejoindre" class="ml-2 back-button mt-12">
-              Retour aux annonces
-            </NuxtLink>
-          </div>
-        </v-col>
-      </v-row>
+  <JoinUsMissionDetail />
 
 
-      <LayoutUITitlePage :title="job.title" />
-
-      <v-row class="blue-content">
-        <v-col cols="6">
-          <v-row class="custom-row">
-            <h4 class="detail-job mt-6 ml-12">
-              Type de contrat :
-              <span class="bold">{{ job.contractType }} </span>
-            </h4></v-row
-          >
-
-          <v-row class="custom-row">
-            <h4 class="detail-job mt-6 ml-12">
-              Location :
-              <span class="bold">{{ job.postalCode }} {{ job.city }}</span>
-            </h4></v-row
-          >
-        </v-col>
-
-        <v-col cols="6">
-          <v-row class="custom-row">
-            <h4 class="detail-job mt-6 ml-12">
-              Secteur d'activité : <span class="bold">{{ job.sector }}</span>
-            </h4></v-row
-          >
-
-          <v-row class="custom-row">
-            <h4 class="detail-job mt-6 ml-12">
-              Date de parution :
-              <span class="bold">{{ formatDate(job.startPublication) }}</span>
-            </h4></v-row
-          >
-        </v-col>
-      </v-row>
-      <v-row>
-        <p class="custom-row description-job mb-12" v-html="job.content"></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>Je postule
-        </v-btn>
-      </v-row>
-    </div>
-
-    <v-row class="d-flex justify-space-between custom-row">
-      <p class="share">MOTS CLÉS</p>
-      <div>
-        <p class="share">PARTAGER</p>
-      </div>
-    </v-row>
-
-    <v-row class="d-flex justify-space-between mb-8 custom-row">
-      <p class="key-word mt-3">ROCK CONCERT FESTIVAL</p>
-      <div class="social-icons">
-        <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>
-  </LayoutContainer>
   <LayoutFooterPrefooter />
   <LayoutFooterPrefooter />
+
   <LayoutFooter />
   <LayoutFooter />
 </template>
 </template>
-
-<script setup lang="ts">
-import { ref } from "vue";
-import { useRoute } from "vue-router";
-import "vue3-carousel/dist/carousel.css";
-import { useMaestroRequestService } from "~/composables/useMaestroRequestService";
-const { apiRequestService } = useMaestroRequestService();
-
-const job = ref(null);
-const route = useRoute();
-const jobID = route.params.id;
-const pending = ref(true);
-const config = useRuntimeConfig();
-
-const formatDate = (date: string) => {
-  const dateObject = new Date(date);
-  const day = dateObject.getDate();
-  const month = dateObject.getMonth() + 1;
-  const year = dateObject.getFullYear();
-  return `${day}/${month}/${year}`;
-};
-
-onMounted(async () => {
-  try {
-    job.value = await apiRequestService.get(
-      `${config.public.apiBaseUrl}/api/job-postings/${jobID}`
-    );
-    pending.value = false;
-  } catch (error) {
-    console.error("Erreur lors de la récupération de l'annonce", error);
-    pending.value = false;
-  }
-});
-
-const getImageUrl = (attachment: string) => {
-  if (attachment) {
-    return `${config.public.apiBaseUrl}/uploads/job_postings/${attachment}`;
-  }
-  console.log("pas d'image");
-};
-</script>
-
-<style scoped>
-.description-job {
-  color: #0e2d32;
-  text-align: justify;
-  font-family: "Barlow";
-  font-size: 1.875rem;
-  font-style: normal;
-  font-weight: 500;
-  line-height: 2.125rem;
-}
-
-.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;
-}
-
-.bold {
-  font-weight: bold;
-}
-
-.detail-job {
-  font-family: "Barlow";
-  font-style: normal;
-  font-weight: 500;
-  font-size: 25px;
-  line-height: 18px;
-  margin-bottom: 1rem;
-}
-.blue-content {
-  background-color: #64afb7;
-  height: 10rem;
-  margin-top: -1rem;
-  margin-bottom: 2rem;
-}
-.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;
-  letter-spacing: 1.8px;
-  text-transform: uppercase;
-}
-
-.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>

+ 29 - 0
services/utils/arrayUtils.ts

@@ -0,0 +1,29 @@
+
+export default class ArrayUtils {
+
+  /**
+   * Trie un tableau
+   *
+   * @param array
+   * @param reverse
+   */
+  public static sort(array: Array<any>, reverse: boolean = false): Array<Object> {
+    return array.sort((a, b) => {
+      return ((a < b) ? -1 : (a > b ? 1 : 0)) * (reverse ? -1 : 1)
+    })
+  }
+
+  /**
+   * Trie un tableau d'objets selon une propriété commune
+   *
+   * @param array Une array d'objets
+   * @param property Le nom d'une propriété possédée par tous les objets
+   * @param reverse
+   */
+  public static sortObjectsByProp(array: Array<Object>, property: string, reverse: boolean = false): Array<Object> {
+    return array.sort((a: any, b: any) => {
+      return ((a[property] < b[property]) ? -1 : (a[property] > b[property] ? 1 : 0)) * (reverse ? -1 : 1)
+    })
+  }
+
+}

+ 68 - 0
services/utils/dateUtils.ts

@@ -0,0 +1,68 @@
+import { format } from 'date-fns';
+import ArrayUtils from "~/services/utils/arrayUtils";
+import { enUS, fr } from 'date-fns/locale'
+
+export const enum supportedLocales {
+  FR = 'fr',
+  EN = 'en'
+}
+
+const defaultLocale = 'fr'
+
+
+export default class DateUtils {
+
+  public static format(date: Date, fmt: string): string {
+    return format(date, fmt)
+  }
+
+  /**
+   * Formate la ou les dates au format donné et retourne la liste concaténée
+   *
+   * @param dates
+   * @param fmt
+   * @param sep
+   */
+  public static formatAndConcat (dates: Date | Array<Date>, fmt: string, sep: string = ' - '): string {
+    dates = Array.isArray(dates) ? dates : [dates]
+    return dates.map((d) => this.format(d, fmt)).join(sep)
+  }
+
+  /**
+   * Trie les dates par ordre chronologique
+   *
+   * @param dates
+   * @param reverse
+   */
+  public static sort(dates: Array<Date>, reverse: boolean = false): Array<Date> {
+    return ArrayUtils.sort(dates, reverse) as Array<Date>
+  }
+
+  public static getFnsLocale(code: supportedLocales): Locale {
+    const mapping = {
+      'en': enUS,
+      'fr' : fr
+    }
+    return mapping[code] ?? mapping[defaultLocale]
+  }
+
+  public static getShortFormatPattern(code: supportedLocales): string {
+    const mapping = {
+      'en': 'MM/dd/yyyy',
+      'fr': 'dd/MM/yyyy'
+    }
+    return mapping[code] ?? mapping[defaultLocale]
+  }
+
+  public static getFormatPattern(code: supportedLocales): string {
+    const mapping = {
+      'en': 'MM/dd/yyyy HH:mm',
+      'fr': 'dd/MM/yyyy HH:mm'
+    }
+    return mapping[code] ?? mapping[defaultLocale]
+  }
+
+  public static formatIsoShortDate(date: Date): string {
+    return format(date, 'yyyy-MM-dd')
+  }
+}

+ 7 - 0
types/interface.d.ts

@@ -136,3 +136,10 @@ interface SocietyMember {
   position: string,
   position: string,
   photo: string,
   photo: string,
 }
 }
+
+interface SocialNetworkShareBtn {
+  name: string,
+  icon: string,
+  colorOnHover?: string,
+  url: string
+}

+ 0 - 5
yarn.lock

@@ -11022,11 +11022,6 @@ vue-router@^4.1.6:
   dependencies:
   dependencies:
     "@vue/devtools-api" "^6.4.5"
     "@vue/devtools-api" "^6.4.5"
 
 
-vue-social-sharing@^3.0.9:
-  version "3.0.9"
-  resolved "https://registry.yarnpkg.com/vue-social-sharing/-/vue-social-sharing-3.0.9.tgz#72fda1e343cdb47cf52e3327a84a3612e9f82c11"
-  integrity sha512-Yg4oz5cSkr7ieMAgumwoRLnFhAaoUGCsN8lZE9yUkuQzbKid8yBOn4mLjfx/DA/E8nxYjunAfIUFs7eKIkBjPA==
-
 vue-template-es2015-compiler@^1.6.0:
 vue-template-es2015-compiler@^1.6.0:
   version "1.9.1"
   version "1.9.1"
   resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
   resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"