Просмотр исходного кода

refactor the joinus/index page

Olivier Massot 2 лет назад
Родитель
Сommit
99c08ee645

+ 25 - 29
components/JoinUs/Banner.vue

@@ -1,56 +1,52 @@
 <template>
 <template>
   <LayoutContainer>
   <LayoutContainer>
-    <LayoutUITitlePage title="Nous rejoindre ?" />
+    <LayoutUITitlePage>
+      Nous rejoindre
+    </LayoutUITitlePage>
 
 
     <v-row>
     <v-row>
       <v-col cols="12">
       <v-col cols="12">
         <CommonBanner
         <CommonBanner
-          :imageSrc="'/images/join/join.jpg'"
-          imageAlt="'line'"
+          imageSrc="/images/join/join.jpg"
+          imageAlt="banner"
         />
         />
       </v-col>
       </v-col>
     </v-row>
     </v-row>
 
 
-
-    <h3 class="text-center join-title bold mt-6">
-      Opentalent, où l'innovation & la passion se rencontre !
+    <h3 id="join-us-anchor" class="mt-6">
+      Opentalent, où l'innovation & la passion se rencontrent !
     </h3>
     </h3>
-    <h4 class="text-center join-title">
+
+    <span class="subtitle">
       Découvrez nos opportunités et rejoignez-nous dans cette aventure
       Découvrez nos opportunités et rejoignez-nous dans cette aventure
       passionnante.
       passionnante.
-    </h4>
+    </span>
   </LayoutContainer>
   </LayoutContainer>
 </template>
 </template>
 
 
-<script setup></script>
+<script setup>
+</script>
 
 
 <style scoped>
 <style scoped>
 
 
-:deep().cover-image {
-  object-position: center 50% !important;
-}
-:deep().text-left,
-:deep().text-right,
-:deep().description,
-:deep().details-square,
-:deep().logo-square {
-  display: none;
-}
-:deep().text-right {
-  display: none;
+h3, .subtitle {
+  font-family: "Barlow", serif;
+  line-height: 2.5rem;
+  text-align: center;
 }
 }
 
 
-.bold {
-  font-weight: 600 !important;
-}
-.join-title {
-  font-family: "Barlow";
+
+h3 {
   font-style: normal;
   font-style: normal;
-  font-weight: 300;
+  font-weight: 600;
   font-size: 1.5rem;
   font-size: 1.5rem;
-  line-height: 2.5rem;
-  text-align: center;
 }
 }
 
 
+.subtitle {
+  display: block;
+  font-weight: 300;
+  font-size: 1.5rem;
+  width: 100%;
+}
 
 
 </style>
 </style>

+ 224 - 139
components/JoinUs/Missions.vue

@@ -1,42 +1,64 @@
 <template>
 <template>
   <LayoutContainer>
   <LayoutContainer>
-    <div v-for="(job, index) in jobs" :key="index" class="mission-container">
-      <!-- Première ligne avec alignement vertical -->
-      <v-row class="announcement-title ml-6 mr-6 d-flex align-center">
+    <div v-if="pending">
+      <v-row class="justify-center progress">
+        <v-progress-circular
+          indeterminate
+          color="grey"
+        />
+      </v-row>
+    </div>
+
+    <div
+      v-else
+      v-for="(job, index) in jobs"
+      :key="index"
+      class="mission-container"
+    >
+      <v-row class="title-container">
         <NuxtLink
         <NuxtLink
           :to="`/nous-rejoindre/${job.id}`"
           :to="`/nous-rejoindre/${job.id}`"
-          class="text-decoration-none"
+          class="title"
         >
         >
-          <div class="title-job ml-4">
-            <!-- Icône étoile pour les annonces 'featured' -->
-            {{ job.title }} - {{ job.contractType }}
-            <v-icon
-              v-if="job.featured"
-              class="fas fa-star"
-              style="color: yellow"
-            ></v-icon>
-          </div>
+          {{ job.title }} - {{ job.contractType }}
+
+          <v-icon
+            v-if="job.featured"
+            class="star fas fa-star"
+          />
         </NuxtLink>
         </NuxtLink>
-        <NuxtLink
+
+        <v-btn
           :to="`/nous-rejoindre/${job.id}`"
           :to="`/nous-rejoindre/${job.id}`"
-          class="text-decoration-none d-flex align-center"
+          class="btn-more"
         >
         >
-          <v-icon class="mr-2"> </v-icon>
-          <v-btn class="btn-more mr-4" text> En savoir plus </v-btn>
-        </NuxtLink>
+          En savoir plus
+        </v-btn>
       </v-row>
       </v-row>
 
 
