fix: with prettier
Some checks failed
Format / formatting (push) Successful in 6s
Build / build (push) Successful in 42s
CI / build (push) Failing after 10s
Format / formatting (pull_request) Successful in 5s

This commit is contained in:
Mohamed Maoulainine Maoulainine 2025-04-21 19:01:15 +02:00
parent e20556ed0f
commit c60fb8945b
17 changed files with 1414 additions and 1286 deletions

View File

@ -1,16 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { /*RouterLink,*/ RouterView } from 'vue-router' import { /*RouterLink,*/ RouterView } from "vue-router";
import ErrorWrapper from "@/views/errorWrapper.vue"; import ErrorWrapper from "@/views/errorWrapper.vue";
</script> </script>
<template> <template>
<Header />
<Header />
<ErrorWrapper /> <ErrorWrapper />
<!--<RouterLink to="/">Home</RouterLink> | --> <!--<RouterLink to="/">Home</RouterLink> | -->
<!--<RouterLink to="/canvas">Canvas</RouterLink> --> <!--<RouterLink to="/canvas">Canvas</RouterLink> -->
<RouterView /> <RouterView />
</template> </template>

View File

@ -1,110 +1,108 @@
<template> <template>
<form class="add-project-form" @submit.prevent="submitProject"> <form class="add-project-form" @submit.prevent="submitProject">
<h2>Ajouter un projet</h2> <h2>Ajouter un projet</h2>
<div class="form-group"> <div class="form-group">
<label for="projectName">Nom du projet</label> <label for="projectName">Nom du projet</label>
<input <input
id="projectName" id="projectName"
v-model="project.projectName" v-model="project.projectName"
type="text" type="text"
required required
/> />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="creationDate">Date de création</label> <label for="creationDate">Date de création</label>
<input <input
id="creationDate" id="creationDate"
v-model="project.creationDate" v-model="project.creationDate"
type="text" type="text"
placeholder="JJ-MM-AAAA" placeholder="JJ-MM-AAAA"
required required
/> />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="logo">Logo</label> <label for="logo">Logo</label>
<input <input
id="logo" id="logo"
v-model="project.logo" v-model="project.logo"
type="text" type="text"
placeholder="(à discuter)" placeholder="(à discuter)"
/> />
</div> </div>
<button type="submit">Ajouter</button> <button type="submit">Ajouter</button>
</form> </form>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue"; import { ref } from "vue";
import { postApi } from "@/services/api.ts"; import { postApi } from "@/services/api.ts";
const project = ref({ const project = ref({
projectName: "", projectName: "",
creationDate: "", creationDate: "",
logo: "to be discussed not yet fixed", logo: "to be discussed not yet fixed",
}); });
function submitProject() { function submitProject() {
postApi("/admin/projects/add", project.value); postApi("/admin/projects/add", project.value);
} }
</script> </script>
<style scoped> <style scoped>
h2 {
h2{
font-size: 1.5rem; font-size: 1.5rem;
color: #333; color: #333;
margin-bottom: 1.2rem; margin-bottom: 1.2rem;
border-bottom: 2px solid #ddd; border-bottom: 2px solid #ddd;
padding-bottom: 0.5rem; padding-bottom: 0.5rem;
} }
.add-project-form { .add-project-form {
max-width: 500px; max-width: 500px;
margin: 0 auto; margin: 0 auto;
padding: 20px; padding: 20px;
background: #fff; background: #fff;
border-radius: 10px; border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
} }
h2 { h2 {
margin-bottom: 20px; margin-bottom: 20px;
font-size: 24px; font-size: 24px;
color: #333; color: #333;
} }
.form-group { .form-group {
margin-bottom: 15px; margin-bottom: 15px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
label { label {
font-weight: bold; font-weight: bold;
margin-bottom: 5px; margin-bottom: 5px;
} }
input { input {
padding: 8px; padding: 8px;
border-radius: 5px; border-radius: 5px;
border: 1px solid #ccc; border: 1px solid #ccc;
} }
button { button {
background-color: #4caf50; background-color: #4caf50;
color: white; color: white;
padding: 10px 15px; padding: 10px 15px;
border: none; border: none;
border-radius: 5px; border-radius: 5px;
cursor: pointer; cursor: pointer;
} }
button:hover { button:hover {
background-color: #45a049; background-color: #45a049;
} }
</style> </style>

View File

@ -2,98 +2,92 @@
<div id="agenda"> <div id="agenda">
<h3>Rendez-vous</h3> <h3>Rendez-vous</h3>
<table> <table>
<thead> <thead>
<tr> <tr>
<th>Projet</th> <th>Projet</th>
<th>Date</th> <th>Date</th>
<th>Lieu</th> <th>Lieu</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="(p, index) in projectRDV" :key="index"> <tr v-for="(p, index) in projectRDV" :key="index">
<td>{{ p.projectName }}</td> <td>{{ p.projectName }}</td>
<td>{{ p.date }}</td> <td>{{ p.date }}</td>
<td>{{ p.lieu }}</td> <td>{{ p.lieu }}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { defineProps } from "vue"; import { defineProps } from "vue";
interface rendezVous{ interface rendezVous {
projectName: string, projectName: string;
date: string, date: string;
lieu: string, lieu: string;
} }
defineProps<{
projectRDV: rendezVous[]
}>();
defineProps<{
projectRDV: rendezVous[];
}>();
</script> </script>
<style scoped> <style scoped>
h3 {
h3{
font-size: 1.5rem; font-size: 1.5rem;
color: #333; color: #333;
margin-bottom: 1.2rem; margin-bottom: 1.2rem;
border-bottom: 2px solid #ddd; border-bottom: 2px solid #ddd;
padding-bottom: 0.5rem; padding-bottom: 0.5rem;
}
#agenda {
padding: 20px;
background-color: white;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
} }
#agenda {
padding: 20px;
background-color: white;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
}
/* Table Styling */ /* Table Styling */
table { table {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
text-align: left; text-align: left;
margin-top: 20px; margin-top: 20px;
border: 1px solid #ccc; border: 1px solid #ccc;
}
th {
background-color: #f0f2f5;
padding: 12px;
font-weight: 600;
color: #333;
} }
th {
background-color: #f0f2f5;
padding: 12px;
font-weight: 600;
color: #333;
}
/* Table Body Rows */ /* Table Body Rows */
tbody tr { tbody tr {
border-bottom: 1px solid #ddd; border-bottom: 1px solid #ddd;
transition: background-color 0.2s ease; /* Smooth hover effect */ transition: background-color 0.2s ease; /* Smooth hover effect */
} }
tbody tr:hover { tbody tr:hover {
background-color: #f9f9f9; /* Highlight row on hover */ background-color: #f9f9f9; /* Highlight row on hover */
} }
/* Cells Styling */ /* Cells Styling */
td { td {
padding: 10px; padding: 10px;
border: 1px solid #eee; border: 1px solid #eee;
font-size: 14px; font-size: 14px;
vertical-align: middle; /* Align text to middle */ vertical-align: middle; /* Align text to middle */
} }
/* First Column Styling */ /* First Column Styling */
td:first-child { td:first-child {
text-align: center; text-align: center;
width: 50px; /* Adjust width as needed */ width: 50px; /* Adjust width as needed */
} }
</style> </style>

View File

