fix: api integration

This commit is contained in:
Mohamed Maoulainine Maoulainine 2025-05-02 01:26:08 +02:00
parent 49e52e1826
commit c3ad092512
7 changed files with 374 additions and 130 deletions

View File

@ -22,16 +22,16 @@ class Project {
this._idProject = value;
}
get projectName(): string | undefined {
return this._projectName;
get projectName(): string {
return this._projectName ?? "";
}
set projectName(value: string | undefined) {
this._projectName = value;
}
get creationDate(): string | undefined {
return this._creationDate;
get creationDate(): string {
return this._creationDate ?? "";
}
set creationDate(value: string | undefined) {
@ -77,6 +77,13 @@ class Project {
status: this.status,
};
}
toCreatePayload() {
return {
projectName: this.projectName,
logo: this.logo,
};
}
}
export default Project;

View File

@ -12,24 +12,13 @@
/>
</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)"
placeholder="(Description)"
/>
</div>
@ -39,16 +28,17 @@
<script setup lang="ts">
import { ref } from "vue";
import { postApi } from "@/services/api.ts";
import Project from "@/ApiClasses/Project";
import { addProjectManually } from "@/services/Apis/Admin";
const project = ref({
projectName: "",
creationDate: "",
logo: "to be discussed not yet fixed",
});
const project = ref(new Project({}));
function submitProject() {
postApi("/admin/projects/add", project.value);
addProjectManually(
project.value.toCreatePayload(),
(res) => console.log("Success:", res.data),
(err) => console.error("Error:", err)
);
}
</script>

View File