-      <v-row class="announcement-location ml-6 mr-6">
-        <v-icon class="icon-location">
-          <i class="fas fa-map-marker"></i>
-        </v-icon>
+      <v-row class="location-container">
+        <v-icon icon="fas fa-map-marker" />
         <div class="location">
         <div class="location">
           {{ job.city }}
           {{ job.city }}
         </div>
         </div>
       </v-row>
       </v-row>
     </div>
     </div>
 
 
-    <v-row class="apply-row ml-6 mb-6">
+    <v-row>
+      <v-col cols="12">
+        <v-pagination
+          v-if="lastPage"
+          :model-value="page"
+          :length="lastPage"
+          rounded="circle"
+          max="10"
+          class="pagination"
+          @update:model-value="pageUpdated"
+        />
+      </v-col>
+    </v-row>
+
+    <v-row class="ml-6 mb-6">
       <v-col cols="12">
       <v-col cols="12">
         <p class="apply-now">
         <p class="apply-now">
           Nous sommes toujours à la recherche de nouveaux talents. N'hésitez pas
           Nous sommes toujours à la recherche de nouveaux talents. N'hésitez pas
@@ -44,40 +66,81 @@
         </p>
         </p>
       </v-col>
       </v-col>
     </v-row>
     </v-row>
+
     <v-row>
     <v-row>
       <v-col cols="12">
       <v-col cols="12">
-        <v-btn class="btn-send" @click="dialog = true">
+        <v-btn
+          class="btn-send"
+          @click="dialog = true"
+        >
           Envoyer ma candidature
           Envoyer ma candidature
         </v-btn>
         </v-btn>
       </v-col>
       </v-col>
     </v-row>
     </v-row>
 
 
-    <v-dialog v-model="dialog" max-width="600px">
+    <!-- Boite de dialogue "soumettre une candidature" -->
+    <v-dialog
+      v-model="dialog"
+      max-width="600px"
+    >
       <v-card>
       <v-card>
-        <v-card-title class="text-center" style="font-family: Barlow">
+        <v-card-title
+          class="text-center"
+        >
           Formulaire de Candidature
           Formulaire de Candidature
         </v-card-title>
         </v-card-title>
+
         <v-card-text>
         <v-card-text>
           <v-form>
           <v-form>
-            <v-text-field label="Nom*" required></v-text-field>
-            <v-text-field label="Prénom*" required></v-text-field>
-            <v-text-field label="Téléphone*" required></v-text-field>
-            <v-text-field label="Email*" required></v-text-field>
+            <v-text-field
+              label="Nom*"
+              required
+            />
+
+            <v-text-field
+              label="Prénom*"
+              required
+            />
+
+            <v-text-field
+              label="Téléphone*"
+              required
+            />
+
+            <v-text-field
+              label="Email*"
+              required
+            />
+
             <v-file-input
             <v-file-input
               label="Dépôt de CV*"
               label="Dépôt de CV*"
               accept=".pdf, .jpeg, .png"
               accept=".pdf, .jpeg, .png"
               required
               required
-            ></v-file-input>
+            />
+
             <v-file-input
             <v-file-input
               label="Dépôt de lettre de motivation"
               label="Dépôt de lettre de motivation"
               accept=".pdf, .jpeg, .png"
               accept=".pdf, .jpeg, .png"
-            ></v-file-input>
-            <v-textarea label="Message*" required></v-textarea>
+            />
+
+            <v-textarea
+              label="Message*"
+              required
+            />
           </v-form>
           </v-form>
         </v-card-text>
         </v-card-text>
-        <p class="text-right mr-6">* Champs obligatoires</p>
-        <v-card-actions>
-          <v-btn class="btn-more" @click="sendApplication">Envoyer</v-btn>
+
+        <p class="text-right mr-6">
+          * Champs obligatoires
+        </p>
+
+        <v-card-actions class="justify-center">
+          <v-btn
+            class="btn-more mb-4"
+            @click="sendApplication"
+          >
+            Envoyer
+          </v-btn>
         </v-card-actions>
         </v-card-actions>
       </v-card>
       </v-card>
     </v-dialog>
     </v-dialog>