@ -1,35 +1,35 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from "vue"; import { onMounted, ref } from "vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import {jwtDecode} from "jwt-decode"; // i hope this doesn't break the code later import { jwtDecode } from "jwt-decode"; // i hope this doesn't break the code later
import { store } from "../main.ts"; import { store } from "../main.ts";
import { callApi } from "@/services/api.ts"; import { callApi } from "@/services/api.ts";
const router = useRouter(); const router = useRouter();
type TokenPayload = { type TokenPayload = {
realm_access?: { realm_access?: {
roles?: string[]; roles?: string[];
}; };
}; };
const customRequest = ref(''); const customRequest = ref("");
onMounted(() => { onMounted(() => {
if (store.authenticated && store.user.token) { if (store.authenticated && store.user.token) {
try { try {
const decoded = jwtDecode<TokenPayload>(store.user.token); const decoded = jwtDecode<TokenPayload>(store.user.token);
const roles = decoded.realm_access?.roles || []; const roles = decoded.realm_access?.roles || [];
if (roles.includes("MyINPulse-admin")) { if (roles.includes("MyINPulse-admin")) {
router.push("/"); router.push("/");
} else if (roles.includes("MyINPulse-entrepreneur")) { } else if (roles.includes("MyINPulse-entrepreneur")) {
router.push("/leanCanva"); router.push("/leanCanva");
} }
} catch (err) { } catch (err) {
console.error("Failed to decode token", err); console.error("Failed to decode token", err);
}
} }
}
}); });
/* /*
const loading = ref(false); const loading = ref(false);
@ -40,145 +40,155 @@ const callApiWithLoading = async (path: string) => {
loading.value = false; loading.value = false;
}; };
*/ */
</script> </script>
<template> <template>
<error-wrapper></error-wrapper> <error-wrapper></error-wrapper>
<div class="auth-container"> <div class="auth-container">
<div class="auth-card"> <div class="auth-card">
<h1>Bienvenue</h1> <h1>Bienvenue</h1>
<div class="status" :class="store.authenticated ? 'success' : 'error'"> <div
<p> class="status"
{{ store.authenticated ? '✅ Authenticated' : '❌ Not Authenticated' }} :class="store.authenticated ? 'success' : 'error'"
</p> >
<p>
{{
store.authenticated
? "✅ Authenticated"
: "❌ Not Authenticated"
}}
</p>
</div>
<div class="actions">
<button @click="store.login">Login</button>
<button @click="store.logout">Logout</button>
<button @click="store.signup">Signup-admin</button>
<button @click="store.signup">Signup-Entrepreneur</button>
<button @click="store.refreshUserToken">Refresh Token</button>
</div>
<div v-if="store.authenticated" class="token-section">
<p><strong>Access Token:</strong></p>
<pre>{{ store.user.token }}</pre>
<p><strong>Refresh Token:</strong></p>
<pre>{{ store.user.refreshToken }}</pre>
</div>
<div class="api-calls">
<h2>Test API Calls</h2>
<button @click="callApi('random')">
Call Entrepreneur API
</button>
<button @click="callApi('random2')">Call Admin API</button>
<button @click="callApi('unauth/dev')">Call Unauth API</button>
<div class="custom-call">
<input
v-model="customRequest"
placeholder="Custom endpoint"
/>
<button @click="callApi(customRequest)">Call</button>
</div>
</div>
</div> </div>
<div class="actions">
<button @click="store.login">Login</button>
<button @click="store.logout">Logout</button>
<button @click="store.signup">Signup-admin</button>
<button @click="store.signup">Signup-Entrepreneur</button>
<button @click="store.refreshUserToken">Refresh Token</button>
</div>
<div v-if="store.authenticated" class="token-section" >
<p><strong>Access Token:</strong></p>
<pre>{{ store.user.token }}</pre>
<p><strong>Refresh Token:</strong></p>
<pre>{{ store.user.refreshToken }}</pre>
</div>
<div class="api-calls">
<h2>Test API Calls</h2>
<button @click="callApi('random')">Call Entrepreneur API</button>
<button @click="callApi('random2')">Call Admin API</button>
<button @click="callApi('unauth/dev')">Call Unauth API</button>
<div class="custom-call">
<input v-model="customRequest" placeholder="Custom endpoint" />
<button @click="callApi(customRequest)">Call</button>
</div>
</div>
</div>
</div> </div>
</template> </template>
<style scoped> <style scoped>
.auth-container { .auth-container {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
padding: 3rem 1rem; padding: 3rem 1rem;
min-height: 100vh; min-height: 100vh;
background-color: #eef1f5; background-color: #eef1f5;
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
} }
.auth-card { .auth-card {
background: white; background: white;
padding: 2rem; padding: 2rem;
border-radius: 12px; border-radius: 12px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
width: 100%; width: 100%;
max-width: 600px; max-width: 600px;
} }
h1 { h1 {
text-align: center; text-align: center;
margin-bottom: 1rem; margin-bottom: 1rem;
color: #333; color: #333;
} }
.status { .status {
text-align: center; text-align: center;
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
font-weight: bold; font-weight: bold;
} }
.success { .success {
color: green; color: green;
} }
.error { .error {
color: red; color: red;
} }
.actions { .actions {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 1rem; gap: 1rem;
justify-content: center; justify-content: center;
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
} }
.actions button { .actions button {
padding: 0.6rem 1rem; padding: 0.6rem 1rem;
background-color: #4a90e2; background-color: #4a90e2;
border: none; border: none;
color: white; color: white;
border-radius: 8px; border-radius: 8px;
cursor: pointer; cursor: pointer;
transition: background-color 0.2s; transition: background-color 0.2s;
} }
.actions button:hover { .actions button:hover {
background-color: #357abd; background-color: #357abd;
} }
.token-section pre { .token-section pre {
background: #f6f8fa; background: #f6f8fa;
padding: 0.5rem; padding: 0.5rem;
overflow-x: auto; overflow-x: auto;
border: 1px solid #ddd; border: 1px solid #ddd;
border-radius: 6px; border-radius: 6px;
margin-bottom: 1rem; margin-bottom: 1rem;
font-size: 0.85rem; font-size: 0.85rem;
} }
.api-calls { .api-calls {
margin-top: 2rem; margin-top: 2rem;
} }
.api-calls h2 { .api-calls h2 {
margin-bottom: 1rem; margin-bottom: 1rem;
color: #444; color: #444;
font-size: 1.1rem; font-size: 1.1rem;
} }
.api-calls button { .api-calls button {
margin-right: 0.5rem; margin-right: 0.5rem;
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
} }
.custom-call { .custom-call {
margin-top: 1rem; margin-top: 1rem;
display: flex; display: flex;
gap: 0.5rem; gap: 0.5rem;
} }
.custom-call input { .custom-call input {
flex: 1; flex: 1;
padding: 0.5rem; padding: 0.5rem;
border: 1px solid #ccc; border: 1px solid #ccc;
border-radius: 6px; border-radius: 6px;
} }
/* /*
.status { .status {
@ -190,9 +200,7 @@ h1 {
} }
*/ */
.status.error { .status.error {
background-color: #ffe2e2; background-color: #ffe2e2;
color: #c62828; color: #c62828;
} }
</style> </style>

View File

@ -5,129 +5,125 @@
<h2>{{ projectName }}</h2> <h2>{{ projectName }}</h2>
<p>Projet mis le: {{ creationDate }}</p> <p>Projet mis le: {{ creationDate }}</p>
</div> </div>
<div class="project-buttons"> <div class="project-buttons">
<button id="accept" @click="acceptProject">Accepter</button> <button id="accept" @click="acceptProject">Accepter</button>
<button id="refus" @click="refuseProject">Refuser</button> <button id="refus" @click="refuseProject">Refuser</button>
</div>
</div> </div>
</div> </div>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { defineProps } from "vue"; import { defineProps } from "vue";
import { postApi } from "@/services/api"; import { postApi } from "@/services/api";
import { addNewMessage, color } from "@/services/popupDisplayer"; import { addNewMessage, color } from "@/services/popupDisplayer";
const props = defineProps<{ const props = defineProps<{
projectName: string; projectName: string;
creationDate: string; creationDate: string;
}>(); }>();
const URI = "/admin/projects/pending/decision"; const URI = "/admin/projects/pending/decision";
const sendDecision = (decision: "true" | "false") => { const sendDecision = (decision: "true" | "false") => {
postApi( postApi(
URI, URI,
{ {
projectName: props.projectName, projectName: props.projectName,
decision, decision,
}, },
() => { () => {
addNewMessage( addNewMessage(
`Projet ${props.projectName} ${decision === "true" ? "accepté" : "refusé"}`, `Projet ${props.projectName} ${decision === "true" ? "accepté" : "refusé"}`,
color.Green color.Green
); );
}, },
(err) => { (err) => {
addNewMessage(`Erreur lors de la décision`, color.Red); addNewMessage(`Erreur lors de la décision`, color.Red);
console.error(err); console.error(err);
} }
); );
}; };
const acceptProject = () => sendDecision("true"); const acceptProject = () => sendDecision("true");
const refuseProject = () => sendDecision("false"); const refuseProject = () => sendDecision("false");
</script> </script>
<style scoped> <style scoped>
.project { .project {
background: linear-gradient(to right, #f8f9fb, #ffffff); background: linear-gradient(to right, #f8f9fb, #ffffff);
border: 1px solid #e0e0e0; border: 1px solid #e0e0e0;
border-radius: 16px; border-radius: 16px;
padding: 1.5rem; padding: 1.5rem;
margin: 1.5rem 0; margin: 1.5rem 0;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
transition: box-shadow 0.3s ease; transition: box-shadow 0.3s ease;
} }
.project:hover { .project:hover {
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.08); box-shadow: 0 6px 18px rgba(0, 0, 0, 0.08);
} }
.project-header { .project-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
flex-wrap: wrap; flex-wrap: wrap;
gap: 1rem; gap: 1rem;
} }
.project-title { .project-title {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.project-title h2 { .project-title h2 {
font-size: 1.25rem; font-size: 1.25rem;
color: #222; color: #222;
margin: 0; margin: 0;
font-weight: 600; font-weight: 600;
} }
.project-title p { .project-title p {
font-size: 0.9rem; font-size: 0.9rem;
color: #666; color: #666;
margin-top: 0.25rem; margin-top: 0.25rem;
} }
.project-buttons { .project-buttons {
display: flex; display: flex;
gap: 0.75rem; gap: 0.75rem;
} }
button { button {
padding: 0.5rem 1.1rem; padding: 0.5rem 1.1rem;
color: white; color: white;
border: none; border: none;
border-radius: 8px; border-radius: 8px;
font-size: 0.9rem; font-size: 0.9rem;
font-weight: 500; font-weight: 500;
cursor: pointer; cursor: pointer;
transition: background-color 0.2s ease, transform 0.2s ease; transition:
background-color 0.2s ease,
transform 0.2s ease;
} }
#accept { #accept {
background-color: #4CAF50; background-color: #4caf50;
} }
#accept:hover { #accept:hover {
background-color: #3e8e41; background-color: #3e8e41;
transform: translateY(-2px); transform: translateY(-2px);
} }
#refus { #refus {
background-color: #e74c3c; background-color: #e74c3c;
} }
#refus:hover { #refus:hover {
background-color: #c0392b; background-color: #c0392b;
transform: translateY(-2px); transform: translateY(-2px);
} }
</style> </style>

