feat: contact fixed

This commit is contained in:
ALAMI Adnane 2025-04-21 19:29:25 +02:00
parent bca88a7b20
commit 4b6d501adc
3 changed files with 163 additions and 112 deletions

View File

@ -1,43 +1,45 @@
<template>
<div class="project" @click="goToLink">
<div class="project-header">
<h2>{{ projectName }}</h2>
<div class="header-actions">
<div class="dropdown-wrapper">
<button class="contact-button" @click="toggleDropdown">
Contact
</button>
<div
class="contact-dropdown"
:class="{ 'dropdown-visible': isDropdownOpen }"
>
<button @click="contactAll">Contacter tous</button>
<button
v-for="(email, index) in entrepreneurEmails"
:key="index"
@click="contactSingle(email)"
>
{{ email }}
</button>
</div>
</div>
</div>
</div>
<div class="project-body">
<ul>
<li v-for="(name, index) in listName" :key="index">
{{ name }}
</li>
</ul>
<div class="project">
<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 -->
<button class="contact-button" @click.stop="toggleDropdown">
Contact
</button>
<div class="dropdown-menu" v-if="isDropdownOpen">
<button @click.stop="contactAll">Contacter tous</button>
<button
v-for="(email, index) in entrepreneurEmails"
:key="index"
@click.stop="contactSingle(email)"
>
{{ email }}
</button>
</div>
</div>
</div>
</div>
<!-- Toute cette partie est cliquable -->
<div class="project-body" @click="goToLink">
<ul>
<li v-for="(name, index) in listName" :key="index">
{{ name }}
</li>
</ul>
</div>
</div>
</template>
<script setup lang="ts">
import { defineProps } from "vue";
import { defineProps, ref, onMounted, onBeforeUnmount } from "vue";
import { useRouter } from "vue-router";
import { ref, onMounted } from "vue";
import axios from "axios";
const IS_MOCK_MODE = true;
const props = defineProps<{
@ -49,32 +51,36 @@ const props = defineProps<{
const router = useRouter();
const isDropdownOpen = ref(false);
const entrepreneurEmails = ref<string[]>([]);
// 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)) {
isDropdownOpen.value = false;
}
};
onMounted(() => {
fetchEntrepreneurs(props.projectId, IS_MOCK_MODE);
document.addEventListener("click", handleClickOutside);
});
onBeforeUnmount(() => {
document.removeEventListener("click", handleClickOutside);
});
const toggleDropdown = () => {
isDropdownOpen.value = !isDropdownOpen.value;
};
const goToLink = () => {
if (props.projectLink) {
router.push(props.projectLink);
}
};
type Entrepreneur = {
idUser: number;
userSurname: string;
userName: string;
primaryMail: string;
secondaryMail: string;
phoneNumber: string;
school: string;
course: string;
sneeStatus: boolean;
};
const isDropdownOpen = ref(false);
const entrepreneurEmails = ref<string[]>([]);
const toggleDropdown = () => {
isDropdownOpen.value = !isDropdownOpen.value;
console.log("Dropdown toggled:", isDropdownOpen.value);
};
const fetchEntrepreneurs = async (
projectId: number,
useMock = IS_MOCK_MODE
@ -88,26 +94,28 @@ const fetchEntrepreneurs = async (
)
).data;
if (responseData.length > 0) {
entrepreneurEmails.value = responseData.map(
(item: Entrepreneur) => item.primaryMail
);
} else {
console.warn("Aucun entrepreneur trouvé.");
}
} catch (error) {
console.error(
"Erreur lors de la récupération des entrepreneurs :",
error
entrepreneurEmails.value = responseData.map(
(item: Entrepreneur) => item.primaryMail
);
} catch (error) {
console.error("Erreur lors de la récupération des entrepreneurs :", error);
}
};
// Fonction de simulation de l'API
const mockFetchEntrepreneurs = async (projectId: number) => {
console.log(`Mock fetch pour projectId: ${projectId}`);
type Entrepreneur = {
idUser: number;
userSurname: string;
userName: string;
primaryMail: string;
secondaryMail: string;
phoneNumber: string;
school: string;
course: string;
sneeStatus: boolean;
};
return new Promise((resolve) => {
const mockFetchEntrepreneurs = async (projectId: number) => {
return new Promise<Entrepreneur[]>((resolve) => {
setTimeout(() => {
resolve([
{
@ -139,30 +147,18 @@ const mockFetchEntrepreneurs = async (projectId: number) => {
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);
});
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");
})
.catch((err) => {
console.error("Erreur lors de la copie :", err);
});
navigator.clipboard.writeText(email).then(() => {
alert(`Adresse copiée : ${email}`);
window.open("https://partage.bordeaux-inp.fr/", "_blank");
});
};
onMounted(() => fetchEntrepreneurs(props.projectId, IS_MOCK_MODE));
</script>
<style scoped>
@ -246,4 +242,37 @@ button {
button:hover {
background-color: #0056b3;
}
.dropdown-wrapper {
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;
border: none;
cursor: pointer;
transition: background-color 0.2s;
}
.dropdown-menu button:hover {
background-color: #f0f0f0;
}
</style>

View File

@ -1,36 +1,38 @@
<template>
<header class="header">
<img src="../icons/logo inpulse.png" alt="INPulse Logo" class="logo" />
<header class="header">
<img src="../icons/logo inpulse.png" alt="INPulse Logo" class="logo" />
<div class="header-actions">
<div class="dropdown-wrapper">
<button class="contact-button" @click="toggleDropdown">
Contact
</button>
<div
class="contact-dropdown"
:class="{ 'dropdown-visible': isDropdownOpen }"
>
<button @click="contactAll">Contacter tous</button>
<button
v-for="(email, index) in entrepreneurEmails"
:key="index"
@click="contactSingle(email)"
>
{{ email }}
</button>
</div>
</div>
<RouterLink to="/" class="return-button">Retour</RouterLink>
<div class="header-actions">
<div class="dropdown-wrapper" ref="dropdownRef">
<button class="contact-button" @click.stop="toggleDropdown">
Contact
</button>
<div
class="contact-dropdown"
:class="{ 'dropdown-visible': isDropdownOpen }"
>
<button @click="contactAll">Contacter tous</button>
<button
v-for="(email, index) in entrepreneurEmails"
:key="index"
@click="contactSingle(email)"
>
{{ email }}
</button>
</div>
</header>
</div>
<RouterLink to="/" class="return-button">Retour</RouterLink>
</div>
</header>
</template>
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { ref, onMounted, onBeforeUnmount } from "vue";
import axios from "axios";
const IS_MOCK_MODE = true;
const dropdownRef = ref<HTMLElement | null>(null); // ref pour le dropdown
const props = defineProps<{
projectId: number;
@ -153,7 +155,27 @@ const copyToClipboard = (email: string) => {
};
*/
onMounted(() => fetchEntrepreneurs(props.projectId, IS_MOCK_MODE));
// Cacher le menu si on clique en dehors
const handleClickOutside = (event: MouseEvent) => {
if (
isDropdownOpen.value &&
dropdownRef.value &&
!dropdownRef.value.contains(event.target as Node)
) {
isDropdownOpen.value = false;
}
};
onMounted(() => {
fetchEntrepreneurs(props.projectId, IS_MOCK_MODE);
document.addEventListener("click", handleClickOutside);
});
onBeforeUnmount(() => {
document.removeEventListener("click", handleClickOutside);
});
</script>
<style scoped>