Merge branch 'front_foundation' of https://gitea.piair.dev/piair/MyINPulse into front_foundation
This commit is contained in:
		| @@ -67,6 +67,16 @@ class Project { | ||||
|     ) { | ||||
|         this._status = value; | ||||
|     } | ||||
|  | ||||
|     toObject() { | ||||
|         return { | ||||
|             idProject: this.idProject, | ||||
|             projectName: this.projectName, | ||||
|             creationDate: this.creationDate, | ||||
|             logo: this.logo, | ||||
|             status: this.status, | ||||
|         }; | ||||
|     } | ||||
| } | ||||
|  | ||||
| export default Project; | ||||
|   | ||||
| @@ -44,7 +44,7 @@ class SectionCell { | ||||
|         this._modificationDate = value; | ||||
|     } | ||||
|  | ||||
|     toPlainObject() { | ||||
|     toObject() { | ||||
|         return { | ||||
|             idSectionCell: this._idSectionCell, | ||||
|             sectionId: this._sectionId, | ||||
|   | ||||
| @@ -3,18 +3,20 @@ | ||||
|         <div class="project-header"> | ||||
|             <h2 @click="goToLink">{{ projectName }}</h2> | ||||
|             <div class="header-actions"> | ||||
|                 <div class="dropdown-wrapper"> | ||||
|                     <!-- Empêche la propagation du clic vers le parent --> | ||||
|                 <div ref="dropdownRef" class="dropdown-wrapper"> | ||||
|                     <button class="contact-button" @click.stop="toggleDropdown"> | ||||
|                         Contact | ||||
|                     </button> | ||||
|  | ||||
|                     <div v-if="isDropdownOpen" class="dropdown-menu"> | ||||
|                         <button @click.stop="contactAll">Contacter tous</button> | ||||
|                     <div | ||||
|                         v-if="entrepreneurEmails.length > 0" | ||||
|                         class="contact-dropdown" | ||||
|                         :class="{ 'dropdown-visible': isDropdownOpen }" | ||||
|                     > | ||||
|                         <button @click="contactAll">Contacter tous</button> | ||||
|                         <button | ||||
|                             v-for="(email, index) in entrepreneurEmails" | ||||
|                             :key="index" | ||||
|                             @click.stop="contactSingle(email)" | ||||
|                             @click="contactSingle(email)" | ||||
|                         > | ||||
|                             {{ email }} | ||||
|                         </button> | ||||
| @@ -37,9 +39,11 @@ | ||||
| <script setup lang="ts"> | ||||
| import { defineProps, ref, onMounted, onBeforeUnmount } from "vue"; | ||||
| import { useRouter } from "vue-router"; | ||||
| import axios from "axios"; | ||||
| import { getProjectEntrepreneurs } from "@/services/Apis/Shared.ts"; | ||||
| import UserEntrepreneur from "@/ApiClasses/UserEntrepreneur.ts"; | ||||
|  | ||||
| const IS_MOCK_MODE = true; | ||||
| const IS_MOCK_MODE = false; | ||||
| const dropdownRef = ref<HTMLElement | null>(null); | ||||
|  | ||||
| const props = defineProps<{ | ||||
|     projectName: string; | ||||
| @@ -52,11 +56,97 @@ const router = useRouter(); | ||||
|  | ||||
| const isDropdownOpen = ref(false); | ||||
| const entrepreneurEmails = ref<string[]>([]); | ||||
| const entrepreneurs = ref<UserEntrepreneur[]>([]); | ||||
|  | ||||
| const goToLink = () => { | ||||
|     if (props.projectLink) { | ||||
|         router.push(props.projectLink); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| const toggleDropdown = () => { | ||||
|     isDropdownOpen.value = !isDropdownOpen.value; | ||||
| }; | ||||
|  | ||||
| const fetchMockEntrepreneurs = () => { | ||||
|     const mockData = [ | ||||
|         { | ||||
|             userName: "Doe", | ||||
|             userSurname: "John", | ||||
|             primaryMail: "john.doe@example.com", | ||||
|         }, | ||||
|         { | ||||
|             userName: "Smith", | ||||
|             userSurname: "Anna", | ||||
|             primaryMail: "anna.smith@example.com", | ||||
|         }, | ||||
|         { | ||||
|             userName: "Mock", | ||||
|             userSurname: "User", | ||||
|             primaryMail: undefined, | ||||
|         }, | ||||
|     ]; | ||||
|  | ||||
|     entrepreneurs.value = mockData.map((item) => new UserEntrepreneur(item)); | ||||
|     entrepreneurEmails.value = entrepreneurs.value | ||||
|         .map((e) => e.primaryMail) | ||||
|         .filter((mail): mail is string => !!mail); | ||||
|  | ||||
|     console.log("Mock entrepreneurs chargés :", entrepreneurs.value); | ||||
| }; | ||||
|  | ||||
| const fetchEntrepreneurs = (projectId: number, useMock = false) => { | ||||
|     if (useMock) { | ||||
|         fetchMockEntrepreneurs(); | ||||
|     } else { | ||||
|         getProjectEntrepreneurs( | ||||
|             projectId, | ||||
|             (response) => { | ||||
|                 const rawData = response.data as Partial<UserEntrepreneur>[]; | ||||
|                 entrepreneurs.value = rawData.map( | ||||
|                     (item) => new UserEntrepreneur(item) | ||||
|                 ); | ||||
|                 entrepreneurEmails.value = entrepreneurs.value | ||||
|                     .map((e) => e.primaryMail) | ||||
|                     .filter((mail): mail is string => !!mail); | ||||
|             }, | ||||
|             (error) => { | ||||
|                 console.error( | ||||
|                     "Erreur lors de la récupération des entrepreneurs :", | ||||
|                     error | ||||
|                 ); | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| const contactAll = () => { | ||||
|     const allEmails = entrepreneurEmails.value.join(", "); | ||||
|     navigator.clipboard | ||||
|         .writeText(allEmails) | ||||
|         .then(() => { | ||||
|             alert("Tous les emails copiés dans le presse-papiers !"); | ||||
|             window.open("https://partage.bordeaux-inp.fr/", "_blank"); | ||||
|         }) | ||||
|         .catch((err) => console.error("Erreur lors de la copie :", err)); | ||||
| }; | ||||
|  | ||||
| const contactSingle = (email: string) => { | ||||
|     navigator.clipboard | ||||
|         .writeText(email) | ||||
|         .then(() => { | ||||
|             alert(`Adresse copiée : ${email}`); | ||||
|             window.open("https://partage.bordeaux-inp.fr/", "_blank"); | ||||
|         }) | ||||
|         .catch((err) => console.error("Erreur lors de la copie :", err)); | ||||
| }; | ||||
|  | ||||
| // Pour fermer le dropdown si on clique ailleurs | ||||
| const handleClickOutside = (event: MouseEvent) => { | ||||
|     const dropdown = document.querySelector(".dropdown-wrapper"); | ||||
|     if (dropdown && !dropdown.contains(event.target as Node)) { | ||||
|     if ( | ||||
|         isDropdownOpen.value && | ||||
|         dropdownRef.value && | ||||
|         !dropdownRef.value.contains(event.target as Node) | ||||
|     ) { | ||||
|         isDropdownOpen.value = false; | ||||
|     } | ||||
| }; | ||||
| @@ -69,98 +159,6 @@ onMounted(() => { | ||||
| onBeforeUnmount(() => { | ||||
|     document.removeEventListener("click", handleClickOutside); | ||||
| }); | ||||
|  | ||||
| const toggleDropdown = () => { | ||||
|     isDropdownOpen.value = !isDropdownOpen.value; | ||||
| }; | ||||
|  | ||||
| const goToLink = () => { | ||||
|     if (props.projectLink) { | ||||
|         router.push(props.projectLink); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| const fetchEntrepreneurs = async ( | ||||
|     projectId: number, | ||||
|     useMock = IS_MOCK_MODE | ||||
| ) => { | ||||
|     try { | ||||
|         const responseData: Entrepreneur[] = useMock | ||||
|             ? await mockFetchEntrepreneurs(/*projectId*/) | ||||
|             : ( | ||||
|                   await axios.get( | ||||
|                       `http://localhost:5000/shared/projects/entrepreneurs/${projectId}` | ||||
|                   ) | ||||
|               ).data; | ||||
|  | ||||
|         entrepreneurEmails.value = responseData.map( | ||||
|             (item: Entrepreneur) => item.primaryMail | ||||
|         ); | ||||
|     } catch (error) { | ||||
|         console.error( | ||||
|             "Erreur lors de la récupération des entrepreneurs :", | ||||
|             error | ||||
|         ); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| type Entrepreneur = { | ||||
|     idUser: number; | ||||
|     userSurname: string; | ||||
|     userName: string; | ||||
|     primaryMail: string; | ||||
|     secondaryMail: string; | ||||
|     phoneNumber: string; | ||||
|     school: string; | ||||
|     course: string; | ||||
|     sneeStatus: boolean; | ||||
| }; | ||||
|  | ||||
| const mockFetchEntrepreneurs = async (/*projectId: number*/) => { | ||||
|     return new Promise<Entrepreneur[]>((resolve) => { | ||||
|         setTimeout(() => { | ||||
|             resolve([ | ||||
|                 { | ||||
|                     idUser: 1, | ||||
|                     userSurname: "Doe", | ||||
|                     userName: "John", | ||||
|                     primaryMail: "john.doe@example.com", | ||||
|                     secondaryMail: "johndoe@backup.com", | ||||
|                     phoneNumber: "612345678", | ||||
|                     school: "ENSEIRB", | ||||
|                     course: "Info", | ||||
|                     sneeStatus: false, | ||||
|                 }, | ||||
|                 { | ||||
|                     idUser: 2, | ||||
|                     userSurname: "Smith", | ||||
|                     userName: "Jane", | ||||
|                     primaryMail: "jane.smith@example.com", | ||||
|                     secondaryMail: "janesmith@backup.com", | ||||
|                     phoneNumber: "698765432", | ||||
|                     school: "ENSEIRB", | ||||
|                     course: "Info", | ||||
|                     sneeStatus: true, | ||||
|                 }, | ||||
|             ]); | ||||
|         }, 500); | ||||
|     }); | ||||
| }; | ||||
|  | ||||
| const contactAll = () => { | ||||
|     const allEmails = entrepreneurEmails.value.join(", "); | ||||
|     navigator.clipboard.writeText(allEmails).then(() => { | ||||
|         alert("Tous les emails copiés dans le presse-papiers !"); | ||||
|         window.open("https://partage.bordeaux-inp.fr/", "_blank"); | ||||
|     }); | ||||
| }; | ||||
|  | ||||
| const contactSingle = (email: string) => { | ||||
|     navigator.clipboard.writeText(email).then(() => { | ||||
|         alert(`Adresse copiée : ${email}`); | ||||
|         window.open("https://partage.bordeaux-inp.fr/", "_blank"); | ||||
|     }); | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
| @@ -233,48 +231,78 @@ const contactSingle = (email: string) => { | ||||
|     line-height: 1.6; | ||||
| } | ||||
|  | ||||
| button { | ||||
|     padding: 10px 15px; | ||||
|     background-color: #007bff; | ||||
|     color: white; | ||||
|     border: none; | ||||
|     cursor: pointer; | ||||
|     border-radius: 5px; | ||||
| } | ||||
| button:hover { | ||||
|     background-color: #0056b3; | ||||
|  | ||||
| .header { | ||||
|     display: flex; | ||||
|     justify-content: space-between; | ||||
|     align-items: center; | ||||
|     padding: 15px 30px; | ||||
|     background-color: #f9f9f9; | ||||
|     box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); | ||||
| } | ||||
|  | ||||
| .dropdown-wrapper { | ||||
| .logo { | ||||
|     height: 50px; | ||||
| } | ||||
|  | ||||
| .header-actions { | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     gap: 20px; | ||||
|     position: relative; | ||||
| } | ||||
|  | ||||
| .dropdown-menu { | ||||
|     position: absolute; | ||||
|     top: 100%; /* juste en dessous du bouton */ | ||||
|     right: 0; | ||||
|     background-color: white; | ||||
|     border: 1px solid #ccc; | ||||
|     padding: 0.5rem; | ||||
|     z-index: 1000; | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     gap: 0.5rem; | ||||
|     box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); | ||||
|     border-radius: 0.25rem; | ||||
|     min-width: 150px; | ||||
| } | ||||
|  | ||||
| .dropdown-menu button { | ||||
|     text-align: left; | ||||
|     padding: 0.3rem 0.5rem; | ||||
|     background: none; | ||||
| .contact-button, | ||||
| .return-button { | ||||
|     background-color: #009cde; | ||||
|     color: white; | ||||
|     border: none; | ||||
|     padding: 10px 15px; | ||||
|     cursor: pointer; | ||||
|     transition: background-color 0.2s; | ||||
|     font-size: 14px; | ||||
|     border-radius: 5px; | ||||
|     text-decoration: none; | ||||
|     transition: background-color 0.2s ease; | ||||
|     font-family: Arial, sans-serif; | ||||
| } | ||||
|  | ||||
| .dropdown-menu button:hover { | ||||
|     background-color: #f0f0f0; | ||||
| .return-button:hover, | ||||
| .contact-button:hover { | ||||
|     background-color: #007bad; | ||||
| } | ||||
| </style> | ||||
|  | ||||
| .contact-dropdown { | ||||
|     position: absolute; | ||||
|     top: 100%; | ||||
|     left: 0; | ||||
|     background-color: #000; | ||||
|     color: white; | ||||
|     box-shadow: 0px 4px 8px rgba(255, 255, 255, 0.2); | ||||
|     border-radius: 8px; | ||||
|     padding: 10px; | ||||
|     margin-top: 5px; | ||||
|     z-index: 1000; | ||||
|     min-width: 200px; | ||||
|     display: none; | ||||
| } | ||||
|  | ||||
| .contact-dropdown button { | ||||
|     display: block; | ||||
|     width: 100%; | ||||
|     padding: 5px; | ||||
|     text-align: left; | ||||
|     border: none; | ||||
|     background: none; | ||||
|     cursor: pointer; | ||||
|     color: white; | ||||
| } | ||||
|  | ||||
| .contact-dropdown button:hover { | ||||
|     background-color: #009cde; | ||||
| } | ||||
|  | ||||
| .contact-dropdown.dropdown-visible { | ||||
|     display: block; | ||||
| } | ||||
|  | ||||
| </style> | ||||
| @@ -35,8 +35,8 @@ | ||||
|                         </button> | ||||
|                     </div> | ||||
|                 </div> | ||||
|                 <RouterLink to="/" class="return-button">Retour</RouterLink> | ||||
|             </div> | ||||
|             <RouterLink to="/" class="return-button">Retour</RouterLink> | ||||
|         </div> | ||||
|     </header> | ||||
| </template> | ||||
|   | ||||
| @@ -9,12 +9,12 @@ import { | ||||
|  | ||||
| // Entrepreneurs API | ||||
| function requestProjectCreation( | ||||
|     projectDetails: Project, // Replace 'any' with a proper type for project details if available | ||||
|     projectDetails: Project, | ||||
|     onSuccessHandler?: (response: AxiosResponse) => void, | ||||
|     onErrorHandler?: (error: AxiosError) => void | ||||
| ): void { | ||||
|     axiosInstance | ||||
|         .post("/entrepreneur/projects/request", projectDetails) | ||||
|         .post("/entrepreneur/projects/request", projectDetails.toObject()) | ||||
|         .then((response) => { | ||||
|             if (onSuccessHandler) { | ||||
|                 onSuccessHandler(response); | ||||
| @@ -32,12 +32,12 @@ function requestProjectCreation( | ||||
| } | ||||
|  | ||||
| function addSectionCell( | ||||
|     sectionCellDetails: SectionCell, // Replace 'any' with a proper type for section cell details if available | ||||
|     sectionCellDetails: SectionCell, | ||||
|     onSuccessHandler?: (response: AxiosResponse) => void, | ||||
|     onErrorHandler?: (error: AxiosError) => void | ||||
| ): void { | ||||
|     axiosInstance | ||||
|         .post("/entrepreneur/sectionCells", sectionCellDetails.toPlainObject()) | ||||
|         .post("/entrepreneur/sectionCells", sectionCellDetails.toObject()) | ||||
|         .then((response) => { | ||||
|             if (onSuccessHandler) { | ||||
|                 onSuccessHandler(response); | ||||
|   | ||||
| @@ -35,6 +35,7 @@ | ||||
| import { ref, onMounted } from "vue"; | ||||
|  | ||||
| import Header from "../components/HeaderComponent.vue"; | ||||
| //import Agenda from "../components/AdminAppointments.vue"; | ||||
| import Agenda from "../components/AgendaComponent.vue"; | ||||
| import ProjectComp from "../components/ProjectComponent.vue"; | ||||
| import PendingProjectComponent from "@/components/PendingProjectComponent.vue"; | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| <template> | ||||
|     <div> | ||||
|         <header> | ||||
|             <HeaderCanvas :project-id="1" :is-admin="isAdmin_" /> | ||||
|             <HeaderCanvas :project-id="101" :is-admin="isAdmin_" /> | ||||
|         </header> | ||||
|     </div> | ||||
|     <div> | ||||
| @@ -10,7 +10,7 @@ | ||||
|             Cliquez sur un champ du tableau pour afficher son contenu en détail | ||||
|             ci-dessous. | ||||
|         </p> | ||||
|         <LeanCanvas :project-id="1" :is-admin="isAdmin_" /> | ||||
|         <LeanCanvas :project-id="101" :is-admin="isAdmin_" /> | ||||
|         <div class="info-box"> | ||||
|             <p v-if="admin"> | ||||
|                 Responsable : | ||||
| @@ -90,7 +90,7 @@ const fetchAdminData = (projectId: number, useMock = IS_MOCK_MODE) => { | ||||
| }; | ||||
|  | ||||
| onMounted(() => { | ||||
|     const projectId = 1; | ||||
|     const projectId = 101; | ||||
|     fetchAdminData(projectId); | ||||
| }); | ||||
| </script> | ||||
|   | ||||
| @@ -40,6 +40,8 @@ | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { ref } from "vue"; | ||||
| import Project from "@/ApiClasses/Project"; | ||||
| import { requestProjectCreation } from "@/services/Apis/Entrepreneurs.ts"; | ||||
|  | ||||
| const choix = ref<string | null>(null); | ||||
| const nomProjet = ref(""); | ||||
| @@ -53,8 +55,36 @@ const validerCreation = () => { | ||||
|         alert("Veuillez entrer un nom de projet."); | ||||
|         return; | ||||
|     } | ||||
|     alert(`Projet "${nomProjet.value}" créé avec succès !`); | ||||
|  | ||||
|     // Obtenir la date actuelle au format YYYY-MM-DD | ||||
|     const today = new Date(); | ||||
|     const yyyy = today.getFullYear(); | ||||
|     const mm = String(today.getMonth() + 1).padStart(2, "0"); | ||||
|     const dd = String(today.getDate()).padStart(2, "0"); | ||||
|     const formattedDate = `${yyyy}-${mm}-${dd}`; | ||||
|  | ||||
|     // Créer une instance de Project | ||||
|     const nouveauProjet = new Project({ | ||||
|         projectName: nomProjet.value.trim(), | ||||
|         creationDate: formattedDate, | ||||
|         status: "PENDING", | ||||
|     }); | ||||
|  | ||||
|     // Appeler l’API | ||||
|     requestProjectCreation( | ||||
|         nouveauProjet, | ||||
|         (response) => { | ||||
|             console.log("Projet créé :", response.data); | ||||
|             alert(`Projet "${nomProjet.value}" créé avec succès !`); | ||||
|         }, | ||||
|         (error) => { | ||||
|             console.error("Erreur lors de la création du projet :", error); | ||||
|             alert("Une erreur est survenue lors de la création du projet."); | ||||
|         } | ||||
|     ); | ||||
| }; | ||||
|  | ||||
|  | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user