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">
import { /*RouterLink,*/ RouterView } from 'vue-router'
import { /*RouterLink,*/ RouterView } from "vue-router";
import ErrorWrapper from "@/views/errorWrapper.vue";
</script>
<template>
<Header />
<Header />
<ErrorWrapper />
<!--<RouterLink to="/">Home</RouterLink> | -->
<!--<RouterLink to="/canvas">Canvas</RouterLink> -->
<RouterView />
</template>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,16 +1,16 @@
<template>
<div class="canvas container-fluid">
<CanvasItem
v-for="(item, index) in items"
:key="index"
:title="item.title"
:title-text="item.title_text"
:description="item.description"
:project-id="item.projectId"
:class="['canvas-item', item.class, 'card', 'shadow', 'p-3']"
:is-admin= props.isAdmin
/>
</div>
<div class="canvas container-fluid">
<CanvasItem
v-for="(item, index) in items"
:key="index"
:title="item.title"
:title-text="item.title_text"
:description="item.description"
:project-id="item.projectId"
:class="['canvas-item', item.class, 'card', 'shadow', 'p-3']"
:is-admin="props.isAdmin"
/>
</div>
</template>
<script setup lang="ts">
@ -18,65 +18,150 @@ import { ref, onMounted } from "vue";
import CanvasItem from "@/components/canvas/CanvasItem.vue";
const props = defineProps<{
isAdmin: number;
isAdmin: number;
}>();
const items = ref([
{ projectId: 1, title: 1, title_text: "1. Problème", description: "3 problèmes essentiels à résoudre pour le client", class: "Probleme" },
{ projectId: 1, title: 2, title_text: "2. Segments", description: "Les segments de clientèle visés", class: "Segments" },
{ projectId: 1, title: 3, title_text: "3. Valeur", description: "La proposition de valeur", class: "Valeur" },
{ projectId: 1, title: 4, title_text: "4. Solution", description: "Les solutions proposées", class: "Solution" },
{ projectId: 1, title: 5, title_text: "5. Avantage", description: "Les avantages concurrentiels", class: "Avantage" },
{ projectId: 1, title: 6, title_text: "6. Canaux", description: "Les canaux de distribution", class: "Canaux" },
{ projectId: 1, title: 7, title_text: "7. Indicateurs", description: "Les indicateurs clés de performance", class: "Indicateurs" },
{ projectId: 1, title: 8, title_text: "8. Coûts", description: "Les coûts associés", class: "Couts" },
{ projectId: 1, title: 9, title_text: "9. Revenus", description: "Les sources de revenus", class: "Revenus" }
{
projectId: 1,
title: 1,
title_text: "1. Problème",
description: "3 problèmes essentiels à résoudre pour le client",
class: "Probleme",
},
{
projectId: 1,
title: 2,
title_text: "2. Segments",
description: "Les segments de clientèle visés",
class: "Segments",
},
{
projectId: 1,
title: 3,
title_text: "3. Valeur",
description: "La proposition de valeur",
class: "Valeur",
},
{
projectId: 1,
title: 4,
title_text: "4. Solution",
description: "Les solutions proposées",
class: "Solution",
},
{
projectId: 1,
title: 5,
title_text: "5. Avantage",
description: "Les avantages concurrentiels",
class: "Avantage",
},
{
projectId: 1,
title: 6,
title_text: "6. Canaux",
description: "Les canaux de distribution",
class: "Canaux",
},
{
projectId: 1,
title: 7,
title_text: "7. Indicateurs",
description: "Les indicateurs clés de performance",
class: "Indicateurs",
},
{
projectId: 1,
title: 8,
title_text: "8. Coûts",
description: "Les coûts associés",
class: "Couts",
},
{
projectId: 1,
title: 9,
title_text: "9. Revenus",
description: "Les sources de revenus",
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 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)
})
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>
@import "@/components/canvas/style-project.css";
.canvas {
display: grid;
grid-template-columns: repeat(10, 1fr);
grid-template-rows: repeat(6, 1fr);
gap: 12px;
padding: 30px;
/*background-color: #f8f9fa;*/
position: relative;
height: 90vh;
overflow: auto;
display: grid;
grid-template-columns: repeat(10, 1fr);
grid-template-rows: repeat(6, 1fr);
gap: 12px;
padding: 30px;
/*background-color: #f8f9fa;*/
position: relative;
height: 90vh;
overflow: auto;
}
.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; }
.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;
/*background-color: white;*/
border: 1px solid #dee2e6;
border-radius: 0.5rem;
}
</style>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,32 +1,40 @@
<template>
<div>
<header>
<HeaderCanvas :project-id="1" />
</header>
<header>
<HeaderCanvas :project-id="1" />
</header>
</div>
<div>
<h1 class="page-title">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 :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 class="canvas-help-text">
Cliquez sur un champ du tableau pour afficher son contenu en détail
ci-dessous.
</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>
</template>
<script setup lang="ts">
<script setup lang="ts">
import HeaderCanvas from "../components/canvas/HeaderCanvas.vue";
import LeanCanvas from '../components/canvas/LeanCanvas.vue';
import { ref, onMounted /*, defineProps*/} from "vue";
import LeanCanvas from "../components/canvas/LeanCanvas.vue";
import { ref, onMounted /*, defineProps*/ } from "vue";
import { axiosInstance } from "@/services/api.ts";
const IS_MOCK_MODE = true;
@ -41,86 +49,93 @@ const props = defineProps<{
is_admin = token.includes("MyINPulse-admin")
*/
const isAdmin = 0
const isAdmin = 0;
// Variables pour les informations de l'administrateur
const admin = ref({
idUser: 0,
userSurname: "",
userName: "",
primaryMail: "",
secondaryMail: "",
phoneNumber: ""
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"
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;
}
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);
}
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);
const projectId = 1;
fetchAdminData(projectId);
});
</script>
<style scoped>
.page-title {
text-align: center;
font-size: 2.5rem;
margin-top: 20px;
text-align: center;
font-size: 2.5rem;
margin-top: 20px;
}
.canvas-help-text {
text-align: center;
font-size: 0.7rem;
color: #666;
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;
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;
font-size: 16px;
line-height: 1.5;
color: #333;
}
.info-box a {
color: #007bff;
text-decoration: none;
color: #007bff;
text-decoration: none;
}
.info-box a:hover {
text-decoration: underline;
text-decoration: underline;
}
</style>
</style>

View File

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

View File

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