feat: using bootstrap..
This commit is contained in:
parent
60290956ec
commit
f8991e90ab
@ -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>
|
||||
</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)"
|
||||
>
|
||||
{{ email }}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -23,35 +28,40 @@
|
||||
import { ref, onMounted } from "vue";
|
||||
import axios from "axios";
|
||||
|
||||
const IS_MOCK_MODE = true;
|
||||
const IS_MOCK_MODE = true;
|
||||
|
||||
const props = defineProps<{
|
||||
projectId: number;
|
||||
}>();
|
||||
|
||||
type Entrepreneur = {
|
||||
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) => {
|
||||
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>
|
||||
|
||||
<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