View File

@ -1,37 +1,41 @@
<template> <template>
<div class="project" @click="goToLink" > <div class="project" @click="goToLink">
<div class="project-header"> <div class="project-header">
<h2 >{{ projectName }}</h2> <h2>{{ projectName }}</h2>
<div class="header-actions"> <div class="header-actions">
<div class="dropdown-wrapper"> <div class="dropdown-wrapper">
<button class="contact-button" @click="toggleDropdown">Contact</button> <button class="contact-button" @click="toggleDropdown">
<div class="contact-dropdown" :class="{ 'dropdown-visible': isDropdownOpen }"> Contact
<button @click="contactAll">Contacter tous</button>
<button
v-for="(email, index) in entrepreneurEmails"
:key="index"
@click="contactSingle(email)"
>
{{ email }}
</button> </button>
</div> <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 }}
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="project-body"> <div class="project-body">
<ul> <ul>
<li v-for="(name, index) in listName" :key="index">{{ name }}</li> <li v-for="(name, index) in listName" :key="index">
{{ name }}
</li>
</ul> </ul>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { defineProps } from "vue"; import { defineProps } from "vue";
import { useRouter } from 'vue-router' import { useRouter } from "vue-router";
import { ref, onMounted } from "vue"; import { ref, onMounted } from "vue";
import axios from "axios"; import axios from "axios";
const IS_MOCK_MODE = true; const IS_MOCK_MODE = true;
@ -46,188 +50,200 @@ const props = defineProps<{
const router = useRouter(); const router = useRouter();
const goToLink = () => { const goToLink = () => {
if (props.projectLink) { if (props.projectLink) {
router.push(props.projectLink); router.push(props.projectLink);
} }
}; };
type Entrepreneur = { type Entrepreneur = {
idUser: number; idUser: number;
userSurname: string; userSurname: string;
userName: string; userName: string;
primaryMail: string; primaryMail: string;
secondaryMail: string; secondaryMail: string;
phoneNumber: string; phoneNumber: string;
school: string; school: string;
course: string; course: string;
sneeStatus: boolean; sneeStatus: boolean;
}; };
const isDropdownOpen = ref(false); const isDropdownOpen = ref(false);
const entrepreneurEmails = ref<string[]>([]); const entrepreneurEmails = ref<string[]>([]);
const toggleDropdown = () => { const toggleDropdown = () => {
isDropdownOpen.value = !isDropdownOpen.value; isDropdownOpen.value = !isDropdownOpen.value;
console.log("Dropdown toggled:", isDropdownOpen.value); console.log("Dropdown toggled:", isDropdownOpen.value);
}; };
const fetchEntrepreneurs = async (projectId: number, useMock = IS_MOCK_MODE) => { const fetchEntrepreneurs = async (
try { projectId: number,
const responseData: Entrepreneur[] = useMock useMock = IS_MOCK_MODE
? await mockFetchEntrepreneurs(projectId) ) => {
: (await axios.get(`http://localhost:5000/shared/projects/entrepreneurs/${projectId}`)).data; try {
const responseData: Entrepreneur[] = useMock
? await mockFetchEntrepreneurs(projectId)
: (
await axios.get(
`http://localhost:5000/shared/projects/entrepreneurs/${projectId}`
)
).data;
if (responseData.length > 0) { if (responseData.length > 0) {
entrepreneurEmails.value = responseData.map((item: Entrepreneur) => item.primaryMail); entrepreneurEmails.value = responseData.map(
} else { (item: Entrepreneur) => item.primaryMail
console.warn("Aucun entrepreneur trouvé."); );
} else {
console.warn("Aucun entrepreneur trouvé.");
}
} catch (error) {
console.error(
"Erreur lors de la récupération des entrepreneurs :",
error
);
} }
} catch (error) {
console.error("Erreur lors de la récupération des entrepreneurs :", error);
}
}; };
// Fonction de simulation de l'API // Fonction de simulation de l'API
const mockFetchEntrepreneurs = async (projectId :number) => { const mockFetchEntrepreneurs = async (projectId: number) => {
console.log(`Mock fetch pour projectId: ${projectId}`); console.log(`Mock fetch pour projectId: ${projectId}`);
return new Promise((resolve) => { return new Promise((resolve) => {
setTimeout(() => { setTimeout(() => {
resolve([ resolve([
{ {
idUser: 1, idUser: 1,
userSurname: "Doe", userSurname: "Doe",
userName: "John", userName: "John",
primaryMail: "john.doe@example.com", primaryMail: "john.doe@example.com",
secondaryMail: "johndoe@backup.com", secondaryMail: "johndoe@backup.com",
phoneNumber: "612345678", phoneNumber: "612345678",
school: "ENSEIRB", school: "ENSEIRB",
course: "Info", course: "Info",
sneeStatus: false sneeStatus: false,
}, },
{ {
idUser: 2, idUser: 2,
userSurname: "Smith", userSurname: "Smith",
userName: "Jane", userName: "Jane",
primaryMail: "jane.smith@example.com", primaryMail: "jane.smith@example.com",
secondaryMail: "janesmith@backup.com", secondaryMail: "janesmith@backup.com",
phoneNumber: "698765432", phoneNumber: "698765432",
school: "ENSEIRB", school: "ENSEIRB",
course: "Info", course: "Info",
sneeStatus: true sneeStatus: true,
} },
]); ]);
}, 500); }, 500);
}); });
}; };
const contactAll = () => { const contactAll = () => {
const allEmails = entrepreneurEmails.value.join(", "); const allEmails = entrepreneurEmails.value.join(", ");
navigator.clipboard.writeText(allEmails) navigator.clipboard
.then(() => { .writeText(allEmails)
alert("Tous les emails copiés dans le presse-papiers !"); .then(() => {
window.open("https://partage.bordeaux-inp.fr/", "_blank"); 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) => { const contactSingle = (email: string) => {
navigator.clipboard.writeText(email) navigator.clipboard
.then(() => { .writeText(email)
alert(`Adresse copiée : ${email}`); .then(() => {
window.open("https://partage.bordeaux-inp.fr/", "_blank"); 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);
});
}; };
onMounted(() => fetchEntrepreneurs(props.projectId, IS_MOCK_MODE)); onMounted(() => fetchEntrepreneurs(props.projectId, IS_MOCK_MODE));
</script> </script>
<style scoped> <style scoped>
.project { .project {
background: linear-gradient(to right, #f8f9fb, #ffffff); background: linear-gradient(to right, #f8f9fb, #ffffff);
border: 1px solid #e0e0e0; border: 1px solid #e0e0e0;
border-radius: 16px; border-radius: 16px;
padding: 1.5rem; padding: 1.5rem;
margin: 1.5rem 0; margin: 1.5rem 0;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
transition: box-shadow 0.3s ease; transition: box-shadow 0.3s ease;
cursor: pointer; cursor: pointer;
} }
.project:hover { .project:hover {
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.08); box-shadow: 0 6px 18px rgba(0, 0, 0, 0.08);
} }
.project-header { .project-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
flex-wrap: wrap; flex-wrap: wrap;
gap: 0.5rem; gap: 0.5rem;
} }
.project-header h2 { .project-header h2 {
font-size: 1.25rem; font-size: 1.25rem;
color: #222; color: #222;
margin: 0; margin: 0;
font-weight: 600; font-weight: 600;
} }
.project-buttons { .project-buttons {
display: flex; display: flex;
gap: 0.5rem; gap: 0.5rem;
} }
.contact-btn { .contact-btn {
background-color: #007bff; background-color: #007bff;
color: #fff; color: #fff;
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
border: none; border: none;
border-radius: 8px; border-radius: 8px;
font-size: 0.9rem; font-size: 0.9rem;
font-weight: 500; font-weight: 500;
transition: background-color 0.2s ease, transform 0.2s ease; transition:
background-color 0.2s ease,
transform 0.2s ease;
} }
.contact-btn:hover { .contact-btn:hover {
background-color: #0056b3; background-color: #0056b3;
transform: translateY(-2px); transform: translateY(-2px);
} }
.project-body { .project-body {
margin-top: 1rem; margin-top: 1rem;
} }
.project-body ul { .project-body ul {
list-style-type: disc; list-style-type: disc;
padding-left: 1.25rem; padding-left: 1.25rem;
margin: 0; margin: 0;
} }
.project-body ul li { .project-body ul li {
font-size: 0.95rem; font-size: 0.95rem;
color: #555; color: #555;
line-height: 1.6; line-height: 1.6;
} }
button {
button { padding: 10px 15px;
padding: 10px 15px; background-color: #007bff;
background-color: #007bff; color: white;
color: white; border: none;
border: none; cursor: pointer;
cursor: pointer; border-radius: 5px;
border-radius: 5px;
} }
button:hover { button:hover {
background-color: #0056b3; background-color: #0056b3;
} }
</style> </style>

View File

@ -1,47 +1,69 @@
<template> <template>
<div :class="['cell', { expanded }]" @click="handleClick"> <div :class="['cell', { expanded }]" @click="handleClick">
<h3 class="fs-5 fw-medium">{{ titleText }}</h3> <h3 class="fs-5 fw-medium">{{ titleText }}</h3>
<div v-for="(desc, index) in currentDescriptions" :key="index" class="section-bloc"> <div
v-for="(desc, index) in currentDescriptions"
:key="index"
class="section-bloc"
>
<!-- ADMIN -------------------------------------------------------------------------------------------->
<!-- ADMIN --------------------------------------------------------------------------------------------> <template v-if="IS_ADMIN">
<div class="description">
<p class="m-0">{{ desc }}</p>
</div>
</template>
<template v-if="IS_ADMIN"> <!-- ENTREP ------------------------------------------------------------------------------------------->
<div class="description">
<p class="m-0">{{ desc }}</p> <template v-if="!IS_ADMIN">
<!-- Mode affichage -->
<template v-if="!isEditing[index]">
<div class="description">
<p class="m-0">{{ desc }}</p>
</div>
<div class="button-container">
<button
v-if="expanded"
class="edit-button"
@click.stop="startEditing(index)"
>
Éditer
</button>
</div>
</template>
<!-- Mode édition -->
<template v-else>
<textarea
v-model="editedDescriptions[index]"
class="edit-input"
></textarea>
<div class="button-container">
<button
class="save-button"
@click.stop="saveEdit(index)"
>
Enregistrer
</button>
<button
class="cancel-button"
@click.stop="cancelEdit(index)"
>
Annuler
</button>
</div>
</template>
</template>
</div> </div>
</template> <!---------------------------------------------------------------------------------------------------->
<template v-if="expanded">
<!-- ENTREP -------------------------------------------------------------------------------------------> <div class="canvas-exit-hint">
Cliquez n'importe pour quitter le canvas
<template v-if="!IS_ADMIN"> </div>
<!-- Mode affichage -->
<template v-if="!isEditing[index]">
<div class="description">
<p class="m-0">{{ desc }}</p>
</div>
<div class="button-container">
<button v-if="expanded" class="edit-button" @click.stop="startEditing(index)">Éditer</button>
</div>
</template> </template>
<!-- Mode édition -->
<template v-else>
<textarea v-model="editedDescriptions[index]" class="edit-input"></textarea>
<div class="button-container">
<button class="save-button" @click.stop="saveEdit(index)">Enregistrer</button>
<button class="cancel-button" @click.stop="cancelEdit(index)">Annuler</button>
</div>
</template>
</template>
</div> </div>
<!---------------------------------------------------------------------------------------------------->
<template v-if="expanded">
<div class="canvas-exit-hint">
Cliquez n'importe pour quitter le canvas
</div>
</template>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -52,11 +74,11 @@ import { axiosInstance } from "@/services/api.ts";
const IS_MOCK_MODE = true; const IS_MOCK_MODE = true;
const props = defineProps<{ const props = defineProps<{
projectId: number; projectId: number;
title: number; title: number;
titleText: string; titleText: string;
description: string; description: string;
isAdmin: number; isAdmin: number;
}>(); }>();
const IS_ADMIN = props.isAdmin; const IS_ADMIN = props.isAdmin;
@ -68,10 +90,9 @@ const editedDescriptions = ref<string[]>([]);
const isEditing = ref<boolean[]>([]); const isEditing = ref<boolean[]>([]);
onMounted(() => { onMounted(() => {
fetchData(props.projectId, props.title, "NaN", IS_MOCK_MODE); fetchData(props.projectId, props.title, "NaN", IS_MOCK_MODE);
}); });
/* FOR LOCAL DATABASE /* FOR LOCAL DATABASE
const fetchData = async () => { const fetchData = async () => {
try { try {
@ -111,59 +132,67 @@ const fetchData = async (projectId: number, title: number, date: string, useMock
*/ */
// Fonction fetchData avec possibilité d'utiliser le mock // Fonction fetchData avec possibilité d'utiliser le mock
const fetchData = async (projectId: number, title: number, date: string, useMock = false) => { const fetchData = async (
try { projectId: number,
const responseData = useMock title: number,
? await mockFetch(projectId, title, date) date: string,
: (await axiosInstance.get<{ txt: string }[]>( useMock = false
`/shared/projects/lcsection/${projectId}/${title}/${date}` ) => {
)).data; 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) { if (responseData.length > 0) {
currentDescriptions.value = responseData.map((item) => item.txt); currentDescriptions.value = responseData.map((item) => item.txt);
editedDescriptions.value = [...currentDescriptions.value]; editedDescriptions.value = [...currentDescriptions.value];
isEditing.value = Array(responseData.length).fill(false); isEditing.value = Array(responseData.length).fill(false);
} else { } else {
console.warn("Aucune donnée reçue."); console.warn("Aucune donnée reçue.");
}
} catch (error) {
console.error("Erreur lors de la récupération des données :", error);
} }
} catch (error) {
console.error("Erreur lors de la récupération des données :", error);
}
}; };
// Fonction de simulation de l'API // Fonction de simulation de l'API
const mockFetch = async (projectId: number, title: number, date: string) => { const mockFetch = async (projectId: number, title: number, date: string) => {
console.log(`Mock fetch pour projectId: ${projectId}, title: ${title}, date: ${date}`); console.log(
`Mock fetch pour projectId: ${projectId}, title: ${title}, date: ${date}`
);
return new Promise<{ txt: string }[]>((resolve) => { return new Promise<{ txt: string }[]>((resolve) => {
setTimeout(() => { setTimeout(() => {
resolve([ resolve([
{txt: "Ceci est une description 1 pour tester le front."}, { txt: "Ceci est une description 1 pour tester le front." },
{txt: "Deuxième description."}, { txt: "Deuxième description." },
{txt: "Troisième description."} { txt: "Troisième description." },
]); ]);
}, 500); // Simule un délai réseau de 500ms }, 500); // Simule un délai réseau de 500ms
}); });
}; };
// Utilisation du mock dans handleClick pour tester sans serveur // Utilisation du mock dans handleClick pour tester sans serveur
const handleClick = async () => { const handleClick = async () => {
if (!expanded.value) { if (!expanded.value) {
await fetchData(props.projectId, props.title, "NaN", IS_MOCK_MODE); await fetchData(props.projectId, props.title, "NaN", IS_MOCK_MODE);
} else if (!isEditing.value.includes(true)) { } else if (!isEditing.value.includes(true)) {
// Réinitialiser les descriptions si aucune édition n'est en cours // Réinitialiser les descriptions si aucune édition n'est en cours
currentDescriptions.value = [props.description]; currentDescriptions.value = [props.description];
editedDescriptions.value = [props.description]; editedDescriptions.value = [props.description];
} }
if (!isEditing.value.includes(true)) { if (!isEditing.value.includes(true)) {
expanded.value = !expanded.value; expanded.value = !expanded.value;
} }
}; };
const startEditing = (index: number) => { const startEditing = (index: number) => {
isEditing.value[index] = true; isEditing.value[index] = true;
}; };
/* /*
@ -184,213 +213,208 @@ const saveEdit = async (index: number) => {
*/ */
const saveEdit = async (index: number) => { const saveEdit = async (index: number) => {
if (IS_MOCK_MODE) { if (IS_MOCK_MODE) {
await mockSaveEdit(index); await mockSaveEdit(index);
} else { } else {
try { try {
const id = index + 1; const id = index + 1;
await axios.put(`http://localhost:5000/data/${id}`, { await axios.put(`http://localhost:5000/data/${id}`, {
canva_data: editedDescriptions.value[index] canva_data: editedDescriptions.value[index],
}); });
// Mettre à jour l'affichage local après la mise à jour réussie // Mettre à jour l'affichage local après la mise à jour réussie
currentDescriptions.value[index] = editedDescriptions.value[index]; currentDescriptions.value[index] = editedDescriptions.value[index];
isEditing.value[index] = false; isEditing.value[index] = false;
} catch (error) { } catch (error) {
console.error("Erreur lors de la mise à jour des données :", error); console.error("Erreur lors de la mise à jour des données :", error);
}
} }
}
}; };
// Fonction de mock pour l'enregistrement // Fonction de mock pour l'enregistrement
const mockSaveEdit = async (index: number) => { const mockSaveEdit = async (index: number) => {
try { try {
const id = index + 1; const id = index + 1;
console.log(`Mock save pour l'ID ${id} avec la description : ${editedDescriptions.value[index]}`); console.log(
`Mock save pour l'ID ${id} avec la description : ${editedDescriptions.value[index]}`
);
await new Promise((resolve) => setTimeout(resolve, 500)); // Simulation de délai 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 // Mettre à jour l'affichage local après la mise à jour réussie
currentDescriptions.value[index] = editedDescriptions.value[index]; currentDescriptions.value[index] = editedDescriptions.value[index];
isEditing.value[index] = false; isEditing.value[index] = false;
} catch (error) { } catch (error) {
console.error("Erreur lors de la mise à jour des données mockées :", error); console.error(
} "Erreur lors de la mise à jour des données mockées :",
error
);
}
}; };
const cancelEdit = (index: number) => { const cancelEdit = (index: number) => {
editedDescriptions.value[index] = currentDescriptions.value[index]; editedDescriptions.value[index] = currentDescriptions.value[index];
isEditing.value[index] = false; isEditing.value[index] = false;
}; };
</script> </script>
<style scoped> <style scoped>
@import "@/components/canvas/style-project.css"; @import "@/components/canvas/style-project.css";
.cell { .cell {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
text-align: center; text-align: center;
transition: all 0.3s ease; transition: all 0.3s ease;
cursor: pointer; cursor: pointer;
box-shadow: 0 4px 5px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 5px rgba(0, 0, 0, 0.1);
} }
.expanded-content { .expanded-content {
justify-content: flex-start !important; justify-content: flex-start !important;
} }
.cell:not(.expanded):hover { .cell:not(.expanded):hover {
transform: scale(1.05); transform: scale(1.05);
box-shadow: 0 8px 9px rgba(0, 0, 0, 0.2); box-shadow: 0 8px 9px rgba(0, 0, 0, 0.2);
} }
.cell h3 { .cell h3 {
font-size: 15px; font-size: 15px;
font-weight: 500; font-weight: 500;
font-family: 'Arial', sans-serif; font-family: "Arial", sans-serif;
} }
.p { .p {
font-size: 10px; font-size: 10px;
color: #666; color: #666;
font-family: 'Arial', sans-serif; font-family: "Arial", sans-serif;
} }
.expanded { .expanded {
padding-top: 10%; padding-top: 10%;
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
background: white; background: white;
z-index: 10; z-index: 10;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: flex-start; justify-content: flex-start;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
} }
.description { .description {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
text-align: center; text-align: center;
width: 100%; width: 100%;
height: 100%; height: 100%;
font-size: 16px; font-size: 16px;
margin-top: 10px; margin-top: 10px;
margin-left: 2%; margin-left: 2%;
margin-right: 4%; margin-right: 4%;
} }
.description + .p { .description + .p {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
text-align: center; text-align: center;
} }
.edit-input { .edit-input {
width: 100%; width: 100%;
height: 100%; height: 100%;
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; box-sizing: border-box;
margin-left: 2%; margin-left: 2%;
} }
.button-container { .button-container {
display: block; display: block;
margin-top: 20px; margin-top: 20px;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
gap: 10px; gap: 10px;
padding-right: 1%; padding-right: 1%;
} }
.section-bloc,
.section-bloc ,.editing-section-bloc { .editing-section-bloc {
width: 100%; width: 100%;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
display: flex; display: flex;
margin-right: 10%; margin-right: 10%;
margin: 10px; margin: 10px;
}
.edit-button {
width: 100px;
height: 40px;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background 0.3s ease;
font-size: 12px;
margin-right: 20px;
}
.save-button, .cancel-button {
width: 100px;
height: 40px;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background 0.3s ease;
font-size: 12px;
margin-bottom: 5px;
} }
.edit-button { .edit-button {
background-color: #007bff; width: 100px;
color: white; height: 40px;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background 0.3s ease;
font-size: 12px;
margin-right: 20px;
}
.save-button,
.cancel-button {
width: 100px;
height: 40px;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background 0.3s ease;
font-size: 12px;
margin-bottom: 5px;
}
.edit-button {
background-color: #007bff;
color: white;
} }
.save-button { .save-button {
background-color: #28a745; background-color: #28a745;
color: white; color: white;
} }
.cancel-button { .cancel-button {
background-color: #dc3545; background-color: #dc3545;
color: white; color: white;
} }
.edit-button:hover { .edit-button:hover {
background-color: #0056b3; background-color: #0056b3;
} }
.save-button:hover { .save-button:hover {
background-color: #218838; background-color: #218838;
} }
.cancel-button:hover { .cancel-button:hover {
background-color: #c82333; background-color: #c82333;
} }
.canvas-exit-hint { .canvas-exit-hint {
font-size: 0.75rem; font-size: 0.75rem;
color: #666; color: #666;
position: fixed; position: fixed;
bottom: 10px; bottom: 10px;
left: 0; left: 0;
width: 100%; width: 100%;
text-align: center; text-align: center;
z-index: 1000; z-index: 1000;
} }
</style> </style>

View File

@ -1,27 +1,31 @@
<template> <template>
<header class="header"> <header class="header">
<img src="../icons/logo inpulse.png" alt="INPulse Logo" class="logo" /> <img src="../icons/logo inpulse.png" alt="INPulse Logo" class="logo" />
<div class="header-actions"> <div class="header-actions">
<div class="dropdown-wrapper"> <div class="dropdown-wrapper">
<button class="contact-button" @click="toggleDropdown">Contact</button> <button class="contact-button" @click="toggleDropdown">
<div class="contact-dropdown" :class="{ 'dropdown-visible': isDropdownOpen }"> Contact
<button @click="contactAll">Contacter tous</button> </button>
<button <div
v-for="(email, index) in entrepreneurEmails" class="contact-dropdown"
:key="index" :class="{ 'dropdown-visible': isDropdownOpen }"
@click="contactSingle(email)" >
> <button @click="contactAll">Contacter tous</button>
{{ email }} <button
</button> v-for="(email, index) in entrepreneurEmails"
:key="index"
@click="contactSingle(email)"
>
{{ email }}
</button>
</div>
</div>
<RouterLink to="/" class="return-button">Retour</RouterLink>
</div> </div>
</div> </header>
<RouterLink to="/" class="return-button">Retour</RouterLink>
</div>
</header>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from "vue"; import { ref, onMounted } from "vue";
import axios from "axios"; import axios from "axios";
@ -29,100 +33,114 @@ import axios from "axios";
const IS_MOCK_MODE = true; const IS_MOCK_MODE = true;
const props = defineProps<{ const props = defineProps<{
projectId: number; projectId: number;
}>(); }>();
type Entrepreneur = { type Entrepreneur = {
idUser: number; idUser: number;
userSurname: string; userSurname: string;
userName: string; userName: string;
primaryMail: string; primaryMail: string;
secondaryMail: string; secondaryMail: string;
phoneNumber: string; phoneNumber: string;
school: string; school: string;
course: string; course: string;
sneeStatus: boolean; sneeStatus: boolean;
}; };
const isDropdownOpen = ref(false); const isDropdownOpen = ref(false);
const entrepreneurEmails = ref<string[]>([]); const entrepreneurEmails = ref<string[]>([]);
const toggleDropdown = () => { const toggleDropdown = () => {
isDropdownOpen.value = !isDropdownOpen.value; isDropdownOpen.value = !isDropdownOpen.value;
console.log("Dropdown toggled:", isDropdownOpen.value); console.log("Dropdown toggled:", isDropdownOpen.value);
}; };
const fetchEntrepreneurs = async (projectId: number, useMock = IS_MOCK_MODE) => { const fetchEntrepreneurs = async (
try { projectId: number,
const responseData: Entrepreneur[] = useMock useMock = IS_MOCK_MODE
? await mockFetchEntrepreneurs(projectId) ) => {
: (await axios.get(`http://localhost:5000/shared/projects/entrepreneurs/${projectId}`)).data; try {
const responseData: Entrepreneur[] = useMock
? await mockFetchEntrepreneurs(projectId)
: (
await axios.get(
`http://localhost:5000/shared/projects/entrepreneurs/${projectId}`
)
).data;
if (responseData.length > 0) { if (responseData.length > 0) {
entrepreneurEmails.value = responseData.map((item: Entrepreneur) => item.primaryMail); entrepreneurEmails.value = responseData.map(
} else { (item: Entrepreneur) => item.primaryMail
console.warn("Aucun entrepreneur trouvé."); );
} else {
console.warn("Aucun entrepreneur trouvé.");
}
} catch (error) {
console.error(
"Erreur lors de la récupération des entrepreneurs :",
error
);
} }
} catch (error) {
console.error("Erreur lors de la récupération des entrepreneurs :", error);
}
}; };
// Fonction de simulation de l'API // Fonction de simulation de l'API
const mockFetchEntrepreneurs = async (projectId :number) => { const mockFetchEntrepreneurs = async (projectId: number) => {
console.log(`Mock fetch pour projectId: ${projectId}`); console.log(`Mock fetch pour projectId: ${projectId}`);
return new Promise((resolve) => { return new Promise((resolve) => {
setTimeout(() => { setTimeout(() => {
resolve([ resolve([
{ {
idUser: 1, idUser: 1,
userSurname: "Doe", userSurname: "Doe",
userName: "John", userName: "John",
primaryMail: "john.doe@example.com", primaryMail: "john.doe@example.com",
secondaryMail: "johndoe@backup.com", secondaryMail: "johndoe@backup.com",
phoneNumber: "612345678", phoneNumber: "612345678",
school: "ENSEIRB", school: "ENSEIRB",
course: "Info", course: "Info",
sneeStatus: false sneeStatus: false,
}, },
{ {
idUser: 2, idUser: 2,
userSurname: "Smith", userSurname: "Smith",
userName: "Jane", userName: "Jane",
primaryMail: "jane.smith@example.com", primaryMail: "jane.smith@example.com",
secondaryMail: "janesmith@backup.com", secondaryMail: "janesmith@backup.com",
phoneNumber: "698765432", phoneNumber: "698765432",
school: "ENSEIRB", school: "ENSEIRB",
course: "Info", course: "Info",
sneeStatus: true sneeStatus: true,
} },
]); ]);
}, 500); }, 500);
}); });
}; };
const contactAll = () => { const contactAll = () => {
const allEmails = entrepreneurEmails.value.join(", "); const allEmails = entrepreneurEmails.value.join(", ");
navigator.clipboard.writeText(allEmails) navigator.clipboard
.then(() => { .writeText(allEmails)
alert("Tous les emails copiés dans le presse-papiers !"); .then(() => {
window.open("https://partage.bordeaux-inp.fr/", "_blank"); 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) => { const contactSingle = (email: string) => {
navigator.clipboard.writeText(email) navigator.clipboard
.then(() => { .writeText(email)
alert(`Adresse copiée : ${email}`); .then(() => {
window.open("https://partage.bordeaux-inp.fr/", "_blank"); 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);
});
}; };
/* /*
@ -142,78 +160,75 @@ onMounted(() => fetchEntrepreneurs(props.projectId, IS_MOCK_MODE));
@import "@/components/canvas/style-project.css"; @import "@/components/canvas/style-project.css";
.header { .header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 15px 30px; padding: 15px 30px;
background-color: #f9f9f9; background-color: #f9f9f9;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
} }
.logo { .logo {
height: 50px; height: 50px;
} }
.header-actions { .header-actions {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 20px; gap: 20px;
position: relative; position: relative;
} }
.contact-button, .contact-button,
.return-button { .return-button {
background-color: #009CDE; background-color: #009cde;
color: white; color: white;
border: none; border: none;
padding: 10px 15px; padding: 10px 15px;
cursor: pointer; cursor: pointer;
font-size: 14px; font-size: 14px;
border-radius: 5px; border-radius: 5px;
text-decoration: none; text-decoration: none;
transition: background-color 0.2s ease; transition: background-color 0.2s ease;
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
} }
.return-button:hover, .return-button:hover,
.contact-button:hover { .contact-button:hover {
background-color: #007bad; background-color: #007bad;
} }
.contact-dropdown { .contact-dropdown {
position: absolute; position: absolute;
top: 100%; top: 100%;
left: 0; left: 0;
background-color: #000; background-color: #000;
color: white; color: white;
box-shadow: 0px 4px 8px rgba(255, 255, 255, 0.2); box-shadow: 0px 4px 8px rgba(255, 255, 255, 0.2);
border-radius: 8px; border-radius: 8px;
padding: 10px; padding: 10px;
margin-top: 5px; margin-top: 5px;
z-index: 1000; z-index: 1000;
min-width: 200px; min-width: 200px;
display: none; display: none;
} }
.contact-dropdown button { .contact-dropdown button {
display: block; display: block;
width: 100%; width: 100%;
padding: 5px; padding: 5px;
text-align: left; text-align: left;
border: none; border: none;
background: none; background: none;
cursor: pointer; cursor: pointer;
color: white; color: white;
} }
.contact-dropdown button:hover { .contact-dropdown button:hover {
background-color: #009CDE; background-color: #009cde;
} }
.contact-dropdown.dropdown-visible { .contact-dropdown.dropdown-visible {
display: block; display: block;
} }
</style> </style>

View File

@ -1,16 +1,16 @@
<template> <template>
<div class="canvas container-fluid"> <div class="canvas container-fluid">
<CanvasItem <CanvasItem
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" :title-text="item.title_text"
:description="item.description" :description="item.description"
:project-id="item.projectId" :project-id="item.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"
/> />
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -18,65 +18,150 @@ import { ref, onMounted } from "vue";
import CanvasItem from "@/components/canvas/CanvasItem.vue"; import CanvasItem from "@/components/canvas/CanvasItem.vue";
const props = defineProps<{ const props = defineProps<{
isAdmin: number; isAdmin: number;
}>(); }>();
const items = ref([ 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,
{ projectId: 1, title: 3, title_text: "3. Valeur", description: "La proposition de valeur", class: "Valeur" }, title: 1,
{ projectId: 1, title: 4, title_text: "4. Solution", description: "Les solutions proposées", class: "Solution" }, title_text: "1. Problème",
{ projectId: 1, title: 5, title_text: "5. Avantage", description: "Les avantages concurrentiels", class: "Avantage" }, description: "3 problèmes essentiels à résoudre pour le client",
{ projectId: 1, title: 6, title_text: "6. Canaux", description: "Les canaux de distribution", class: "Canaux" }, class: "Probleme",
{ 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", class: "Revenus" } 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",
class: "Revenus",
},
]); ]);
onMounted(() => { onMounted(() => {
const bootstrapCss = document.createElement('link') const bootstrapCss = document.createElement("link");
bootstrapCss.rel = 'stylesheet' bootstrapCss.rel = "stylesheet";
bootstrapCss.href = 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css' bootstrapCss.href =
bootstrapCss.integrity = 'sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+Fpc+NC' "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css";
bootstrapCss.crossOrigin = 'anonymous' bootstrapCss.integrity =
document.head.appendChild(bootstrapCss) "sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+Fpc+NC";
bootstrapCss.crossOrigin = "anonymous";
document.head.appendChild(bootstrapCss);
const bootstrapJs = document.createElement('script') const bootstrapJs = document.createElement("script");
bootstrapJs.src = 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js' bootstrapJs.src =
bootstrapJs.integrity = 'sha384-mQ93S0EhrF4Z1nM+fTflmYf0DyzsY5j7F5H3WlClDD6H3WUJh6kxBkF3GDW8n1j6' "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js";
bootstrapJs.crossOrigin = 'anonymous' bootstrapJs.integrity =
document.body.appendChild(bootstrapJs) "sha384-mQ93S0EhrF4Z1nM+fTflmYf0DyzsY5j7F5H3WlClDD6H3WUJh6kxBkF3GDW8n1j6";
}) bootstrapJs.crossOrigin = "anonymous";
document.body.appendChild(bootstrapJs);
});
</script> </script>
<style scoped> <style scoped>
@import "@/components/canvas/style-project.css"; @import "@/components/canvas/style-project.css";
.canvas { .canvas {
display: grid; display: grid;
grid-template-columns: repeat(10, 1fr); grid-template-columns: repeat(10, 1fr);
grid-template-rows: repeat(6, 1fr); grid-template-rows: repeat(6, 1fr);
gap: 12px; gap: 12px;
padding: 30px; padding: 30px;
/*background-color: #f8f9fa;*/ /*background-color: #f8f9fa;*/
position: relative; position: relative;
height: 90vh; height: 90vh;
overflow: auto; overflow: auto;
} }
.Probleme { grid-column: 1 / 3; grid-row: 1 / 5; } .Probleme {
.Segments { grid-column: 9 / 11; grid-row: 1 / 5; } grid-column: 1 / 3;
.Valeur { grid-column: 5 / 7; grid-row: 1 / 5; } grid-row: 1 / 5;
.Solution { grid-column: 3 / 5; grid-row: 1 / 3; } }
.Avantage { grid-column: 7 / 9; grid-row: 1 / 3; } .Segments {
.Canaux { grid-column: 7 / 9; grid-row: 3 / 5; } grid-column: 9 / 11;
.Indicateurs { grid-column: 3 / 5; grid-row: 3 / 5; } grid-row: 1 / 5;
.Couts { grid-column: 1 / 6; grid-row: 5 / 7; } }
.Revenus { grid-column: 6 / 11; grid-row: 5 / 7; } .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 { .canvas-item {
/*background-color: white;*/ /*background-color: white;*/
border: 1px solid #dee2e6; border: 1px solid #dee2e6;
border-radius: 0.5rem; border-radius: 0.5rem;
} }
</style> </style>

View File

@ -2,63 +2,62 @@ body {
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
margin: 0; margin: 0;
padding: 10px; padding: 10px;
} }
.row { .row {
display: flex; display: flex;
} }
.cell { .cell {
flex: 1; flex: 1;
border: 1px solid #ddd; border: 1px solid #ddd;
padding: 10px; padding: 10px;
text-align: center; text-align: center;
background-color: #f1f1f1; background-color: #f1f1f1;
} }
.produit { .produit {
background-color: #f9e4e4; background-color: #f9e4e4;
} }
.marche { .marche {
background-color: #e4f1f9; background-color: #e4f1f9;
} }
.valeur { .valeur {
background-color: #f9f4e4; background-color: #f9f4e4;
} }
h3 { h3 {
margin: 0; margin: 0;
font-size: 18px; font-size: 18px;
color: #333; color: #333;
} }
p { p {
margin: 5px 0 0; margin: 5px 0 0;
font-size: 14px; font-size: 14px;
} }
body {
body {
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
margin: 0; margin: 0;
padding: 0; padding: 0;
background-color: #f9f9f9; background-color: #f9f9f9;
} }
h1 img { h1 img {
height: 80px; height: 80px;
margin: 40px; margin: 40px;
text-align: center; text-align: center;
} }
.row { .row {
display: flex; display: flex;
margin-bottom: 10px; margin-bottom: 10px;
} }
#ade { #ade {
max-width: 1200px; max-width: 1200px;
margin: 20px auto; margin: 20px auto;
padding: 20px; padding: 20px;
@ -66,49 +65,51 @@ body {
background-color: #e8f5e9; background-color: #e8f5e9;
border: 2px solid #4caf50; border: 2px solid #4caf50;
border-radius: 10px; border-radius: 10px;
} }
#ade h3 { #ade h3 {
color: #2e7d32; color: #2e7d32;
} }
#ade p { #ade p {
margin: 10px 0; margin: 10px 0;
font-size: 16px; font-size: 16px;
color: #333; color: #333;
} }
header { header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 10px 20px; padding: 10px 20px;
background-color: #fff; background-color: #fff;
border-bottom: 2px solid #ddd; border-bottom: 2px solid #ddd;
} }
header img { header img {
height: 60px; height: 60px;
} }
header .contact-menu { header .contact-menu {
position: relative; position: relative;
} }
.contact-button, .return { .contact-button,
.return {
padding: 10px 15px; padding: 10px 15px;
border: none; border: none;
border-radius: 4px; border-radius: 4px;
background-color: #2196f3; background-color: #2196f3;
color: #fff; color: #fff;
cursor: pointer; cursor: pointer;
} }
.contact-button:hover, .return:hover { .contact-button:hover,
.return:hover {
background-color: #1976d2; background-color: #1976d2;
} }
/* Dropdown styling */ /* Dropdown styling */
.contact-dropdown { .contact-dropdown {
position: absolute; position: absolute;
right: 0; right: 0;
top: 50px; top: 50px;
@ -121,36 +122,35 @@ body {
border-radius: 8px; border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
z-index: 10; z-index: 10;
} }
.contact-dropdown button { .contact-dropdown button {
padding: 8px 12px; padding: 8px 12px;
border: none; border: none;
border-radius: 4px; border-radius: 4px;
background-color: #4caf50; background-color: #4caf50;
color: #fff; color: #fff;
cursor: pointer; cursor: pointer;
} }
.contact-dropdown button:hover { .contact-dropdown button:hover {
background-color: #388e3c; background-color: #388e3c;
} }
.return { .return {
background-color: #f44336; background-color: #f44336;
} }
.return:hover { .return:hover {
background-color: #d32f2f; background-color: #d32f2f;
} }
.header-buttons { .header-buttons {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 15px; gap: 15px;
} }
a {
a{
color: white; color: white;
} }

View File

@ -26,8 +26,7 @@ keycloakService.CallInit(() => {
console.error(e); console.error(e);
createApp(App).mount("#app"); createApp(App).mount("#app");
} }
});
})
// this shit made by me so i can run the canva vue app // this shit made by me so i can run the canva vue app
//createApp(App).use(router).mount('#app'); //createApp(App).use(router).mount('#app');
@ -71,7 +70,4 @@ app.use(VueKeyCloak,{
} ); } );
*/ */
export { store }; export { store };