@@ -85,106 +148,142 @@
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import { ref } from "vue";
-import { useMaestroRequestService } from "~/composables/useMaestroRequestService";
-const { apiRequestService } = useMaestroRequestService();
+import { useEntityFetch } from "~/composables/data/useEntityFetch";
+import JobPosting from "~/models/Maestro/JobPosting";
+import { ComputedRef } from "vue";
+import { AnyJson } from "~/types/data";
 
 
-const dialog = ref(false);
+const i18n = useI18n();
+const router = useRouter()
+const { fetchCollection } = useEntityFetch()
 
 
-const sendApplication = () => {
-  console.log("Formulaire envoyé");
-  dialog.value = false;
-};
+const page: Ref<number> = ref(1);
 
 
-const query = computed(() => {
-  const queryParams: {
-    page: number;
-    type?: string;
-    [key: string]: number | string;
-  } = {
-    page: page.value,
-    type: "ENTREPRISE",
-  };
+const query: ComputedRef<AnyJson> = computed(() => {
+    return { type: "ENTREPRISE", page: page.value }
+  }
+)
 
 
-  return queryParams;
-});
+const { data: jobCollection, pending, refresh } = fetchCollection(
+  JobPosting,
+  null,
+  query
+)
 
 
-const totalItems = ref(0);
-const config = useRuntimeConfig();
-const baseUrl = `${config.public.apiBaseUrl}/api/job-postings`;
+const jobs: ComputedRef<JobPosting[]> = computed(() => {
+  return jobCollection.value !== null ?
+         jobCollection.value.items as JobPosting[] :
+         []
+})
 
 
-const page: Ref<number> = ref(1);
-const itemsPerPage: Ref<number> = ref(10);
-const { t } = useI18n();
-
-const {
-  data: jobs = [],
-  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 annonces 'featured'
-  const featuredJobs = collection.filter(
-    (item) => item.featured && new Date(item.startPublication) <= currentDate
-  );
-
-  // Filtrer et trier les autres annonces
-  const regularJobs = collection.filter((item) => {
-    const startPublicationDate = new Date(item.startPublication);
-    const endPublicationDate = new Date(item.endPublication);
-    return (
-      !item.featured &&
-      startPublicationDate <= currentDate &&
-      endPublicationDate >= currentDate
-    );
-  });
-
-  // Combinez les annonces 'featured' et les autres annonces
-  const combinedJobs = [...featuredJobs, ...regularJobs];
-
-  totalItems.value = combinedJobs.length;
-  console.log(combinedJobs);
-  return combinedJobs;
-});
-</script>
+const lastPage: ComputedRef<number | null> = computed(() => {
+  return jobCollection.value !== null && jobCollection.value.pagination ?
+    jobCollection.value.pagination.last ?? null :
+    null
+})
 
 
-<style scoped>
-<style scoped>
-.location {
-  font-size: 1.3rem !important;
-  color: #0e2d32;
-}
-.announcement-title {
-  background: #0e2d32;
-  color: white;
-  display: flex;
-  justify-content: space-between;
-  height: 80px;
-  padding: 10px 10px 10px 1;
+const pageUpdated = async (newVal: number): Promise<void> => {
+  page.value = newVal
+
+  pending.value = true
+  await navigateTo({ path: '', hash: '#join-us-anchor' })
+
+  setTimeout(
+    async () => await refresh(),
+    100
+  )
 }
 }
 
 
-.title-job {
-  font-family: "Barlow";
-  font-style: normal;
-  font-weight: 600;
-  font-size: 1.5rem;
-  line-height: 39px;
-  color: #ffffff;
+/**
+ * Faut-il afficher la boite de dialogue de candidature
+ */
+const dialog = ref(false);
+
+/**
+ * Soumet le formulaire de candidature (boite de dialogue)
+ */
+const sendApplication = () => {
+  // TODO: implémenter le submit
+  dialog.value = false;
+};
+</script>
+
+<style scoped lang="scss">
+.progress {
+  margin: 32px auto 128px auto;
 }
 }
 
 
-.btn-more,
-.btn-send {
+.v-btn {
   font-weight: 600;
   font-weight: 600;
   height: 50px;
   height: 50px;
-  background: #64afb7;
+  background: var(--secondary-color);
   border-radius: 6px;
   border-radius: 6px;
-  color: white;
+  color: var(--on-secondary-color);
   gap: 9px;
   gap: 9px;
 }
 }
 
 