@ -15,26 +15,29 @@
<script setup lang="ts">
import { defineProps } from "vue";
import { postApi } from "@/services/api";
import { addNewMessage, color } from "@/services/popupDisplayer";
import { decidePendingProject } from "@/services/Apis/Admin";
import ProjectDecision from "@/ApiClasses/ProjectDecision";
const props = defineProps<{
projectId?: number;
projectName: string;
creationDate: string;
adminId?: number;
}>();
const URI = "/admin/projects/pending/decision";
const sendDecision = (isAccepted: boolean) => {
const decision = new ProjectDecision({
projectId: props.projectId,
adminId: props.adminId,
isAccepted,
});
const sendDecision = (decision: "true" | "false") => {
postApi(
URI,
{
projectName: props.projectName,
decidePendingProject(
decision,
},
() => {
addNewMessage(
`Projet ${props.projectName} ${decision === "true" ? "accepté" : "refusé"}`,
`Projet ${props.projectName} ${isAccepted ? "accepté" : "refusé"}`,
color.Green
);
},
@ -45,8 +48,8 @@ const sendDecision = (decision: "true" | "false") => {
);
};
const acceptProject = () => sendDecision("true");
const refuseProject = () => sendDecision("false");
const acceptProject = () => sendDecision(true);
const refuseProject = () => sendDecision(false);
</script>
<style scoped>

View File

@ -0,0 +1,228 @@
<!-- <template>
<section class="pending-requests">
<h2>Comptes en attente</h2>
<ul v-if="pendingAccounts.length">
<li v-for="account in pendingAccounts" :key="account.userId">
{{ account.userName }} ({{ account.email }})
<button @click="validateAccount(account.userId)">Valider</button>
</li>
</ul>
<p v-else>Aucun compte en attente</p>
<h2>Demandes de participation aux projets</h2>
<ul v-if="joinRequests.length">
<li v-for="request in joinRequests" :key="request.joinRequestId">
{{ request.userName }} veut rejoindre le projet {{ request.projectName }}
<button @click="decideJoinRequest(request.joinRequestId, true)">Accepter</button>
<button @click="decideJoinRequest(request.joinRequestId, false)">Refuser</button>
</li>
</ul>
<p v-else>Aucune demande de participation</p>
</section>
</template> -->
<template>
<section class="section">
<h2>Comptes en attente</h2>
<div v-if="pendingAccounts.length">
<div
v-for="account in pendingAccounts"
:key="account.userId"
class="request-item"
>
<div class="request-info">
{{ account.userName }} ({{ account.email }})
</div>
<div class="request-buttons">
<button
class="accept"
@click="validateAccount(account.userId)"
>
Valider
</button>
</div>
</div>
</div>
<div v-else class="empty-message">Aucun compte en attente</div>
</section>
<section class="section">
<h2>Demandes de participation aux projets</h2>
<div v-if="joinRequests.length">
<div
v-for="request in joinRequests"
:key="request.joinRequestId"
class="request-item"
>
<div class="request-info">
{{ request.userName }} veut rejoindre le projet "{{
request.projectName
}}"
</div>
<div class="request-buttons">
<button
class="accept"
@click="decideJoinRequest(request.joinRequestId, true)"
>
Accepter
</button>
<button
class="reject"
@click="decideJoinRequest(request.joinRequestId, false)"
>
Refuser
</button>
</div>
</div>
</div>
<div v-else class="empty-message">Aucune demande de participation</div>
</section>
</template>
<script setup lang="ts">
import { ref, onMounted } from "vue";
import {
getPendingAccounts,
validateUserAccount,
getPendingProjectJoinRequests,
decideProjectJoinRequest,
} from "@/services/Apis/Admin";
type PendingAccount = {
userId: number;
userName: string;
email: string;
};
type JoinRequest = {
joinRequestId: number;
userName: string;
projectName: string;
};
const pendingAccounts = ref<PendingAccount[]>([]);
const joinRequests = ref<JoinRequest[]>([]);
function fetchAccounts() {
getPendingAccounts(
(res) => (pendingAccounts.value = res.data),
(err) => {
console.error("Erreur lors du chargement des comptes", err);
}
);
}
function fetchJoinRequests() {
getPendingProjectJoinRequests(
(res) => (joinRequests.value = res.data),
(err) => {
console.error("Erreur lors du chargement des demandes", err);
}
);
}
function validateAccount(userId: number) {
validateUserAccount(userId, () => {
pendingAccounts.value = pendingAccounts.value.filter(
(a) => a.userId !== userId
);
});
}
function decideJoinRequest(joinRequestId: number, isAccepted: boolean) {
decideProjectJoinRequest(joinRequestId, { isAccepted }, () => {
joinRequests.value = joinRequests.value.filter(
(r) => r.joinRequestId !== joinRequestId
);
});
}
onMounted(() => {
fetchAccounts();
fetchJoinRequests();
});
</script>
<style>
.section {
background: #ffffff;
border-radius: 16px;
padding: 1.5rem;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
margin-bottom: 2rem;
}
.section h2 {
font-size: 1.4rem;
color: #2c3e50;
border-bottom: 2px solid #ddd;
padding-bottom: 0.5rem;
margin-bottom: 1rem;
}
.request-item {
display: flex;
justify-content: space-between;
align-items: center;
border: 1px solid #e0e0e0;
border-radius: 12px;
background: linear-gradient(to right, #f8f9fb, #ffffff);
padding: 1rem 1.25rem;
margin-bottom: 1rem;
transition: box-shadow 0.2s ease;
}
.request-item:hover {
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.06);
}
.request-info {
flex: 1;
font-size: 1rem;
color: #333;
}
.request-buttons {
display: flex;
gap: 0.75rem;
}
.request-buttons button {
padding: 0.45rem 1rem;
font-size: 0.9rem;
border: none;
border-radius: 8px;
color: #fff;
font-weight: 500;
cursor: pointer;
transition:
background-color 0.2s ease,
transform 0.2s ease;
}
.request-buttons button:hover {
transform: translateY(-1px);
}
button.accept {
background-color: #4caf50;
}
button.accept:hover {
background-color: #3e8e41;
}
button.reject {
background-color: #e74c3c;
}
button.reject:hover {
background-color: #c0392b;
}
.empty-message {
font-style: italic;
color: #777;
padding: 1rem 0;
}
</style>

View File

@ -1,6 +1,6 @@
import { type AxiosError, type AxiosResponse } from "axios";
import Project from "@/ApiClasses/Project";
import Report from "@/ApiClasses/Repport";
import ProjectDecision from "@/ApiClasses/ProjectDecision";
import {
axiosInstance,
defaultApiErrorHandler,
@ -53,51 +53,50 @@ function validateUserAccount(
});
}
// function getPendingProjectJoinRequests( // Not yet implemented [cite: 3]
// onSuccessHandler?: (response: AxiosResponse) => void,
// onErrorHandler?: (error: AxiosError) => void
// ): void {
// axiosInstance
// .get("/admin/request-join")
// .then((response) => {
// if (onSuccessHandler) {
// onSuccessHandler(response);
// } else {
// defaultApiSuccessHandler(response);
// }
// })
// .catch((error: AxiosError) => {
// if (onErrorHandler) {
// onErrorHandler(error);
// } else {
// defaultApiErrorHandler(error);
// }
// });
// }
// function decideProjectJoinRequest( // Not yet implemented [cite: 3]
// joinRequestId: number,
// decision: { isAccepted: boolean },
// onSuccessHandler?: (response: AxiosResponse) => void,
// onErrorHandler?: (error: AxiosError) => void
// ): void {
// axiosInstance
// .post(`/admin/request-join/decision/${joinRequestId}`, decision)
// .then((response) => {
// if (onSuccessHandler) {
// onSuccessHandler(response);
// } else {
// defaultApiSuccessHandler(response);
// }
// })
// .catch((error: AxiosError) => {
// if (onErrorHandler) {
// onErrorHandler(error);
// } else {
// defaultApiErrorHandler(error);
// }
// });
// }
function getPendingProjectJoinRequests( // Not yet implemented
onSuccessHandler?: (response: AxiosResponse) => void,
onErrorHandler?: (error: AxiosError) => void
): void {
axiosInstance
.get("/admin/request-join")
.then((response) => {
if (onSuccessHandler) {
onSuccessHandler(response);
} else {
defaultApiSuccessHandler(response);
}
})
.catch((error: AxiosError) => {
if (onErrorHandler) {
onErrorHandler(error);
} else {
defaultApiErrorHandler(error);
}
});
}
function decideProjectJoinRequest( // Not yet implemented
joinRequestId: number,
decision: { isAccepted: boolean },
onSuccessHandler?: (response: AxiosResponse) => void,
onErrorHandler?: (error: AxiosError) => void
): void {
axiosInstance
.post(`/admin/request-join/decision/${joinRequestId}`, decision)
.then((response) => {
if (onSuccessHandler) {
onSuccessHandler(response);
} else {
defaultApiSuccessHandler(response);
}
})
.catch((error: AxiosError) => {
if (onErrorHandler) {
onErrorHandler(error);
} else {
defaultApiErrorHandler(error);
}
});
}
function getAdminProjects(
onSuccessHandler?: (response: AxiosResponse) => void,
@ -121,27 +120,20 @@ function getAdminProjects(
});
}
type ProjectCreatePayload = {
projectName: string;
logo?: string;
};
function addProjectManually(
projectDetails: Project, // Replace 'any' with a proper type for project details if available
onSuccessHandler?: (response: AxiosResponse) => void,
onErrorHandler?: (error: AxiosError) => void
payload: ProjectCreatePayload,
onSuccess?: (response: AxiosResponse) => void,
onError?: (error: AxiosError) => void
): void {
axiosInstance
.post("/admin/projects", projectDetails)
.then((response) => {
if (onSuccessHandler) {
onSuccessHandler(response);
} else {
defaultApiSuccessHandler(response);
}
})
.catch((error: AxiosError) => {
if (onErrorHandler) {
onErrorHandler(error);
} else {
defaultApiErrorHandler(error);
}
});
.post("/admin/projects", payload)
.then((response) => onSuccess?.(response))
.catch((error: AxiosError) => onError?.(error));
}
function getPendingProjects(
@ -167,13 +159,12 @@ function getPendingProjects(
}
function decidePendingProject(
pendingProjectId: number,
decision: { projectId: number; adminId: number; isAccepted: boolean }, // Replace 'any' with a proper type for project decision if available
decision: ProjectDecision,
onSuccessHandler?: (response: AxiosResponse) => void,
onErrorHandler?: (error: AxiosError) => void
): void {
axiosInstance
.post(`/admin/projects/pending/decision/${pendingProjectId}`, decision)
.post(`/admin/projects/pending/decision`, decision.toObject())
.then((response) => {
if (onSuccessHandler) {
onSuccessHandler(response);
@ -308,11 +299,11 @@ function grantAdminRights(
export {
axiosInstance,
// requestJoinProject, // Not yet implemented [cite: 4]
//requestJoinProject, // Not yet implemented [cite: 4]
getPendingAccounts,
validateUserAccount,
// getPendingProjectJoinRequests, // Not yet implemented [cite: 3]
// decideProjectJoinRequest, // Not yet implemented [cite: 3]
getPendingProjectJoinRequests, // Not yet implemented [cite: 3]
decideProjectJoinRequest, // Not yet implemented [cite: 3]
getAdminProjects,
addProjectManually,
getPendingProjects,

View File

@ -1,6 +1,7 @@
<template>
<Header />
<error-wrapper></error-wrapper>
<error-wrapper />
<div id="container">
<div id="main">
<h3>Projet en cours</h3>
@ -13,18 +14,16 @@
:project-id="0"
/>
<div id="main">
<h3>Projet en attente</h3>
<PendingProjectComponent
v-for="(project, index) in pendingProjects"
:key="index"
:project-name="project.name"
:project-name="project.projectName"
:creation-date="project.creationDate"
/>
</div>
<AddProjectForm />
<PendingRequestsManager />
</div>
<Agenda :project-r-d-v="rendezVous" />
@ -33,16 +32,17 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { getAdminProjects, getPendingProjects } from "@/services/Apis/Admin";
import { getProjectEntrepreneurs } from "@/services/Apis/Shared";
import { type AxiosError, type AxiosResponse } from "axios";
import Header from "../components/HeaderComponent.vue";
//import Agenda from "../components/AdminAppointments.vue";
import Agenda from "../components/AgendaComponent.vue";
import ProjectComp from "../components/ProjectComponent.vue";
import PendingProjectComponent from "@/components/PendingProjectComponent.vue";
import AddProjectForm from "@/components/AddProjectForm.vue";
import { getAdminProjects } from "@/services/Apis/Admin";
import { getProjectEntrepreneurs } from "@/services/Apis/Shared";
import { type AxiosError, type AxiosResponse } from "axios";
import PendingRequestsManager from "@/components/PendingRequestsManager.vue";
import Project from "@/ApiClasses/Project";
import UserEntrepreneur from "@/ApiClasses/UserEntrepreneur";
@ -75,7 +75,7 @@ const fetchProjects = () => {
);
const projectPromises = projectList.map(
(project: { idProject: number; projectName: string }) =>
(project: Project) =>
new Promise<void>((resolve) => {
getProjectEntrepreneurs(
project.idProject!,
@ -85,7 +85,6 @@ const fetchProjects = () => {
new UserEntrepreneur(m).userName ||
"Unknown"
);
projects.value.push({
name:
project.projectName ||
@ -93,7 +92,6 @@ const fetchProjects = () => {
link: `/project/${project.idProject}`,
members,
});
resolve();
},
() => {
@ -111,7 +109,6 @@ const fetchProjects = () => {
);
Promise.all(projectPromises).catch(() => {
// fallback in case member fetches fail
projects.value = fallbackProjects;
});
},
@ -124,10 +121,39 @@ const fetchProjects = () => {
onMounted(fetchProjects);
const pendingProjects = ref([
{ name: "l'eau", creationDate: "26-02-2024" },
{ name: "l'air", creationDate: "09-03-2023" },
]);
const pendingProjects = ref<Project[]>([]);
const mockPendingProjects = [
new Project({ projectName: "l'eau", creationDate: "26-02-2024" }),
new Project({ projectName: "l'air", creationDate: "09-03-2023" }),
];
onMounted(() => {
getPendingProjects(
(response) => {
pendingProjects.value = response.data.map(
(p: Project) =>
new Project({
projectName: p.projectName,
creationDate: p.creationDate,
})
);
},
(error) => {
console.error(
"Failed to fetch pending projects, using mock:",
error
);
pendingProjects.value = mockPendingProjects.map(
(p) =>
new Project({
projectName: p.projectName,
creationDate: p.creationDate,
})
);
}
);
});
const rendezVous = ref([
{ projectName: "Projet Alpha", date: "2025-03-10", lieu: "P106" },
@ -176,7 +202,6 @@ button:hover {
background-color: #0056b3;
}
/* Add spacing between project sections */
#main > * + * {
margin-top: 2rem;
}