View File

@ -1,46 +1,46 @@
import { createRouter, createWebHistory } from "vue-router"; import { createRouter, createWebHistory } from "vue-router";
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
routes: [ routes: [
{ {
path: '/test', path: "/test",
name: 'test', name: "test",
// route level code-splitting // route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route // this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited. // which is lazy-loaded when the route is visited.
component: () => import('../views/testComponent.vue'), component: () => import("../views/testComponent.vue"),
}, },
{ {
path: '/login', path: "/login",
name: 'login', name: "login",
component: () => import('../components/LoginComponent.vue'), component: () => import("../components/LoginComponent.vue"),
}, },
{ {
path: '/', path: "/",
name: 'Admin-main', name: "Admin-main",
component: () => import('../views/AdminMain.vue'), component: () => import("../views/AdminMain.vue"),
}, },
// route pour les canvas (made by adnane), in fact the two vue apps are separated for now // route pour les canvas (made by adnane), in fact the two vue apps are separated for now
{ {
path: '/canvas', path: "/canvas",
name: 'canvas', name: "canvas",
component: () => import('../views/CanvasView.vue'), component: () => import("../views/CanvasView.vue"),
}, },
{ {
path: '/signup', path: "/signup",
name: 'signup', name: "signup",
component: () => import('../views/EntrepSignUp.vue'), component: () => import("../views/EntrepSignUp.vue"),
}, },
{ {
path: '/JorCproject', path: "/JorCproject",
name: 'JorCproject', name: "JorCproject",
component: () => import('../views/JoinOrCreatProjectForEntrep.vue'), component: () => import("../views/JoinOrCreatProjectForEntrep.vue"),
}, },
], ],
}) });
export default router; export default router;

