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 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
const startEditing = (index: number) => {
isEditing.value[index] = true;
@ -169,7 +149,6 @@ const handleClick = () => {
}
};
// fetchData
const handleFetchSuccess = (sectionCells: SectionCell[]) => {
currentDescriptions.value = sectionCells;
editedDescriptions.value = sectionCells.map(
@ -186,8 +165,20 @@ const handleFetchSuccess = (sectionCells: SectionCell[]) => {
const handleFetchError = (error: unknown) => {
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 (
projectId: number,
title: number,
@ -314,6 +305,26 @@ const mockFetch = async (
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>
<style scoped>

View File

@ -14,22 +14,26 @@
<div class="header-actions">
<button class="return-button" @click="store.logout">Logout</button>
<div ref="dropdownRef" class="dropdown-wrapper">
<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 }}
<div v-if="props.isAdmin">
<div ref="dropdownRef" class="dropdown-wrapper">
<button class="contact-button" @click.stop="toggleDropdown">
Contact
</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>
<RouterLink to="/" class="return-button">Retour</RouterLink>
@ -39,136 +43,106 @@
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from "vue";
import axios from "axios";
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); // ref pour le dropdown
const dropdownRef = ref<HTMLElement | null>(null);
const props = defineProps<{
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 entrepreneurEmails = ref<string[]>([]);
const entrepreneurs = ref<UserEntrepreneur[]>([]);
const IS_MOCK_MODE = false;
const toggleDropdown = () => {
isDropdownOpen.value = !isDropdownOpen.value;
console.log("Dropdown toggled:", isDropdownOpen.value);
};
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;
if (responseData.length > 0) {
entrepreneurEmails.value = responseData.map(
(item: Entrepreneur) => item.primaryMail
);
} else {
console.warn("Aucun entrepreneur trouvé.");
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: null
}
} catch (error) {
console.error(
"Erreur lors de la récupération des entrepreneurs :",
error
];
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;
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 allEmails = entrepreneurEmails.value.join(", ");
navigator.clipboard
.writeText(allEmails)
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);
});
.catch((err) => console.error("Erreur lors de la copie :", err));
};
const contactSingle = (email: string) => {
navigator.clipboard
.writeText(email)
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);
});
.catch((err) => console.error("Erreur lors de la copie :", err));
};
// Cacher le menu si on clique en dehors
const handleClickOutside = (event: MouseEvent) => {
if (
isDropdownOpen.value &&
dropdownRef.value &&
!dropdownRef.value.contains(event.target as Node)
) {
if (isDropdownOpen.value && dropdownRef.value && !dropdownRef.value.contains(event.target as Node)) {
isDropdownOpen.value = false;
}
};
onMounted(() => {
fetchEntrepreneurs(props.projectId, IS_MOCK_MODE);
if (props.isAdmin) {
fetchEntrepreneurs(props.projectId, IS_MOCK_MODE);
}
document.addEventListener("click", handleClickOutside);
});
@ -177,6 +151,7 @@ onBeforeUnmount(() => {
});
</script>
<style scoped>
@import "@/components/canvas/style-project.css";

View File

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

View File

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