+.mission-container {
+  margin: 64px 12%;
+
+  .title-container {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    background: var(--primary-color);
+    color: var(--on-primary-color);
+    height: 80px;
+    padding: 10px 10px 10px 1px;
+
+    .title {
+      font-family: "Barlow",serif;
+      font-style: normal;
+      font-weight: 600;
+      font-size: 1.5rem;
+      line-height: 39px;
+      color: #ffffff;
+      text-decoration: none;
+      margin-left: 36px;
+    }
+
+    .star {
+      margin-left: 12px;
+      font-size: 24px;
+      color: yellow;
+      vertical-align: baseline;
+    }
+
+    .btn-more {
+      margin-right: 8px;
+      display: flex;
+      align-items: center;
+    }
+  }
+
+  .location-container {
+    background: #9edbdd;
+    display: flex;
+    align-items: center;
+    padding: 10px;
+
+    .v-icon {
+      font-size: 1rem !important;
+      color: #0e2d32;
+    }
+
+    .location {
+      color: var(--primary-color);
+      margin-left: 10px;
+      font-size: 1.3rem;
+    }
+  }
+}
+
+@media (max-width: 600px) {
+  .mission-container {
+    margin: 64px 6%;
+  }
+}
+
 .btn-send {
 .btn-send {
   display: flex;
   display: flex;
   justify-content: center;
   justify-content: center;
@@ -195,23 +294,6 @@ const {
   font-weight: 700;
   font-weight: 700;
 }
 }
 
 
-.announcement-location {
-  background: #9edbdd;
-  display: flex;
-  align-items: center;
-  padding: 10px;
-}
-
-.icon-location {
-  font-size: 1rem !important;
-  color: #0e2d32;
-}
-
-.location {
-  margin-left: 10px;
-  font-size: 2rem;
-}
-
 .apply-now {
 .apply-now {
   text-align: center;
   text-align: center;
   font-style: italic;
   font-style: italic;
@@ -222,8 +304,11 @@ const {
   margin-bottom: 2rem;
   margin-bottom: 2rem;
 }
 }
 
 
-.mission-container {
-  margin-top: 3rem;
-  margin-bottom: 5%;
+.v-dialog {
+  font-family: Barlow, serif;
+
+  .btn-more {
+    width: 128px;
+  }
 }
 }
 </style>
 </style>

+ 0 - 65
pages/nous-rejoindre/index.vue

@@ -9,68 +9,3 @@
 
 
   <LayoutFooter />
   <LayoutFooter />
 </template>
 </template>
-
-<script setup lang="ts">
-import { ref } from "vue";
-import { useMaestroRequestService } from "~/composables/useMaestroRequestService";
-
-const { apiRequestService } = useMaestroRequestService();
-
-const query = computed(() => {
-  const queryParams: { page: number; type?: string; [key: string]: number | string } = {
-    page: page.value,
-    type: 'ENTREPRISE'
-  };
-
-  return queryParams;
-});
-
-// Base URL for API requests
-const totalItems = ref(0);
-const config = useRuntimeConfig();
-const baseUrl = `${config.public.apiBaseUrl}/api/job-postings`;
-
-const page: Ref<number> = ref(1);
-const itemsPerPage: Ref<number> = ref(10);
-const { t } = useI18n();
-
-
-const {
-  data: jobs = [],
-  pending,
-  refresh,
-} = useLazyAsyncData("files", async () => {
-  const response = await apiRequestService.get(baseUrl, query.value);
-  const collection = response["hydra:member"];
-
-  const currentDate = new Date();
-
-  const filteredJobs = collection.filter(item => {
-    const endPublicationDate = new Date(item.endPublication);
-    return item.type === 'ENTREPRISE' && endPublicationDate >= currentDate;
-  });
-
-  totalItems.value = filteredJobs.length;
-  console.log(filteredJobs);
-  return filteredJobs;
-});
-
-
-</script>
-
-<style scoped lang="scss">
-#sticky-menu {
-  position: sticky;
-  top: 20rem;
-  z-index: 1;
-  margin-bottom: -32rem;
-  float: right;
-}
-
-@media (max-width: 1800px) {
-  #sticky-menu {
-    top: 16rem;
-    margin-bottom: -28rem;
-  }
-}
-</style>

+ 10 - 0
stores/repositories/BaseRepository.ts

@@ -0,0 +1,10 @@
+import {Repository} from "pinia-orm";
+
+
+abstract class BaseRepository extends Repository {
+  public getQuery() {
+    return this.where((val) => Number.isInteger(val.id) ) // exclude _clone_ ids
+  }
+}
+
+export default BaseRepository

+ 16 - 0
stores/repositories/JobPostingRepository.ts

@@ -0,0 +1,16 @@
+import type { Collection } from 'pinia-orm'
+import BaseRepository from '~/stores/repositories/BaseRepository'
+import JobPosting from "~/models/Maestro/JobPosting";
+
+class JobPostingRepository extends BaseRepository {
+  use = JobPosting
+
+  /**
+   * On récupère les RésidenceArea via le store
+   */
+  public getJobPostings(): Collection<JobPosting> {
+    return this.getQuery().get() as Collection<JobPosting>
+  }
+}
+
+export default JobPostingRepository