View File

@ -88,5 +88,4 @@ function deleteApi(
.catch(onErrorHandler ?? defaultApiErrorHandler); .catch(onErrorHandler ?? defaultApiErrorHandler);
} }
export { axiosInstance, callApi, postApi, deleteApi }; export { axiosInstance, callApi, postApi, deleteApi };

View File

@ -2,38 +2,37 @@
<Header /> <Header />
<error-wrapper></error-wrapper> <error-wrapper></error-wrapper>
<div id="container"> <div id="container">
<div id="main"> <div id="main">
<h3> Projet en cours </h3> <h3>Projet en cours</h3>
<ProjectComp <ProjectComp
v-for="(project, index) in projects" v-for="(project, index) in projects"
:key="index" :key="index"
:project-name="project.name" :project-name="project.name"
:list-name="project.members" :list-name="project.members"
:project-link="project.link" :project-link="project.link"
:project-id="0" :project-id="0"
/> />
<div id ="main"> <div id="main">
<h3> Projet en attente </h3> <h3>Projet en attente</h3>
<PendingProjectComponent <PendingProjectComponent
v-for="( project, index) in pendingProjects" v-for="(project, index) in pendingProjects"
:key="index" :key="index"
:project-name="project.name" :project-name="project.name"
:creation-date="project.creationDate" :creation-date="project.creationDate"
/> />
</div>
<AddProjectForm />
</div> </div>
<AddProjectForm/> <Agenda :project-r-d-v="rendezVous" />
</div>
<Agenda :project-r-d-v="rendezVous" />
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref/*, onMounted*/ } from "vue"; import { ref /*, onMounted*/ } from "vue";
//import { callApi } from "@/services/api"; //import { callApi } from "@/services/api";
import Header from "../components/HeaderComponent.vue"; import Header from "../components/HeaderComponent.vue";
@ -89,79 +88,73 @@ import AddProjectForm from "@/components/AddProjectForm.vue";
onMounted(fetchProjects); onMounted(fetchProjects);
*/ */
const projects = ref([ const projects = ref([
{ {
name: "Projet Alpha", name: "Projet Alpha",
link: "/canvas", // to test link: "/canvas", // to test
members: ["Alice", "Bob", "Charlie"], members: ["Alice", "Bob", "Charlie"],
}, },
{ {
name: "Projet Beta", name: "Projet Beta",
link: "./canvas", // to test link: "./canvas", // to test
members: ["David", "Eve", "Frank"], members: ["David", "Eve", "Frank"],
}, },
]); ]);
const pendingProjects = ref([
const pendingProjects = ref ([ { name: "l'eau", creationDate: "26-02-2024" },
{ name: "l'eau", creationDate: "26-02-2024" }, { name: "l'air", creationDate: "09-03-2023" },
{ name: "l'air", creationDate: "09-03-2023" }, ]);
])
const rendezVous = ref([ const rendezVous = ref([
{ projectName: "Projet Alpha", date: "2025-03-10", lieu: "P106" }, { projectName: "Projet Alpha", date: "2025-03-10", lieu: "P106" },
{ projectName: "Projet Beta", date: "2025-04-15", lieu: "Td10" }, { projectName: "Projet Beta", date: "2025-04-15", lieu: "Td10" },
]); ]);
</script> </script>
<style scoped> <style scoped>
#container { #container {
display: grid; display: grid;
grid-template-columns: 3fr 1fr; grid-template-columns: 3fr 1fr;
gap: 2rem; gap: 2rem;
padding: 2rem; padding: 2rem;
background-color: #f4f6f9; background-color: #f4f6f9;
min-height: 100vh; min-height: 100vh;
box-sizing: border-box; box-sizing: border-box;
} }
#main { #main {
background-color: #fff; background-color: #fff;
padding: 2rem; padding: 2rem;
border-radius: 12px; border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
} }
h3 { h3 {
font-size: 1.5rem; font-size: 1.5rem;
color: #333; color: #333;
margin-bottom: 1.2rem; margin-bottom: 1.2rem;
border-bottom: 2px solid #ddd; border-bottom: 2px solid #ddd;
padding-bottom: 0.5rem; padding-bottom: 0.5rem;
} }
button { button {
padding: 10px 15px; padding: 10px 15px;
background-color: #007bff; background-color: #007bff;
color: white; color: white;
border: none; border: none;
cursor: pointer; cursor: pointer;
border-radius: 6px; border-radius: 6px;
font-weight: 500; font-weight: 500;
transition: background-color 0.2s ease; transition: background-color 0.2s ease;
} }
button:hover { button:hover {
background-color: #0056b3; background-color: #0056b3;
} }
/* Add spacing between project sections */ /* Add spacing between project sections */
#main > * + * { #main > * + * {
margin-top: 2rem; margin-top: 2rem;
} }
</style> </style>

