front_foundation #9
@ -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 />
|
||||
piair
commented
same, I don't think it's really important to keep comments same, I don't think it's really important to keep comments
|
||||
<!--<RouterLink to="/">Home</RouterLink> | -->
|
||||
<!--<RouterLink to="/canvas">Canvas</RouterLink> -->
|
||||
<RouterView />
|
||||
</template>
|
||||
|
||||
|
||||
|
||||
|
@ -1,110 +1,108 @@
|
||||
<template>
|
||||
piair
commented
We must keep the same order for template and script everywhere. We must keep the same order for template and script everywhere.
|
||||
<form class="add-project-form" @submit.prevent="submitProject">
|
||||
<h2>Ajouter un projet</h2>
|
||||
<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="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="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>
|
||||
<div class="form-group">
|
||||
piair
commented
For now, it would be better to hide this altogether, I don't think we will have time to implement it. For now, it would be better to hide this altogether, I don't think we will have time to implement it.
|
||||
<label for="logo">Logo</label>
|
||||
<input
|
||||
id="logo"
|
||||
v-model="project.logo"
|
||||
type="text"
|
||||
placeholder="(à discuter)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button type="submit">Ajouter</button>
|
||||
<button type="submit">Ajouter</button>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { postApi } from "@/services/api.ts";
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { postApi } from "@/services/api.ts";
|
||||
|
||||
const project = ref({
|
||||
const project = ref({
|
||||
projectName: "",
|
||||
creationDate: "",
|
||||
logo: "to be discussed not yet fixed",
|
||||
});
|
||||
});
|
||||
|
||||
function submitProject() {
|
||||
function submitProject() {
|
||||
postApi("/admin/projects/add", project.value);
|
||||
}
|
||||
</script>
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
h2{
|
||||
<style scoped>
|
||||
h2 {
|
||||
piair
commented
The h2 from this file is the same as the h3 from the next (AgendaComponent). It would be better to have a single css file for this part. The h2 from this file is the same as the h3 from the next (AgendaComponent). It would be better to have a single css file for this part.
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
piair
commented
same, button should look the same in the project, so the css shouldn't be scoped here same, button should look the same in the project, so the css shouldn't be scoped here
|
||||
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>
|
||||
|
@ -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[];
|
||||
Theo
commented
où est-ce que les RDV sont récupérés ? où est-ce que les RDV sont récupérés ?
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
h3{
|
||||
h3 {
|
||||
piair
commented
same comment as before, most of the style here is not for this part only, so it should not be here same comment as before, most of the style here is not for this part only, so it should not be here
|
||||
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;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #f0f2f5;
|
||||
padding: 12px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* Table Body Rows */
|
||||
tbody tr {
|
||||
/* 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>
|
@ -1,35 +1,35 @@
|
||||
<script lang="ts" setup>
|
||||
piair
commented
The order changed here The order changed here
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
/*
|
||||
piair
commented
should this commented part really stay should this commented part really stay
|
||||
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>
|
||||
|
||||
piair
commented
This should not be shown to the user This should not be shown to the user
|
||||
<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>
|
||||
|
||||
piair
commented
same, it should be hidden same, it should be hidden
|
||||
<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;
|
||||
}
|
||||
|
||||
piair
commented
global style once again global style once again
|
||||
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>
|
||||
|
@ -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>
|
@ -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">
|
||||
<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) {
|
||||
adnane marked this conversation as resolved
Outdated
piair
commented
We should not use axios.get EVER, it does not send the authentication token. We should not use axios.get EVER, it does not send the authentication token.
|
||||
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",
|
||||
adnane marked this conversation as resolved
Outdated
piair
commented
I don't love the fact that the mock tests are in the code here, it should be better to have a server. It's not a big probleme though I don't love the fact that the mock tests are in the code here, it should be better to have a server. It's not a big probleme though
|
||||
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) => {
|
||||
adnane marked this conversation as resolved
Outdated
piair
commented
I personally hate that, can't we use an error mesage in green ? I personally hate that, can't we use an error mesage in green ?
adnane
commented
ok ok
|
||||
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;
|
||||
}
|
||||
adnane marked this conversation as resolved
Outdated
piair
commented
generic style once again generic style once again
|
||||
|
||||
|
||||
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>
|
@ -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 où 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 où pour quitter le canvas
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@ -52,11 +74,11 @@ import { axiosInstance } from "@/services/api.ts";
|
||||
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 }[]>(
|
||||
adnane marked this conversation as resolved
Outdated
piair
commented
be careful with axiosInstance, but i may be fine be careful with axiosInstance, but i may be fine
|
||||
`/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}`, {
|
||||
adnane marked this conversation as resolved
Outdated
piair
commented
same same
|
||||
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>
|
@ -1,27 +1,31 @@
|
||||
<template>
|
||||
adnane marked this conversation as resolved
piair
commented
Doesn't this header overlap with the main app canvas ? Doesn't this header overlap with the main app canvas ?
|
||||
<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";
|
||||
adnane marked this conversation as resolved
Outdated
piair
commented
I won't talk again about axios I won't talk again about 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>
|
||||
|
@ -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";
|
||||
adnane marked this conversation as resolved
Outdated
piair
commented
Why is bootstrap imported here ? Why is bootstrap imported here ?
|
||||
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>
|
||||
|
@ -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;
|
||||
adnane marked this conversation as resolved
piair
commented
it would be better to use variables for colors, but not a big issue either. it would be better to use variables for colors, but not a big issue either.
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
adnane marked this conversation as resolved
Outdated
piair
commented
We can't let this kind of comments We can't let this kind of comments
|
||||
//createApp(App).use(router).mount('#app');
|
||||
@ -71,7 +70,4 @@ app.use(VueKeyCloak,{
|
||||
} );
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
export { store };
|
||||
|
@ -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",
|
||||
adnane marked this conversation as resolved
piair
commented
Can we find a better name ? Can we find a better name ?
adnane
commented
ok ok
|
||||
name: "JorCproject",
|
||||
component: () => import("../views/JoinOrCreatProjectForEntrep.vue"),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
@ -88,5 +88,4 @@ function deleteApi(
|
||||
.catch(onErrorHandler ?? defaultApiErrorHandler);
|
||||
}
|
||||
|
||||
|
||||
export { axiosInstance, callApi, postApi, deleteApi };
|
||||
|
@ -2,38 +2,37 @@
|
||||
<Header />
|
||||
<error-wrapper></error-wrapper>
|
||||
<div id="container">
|
||||
piair
commented
it's a bit weird to have content here, if this file replaces main.vue, fine bud don"t put content on it it's a bit weird to have content here, if this file replaces main.vue, fine bud don"t put content on it
|
||||
<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 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>
|
||||
<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"
|
||||
/>
|
||||
<PendingProjectComponent
|
||||
v-for="(project, index) in pendingProjects"
|
||||
:key="index"
|
||||
:project-name="project.name"
|
||||
:creation-date="project.creationDate"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<AddProjectForm />
|
||||
</div>
|
||||
|
||||
<AddProjectForm/>
|
||||
</div>
|
||||
|
||||
<Agenda :project-r-d-v="rendezVous" />
|
||||
<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);
|
||||
*/
|
||||
|
||||
piair
commented
what is that for ? what is that for ?
|
||||
|
||||
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 {
|
||||
piair
commented
same comment for CSS same comment for CSS
|
||||
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>
|
@ -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">
|
||||
|
||||
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;
|
||||
adnane marked this conversation as resolved
Outdated
piair
commented
This should not be true This should not be true
adnane
commented
Le fetching n'est pas encore prêt pour passer à la base de données Le fetching n'est pas encore prêt pour passer à la base de données
|
||||
@ -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(
|
||||
adnane marked this conversation as resolved
Outdated
piair
commented
weird to use axiosInstance. weird to use axiosInstance.
|
||||
`/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>
|
@ -1,167 +1,162 @@
|
||||
<template>
|
||||
<form class="add-project-form" @submit.prevent="submitForm">
|
||||
<h2>Ajouter un projet</h2>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<button type="submit">Soumettre</button>
|
||||
</form>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { postApi } from "@/services/api";
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { postApi } from "@/services/api";
|
||||
|
||||
const form = ref({
|
||||
name: '',
|
||||
const form = ref({
|
||||
name: "",
|
||||
founder: {
|
||||
userSurname: '',
|
||||
userName: '',
|
||||
primaryMail: '',
|
||||
secondaryMail: '',
|
||||
phoneNumber: '',
|
||||
school: '',
|
||||
course: '',
|
||||
sneeStatus: false
|
||||
}
|
||||
});
|
||||
userSurname: "",
|
||||
userName: "",
|
||||
primaryMail: "",
|
||||
secondaryMail: "",
|
||||
phoneNumber: "",
|
||||
school: "",
|
||||
course: "",
|
||||
sneeStatus: false,
|
||||
},
|
||||
});
|
||||
|
||||
function submitForm() {
|
||||
function submitForm() {
|
||||
postApi("/entrepreneur/projects/request", form.value);
|
||||
}
|
||||
</script>
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap');
|
||||
<style scoped>
|
||||
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap");
|
||||
adnane marked this conversation as resolved
piair
commented
this should be in a specific css file. this should be in a specific css file.
|
||||
|
||||
.add-project-form {
|
||||
font-family: 'Inter', sans-serif;
|
||||
.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);
|
||||
}
|
||||
}
|
||||
|
||||
adnane marked this conversation as resolved
piair
commented
same comment, it should be gloabal same comment, it should be gloabal
|
||||
/* 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>
|
||||
|
@ -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;*/
|
||||
piair
commented
why ? why ?
|
||||
height: 100%;
|
||||
width: 30%;
|
||||
}
|
||||
</style>
|
||||
|
I don't like the comments in the middle of the line, it would be better to delete this part.