front_foundation #5
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
|
||||
<div :class="['cell', { expanded }]" @click="handleClick">
|
||||
<h3>{{ title_text }}</h3>
|
||||
<h3 class="fs-5 fw-medium">{{ title_text }}</h3>
|
||||
|
||||
<div class="section-bloc" v-for="(desc, index) in currentDescriptions" :key="index">
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
<template v-if="IS_ADMIN">
|
||||
<div class="description">
|
||||
<p>{{ desc }}</p>
|
||||
<p class="m-0">{{ desc }}</p>
|
||||
</div>
|
||||
mohamed_maoulainine marked this conversation as resolved
Outdated
piair
commented
ce n'est pas de la composition API ce n'est pas de la composition API
|
||||
</template>
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
<!-- Mode affichage -->
|
||||
<template v-if="!isEditing[index]">
|
||||
<div class="description">
|
||||
<p>{{ desc }}</p>
|
||||
<p class="m-0">{{ desc }}</p>
|
||||
</div>
|
||||
<div class="button-container">
|
||||
<button v-if="expanded" @click.stop="startEditing(index)" class="edit-button">Éditer</button>
|
||||
@ -45,8 +45,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, defineProps } from "vue";
|
||||
import { ref, defineProps, onMounted } from "vue";
|
||||
import axios from "axios";
|
||||
import { axiosInstance } from "@/services/api.ts";
|
||||
|
||||
const IS_MOCK_MODE = true;
|
||||
const IS_ADMIN = false;
|
||||
@ -64,8 +65,12 @@ currentDescriptions.value[0] = props.description;
|
||||
const editedDescriptions = ref<string[]>([]);
|
||||
const isEditing = ref<boolean[]>([]);
|
||||
|
||||
onMounted(() => {
|
||||
fetchData(props.projectId, props.title, "NaN", IS_MOCK_MODE);
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
/* FOR LOCAL DATABASE
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const response = await axios.get("http://localhost:5000/data"); // Met à jour l'URL
|
||||
@ -82,6 +87,7 @@ const fetchData = async () => {
|
||||
*/
|
||||
|
||||
// Fonction fetchData avec possibilité d'utiliser le mock
|
||||
/* FOR FETCHING WITH AXIOS DIRECTLY
|
||||
const fetchData = async (projectId: number, title: number, date: string, useMock = false) => {
|
||||
try {
|
||||
const responseData = useMock
|
||||
@ -89,6 +95,27 @@ const fetchData = async (projectId: number, title: number, date: string, useMock
|
||||
: (await axios.get<{ txt: string }[]>(
|
||||
`http://localhost:5000/shared/projects/lcsection/${projectId}/${title}/${date}`
|
||||
)).data;
|
||||
if (responseData.length > 0) {
|
||||
currentDescriptions.value = responseData.map((item) => item.txt);
|
||||
editedDescriptions.value = [...currentDescriptions.value];
|
||||
isEditing.value = Array(responseData.length).fill(false);
|
||||
} else {
|
||||
console.warn("Aucune donnée reçue.");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Erreur lors de la récupération des données :", error);
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
// Fonction fetchData avec possibilité d'utiliser le mock
|
||||
const fetchData = async (projectId: number, title: number, date: string, useMock = false) => {
|
||||
try {
|
||||
const responseData = useMock
|
||||
? await mockFetch(projectId, title, date)
|
||||
: (await axiosInstance.get<{ txt: string }[]>(
|
||||
`/shared/projects/lcsection/${projectId}/${title}/${date}`
|
||||
)).data;
|
||||
|
||||
if (responseData.length > 0) {
|
||||
currentDescriptions.value = responseData.map((item) => item.txt);
|
||||
@ -102,7 +129,6 @@ const fetchData = async (projectId: number, title: number, date: string, useMock
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Fonction de simulation de l'API
|
||||
const mockFetch = async (projectId: number, title: number, date: string) => {
|
||||
console.log(`Mock fetch pour projectId: ${projectId}, title: ${title}, date: ${date}`);
|
||||
@ -110,9 +136,9 @@ const mockFetch = async (projectId: number, title: number, date: string) => {
|
||||
return new Promise<{ txt: string }[]>((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve([
|
||||
{txt: "Ceci est une description 1 pour tester le front. Franchement, si tu es encore en train de lire, c’est probablement que tu veux t'assurer que tout s’affiche bien même avec un texte bien long. Allez, encore quelques mots inutiles pour bien remplir la ligne et tester les retours à la ligne. Bravo champion."},
|
||||
{txt: "Deuxième description : elle est là pour t’aider à tester le comportement du composant avec du texte plus dense. Peut-être même avec quelques caractères spéciaux, tiens : éèàçù$#@! Et si on faisait semblant d’avoir du contenu sérieux ? Non, toujours pas."},
|
||||
{txt: "Troisième description, probablement inutile mais bon, on continue pour la forme. À ce stade, tu testes sûrement si le champ de texte peut supporter un pavé sans exploser. Spoiler alert : si t'as tout bien codé, ça devrait aller. Sinon, bah... débogue. Courage."}
|
||||
{txt: "Ceci est une description 1 pour tester le front."},
|
||||
{txt: "Deuxième description."},
|
||||
{txt: "Troisième description."}
|
||||
]);
|
||||
}, 500); // Simule un délai réseau de 500ms
|
||||
});
|
||||
@ -199,6 +225,7 @@ const cancelEdit = (index: number) => {
|
||||
<style scoped>
|
||||
@import "@/components/canvas/style-project.css";
|
||||
|
||||
|
||||
.cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -210,6 +237,7 @@ const cancelEdit = (index: number) => {
|
||||
box-shadow: 0 4px 5px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
|
||||
.expanded-content {
|
||||
justify-content: flex-start !important;
|
||||
}
|
||||
@ -220,13 +248,13 @@ const cancelEdit = (index: number) => {
|
||||
}
|
||||
|
||||
.cell h3 {
|
||||
font-size: 25px;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
font-family: 'Arial', sans-serif;
|
||||
}
|
||||
|
||||
.p {
|
||||
font-size: 25px;
|
||||
font-size: 10px;
|
||||
color: #666;
|
||||
font-family: 'Arial', sans-serif;
|
||||
}
|
||||
@ -266,6 +294,8 @@ const cancelEdit = (index: number) => {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.edit-input {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@ -287,6 +317,7 @@ const cancelEdit = (index: number) => {
|
||||
padding-right: 1%;
|
||||
}
|
||||
|
||||
|
||||
.section-bloc ,.editing-section-bloc {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
@ -296,6 +327,10 @@ const cancelEdit = (index: number) => {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.edit-button {
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
|
@ -7,9 +7,14 @@
|
||||
<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">
|
||||
<button
|
||||
v-for="(email, index) in entrepreneurEmails"
|
||||
:key="index"
|
||||
@click="copyToClipboard(email)"
|
||||
mohamed_maoulainine marked this conversation as resolved
Outdated
piair
commented
plutôt utiliser des routerLink au lieu de href plutôt utiliser des routerLink au lieu de href
https://stackoverflow.com/questions/52675885/when-to-use-router-link-vs-a
|
||||
>
|
||||
{{ email }}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
mohamed_maoulainine marked this conversation as resolved
Outdated
piair
commented
Ce n'est pas de la composition API, a changer Ce n'est pas de la composition API, a changer
|
||||
@ -30,28 +35,33 @@ const props = defineProps<{
|
||||
}>();
|
||||
|
||||
type Entrepreneur = {
|
||||
mohamed_maoulainine marked this conversation as resolved
Outdated
piair
commented
il faut pas Hardcoder une addresse, elle est dans le fichier .env. Dans tout les cas, cette adresse ne devrait pas être là. il faut pas Hardcoder une addresse, elle est dans le fichier .env. Dans tout les cas, cette adresse ne devrait pas être là.
piair
commented
je viens de voir le message de commit, c'est en effet logique d'utiliser cette donnée. Par contre je suis pas sur que ce soit la bonne méthode pour faire des tests, c'est a discuter. Il serait bien d'appeler mock-data au lieu de fake-data je viens de voir le message de commit, c'est en effet logique d'utiliser cette donnée. Par contre je suis pas sur que ce soit la bonne méthode pour faire des tests, c'est a discuter.
Il serait bien d'appeler mock-data au lieu de fake-data
|
||||
id: number;
|
||||
name: string;
|
||||
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([]);
|
||||
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) => {
|
||||
try {
|
||||
const responseData = useMock
|
||||
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) => item.primaryMail);
|
||||
entrepreneurEmails.value = responseData.map((item: Entrepreneur) => item.primaryMail);
|
||||
} else {
|
||||
console.warn("Aucun entrepreneur trouvé.");
|
||||
}
|
||||
@ -98,6 +108,14 @@ const contactAll = () => {
|
||||
alert("Contacter tous les entrepreneurs : " + entrepreneurEmails.value.join(", "));
|
||||
};
|
||||
|
||||
const copyToClipboard = (email: string) => {
|
||||
navigator.clipboard.writeText(email).then(() => {
|
||||
alert(`Adresse copiée : ${email}`);
|
||||
}).catch(err => {
|
||||
console.error("Erreur lors de la copie :", err);
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => fetchEntrepreneurs(props.projectId, IS_MOCK_MODE));
|
||||
</script>
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="canvas">
|
||||
<div class="canvas container-fluid">
|
||||
<CanvasItem
|
||||
v-for="(item, index) in items"
|
||||
:key="index"
|
||||
@ -7,13 +7,13 @@
|
||||
:title_text="item.title_text"
|
||||
:description="item.description"
|
||||
:projectId="item.projectId"
|
||||
:class="item.class"
|
||||
:class="['canvas-item', item.class, 'card', 'shadow', 'p-3']"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
mohamed_maoulainine marked this conversation as resolved
Outdated
piair
commented
Ce n'est pas de la composition API Ce n'est pas de la composition API
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { ref, onMounted } from "vue";
|
||||
import CanvasItem from "@/components/canvas/CanvasItem.vue";
|
||||
|
||||
const items = ref([
|
||||
@ -28,6 +28,20 @@ const items = ref([
|
||||
{ projectId: 1, title: 9, title_text: "9. Revenus", description: "Les sources de revenus", class: "Revenus" }
|
||||
]);
|
||||
|
||||
onMounted(() => {
|
||||
const bootstrapCss = document.createElement('link')
|
||||
bootstrapCss.rel = 'stylesheet'
|
||||
bootstrapCss.href = 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css'
|
||||
bootstrapCss.integrity = 'sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+Fpc+NC'
|
||||
bootstrapCss.crossOrigin = 'anonymous'
|
||||
document.head.appendChild(bootstrapCss)
|
||||
|
||||
const bootstrapJs = document.createElement('script')
|
||||
bootstrapJs.src = 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js'
|
||||
bootstrapJs.integrity = 'sha384-mQ93S0EhrF4Z1nM+fTflmYf0DyzsY5j7F5H3WlClDD6H3WUJh6kxBkF3GDW8n1j6'
|
||||
bootstrapJs.crossOrigin = 'anonymous'
|
||||
document.body.appendChild(bootstrapJs)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@ -37,23 +51,27 @@ const items = ref([
|
||||
display: grid;
|
||||
grid-template-columns: repeat(10, 1fr);
|
||||
grid-template-rows: repeat(6, 1fr);
|
||||
gap: 10px;
|
||||
padding: 10px;
|
||||
max-width: 1200px;
|
||||
margin: 20px auto;
|
||||
background-color: #fff;
|
||||
gap: 12px;
|
||||
padding: 30px;
|
||||
/*background-color: #f8f9fa;*/
|
||||
position: relative;
|
||||
height: 80vh;
|
||||
height: 90vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.Probleme { grid-column: 1 / 3; grid-row: 1 / 5; padding-top: 20px;}
|
||||
.Segments { grid-column: 9 / 11; grid-row: 1 / 5; padding-top: 20px;}
|
||||
.Valeur { grid-column: 5 / 7; grid-row: 1 / 5; padding-top: 20px;}
|
||||
.Solution { grid-column: 3 / 5; grid-row: 1 / 3; padding-top: 20px;}
|
||||
.Avantage { grid-column: 7 / 9; grid-row: 1 / 3; padding-top: 20px;}
|
||||
.Canaux { grid-column: 7 / 9; grid-row: 3 / 5; padding-top: 20px;}
|
||||
.Indicateurs { grid-column: 3 / 5; grid-row: 3 / 5; padding-top: 20px;}
|
||||
.Couts { grid-column: 1 / 6; grid-row: 5 / 7; padding-top: 20px;}
|
||||
.Revenus { grid-column: 6 / 11; grid-row: 5 / 7; padding-top: 20px;}
|
||||
.Probleme { grid-column: 1 / 3; grid-row: 1 / 5; }
|
||||
.Segments { grid-column: 9 / 11; grid-row: 1 / 5; }
|
||||
.Valeur { grid-column: 5 / 7; grid-row: 1 / 5; }
|
||||
.Solution { grid-column: 3 / 5; grid-row: 1 / 3; }
|
||||
.Avantage { grid-column: 7 / 9; grid-row: 1 / 3; }
|
||||
.Canaux { grid-column: 7 / 9; grid-row: 3 / 5; }
|
||||
.Indicateurs { grid-column: 3 / 5; grid-row: 3 / 5; }
|
||||
.Couts { grid-column: 1 / 6; grid-row: 5 / 7; }
|
||||
.Revenus { grid-column: 6 / 11; grid-row: 5 / 7; }
|
||||
|
||||
.canvas-item {
|
||||
/*background-color: white;*/
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
@ -89,4 +89,4 @@ function deleteApi(
|
||||
}
|
||||
|
||||
|
||||
export { callApi, postApi, deleteApi };
|
||||
export { axiosInstance, callApi, postApi, deleteApi };
|
||||
|
@ -1,12 +1,24 @@
|
||||
<template>
|
||||
<div>
|
||||
<header>
|
||||
<HeaderCanvas />
|
||||
<HeaderCanvas :projectId="1" />
|
||||
</header>
|
||||
</div>
|
||||
<div>
|
||||
<h1>Page Canvas</h1>
|
||||
<h1 class="page-title">PAGE CANVAS</h1>
|
||||
|
||||
<p class="canvas-help-text">
|
||||
Cliquez sur un champ du tableau pour afficher son contenu en détail ci-dessous.
|
||||
</p>
|
||||
<LeanCanvas />
|
||||
|
||||
<div class="info-box">
|
||||
<p>
|
||||
Responsable : <strong>{{ admin.userName }} {{ admin.userSurname }}</strong><br />
|
||||
Contact : <a href="mailto:{{ admin.primaryMail }}">{{ admin.primaryMail }}</a> |
|
||||
<a href="tel:{{ admin.phoneNumber }}">{{ admin.phoneNumber }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -14,5 +26,89 @@
|
||||
// @ts-ignore
|
||||
import HeaderCanvas from "../components/canvas/HeaderCanvas.vue";
|
||||
import LeanCanvas from '../components/canvas/LeanCanvas.vue';
|
||||
import { ref, onMounted } from "vue";
|
||||
import { axiosInstance } from "@/services/api.ts";
|
||||
|
||||
const IS_MOCK_MODE = true;
|
||||
|
||||
// Variables pour les informations de l'administrateur
|
||||
const admin = ref({
|
||||
idUser: 0,
|
||||
userSurname: "",
|
||||
userName: "",
|
||||
primaryMail: "",
|
||||
secondaryMail: "",
|
||||
phoneNumber: ""
|
||||
});
|
||||
|
||||
const mockAdminData = {
|
||||
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;
|
||||
}
|
||||
|
||||
const response = await axiosInstance.get(`/shared/projects/admin/${projectId}`);
|
||||
admin.value = response.data;
|
||||
} catch (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);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-title {
|
||||
text-align: center;
|
||||
font-size: 2.5rem;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.canvas-help-text {
|
||||
text-align: center;
|
||||
font-size: 0.7rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.info-box {
|
||||
background-color: #f9f9f9;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
font-family: Arial, sans-serif;
|
||||
width: 30%;
|
||||
max-width: 600px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
.info-box p {
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.info-box a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.info-box a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
Loading…
x
Reference in New Issue
Block a user
Il serait mieux d'afficher le contenu du lean canvas sans devoir clicker dessus, un peu comme sur ce lien.
De plus, ce n'est pas important d'afficher en gros le titre de la section, il devrait être plus discret.
https://cms.boardmix.com/images/articles/lean-canvas.png
En plus il y a des jolies couleur c'est sympa.