View File

@ -1,32 +1,40 @@
<template> <template>
<div> <div>
<header> <header>
<HeaderCanvas :project-id="1" /> <HeaderCanvas :project-id="1" />
</header> </header>
</div> </div>
<div> <div>
<h1 class="page-title">PAGE CANVAS</h1> <h1 class="page-title">PAGE CANVAS</h1>
<p class="canvas-help-text"> <p class="canvas-help-text">
Cliquez sur un champ du tableau pour afficher son contenu en détail ci-dessous. Cliquez sur un champ du tableau pour afficher son contenu en détail
</p> ci-dessous.
<LeanCanvas :is-admin=isAdmin />
<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> </p>
</div> <LeanCanvas :is-admin="isAdmin" />
<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> </div>
</template> </template>
<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 /*, defineProps*/ } from "vue";
import { axiosInstance } from "@/services/api.ts"; import { axiosInstance } from "@/services/api.ts";
const IS_MOCK_MODE = true; const IS_MOCK_MODE = true;
@ -41,86 +49,93 @@ const props = defineProps<{
is_admin = token.includes("MyINPulse-admin") is_admin = token.includes("MyINPulse-admin")
*/ */
const isAdmin = 0 const isAdmin = 0;
// Variables pour les informations de l'administrateur // Variables pour les informations de l'administrateur
const admin = ref({ const admin = ref({
idUser: 0, idUser: 0,
userSurname: "", userSurname: "",
userName: "", userName: "",
primaryMail: "", primaryMail: "",
secondaryMail: "", secondaryMail: "",
phoneNumber: "" phoneNumber: "",
}); });
const mockAdminData = { const mockAdminData = {
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 // 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 { 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(`/shared/projects/admin/${projectId}`); const response = await axiosInstance.get(
admin.value = response.data; `/shared/projects/admin/${projectId}`
} catch (error) { );
console.error("Erreur lors de la récupération des données de l'administrateur :", error); 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 // Appeler la fonction fetch au montage du composant
onMounted(() => { onMounted(() => {
const projectId = 1; const projectId = 1;
fetchAdminData(projectId); fetchAdminData(projectId);
}); });
</script> </script>
<style scoped> <style scoped>
.page-title { .page-title {
text-align: center; text-align: center;
font-size: 2.5rem; font-size: 2.5rem;
margin-top: 20px; margin-top: 20px;
} }
.canvas-help-text { .canvas-help-text {
text-align: center; text-align: center;
font-size: 0.7rem; font-size: 0.7rem;
color: #666; color: #666;
} }
.info-box { .info-box {
background-color: #f9f9f9; background-color: #f9f9f9;
padding: 15px; padding: 15px;
border-radius: 8px; border-radius: 8px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
width: 30%; width: 30%;
max-width: 600px; max-width: 600px;
margin: 20px auto; margin: 20px auto;
} }
.info-box p { .info-box p {
font-size: 16px; font-size: 16px;
line-height: 1.5; line-height: 1.5;
color: #333; color: #333;
} }
.info-box a { .info-box a {
color: #007bff; color: #007bff;
text-decoration: none; text-decoration: none;
} }
.info-box a:hover { .info-box a:hover {
text-decoration: underline; text-decoration: underline;
} }
</style> </style>

View File

@ -1,167 +1,162 @@
<template> <template>
<form class="add-project-form" @submit.prevent="submitForm"> <form class="add-project-form" @submit.prevent="submitForm">
<h2>Ajouter un projet</h2> <h2>Ajouter un projet</h2>
<div class="form-group"> <div class="form-group">
<label for="name">Nom du projet</label> <label for="name">Nom du projet</label>
<input <input id="name" v-model="form.name" type="text" required />
id="name" </div>
v-model="form.name"
type="text"
required
/>
</div>
<h3>Entrepreneur</h3> <h3>Entrepreneur</h3>
<div class="form-group"> <div class="form-group">
<label for="founderName">Nom</label> <label for="founderName">Nom</label>
<input <input
id="founderName" id="founderName"
v-model="form.founder.userName" v-model="form.founder.userName"
type="text" type="text"
required required
/> />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="founderSurname">Prénom</label> <label for="founderSurname">Prénom</label>
<input <input
id="founderSurname" id="founderSurname"
v-model="form.founder.userSurname" v-model="form.founder.userSurname"
type="text" type="text"
required required
/> />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="founderPrimaryMail">Email Principal</label> <label for="founderPrimaryMail">Email Principal</label>
<input <input
id="founderPrimaryMail" id="founderPrimaryMail"
v-model="form.founder.primaryMail" v-model="form.founder.primaryMail"
type="email" type="email"
required required
/> />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="founderSecondaryMail">Email Secondaire</label> <label for="founderSecondaryMail">Email Secondaire</label>
<input <input
id="founderSecondaryMail" id="founderSecondaryMail"
v-model="form.founder.secondaryMail" v-model="form.founder.secondaryMail"
type="email" type="email"
/> />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="founderPhoneNumber">Numéro de téléphone</label> <label for="founderPhoneNumber">Numéro de téléphone</label>
<input <input
id="founderPhoneNumber" id="founderPhoneNumber"
v-model="form.founder.phoneNumber" v-model="form.founder.phoneNumber"
type="tel" type="tel"
required required
/> />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="founderSchool">École</label> <label for="founderSchool">École</label>
<input <input
id="founderSchool" id="founderSchool"
v-model="form.founder.school" v-model="form.founder.school"
type="text" type="text"
required required
/> />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="founderCourse">Département</label> <label for="founderCourse">Département</label>
<input <input
id="founderCourse" id="founderCourse"
v-model="form.founder.course" v-model="form.founder.course"
type="text" type="text"
required required
/> />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="founderSneeStatus">Statut étudiant entrepreneur</label> <label for="founderSneeStatus">Statut étudiant entrepreneur</label>
<input <input
id="founderSneeStatus" id="founderSneeStatus"
v-model="form.founder.sneeStatus" v-model="form.founder.sneeStatus"
type="checkbox" type="checkbox"
/> />
</div> </div>
<button type="submit">Soumettre</button> <button type="submit">Soumettre</button>
</form> </form>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue"; import { ref } from "vue";
import { postApi } from "@/services/api"; import { postApi } from "@/services/api";
const form = ref({ const form = ref({
name: '', name: "",
founder: { founder: {
userSurname: '', userSurname: "",
userName: '', userName: "",
primaryMail: '', primaryMail: "",
secondaryMail: '', secondaryMail: "",
phoneNumber: '', phoneNumber: "",
school: '', school: "",
course: '', course: "",
sneeStatus: false sneeStatus: false,
} },
}); });
function submitForm() { function submitForm() {
postApi("/entrepreneur/projects/request", form.value); postApi("/entrepreneur/projects/request", form.value);
} }
</script> </script>
<style scoped> <style scoped>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap'); @import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap");
.add-project-form { .add-project-form {
font-family: 'Inter', sans-serif; font-family: "Inter", sans-serif;
max-width: 600px; max-width: 600px;
margin: 0 auto; margin: 0 auto;
padding: 20px; padding: 20px;
background: #fff; background: #fff;
border-radius: 10px; border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
} }
/* Le reste reste inchangé */ /* Le reste reste inchangé */
h2 { h2 {
margin-bottom: 20px; margin-bottom: 20px;
font-size: 24px; font-size: 24px;
color: #333; color: #333;
} }
h3 { h3 {
margin-top: 20px; margin-top: 20px;
font-size: 20px; font-size: 20px;
color: #555; color: #555;
} }
.form-group { .form-group {
margin-bottom: 15px; margin-bottom: 15px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
label { label {
font-weight: 500; font-weight: 500;
margin-bottom: 5px; margin-bottom: 5px;
} }
input { input {
padding: 8px; padding: 8px;
border-radius: 5px; border-radius: 5px;
border: 1px solid #ccc; border: 1px solid #ccc;
font-size: 1em; font-size: 1em;
} }
input[type="checkbox"] { input[type="checkbox"] {
width: auto; width: auto;
margin-right: 10px; margin-right: 10px;
} }
button { button {
background-color: #4caf50; background-color: #4caf50;
color: white; color: white;
padding: 10px 15px; padding: 10px 15px;
@ -171,20 +166,20 @@
font-size: 1em; font-size: 1em;
width: 100%; width: 100%;
font-weight: 500; font-weight: 500;
} }
button:hover { button:hover {
background-color: #45a049; background-color: #45a049;
} }
button:active { button:active {
background-color: #388e3c; background-color: #388e3c;
} }
input[type="text"]:focus, input[type="text"]:focus,
input[type="email"]:focus, input[type="email"]:focus,
input[type="tel"]:focus { input[type="tel"]:focus {
border-color: #4CAF50; border-color: #4caf50;
outline: none; outline: none;
} }
</style> </style>

View File

@ -15,13 +15,11 @@ import ErrorModal from "@/components/errorModal.vue";
</template> </template>
<style scoped> <style scoped>
.error-wrapper {
.error-wrapper{ position: absolute;
position: absolute; left: 70%;
left: 70%; /*background-color: blue;*/
/*background-color: blue;*/ height: 100%;
height: 100%; width: 30%;
width: 30%;
} }
</style> </style>