feat: all Canvas endpoints implemented, just waiting for projectId to test
Some checks failed
Format / formatting (push) Successful in 6s
Build / build (push) Successful in 42s
CI / build (push) Failing after 9s
Format / formatting (pull_request) Successful in 6s

This commit is contained in:
ALAMI Adnane 2025-04-29 11:24:26 +02:00
parent 351727f3d5
commit e890a03a48
4 changed files with 144 additions and 178 deletions

View File

@ -106,26 +106,6 @@ const currentDescriptions = ref<SectionCell[]>([]);
const editedDescriptions = ref<SectionCell[]>([]); const editedDescriptions = ref<SectionCell[]>([]);
const isEditing = ref<boolean[]>([]); const isEditing = ref<boolean[]>([]);
function getCurrentFormattedDate(): string {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, "0"); // +1 car janvier = 0
const day = String(now.getDate()).padStart(2, "0");
const hours = String(now.getHours()).padStart(2, "0");
const minutes = String(now.getMinutes()).padStart(2, "0");
return `${year}-${month}-${day} ${hours}:${minutes}`;
}
onMounted(() => {
fetchData(
props.projectId,
props.title,
getCurrentFormattedDate(),
IS_MOCK_MODE
);
});
// Fonctions // Fonctions
const startEditing = (index: number) => { const startEditing = (index: number) => {
isEditing.value[index] = true; isEditing.value[index] = true;
@ -169,7 +149,6 @@ const handleClick = () => {
} }
}; };
// fetchData
const handleFetchSuccess = (sectionCells: SectionCell[]) => { const handleFetchSuccess = (sectionCells: SectionCell[]) => {
currentDescriptions.value = sectionCells; currentDescriptions.value = sectionCells;
editedDescriptions.value = sectionCells.map( editedDescriptions.value = sectionCells.map(
@ -186,8 +165,20 @@ const handleFetchSuccess = (sectionCells: SectionCell[]) => {
const handleFetchError = (error: unknown) => { const handleFetchError = (error: unknown) => {
console.error("Erreur lors de la récupération des données :", error); console.error("Erreur lors de la récupération des données :", error);
const errorCell = new SectionCell({
idSectionCell: -1,
sectionId: -1,
contentSectionCell: "Échec du chargement des données.",
modificationDate: new Date().toISOString(),
});
currentDescriptions.value = [errorCell];
editedDescriptions.value = [errorCell];
isEditing.value = [false];
}; };
const fetchData = async ( const fetchData = async (
projectId: number, projectId: number,
title: number, title: number,
@ -314,6 +305,26 @@ const mockFetch = async (
setTimeout(() => resolve(result), 500); setTimeout(() => resolve(result), 500);
}); });
}; };
function getCurrentFormattedDate(): string {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, "0"); // +1 car janvier = 0
const day = String(now.getDate()).padStart(2, "0");
const hours = String(now.getHours()).padStart(2, "0");
const minutes = String(now.getMinutes()).padStart(2, "0");
return `${year}-${month}-${day} ${hours}:${minutes}`;
}
onMounted(() => {
fetchData(
props.projectId,
props.title,
getCurrentFormattedDate(),
IS_MOCK_MODE
);
});
</script> </script>
<style scoped> <style scoped>

View File

@ -14,22 +14,26 @@
<div class="header-actions"> <div class="header-actions">
<button class="return-button" @click="store.logout">Logout</button> <button class="return-button" @click="store.logout">Logout</button>
<div ref="dropdownRef" class="dropdown-wrapper">
<button class="contact-button" @click.stop="toggleDropdown"> <div v-if="props.isAdmin">
Contact <div ref="dropdownRef" class="dropdown-wrapper">
</button> <button class="contact-button" @click.stop="toggleDropdown">
<div Contact
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> </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="contactSingle(email)"
>
{{ email }}
</button>
</div>
</div> </div>
</div> </div>
<RouterLink to="/" class="return-button">Retour</RouterLink> <RouterLink to="/" class="return-button">Retour</RouterLink>
@ -39,136 +43,106 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from "vue"; import { ref, onMounted, onBeforeUnmount } from "vue";
import axios from "axios";
import { store } from "@/main.ts"; import { store } from "@/main.ts";
import { getProjectEntrepreneurs } from "@/services/Apis/Shared.ts";
import UserEntrepreneur from "@/ApiClasses/UserEntrepreneur.ts";
const IS_MOCK_MODE = true; const dropdownRef = ref<HTMLElement | null>(null);
const dropdownRef = ref<HTMLElement | null>(null); // ref pour le dropdown
const props = defineProps<{ const props = defineProps<{
projectId: number; projectId: number;
isAdmin: boolean;
}>(); }>();
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 isDropdownOpen = ref(false);
const entrepreneurEmails = ref<string[]>([]); const entrepreneurEmails = ref<string[]>([]);
const entrepreneurs = ref<UserEntrepreneur[]>([]);
const IS_MOCK_MODE = false;
const toggleDropdown = () => { const toggleDropdown = () => {
isDropdownOpen.value = !isDropdownOpen.value; isDropdownOpen.value = !isDropdownOpen.value;
console.log("Dropdown toggled:", isDropdownOpen.value);
}; };
const fetchEntrepreneurs = async ( const fetchMockEntrepreneurs = () => {
projectId: number, const mockData = [
useMock = IS_MOCK_MODE {
) => { userName: "Doe",
try { userSurname: "John",
const responseData: Entrepreneur[] = useMock primaryMail: "john.doe@example.com"
? await mockFetchEntrepreneurs(projectId) },
: ( {
await axios.get( userName: "Smith",
`http://localhost:5000/shared/projects/entrepreneurs/${projectId}` userSurname: "Anna",
) primaryMail: "anna.smith@example.com"
).data; },
{
if (responseData.length > 0) { userName: "Mock",
entrepreneurEmails.value = responseData.map( userSurname: "User",
(item: Entrepreneur) => item.primaryMail primaryMail: null
);
} else {
console.warn("Aucun entrepreneur trouvé.");
} }
} catch (error) { ];
console.error(
"Erreur lors de la récupération des entrepreneurs :", entrepreneurs.value = mockData.map((item) => new UserEntrepreneur(item));
error 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;
entrepreneurs.value = rawData.map(
(item: any) => new UserEntrepreneur(item)
);
entrepreneurEmails.value = entrepreneurs.value
.map((e) => e.primaryMail)
.filter((mail): mail is string => !!mail); // filtrer undefined
},
(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}`);
return new Promise((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 contactAll = () => {
const allEmails = entrepreneurEmails.value.join(", "); const allEmails = entrepreneurEmails.value.join(", ");
navigator.clipboard navigator.clipboard.writeText(allEmails)
.writeText(allEmails)
.then(() => { .then(() => {
alert("Tous les emails copiés dans le presse-papiers !"); alert("Tous les emails copiés dans le presse-papiers !");
window.open("https://partage.bordeaux-inp.fr/", "_blank"); window.open("https://partage.bordeaux-inp.fr/", "_blank");
}) })
.catch((err) => { .catch((err) => console.error("Erreur lors de la copie :", err));
console.error("Erreur lors de la copie :", err);
});
}; };
const contactSingle = (email: string) => { const contactSingle = (email: string) => {
navigator.clipboard navigator.clipboard.writeText(email)
.writeText(email)
.then(() => { .then(() => {
alert(`Adresse copiée : ${email}`); alert(`Adresse copiée : ${email}`);
window.open("https://partage.bordeaux-inp.fr/", "_blank"); window.open("https://partage.bordeaux-inp.fr/", "_blank");
}) })
.catch((err) => { .catch((err) => console.error("Erreur lors de la copie :", err));
console.error("Erreur lors de la copie :", err);
});
}; };
// Cacher le menu si on clique en dehors
const handleClickOutside = (event: MouseEvent) => { const handleClickOutside = (event: MouseEvent) => {
if ( if (isDropdownOpen.value && dropdownRef.value && !dropdownRef.value.contains(event.target as Node)) {
isDropdownOpen.value &&
dropdownRef.value &&
!dropdownRef.value.contains(event.target as Node)
) {
isDropdownOpen.value = false; isDropdownOpen.value = false;
} }
}; };
onMounted(() => { onMounted(() => {
fetchEntrepreneurs(props.projectId, IS_MOCK_MODE); if (props.isAdmin) {
fetchEntrepreneurs(props.projectId, IS_MOCK_MODE);
}
document.addEventListener("click", handleClickOutside); document.addEventListener("click", handleClickOutside);
}); });
@ -177,6 +151,7 @@ onBeforeUnmount(() => {
}); });
</script> </script>
<style scoped> <style scoped>
@import "@/components/canvas/style-project.css"; @import "@/components/canvas/style-project.css";

View File

@ -6,7 +6,7 @@
:title="item.title" :title="item.title"
:title-text="item.title_text" :title-text="item.title_text"
:description="item.description" :description="item.description"
:project-id="item.projectId" :project-id="props.projectId"
:class="['canvas-item', item.class, 'card', 'shadow', 'p-3']" :class="['canvas-item', item.class, 'card', 'shadow', 'p-3']"
:is-admin="props.isAdmin" :is-admin="props.isAdmin"
/> />
@ -18,68 +18,60 @@ import { ref } from "vue";
import CanvasItem from "@/components/canvas/CanvasItem.vue"; import CanvasItem from "@/components/canvas/CanvasItem.vue";
const props = defineProps<{ const props = defineProps<{
projectId: number;
isAdmin: boolean; isAdmin: boolean;
}>(); }>();
const items = ref([ const items = ref([
{ {
projectId: 1,
title: 1, title: 1,
title_text: "1. Problème", title_text: "1. Problème",
description: "3 problèmes essentiels à résoudre pour le client", description: "3 problèmes essentiels à résoudre pour le client",
class: "Probleme", class: "Probleme",
}, },
{ {
projectId: 1,
title: 2, title: 2,
title_text: "2. Segments", title_text: "2. Segments",
description: "Les segments de clientèle visés", description: "Les segments de clientèle visés",
class: "Segments", class: "Segments",
}, },
{ {
projectId: 1,
title: 3, title: 3,
title_text: "3. Valeur", title_text: "3. Valeur",
description: "La proposition de valeur", description: "La proposition de valeur",
class: "Valeur", class: "Valeur",
}, },
{ {
projectId: 1,
title: 4, title: 4,
title_text: "4. Solution", title_text: "4. Solution",
description: "Les solutions proposées", description: "Les solutions proposées",
class: "Solution", class: "Solution",
}, },
{ {
projectId: 1,
title: 5, title: 5,
title_text: "5. Avantage", title_text: "5. Avantage",
description: "Les avantages concurrentiels", description: "Les avantages concurrentiels",
class: "Avantage", class: "Avantage",
}, },
{ {
projectId: 1,
title: 6, title: 6,
title_text: "6. Canaux", title_text: "6. Canaux",
description: "Les canaux de distribution", description: "Les canaux de distribution",
class: "Canaux", class: "Canaux",
}, },
{ {
projectId: 1,
title: 7, title: 7,
title_text: "7. Indicateurs", title_text: "7. Indicateurs",
description: "Les indicateurs clés de performance", description: "Les indicateurs clés de performance",
class: "Indicateurs", class: "Indicateurs",
}, },
{ {
projectId: 1,
title: 8, title: 8,
title_text: "8. Coûts", title_text: "8. Coûts",
description: "Les coûts associés", description: "Les coûts associés",
class: "Couts", class: "Couts",
}, },
{ {
projectId: 1,
title: 9, title: 9,
title_text: "9. Revenus", title_text: "9. Revenus",
description: "Les sources de revenus", description: "Les sources de revenus",

View File

@ -1,7 +1,10 @@
<template> <template>
<div> <div>
<header> <header>
<HeaderCanvas :project-id="1" /> <HeaderCanvas
:project-id="1"
:is-admin="isAdmin_"
/>
</header> </header>
</div> </div>
<div> <div>
@ -10,22 +13,22 @@
Cliquez sur un champ du tableau pour afficher son contenu en détail Cliquez sur un champ du tableau pour afficher son contenu en détail
ci-dessous. ci-dessous.
</p> </p>
<LeanCanvas :is-admin="isAdmin_" /> <LeanCanvas
:project-id="1"
:is-admin="isAdmin_"
/>
<div class="info-box"> <div class="info-box">
<p> <p v-if="admin">
Responsable : Responsable :
<strong>{{ admin.userName }} {{ admin.userSurname }}</strong <strong>{{ admin.userName }} {{ admin.userSurname }}</strong><br />
><br />
Contact : Contact :
<a href="mailto:{{ admin.primaryMail }}">{{ <a :href="`mailto:${admin.primaryMail}`">{{ admin.primaryMail }}</a>
admin.primaryMail
}}</a>
| |
<a href="tel:{{ admin.phoneNumber }}">{{ <a :href="`tel:${admin.phoneNumber}`">{{ admin.phoneNumber }}</a>
admin.phoneNumber </p>
}}</a> <p v-else>
Chargement des informations du responsable...
</p> </p>
<div class="main"></div>
</div> </div>
</div> </div>
</template> </template>
@ -33,10 +36,11 @@
<script setup lang="ts"> <script setup lang="ts">
import HeaderCanvas from "../components/canvas/HeaderCanvas.vue"; import HeaderCanvas from "../components/canvas/HeaderCanvas.vue";
import LeanCanvas from "../components/canvas/LeanCanvas.vue"; import LeanCanvas from "../components/canvas/LeanCanvas.vue";
import { ref, onMounted /*, defineProps*/ } from "vue"; import { ref, onMounted } from "vue";
import { axiosInstance } from "@/services/api.ts"; import { axiosInstance } from "@/services/api.ts";
import { isAdmin } from "@/services/tools.ts"; import { isAdmin } from "@/services/tools.ts";
const IS_MOCK_MODE = true; import { getProjectAdmin } from "@/services/Apis/Shared.ts";
import UserAdmin from "@/ApiClasses/UserAdmin.ts";
/* /*
const props = defineProps<{ const props = defineProps<{
@ -46,51 +50,35 @@ const props = defineProps<{
const projectId = props.projectId; const projectId = props.projectId;
*/ */
const isAdmin_: boolean = isAdmin(); const IS_MOCK_MODE = false;
const isAdmin_ = isAdmin();
const admin = ref<UserAdmin | null>(null);
// Variables pour les informations de l'administrateur
const admin = ref({
idUser: 0,
userSurname: "",
userName: "",
primaryMail: "",
secondaryMail: "",
phoneNumber: "",
});
const mockAdminData = { const mockAdminData = new UserAdmin({
idUser: 1, idUser: 1,
userSurname: "ALAMI", userSurname: "ALAMI",
userName: "Adnane", userName: "Adnane",
primaryMail: "mock.admin@example.com", primaryMail: "mock.admin@example.com",
secondaryMail: "admin.backup@example.com", secondaryMail: "admin.backup@example.com",
phoneNumber: "0600000000", phoneNumber: "0600000000",
}; });
// Fonction pour récupérer les données de l'administrateur
const fetchAdminData = async (projectId: number, useMock = IS_MOCK_MODE) => { const fetchAdminData = async (projectId: number, useMock = IS_MOCK_MODE) => {
try { if (useMock) {
if (useMock) { console.log("Utilisation des données mockées pour l'administrateur");
console.log( admin.value = mockAdminData;
"Utilisation des données mockées pour l'administrateur" return;
); }
admin.value = mockAdminData;
return;
}
const response = await axiosInstance.get( try {
`/shared/projects/admin/${projectId}` const response = await axiosInstance.get(`/shared/projects/admin/${projectId}`);
); admin.value = new UserAdmin(response.data);
admin.value = response.data;
} catch (error) { } catch (error) {
console.error( console.error("Erreur lors de la récupération des données de l'administrateur :", error);
"Erreur lors de la récupération des données de l'administrateur :",
error
);
} }
}; };
// Appeler la fonction fetch au montage du composant
onMounted(() => { onMounted(() => {
const projectId = 1; const projectId = 1;
fetchAdminData(projectId); fetchAdminData(projectId);