front_foundation #5
@ -5,6 +5,59 @@
|
|||||||
{ "id": 3, "name": "Charlie", "email": "charlie@example.com" }
|
{ "id": 3, "name": "Charlie", "email": "charlie@example.com" }
|
||||||
],
|
],
|
||||||
"data": [
|
"data": [
|
||||||
{ "canva_data": "this is a fake data to test api" }
|
{
|
||||||
|
"projectId": 1,
|
||||||
|
"title": 1,
|
||||||
|
"title_text": "1. Problème",
|
||||||
|
"description": "3 problèmes essentiels à résoudre pour le client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"projectId": 1,
|
||||||
|
"title": 2,
|
||||||
|
"title_text": "2. Segments",
|
||||||
|
"description": "Les segments de clientèle visés"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"projectId": 1,
|
||||||
|
"title": 3,
|
||||||
|
"title_text": "3. Valeur",
|
||||||
|
"description": "La proposition de valeur"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"projectId": 1,
|
||||||
|
"title": 4,
|
||||||
|
"title_text": "4. Solution",
|
||||||
|
"description": "Les solutions proposées"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"projectId": 1,
|
||||||
|
"title": 5,
|
||||||
|
"title_text": "5. Avantage",
|
||||||
|
"description": "Les avantages concurrentiels"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"projectId": 1,
|
||||||
|
"title": 6,
|
||||||
|
"title_text": "6. Canaux",
|
||||||
|
"description": "Les canaux de distribution"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"projectId": 1,
|
||||||
|
"title": 7,
|
||||||
|
"title_text": "7. Indicateurs",
|
||||||
|
"description": "Les indicateurs clés de performance"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"projectId": 1,
|
||||||
|
"title": 8,
|
||||||
|
"title_text": "8. Coûts",
|
||||||
|
"description": "Les coûts associés"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"projectId": 1,
|
||||||
|
"title": 9,
|
||||||
|
"title_text": "9. Revenus",
|
||||||
|
"description": "Les sources de revenus"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
173
front/MyINPulse-front/src/components/canvas/CanvasItem.vue
Normal file → Executable file
173
front/MyINPulse-front/src/components/canvas/CanvasItem.vue
Normal file → Executable file
@ -1,21 +1,28 @@
|
|||||||
<template>
|
<template>
|
||||||
|
|||||||
<div :class="['cell', { expanded }]" @click="handleClick">
|
<div :class="['cell', { expanded }]" @click="handleClick">
|
||||||
<h3>{{ title }}</h3>
|
<h3>{{ title_text }}</h3>
|
||||||
|
|
||||||
|
<div v-for="(desc, index) in currentDescriptions" :key="index">
|
||||||
<!-- Mode affichage -->
|
<!-- Mode affichage -->
|
||||||
<template v-if="!isEditing">
|
<template v-if="!isEditing[index]">
|
||||||
<p>{{ currentDescription }}</p>
|
<div class="description">
|
||||||
<button v-if="expanded" @click.stop="startEditing" class="edit-button">Éditer</button>
|
<p>{{ desc }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="button-container">
|
||||||
|
<button v-if="expanded" @click.stop="startEditing(index)" class="edit-button">Éditer</button>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- Mode édition -->
|
<!-- Mode édition -->
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<textarea v-model="editedDescription" class="edit-input"></textarea>
|
<textarea v-model="editedDescriptions[index]" class="edit-input"></textarea>
|
||||||
<div class="button-container">
|
<div class="button-container">
|
||||||
<button @click.stop="saveEdit" class="save-button">Enregistrer</button>
|
<button @click.stop="saveEdit(index)" class="save-button">Enregistrer</button>
|
||||||
<button @click.stop="cancelEdit" class="cancel-button">Annuler</button>
|
<button @click.stop="cancelEdit(index)" class="cancel-button">Annuler</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -23,16 +30,22 @@
|
|||||||
import { ref, defineProps } from "vue";
|
import { ref, defineProps } from "vue";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
|
const IS_MOCK_MODE = true;
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
title: string;
|
projectId: number;
|
||||||
|
title: number;
|
||||||
|
title_text: string;
|
||||||
description: string;
|
description: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const expanded = ref(false);
|
const expanded = ref(false);
|
||||||
const isEditing = ref(false);
|
const currentDescriptions = ref<string[]>([]);
|
||||||
const currentDescription = ref(props.description);
|
const editedDescriptions = ref<string[]>([]);
|
||||||
const editedDescription = ref(props.description);
|
const isEditing = ref<boolean[]>([]);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get("http://localhost:5000/data"); // Met à jour l'URL
|
const response = await axios.get("http://localhost:5000/data"); // Met à jour l'URL
|
||||||
@ -46,32 +59,121 @@ const fetchData = async () => {
|
|||||||
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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 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}`);
|
||||||
|
|
||||||
|
return new Promise<{ txt: string }[]>((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve([
|
||||||
|
{ txt: "Ceci est une description 1 pour tester the damn front, Why are u still reading dumbass this is just some nonsense sentence XD" },
|
||||||
|
{ txt: "Ceci est une description 1 pour tester the damn front, Bruh are u still here?" },
|
||||||
|
{ txt: "Ceci est une description 1 pour tester the damn front, .-. BRUH" }
|
||||||
|
]);
|
||||||
|
}, 500); // Simule un délai réseau de 500ms
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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 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]; // Copie initiale
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Utilisation du mock dans handleClick pour tester sans serveur
|
||||||
const handleClick = async () => {
|
const handleClick = async () => {
|
||||||
if (!expanded.value) {
|
if (!expanded.value) {
|
||||||
await fetchData();
|
await fetchData(props.projectId, props.title, "NaN", IS_MOCK_MODE); // true pour activer le mock
|
||||||
} else if (!isEditing.value) {
|
} else if (!isEditing.value.includes(true)) {
|
||||||
currentDescription.value = props.description;
|
// Réinitialiser les descriptions si aucune édition n'est en cours
|
||||||
editedDescription.value = props.description;
|
currentDescriptions.value = [props.description];
|
||||||
|
editedDescriptions.value = [props.description];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isEditing.value) {
|
if (!isEditing.value.includes(true)) {
|
||||||
expanded.value = !expanded.value;
|
expanded.value = !expanded.value;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const startEditing = () => {
|
|
||||||
isEditing.value = true;
|
const startEditing = (index: number) => {
|
||||||
|
isEditing.value[index] = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveEdit = () => {
|
/*
|
||||||
currentDescription.value = editedDescription.value;
|
const saveEdit = async (index: number) => {
|
||||||
isEditing.value = false;
|
try {
|
||||||
|
const id = index + 1; // À adapter selon l'ID réel des données
|
||||||
|
await axios.put(`http://localhost:5000/data/${id}`, {
|
||||||
|
canva_data: editedDescriptions.value[index]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mettre à jour l'affichage local après la mise à jour réussie
|
||||||
|
currentDescriptions.value[index] = editedDescriptions.value[index];
|
||||||
|
isEditing.value[index] = false;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Erreur lors de la mise à jour des données :", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Fonction de mock pour l'enregistrement
|
||||||
|
const mockSaveEdit = async (index: number) => {
|
||||||
|
try {
|
||||||
|
const id = index + 1; // À adapter selon l'ID réel des données
|
||||||
|
console.log(`Mock save pour l'ID ${id} avec la description : ${editedDescriptions.value[index]}`);
|
||||||
|
|
||||||
|
// Simuler un délai d'enregistrement comme une requête réseau
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 500)); // Simulation de délai réseau
|
||||||
|
|
||||||
|
// Mettre à jour l'affichage local après la mise à jour réussie
|
||||||
|
currentDescriptions.value[index] = editedDescriptions.value[index];
|
||||||
|
isEditing.value[index] = false;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Erreur lors de la mise à jour des données mockées :", error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const cancelEdit = () => {
|
// Utilisation de `mockSaveEdit` au lieu de `saveEdit` dans `handleClick` ou tout autre endroit
|
||||||
editedDescription.value = currentDescription.value;
|
const saveEdit = async (index: number) => {
|
||||||
isEditing.value = false;
|
if (IS_MOCK_MODE) {
|
||||||
|
await mockSaveEdit(index);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const id = index + 1;
|
||||||
|
await axios.put(`http://localhost:5000/data/${id}`, {
|
||||||
|
canva_data: editedDescriptions.value[index]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mettre à jour l'affichage local après la mise à jour réussie
|
||||||
|
currentDescriptions.value[index] = editedDescriptions.value[index];
|
||||||
|
isEditing.value[index] = false;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Erreur lors de la mise à jour des données :", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const cancelEdit = (index: number) => {
|
||||||
|
editedDescriptions.value[index] = currentDescriptions.value[index];
|
||||||
|
isEditing.value[index] = false;
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -109,6 +211,17 @@ const cancelEdit = () => {
|
|||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cell .description {
|
||||||
|
width: 800px;
|
||||||
|
height: 100px;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.expanded {
|
.expanded {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -124,21 +237,21 @@ const cancelEdit = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.edit-input {
|
.edit-input {
|
||||||
width: 80%;
|
width: 800px;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.button-container {
|
.button-container {
|
||||||
position: absolute;
|
|
||||||
bottom: 20px;
|
|
||||||
right: 20px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-button, .save-button, .cancel-button {
|
.edit-button, .save-button, .cancel-button {
|
||||||
@ -147,7 +260,8 @@ const cancelEdit = () => {
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background 0.3s ease;
|
transition: background 0.3s ease;
|
||||||
font-size: 16px;
|
font-size: 12px;
|
||||||
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-button {
|
.edit-button {
|
||||||
@ -178,4 +292,3 @@ const cancelEdit = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -4,7 +4,9 @@
|
|||||||
v-for="(item, index) in items"
|
v-for="(item, index) in items"
|
||||||
:key="index"
|
:key="index"
|
||||||
:title="item.title"
|
:title="item.title"
|
||||||
|
:title_text="item.title_text"
|
||||||
:description="item.description"
|
:description="item.description"
|
||||||
|
:projectId="item.projectId"
|
||||||
:class="item.class"
|
:class="item.class"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -15,16 +17,17 @@ import { ref } from "vue";
|
|||||||
import CanvasItem from "@/components/canvas/CanvasItem.vue";
|
import CanvasItem from "@/components/canvas/CanvasItem.vue";
|
||||||
|
|
||||||
const items = ref([
|
const items = ref([
|
||||||
{ title: "1. Problème", description: "3 problèmes essentiels à résoudre pour le client", class: "Probleme" },
|
{ projectId: 1, title: 1, title_text: "1. Problème", description: "3 problèmes essentiels à résoudre pour le client", class: "Probleme" },
|
||||||
{ title: "2. Segments", description: "Les segments de clientèle visés", class: "Segments" },
|
{ projectId: 1, title: 2, title_text: "2. Segments", description: "Les segments de clientèle visés", class: "Segments" },
|
||||||
{ title: "3. Valeur", description: "La proposition de valeur", class: "Valeur" },
|
{ projectId: 1, title: 3, title_text: "3. Valeur", description: "La proposition de valeur", class: "Valeur" },
|
||||||
{ title: "4. Solution", description: "Les solutions proposées", class: "Solution" },
|
{ projectId: 1, title: 4, title_text: "4. Solution", description: "Les solutions proposées", class: "Solution" },
|
||||||
{ title: "5. Avantage", description: "Les avantages concurrentiels", class: "Avantage" },
|
{ projectId: 1, title: 5, title_text: "5. Avantage", description: "Les avantages concurrentiels", class: "Avantage" },
|
||||||
{ title: "6. Canaux", description: "Les canaux de distribution", class: "Canaux" },
|
{ projectId: 1, title: 6, title_text: "6. Canaux", description: "Les canaux de distribution", class: "Canaux" },
|
||||||
{ title: "7. Indicateurs", description: "Les indicateurs clés de performance", class: "Indicateurs" },
|
{ projectId: 1, title: 7, title_text: "7. Indicateurs", description: "Les indicateurs clés de performance", class: "Indicateurs" },
|
||||||
{ title: "8. Coûts", description: "Les coûts associés", class: "Couts" },
|
{ projectId: 1, title: 8, title_text: "8. Coûts", description: "Les coûts associés", class: "Couts" },
|
||||||
{ title: "9. Revenus", description: "Les sources de revenus", class: "Revenus" }
|
{ projectId: 1, title: 9, title_text: "9. Revenus", description: "Les sources de revenus", class: "Revenus" }
|
||||||
]);
|
]);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
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.