Compare commits
241 Commits
openapi_in
...
front_foun
Author | SHA1 | Date | |
---|---|---|---|
47be7d340b | |||
232d10b164 | |||
bc7ce888ad | |||
ed67a3734a | |||
95eb154556 | |||
19fef63b0e | |||
1fd95265ea | |||
3ef2d8a198 | |||
6b49bbbe57 | |||
4c15cab607 | |||
abfe92bc87 | |||
85b4fe6a4c | |||
f2448a029f | |||
cef4daef15 | |||
f5aba70017 | |||
27adc81ddc | |||
48f14e8a04 | |||
d4533ea725 | |||
7fc06035c7 | |||
1b559f29b7 | |||
63327bc312 | |||
f0cef41e2b | |||
7f16cdc86f | |||
72d6f49995 | |||
695ec5d9b8 | |||
0abafb4f7f | |||
3cd63e78e9 | |||
255af7ee7f | |||
3b308cfa6d | |||
d039105f0a | |||
0a15dbbf2d | |||
d1fce63ac5 | |||
d9aaa225aa | |||
d31bf259dd | |||
43b40c9432 | |||
e84f69c21a | |||
cc1fc9b45b | |||
c76e83f2bf | |||
0d0ec255a5 | |||
e0c43a5c95 | |||
1f0f9196c4 | |||
40e577ef07 | |||
60302c44d2 | |||
a6e4f80a01 | |||
02bff19de0 | |||
ae36549de9 | |||
b1a4c874ec | |||
829baac85e | |||
2e9d841709 | |||
25235f418a | |||
13845394e3 | |||
73aac1875a | |||
c3ad092512 | |||
f4589c6306 | |||
6004bce4e8 | |||
0730275e75 | |||
4356a01e4a | |||
592331236e | |||
49e52e1826 | |||
efcea7f680 | |||
8154814805 | |||
7d41da97de | |||
35f314498f | |||
f46c3756f0 | |||
92696c3e16 | |||
5130c00796 | |||
5183a088e7 | |||
8ec569e6ff | |||
b503cae235 | |||
fcf4e1c01d | |||
4f90da69f3 | |||
0fe64fd844 | |||
eb302268ba | |||
3f18304028 | |||
bbb4debcd8 | |||
6f7fc70c4c | |||
4044a95dd1 | |||
aee8e8797c | |||
3d57ecb01a | |||
1f91ab72d8 | |||
a4e13b0f0a | |||
b116771375 | |||
cbef042e97 | |||
63f6f16d83 | |||
864bbbb9fd | |||
e890a03a48 | |||
351727f3d5 | |||
b5637e4552 | |||
8024b8070b | |||
008e003207 | |||
820c979c94 | |||
2ebea35e5d | |||
c3dded1e05 | |||
8b48a55bf0 | |||
32de0d6ca8 | |||
7534d0d9c6 | |||
a0cbd5e324 | |||
09eeaacfa6 | |||
de09ab2891 | |||
6ae50f9cf7 | |||
0199e9d0dc | |||
05a56dc022 | |||
9f4596d0ba | |||
be73815861 | |||
6d84b3ff93 | |||
549028b1d0 | |||
3de1ec71ff | |||
a895b0afda | |||
bb55209946 | |||
a8e22de4a3 | |||
f9ee12ab6f | |||
4d3aae1249 | |||
fa96bd2b0d | |||
3673aa379c | |||
b672b2e9f9 | |||
193876e51c | |||
b8c7c6f587 | |||
bee47473d5 | |||
e7739af80b | |||
32557f8f87 | |||
8ee06b93a6 | |||
fcc20d1f42 | |||
28b0e69da1 | |||
6861d07dfc | |||
6226c9f632 | |||
7fce831c75 | |||
8403bc0592 | |||
5edcf9ffc8 | |||
5615b0fb11 | |||
8a13993d8a | |||
561f6d16b3 | |||
7199e7ebbf | |||
9762ca27fb | |||
ca8c5d9209 | |||
4b6d501adc | |||
08706af6c2 | |||
01f062211a | |||
bca88a7b20 | |||
c60fb8945b | |||
e20556ed0f | |||
d9c5f7bacf | |||
fdae3e4c04 | |||
36db3c2968 | |||
a0eeb6715e | |||
6d875d9df1 | |||
7c8f3ba36a | |||
832539f43b | |||
dfea20b9c4 | |||
8b863ee4b1 | |||
f96872fb6b | |||
0140672812 | |||
7df2c768c8 | |||
6b3cb2610d | |||
6029457735 | |||
ba99b3c2b0 | |||
31d82f6271 | |||
37e631a096 | |||
6306a00eca | |||
55112c8508 | |||
676f1204cb | |||
c4ba7646d5 | |||
4e1908d528 | |||
03bbc77e8a | |||
ad1fd45bed | |||
f0c4a3a10d | |||
70658e4fb9 | |||
2b31753265 | |||
f8991e90ab | |||
66be0baca6 | |||
60290956ec | |||
b9647ce36e | |||
8c4b9ceb9d | |||
84d8d4523b | |||
647812576e | |||
2dfee66958 | |||
7e1271cfe2 | |||
801ecb3817 | |||
cc89d4c79f | |||
adf9a93e2e | |||
37d8bcc719 | |||
385c5cd8d0 | |||
b672dd200c | |||
9e1f568ea4 | |||
aaa6e46d0c | |||
2b1666c949 | |||
0c724cae7f | |||
6de45801d2 | |||
03897e1139 | |||
9b9cfbdb2e | |||
00a733c03b | |||
5b6b647697 | |||
3dc8131c33 | |||
7c271d8c47 | |||
ead11215ba | |||
259d56271c | |||
b9f3bbbe15 | |||
14a2a59786 | |||
0ae6e7dfda | |||
15ccb5630a | |||
4ec292cca7 | |||
14a953536a | |||
288f983816 | |||
dd6032f3ef | |||
7e2f5bc506 | |||
4ef92efd0e | |||
e769dd6757 | |||
550a51523f | |||
323cb05388 | |||
7e0851bfef | |||
98b6d167e8 | |||
79baddb8f6 | |||
0f8c83c2e2 | |||
fad52644d2 | |||
5f51a1008b | |||
279c171ba2 | |||
9ba8e3e84e | |||
6de38a9725 | |||
f48b570494 | |||
0733f8d5af | |||
8071c01c5d | |||
4ee3d9bc44 | |||
d75d45e204 | |||
9f3754776f | |||
651fb2b1a1 | |||
aa5988ce75 | |||
9ae18e1e4b | |||
22ebb0e1f4 | |||
09e4b3262f | |||
6a3d4239ab | |||
9d71c93b5b | |||
b1df7421dc | |||
7a03146bf8 | |||
5145b833ae | |||
f0a371dc52 | |||
ac19d33bdb | |||
3d4d5b90d1 | |||
4080cee818 | |||
f4d73654d1 | |||
4fda5513a9 | |||
32407b0e8f | |||
b30e1196f4 |
@ -25,9 +25,8 @@ jobs:
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
with:
|
||||
with:
|
||||
cache-disabled: true # Once the code has been pushed once in main, this should be reenabled.
|
||||
|
||||
- name: init gradle
|
||||
working-directory: ./MyINPulse-back/
|
||||
run: ./gradlew build # todo: run test, currently fail because no database is present
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,6 +1,7 @@
|
||||
.env
|
||||
.idea
|
||||
keycloak/CAS/target
|
||||
keycloak/.installed
|
||||
docker-compose.yaml
|
||||
node_modules
|
||||
.vscode
|
||||
|
@ -29,6 +29,8 @@ dependencies {
|
||||
implementation 'org.postgresql:postgresql'
|
||||
implementation group: 'com.itextpdf', name: 'itextpdf', version: '5.5.13.3'
|
||||
|
||||
runtimeOnly 'com.h2database:h2'
|
||||
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
testImplementation 'com.h2database:h2'
|
||||
|
||||
|
@ -31,7 +31,7 @@ public class WebSecurityCustomConfiguration {
|
||||
public CorsConfigurationSource corsConfigurationSource() {
|
||||
CorsConfiguration configuration = new CorsConfiguration();
|
||||
configuration.setAllowedOrigins(List.of(frontendUrl));
|
||||
configuration.setAllowedMethods(Arrays.asList("GET", "OPTIONS"));
|
||||
configuration.setAllowedMethods(Arrays.asList("GET", "OPTIONS", "POST", "PUT", "DELETE"));
|
||||
configuration.setAllowedHeaders(
|
||||
Arrays.asList("authorization", "content-type", "x-auth-token"));
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
@ -56,14 +56,18 @@ public class WebSecurityCustomConfiguration {
|
||||
http.authorizeHttpRequests(
|
||||
authorize ->
|
||||
authorize
|
||||
.requestMatchers("/entrepreneur/**", "/shared/**")
|
||||
.requestMatchers("/entrepreneur/**")
|
||||
.access(hasRole("REALM_MyINPulse-entrepreneur"))
|
||||
.requestMatchers("/admin/**", "/shared/**")
|
||||
.requestMatchers("/admin/**")
|
||||
.access(hasRole("REALM_MyINPulse-admin"))
|
||||
.requestMatchers("/shared/**")
|
||||
.hasAnyRole(
|
||||
"REALM_MyINPulse-admin",
|
||||
"REALM_MyINPulse-entrepreneur")
|
||||
.requestMatchers("/unauth/**")
|
||||
.permitAll()
|
||||
.authenticated()
|
||||
.anyRequest()
|
||||
.authenticated())
|
||||
.denyAll())
|
||||
.oauth2ResourceServer(
|
||||
oauth2 ->
|
||||
oauth2.jwt(
|
||||
|
@ -57,7 +57,7 @@ public class AdminApi {
|
||||
*
|
||||
* @return the status code of the request
|
||||
*/
|
||||
@PostMapping("/admin/projects/decision")
|
||||
@PostMapping("/admin/projects/pending/decision")
|
||||
public void validateProject(@RequestBody ProjectDecision decision) {
|
||||
adminApiService.validateProject(decision);
|
||||
}
|
||||
@ -67,7 +67,7 @@ public class AdminApi {
|
||||
*
|
||||
* @return the status code of the request
|
||||
*/
|
||||
@PostMapping("/admin/project/add")
|
||||
@PostMapping("/admin/project")
|
||||
public void addNewProject(@RequestBody Project project) {
|
||||
adminApiService.addNewProject(project);
|
||||
}
|
||||
@ -79,7 +79,7 @@ public class AdminApi {
|
||||
*
|
||||
* @return the status code of the request
|
||||
*/
|
||||
@PostMapping("/admin/appoitements/report/{appointmentId}")
|
||||
@PostMapping("/admin/appointments/report/{appointmentId}")
|
||||
public void createAppointmentReport(
|
||||
@PathVariable long appointmentId,
|
||||
@RequestBody Report report,
|
||||
@ -95,8 +95,35 @@ public class AdminApi {
|
||||
*
|
||||
* @return the status code of the request
|
||||
*/
|
||||
@DeleteMapping("/admin/projects/remove/{projectId}")
|
||||
@DeleteMapping("/admin/projects/{projectId}")
|
||||
public void deleteProject(@PathVariable long projectId) {
|
||||
adminApiService.deleteProject(projectId);
|
||||
}
|
||||
|
||||
@PostMapping("/admin/make-admin/{userId}")
|
||||
public void setAdmin(@PathVariable long userId, @AuthenticationPrincipal Jwt principal) {
|
||||
this.adminApiService.setAdmin(userId, principal.getTokenValue());
|
||||
}
|
||||
|
||||
@PostMapping("/admin/accounts/validate/{userId}")
|
||||
public void validateEntrepreneurAcc(
|
||||
@PathVariable long userId, @AuthenticationPrincipal Jwt principal) {
|
||||
this.adminApiService.validateEntrepreneurAccount(userId, principal.getTokenValue());
|
||||
}
|
||||
|
||||
@GetMapping("/admin/pending-accounts")
|
||||
public Iterable<User> validateEntrepreneurAcc() {
|
||||
return this.adminApiService.getPendingUsers();
|
||||
}
|
||||
|
||||
@PostMapping("/admin/create-account")
|
||||
public void createAccount(@AuthenticationPrincipal Jwt principal) {
|
||||
String userSurname = principal.getClaimAsString("userSurname");
|
||||
String username = principal.getClaimAsString("preferred_username");
|
||||
String primaryMail = principal.getClaimAsString("email");
|
||||
String secondaryMail = principal.getClaimAsString("secondaryMail");
|
||||
String phoneNumber = principal.getClaimAsString("phoneNumber");
|
||||
this.adminApiService.createAccount(
|
||||
userSurname, username, primaryMail, secondaryMail, phoneNumber);
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,20 @@
|
||||
package enseirb.myinpulse.controller;
|
||||
|
||||
import enseirb.myinpulse.model.SectionCell;
|
||||
import enseirb.myinpulse.model.Project;
|
||||
import enseirb.myinpulse.service.EntrepreneurApiService;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import enseirb.myinpulse.model.Project;
|
||||
import enseirb.myinpulse.model.SectionCell;
|
||||
import enseirb.myinpulse.service.EntrepreneurApiService;
|
||||
|
||||
@SpringBootApplication
|
||||
@RestController
|
||||
@ -28,13 +34,26 @@ public class EntrepreneurApi {
|
||||
*
|
||||
* @return status code
|
||||
*/
|
||||
@PutMapping("/entrepreneur/lcsection/modify/{sectionId}")
|
||||
@PutMapping("/entrepreneur/sectionCells/{sectionCellId}")
|
||||
public void editSectionCell(
|
||||
@PathVariable Long sectionId,
|
||||
@RequestBody SectionCell sectionCell,
|
||||
@PathVariable Long sectionCellId,
|
||||
@RequestBody String content,
|
||||
@AuthenticationPrincipal Jwt principal) {
|
||||
entrepreneurApiService.editSectionCell(
|
||||
sectionId, sectionCell, principal.getClaimAsString("email"));
|
||||
sectionCellId, content, principal.getClaimAsString("email"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Endpoint used to update a LC section.
|
||||
*
|
||||
* @return status code
|
||||
*/
|
||||
@GetMapping("/entrepreneur/projects")
|
||||
public Iterable<Project> getEntrepreneurProjectId(
|
||||
@PathVariable Long sectionCellId,
|
||||
@RequestBody String content,
|
||||
@AuthenticationPrincipal Jwt principal) {
|
||||
return entrepreneurApiService.getProjectIdViaClaim(principal.getClaimAsString("email"));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -44,10 +63,11 @@ public class EntrepreneurApi {
|
||||
*
|
||||
* @return status code
|
||||
*/
|
||||
@DeleteMapping("/entrepreneur/lcsection/remove/{sectionId}")
|
||||
@DeleteMapping("/entrepreneur/sectionCells/{sectionCellId}")
|
||||
public void removeSectionCell(
|
||||
@PathVariable Long sectionId, @AuthenticationPrincipal Jwt principal) {
|
||||
entrepreneurApiService.removeSectionCell(sectionId, principal.getClaimAsString("email"));
|
||||
@PathVariable Long sectionCellId, @AuthenticationPrincipal Jwt principal) {
|
||||
entrepreneurApiService.removeSectionCell(
|
||||
sectionCellId, principal.getClaimAsString("email"));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -57,7 +77,7 @@ public class EntrepreneurApi {
|
||||
*
|
||||
* @return status code
|
||||
*/
|
||||
@PostMapping("/entrepreneur/lcsection/add") // remove id from doc aswell
|
||||
@PostMapping("/entrepreneur/sectionCells")
|
||||
public void addLCSection(
|
||||
@RequestBody SectionCell sectionCell, @AuthenticationPrincipal Jwt principal) {
|
||||
entrepreneurApiService.addSectionCell(sectionCell, principal.getClaimAsString("email"));
|
||||
@ -70,9 +90,27 @@ public class EntrepreneurApi {
|
||||
*
|
||||
* @return status code
|
||||
*/
|
||||
@PostMapping("/entrepreneur/project/request")
|
||||
@PostMapping("/entrepreneur/projects/request")
|
||||
public void requestNewProject(
|
||||
@RequestBody Project project, @AuthenticationPrincipal Jwt principal) {
|
||||
entrepreneurApiService.requestNewProject(project, principal.getClaimAsString("email"));
|
||||
}
|
||||
|
||||
/*
|
||||
* <p>Endpoint to check if project is has already been validated by an admin
|
||||
*/
|
||||
@GetMapping("/entrepreneur/projects/project-is-active")
|
||||
public Boolean checkIfProjectValidated(@AuthenticationPrincipal Jwt principal) {
|
||||
return entrepreneurApiService.checkIfEntrepreneurProjectActive(
|
||||
principal.getClaimAsString("email"));
|
||||
}
|
||||
|
||||
/*
|
||||
* <p>Endpoint to check if a user requested a project (used when project is pending)
|
||||
*/
|
||||
@GetMapping("/entrepreneur/projects/has-pending-request")
|
||||
public Boolean checkIfHasRequested(@AuthenticationPrincipal Jwt principal) {
|
||||
return entrepreneurApiService.entrepreneurHasPendingRequestedProject(
|
||||
principal.getClaimAsString("email"));
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ public class SharedApi {
|
||||
*
|
||||
* @return a list of lean canvas sections
|
||||
*/
|
||||
@GetMapping("/shared/project/lcsection/{projectId}/{sectionId}/{date}")
|
||||
@GetMapping("/shared/projects/sectionCells/{projectId}/{sectionId}/{date}")
|
||||
public Iterable<SectionCell> getLCSection(
|
||||
@PathVariable("projectId") Long projectId,
|
||||
@PathVariable("sectionId") Long sectionId,
|
||||
@ -45,7 +45,7 @@ public class SharedApi {
|
||||
*
|
||||
* @return a list of all entrepreneurs in a project
|
||||
*/
|
||||
@GetMapping("/shared/entrepreneurs/{projectId}")
|
||||
@GetMapping("/shared/projects/entrepreneurs/{projectId}")
|
||||
public Iterable<Entrepreneur> getEntrepreneursByProjectId(
|
||||
@PathVariable int projectId, @AuthenticationPrincipal Jwt principal) {
|
||||
return sharedApiService.getEntrepreneursByProjectId(
|
||||
@ -80,7 +80,7 @@ public class SharedApi {
|
||||
*
|
||||
* @return a PDF file? TODO: how does that works ?
|
||||
*/
|
||||
@GetMapping("/shared/projects/appointments/report/{appointmentId}")
|
||||
@GetMapping("/shared/appointments/report/{appointmentId}")
|
||||
public void getPDFReport(
|
||||
@PathVariable int appointmentId, @AuthenticationPrincipal Jwt principal) {
|
||||
try {
|
||||
@ -97,7 +97,7 @@ public class SharedApi {
|
||||
/**
|
||||
* @return TODO
|
||||
*/
|
||||
@PostMapping("/shared/appointment/request")
|
||||
@PostMapping("/shared/appointments/request")
|
||||
public void createAppointmentRequest(
|
||||
@RequestBody Appointment appointment, @AuthenticationPrincipal Jwt principal) {
|
||||
sharedApiService.createAppointmentRequest(appointment, principal.getClaimAsString("email"));
|
||||
|
@ -0,0 +1,61 @@
|
||||
package enseirb.myinpulse.controller;
|
||||
|
||||
import enseirb.myinpulse.model.Entrepreneur;
|
||||
import enseirb.myinpulse.service.EntrepreneurApiService;
|
||||
import enseirb.myinpulse.service.UtilsService;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@SpringBootApplication
|
||||
@RestController
|
||||
public class UnauthApi {
|
||||
|
||||
private final EntrepreneurApiService entrepreneurApiService;
|
||||
private final UtilsService utilsService;
|
||||
|
||||
@Autowired
|
||||
UnauthApi(EntrepreneurApiService entrepreneurApiService, UtilsService utilsService) {
|
||||
this.entrepreneurApiService = entrepreneurApiService;
|
||||
this.utilsService = utilsService;
|
||||
}
|
||||
|
||||
@PostMapping("/unauth/finalize")
|
||||
public void createAccount(@AuthenticationPrincipal Jwt principal) {
|
||||
boolean sneeStatus;
|
||||
if (principal.getClaimAsString("sneeStatus") != null) {
|
||||
sneeStatus = principal.getClaimAsString("sneeStatus").equals("true");
|
||||
} else {
|
||||
sneeStatus = false;
|
||||
}
|
||||
String userSurname = principal.getClaimAsString("userSurname");
|
||||
String username = principal.getClaimAsString("preferred_username");
|
||||
String primaryMail = principal.getClaimAsString("email");
|
||||
String secondaryMail = principal.getClaimAsString("secondaryMail");
|
||||
String phoneNumber = principal.getClaimAsString("phoneNumber");
|
||||
String school = principal.getClaimAsString("school");
|
||||
String course = principal.getClaimAsString("course");
|
||||
Entrepreneur e =
|
||||
new Entrepreneur(
|
||||
userSurname,
|
||||
username,
|
||||
primaryMail,
|
||||
secondaryMail,
|
||||
phoneNumber,
|
||||
school,
|
||||
course,
|
||||
sneeStatus,
|
||||
true);
|
||||
|
||||
entrepreneurApiService.createAccount(e);
|
||||
}
|
||||
|
||||
@GetMapping("/unauth/check-if-not-pending")
|
||||
public Boolean checkAccountStatus(@AuthenticationPrincipal Jwt principal) {
|
||||
// Throws 404 if user not found
|
||||
return utilsService.checkEntrepreneurNotPending(principal.getClaimAsString("email"));
|
||||
}
|
||||
}
|
@ -37,7 +37,7 @@ public class Administrator extends User {
|
||||
String primaryMail,
|
||||
String secondaryMail,
|
||||
String phoneNumber) {
|
||||
super(null, userSurname, username, primaryMail, secondaryMail, phoneNumber);
|
||||
super(userSurname, username, primaryMail, secondaryMail, phoneNumber, false);
|
||||
}
|
||||
|
||||
public List<Project> getListProject() {
|
||||
|
@ -44,15 +44,30 @@ public class Entrepreneur extends User {
|
||||
String phoneNumber,
|
||||
String school,
|
||||
String course,
|
||||
boolean sneeStatus) {
|
||||
super(userSurname, username, primaryMail, secondaryMail, phoneNumber);
|
||||
boolean sneeStatus,
|
||||
boolean pending) {
|
||||
super(userSurname, username, primaryMail, secondaryMail, phoneNumber, pending);
|
||||
this.school = school;
|
||||
this.course = course;
|
||||
this.sneeStatus = sneeStatus;
|
||||
}
|
||||
|
||||
public Entrepreneur(
|
||||
String userSurname,
|
||||
String username,
|
||||
String primaryMail,
|
||||
String secondaryMail,
|
||||
String phoneNumber,
|
||||
String school,
|
||||
String course,
|
||||
boolean sneeStatus) {
|
||||
super(userSurname, username, primaryMail, secondaryMail, phoneNumber, true);
|
||||
this.school = school;
|
||||
this.course = course;
|
||||
this.sneeStatus = sneeStatus;
|
||||
}
|
||||
|
||||
public Entrepreneur(
|
||||
Long idUser,
|
||||
String userSurname,
|
||||
String userName,
|
||||
String primaryMail,
|
||||
@ -63,8 +78,9 @@ public class Entrepreneur extends User {
|
||||
boolean sneeStatus,
|
||||
Project projectParticipation,
|
||||
Project projectProposed,
|
||||
MakeAppointment makeAppointment) {
|
||||
super(idUser, userSurname, userName, primaryMail, secondaryMail, phoneNumber);
|
||||
MakeAppointment makeAppointment,
|
||||
boolean pending) {
|
||||
super(userSurname, userName, primaryMail, secondaryMail, phoneNumber, pending);
|
||||
this.school = school;
|
||||
this.course = course;
|
||||
this.sneeStatus = sneeStatus;
|
||||
|
@ -66,6 +66,15 @@ public class Project {
|
||||
this.entrepreneurProposed = entrepreneurProposed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
Project project = (Project) o;
|
||||
return this.idProject == project.idProject;
|
||||
}
|
||||
|
||||
public Long getIdProject() {
|
||||
return idProject;
|
||||
}
|
||||
|
@ -2,6 +2,9 @@ package enseirb.myinpulse.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import org.hibernate.annotations.Generated;
|
||||
import org.hibernate.generator.EventType;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -20,6 +23,10 @@ public class SectionCell {
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long idSectionCell;
|
||||
|
||||
@Column(columnDefinition = "serial")
|
||||
@Generated(event = EventType.INSERT)
|
||||
private Long idReference;
|
||||
|
||||
@Column() private long sectionId;
|
||||
private String contentSectionCell;
|
||||
|
||||
@ -56,6 +63,14 @@ public class SectionCell {
|
||||
this.idSectionCell = idSectionCell;
|
||||
}
|
||||
|
||||
public Long getIdReference() {
|
||||
return idReference;
|
||||
}
|
||||
|
||||
public void setIdReference(Long idReference) {
|
||||
this.idReference = idReference;
|
||||
}
|
||||
|
||||
public Long getSectionId() {
|
||||
return sectionId;
|
||||
}
|
||||
|
@ -26,36 +26,23 @@ public class User {
|
||||
@Column(length = 20)
|
||||
private String phoneNumber;
|
||||
|
||||
@Column private boolean pending;
|
||||
|
||||
public User() {}
|
||||
|
||||
// TODO: this should be removed as we shouldn't be able to chose the ID. Leaving it for
|
||||
// compatibility purposes, as soon as it's not used anymore, delete it
|
||||
public User(
|
||||
Long idUser,
|
||||
String userSurname,
|
||||
String userName,
|
||||
String primaryMail,
|
||||
String secondaryMail,
|
||||
String phoneNumber) {
|
||||
this.idUser = idUser;
|
||||
this.userSurname = userSurname;
|
||||
this.userName = userName;
|
||||
this.primaryMail = primaryMail;
|
||||
this.secondaryMail = secondaryMail;
|
||||
this.phoneNumber = phoneNumber;
|
||||
}
|
||||
|
||||
public User(
|
||||
String userSurname,
|
||||
String userName,
|
||||
String primaryMail,
|
||||
String secondaryMail,
|
||||
String phoneNumber) {
|
||||
String phoneNumber,
|
||||
boolean pending) {
|
||||
this.userSurname = userSurname;
|
||||
this.userName = userName;
|
||||
this.primaryMail = primaryMail;
|
||||
this.secondaryMail = secondaryMail;
|
||||
this.phoneNumber = phoneNumber;
|
||||
this.pending = pending;
|
||||
}
|
||||
|
||||
public Long getIdUser() {
|
||||
@ -105,4 +92,12 @@ public class User {
|
||||
public void setPhoneNumber(String phoneNumber) {
|
||||
phoneNumber = phoneNumber;
|
||||
}
|
||||
|
||||
public boolean isPending() {
|
||||
return pending;
|
||||
}
|
||||
|
||||
public void setPending(boolean pending) {
|
||||
this.pending = pending;
|
||||
}
|
||||
}
|
||||
|
@ -15,4 +15,6 @@ public interface SectionCellRepository extends JpaRepository<SectionCell, Long>
|
||||
|
||||
Iterable<SectionCell> findByProjectSectionCellAndSectionIdAndModificationDateBefore(
|
||||
Project project, long sectionId, LocalDateTime date);
|
||||
|
||||
Iterable<SectionCell> findByProjectSectionCell(Project project);
|
||||
}
|
||||
|
@ -9,8 +9,9 @@ import java.util.Optional;
|
||||
|
||||
@RepositoryRestResource
|
||||
public interface UserRepository extends JpaRepository<User, Long> {
|
||||
Optional<User> findByPrimaryMail(String email);
|
||||
Optional<User> findByPrimaryMail(String primaryMail);
|
||||
|
||||
Iterable<User> findAllByPendingEquals(boolean pending);
|
||||
/* @Query("SELECT u from User u")
|
||||
User findAllUser(); */
|
||||
|
||||
|
@ -24,6 +24,7 @@ public class AdminApiService {
|
||||
private final ProjectService projectService;
|
||||
private final UserService userService;
|
||||
private final AdministratorService administratorService;
|
||||
private final EntrepreneurService entrepreneurService;
|
||||
private final UtilsService utilsService;
|
||||
private final AppointmentService appointmentService;
|
||||
private final ReportService reportService;
|
||||
@ -35,6 +36,7 @@ public class AdminApiService {
|
||||
UserService userService,
|
||||
AdministratorService administratorService,
|
||||
UtilsService utilsService,
|
||||
EntrepreneurService entrepreneurService,
|
||||
AppointmentService appointmentService,
|
||||
ReportService reportService,
|
||||
SectionCellService sectionCellService) {
|
||||
@ -45,6 +47,7 @@ public class AdminApiService {
|
||||
this.appointmentService = appointmentService;
|
||||
this.reportService = reportService;
|
||||
this.sectionCellService = sectionCellService;
|
||||
this.entrepreneurService = entrepreneurService;
|
||||
}
|
||||
|
||||
// TODO: check if tests are sufficient - peer verification required
|
||||
@ -75,6 +78,12 @@ public class AdminApiService {
|
||||
}
|
||||
if (user instanceof Entrepreneur) {
|
||||
Project project = ((Entrepreneur) user).getProjectParticipation();
|
||||
if (project == null) {
|
||||
throw new ResponseStatusException(
|
||||
HttpStatus.NOT_FOUND,
|
||||
"The user has no project, thus no appointments. No users should have no project");
|
||||
}
|
||||
|
||||
project.getListSectionCell()
|
||||
.forEach(
|
||||
sectionCell -> {
|
||||
@ -97,13 +106,14 @@ public class AdminApiService {
|
||||
decision.projectId,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
(decision.isAccepted == 1) ? ACTIVE : REJECTED,
|
||||
null,
|
||||
null,
|
||||
this.administratorService.getAdministratorById(decision.adminId));
|
||||
}
|
||||
|
||||
// TODO: check if tests are sufficient - peer verification required
|
||||
public void addNewProject(Project project) {
|
||||
public Project addNewProject(Project project) {
|
||||
project.setIdProject(null);
|
||||
// We remove the ID from the request to be sure that it will be auto generated
|
||||
try {
|
||||
@ -135,6 +145,7 @@ public class AdminApiService {
|
||||
sectionCell -> {
|
||||
sectionCell.setProjectSectionCell(newProject);
|
||||
});
|
||||
return newProject;
|
||||
}
|
||||
|
||||
public void createAppointmentReport(long appointmentId, Report report, String mail) {
|
||||
@ -163,4 +174,51 @@ public class AdminApiService {
|
||||
public void deleteProject(long projectId) {
|
||||
this.projectService.deleteProjectById(projectId);
|
||||
}
|
||||
|
||||
public void setAdmin(long userId, String token) {
|
||||
Entrepreneur e = this.entrepreneurService.getEntrepreneurById(userId);
|
||||
Administrator a =
|
||||
new Administrator(
|
||||
e.getUserSurname(),
|
||||
e.getUserName(),
|
||||
e.getPrimaryMail(),
|
||||
e.getSecondaryMail(),
|
||||
e.getPhoneNumber());
|
||||
this.entrepreneurService.deleteEntrepreneur(e);
|
||||
this.administratorService.addAdministrator(a);
|
||||
try {
|
||||
KeycloakApi.setRoleToUser(a.getUserName(), "MyINPulse-admin", token);
|
||||
} catch (Exception err) {
|
||||
logger.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
public void validateEntrepreneurAccount(long userId, String token) {
|
||||
Entrepreneur e = this.entrepreneurService.getEntrepreneurById(userId);
|
||||
try {
|
||||
KeycloakApi.setRoleToUser(e.getUserName(), "MyINPulse-entrepreneur", token);
|
||||
} catch (Exception err) {
|
||||
logger.error(err);
|
||||
}
|
||||
this.entrepreneurService.validateEntrepreneurById(userId);
|
||||
}
|
||||
|
||||
public Iterable<User> getPendingUsers() {
|
||||
return this.userService.getPendingAccounts();
|
||||
}
|
||||
|
||||
public void createAccount(
|
||||
String username,
|
||||
String userSurname,
|
||||
String primaryMail,
|
||||
String secondaryMail,
|
||||
String phoneNumber) {
|
||||
Administrator a =
|
||||
new Administrator(username, userSurname, primaryMail, secondaryMail, phoneNumber);
|
||||
this.administratorService.addAdministrator(a);
|
||||
}
|
||||
|
||||
public Iterable<Administrator> getAllAdmins() {
|
||||
return this.administratorService.allAdministrators();
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
package enseirb.myinpulse.service;
|
||||
|
||||
import static enseirb.myinpulse.model.ProjectDecisionValue.PENDING;
|
||||
import static enseirb.myinpulse.model.ProjectDecisionValue.ACTIVE;
|
||||
|
||||
import enseirb.myinpulse.model.Entrepreneur;
|
||||
import enseirb.myinpulse.model.Project;
|
||||
import enseirb.myinpulse.model.SectionCell;
|
||||
import enseirb.myinpulse.service.database.ProjectService;
|
||||
import enseirb.myinpulse.service.database.SectionCellService;
|
||||
import enseirb.myinpulse.model.User;
|
||||
import enseirb.myinpulse.service.database.*;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@ -14,6 +16,10 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class EntrepreneurApiService {
|
||||
|
||||
@ -22,24 +28,39 @@ public class EntrepreneurApiService {
|
||||
private final SectionCellService sectionCellService;
|
||||
private final ProjectService projectService;
|
||||
private final UtilsService utilsService;
|
||||
private final UserService userService;
|
||||
private final EntrepreneurService entrepreneurService;
|
||||
private final AdministratorService administratorService;
|
||||
private final AppointmentService appointmentService;
|
||||
private final AnnotationService annotationService;
|
||||
|
||||
@Autowired
|
||||
EntrepreneurApiService(
|
||||
SectionCellService sectionCellService,
|
||||
ProjectService projectService,
|
||||
UtilsService utilsService) {
|
||||
UtilsService utilsService,
|
||||
UserService userService,
|
||||
EntrepreneurService entrepreneurService,
|
||||
AdministratorService administratorService,
|
||||
AppointmentService appointmentService,
|
||||
AnnotationService annotationService) {
|
||||
this.sectionCellService = sectionCellService;
|
||||
this.projectService = projectService;
|
||||
this.utilsService = utilsService;
|
||||
this.userService = userService;
|
||||
this.entrepreneurService = entrepreneurService;
|
||||
this.administratorService = administratorService;
|
||||
this.appointmentService = appointmentService;
|
||||
this.annotationService = annotationService;
|
||||
}
|
||||
|
||||
public void editSectionCell(Long sectionCellId, SectionCell sectionCell, String mail) {
|
||||
SectionCell editSectionCell = sectionCellService.getSectionCellById(sectionCellId);
|
||||
if (editSectionCell == null) {
|
||||
System.err.println("Trying to edit unknown section cell");
|
||||
public void editSectionCell(Long sectionCellId, String content, String mail) {
|
||||
if (sectionCellId == null) {
|
||||
logger.warn("Trying to edit unknown section cell");
|
||||
throw new ResponseStatusException(
|
||||
HttpStatus.NOT_FOUND, "Cette cellule de section n'existe pas");
|
||||
}
|
||||
SectionCell sectionCell = sectionCellService.getSectionCellById(sectionCellId);
|
||||
if (!utilsService.isAllowedToCheckProject(
|
||||
mail, this.sectionCellService.getProjectId(sectionCellId))) {
|
||||
logger.warn(
|
||||
@ -55,27 +76,45 @@ public class EntrepreneurApiService {
|
||||
mail,
|
||||
sectionCellId,
|
||||
this.sectionCellService.getProjectId(sectionCellId));
|
||||
sectionCellService.updateSectionCell(
|
||||
sectionCellId,
|
||||
sectionCell.getSectionId(),
|
||||
sectionCell.getContentSectionCell(),
|
||||
sectionCell.getModificationDate());
|
||||
SectionCell newSectionCell =
|
||||
new SectionCell(
|
||||
null,
|
||||
sectionCell.getSectionId(),
|
||||
content,
|
||||
LocalDateTime.now(),
|
||||
sectionCell.getProjectSectionCell());
|
||||
newSectionCell.setIdReference(sectionCell.getIdReference());
|
||||
this.addSectionCell(newSectionCell, mail);
|
||||
sectionCell
|
||||
.getAppointmentSectionCell()
|
||||
.forEach(
|
||||
appointment -> {
|
||||
this.appointmentService.updateAppointmentListSectionCell(
|
||||
appointment.getIdAppointment(), newSectionCell);
|
||||
});
|
||||
sectionCell
|
||||
.getListAnnotation()
|
||||
.forEach(
|
||||
annotation -> {
|
||||
this.annotationService.updateAnnotationSectionCell(
|
||||
annotation.getIdAnnotation(), newSectionCell);
|
||||
});
|
||||
}
|
||||
|
||||
public void removeSectionCell(Long sectionCellId, String mail) {
|
||||
SectionCell editSectionCell = sectionCellService.getSectionCellById(sectionCellId);
|
||||
if (editSectionCell == null) {
|
||||
System.err.println("Trying to remove unknown section cell");
|
||||
if (sectionCellId == null) {
|
||||
logger.warn("Trying to remove unknown section cell");
|
||||
throw new ResponseStatusException(
|
||||
HttpStatus.NOT_FOUND, "Cette cellule de section n'existe pas");
|
||||
}
|
||||
SectionCell editSectionCell = sectionCellService.getSectionCellById(sectionCellId);
|
||||
if (!utilsService.isAllowedToCheckProject(
|
||||
mail, this.sectionCellService.getProjectId(sectionCellId))) {
|
||||
logger.warn(
|
||||
"User {} tried to remove section cells {} of the project {} but is not allowed to.",
|
||||
mail,
|
||||
sectionCellId,
|
||||
this.sectionCellService.getSectionCellById(sectionCellId));
|
||||
this.sectionCellService.getProjectId(sectionCellId));
|
||||
throw new ResponseStatusException(
|
||||
HttpStatus.UNAUTHORIZED, "You're not allowed to check this project");
|
||||
}
|
||||
@ -84,52 +123,162 @@ public class EntrepreneurApiService {
|
||||
mail,
|
||||
sectionCellId,
|
||||
this.sectionCellService.getProjectId(sectionCellId));
|
||||
sectionCellService.removeSectionCellById(sectionCellId);
|
||||
SectionCell removedSectionCell =
|
||||
new SectionCell(
|
||||
null,
|
||||
-1L,
|
||||
"",
|
||||
LocalDateTime.now(),
|
||||
this.projectService.getProjectById(
|
||||
editSectionCell.getProjectSectionCell().getIdProject()));
|
||||
sectionCellService.addNewSectionCell(removedSectionCell);
|
||||
this.sectionCellService.updateSectionCellReferenceId(
|
||||
removedSectionCell.getIdSectionCell(), editSectionCell.getIdReference());
|
||||
projectService.updateProjectListSectionCell(
|
||||
sectionCellService.getProjectId(sectionCellId), removedSectionCell);
|
||||
// sectionCellService.removeSectionCellById(sectionCellId);
|
||||
}
|
||||
|
||||
public void addSectionCell(SectionCell sectionCell, String mail) {
|
||||
if (sectionCell == null) {
|
||||
System.err.println("Trying to create an empty section cell");
|
||||
logger.warn("Trying to create an empty section cell");
|
||||
throw new ResponseStatusException(
|
||||
HttpStatus.BAD_REQUEST, "La cellule de section fournie est vide");
|
||||
}
|
||||
if (sectionCell.getSectionId() == -1) {
|
||||
logger.warn("Trying to create an illegal section cell");
|
||||
throw new ResponseStatusException(
|
||||
HttpStatus.BAD_REQUEST, "La cellule de section fournie n'est pas valide");
|
||||
}
|
||||
if (!utilsService.isAllowedToCheckProject(
|
||||
mail, this.sectionCellService.getProjectId(sectionCell.getIdSectionCell()))) {
|
||||
mail, sectionCell.getProjectSectionCell().getIdProject())) {
|
||||
logger.warn(
|
||||
"User {} tried to add a section cell to the project {} but is not allowed to.",
|
||||
mail,
|
||||
this.sectionCellService.getProjectId(sectionCell.getIdSectionCell()));
|
||||
sectionCell.getProjectSectionCell().getIdProject());
|
||||
throw new ResponseStatusException(
|
||||
HttpStatus.UNAUTHORIZED, "You're not allowed to check this project");
|
||||
}
|
||||
logger.info(
|
||||
"User {} added a new section cell {} to the project with id {}",
|
||||
"User {} added a new section cell {} to the project {}",
|
||||
mail,
|
||||
sectionCell.getIdSectionCell(),
|
||||
this.sectionCellService.getProjectId(sectionCell.getIdSectionCell()));
|
||||
SectionCell newSectionCell = sectionCellService.addNewSectionCell(sectionCell);
|
||||
sectionCell.getProjectSectionCell().getIdProject());
|
||||
SectionCell newSectionCell =
|
||||
sectionCellService.addNewSectionCell(
|
||||
sectionCell); // if here, logger fails cause id is null (not added yet)
|
||||
newSectionCell.getProjectSectionCell().updateListSectionCell(newSectionCell);
|
||||
newSectionCell
|
||||
.getAppointmentSectionCell()
|
||||
.forEach(
|
||||
appointment -> {
|
||||
appointment.updateListSectionCell(newSectionCell);
|
||||
this.appointmentService.updateAppointmentListSectionCell(
|
||||
appointment.getIdAppointment(), newSectionCell);
|
||||
});
|
||||
newSectionCell
|
||||
.getListAnnotation()
|
||||
.forEach(
|
||||
annotation -> {
|
||||
annotation.setSectionCellAnnotation(newSectionCell);
|
||||
this.annotationService.updateAnnotationSectionCell(
|
||||
annotation.getIdAnnotation(), newSectionCell);
|
||||
});
|
||||
}
|
||||
|
||||
public void requestNewProject(Project project, String mail) {
|
||||
if (project == null) {
|
||||
logger.error("Trying to request the creation of a null project");
|
||||
logger.warn("Trying to request the creation of a null project");
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Le projet fourni est vide");
|
||||
}
|
||||
logger.info("User {} created a new project with id {}", mail, project.getIdProject());
|
||||
project.setProjectStatus(PENDING);
|
||||
logger.info("User {} created a new project named {}", mail, project.getProjectName());
|
||||
project.setEntrepreneurProposed((Entrepreneur) this.userService.getUserByEmail(mail));
|
||||
projectService.addNewProject(project);
|
||||
this.projectService.updateProjectStatus(project.getIdProject(), PENDING);
|
||||
if (project.getProjectAdministrator() != null) {
|
||||
this.administratorService.updateAdministratorListProject(
|
||||
project.getProjectAdministrator().getIdUser(), project);
|
||||
}
|
||||
this.entrepreneurService.updateEntrepreneurProjectProposed(
|
||||
this.userService.getUserByEmail(mail).getIdUser(), project);
|
||||
this.entrepreneurService.updateEntrepreneurProjectParticipation(
|
||||
this.userService.getUserByEmail(mail).getIdUser(), project);
|
||||
project.getListEntrepreneurParticipation()
|
||||
.forEach(
|
||||
entrepreneur ->
|
||||
this.entrepreneurService.updateEntrepreneurProjectParticipation(
|
||||
entrepreneur.getIdUser(), project));
|
||||
project.getListSectionCell()
|
||||
.forEach(
|
||||
sectionCell ->
|
||||
this.sectionCellService.updateSectionCellProject(
|
||||
sectionCell.getIdSectionCell(), project));
|
||||
}
|
||||
|
||||
public void createAccount(Entrepreneur e) {
|
||||
try {
|
||||
userService.getUserByEmail(e.getPrimaryMail());
|
||||
logger.error("The user {} already exists in the system", e.getPrimaryMail());
|
||||
} catch (ResponseStatusException err) {
|
||||
this.entrepreneurService.addEntrepreneur(e);
|
||||
return;
|
||||
}
|
||||
throw new ResponseStatusException(HttpStatus.CONFLICT, "User already exists in the system");
|
||||
}
|
||||
|
||||
public Iterable<Project> getProjectIdViaClaim(String email) {
|
||||
Long UserId = this.userService.getUserByEmail(email).getIdUser();
|
||||
Entrepreneur entrepreneur = this.entrepreneurService.getEntrepreneurById(UserId);
|
||||
List<Project> Project_List = new ArrayList<>();
|
||||
|
||||
Project_List.add(entrepreneur.getProjectParticipation());
|
||||
return Project_List;
|
||||
}
|
||||
|
||||
public Iterable<Entrepreneur> getAllEntrepreneurs() {
|
||||
return entrepreneurService.getAllEntrepreneurs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an entrepreneur with the given email has a project that is ACTIVE.
|
||||
*
|
||||
* @param email The email of the entrepreneur.
|
||||
* @return true if the entrepreneur has an active project, false otherwise.
|
||||
*/
|
||||
public Boolean checkIfEntrepreneurProjectActive(String email) {
|
||||
User user = this.userService.getUserByEmail(email);
|
||||
if (user == null) {
|
||||
return false;
|
||||
}
|
||||
Long userId = user.getIdUser();
|
||||
|
||||
Entrepreneur entrepreneur = this.entrepreneurService.getEntrepreneurById(userId);
|
||||
if (entrepreneur == null) {
|
||||
return false;
|
||||
}
|
||||
Project proposedProject = entrepreneur.getProjectProposed();
|
||||
return proposedProject != null && proposedProject.getProjectStatus() == ACTIVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an entrepreneur with the given email has proposed a project.
|
||||
*
|
||||
* @param email The email of the entrepreneur.
|
||||
* @return true if the entrepreneur has a proposed project, false otherwise.
|
||||
*/
|
||||
public Boolean entrepreneurHasPendingRequestedProject(String email) {
|
||||
User user = this.userService.getUserByEmail(email);
|
||||
if (user == null) {
|
||||
return false;
|
||||
}
|
||||
Long userId = user.getIdUser();
|
||||
|
||||
Entrepreneur entrepreneur = this.entrepreneurService.getEntrepreneurById(userId);
|
||||
if (entrepreneur == null) {
|
||||
return false;
|
||||
}
|
||||
Project proposedProject = entrepreneur.getProjectProposed();
|
||||
if (entrepreneur.getProjectProposed() == null) {
|
||||
return false;
|
||||
}
|
||||
return proposedProject.getProjectStatus() == PENDING;
|
||||
}
|
||||
}
|
||||
|
@ -6,12 +6,17 @@ import enseirb.myinpulse.exception.UserNotFoundException;
|
||||
import enseirb.myinpulse.model.RoleRepresentation;
|
||||
import enseirb.myinpulse.model.UserRepresentation;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.springframework.web.client.RestClient;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.management.relation.RoleNotFoundException;
|
||||
|
||||
public class KeycloakApi {
|
||||
|
||||
protected static final Logger logger = LogManager.getLogger();
|
||||
static final String keycloakUrl;
|
||||
static final String realmName;
|
||||
|
||||
@ -29,44 +34,48 @@ public class KeycloakApi {
|
||||
realmName = System.getenv("VITE_KEYCLOAK_REALM");
|
||||
}
|
||||
|
||||
static String toBearer(String b) {
|
||||
return "Bearer " + b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses Keycloak API to retrieve a role representation of a role by its name
|
||||
*
|
||||
* @param roleName name of the role
|
||||
* @param bearer authorization header used by the client to authenticate to keycloak
|
||||
* @param token authorization header used by the client to authenticate to keycloak
|
||||
*/
|
||||
public static RoleRepresentation getRoleRepresentationByName(String roleName, String bearer)
|
||||
public static RoleRepresentation getRoleRepresentationByName(String roleName, String token)
|
||||
throws RoleNotFoundException {
|
||||
RoleRepresentation[] response =
|
||||
RoleRepresentation response =
|
||||
RestClient.builder()
|
||||
.baseUrl(keycloakUrl)
|
||||
.defaultHeader("Authorization", bearer)
|
||||
.defaultHeader("Authorization", toBearer(token))
|
||||
.build()
|
||||
.get()
|
||||
.uri("/admin/realms/{realmName}/roles/{roleName}", realmName, roleName)
|
||||
.retrieve()
|
||||
.body(RoleRepresentation[].class);
|
||||
|
||||
if (response == null || response.length == 0) {
|
||||
throw new RoleNotFoundException("Role not found");
|
||||
}
|
||||
return response[0];
|
||||
.body(RoleRepresentation.class);
|
||||
/*
|
||||
{"id":"7a845f2e-c832-4465-8cd8-894d72bc13f1","name":"MyINPulse-entrepreneur","description":"Role for entrepreneur","composite":false,"clientRole":false,"containerId":"0d6f691b-e328-471a-b89e-c30bd7e5b6b0","attributes":{}}
|
||||
*/
|
||||
// TODO: check what happens when role does not exist
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use keycloak API to to retreive a userID via his name or email.
|
||||
*
|
||||
* @param username username or mail of the user
|
||||
* @param bearer bearer of the user, allowing access to database
|
||||
* @param token bearer of the user, allowing access to database
|
||||
* @return the userid, as a String
|
||||
* @throws UserNotFoundException
|
||||
*/
|
||||
public static String getUserIdByName(String username, String bearer)
|
||||
public static String getUserIdByName(String username, String token)
|
||||
throws UserNotFoundException {
|
||||
UserRepresentation[] response =
|
||||
RestClient.builder()
|
||||
.baseUrl(keycloakUrl)
|
||||
.defaultHeader("Authorization", bearer)
|
||||
.defaultHeader("Authorization", toBearer(token))
|
||||
.build()
|
||||
.get()
|
||||
.uri(
|
||||
@ -91,27 +100,26 @@ public class KeycloakApi {
|
||||
*
|
||||
* @param username
|
||||
* @param roleName
|
||||
* @param bearer
|
||||
* @param token
|
||||
* @throws RoleNotFoundException
|
||||
* @throws UserNotFoundException
|
||||
*/
|
||||
public static void setRoleToUser(String username, String roleName, String bearer)
|
||||
public static void setRoleToUser(String username, String roleName, String token)
|
||||
throws RoleNotFoundException, UserNotFoundException {
|
||||
RoleRepresentation roleRepresentation = getRoleRepresentationByName(roleName, bearer);
|
||||
String userId = getUserIdByName(username, bearer);
|
||||
|
||||
RoleRepresentation roleRepresentation = getRoleRepresentationByName(roleName, token);
|
||||
String userId = getUserIdByName(username, token);
|
||||
List<RoleRepresentation> rolesToAdd = List.of(roleRepresentation);
|
||||
logger.debug("Adding role {} to user {}", roleRepresentation.id, userId);
|
||||
RestClient.builder()
|
||||
.baseUrl(keycloakUrl)
|
||||
.defaultHeader("Authorization", bearer)
|
||||
.defaultHeader("Authorization", toBearer(token))
|
||||
.build()
|
||||
.post()
|
||||
.uri(
|
||||
"/admin/realms/${realmName}/users/${userId}/role-mappings/realm",
|
||||
realmName,
|
||||
userId)
|
||||
.body(roleRepresentation)
|
||||
.uri("/admin/realms/" + realmName + "/users/" + userId + "/role-mappings/realm")
|
||||
.body(rolesToAdd)
|
||||
.contentType(APPLICATION_JSON)
|
||||
.retrieve();
|
||||
.retrieve()
|
||||
.toBodilessEntity();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,7 +2,8 @@ package enseirb.myinpulse.service;
|
||||
|
||||
import com.itextpdf.text.*;
|
||||
import com.itextpdf.text.pdf.PdfWriter;
|
||||
|
||||
import enseirb.myinpulse.controller.AdminApi;
|
||||
import enseirb.myinpulse.controller.EntrepreneurApi;
|
||||
import enseirb.myinpulse.model.*;
|
||||
import enseirb.myinpulse.service.database.*;
|
||||
|
||||
@ -24,11 +25,19 @@ import java.nio.file.StandardCopyOption;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Service
|
||||
public class SharedApiService {
|
||||
|
||||
private final AdminApi adminApi;
|
||||
|
||||
private final EntrepreneurApi entrepreneurApi;
|
||||
|
||||
protected static final Logger logger = LogManager.getLogger();
|
||||
|
||||
private final ProjectService projectService;
|
||||
@ -44,12 +53,16 @@ public class SharedApiService {
|
||||
EntrepreneurService entrepreneurService,
|
||||
SectionCellService sectionCellService,
|
||||
AppointmentService appointmentService,
|
||||
UtilsService utilsService) {
|
||||
UtilsService utilsService,
|
||||
EntrepreneurApi entrepreneurApi,
|
||||
AdminApi adminApi) {
|
||||
this.projectService = projectService;
|
||||
this.entrepreneurService = entrepreneurService;
|
||||
this.sectionCellService = sectionCellService;
|
||||
this.appointmentService = appointmentService;
|
||||
this.utilsService = utilsService;
|
||||
this.entrepreneurApi = entrepreneurApi;
|
||||
this.adminApi = adminApi;
|
||||
}
|
||||
|
||||
// TODO filter this with date
|
||||
@ -69,10 +82,54 @@ public class SharedApiService {
|
||||
LocalDateTime dateTime = LocalDateTime.parse(date, formatter);
|
||||
|
||||
Project project = this.projectService.getProjectById(projectId);
|
||||
return this.sectionCellService.getSectionCellsByProjectAndSectionIdBeforeDate(
|
||||
return this.sectionCellService.getLatestSectionCellsByIdReferenceBeforeDate(
|
||||
project, sectionId, dateTime);
|
||||
}
|
||||
|
||||
// Retrieve all up to date (for every sectionId) sectionCells of a project
|
||||
public Iterable<SectionCell> getAllSectionCells(long projectId, String mail) {
|
||||
if (!utilsService.isAllowedToCheckProject(mail, projectId)) {
|
||||
logger.warn(
|
||||
"User {} tried to check section cells of the project {} but is not allowed to.",
|
||||
mail,
|
||||
projectId);
|
||||
throw new ResponseStatusException(
|
||||
HttpStatus.UNAUTHORIZED, "You're not allowed to check this project");
|
||||
}
|
||||
|
||||
Project project = this.projectService.getProjectById(projectId);
|
||||
|
||||
Map<Long, SectionCell> latestSectionCellsMap =
|
||||
new HashMap<>(); // List for the intermediate result
|
||||
|
||||
// Iterate through all SectionCells associated with the project
|
||||
// This loop iterates over project.getListSectionCell() but does NOT modify it which causes
|
||||
// ConcurrentModificationException.
|
||||
// Modifications are done only on the latestSectionCellsMap (which is safe).
|
||||
project.getListSectionCell() // <-- Iterating over the original list (read-only)
|
||||
.forEach(
|
||||
projectCell -> {
|
||||
Long idReference = projectCell.getIdReference();
|
||||
// Check if we have already seen a SectionCell with this idReference in
|
||||
// our map
|
||||
if (latestSectionCellsMap.containsKey(idReference)) {
|
||||
SectionCell existingCell = latestSectionCellsMap.get(idReference);
|
||||
// Compare modification dates. If the current cell is newer, replace
|
||||
// the one in the map.
|
||||
if (projectCell
|
||||
.getModificationDate()
|
||||
.isAfter(existingCell.getModificationDate())) {
|
||||
latestSectionCellsMap.put(idReference, projectCell);
|
||||
}
|
||||
} else {
|
||||
// If this is the first time we encounter this idReference, add the
|
||||
// cell to the map.
|
||||
latestSectionCellsMap.put(idReference, projectCell);
|
||||
}
|
||||
});
|
||||
return new ArrayList<>(latestSectionCellsMap.values());
|
||||
}
|
||||
|
||||
// TODO: test
|
||||
public Iterable<Entrepreneur> getEntrepreneursByProjectId(long projectId, String mail) {
|
||||
if (!utilsService.isAllowedToCheckProject(mail, projectId)) {
|
||||
@ -114,18 +171,26 @@ public class SharedApiService {
|
||||
"User {} tried to check the appointments related to the project {}",
|
||||
mail,
|
||||
projectId);
|
||||
Iterable<SectionCell> sectionCells =
|
||||
this.sectionCellService.getSectionCellsByProject(
|
||||
projectService.getProjectById(projectId),
|
||||
2L); // sectionId useless in this function ?
|
||||
List<Appointment> appointments = new ArrayList<Appointment>();
|
||||
sectionCells.forEach(
|
||||
|
||||
Project project = projectService.getProjectById(projectId);
|
||||
|
||||
Iterable<SectionCell> sectionCellsIterable =
|
||||
this.sectionCellService.getSectionCellsByProject(project);
|
||||
|
||||
// Use a Set to collect unique appointments
|
||||
Set<Appointment> uniqueAppointments = new HashSet<>();
|
||||
|
||||
sectionCellsIterable.forEach(
|
||||
sectionCell -> {
|
||||
appointments.addAll(
|
||||
List<Appointment> sectionAppointments =
|
||||
this.sectionCellService.getAppointmentsBySectionCellId(
|
||||
sectionCell.getIdSectionCell()));
|
||||
sectionCell.getIdSectionCell());
|
||||
// Add all appointments from this section cell to the Set
|
||||
uniqueAppointments.addAll(sectionAppointments);
|
||||
});
|
||||
return appointments;
|
||||
|
||||
// Convert the Set back to a List for the return value
|
||||
return new ArrayList<>(uniqueAppointments);
|
||||
}
|
||||
|
||||
public void getPDFReport(long appointmentId, String mail)
|
||||
@ -247,6 +312,13 @@ public class SharedApiService {
|
||||
sectionCell -> {
|
||||
sectionCell.updateAppointmentSectionCell(newAppointment);
|
||||
});
|
||||
newAppointment.getAppointmentReport().setAppointmentReport(newAppointment);
|
||||
|
||||
/*
|
||||
* On initial insertion, the resport value is null unless that report does already exist in the db somewhere
|
||||
* If a non null value is passed and it does not exist in db it will throw an exception
|
||||
*/
|
||||
if (newAppointment.getAppointmentReport() != null) {
|
||||
newAppointment.getAppointmentReport().setAppointmentReport(newAppointment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@Service
|
||||
public class UtilsService {
|
||||
|
||||
@ -44,19 +46,36 @@ public class UtilsService {
|
||||
}
|
||||
User user = this.userService.getUserByEmail(mail);
|
||||
Entrepreneur entrepreneur = this.entrepreneurService.getEntrepreneurById(user.getIdUser());
|
||||
if (entrepreneur == null) {
|
||||
logger.debug("testing access with an unknown Entrepreneur");
|
||||
return false;
|
||||
}
|
||||
if (entrepreneur.getProjectParticipation() == null) {
|
||||
logger.debug("testing access with an user with no project participation");
|
||||
return false;
|
||||
}
|
||||
Project project = this.projectService.getProjectById(projectId);
|
||||
return entrepreneur.getProjectParticipation() == project;
|
||||
// We compare the ID instead of the project themselves
|
||||
return Objects.equals(
|
||||
entrepreneur.getProjectParticipation().getIdProject(), project.getIdProject());
|
||||
}
|
||||
|
||||
// TODO: test
|
||||
Boolean isAnAdmin(String mail) {
|
||||
public Boolean isAnAdmin(String mail) {
|
||||
try {
|
||||
long userId = this.userService.getUserByEmail(mail).getIdUser();
|
||||
Administrator a = this.administratorService.getAdministratorById(userId);
|
||||
return true;
|
||||
} catch (ResponseStatusException e) {
|
||||
logger.info(e);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean checkEntrepreneurNotPending(String email) {
|
||||
// Throws 404 if user not found
|
||||
User user = userService.getUserByEmail(email);
|
||||
return !user.isPending();
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
package enseirb.myinpulse.service.database;
|
||||
|
||||
import enseirb.myinpulse.model.Administrator;
|
||||
import enseirb.myinpulse.model.Annotation;
|
||||
import enseirb.myinpulse.model.MakeAppointment;
|
||||
import enseirb.myinpulse.model.Project;
|
||||
import enseirb.myinpulse.repository.AdministratorRepository;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@ -52,6 +55,49 @@ public class AdministratorService {
|
||||
return this.administratorRepository.save(administrator);
|
||||
}
|
||||
|
||||
public void updateAdministratorListProject(long idAdministrator, Project project) {
|
||||
Administrator administrator = getAdministratorById(idAdministrator);
|
||||
administrator.updateListProject(project);
|
||||
this.administratorRepository.save(administrator);
|
||||
}
|
||||
|
||||
public void updateAdministratorListAnnotation(long idAdministrator, Annotation annotation) {
|
||||
Administrator administrator = getAdministratorById(idAdministrator);
|
||||
administrator.updateListAnnotation(annotation);
|
||||
this.administratorRepository.save(administrator);
|
||||
}
|
||||
|
||||
public void updateAdministratorMakeAppointment(
|
||||
long idAdministrator, MakeAppointment makeAppointment) {
|
||||
Administrator administrator = getAdministratorById(idAdministrator);
|
||||
administrator.setMakeAppointment(makeAppointment);
|
||||
this.administratorRepository.save(administrator);
|
||||
}
|
||||
|
||||
public Administrator updateAdministrator(
|
||||
Long idAdministrator,
|
||||
Project project,
|
||||
Annotation annotation,
|
||||
MakeAppointment makeAppointment) {
|
||||
Optional<Administrator> administrator = administratorRepository.findById(idAdministrator);
|
||||
if (administrator.isEmpty()) {
|
||||
logger.error(
|
||||
"updateAdministrator : No administrator found with id {}", idAdministrator);
|
||||
throw new ResponseStatusException(
|
||||
HttpStatus.NOT_FOUND, "Cet administrateur n'existe pas");
|
||||
}
|
||||
if (project != null) {
|
||||
administrator.get().updateListProject(project);
|
||||
}
|
||||
if (annotation != null) {
|
||||
administrator.get().updateListAnnotation(annotation);
|
||||
}
|
||||
if (makeAppointment != null) {
|
||||
administrator.get().setMakeAppointment(makeAppointment);
|
||||
}
|
||||
return this.administratorRepository.save(administrator.get());
|
||||
}
|
||||
|
||||
/*
|
||||
public Administrator getAdministratorByProject(Project project) {
|
||||
r
|
||||
|
@ -1,6 +1,8 @@
|
||||
package enseirb.myinpulse.service.database;
|
||||
|
||||
import enseirb.myinpulse.model.Administrator;
|
||||
import enseirb.myinpulse.model.Annotation;
|
||||
import enseirb.myinpulse.model.SectionCell;
|
||||
import enseirb.myinpulse.repository.AnnotationRepository;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@ -46,6 +48,12 @@ public class AnnotationService {
|
||||
this.annotationRepository.deleteById(id);
|
||||
}
|
||||
|
||||
public void updateAnnotationComment(long idAnnotation, String comment) {
|
||||
Annotation annotation = getAnnotationById(idAnnotation);
|
||||
annotation.setComment(comment);
|
||||
this.annotationRepository.save(annotation);
|
||||
}
|
||||
|
||||
public Annotation updateAnnotation(Long id, String comment) {
|
||||
Optional<Annotation> annotation = annotationRepository.findById(id);
|
||||
if (annotation.isEmpty()) {
|
||||
@ -58,4 +66,16 @@ public class AnnotationService {
|
||||
}
|
||||
return this.annotationRepository.save(annotation.get());
|
||||
}
|
||||
|
||||
public void updateAnnotationSectionCell(long idAnnotation, SectionCell sectionCell) {
|
||||
Annotation annotation = getAnnotationById(idAnnotation);
|
||||
annotation.setSectionCellAnnotation(sectionCell);
|
||||
this.annotationRepository.save(annotation);
|
||||
}
|
||||
|
||||
public void updateAnnotationAdministrator(long idAnnotation, Administrator administrator) {
|
||||
Annotation annotation = getAnnotationById(idAnnotation);
|
||||
annotation.setAdministratorAnnotation(administrator);
|
||||
this.annotationRepository.save(annotation);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package enseirb.myinpulse.service.database;
|
||||
|
||||
import enseirb.myinpulse.model.Appointment;
|
||||
import enseirb.myinpulse.model.SectionCell;
|
||||
import enseirb.myinpulse.repository.AppointmentRepository;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@ -47,13 +48,50 @@ public class AppointmentService {
|
||||
this.appointmentRepository.deleteById(id);
|
||||
}
|
||||
|
||||
public void updateAppointmentDate(long idAppointment, LocalDate date) {
|
||||
Appointment appointment = getAppointmentById(idAppointment);
|
||||
appointment.setAppointmentDate(date);
|
||||
this.appointmentRepository.save(appointment);
|
||||
}
|
||||
|
||||
public void updateAppointmentTime(long idAppointment, LocalTime time) {
|
||||
Appointment appointment = getAppointmentById(idAppointment);
|
||||
appointment.setAppointmentTime(time);
|
||||
this.appointmentRepository.save(appointment);
|
||||
}
|
||||
|
||||
public void updateAppointmentDuration(long idAppointment, LocalTime duration) {
|
||||
Appointment appointment = getAppointmentById(idAppointment);
|
||||
appointment.setAppointmentDuration(duration);
|
||||
this.appointmentRepository.save(appointment);
|
||||
}
|
||||
|
||||
public void updateAppointmentPlace(long idAppointment, String place) {
|
||||
Appointment appointment = getAppointmentById(idAppointment);
|
||||
appointment.setAppointmentPlace(place);
|
||||
this.appointmentRepository.save(appointment);
|
||||
}
|
||||
|
||||
public void updateAppointmentSubject(long idAppointment, String subject) {
|
||||
Appointment appointment = getAppointmentById(idAppointment);
|
||||
appointment.setAppointmentSubject(subject);
|
||||
this.appointmentRepository.save(appointment);
|
||||
}
|
||||
|
||||
public void updateAppointmentListSectionCell(long idAppointment, SectionCell sectionCell) {
|
||||
Appointment appointment = getAppointmentById(idAppointment);
|
||||
appointment.updateListSectionCell(sectionCell);
|
||||
this.appointmentRepository.save(appointment);
|
||||
}
|
||||
|
||||
public Appointment updateAppointment(
|
||||
Long id,
|
||||
LocalDate appointmentDate,
|
||||
LocalTime appointmentTime,
|
||||
LocalTime appointmentDuration,
|
||||
String appointmentPlace,
|
||||
String appointmentSubject) {
|
||||
String appointmentSubject,
|
||||
SectionCell sectionCell) {
|
||||
Optional<Appointment> appointment = this.appointmentRepository.findById(id);
|
||||
if (appointment.isEmpty()) {
|
||||
logger.error("updateAppointment : No appointment found with id {}", id);
|
||||
@ -74,6 +112,9 @@ public class AppointmentService {
|
||||
if (appointmentSubject != null) {
|
||||
appointment.get().setAppointmentSubject(appointmentSubject);
|
||||
}
|
||||
if (sectionCell != null) {
|
||||
appointment.get().updateListSectionCell(sectionCell);
|
||||
}
|
||||
return this.appointmentRepository.save(appointment.get());
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package enseirb.myinpulse.service.database;
|
||||
|
||||
import enseirb.myinpulse.model.Entrepreneur;
|
||||
import enseirb.myinpulse.model.MakeAppointment;
|
||||
import enseirb.myinpulse.model.Project;
|
||||
import enseirb.myinpulse.repository.EntrepreneurRepository;
|
||||
|
||||
@ -41,8 +42,52 @@ public class EntrepreneurService {
|
||||
return this.entrepreneurRepository.save(entrepreneur);
|
||||
}
|
||||
|
||||
public void updateEntrepreneurSchool(long idEntrepreneur, String school) {
|
||||
Entrepreneur entrepreneur = getEntrepreneurById(idEntrepreneur);
|
||||
entrepreneur.setSchool(school);
|
||||
this.entrepreneurRepository.save(entrepreneur);
|
||||
}
|
||||
|
||||
public void updateEntrepreneurCourse(long idEntrepreneur, String course) {
|
||||
Entrepreneur entrepreneur = getEntrepreneurById(idEntrepreneur);
|
||||
entrepreneur.setCourse(course);
|
||||
this.entrepreneurRepository.save(entrepreneur);
|
||||
}
|
||||
|
||||
public void updateEntrepreneurSneeStatus(long idEntrepreneur, boolean status) {
|
||||
Entrepreneur entrepreneur = getEntrepreneurById(idEntrepreneur);
|
||||
entrepreneur.setSneeStatus(status);
|
||||
this.entrepreneurRepository.save(entrepreneur);
|
||||
}
|
||||
|
||||
public void updateEntrepreneurProjectParticipation(
|
||||
long idEntrepreneur, Project projectParticipation) {
|
||||
Entrepreneur entrepreneur = getEntrepreneurById(idEntrepreneur);
|
||||
entrepreneur.setProjectParticipation(projectParticipation);
|
||||
this.entrepreneurRepository.save(entrepreneur);
|
||||
}
|
||||
|
||||
public void updateEntrepreneurProjectProposed(long idEntrepreneur, Project projectProposed) {
|
||||
Entrepreneur entrepreneur = getEntrepreneurById(idEntrepreneur);
|
||||
entrepreneur.setProjectParticipation(projectProposed);
|
||||
this.entrepreneurRepository.save(entrepreneur);
|
||||
}
|
||||
|
||||
public void updateEntrepreneurMakeAppointment(
|
||||
long idEntrepreneur, MakeAppointment makeAppointment) {
|
||||
Entrepreneur entrepreneur = getEntrepreneurById(idEntrepreneur);
|
||||
entrepreneur.setMakeAppointment(makeAppointment);
|
||||
this.entrepreneurRepository.save(entrepreneur);
|
||||
}
|
||||
|
||||
public Entrepreneur updateEntrepreneur(
|
||||
Long id, String school, String course, Boolean sneeStatus) {
|
||||
Long id,
|
||||
String school,
|
||||
String course,
|
||||
Boolean sneeStatus,
|
||||
Project projectParticipation,
|
||||
Project projectProposed,
|
||||
MakeAppointment makeAppointment) {
|
||||
Optional<Entrepreneur> entrepreneur = entrepreneurRepository.findById(id);
|
||||
if (entrepreneur.isEmpty()) {
|
||||
logger.error("updateEntrepreneur : No entrepreneur found with id {}", id);
|
||||
@ -58,10 +103,33 @@ public class EntrepreneurService {
|
||||
if (sneeStatus != null) {
|
||||
entrepreneur.get().setSneeStatus(sneeStatus);
|
||||
}
|
||||
if (projectParticipation != null) {
|
||||
entrepreneur.get().setProjectParticipation(projectParticipation);
|
||||
}
|
||||
if (projectProposed != null) {
|
||||
entrepreneur.get().setProjectParticipation(projectProposed);
|
||||
}
|
||||
if (makeAppointment != null) {
|
||||
entrepreneur.get().setMakeAppointment(makeAppointment);
|
||||
}
|
||||
return this.entrepreneurRepository.save(entrepreneur.get());
|
||||
}
|
||||
|
||||
public Iterable<Entrepreneur> GetEntrepreneurByProject(Project project) {
|
||||
return this.entrepreneurRepository.getEntrepreneurByProjectParticipation(project);
|
||||
}
|
||||
|
||||
public void deleteEntrepreneur(Entrepreneur e) {
|
||||
this.entrepreneurRepository.delete(e);
|
||||
}
|
||||
|
||||
public void validateEntrepreneurById(Long id) {
|
||||
System.out.println("\nVALIDATING\n");
|
||||
Optional<Entrepreneur> e = this.entrepreneurRepository.findById(id);
|
||||
if (e.isEmpty()) {
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Entrepreneur n'existe pas");
|
||||
}
|
||||
e.get().setPending(false);
|
||||
this.entrepreneurRepository.save(e.get());
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,7 @@ package enseirb.myinpulse.service.database;
|
||||
|
||||
import static enseirb.myinpulse.model.ProjectDecisionValue.PENDING;
|
||||
|
||||
import enseirb.myinpulse.model.Administrator;
|
||||
import enseirb.myinpulse.model.Project;
|
||||
import enseirb.myinpulse.model.ProjectDecisionValue;
|
||||
import enseirb.myinpulse.model.*;
|
||||
import enseirb.myinpulse.repository.ProjectRepository;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@ -14,7 +12,6 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@ -52,12 +49,50 @@ public class ProjectService {
|
||||
return this.projectRepository.save(project);
|
||||
}
|
||||
|
||||
public void updateProjectName(long idProject, String name) {
|
||||
Project project = getProjectById(idProject);
|
||||
project.setProjectName(name);
|
||||
this.projectRepository.save(project);
|
||||
}
|
||||
|
||||
public void updateProjectLogo(long idProject, byte[] logo) {
|
||||
Project project = getProjectById(idProject);
|
||||
project.setLogo(logo);
|
||||
this.projectRepository.save(project);
|
||||
}
|
||||
|
||||
public void updateProjectStatus(long idProject, ProjectDecisionValue status) {
|
||||
Project project = getProjectById(idProject);
|
||||
project.setProjectStatus(status);
|
||||
this.projectRepository.save(project);
|
||||
}
|
||||
|
||||
public void updateProjectEntrepreneurParticipation(
|
||||
long idProject, Entrepreneur entrepreneurParticipation) {
|
||||
Project project = getProjectById(idProject);
|
||||
project.updateListEntrepreneurParticipation(entrepreneurParticipation);
|
||||
this.projectRepository.save(project);
|
||||
}
|
||||
|
||||
public void updateProjectListSectionCell(long idProject, SectionCell sectionCell) {
|
||||
Project project = getProjectById(idProject);
|
||||
project.updateListSectionCell(sectionCell);
|
||||
this.projectRepository.save(project);
|
||||
}
|
||||
|
||||
public void updateProjectAdministrator(long idProject, Administrator administrator) {
|
||||
Project project = getProjectById(idProject);
|
||||
project.setProjectAdministrator(administrator);
|
||||
this.projectRepository.save(project);
|
||||
}
|
||||
|
||||
public Project updateProject(
|
||||
Long id,
|
||||
String projectName,
|
||||
byte[] logo,
|
||||
LocalDate creationDate,
|
||||
ProjectDecisionValue projectStatus,
|
||||
Entrepreneur entrepreneurParticipation,
|
||||
SectionCell sectionCell,
|
||||
Administrator administrator) {
|
||||
Optional<Project> project = this.projectRepository.findById(id);
|
||||
|
||||
@ -73,11 +108,6 @@ public class ProjectService {
|
||||
if (logo != null) {
|
||||
project.get().setLogo(logo);
|
||||
}
|
||||
|
||||
if (creationDate != null) {
|
||||
project.get().setCreationDate(creationDate);
|
||||
}
|
||||
|
||||
if (projectStatus != null) {
|
||||
// TODO: check if this is really useful
|
||||
/*
|
||||
@ -89,7 +119,12 @@ public class ProjectService {
|
||||
*/
|
||||
project.get().setProjectStatus(projectStatus);
|
||||
}
|
||||
|
||||
if (entrepreneurParticipation != null) {
|
||||
project.get().updateListEntrepreneurParticipation(entrepreneurParticipation);
|
||||
}
|
||||
if (sectionCell != null) {
|
||||
project.get().updateListSectionCell(sectionCell);
|
||||
}
|
||||
if (administrator != null) {
|
||||
project.get().setProjectAdministrator(administrator);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package enseirb.myinpulse.service.database;
|
||||
|
||||
import enseirb.myinpulse.model.Appointment;
|
||||
import enseirb.myinpulse.model.Report;
|
||||
import enseirb.myinpulse.repository.ReportRepository;
|
||||
|
||||
@ -46,7 +47,19 @@ public class ReportService {
|
||||
this.reportRepository.deleteById(id);
|
||||
}
|
||||
|
||||
public Report updateReport(Long id, String reportContent) {
|
||||
public void updateReportContent(long idReport, String content) {
|
||||
Report report = getReportById(idReport);
|
||||
report.setReportContent(content);
|
||||
this.reportRepository.save(report);
|
||||
}
|
||||
|
||||
public void updateReportAppointment(long idReport, Appointment appointment) {
|
||||
Report report = getReportById(idReport);
|
||||
report.setAppointmentReport(appointment);
|
||||
this.reportRepository.save(report);
|
||||
}
|
||||
|
||||
public Report updateReport(Long id, String reportContent, Appointment appointment) {
|
||||
Optional<Report> report = this.reportRepository.findById(id);
|
||||
if (report.isEmpty()) {
|
||||
logger.error("updateReport : No report found with id {}", id);
|
||||
@ -55,6 +68,9 @@ public class ReportService {
|
||||
if (reportContent != null) {
|
||||
report.get().setReportContent(reportContent);
|
||||
}
|
||||
if (appointment != null) {
|
||||
report.get().setAppointmentReport(appointment);
|
||||
}
|
||||
return this.reportRepository.save(report.get());
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package enseirb.myinpulse.service.database;
|
||||
|
||||
import enseirb.myinpulse.model.Annotation;
|
||||
import enseirb.myinpulse.model.Appointment;
|
||||
import enseirb.myinpulse.model.Project;
|
||||
import enseirb.myinpulse.model.SectionCell;
|
||||
@ -13,7 +14,10 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
@ -50,22 +54,63 @@ public class SectionCellService {
|
||||
this.sectionCellRepository.deleteById(id);
|
||||
}
|
||||
|
||||
public void updateSectionCellReferenceId(Long idSectionCell, Long referenceId) {
|
||||
SectionCell sectionCell = this.getSectionCellById(idSectionCell);
|
||||
sectionCell.setIdReference(referenceId);
|
||||
this.sectionCellRepository.save(sectionCell);
|
||||
}
|
||||
|
||||
public void updateSectionCellContent(long idSectionCell, String content) {
|
||||
SectionCell sectionCell = getSectionCellById(idSectionCell);
|
||||
sectionCell.setContentSectionCell(content);
|
||||
this.sectionCellRepository.save(sectionCell);
|
||||
}
|
||||
|
||||
public void updateSectionCellListAppointment(long idSectionCell, Appointment appointment) {
|
||||
SectionCell sectionCell = getSectionCellById(idSectionCell);
|
||||
sectionCell.updateAppointmentSectionCell(appointment);
|
||||
this.sectionCellRepository.save(sectionCell);
|
||||
}
|
||||
|
||||
public void updateSectionCellListAnnotation(long idSectionCell, Annotation annotation) {
|
||||
SectionCell sectionCell = getSectionCellById(idSectionCell);
|
||||
sectionCell.updateListAnnotation(annotation);
|
||||
this.sectionCellRepository.save(sectionCell);
|
||||
}
|
||||
|
||||
public void updateSectionCellProject(long idSectionCell, Project project) {
|
||||
SectionCell sectionCell = getSectionCellById(idSectionCell);
|
||||
sectionCell.setProjectSectionCell(project);
|
||||
this.sectionCellRepository.save(sectionCell);
|
||||
}
|
||||
|
||||
public SectionCell updateSectionCell(
|
||||
Long id, Long sectionId, String contentSectionCell, LocalDateTime modificationDate) {
|
||||
Long id,
|
||||
String contentSectionCell,
|
||||
Appointment appointment,
|
||||
Annotation annotation,
|
||||
Project project) {
|
||||
Optional<SectionCell> sectionCell = this.sectionCellRepository.findById(id);
|
||||
if (sectionCell.isEmpty()) {
|
||||
logger.error("updateSectionCell : No sectionCell found with id {}", id);
|
||||
throw new ResponseStatusException(
|
||||
HttpStatus.NOT_FOUND, "Cette cellule de section n'existe pas");
|
||||
}
|
||||
if (sectionId != null) {
|
||||
sectionCell.get().setSectionId(sectionId);
|
||||
}
|
||||
if (contentSectionCell != null) {
|
||||
sectionCell.get().setContentSectionCell(contentSectionCell);
|
||||
sectionCell.get().setModificationDate(LocalDateTime.now());
|
||||
}
|
||||
if (modificationDate != null) {
|
||||
sectionCell.get().setModificationDate(modificationDate);
|
||||
if (appointment != null) {
|
||||
sectionCell.get().updateAppointmentSectionCell(appointment);
|
||||
sectionCell.get().setModificationDate(LocalDateTime.now());
|
||||
}
|
||||
if (annotation != null) {
|
||||
sectionCell.get().updateListAnnotation(annotation);
|
||||
sectionCell.get().setModificationDate(LocalDateTime.now());
|
||||
}
|
||||
if (project != null) {
|
||||
sectionCell.get().setProjectSectionCell(project);
|
||||
sectionCell.get().setModificationDate(LocalDateTime.now());
|
||||
}
|
||||
return this.sectionCellRepository.save(sectionCell.get());
|
||||
}
|
||||
@ -74,6 +119,18 @@ public class SectionCellService {
|
||||
return this.sectionCellRepository.findByProjectSectionCellAndSectionId(project, sectionId);
|
||||
}
|
||||
|
||||
public Iterable<SectionCell> getSectionCellsByProject(Project project) {
|
||||
logger.info("Fetching SectionCells for Project ID: {}", project.getIdProject());
|
||||
Iterable<SectionCell> sectionCells =
|
||||
this.sectionCellRepository.findByProjectSectionCell(project);
|
||||
List<SectionCell> sectionCellList = new ArrayList<>();
|
||||
sectionCells.forEach(
|
||||
cell -> {
|
||||
sectionCellList.add(cell);
|
||||
});
|
||||
return sectionCellList;
|
||||
}
|
||||
|
||||
public Long getProjectId(Long sectionCellId) {
|
||||
SectionCell sectionCell = getSectionCellById(sectionCellId);
|
||||
Project sectionProject = sectionCell.getProjectSectionCell();
|
||||
@ -90,4 +147,37 @@ public class SectionCellService {
|
||||
return sectionCellRepository.findByProjectSectionCellAndSectionIdAndModificationDateBefore(
|
||||
project, sectionId, date);
|
||||
}
|
||||
|
||||
public Iterable<SectionCell> getLatestSectionCellsByIdReferenceBeforeDate(
|
||||
Project project, long sectionId, LocalDateTime date) {
|
||||
|
||||
// 1. Fetch ALL relevant SectionCells modified before the date
|
||||
Iterable<SectionCell> allMatchingCells =
|
||||
sectionCellRepository.findByProjectSectionCellAndSectionIdAndModificationDateBefore(
|
||||
project, sectionId, date);
|
||||
|
||||
// 2. Find the latest for each idReference
|
||||
Map<Long, SectionCell> latestCellsByIdReference = new HashMap<>();
|
||||
|
||||
for (SectionCell cell : allMatchingCells) {
|
||||
Long idReference = cell.getIdReference();
|
||||
|
||||
// Check if we've seen this idReference before
|
||||
if (latestCellsByIdReference.containsKey(idReference)) {
|
||||
// If yes, compare modification dates
|
||||
SectionCell existingLatest = latestCellsByIdReference.get(idReference);
|
||||
|
||||
// If the current cell is more recent, update the map
|
||||
if (cell.getModificationDate().isAfter(existingLatest.getModificationDate())) {
|
||||
latestCellsByIdReference.put(idReference, cell);
|
||||
}
|
||||
} else {
|
||||
// If this is the first time we see this idReference, add it to the map
|
||||
latestCellsByIdReference.put(idReference, cell);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Return the collection of the latest cells (the values from the map)
|
||||
return latestCellsByIdReference.values();
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,15 @@ public class UserService {
|
||||
return this.userRepository.findAll();
|
||||
}
|
||||
|
||||
public User getUserById(long id) {
|
||||
Optional<User> user = this.userRepository.findById(id);
|
||||
if (user.isEmpty()) {
|
||||
logger.error("getUserById : No user found with id {}", id);
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Cet utilisateur n'existe pas");
|
||||
}
|
||||
return user.get();
|
||||
}
|
||||
|
||||
// TODO
|
||||
public User getUserByEmail(String email) {
|
||||
Optional<User> opt_user = this.userRepository.findByPrimaryMail(email);
|
||||
@ -49,6 +58,36 @@ public class UserService {
|
||||
return this.userRepository.save(user);
|
||||
}
|
||||
|
||||
public void updateUserSurname(long idUser, String surname) {
|
||||
User user = getUserById(idUser);
|
||||
user.setUserSurname(surname);
|
||||
this.userRepository.save(user);
|
||||
}
|
||||
|
||||
public void updateUserName(long idUser, String name) {
|
||||
User user = getUserById(idUser);
|
||||
user.setUserName(name);
|
||||
this.userRepository.save(user);
|
||||
}
|
||||
|
||||
public void updateUserPrimaryMail(long idUser, String primaryMail) {
|
||||
User user = getUserById(idUser);
|
||||
user.setPrimaryMail(primaryMail);
|
||||
this.userRepository.save(user);
|
||||
}
|
||||
|
||||
public void updateUserSecondaryMail(long idUser, String secondaryMail) {
|
||||
User user = getUserById(idUser);
|
||||
user.setSecondaryMail(secondaryMail);
|
||||
this.userRepository.save(user);
|
||||
}
|
||||
|
||||
public void updateUserPhoneNumber(long idUser, String phoneNumber) {
|
||||
User user = getUserById(idUser);
|
||||
user.setPhoneNumber(phoneNumber);
|
||||
this.userRepository.save(user);
|
||||
}
|
||||
|
||||
public User updateUser(
|
||||
@PathVariable Long id,
|
||||
String userSurname,
|
||||
@ -78,4 +117,8 @@ public class UserService {
|
||||
}
|
||||
return this.userRepository.save(user.get());
|
||||
}
|
||||
|
||||
public Iterable<User> getPendingAccounts() {
|
||||
return this.userRepository.findAllByPendingEquals(true);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
spring.application.name=myinpulse
|
||||
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://localhost:7080/realms/test/protocol/openid-connect/certs
|
||||
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:7080/realms/test
|
||||
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://localhost:7080/realms/${VITE_KEYCLOAK_REALM}/protocol/openid-connect/certs
|
||||
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:7080/realms/${VITE_KEYCLOAK_REALM}
|
||||
spring.datasource.url=jdbc:postgresql://${DATABASE_URL}/${BACKEND_DB}
|
||||
spring.datasource.username=${BACKEND_USER}
|
||||
spring.datasource.password=${BACKEND_PASSWORD}
|
||||
|
@ -0,0 +1,13 @@
|
||||
spring.application.name=myinpulse
|
||||
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://localhost:7080/realms/test/protocol/openid-connect/certs
|
||||
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:7080/realms/test
|
||||
|
||||
logging.pattern.console=%d{yyyy-MMM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{15}) - %msg %n
|
||||
|
||||
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
|
||||
spring.datasource.driverClassName=org.h2.Driver
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=
|
||||
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
|
||||
|
||||
spring.jpa.hibernate.ddl-auto=create
|
@ -1,99 +0,0 @@
|
||||
TRUNCATE project, user_inpulse, entrepreneur, administrator, section_cell, appointment, report, annotation CASCADE;
|
||||
|
||||
SELECT setval('annotation_id_annotation_seq', 1, false);
|
||||
SELECT setval('appointment_id_appointment_seq', 1, false);
|
||||
SELECT setval('make_appointment_id_make_appointment_seq', 1, false);
|
||||
SELECT setval('project_id_project_seq', 1, false);
|
||||
SELECT setval('report_id_report_seq', 1, false);
|
||||
SELECT setval('section_cell_id_section_cell_seq', 1, false);
|
||||
SELECT setval('user_inpulse_id_user_seq', 1, false);
|
||||
|
||||
INSERT INTO user_inpulse (user_surname, user_name, primary_mail, secondary_mail, phone_number)
|
||||
VALUES ('Dupont', 'Dupond', 'super@mail.fr', 'super2@mail.fr', '06 45 72 45 98'),
|
||||
('Martin', 'Matin', 'genial@mail.fr', 'genial2@mail.fr', '06 52 14 58 73'),
|
||||
('Charvet', 'Lautre', 'mieux@tmail.fr', 'mieux2@tmail.fr', '07 49 82 16 35'),
|
||||
('Leguez', 'Theo', 'bof@mesmails.fr', 'bof2@mesmails.fr', '+33 6 78 14 25 29'),
|
||||
('Kia', 'Bi', 'special@mail.fr', 'special2@mail.fr', '07 65 31 38 95'),
|
||||
('Ducaillou', 'Pierre', 'maildefou@xyz.fr', 'maildefou2@xyz.fr', '06 54 78 12 62'),
|
||||
('Janine', 'Dave', 'janine@labri.fr', 'janine2@labri.fr', '06 87 12 45 95');
|
||||
|
||||
INSERT INTO administrator (id_administrator)
|
||||
VALUES (7);
|
||||
|
||||
INSERT INTO project (project_name, logo, creation_date, project_status, id_administrator)
|
||||
VALUES ('Eau du robinet', decode('013d7d16d7ad4fefb61bd95b765c8ceb', 'hex'), TO_DATE('01-OCT-2023', 'DD-MON-YYYY'),
|
||||
'En cours', 7),
|
||||
('Air oxygéné', decode('150647a0984e8f228cd14b54', 'hex'), TO_DATE('04-APR-2024', 'DD-MON-YYYY'), 'En cours', 7),
|
||||
('Débat concours', decode('022024abd5486e245c145dda65116f', 'hex'), TO_DATE('22-NOV-2023', 'DD-MON-YYYY'),
|
||||
'Suspendu', 7),
|
||||
('HDeirbMI', decode('ab548d6c1d595a2975e6476f544d14c55a', 'hex'), TO_DATE('07-DEC-2024', 'DD-MON-YYYY'),
|
||||
'Lancement', 7);
|
||||
|
||||
|
||||
INSERT INTO entrepreneur (school, course, snee_status, id_entrepreneur, id_project_participation, id_project_proposed)
|
||||
VALUES ('ENSEIRB-MATMECA', 'INFO', TRUE, 1, 4, 4),
|
||||
('ENSC', 'Cognitique', TRUE, 2, 2, null),
|
||||
('ENSEIRB-MATMECA', 'MATMECA', FALSE, 3, 3, 3),
|
||||
('SupOptique', 'Classique', TRUE, 4, 1, 1),
|
||||
('ENSEGID', 'Géoscience', FALSE, 5, 1, null),
|
||||
('ENSMAC', 'Matériaux composites - Mécanique', FALSE, 6, 2, 2);
|
||||
|
||||
|
||||
INSERT INTO section_cell (title, content_section_cell, modification_date, id_project)
|
||||
VALUES ('Problème', 'les problèmes...', TO_TIMESTAMP('15-JAN-2025 09:30:20', 'DD-MON-YYYY, HH24:MI:SS'), 2),
|
||||
('Segment de client', 'Le segment AB passant le client n°8 est de longueur 32mm.
|
||||
Le segment BC a quant à lui un longueur de 28mm. Quelle la longueur du segment AC ?',
|
||||
TO_TIMESTAMP('12-OCT-2022 17:47:38', 'DD-MON-YYYY, HH24:MI:SS'), 3),
|
||||
('Proposition de valeur unique', '''Son prix est de 2594€'' ''Ah oui c''est unique en effet',
|
||||
TO_TIMESTAMP('25-MAY-2024 11:12:04', 'DD-MON-YYYY, HH24:MI:SS'), 2),
|
||||
('Solution', 'Un problème ? Une solution', TO_TIMESTAMP('08-FEB-2024 10:17:53', 'DD-MON-YYYY, HH24:MI:SS'), 1),
|
||||
('Canaux', 'Ici nous avons la Seine, là-bas le Rhin, oh et plus loin le canal de Suez',
|
||||
TO_TIMESTAMP('19-JUL-2023 19:22:45', 'DD-MON-YYYY, HH24:MI:SS'), 4),
|
||||
('Sources de revenus', 'Y''en n''a pas on est pas payé. Enfin y''a du café quoi',
|
||||
TO_TIMESTAMP('12-JAN-2025 11:40:26', 'DD-MON-YYYY, HH24:MI:SS'), 1),
|
||||
('Structure des coûts', '''Ah oui là ça va faire au moins 1000€ par mois'', Eirbware',
|
||||
TO_TIMESTAMP('06-FEB-2025 13:04:06', 'DD-MON-YYYY, HH24:MI:SS'), 3),
|
||||
('Indicateurs clés', 'On apprend les clés comme des badges, ça se fait',
|
||||
TO_TIMESTAMP('05-FEB-2025 12:42:38', 'DD-MON-YYYY, HH24:MI:SS'), 4),
|
||||
('Avantages concurrentiel', 'On est meilleur', TO_TIMESTAMP('23-APR-2024 16:24:02', 'DD-MON-YYYY, HH24:MI:SS'),
|
||||
2);
|
||||
|
||||
INSERT INTO appointment (appointment_date, appointment_time, appointment_duration, appointment_place,
|
||||
appointment_subject)
|
||||
VALUES (TO_DATE('24-DEC-2023', 'DD-MON-YYYY'), '00:00:00', '00:37:53', 'À la maison', 'Ouvrir les cadeaux'),
|
||||
(TO_DATE('15-AUG-2024', 'DD-MON-YYYY'), '22:35:00', '00:12:36', 'Sur les quais ou dans un champ probablement',
|
||||
'BOUM BOUM les feux d''artifices (on fête quoi déjà ?)'),
|
||||
(TO_DATE('28-FEB-2023', 'DD-MON-YYYY'), '14:20:00', '00:20:00', 'Salle TD 15',
|
||||
'Ah mince c''est pas une année bissextile !'),
|
||||
(TO_DATE('23-JAN-2024', 'DD-MON-YYYY'), '12:56:27', '11:03:33', 'Là où le vent nous porte',
|
||||
'Journée la plus importante de l''année'),
|
||||
(TO_DATE('25-AUG-2025', 'DD-MON-YYYY'), '00:09:00', '01:00:00', 'Euh c''est par où l''amphi 56 ?',
|
||||
'Rentrée scolaire (il fait trop froid c''est quoi ça on est en août)');
|
||||
|
||||
INSERT INTO report (report_content, id_appointment)
|
||||
VALUES ('Ah oui ça c''est super, ah ouais j''aime bien, bien vu de penser à ça', 1),
|
||||
('Bonne réunion', 3),
|
||||
('Ouais, j''ai rien compris mais niquel on fait comme vous avez dit', 3),
|
||||
('Non non ça va pas du tout ce que tu me proposes, faut tout refaire', 4),
|
||||
('Réponse de la DSI : non', 2),
|
||||
('Trop dommage qu''Apple ait sorti leur logiciel avant nous, on avait la même idée et tout on aurait tellement pu leur faire de la concurrence',
|
||||
5);
|
||||
|
||||
INSERT INTO annotation (comment, id_administrator, id_section_cell)
|
||||
VALUES ('faut changer ça hein', 7, 5),
|
||||
('??? sérieusement, vous pensez que c''est une bonne idée ?', 7, 7),
|
||||
('ok donc ça c''est votre business plan, bah glhf la team', 7, 2);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,2 +0,0 @@
|
||||
DROP TABLE IF EXISTS administrateurs, projets, utilisateurs, entrepreneurs, sections, rendez_vous, comptes_rendus, concerner CASCADE;
|
||||
DROP TABLE IF EXISTS administrator, project, user_inpulse, entrepreneur, section_cell, appointment, make_appointment, report, annotation, concern CASCADE;
|
92
MyINPulse-back/src/main/resources/import.sql
Normal file
92
MyINPulse-back/src/main/resources/import.sql
Normal file
@ -0,0 +1,92 @@
|
||||
-- Initial Database State Script
|
||||
|
||||
-- Insert Administrators
|
||||
INSERT INTO administrator (idAdministrator) VALUES
|
||||
(1),
|
||||
(2),
|
||||
(3); -- Added more administrators
|
||||
|
||||
-- Insert User Inpulse (some pending)
|
||||
INSERT INTO user_inpulse (idUser, userSurname, userName, primaryMail, secondaryMail, phoneNumber, pending) VALUES
|
||||
(1, 'Doe', 'John', 'john.doe@example.com', NULL, '123-456-7890', FALSE),
|
||||
(2, 'Smith', 'Jane', 'jane.smith@example.com', 'jane.s@altmail.com', '987-654-3210', FALSE),
|
||||
(3, 'Williams', 'Peter', 'peter.w@example.com', NULL, NULL, TRUE), -- Pending user
|
||||
(4, 'Jones', 'Mary', 'mary.j@example.com', NULL, '555-123-4567', FALSE),
|
||||
(5, 'Brown', 'Michael', 'michael.b@example.com', 'mike.brown@work.com', '111-222-3333', FALSE),
|
||||
(6, 'Garcia', 'Maria', 'maria.g@example.com', NULL, '444-555-6666', TRUE), -- Another pending user
|
||||
(7, 'Miller', 'David', 'david.m@example.com', NULL, '777-888-9999', FALSE);
|
||||
|
||||
-- Insert Entrepreneurs
|
||||
INSERT INTO entrepreneur (idEntrepreneur, school, course, sneeStatus, idProjectParticipation, idProjectProposed, idMakeAppointment) VALUES
|
||||
(1, 'Business School A', 'MBA', TRUE, NULL, NULL, NULL),
|
||||
(2, 'Tech University B', 'Computer Science', FALSE, NULL, NULL, NULL),
|
||||
(3, 'Art Institute C', 'Graphic Design', TRUE, NULL, NULL, NULL),
|
||||
(4, 'Science College D', 'Biology', FALSE, NULL, NULL, NULL),
|
||||
(5, 'Engineering School E', 'Mechanical Engineering', TRUE, NULL, NULL, NULL); -- Added more entrepreneurs
|
||||
|
||||
-- Insert Projects
|
||||
-- Main project
|
||||
INSERT INTO project (IdProject, projectName, loga, creationDate, projectStatus, pending, idAdministrator, entrepreneurProposed) VALUES
|
||||
(101, 'Innovative Startup Idea', NULL, '2023-10-26', 'In Progress', FALSE, 1, NULL),
|
||||
(102, 'Pending Project Alpha', NULL, '2024-01-15', 'Planning', TRUE, 1, NULL), -- Pending project
|
||||
(103, 'Pending Project Beta', NULL, '2024-02-20', 'Idea Stage', TRUE, NULL, 1), -- Another pending project, proposed by entrepreneur 1
|
||||
(104, 'E-commerce Platform Development', NULL, '2024-03-10', 'Completed', FALSE, 2, NULL), -- Completed project
|
||||
(105, 'Mobile App for Education', NULL, '2024-04-01', 'In Progress', FALSE, 3, NULL),
|
||||
(106, 'Pending Research Proposal', NULL, '2024-04-25', 'Drafting', TRUE, NULL, 4); -- Pending project proposed by entrepreneur 4
|
||||
|
||||
-- Link Entrepreneurs to projects (Project Participation and Proposed)
|
||||
-- Based on the current schema, we'll update the entrepreneur table directly.
|
||||
-- This might need adjustment based on actual application logic if it's a many-to-many.
|
||||
UPDATE entrepreneur SET idProjectParticipation = 101 WHERE idEntrepreneur IN (1, 2); -- Entrepreneurs 1 and 2 participate in Project 101
|
||||
UPDATE entrepreneur SET idProjectParticipation = 104 WHERE idEntrepreneur = 3; -- Entrepreneur 3 participates in Project 104
|
||||
UPDATE entrepreneur SET idProjectProposed = 103 WHERE idEntrepreneur = 1; -- Entrepreneur 1 proposed Project 103
|
||||
UPDATE entrepreneur SET idProjectProposed = 106 WHERE idEntrepreneur = 4; -- Entrepreneur 4 proposed Project 106
|
||||
|
||||
-- Insert Section Cells for the main project (Project 101) and other projects
|
||||
INSERT INTO section_cell (idSectionCell, IdReference, sectionid, contentSectionCell, modificationDate, idProject) VALUES
|
||||
(1001, NULL, 1, 'Initial project description for Project 101.', '2023-10-26 10:00:00', 101),
|
||||
(1002, 1001, 2, 'Market analysis summary for Project 101.', '2023-10-27 14:30:00', 101),
|
||||
(1003, NULL, 3, 'Team member profiles for Project 101.', '2023-10-28 09:00:00', 101),
|
||||
(1004, NULL, 1, 'Project brief for Project 104.', '2024-03-10 11:00:00', 104),
|
||||
(1005, 1004, 2, 'Technical specifications for Project 104.', '2024-03-15 16:00:00', 104),
|
||||
(1006, NULL, 1, 'Initial concept for Project 105.', '2024-04-01 09:30:00', 105);
|
||||
|
||||
-- Insert Appointments
|
||||
INSERT INTO appointment (idAppointment, appointmentDate, appointmentTime, appointmentDuration, appointmentPlace, appointmentSubject) VALUES
|
||||
(2001, '2023-11-05', '11:00:00', '01:00:00', 'Meeting Room A', 'Project 101 Kick-off Meeting'),
|
||||
(2002, '2023-11-10', '14:00:00', '00:30:00', 'Online', 'Project 101 Follow-up Discussion'),
|
||||
(2003, '2024-03-20', '10:00:00', '01:30:00', 'Client Office', 'Project 104 Final Review'),
|
||||
(2004, '2024-04-10', '15:00:00', '00:45:00', 'Video Call', 'Project 105 Initial Sync'); -- Added more appointments
|
||||
|
||||
-- Insert Concerns (linking Appointments and Section Cells)
|
||||
INSERT INTO concern (IdAppointment, idSectionCell) VALUES
|
||||
(2001, 1001), -- Kick-off meeting concerns section 1001 (Project 101)
|
||||
(2001, 1002), -- Kick-off meeting concerns section 1002 (Project 101)
|
||||
(2002, 1002), -- Follow-up concerns section 1002 (Project 101)
|
||||
(2003, 1004), -- Project 104 review concerns section 1004
|
||||
(2003, 1005), -- Project 104 review concerns section 1005
|
||||
(2004, 1006); -- Project 105 sync concerns section 1006
|
||||
|
||||
-- Insert Make Appointments (linking Appointments, Administrators, and Entrepreneurs)
|
||||
INSERT INTO make_appointment (idMakeAppointment, idAppointment, idAdministrator, idEntrepreneur) VALUES
|
||||
(3001, 2001, 1, 1), -- Admin 1 scheduled appointment 2001 with Entrepreneur 1
|
||||
(3002, 2001, 1, 2), -- Admin 1 scheduled appointment 2001 with Entrepreneur 2
|
||||
(3003, 2002, 1, 1), -- Admin 1 scheduled appointment 2002 with Entrepreneur 1
|
||||
(3004, 2003, 2, 3), -- Admin 2 scheduled appointment 2003 with Entrepreneur 3
|
||||
(3005, 2004, 3, 5); -- Admin 3 scheduled appointment 2004 with Entrepreneur 5
|
||||
|
||||
-- Insert Annotations (linking to Section Cells and Administrators)
|
||||
INSERT INTO annotation (IdAnnotation, comment, idSectionCell, idAdministrator) VALUES
|
||||
(4001, 'Needs more detail on market size.', 1002, 1),
|
||||
(4002, 'Looks good.', 1001, 1),
|
||||
(4003, 'Confirm technical requirements.', 1005, 2),
|
||||
(4004, 'Initial thoughts on UI/UX.', 1006, 3); -- Added more annotations
|
||||
|
||||
-- Insert Reports (linking to Make Appointments)
|
||||
INSERT INTO report (IdReport, reportContent, idMakeAppointment) VALUES
|
||||
(5001, 'Discussed project scope and timelines for Project 101.', 3001),
|
||||
(5002, 'Reviewed market analysis feedback for Project 101.', 3003),
|
||||
(5003, 'Final sign-off on Project 104 deliverables.', 3004),
|
||||
(5004, 'Discussed initial concepts for Project 105.', 3005); -- Added more reports
|
||||
|
||||
-- The project ID for the main project is 101.
|
@ -4,14 +4,10 @@ import static enseirb.myinpulse.model.ProjectDecisionValue.*;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import enseirb.myinpulse.model.Administrator;
|
||||
import enseirb.myinpulse.model.Entrepreneur;
|
||||
import enseirb.myinpulse.model.Project;
|
||||
import enseirb.myinpulse.model.ProjectDecision;
|
||||
import enseirb.myinpulse.model.*;
|
||||
import enseirb.myinpulse.service.AdminApiService;
|
||||
import enseirb.myinpulse.service.database.AdministratorService;
|
||||
import enseirb.myinpulse.service.database.EntrepreneurService;
|
||||
import enseirb.myinpulse.service.database.ProjectService;
|
||||
import enseirb.myinpulse.service.UtilsService;
|
||||
import enseirb.myinpulse.service.database.*;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -21,6 +17,8 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ -30,14 +28,22 @@ public class AdminApiServiceTest {
|
||||
private static long administratorid;
|
||||
private static Administrator administrator;
|
||||
private static Entrepreneur entrepreneur;
|
||||
private static Appointment appt;
|
||||
private static Project p;
|
||||
@Autowired private AdminApiService adminApiService;
|
||||
@Autowired private ProjectService projectService;
|
||||
@Autowired private EntrepreneurService entrepreneurService;
|
||||
@Autowired private SectionCellService sectionCellService;
|
||||
@Autowired private AppointmentService appointmentService;
|
||||
@Autowired private UtilsService utilsService;
|
||||
|
||||
@BeforeAll
|
||||
static void setup(
|
||||
@Autowired AdministratorService administratorService,
|
||||
@Autowired ProjectService projectService,
|
||||
@Autowired EntrepreneurService entrepreneurService) {
|
||||
@Autowired EntrepreneurService entrepreneurService,
|
||||
@Autowired AppointmentService appoitmentService,
|
||||
@Autowired SectionCellService sectionCellService) {
|
||||
administratorService.addAdministrator(
|
||||
new Administrator(
|
||||
"admin",
|
||||
@ -54,6 +60,7 @@ public class AdminApiServiceTest {
|
||||
"testAdmin@example.com",
|
||||
""));
|
||||
administratorid = administrator.getIdUser();
|
||||
|
||||
entrepreneur =
|
||||
new Entrepreneur(
|
||||
"JeSuisUnEntrepreneurDeCompet",
|
||||
@ -65,14 +72,32 @@ public class AdminApiServiceTest {
|
||||
"info ofc",
|
||||
false);
|
||||
entrepreneurService.addEntrepreneur(entrepreneur);
|
||||
projectService.addNewProject(
|
||||
new Project(
|
||||
"sampleProjectAdminApiService",
|
||||
|
||||
Entrepreneur entrepreneur2 =
|
||||
new Entrepreneur(
|
||||
"GDProjets", "", "Entrepreneur2@inpulse.com", "", "", "", "info ofc", true);
|
||||
entrepreneurService.addEntrepreneur(entrepreneur2);
|
||||
|
||||
p =
|
||||
projectService.addNewProject(
|
||||
new Project(
|
||||
"sampleProjectAdminApiService",
|
||||
null,
|
||||
LocalDate.now(),
|
||||
ACTIVE,
|
||||
administratorService.getAdministratorByPrimaryMain(
|
||||
"testAdminFull@example.com")));
|
||||
|
||||
entrepreneurService.updateEntrepreneurProjectParticipation(entrepreneur2.getIdUser(), p);
|
||||
|
||||
appt =
|
||||
new Appointment(
|
||||
null,
|
||||
LocalDate.now(),
|
||||
ACTIVE,
|
||||
administratorService.getAdministratorByPrimaryMain(
|
||||
"testAdminFull@example.com")));
|
||||
LocalTime.now(),
|
||||
LocalTime.now(),
|
||||
"Salle TD 03",
|
||||
"Discussion importante");
|
||||
}
|
||||
|
||||
private <T> List<T> IterableToList(Iterable<T> iterable) {
|
||||
@ -106,7 +131,7 @@ public class AdminApiServiceTest {
|
||||
List<Project> l = IterableToList(projects);
|
||||
assertEquals(1, l.size());
|
||||
Project p = l.getFirst();
|
||||
assertEquals(p.getProjectName(), "sampleProjectAdminApiService");
|
||||
assertEquals("sampleProjectAdminApiService", p.getProjectName());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -180,7 +205,7 @@ public class AdminApiServiceTest {
|
||||
@Test
|
||||
void addProjectToAdmin() {
|
||||
assertEquals(0, administrator.getListProject().size());
|
||||
Project p1 = new Project("assProjectToAdmin", null, LocalDate.now(), ACTIVE, administrator);
|
||||
Project p1 = new Project("addProjectToAdmin", null, LocalDate.now(), ACTIVE, administrator);
|
||||
this.adminApiService.addNewProject(p1);
|
||||
assertEquals(1, administrator.getListProject().size());
|
||||
}
|
||||
@ -189,7 +214,7 @@ public class AdminApiServiceTest {
|
||||
void addProjectToUser() {
|
||||
assertNull(entrepreneur.getProjectParticipation());
|
||||
Project p1 =
|
||||
new Project("assProjectToAdmin", null, LocalDate.now(), ACTIVE, null, entrepreneur);
|
||||
new Project("addProjectToAdmin", null, LocalDate.now(), ACTIVE, null, entrepreneur);
|
||||
this.adminApiService.addNewProject(p1);
|
||||
assertEquals(p1, entrepreneur.getProjectParticipation());
|
||||
}
|
||||
@ -202,7 +227,7 @@ public class AdminApiServiceTest {
|
||||
assertNull(e1.getProjectParticipation());
|
||||
assertNull(e2.getProjectParticipation());
|
||||
assertNull(e3.getProjectParticipation());
|
||||
Project p1 = new Project("assProjectToAdmin", null, LocalDate.now(), ACTIVE, null, null);
|
||||
Project p1 = new Project("addProjectToAdmin", null, LocalDate.now(), ACTIVE, null, null);
|
||||
p1.updateListEntrepreneurParticipation(e1);
|
||||
p1.updateListEntrepreneurParticipation(e2);
|
||||
p1.updateListEntrepreneurParticipation(e3);
|
||||
@ -221,4 +246,86 @@ public class AdminApiServiceTest {
|
||||
this.adminApiService.addNewProject(p1);
|
||||
assertThrows(ResponseStatusException.class, () -> this.adminApiService.addNewProject(p2));
|
||||
}
|
||||
|
||||
// We could do a delete active project, but it's not really useful.
|
||||
@Test
|
||||
void deletePendingProject() {
|
||||
int oldsize = IterableToList(this.adminApiService.getPendingProjects()).size();
|
||||
Project p1 =
|
||||
new Project("PendingProjectAdminApiService2", null, LocalDate.now(), PENDING, null);
|
||||
Project p2 = this.adminApiService.addNewProject(p1);
|
||||
|
||||
assertEquals(oldsize + 1, IterableToList(this.adminApiService.getPendingProjects()).size());
|
||||
this.adminApiService.deleteProject(p2.getIdProject());
|
||||
|
||||
assertEquals(oldsize, IterableToList(this.adminApiService.getPendingProjects()).size());
|
||||
for (int i = 0; i < oldsize; i++) {
|
||||
assertNotEquals(
|
||||
p1.getIdProject(),
|
||||
IterableToList(this.adminApiService.getPendingProjects())
|
||||
.get(i)
|
||||
.getIdProject());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void getUpcommingAppointmentUnkwnownUser() {
|
||||
assertThrows(
|
||||
ResponseStatusException.class,
|
||||
() -> {
|
||||
Iterable<Appointment> a =
|
||||
this.adminApiService.getUpcomingAppointments(
|
||||
"entrepreneur-inexistent@mail.fr");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void getUpcommingAppointmentNoProject() {
|
||||
assertThrows(
|
||||
ResponseStatusException.class,
|
||||
() -> {
|
||||
Iterable<Appointment> a =
|
||||
this.adminApiService.getUpcomingAppointments(
|
||||
"Entrepreneur@inpulse.com");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void getUpcommingAppointmentEmpty() {
|
||||
Iterable<Appointment> a =
|
||||
this.adminApiService.getUpcomingAppointments("Entrepreneur2@inpulse.com");
|
||||
assertEquals(0, IterableToList(a).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void validateEntrepreneurAccount() {
|
||||
assertTrue(entrepreneurService.getEntrepreneurById(entrepreneur.getIdUser()).isPending());
|
||||
assertEquals(2, IterableToList(adminApiService.getPendingUsers()).size());
|
||||
adminApiService.validateEntrepreneurAccount(entrepreneur.getIdUser(), "");
|
||||
assertFalse(entrepreneurService.getEntrepreneurById(entrepreneur.getIdUser()).isPending());
|
||||
assertEquals(1, IterableToList(adminApiService.getPendingUsers()).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreateApptRepport() {
|
||||
System.err.println(appt.getIdAppointment());
|
||||
SectionCell s =
|
||||
sectionCellService.addNewSectionCell(
|
||||
new SectionCell(null, 1L, "jaja", LocalDateTime.now(), p));
|
||||
appointmentService.addNewAppointment(appt);
|
||||
|
||||
appointmentService.updateAppointmentListSectionCell(appt.getIdAppointment(), s);
|
||||
projectService.updateProjectListSectionCell(p.getIdProject(), s);
|
||||
this.adminApiService.createAppointmentReport(
|
||||
appt.getIdAppointment(),
|
||||
new Report(null, "je rapporte de fou"),
|
||||
"testAdminFull@example.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetAdmin() {
|
||||
assertFalse(utilsService.isAnAdmin(entrepreneur.getPrimaryMail()));
|
||||
adminApiService.setAdmin(entrepreneur.getIdUser(), "");
|
||||
assertTrue(utilsService.isAnAdmin(entrepreneur.getPrimaryMail()));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,324 @@
|
||||
package enseirb.myinpulse;
|
||||
|
||||
import static enseirb.myinpulse.model.ProjectDecisionValue.*;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import enseirb.myinpulse.model.*;
|
||||
import enseirb.myinpulse.service.EntrepreneurApiService;
|
||||
import enseirb.myinpulse.service.database.*;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@SpringBootTest
|
||||
@Transactional
|
||||
public class EntrepreneurApiServiceTest {
|
||||
private static Entrepreneur entrepreneur;
|
||||
private static Project project;
|
||||
private static Iterable<SectionCell> sectionCells2;
|
||||
private static Iterable<SectionCell> sectionCells3;
|
||||
@Autowired private EntrepreneurApiService entrepreneurApiService;
|
||||
@Autowired private EntrepreneurService entrepreneurService;
|
||||
@Autowired private ProjectService projectService;
|
||||
@Autowired private SectionCellService sectionCellService;
|
||||
@Autowired private AnnotationService annotationService;
|
||||
@Autowired private AppointmentService appointmentService;
|
||||
|
||||
@BeforeAll
|
||||
static void setup(
|
||||
@Autowired EntrepreneurService entrepreneurService,
|
||||
@Autowired ProjectService projectService,
|
||||
@Autowired SectionCellService sectionCellService) {
|
||||
entrepreneur =
|
||||
entrepreneurService.addEntrepreneur(
|
||||
new Entrepreneur(
|
||||
"entre",
|
||||
"preneur",
|
||||
"entrepreneur@mail.fr",
|
||||
"entrepreneur2@mail.fr",
|
||||
"01 45 71 25 48",
|
||||
"ENSEIRB",
|
||||
"Info",
|
||||
false));
|
||||
entrepreneurService.addEntrepreneur(
|
||||
new Entrepreneur(
|
||||
"entre2",
|
||||
"preneur2",
|
||||
"testentrepreneur@mail.fr",
|
||||
"testentrepreneur2@mail.fr",
|
||||
"",
|
||||
"ENSEGID",
|
||||
"",
|
||||
true));
|
||||
project =
|
||||
projectService.addNewProject(
|
||||
new Project("Project", null, LocalDate.now(), ACTIVE, null, entrepreneur));
|
||||
entrepreneurService.updateEntrepreneurProjectProposed(entrepreneur.getIdUser(), project);
|
||||
entrepreneurService.updateEntrepreneurProjectParticipation(
|
||||
entrepreneur.getIdUser(), project);
|
||||
SectionCell s1 =
|
||||
sectionCellService.addNewSectionCell(
|
||||
new SectionCell(
|
||||
null,
|
||||
2L,
|
||||
"contenu très intéressant",
|
||||
LocalDateTime.now(),
|
||||
project));
|
||||
|
||||
SectionCell s2 =
|
||||
sectionCellService.addNewSectionCell(
|
||||
new SectionCell(
|
||||
null,
|
||||
3L,
|
||||
"contenu très intéressant2",
|
||||
LocalDateTime.now(),
|
||||
project));
|
||||
sectionCells2 = sectionCellService.getSectionCellsByProject(project, 2L);
|
||||
sectionCells3 = sectionCellService.getSectionCellsByProject(project, 3L);
|
||||
}
|
||||
|
||||
private <T> List<T> IterableToList(Iterable<T> iterable) {
|
||||
List<T> l = new ArrayList<>();
|
||||
iterable.forEach(l::add);
|
||||
return l;
|
||||
}
|
||||
|
||||
@Test
|
||||
void editValidSectionCell() {
|
||||
System.out.println("editValidSectionCell : ");
|
||||
SectionCell modified = IterableToList(sectionCells2).getLast();
|
||||
this.sectionCellService.updateSectionCellListAnnotation(
|
||||
modified.getIdSectionCell(),
|
||||
annotationService.addNewAnnotation(new Annotation(null, "oui j'annote encore")));
|
||||
this.sectionCellService.updateSectionCellListAppointment(
|
||||
modified.getIdSectionCell(),
|
||||
appointmentService.addNewAppointment(
|
||||
new Appointment(
|
||||
null,
|
||||
LocalDate.now(),
|
||||
LocalTime.now(),
|
||||
LocalTime.of(2, 5),
|
||||
"TD14",
|
||||
"clément s'est plaint")));
|
||||
entrepreneurApiService.editSectionCell(
|
||||
modified.getIdSectionCell(), "modified content", "entrepreneur@mail.fr");
|
||||
// We get the data from the database again.
|
||||
SectionCell s =
|
||||
IterableToList(sectionCellService.getSectionCellsByProject(project, 2L)).getLast();
|
||||
assertEquals("modified content", s.getContentSectionCell());
|
||||
assertEquals(
|
||||
2, IterableToList(sectionCellService.getSectionCellsByProject(project, 2L)).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void editInvalidSectionCell() {
|
||||
System.out.println("editInvalidSectionCell : ");
|
||||
assertThrows(
|
||||
ResponseStatusException.class,
|
||||
() ->
|
||||
entrepreneurApiService.editSectionCell(
|
||||
-1L, "should not be modified", "entrepreneur@mail.fr"));
|
||||
SectionCell s =
|
||||
IterableToList(sectionCellService.getSectionCellsByProject(project, 2L)).getLast();
|
||||
assertEquals("contenu très intéressant", s.getContentSectionCell());
|
||||
assertEquals(
|
||||
1, IterableToList(sectionCellService.getSectionCellsByProject(project, 2L)).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void editSectionCellInvalidAccess() {
|
||||
System.out.println("editSectionCellInvalidAccess : ");
|
||||
assertThrows(
|
||||
ResponseStatusException.class,
|
||||
() ->
|
||||
entrepreneurApiService.editSectionCell(
|
||||
IterableToList(sectionCells3).getFirst().getIdSectionCell(),
|
||||
"should not be modified",
|
||||
"testentrepreneur@mail.fr"));
|
||||
SectionCell s =
|
||||
IterableToList(sectionCellService.getSectionCellsByProject(project, 2L)).getFirst();
|
||||
|
||||
assertEquals("contenu très intéressant", s.getContentSectionCell());
|
||||
}
|
||||
|
||||
@Test
|
||||
void editNullSectionCell() {
|
||||
System.out.println("editNullSectionCell : ");
|
||||
assertThrows(
|
||||
ResponseStatusException.class,
|
||||
() ->
|
||||
entrepreneurApiService.editSectionCell(
|
||||
null, "modified content", "entrepreneur@mail.fr"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void removeValidSectionCell() {
|
||||
System.out.println("removeValidSectionCell : ");
|
||||
SectionCell tmpCell =
|
||||
sectionCellService.addNewSectionCell(
|
||||
new SectionCell(
|
||||
null, 2L, "contenu temporaire", LocalDateTime.now(), project));
|
||||
assertEquals(
|
||||
2, IterableToList(sectionCellService.getSectionCellsByProject(project, 2L)).size());
|
||||
assertDoesNotThrow(
|
||||
() ->
|
||||
entrepreneurApiService.removeSectionCell(
|
||||
tmpCell.getIdSectionCell(), "entrepreneur@mail.fr"));
|
||||
assertEquals(
|
||||
tmpCell.getIdReference(),
|
||||
IterableToList(sectionCellService.getSectionCellsByProject(project, -1L))
|
||||
.getLast()
|
||||
.getIdReference());
|
||||
}
|
||||
|
||||
@Test
|
||||
void removeInvalidSectionCell() {
|
||||
System.out.println("removeInvalidSectionCell : ");
|
||||
assertThrows(
|
||||
ResponseStatusException.class,
|
||||
() -> entrepreneurApiService.removeSectionCell(-1L, "entrepreneur@mail.fr"));
|
||||
SectionCell s =
|
||||
IterableToList(sectionCellService.getSectionCellsByProject(project, 2L)).getFirst();
|
||||
|
||||
assertEquals("contenu très intéressant", s.getContentSectionCell());
|
||||
}
|
||||
|
||||
@Test
|
||||
void removeNullSectionCell() {
|
||||
System.out.println("removeNullSectionCell : ");
|
||||
assertThrows(
|
||||
ResponseStatusException.class,
|
||||
() -> entrepreneurApiService.removeSectionCell(null, "entrepreneur@mail.fr"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void addValidSectionCell() {
|
||||
System.out.println("addValidSectionCell : ");
|
||||
SectionCell added =
|
||||
sectionCellService.addNewSectionCell(
|
||||
new SectionCell(null, 2L, "contenu ajouté", LocalDateTime.now(), project));
|
||||
added.updateListAnnotation(
|
||||
annotationService.addNewAnnotation(new Annotation(null, "oui j'annote")));
|
||||
added.updateAppointmentSectionCell(
|
||||
appointmentService.addNewAppointment(
|
||||
new Appointment(
|
||||
null,
|
||||
LocalDate.now(),
|
||||
LocalTime.now(),
|
||||
LocalTime.of(2, 5),
|
||||
"TD15",
|
||||
"clément qui se plaint")));
|
||||
entrepreneurApiService.addSectionCell(added, "entrepreneur@mail.fr");
|
||||
SectionCell s =
|
||||
IterableToList(sectionCellService.getSectionCellsByProject(project, 2L)).getLast();
|
||||
assertEquals("contenu ajouté", s.getContentSectionCell());
|
||||
assertEquals(
|
||||
2, IterableToList(sectionCellService.getSectionCellsByProject(project, 2L)).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void addSectionCellInvalidAccess() {
|
||||
System.out.println("addSectionCellInvalidAccess : ");
|
||||
SectionCell added =
|
||||
new SectionCell(null, 2L, "contenu ajouté", LocalDateTime.now(), project);
|
||||
assertThrows(
|
||||
ResponseStatusException.class,
|
||||
() -> entrepreneurApiService.addSectionCell(added, "fauxentrepreneur@mail.fr"));
|
||||
SectionCell s =
|
||||
IterableToList(sectionCellService.getSectionCellsByProject(project, 2L)).getLast();
|
||||
assertEquals(
|
||||
1, IterableToList(sectionCellService.getSectionCellsByProject(project, 2L)).size());
|
||||
assertEquals("contenu très intéressant", s.getContentSectionCell());
|
||||
}
|
||||
|
||||
@Test
|
||||
void addInvalidSectionCell() {
|
||||
System.out.println("addInvalidSectionCell : ");
|
||||
SectionCell added =
|
||||
sectionCellService.addNewSectionCell(
|
||||
new SectionCell(null, -1L, "contenu ajouté", LocalDateTime.now(), project));
|
||||
assertThrows(
|
||||
ResponseStatusException.class,
|
||||
() -> entrepreneurApiService.addSectionCell(added, "entrepreneur@mail.fr"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void addNullSectionCell() {
|
||||
System.out.println("addNullSectionCell : ");
|
||||
assertThrows(
|
||||
ResponseStatusException.class,
|
||||
() -> entrepreneurApiService.addSectionCell(null, "entrepreneur@mail.fr"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void requestValidProject() {
|
||||
System.out.println("requestValidProject : ");
|
||||
int nb_project = IterableToList(this.projectService.getAllProjects()).size();
|
||||
Project validProject =
|
||||
new Project("validProject", null, LocalDate.now(), ACTIVE, null, entrepreneur);
|
||||
validProject.updateListEntrepreneurParticipation(
|
||||
IterableToList(entrepreneurService.getAllEntrepreneurs()).getLast());
|
||||
validProject.updateListSectionCell((IterableToList(sectionCells2).getFirst()));
|
||||
entrepreneurApiService.requestNewProject(validProject, "entrepreneur@mail.fr");
|
||||
assertEquals(PENDING, validProject.getProjectStatus());
|
||||
assertEquals((nb_project + 1), IterableToList(this.projectService.getAllProjects()).size());
|
||||
assertEquals(
|
||||
IterableToList(entrepreneurService.getAllEntrepreneurs()).getLast(),
|
||||
validProject.getListEntrepreneurParticipation().getLast());
|
||||
assertEquals(
|
||||
IterableToList(sectionCells2).getFirst().getIdSectionCell(),
|
||||
validProject.getListSectionCell().getFirst().getIdSectionCell());
|
||||
}
|
||||
|
||||
@Test
|
||||
void requestNullProject() {
|
||||
System.out.println("requestNullProject : ");
|
||||
assertThrows(
|
||||
ResponseStatusException.class,
|
||||
() -> entrepreneurApiService.requestNewProject(null, "entrepreneur@mail.fr"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createNewValidAccount() {
|
||||
System.out.println("createNewValidAccount : ");
|
||||
int nb_entrepreneur = IterableToList(this.entrepreneurService.getAllEntrepreneurs()).size();
|
||||
Entrepreneur newEntrepreneur =
|
||||
new Entrepreneur(
|
||||
"New",
|
||||
"Test",
|
||||
"mailtest@test.fr",
|
||||
"mailtest2@test.fr",
|
||||
"0888888888",
|
||||
"ENSEIRB",
|
||||
"ELEC",
|
||||
false,
|
||||
true);
|
||||
assertDoesNotThrow(() -> entrepreneurApiService.createAccount(newEntrepreneur));
|
||||
assertEquals(
|
||||
(nb_entrepreneur + 1),
|
||||
IterableToList(this.entrepreneurService.getAllEntrepreneurs()).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void createExistingAccount() {
|
||||
System.out.println("createExistingAccount : ");
|
||||
int nb_entrepreneur = IterableToList(this.entrepreneurService.getAllEntrepreneurs()).size();
|
||||
assertThrows(
|
||||
ResponseStatusException.class,
|
||||
() -> entrepreneurApiService.createAccount(entrepreneur));
|
||||
assertEquals(
|
||||
nb_entrepreneur,
|
||||
IterableToList(this.entrepreneurService.getAllEntrepreneurs()).size());
|
||||
}
|
||||
}
|
@ -0,0 +1,922 @@
|
||||
package enseirb.myinpulse;
|
||||
|
||||
import static enseirb.myinpulse.model.ProjectDecisionValue.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import enseirb.myinpulse.model.*;
|
||||
import enseirb.myinpulse.service.SharedApiService;
|
||||
import enseirb.myinpulse.service.database.*;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import enseirb.myinpulse.service.UtilsService;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll; // Use BeforeAll for static setup
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test; // Keep this import
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
// Helper to easily convert Iterable to List
|
||||
class TestUtils {
|
||||
static <T> List<T> toList(Iterable<T> iterable) {
|
||||
List<T> list = new ArrayList<>();
|
||||
if (iterable != null) {
|
||||
iterable.forEach(list::add);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
@SpringBootTest
|
||||
@Transactional // Each @Test method runs in a transaction that is rolled back
|
||||
public class SharedApiServiceTest {
|
||||
|
||||
@Autowired private SharedApiService sharedApiService;
|
||||
|
||||
// Autowire actual services to use in setup and test verification
|
||||
@Autowired private ProjectService projectService;
|
||||
@Autowired private AdministratorService administratorService;
|
||||
@Autowired private EntrepreneurService entrepreneurService;
|
||||
@Autowired private SectionCellService sectionCellService;
|
||||
@Autowired private AppointmentService appointmentService;
|
||||
|
||||
// Mock UtilsService to control authorization logic
|
||||
@MockitoBean private UtilsService mockUtilsService;
|
||||
|
||||
// Static variables for data created once before all tests
|
||||
private static Project staticAuthorizedProject;
|
||||
private static String staticAuthorizedMail;
|
||||
private static Administrator staticAuthorizedAdmin;
|
||||
|
||||
private static Project staticUnauthorizedProject;
|
||||
private static String staticUnauthorizedMail;
|
||||
|
||||
// --- Static Setup (Runs once before all tests) ---
|
||||
// Use @BeforeAll static method with injected services
|
||||
@BeforeAll
|
||||
static void setupOnce(
|
||||
@Autowired AdministratorService administratorService,
|
||||
@Autowired ProjectService projectService,
|
||||
@Autowired EntrepreneurService entrepreneurService) {
|
||||
|
||||
// Create and Save core test data here using injected services
|
||||
staticAuthorizedAdmin =
|
||||
administratorService.addAdministrator(getTestAdmin("static_authorized_admin"));
|
||||
staticAuthorizedMail = staticAuthorizedAdmin.getPrimaryMail();
|
||||
|
||||
staticUnauthorizedProject =
|
||||
projectService.addNewProject(
|
||||
getTestProject(
|
||||
"static_unauthorized_project",
|
||||
administratorService.addAdministrator(
|
||||
getTestAdmin("static_unauthorized_admin"))));
|
||||
staticUnauthorizedMail =
|
||||
administratorService
|
||||
.addAdministrator(getTestAdmin("static_unauthorized_user"))
|
||||
.getPrimaryMail(); // User who is NOT admin of the unauthorized project
|
||||
|
||||
staticAuthorizedProject =
|
||||
projectService.addNewProject(
|
||||
getTestProject("static_authorized_project", staticAuthorizedAdmin));
|
||||
|
||||
// Link a static entrepreneur to the authorized project if needed for some tests
|
||||
// Entrepreneur staticLinkedEntrepreneur =
|
||||
// entrepreneurService.addEntrepreneur(getTestEntrepreneur("static_linked_entrepreneur"));
|
||||
// staticAuthorizedProject.updateListEntrepreneurParticipation(staticLinkedEntrepreneur);
|
||||
// projectService.addNewProject(staticAuthorizedProject); // Re-save the project after
|
||||
// updating lists
|
||||
}
|
||||
|
||||
// --- Per-Test Setup (Runs before each test method) ---
|
||||
@BeforeEach
|
||||
void setupForEach() {
|
||||
// Reset mock expectations before each test
|
||||
MockitoAnnotations.openMocks(
|
||||
this); // Needed for mocks if not using @ExtendWith(MockitoExtension.class)
|
||||
|
||||
// --- Configure the mock UtilsService based on the actual authorization rules ---
|
||||
|
||||
// Rule: Any admin can check any project.
|
||||
// Assuming staticAuthorizedMail is an admin:
|
||||
when(mockUtilsService.isAllowedToCheckProject(eq(staticAuthorizedMail), anyLong()))
|
||||
.thenReturn(true); // Admin allowed for ANY project ID
|
||||
|
||||
// Rule: An entrepreneur can only check their own stuff.
|
||||
// Assuming staticUnauthorizedMail is an entrepreneur NOT linked to staticAuthorizedProject
|
||||
// or staticUnauthorizedProject:
|
||||
when(mockUtilsService.isAllowedToCheckProject(eq(staticUnauthorizedMail), anyLong()))
|
||||
.thenReturn(false); // Unauthorized entrepreneur NOT allowed for ANY project ID by
|
||||
// default
|
||||
}
|
||||
|
||||
// --- Helper Methods (Can remain non-static or static as needed) ---
|
||||
private static Administrator getTestAdmin(String name) {
|
||||
return new Administrator(
|
||||
name + "_surname",
|
||||
name,
|
||||
name + "@example.com",
|
||||
"secondary_" + name + "@example.com",
|
||||
"0123456789");
|
||||
}
|
||||
|
||||
private static Entrepreneur getTestEntrepreneur(String name) {
|
||||
return new Entrepreneur(
|
||||
name + "_surname",
|
||||
name,
|
||||
name + "@example.com",
|
||||
"secondary_" + name + "@example.com",
|
||||
"0123456789",
|
||||
"Test School",
|
||||
"Test Course",
|
||||
false);
|
||||
}
|
||||
|
||||
private static Project getTestProject(String name, Administrator admin) {
|
||||
Project project = new Project(name, null, LocalDate.now(), ACTIVE, admin);
|
||||
return project;
|
||||
}
|
||||
|
||||
private static SectionCell getTestSectionCell(
|
||||
Project project, Long sectionId, String content, LocalDateTime date) {
|
||||
SectionCell sectionCell = new SectionCell();
|
||||
sectionCell.setProjectSectionCell(project);
|
||||
sectionCell.setSectionId(sectionId);
|
||||
sectionCell.setContentSectionCell(content);
|
||||
sectionCell.setModificationDate(date);
|
||||
return sectionCell;
|
||||
}
|
||||
|
||||
private static SectionCell getTestSectionCell(
|
||||
Project project, Long sectionId, String content, LocalDateTime date, Long refrenceId) {
|
||||
SectionCell sectionCell = new SectionCell();
|
||||
sectionCell.setProjectSectionCell(project);
|
||||
sectionCell.setSectionId(sectionId);
|
||||
sectionCell.setContentSectionCell(content);
|
||||
sectionCell.setModificationDate(date);
|
||||
sectionCell.setIdReference(refrenceId);
|
||||
return sectionCell;
|
||||
}
|
||||
|
||||
private static Appointment getTestAppointment(
|
||||
LocalDate date,
|
||||
LocalTime time,
|
||||
LocalTime duration,
|
||||
String place,
|
||||
String subject,
|
||||
List<SectionCell> sectionCells,
|
||||
Report report) {
|
||||
Appointment appointment = new Appointment();
|
||||
appointment.setAppointmentDate(date);
|
||||
appointment.setAppointmentTime(time);
|
||||
appointment.setAppointmentDuration(duration);
|
||||
appointment.setAppointmentPlace(place);
|
||||
appointment.setAppointmentSubject(subject);
|
||||
|
||||
if (sectionCells != null) {
|
||||
sectionCells.forEach(appointment::updateListSectionCell);
|
||||
}
|
||||
|
||||
if (report != null) {
|
||||
appointment.setAppointmentReport(report);
|
||||
report.setAppointmentReport(appointment);
|
||||
}
|
||||
|
||||
return appointment;
|
||||
}
|
||||
|
||||
private static Report getTestReport(String content) {
|
||||
Report report = new Report();
|
||||
report.setReportContent(content);
|
||||
return report;
|
||||
}
|
||||
|
||||
/*
|
||||
* _____ _ ____ _ _ ____ _ _
|
||||
* |_ _|__ ___| |_/ ___| ___ ___| |_(_) ___ _ __ / ___|___| | |
|
||||
* | |/ _ \/ __| __\___ \ / _ \/ __| __| |/ _ \| '_ \| | / _ \ | |
|
||||
* | | __/\__ \ |_ ___) | __/ (__| |_| | (_) | | | | |__| __/ | |
|
||||
* |_|\___||___/\__|____/ \___|\___|\__|_|\___/|_| |_|\____\___|_|_|
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tests retrieving section cells for a specific project and section ID before a given date
|
||||
* when the user is authorized but no matching cells exist.
|
||||
* Verifies that an empty list is returned.
|
||||
*/
|
||||
@Test
|
||||
void testGetSectionCells_Authorized_NotFound() {
|
||||
// Arrange: No specific section cells needed for this test, rely on clean @BeforeEach state
|
||||
Long targetSectionId = 1L;
|
||||
LocalDateTime dateFilter = LocalDateTime.now().plusDays(1);
|
||||
|
||||
// Act
|
||||
Iterable<SectionCell> result =
|
||||
sharedApiService.getSectionCells(
|
||||
staticAuthorizedProject.getIdProject(),
|
||||
targetSectionId,
|
||||
dateFilter.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")),
|
||||
staticAuthorizedMail);
|
||||
|
||||
List<SectionCell> resultList = TestUtils.toList(result);
|
||||
|
||||
// Assert
|
||||
assertTrue(resultList.isEmpty());
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests retrieving section cells when the user is not authorized for the project.
|
||||
* Verifies that an Unauthorized ResponseStatusException is thrown.
|
||||
*/
|
||||
@Test
|
||||
void testGetSectionCells_Unauthorized() {
|
||||
// Arrange: mockUtilsService configured in BeforeEach
|
||||
// Act & Assert
|
||||
ResponseStatusException exception =
|
||||
assertThrows(
|
||||
ResponseStatusException.class,
|
||||
() -> {
|
||||
sharedApiService.getSectionCells(
|
||||
staticAuthorizedProject
|
||||
.getIdProject(), // Project static user is not
|
||||
// authorized for
|
||||
1L,
|
||||
LocalDateTime.now()
|
||||
.format(
|
||||
DateTimeFormatter.ofPattern(
|
||||
"yyyy-MM-dd HH:mm")),
|
||||
staticUnauthorizedMail); // Static unauthorized user mail
|
||||
});
|
||||
|
||||
assertEquals(HttpStatus.UNAUTHORIZED, exception.getStatusCode());
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests retrieving all section cells for a project when the user is authorized
|
||||
* but the project has no section cells.
|
||||
* Verifies that an empty list is returned.
|
||||
*/
|
||||
@Test
|
||||
void testGetAllSectionCells_Authorized_NoCells() {
|
||||
// Arrange: staticAuthorizedProject has no section cells initially in BeforeAll
|
||||
// Act
|
||||
Iterable<SectionCell> result =
|
||||
sharedApiService.getAllSectionCells(
|
||||
staticAuthorizedProject.getIdProject(), staticAuthorizedMail);
|
||||
|
||||
List<SectionCell> resultList = TestUtils.toList(result);
|
||||
|
||||
// Assert
|
||||
assertTrue(resultList.isEmpty());
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests retrieving all section cells when the user is not authorized for the project.
|
||||
* Verifies that an Unauthorized ResponseStatusException is thrown.
|
||||
*/
|
||||
@Test
|
||||
void testGetAllSectionCells_Unauthorized() {
|
||||
// Arrange: mockUtilsService configured in BeforeEach
|
||||
// Act & Assert
|
||||
ResponseStatusException exception =
|
||||
assertThrows(
|
||||
ResponseStatusException.class,
|
||||
() -> {
|
||||
sharedApiService.getAllSectionCells(
|
||||
staticAuthorizedProject.getIdProject(), staticUnauthorizedMail);
|
||||
});
|
||||
|
||||
assertEquals(HttpStatus.UNAUTHORIZED, exception.getStatusCode());
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests retrieving section cells for a specific project and section ID before a given date
|
||||
* when the user is authorized and matching cells exist.
|
||||
* Verifies that only the correct cells are returned.
|
||||
*/
|
||||
@Test
|
||||
// Commenting out failing test
|
||||
void testGetSectionCells_Authorized_Found() {
|
||||
Long targetSectionId = 1L;
|
||||
// Set a date filter slightly in the future so our "latest before" cell is included
|
||||
LocalDateTime dateFilter = LocalDateTime.now().plusMinutes(5);
|
||||
|
||||
// Creating versions of the SAME SectionCell (share the same idReference)
|
||||
|
||||
// the first version. This will get a GENERATED idReference.
|
||||
SectionCell firstVersion =
|
||||
getTestSectionCell(
|
||||
staticAuthorizedProject,
|
||||
targetSectionId,
|
||||
"Content V1 (Oldest)",
|
||||
LocalDateTime.now().minusDays(3) // Oldest date
|
||||
);
|
||||
sectionCellService.addNewSectionCell(firstVersion);
|
||||
|
||||
Long sharedIdReference = firstVersion.getIdReference();
|
||||
assertNotNull(
|
||||
sharedIdReference,
|
||||
"idReference should be generated after saving the first version");
|
||||
System.out.println("Generated sharedIdReference: " + sharedIdReference);
|
||||
|
||||
// Create subsequent versions and MANUALLY set the SAME idReference.
|
||||
// These represent updates to the cell identified by sharedIdReference.
|
||||
|
||||
SectionCell middleVersion =
|
||||
getTestSectionCell(
|
||||
staticAuthorizedProject,
|
||||
targetSectionId,
|
||||
"Content V2 (Middle)",
|
||||
LocalDateTime.now().minusDays(2), // Middle date, before filter
|
||||
sharedIdReference);
|
||||
middleVersion = sectionCellService.addNewSectionCell(middleVersion);
|
||||
sectionCellService.updateSectionCellReferenceId(
|
||||
middleVersion.getIdSectionCell(), sharedIdReference);
|
||||
|
||||
SectionCell latestBeforeFilter =
|
||||
getTestSectionCell(
|
||||
staticAuthorizedProject,
|
||||
targetSectionId,
|
||||
"Content V3 (Latest Before Filter)",
|
||||
LocalDateTime.now().minusDays(1), // Latest date before filter
|
||||
sharedIdReference);
|
||||
latestBeforeFilter = sectionCellService.addNewSectionCell(latestBeforeFilter);
|
||||
sectionCellService.updateSectionCellReferenceId(
|
||||
latestBeforeFilter.getIdSectionCell(), sharedIdReference);
|
||||
|
||||
SectionCell futureVersion =
|
||||
getTestSectionCell(
|
||||
staticAuthorizedProject,
|
||||
targetSectionId,
|
||||
"Content V4 (Future - Should Be Excluded)",
|
||||
LocalDateTime.now().plusDays(1), // Date is AFTER the filter
|
||||
sharedIdReference);
|
||||
futureVersion = sectionCellService.addNewSectionCell(futureVersion);
|
||||
sectionCellService.updateSectionCellReferenceId(
|
||||
futureVersion.getIdSectionCell(), sharedIdReference);
|
||||
|
||||
// --- Create other SectionCells that should NOT be included (different sectionId or
|
||||
// project) ---
|
||||
|
||||
// Cell in a different section ID
|
||||
sectionCellService.addNewSectionCell(
|
||||
getTestSectionCell(
|
||||
staticAuthorizedProject,
|
||||
99L, // Different sectionId
|
||||
"Content in Different Section",
|
||||
LocalDateTime.now()));
|
||||
|
||||
// Act
|
||||
Iterable<SectionCell> result =
|
||||
sharedApiService.getSectionCells(
|
||||
staticAuthorizedProject.getIdProject(), // Use static project ID
|
||||
targetSectionId,
|
||||
dateFilter.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")),
|
||||
staticAuthorizedMail); // Use static authorized mail
|
||||
|
||||
List<SectionCell> resultList = TestUtils.toList(result);
|
||||
|
||||
assertEquals(1, resultList.size());
|
||||
// Verify that the returned cell is the 'latestBeforeFilter' cell
|
||||
// Comparing by idSectionCell is a good way to verify the exact entity
|
||||
assertEquals(
|
||||
latestBeforeFilter.getIdSectionCell(),
|
||||
resultList.get(0).getIdSectionCell(),
|
||||
"The returned SectionCell should be the one with the latest modification date before the filter.");
|
||||
|
||||
// Also assert the idReference and content
|
||||
assertEquals(
|
||||
sharedIdReference,
|
||||
resultList.get(0).getIdReference(),
|
||||
"The returned cell should have the shared idReference.");
|
||||
assertEquals(
|
||||
"Content V3 (Latest Before Filter)",
|
||||
resultList.get(0).getContentSectionCell(),
|
||||
"The returned cell should have the correct content.");
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests retrieving the most recent section cell for each unique idReference
|
||||
* within a project when the user is authorized and cells exist.
|
||||
* Verifies that only the latest version of each referenced cell is returned.
|
||||
*/
|
||||
// Tests getAllSectionCells
|
||||
@Test
|
||||
// Commenting out failing test - Removed this comment as we are fixing it
|
||||
void testGetAllSectionCells_Authorized_FoundLatest() {
|
||||
// Arrange: Create specific SectionCells for this test
|
||||
// Define the idReference values we will use for grouping
|
||||
Long refIdGroup1 = 101L;
|
||||
Long refIdGroup2 = 102L;
|
||||
Long refIdOtherProject = 103L;
|
||||
|
||||
// --- Create and Add Cells for Group 1 (refIdGroup1) ---
|
||||
// Create the older cell for group 1
|
||||
SectionCell tempOldCell1 =
|
||||
getTestSectionCell(
|
||||
staticAuthorizedProject, // Project
|
||||
1L, // Section ID (assuming this groups by section within refId)
|
||||
"Ref1 Old", // Name
|
||||
LocalDateTime.now().minusDays(3), // Date (older)
|
||||
null); // Pass null or let getTestSectionCell handle it, we'll set
|
||||
// idReference later
|
||||
final SectionCell oldCell1 =
|
||||
sectionCellService.addNewSectionCell(tempOldCell1); // Add to DB
|
||||
|
||||
// Create the newer cell for group 1
|
||||
SectionCell tempNewerCell1 =
|
||||
getTestSectionCell(
|
||||
staticAuthorizedProject, // Project
|
||||
1L, // Section ID
|
||||
"Ref1 Newer", // Name
|
||||
LocalDateTime.now().minusDays(2), // Date (newer than oldCell1)
|
||||
null); // Pass null
|
||||
final SectionCell newerCell1 =
|
||||
sectionCellService.addNewSectionCell(tempNewerCell1); // Add to DB
|
||||
|
||||
// Now, update the idReference for both cells in Group 1 to the desired value
|
||||
sectionCellService.updateSectionCellReferenceId(oldCell1.getIdSectionCell(), refIdGroup1);
|
||||
sectionCellService.updateSectionCellReferenceId(newerCell1.getIdSectionCell(), refIdGroup1);
|
||||
|
||||
// --- Create and Add Cells for Group 2 (refIdGroup2) ---
|
||||
// Create the older cell for group 2
|
||||
SectionCell tempOldCell2 =
|
||||
getTestSectionCell(
|
||||
staticAuthorizedProject, // Project
|
||||
2L, // Section ID (different section)
|
||||
"Ref2 Old", // Name
|
||||
LocalDateTime.now().minusDays(1), // Date (older than newerCell2)
|
||||
null); // Pass null
|
||||
final SectionCell oldCell2 =
|
||||
sectionCellService.addNewSectionCell(tempOldCell2); // Add to DB
|
||||
|
||||
// Create the newer cell for group 2
|
||||
SectionCell tempNewerCell2 =
|
||||
getTestSectionCell(
|
||||
staticAuthorizedProject, // Project
|
||||
2L, // Section ID
|
||||
"Ref2 Newer", // Name
|
||||
LocalDateTime.now(), // Date (latest)
|
||||
null); // Pass null
|
||||
final SectionCell newerCell2 =
|
||||
sectionCellService.addNewSectionCell(tempNewerCell2); // Add to DB
|
||||
|
||||
// Now, update the idReference for both cells in Group 2 to the desired value
|
||||
sectionCellService.updateSectionCellReferenceId(oldCell2.getIdSectionCell(), refIdGroup2);
|
||||
sectionCellService.updateSectionCellReferenceId(newerCell2.getIdSectionCell(), refIdGroup2);
|
||||
|
||||
// --- Create and Add Cell for Other Project (refIdOtherProject) ---
|
||||
Project otherProject =
|
||||
projectService.addNewProject(
|
||||
getTestProject(
|
||||
"other_project_for_cell_test",
|
||||
administratorService.addAdministrator(
|
||||
getTestAdmin("other_admin_cell_test"))));
|
||||
|
||||
SectionCell tempOtherProjectCell =
|
||||
getTestSectionCell(
|
||||
otherProject, // DIFFERENT Project
|
||||
1L, // Section ID
|
||||
"Other Project Cell", // Name
|
||||
LocalDateTime.now(), // Date
|
||||
null); // Pass null
|
||||
final SectionCell otherProjectCell =
|
||||
sectionCellService.addNewSectionCell(tempOtherProjectCell); // Add to DB
|
||||
|
||||
// Now, update the idReference for the Other Project cell
|
||||
sectionCellService.updateSectionCellReferenceId(
|
||||
otherProjectCell.getIdSectionCell(), refIdOtherProject);
|
||||
|
||||
// Act
|
||||
// Ensure the service call uses the correct project ID and mail
|
||||
Iterable<SectionCell> result =
|
||||
sharedApiService.getAllSectionCells(
|
||||
staticAuthorizedProject.getIdProject(), // Use static project ID
|
||||
staticAuthorizedMail); // Use static authorized mail
|
||||
|
||||
List<SectionCell> resultList = TestUtils.toList(result);
|
||||
|
||||
// Assert
|
||||
// We expect 2 cells from the staticAuthorizedProject:
|
||||
// - The latest one from refIdGroup1 (newerCell1)
|
||||
// - The latest one from refIdGroup2 (newerCell2)
|
||||
assertEquals(2, resultList.size());
|
||||
|
||||
// Assert that the result list contains the LATEST cell from each group within the correct
|
||||
// project
|
||||
assertTrue(
|
||||
resultList.stream()
|
||||
.anyMatch(
|
||||
cell ->
|
||||
cell.getIdSectionCell()
|
||||
.equals(newerCell1.getIdSectionCell())),
|
||||
"Should contain the latest cell for Group 1"); // Add assertion message
|
||||
assertTrue(
|
||||
resultList.stream()
|
||||
.anyMatch(
|
||||
cell ->
|
||||
cell.getIdSectionCell()
|
||||
.equals(newerCell2.getIdSectionCell())),
|
||||
"Should contain the latest cell for Group 2"); // Add assertion message
|
||||
|
||||
// Assert that the result list does NOT contain the OLDER cells from the correct project
|
||||
assertFalse(
|
||||
resultList.stream()
|
||||
.anyMatch(
|
||||
cell ->
|
||||
cell.getIdSectionCell()
|
||||
.equals(oldCell1.getIdSectionCell())),
|
||||
"Should not contain the older cell for Group 1"); // Add assertion message
|
||||
assertFalse(
|
||||
resultList.stream()
|
||||
.anyMatch(
|
||||
cell ->
|
||||
cell.getIdSectionCell()
|
||||
.equals(oldCell2.getIdSectionCell())),
|
||||
"Should not contain the older cell for Group 2"); // Add assertion message
|
||||
|
||||
// Assert that the result list does NOT contain the cell from the other project
|
||||
assertFalse(
|
||||
resultList.stream()
|
||||
.anyMatch(
|
||||
cell ->
|
||||
cell.getIdSectionCell()
|
||||
.equals(otherProjectCell.getIdSectionCell())),
|
||||
"Should not contain cells from other projects"); // Add assertion message
|
||||
}
|
||||
|
||||
/*
|
||||
* _____ _ ____ _ ____ _ _ ____
|
||||
* |_ _|__ ___| |_ / ___| ___| |_| _ \ _ __ ___ (_) ___ ___| |_| __ ) _ _
|
||||
* | |/ _ \/ __| __| | _ / _ \ __| |_) | '__/ _ \| |/ _ \/ __| __| _ \| | | |
|
||||
* | | __/\__ \ |_| |_| | __/ |_| __/| | | (_) | | __/ (__| |_| |_) | |_| |
|
||||
* _|_|\___||___/\__|\____|\___|\__|_| |_| \___// |\___|\___|\__|____/ \__, |
|
||||
* |_ _| _ \ |__/ |___/
|
||||
* | || | | |
|
||||
* | || |_| |
|
||||
* |___|____/
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tests retrieving entrepreneurs linked to a project when the user is authorized
|
||||
* but no entrepreneurs are linked.
|
||||
* Verifies that an empty list is returned.
|
||||
*/
|
||||
@Test
|
||||
void testGetEntrepreneursByProjectId_Authorized_NotFound() {
|
||||
// Arrange: staticAuthorizedProject has no entrepreneurs linked initially in BeforeAll
|
||||
// Act
|
||||
Iterable<Entrepreneur> result =
|
||||
sharedApiService.getEntrepreneursByProjectId(
|
||||
staticAuthorizedProject.getIdProject(), staticAuthorizedMail);
|
||||
|
||||
List<Entrepreneur> resultList = TestUtils.toList(result);
|
||||
|
||||
// Assert
|
||||
assertTrue(resultList.isEmpty());
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests retrieving entrepreneurs linked to a project when the user is not authorized.
|
||||
* Verifies that an Unauthorized ResponseStatusException is thrown.
|
||||
*/
|
||||
@Test
|
||||
void testGetEntrepreneursByProjectId_Unauthorized() {
|
||||
// Arrange: mockUtilsService configured in BeforeEach
|
||||
// Act & Assert
|
||||
ResponseStatusException exception =
|
||||
assertThrows(
|
||||
ResponseStatusException.class,
|
||||
() -> {
|
||||
sharedApiService.getEntrepreneursByProjectId(
|
||||
staticAuthorizedProject.getIdProject(), staticUnauthorizedMail);
|
||||
});
|
||||
|
||||
assertEquals(HttpStatus.UNAUTHORIZED, exception.getStatusCode());
|
||||
}
|
||||
|
||||
/*
|
||||
* _____ _ ____ _ _ _ _ ____
|
||||
* |_ _|__ ___| |_ / ___| ___| |_ / \ __| |_ __ ___ (_)_ __ | __ ) _ _
|
||||
* | |/ _ \/ __| __| | _ / _ \ __| / _ \ / _` | '_ ` _ \| | '_ \| _ \| | | |
|
||||
* | | __/\__ \ |_| |_| | __/ |_ / ___ \ (_| | | | | | | | | | | |_) | |_| |
|
||||
* _|_|\___||___/\__|\____|\___|\__/_/ \_\__,_|_| |_| |_|_|_| |_|____/ \__, |
|
||||
* |_ _| _ \ |___/
|
||||
* | || | | |
|
||||
* | || |_| |
|
||||
* |___|____/
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tests retrieving appointments linked to a project's section cells when the user is authorized
|
||||
* but no such appointments exist.
|
||||
* Verifies that an empty list is returned.
|
||||
*/
|
||||
@Test
|
||||
void testGetAppointmentsByProjectId_Authorized_NotFound() {
|
||||
// Arrange: staticAuthorizedProject has no linked section cells or appointments initially
|
||||
// Act
|
||||
Iterable<Appointment> result =
|
||||
sharedApiService.getAppointmentsByProjectId(
|
||||
staticAuthorizedProject.getIdProject(), staticAuthorizedMail);
|
||||
|
||||
List<Appointment> resultList = TestUtils.toList(result);
|
||||
|
||||
// Assert
|
||||
assertTrue(resultList.isEmpty());
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests retrieving the administrator linked to a project when the user is authorized
|
||||
* and an administrator is linked.
|
||||
* Verifies that the correct administrator is returned.
|
||||
*/
|
||||
// Tests getAdminByProjectId
|
||||
@Test
|
||||
void testGetAdminByProjectId_Authorized_Found() {
|
||||
// Arrange: staticAuthorizedProject is created with staticAuthorizedAdmin in BeforeAll
|
||||
// Act
|
||||
Administrator result =
|
||||
sharedApiService.getAdminByProjectId(
|
||||
staticAuthorizedProject.getIdProject(), staticAuthorizedMail);
|
||||
|
||||
// Assert
|
||||
assertNotNull(result);
|
||||
assertEquals(staticAuthorizedAdmin.getIdUser(), result.getIdUser());
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests retrieving the administrator linked to a project when the user is not authorized.
|
||||
* Verifies that an Unauthorized ResponseStatusException is thrown.
|
||||
*/
|
||||
@Test
|
||||
void testGetAdminByProjectId_Unauthorized() {
|
||||
// Arrange: mockUtilsService configured in BeforeEach
|
||||
// Act & Assert
|
||||
ResponseStatusException exception =
|
||||
assertThrows(
|
||||
ResponseStatusException.class,
|
||||
() -> {
|
||||
sharedApiService.getAdminByProjectId(
|
||||
staticAuthorizedProject.getIdProject(), staticUnauthorizedMail);
|
||||
});
|
||||
|
||||
assertEquals(HttpStatus.UNAUTHORIZED, exception.getStatusCode());
|
||||
}
|
||||
|
||||
/*
|
||||
* _____ _
|
||||
* |_ _|__ ___| |_
|
||||
* | |/ _ \/ __| __|
|
||||
* | | __/\__ \ |_
|
||||
* |_|\___||___/\__| _ _ _
|
||||
* / \ _ __ _ __ ___ (_)_ __ | |_ ___ _ __ ___ ___ _ __ | |_ ___
|
||||
* / _ \ | '_ \| '_ \ / _ \| | '_ \| __/ _ \ '_ ` _ \ / _ \ '_ \| __/ __|
|
||||
* / ___ \| |_) | |_) | (_) | | | | | || __/ | | | | | __/ | | | |_\__ \
|
||||
* /_/ \_\ .__/| .__/ \___/|_|_| |_|\__\___|_| |_| |_|\___|_| |_|\__|___/
|
||||
* |_| |_|
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tests retrieving appointments linked to a project's section cells when the user is not authorized.
|
||||
* Verifies that an Unauthorized ResponseStatusException is thrown.
|
||||
*/
|
||||
@Test
|
||||
void testGetAppointmentsByProjectId_Unauthorized() {
|
||||
// Arrange: mockUtilsService configured in BeforeEach
|
||||
// Act & Assert
|
||||
ResponseStatusException exception =
|
||||
assertThrows(
|
||||
ResponseStatusException.class,
|
||||
() -> {
|
||||
sharedApiService.getAppointmentsByProjectId(
|
||||
staticAuthorizedProject.getIdProject(), staticUnauthorizedMail);
|
||||
});
|
||||
|
||||
assertEquals(HttpStatus.UNAUTHORIZED, exception.getStatusCode());
|
||||
}
|
||||
|
||||
@PersistenceContext // Inject EntityManager
|
||||
private EntityManager entityManager;
|
||||
|
||||
// Assume these static variables are defined elsewhere in your test class
|
||||
// private static Project staticAuthorizedProject;
|
||||
// private static String staticAuthorizedMail;
|
||||
// private static Administrator staticAuthorizedAdmin;
|
||||
|
||||
// Assume getTestSectionCell, getTestProject, getTestAdmin, getTestAppointment, TestUtils.toList
|
||||
// are defined elsewhere
|
||||
|
||||
@Test
|
||||
void testGetAppointmentsByProjectId_Authorized_Found() {
|
||||
// Arrange: Create specific SectionCells and Appointments for this test
|
||||
SectionCell cell1 =
|
||||
sectionCellService.addNewSectionCell(
|
||||
getTestSectionCell(
|
||||
staticAuthorizedProject, 1L, "Cell 1 Test", LocalDateTime.now()));
|
||||
SectionCell cell2 =
|
||||
sectionCellService.addNewSectionCell(
|
||||
getTestSectionCell(
|
||||
staticAuthorizedProject, 2L, "Cell 2 Test", LocalDateTime.now()));
|
||||
Project otherProject =
|
||||
projectService.addNewProject(
|
||||
getTestProject(
|
||||
"other_project_app_test",
|
||||
administratorService.addAdministrator(
|
||||
getTestAdmin("other_admin_app_test"))));
|
||||
SectionCell otherProjectCell =
|
||||
sectionCellService.addNewSectionCell(
|
||||
getTestSectionCell(
|
||||
otherProject,
|
||||
1L,
|
||||
"Other Project Cell App Test",
|
||||
LocalDateTime.now()));
|
||||
|
||||
// Create Appointments with SectionCells lists (Owning side)
|
||||
Appointment app1 =
|
||||
getTestAppointment(
|
||||
LocalDate.now().plusDays(10),
|
||||
LocalTime.NOON,
|
||||
LocalTime.of(0, 30),
|
||||
"Place 1 App Test",
|
||||
"Subject 1 App Test",
|
||||
List.of(cell1), // This links Appointment to SectionCell
|
||||
null);
|
||||
Appointment savedApp1 = appointmentService.addNewAppointment(app1);
|
||||
|
||||
Appointment app2 =
|
||||
getTestAppointment(
|
||||
LocalDate.now().plusDays(11),
|
||||
LocalTime.NOON.plusHours(1),
|
||||
LocalTime.of(1, 0),
|
||||
"Place 2 App Test",
|
||||
"Subject 2 App Test",
|
||||
List.of(cell1, cell2), // This links Appointment to SectionCells
|
||||
null);
|
||||
Appointment savedApp2 = appointmentService.addNewAppointment(app2);
|
||||
|
||||
Appointment otherApp =
|
||||
getTestAppointment(
|
||||
LocalDate.now().plusDays(12),
|
||||
LocalTime.MIDNIGHT,
|
||||
LocalTime.of(0, 15),
|
||||
"Other Place App Test",
|
||||
"Other Subject App Test",
|
||||
List.of(otherProjectCell), // This links Appointment to SectionCell
|
||||
null);
|
||||
Appointment savedOtherApp =
|
||||
appointmentService.addNewAppointment(otherApp); // Capture saved entity
|
||||
|
||||
// --- IMPORTANT DEBUGGING STEPS ---
|
||||
// Flush pending changes to the database (including join table inserts)
|
||||
entityManager.flush();
|
||||
// Clear the persistence context cache to ensure entities are loaded fresh from the database
|
||||
entityManager.clear();
|
||||
// --- END IMPORTANT DEBUGGING STEPS ---
|
||||
|
||||
// --- Add Debug Logging Here ---
|
||||
// Re-fetch cells to see their state after saving Appointments and flushing/clearing cache
|
||||
// These fetches should load from the database due to entityManager.clear()
|
||||
SectionCell fetchedCell1_postPersist =
|
||||
sectionCellService.getSectionCellById(cell1.getIdSectionCell());
|
||||
SectionCell fetchedCell2_postPersist =
|
||||
sectionCellService.getSectionCellById(cell2.getIdSectionCell());
|
||||
SectionCell fetchedOtherCell_postPersist =
|
||||
sectionCellService.getSectionCellById(otherProjectCell.getIdSectionCell());
|
||||
|
||||
// Access the lazy collections to see if they are populated from the DB
|
||||
// This access should trigger lazy loading if the data is in the DB
|
||||
List<Appointment> cell1Apps_postPersist =
|
||||
fetchedCell1_postPersist.getAppointmentSectionCell();
|
||||
List<Appointment> cell2Apps_postPersist =
|
||||
fetchedCell2_postPersist.getAppointmentSectionCell();
|
||||
List<Appointment> otherCellApps_postPersist =
|
||||
fetchedOtherCell_postPersist.getAppointmentSectionCell();
|
||||
|
||||
// Ensure logging is enabled in SharedApiService and SectionCellService methods called below
|
||||
Iterable<Appointment> result =
|
||||
sharedApiService.getAppointmentsByProjectId(
|
||||
staticAuthorizedProject.getIdProject(), // Use static project ID
|
||||
staticAuthorizedMail); // Use static authorized mail
|
||||
|
||||
List<Appointment> resultList = TestUtils.toList(result);
|
||||
|
||||
// Assert
|
||||
assertEquals(2, resultList.size());
|
||||
|
||||
assertTrue(
|
||||
resultList.stream()
|
||||
.anyMatch(a -> a.getIdAppointment().equals(savedApp1.getIdAppointment())));
|
||||
assertTrue(
|
||||
resultList.stream()
|
||||
.anyMatch(a -> a.getIdAppointment().equals(savedApp2.getIdAppointment())));
|
||||
|
||||
assertFalse(
|
||||
resultList.stream()
|
||||
.anyMatch(
|
||||
a ->
|
||||
a.getIdAppointment()
|
||||
.equals(savedOtherApp.getIdAppointment())));
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests creating a new appointment request when the user is authorized
|
||||
* for the project linked to the appointment's section cell.
|
||||
* Verifies that the appointment and its relationships are saved correctly in the database.
|
||||
*/
|
||||
// Tests createAppointmentRequest
|
||||
@Test
|
||||
// Commenting out failing test
|
||||
void testCreateAppointmentRequest_Authorized_Success() {
|
||||
// Arrange: Create transient appointment linked to a cell in the static authorized project
|
||||
LocalDate date = LocalDate.parse("2026-01-01");
|
||||
LocalTime time = LocalTime.parse("10:00:00");
|
||||
LocalTime duration = LocalTime.parse("00:30:00");
|
||||
String place = "Meeting Room Integrated";
|
||||
String subject = "Discuss Project Integrated";
|
||||
|
||||
SectionCell linkedCell =
|
||||
sectionCellService.addNewSectionCell(
|
||||
getTestSectionCell(
|
||||
staticAuthorizedProject,
|
||||
0L,
|
||||
"Related Section Content Integrated",
|
||||
LocalDateTime.now()));
|
||||
|
||||
Report newReport = null; // getTestReport(reportContent); // Uses no-arg constructor
|
||||
|
||||
Appointment newAppointment =
|
||||
getTestAppointment(
|
||||
date, time, duration, place, subject, List.of(linkedCell), newReport);
|
||||
|
||||
// mockUtilsService is configured in BeforeEach to allow staticAuthorizedMail for
|
||||
// staticAuthorizedProject
|
||||
|
||||
// Act
|
||||
// Allow the service method to call the actual appointmentService.addNewAppointment
|
||||
assertDoesNotThrow(
|
||||
() ->
|
||||
sharedApiService.createAppointmentRequest(
|
||||
newAppointment, staticAuthorizedMail));
|
||||
|
||||
// Assert: Retrieve the appointment from the DB and verify it and its relationships were
|
||||
// saved
|
||||
// We find it by looking for appointments linked to the authorized project's cells
|
||||
Iterable<SectionCell> projectCells =
|
||||
sectionCellService.getSectionCellsByProject(
|
||||
staticAuthorizedProject, linkedCell.getSectionId()); // Fetch relevant cells
|
||||
List<Appointment> projectAppointmentsList = new ArrayList<>();
|
||||
projectCells.forEach(
|
||||
cell ->
|
||||
projectAppointmentsList.addAll(
|
||||
sectionCellService.getAppointmentsBySectionCellId(
|
||||
cell.getIdSectionCell()))); // Get appointments for
|
||||
// those cells
|
||||
|
||||
Optional<Appointment> createdAppointmentOpt =
|
||||
projectAppointmentsList.stream()
|
||||
.filter(
|
||||
a ->
|
||||
a.getAppointmentDate().equals(date)
|
||||
&& a.getAppointmentTime().equals(time)
|
||||
&& a.getAppointmentPlace().equals(place)
|
||||
&& a.getAppointmentSubject().equals(subject))
|
||||
.findFirst();
|
||||
|
||||
assertTrue(createdAppointmentOpt.isPresent());
|
||||
Appointment createdAppointment = createdAppointmentOpt.get();
|
||||
|
||||
// FIX: Corrected bidirectional link check
|
||||
assertEquals(1, createdAppointment.getAppointmentListSectionCell().size());
|
||||
assertTrue(
|
||||
createdAppointment.getAppointmentListSectionCell().stream()
|
||||
.anyMatch(
|
||||
sc -> sc.getIdSectionCell().equals(linkedCell.getIdSectionCell())));
|
||||
|
||||
List<Appointment> appointmentsLinkedToCell =
|
||||
TestUtils.toList(
|
||||
sectionCellService.getAppointmentsBySectionCellId(
|
||||
linkedCell.getIdSectionCell()));
|
||||
assertTrue(
|
||||
appointmentsLinkedToCell.stream()
|
||||
.anyMatch(
|
||||
a ->
|
||||
a.getIdAppointment()
|
||||
.equals(createdAppointment.getIdAppointment())));
|
||||
}
|
||||
}
|
@ -48,4 +48,4 @@ services:
|
||||
# container_name: MyINPulse-back
|
||||
# ports:
|
||||
# - "8081:8080"
|
||||
#
|
||||
|
@ -16,7 +16,7 @@ BACKEND_PASSWORD=backend_db_user_password
|
||||
DATABASE_URL=localhost:5433
|
||||
|
||||
VITE_KEYCLOAK_URL=http://localhost:7080
|
||||
VITE_KEYCLOAK_CLIENT_ID=myinpulse-dev
|
||||
VITE_KEYCLOAK_REALM=test
|
||||
VITE_KEYCLOAK_CLIENT_ID=MyINPulse-vite
|
||||
VITE_KEYCLOAK_REALM=MyINPulse
|
||||
VITE_APP_URL=http://localhost:5173
|
||||
VITE_BACKEND_URL=http://localhost:8081/
|
||||
|
@ -42,11 +42,11 @@ services:
|
||||
# ports:
|
||||
# - "8080:80"
|
||||
|
||||
#back:
|
||||
# build:
|
||||
# context: ./MyINPulse-back/
|
||||
# dockerfile: Dockerfile
|
||||
# container_name: MyINPulse-back
|
||||
# ports:
|
||||
# - "8081:8080"
|
||||
back:
|
||||
build:
|
||||
context: ./MyINPulse-back/
|
||||
dockerfile: Dockerfile
|
||||
container_name: MyINPulse-back
|
||||
ports:
|
||||
- "8081:8080"
|
||||
|
@ -45,11 +45,11 @@ services:
|
||||
ports:
|
||||
- "8080:80"
|
||||
|
||||
#back:
|
||||
# build:
|
||||
# context: ./MyINPulse-back/
|
||||
# dockerfile: Dockerfile
|
||||
# container_name: MyINPulse-back
|
||||
# #ports:
|
||||
# # - "8081:8080"
|
||||
back:
|
||||
build:
|
||||
context: ./MyINPulse-back/
|
||||
dockerfile: Dockerfile
|
||||
container_name: MyINPulse-back
|
||||
#ports:
|
||||
# - "8081:8080"
|
||||
|
@ -5,43 +5,9 @@
|
||||
- `/entrepreneur/lcsection/modify/{sectionId}` → `/entrepreneur/sectionCell/modify/{sectionId}`
|
||||
|
||||
### Admin api
|
||||
- `/admin/appointments/upcoming`: is shared not admin
|
||||
- `/admin/projects/decision`: instanciates classes with `adminId` instead of taking the id from the token
|
||||
- `/admin/project/add`:
|
||||
- point 1: the doc has this `projects` everywhere this should be `/admin/projects/add` to avoid confusion I think
|
||||
- point 2: this doesn't assiociate users with a project I need to add other endopint for that
|
||||
- `/admin/appoitements/report/{appointmentId}`:
|
||||
- typo: `appoitements` → `appointments`
|
||||
- `/admin/projects/remove/{projectId}`, `/admin/project/add`, `/admin/projects/decision`, `/admin/projects/pending`:
|
||||
- should need token to delete or add project
|
||||
- `/admin/appointments/report/{appointmentId}` has no PUT and DELETE
|
||||
- `/admin/request-join` and `/admin/request-join/decision/{joinRequestId}` have not yet been implemented
|
||||
|
||||
### Entrepreneur api
|
||||
- `/entrepreneur/sectionCell/modify/{sectionId}`:
|
||||
- the section-id because of the definition of `sectionCell` schema the `sectionId` is given twice possibly leading to inconsistency. Which is why the path var to be removed:
|
||||
- → `/entrepreneur/sectionCell/modify`
|
||||
|
||||
### Shared api
|
||||
- `/shared/project/sectionCell/{projectId}/{sectionId}/{date}`:
|
||||
- point 1:
|
||||
same point for `project` → `projects`
|
||||
- point 2:
|
||||
have yet to read `sharedApiService` to see how dates are handled and to see if we agree on values of `date` to make it so it gets the version relative to current date
|
||||
- `/shared/entrepreneurs/{projectId}`:
|
||||
- maybe change to `/shared/projects/entrepreneurs/{projectId}` to match other similair endpoints like `/shared/projects/admin/{projectId}`
|
||||
- `/shared/appointment/request`:
|
||||
- creates the apointement but don't know how it associates other users, potentially multiple classes in one request body, is that possible ?
|
||||
|
||||
## TODOs for me
|
||||
|
||||
### list 1:
|
||||
- add back-end server links (backend and auth) for interacting with api through swagger
|
||||
- get config for that set up in the project
|
||||
|
||||
### list 2:
|
||||
- see what to do about logo img
|
||||
- see format for date and add it in examples
|
||||
- ask the form of return of the json of iterables, for now I have put array
|
||||
- add endpoint for adding users to a project
|
||||
- update endpoint descriptions
|
||||
- add examples for values in schemas
|
||||
### Unauth api
|
||||
- `/unauth/request-join/{projectId}` has not yet been implemented
|
||||
|
||||
|
@ -1,214 +1,387 @@
|
||||
# _ ____ __ __ ___ _ _ _ ____ ___
|
||||
# / \ | _ \| \/ |_ _| \ | | / \ | _ \_ _|
|
||||
# / _ \ | | | | |\/| || || \| | / _ \ | |_) | |
|
||||
# / ___ \| |_| | | | || || |\ | / ___ \| __/| |
|
||||
# /_/ \_\____/|_| |_|___|_| \_| /_/ \_\_| |___|
|
||||
#
|
||||
# Admin API Endpoints
|
||||
paths:
|
||||
/admin/projects:
|
||||
get:
|
||||
summary: Retourne la liste of projets associés à l'admin
|
||||
tags:
|
||||
- Admin API
|
||||
operationId: getAdminProjects
|
||||
summary: Get projects associated with the admin
|
||||
tags:
|
||||
- Admin API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
description:
|
||||
JSON array of who's elements are objects containing necessary information for the view
|
||||
(project name, entrepreneur names, etc..)
|
||||
of the projects an admin is watching over.
|
||||
- MyINPulse: [MyINPulse-admin]
|
||||
description: Retrieves a list of projects managed by the requesting admin, including details for overview.
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
description: OK - List of projects returned successfully.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "main.yaml#/components/schemas/project"
|
||||
$ref: "./main.yaml#/components/schemas/project"
|
||||
"400":
|
||||
description: Bad request
|
||||
description: Bad Request - Invalid project data provided (e.g., missing required fields).
|
||||
"401":
|
||||
description: Authorization information is missing or invalid
|
||||
description: Unauthorized - Authentication required or invalid token.
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
|
||||
post:
|
||||
operationId: addProjectManually
|
||||
summary: Manually add a new project
|
||||
description: Creates a new project with the provided details. (NOTE that this meant for manually inserting projects, for example importing already existing projects).
|
||||
tags:
|
||||
- Admin API
|
||||
security:
|
||||
- MyINPulse: [MyINPulse-admin]
|
||||
requestBody:
|
||||
required: true
|
||||
description: Project details to create. `idProject` and `creationDate` will be ignored if sent and set by the server.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "./main.yaml#/components/schemas/project"
|
||||
responses:
|
||||
"200": # Use 200 Created for successful creation
|
||||
description: Created - Project added successfully. Returns the created project.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "./main.yaml#/components/schemas/project"
|
||||
"409":
|
||||
description: Bad Request - Project already exists.
|
||||
"401":
|
||||
description: Unauthorized.
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
|
||||
|
||||
/admin/projects/pending:
|
||||
get:
|
||||
operationId: getPendingProjects
|
||||
summary: Get projects awaiting validation
|
||||
tags:
|
||||
- Admin API
|
||||
security:
|
||||
- MyINPulse: [MyINPulse-admin]
|
||||
description: Retrieves a list of projects submitted by entrepreneurs that are pending admin approval.
|
||||
responses:
|
||||
"200":
|
||||
description: OK - List of pending projects returned.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "./main.yaml#/components/schemas/project" # Assuming pending projects use the same schema
|
||||
"401":
|
||||
description: Unauthorized.
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
|
||||
/admin/request-join:
|
||||
get:
|
||||
operationId: getPendingProjects
|
||||
summary: Get entrepreneurs project join requests
|
||||
tags:
|
||||
- Admin API
|
||||
security:
|
||||
- MyINPulse: [MyINPulse-admin]
|
||||
description: Retrieves a list of pending requests from entrepreneurs to join an existing project.
|
||||
responses:
|
||||
"200":
|
||||
description: OK - List of pending project join requests.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "./main.yaml#/components/schemas/joinRequest"
|
||||
"401":
|
||||
description: Unauthorized.
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
|
||||
/admin/request-join/decision/{joinRequestId}:
|
||||
post:
|
||||
summary: Approve or reject a pending project join request
|
||||
tags:
|
||||
- Admin API
|
||||
security:
|
||||
- MyINPulse: [MyINPulse-admin]
|
||||
parameters:
|
||||
- in: path
|
||||
name: joinRequestId
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
description: The ID of the pending join request to decide upon.
|
||||
|
||||
description: |-
|
||||
Allows an admin to make a decision on an ebtrepreneur's request to join an existing project awaiting validation.
|
||||
If approved (isAccepted=true), the entrepreneur is linked to the project with ID joinRequestId.
|
||||
If rejected (isAccepted=false), the pending request data might be archived or deleted based on business logic.
|
||||
responses:
|
||||
"200":
|
||||
description: OK - No Content, decision processed successfully..
|
||||
content:
|
||||
application/json:
|
||||
$ref: "./main.yaml#/components/schemas/joinRequestDecision"
|
||||
"400":
|
||||
description: Bad Request - Invalid input (e.g., missing decision).
|
||||
"401":
|
||||
description: Unauthorized.
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
|
||||
|
||||
/admin/projects/pending/decision:
|
||||
post:
|
||||
summary: valider un projet en attente de validation
|
||||
operationId: decidePendingProject
|
||||
summary: Approve or reject a pending project
|
||||
tags:
|
||||
- Admin API
|
||||
description:
|
||||
if the request is accepted the status of the
|
||||
project is changed to ongoing, entrepreneur
|
||||
account is confirmed and the project is linked
|
||||
to the admin accepting the request and the
|
||||
entrepreneur requesting it. Else the pending
|
||||
project and user info are deleted.
|
||||
description: |-
|
||||
Allows an admin to make a decision on a project awaiting validation.
|
||||
If approved (isAccepted=true), the project status changes, and it's linked to the involved users.
|
||||
If rejected (isAccepted=false), the pending project data might be archived or deleted based on business logic.
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
- MyINPulse: [MyINPulse-admin]
|
||||
requestBody:
|
||||
required: true
|
||||
description: Decision payload.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
pedingProjectId:
|
||||
type: integer
|
||||
decision:
|
||||
type: boolean
|
||||
|
||||
$ref: './main.yaml#/components/schemas/projectDecision'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
"200": # Use 200 No Content for successful action with no body
|
||||
description: No Content - Decision processed successfully.
|
||||
"400":
|
||||
description: Bad request
|
||||
description: Bad Request - Invalid input (e.g., missing decision).
|
||||
"401":
|
||||
description: Authorization information is
|
||||
missing or invalid
|
||||
|
||||
/admin/projects/add:
|
||||
post:
|
||||
summary: Ajout manuel d'un projet
|
||||
description:
|
||||
Adds a project with the
|
||||
inputed details
|
||||
description: Unauthorized.
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
|
||||
|
||||
/admin/pending-accounts: # Path updated
|
||||
get:
|
||||
operationId: getPendingAccounts
|
||||
summary: Get accounts awaiting validation
|
||||
description: Retrieves a list of entrepreneur user accounts that are pending admin validation.
|
||||
tags:
|
||||
- Admin API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "main.yaml#/components/schemas/project"
|
||||
|
||||
- MyINPulse: [MyINPulse-admin]
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
"400":
|
||||
description: Bad request
|
||||
"200":
|
||||
description: OK - List of pending accounts returned.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "./main.yaml#/components/schemas/user-entrepreneur"
|
||||
"401":
|
||||
description: Authorization information is
|
||||
missing or invalid
|
||||
description: Unauthorized.
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
|
||||
/admin/accounts/validate/{userId}:
|
||||
post: # Changed to POST as it changes state
|
||||
operationId: validateUserAccount
|
||||
summary: Validate a pending user account
|
||||
description: Marks the user account specified by userId as validated/active.
|
||||
tags:
|
||||
- Admin API
|
||||
security:
|
||||
- MyINPulse: [MyINPulse-admin]
|
||||
parameters:
|
||||
- in: path
|
||||
name: userId
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
description: The ID of the user account to validate.
|
||||
example: 102
|
||||
responses:
|
||||
"200":
|
||||
description: No Content - Account validated successfully.
|
||||
"400":
|
||||
description: Bad Request - Invalid user ID format.
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
"401":
|
||||
description: Unauthorized.
|
||||
|
||||
/admin/appointments/upcoming:
|
||||
get:
|
||||
operationId: getUpcomingAppointments
|
||||
summary: Get upcoming appointments for an admin
|
||||
tags:
|
||||
- Admin API
|
||||
security:
|
||||
- MyINPulse: [MyINPulse-admin]
|
||||
description: Retrieves a list of appointments scheduled for an admin in the future.
|
||||
responses:
|
||||
"200":
|
||||
description: OK - List of upcoming appointments.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "./main.yaml#/components/schemas/appointment"
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
"404":
|
||||
description: no appointments found.
|
||||
"401":
|
||||
description: Unauthorized.
|
||||
|
||||
/admin/appointments/report/{appointmentId}:
|
||||
put:
|
||||
summary: enregistrer un rapport du rendez-vous
|
||||
description:
|
||||
Generate a PDF file formatted
|
||||
from input text and links it
|
||||
to the appointement.
|
||||
tags:
|
||||
- Admin API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
parameters:
|
||||
- in: path
|
||||
name: appointmentId
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "main.yaml#/components/schemas/report"
|
||||
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
"400":
|
||||
description: Bad request
|
||||
"401":
|
||||
description: Authorization information is
|
||||
missing or invalid
|
||||
post:
|
||||
summary: modifier un rapport déja éxistant du rendez-vous
|
||||
description:
|
||||
Modifies the report file to input
|
||||
text and links it to the appointement.
|
||||
operationId: createAppointmentReport
|
||||
summary: Create a report for an appointment
|
||||
description: Creates and links a new report (e.g., meeting minutes) to the specified appointment using the provided content.
|
||||
tags:
|
||||
- Admin API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
- MyINPulse: [MyINPulse-admin]
|
||||
parameters:
|
||||
- in: path
|
||||
name: appointmentId
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
description: ID of the appointment to add a report to.
|
||||
example: 303
|
||||
requestBody:
|
||||
required: true
|
||||
description: Report content. `idReport` will be ignored if sent.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "main.yaml#/components/schemas/report"
|
||||
|
||||
$ref: "./main.yaml#/components/schemas/report"
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
"200":
|
||||
description: Created - Report created and linked successfully. Returns the created report.
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: "./main.yaml#/components/schemas/report" }
|
||||
"400":
|
||||
description: Bad request
|
||||
description: Bad Request - Invalid input (e.g., missing content, invalid appointment ID format).
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
"401":
|
||||
description: Authorization information is
|
||||
missing or invalid
|
||||
|
||||
|
||||
/admin/projects/remove/{projectId}:
|
||||
delete:
|
||||
summary: supression d'un project
|
||||
description:
|
||||
Removes the project
|
||||
with the inputed id projectId
|
||||
description: Unauthorized.
|
||||
|
||||
put: # Changed to PUT for update/replacement
|
||||
operationId: updateAppointmentReport
|
||||
summary: Update an existing appointment report
|
||||
description: Updates the content of an existing report linked to the specified appointment. Replaces the entire report content.
|
||||
tags:
|
||||
- Admin API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
- MyINPulse: [MyINPulse-admin]
|
||||
parameters:
|
||||
- in: path
|
||||
name: appointmentId
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
description: ID of the appointment whose report needs updating.
|
||||
example: 303
|
||||
requestBody:
|
||||
required: true
|
||||
description: New report content. `idReport` in the body should match the existing report's ID or will be ignored.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "./main.yaml#/components/schemas/report"
|
||||
responses:
|
||||
"200":
|
||||
description: OK - Report updated successfully. Returns the updated report.
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: "./main.yaml#/components/schemas/report" }
|
||||
"400":
|
||||
description: Bad Request - Invalid input (e.g., missing content).
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
"401":
|
||||
description: Unauthorized.
|
||||
|
||||
|
||||
/admin/projects/{projectId}:
|
||||
delete:
|
||||
operationId: removeProject
|
||||
summary: Remove a project
|
||||
description: Permanently removes the project specified by projectId and potentially related data (use with caution).
|
||||
tags:
|
||||
- Admin API
|
||||
security:
|
||||
- MyINPulse: [MyINPulse-admin]
|
||||
parameters:
|
||||
- in: path
|
||||
name: projectId
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
"400":
|
||||
description: Bad request
|
||||
"401":
|
||||
description: Authorization information is
|
||||
missing or invalid
|
||||
|
||||
|
||||
/admin/projects/pending:
|
||||
get:
|
||||
summary: Retourne la liste des projets en attente de validation
|
||||
tags:
|
||||
- Admin API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
description:
|
||||
JSON array of who's elements are objects containing
|
||||
necessary information for the view (project name, etc..)
|
||||
of all pending projects.
|
||||
description: The ID of the project to remove.
|
||||
example: 12
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "main.yaml#/components/schemas/project"
|
||||
description: No Content - Project removed successfully.
|
||||
"400":
|
||||
description: Bad request
|
||||
description: Bad Request - Invalid project ID format.
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
"401":
|
||||
description: Authorization information is missing or invalid
|
||||
description: Unauthorized.
|
||||
|
||||
|
||||
|
||||
/admin/make-admin/{userId}:
|
||||
post:
|
||||
operationId: grantAdminRights
|
||||
summary: Grant admin rights to a user
|
||||
tags:
|
||||
- Admin API
|
||||
security:
|
||||
- MyINPulse: [MyINPulse-admin]
|
||||
description: Elevates the specified user to also have administrator privileges. Assumes the user already exists.
|
||||
parameters:
|
||||
- in: path
|
||||
name: userId
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
description: The ID of the user to grant admin rights.
|
||||
example: 103
|
||||
responses:
|
||||
"200": # Use 200 No Content
|
||||
description: No Content - Admin rights granted successfully.
|
||||
"400":
|
||||
description: Bad Request - Invalid user ID format or user is already an admin.
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
"401":
|
||||
description: Unauthorized.
|
||||
|
||||
/admin/create-account:
|
||||
post:
|
||||
summary: Creates Admin out Jwt Token
|
||||
tags:
|
||||
- Admin API
|
||||
security:
|
||||
- MyINPulse: [MyINPulse-admin]
|
||||
description: Create an admin instance in the MyINPulse DB of the information provided from the authenticated user's keycloack token.
|
||||
The information required in the token are `userSurname`, `username`, `primaryMail`, `secondaryMail`, `phoneNumber`.
|
||||
responses:
|
||||
"200":
|
||||
description: No Content - Admin user created successfully.
|
||||
"401":
|
||||
description: Unauthorized.
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
@ -1,600 +0,0 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: MyInpulse Backend Api
|
||||
description: this document servers as a documentation for the backend api.
|
||||
version: 0.2.0
|
||||
tags:
|
||||
- name: Entrepreneurs API
|
||||
description: La partie de l'api dédiée aux entrepreneurs
|
||||
- name: Admin API
|
||||
description: La partie de l'api dédiée aux entrepreneurs
|
||||
- name: Shared API
|
||||
description: La partie de l'api dédiée aux entrepreneurs et admins
|
||||
components:
|
||||
schemas:
|
||||
user:
|
||||
type: object
|
||||
properties:
|
||||
idUser:
|
||||
type: integer
|
||||
userSurname:
|
||||
type: string
|
||||
userName:
|
||||
type: string
|
||||
primaryMail:
|
||||
type: string
|
||||
example: example@exmaple.com
|
||||
secondaryMail:
|
||||
type: string
|
||||
example: example@exmaple.com
|
||||
phoneNumber:
|
||||
type: string
|
||||
example: 0612345678
|
||||
user-entrepreneur:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/user'
|
||||
- type: object
|
||||
properties:
|
||||
school:
|
||||
type: string
|
||||
example: enseirb
|
||||
course:
|
||||
type: string
|
||||
example: info
|
||||
sneeStatus:
|
||||
type: boolean
|
||||
example: false
|
||||
user-admin:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/user'
|
||||
sectionCell:
|
||||
type: object
|
||||
properties:
|
||||
idSectionCell:
|
||||
type: integer
|
||||
example: this the cell (postit id)
|
||||
sectionId:
|
||||
type: integer
|
||||
contentSectionCell:
|
||||
type: string
|
||||
modificationDate:
|
||||
type: string
|
||||
example: 02-05-2025
|
||||
project:
|
||||
type: object
|
||||
properties:
|
||||
idProject:
|
||||
type: integer
|
||||
projectName:
|
||||
type: string
|
||||
creationDate:
|
||||
type: string
|
||||
example: 02-05-2025
|
||||
logo:
|
||||
example: to be discussed not yet fixed
|
||||
type: string
|
||||
format: binary
|
||||
report:
|
||||
type: object
|
||||
properties:
|
||||
idReport:
|
||||
type: integer
|
||||
reportContent:
|
||||
type: string
|
||||
appointement:
|
||||
type: object
|
||||
properties:
|
||||
appointmentDate:
|
||||
type: string
|
||||
example: 02-05-2025
|
||||
appointmentTime:
|
||||
type: string
|
||||
example: '10:15:30'
|
||||
appointmentDuration:
|
||||
type: string
|
||||
appointmentPlace:
|
||||
type: string
|
||||
appointmentSubject:
|
||||
type: string
|
||||
securitySchemes:
|
||||
MyINPulse:
|
||||
type: oauth2
|
||||
flows:
|
||||
implicit:
|
||||
authorizationUrl: 'http://localhost:7080'
|
||||
scopes:
|
||||
MyINPulse-admin: Administrateur
|
||||
MyINPulse-entrepreneur: Utilisateur
|
||||
servers:
|
||||
- url: 'http://localhost:8081/'
|
||||
description: Backend server
|
||||
paths:
|
||||
/admin/projects:
|
||||
get:
|
||||
summary: Retourne la liste of projets associés à l'admin
|
||||
tags:
|
||||
- Admin API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
description: 'JSON array of who''s elements are objects containing necessary information for the view (project name, entrepreneur names, etc..) of the projects an admin is watching over.'
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/project'
|
||||
'400':
|
||||
description: Bad request
|
||||
'401':
|
||||
description: Authorization information is missing or invalid
|
||||
/admin/projects/pending/decision:
|
||||
post:
|
||||
summary: valider un projet en attente de validation
|
||||
tags:
|
||||
- Admin API
|
||||
description: 'if the request is accepted the status of the project is changed to ongoing, entrepreneur account is confirmed and the project is linked to the admin accepting the request and the entrepreneur requesting it. Else the pending project and user info are deleted.'
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
pedingProjectId:
|
||||
type: integer
|
||||
decision:
|
||||
type: boolean
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
'400':
|
||||
description: Bad request
|
||||
'401':
|
||||
description: Authorization information is missing or invalid
|
||||
/admin/projects/add:
|
||||
post:
|
||||
summary: Ajout manuel d'un projet
|
||||
description: Adds a project with the inputed details
|
||||
tags:
|
||||
- Admin API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/project'
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
'400':
|
||||
description: Bad request
|
||||
'401':
|
||||
description: Authorization information is missing or invalid
|
||||
'/admin/appointments/report/{appointmentId}':
|
||||
put:
|
||||
summary: enregistrer un rapport du rendez-vous
|
||||
description: Generate a PDF file formatted from input text and links it to the appointement.
|
||||
tags:
|
||||
- Admin API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
parameters:
|
||||
- in: path
|
||||
name: appointmentId
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/report'
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
'400':
|
||||
description: Bad request
|
||||
'401':
|
||||
description: Authorization information is missing or invalid
|
||||
post:
|
||||
summary: modifier un rapport déja éxistant du rendez-vous
|
||||
description: Modifies the report file to input text and links it to the appointement.
|
||||
tags:
|
||||
- Admin API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
parameters:
|
||||
- in: path
|
||||
name: appointmentId
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/report'
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
'400':
|
||||
description: Bad request
|
||||
'401':
|
||||
description: Authorization information is missing or invalid
|
||||
'/admin/projects/remove/{projectId}':
|
||||
delete:
|
||||
summary: supression d'un project
|
||||
description: Removes the project with the inputed id projectId
|
||||
tags:
|
||||
- Admin API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
parameters:
|
||||
- in: path
|
||||
name: projectId
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
'400':
|
||||
description: Bad request
|
||||
'401':
|
||||
description: Authorization information is missing or invalid
|
||||
/admin/projects/pending:
|
||||
get:
|
||||
summary: Retourne la liste des projets en attente de validation
|
||||
tags:
|
||||
- Admin API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
description: 'JSON array of who''s elements are objects containing necessary information for the view (project name, etc..) of all pending projects.'
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/project'
|
||||
'400':
|
||||
description: Bad request
|
||||
'401':
|
||||
description: Authorization information is missing or invalid
|
||||
/shared/appointments/upcoming:
|
||||
get:
|
||||
summary: Retourne la list des prochains rendez-vous de l'utilisateur
|
||||
tags:
|
||||
- Shared API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
- MyINPulse-entrepreneur
|
||||
description: 'JSON array of upcoming appointment data (name, date, time etc..) for a user.'
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/appointement'
|
||||
'400':
|
||||
description: Bad request
|
||||
'401':
|
||||
description: Authorization information is missing or invalid
|
||||
'/shared/projects/sectionCell/{projectId}/{sectionId}/{date}':
|
||||
get:
|
||||
summary: Retourne la liste de sections de LC avec un titre donné
|
||||
tags:
|
||||
- Shared API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
- MyINPulse-entrepreneur
|
||||
description: JSON array containing Lean Canvas section data with a title for the current date (or given date if the date parameter is passed)
|
||||
parameters:
|
||||
- in: path
|
||||
required: true
|
||||
name: projectId
|
||||
schema:
|
||||
type: integer
|
||||
- in: path
|
||||
required: true
|
||||
description: 'this number can be 1, 2,...,8. It is associated with the title of the sectionCell'
|
||||
name: sectionId
|
||||
schema:
|
||||
type: integer
|
||||
enum:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- 4
|
||||
- 5
|
||||
- 6
|
||||
- 7
|
||||
- 8
|
||||
- in: path
|
||||
required: true
|
||||
name: date
|
||||
description: the date corresponding to the wanted version of lc section. "Nan" for the latest version
|
||||
example: NaN
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/sectionCell'
|
||||
'400':
|
||||
description: Bad request
|
||||
'401':
|
||||
description: Authorization information is missing or invalid
|
||||
'/shared/projects/entrepreneurs/{projectId}':
|
||||
get:
|
||||
summary: Retourne la liste d'entrepreneurs associée à un projet donné
|
||||
tags:
|
||||
- Shared API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
- MyINPulse-entrepreneur
|
||||
description: JSON array of entrepreneur names associated with a project
|
||||
parameters:
|
||||
- in: path
|
||||
name: projectId
|
||||
schema:
|
||||
type: integer
|
||||
required: true
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/user-entrepreneur'
|
||||
'400':
|
||||
description: Bad request
|
||||
'401':
|
||||
description: Authorization information is missing or invalid
|
||||
'/shared/projects/admin/{projectId}':
|
||||
get:
|
||||
summary: Retourne les informations de l'admin qui accompagne le projet
|
||||
tags:
|
||||
- Shared API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
- MyINPulse-entrepreneur
|
||||
description: 'JSON object containing information (name, gmail, tel, etc..) the admin supervising the project with id projectID.'
|
||||
parameters:
|
||||
- in: path
|
||||
name: projectId
|
||||
schema:
|
||||
type: integer
|
||||
required: true
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/user-admin'
|
||||
'400':
|
||||
description: Bad request
|
||||
'401':
|
||||
description: Authorization information is missing or invalid
|
||||
'/shared/projects/appointments/{projectId}':
|
||||
get:
|
||||
summary: Retourne les rendez-vous du projet
|
||||
tags:
|
||||
- Shared API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
- MyINPulse-entrepreneur
|
||||
description: JSON array of upcoming and past appointment data for the project with id projectID.
|
||||
parameters:
|
||||
- in: path
|
||||
name: projectId
|
||||
schema:
|
||||
type: integer
|
||||
required: true
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/appointement'
|
||||
'400':
|
||||
description: Bad request
|
||||
'401':
|
||||
description: Authorization information is missing or invalid
|
||||
'/shared/projects/appointments/report/{appointmentId}':
|
||||
get:
|
||||
summary: Retourne le rapport pdf du rendez-vous
|
||||
tags:
|
||||
- Shared API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
- MyINPulse-entrepreneur
|
||||
description: PDF file containing the ap- pointment report
|
||||
parameters:
|
||||
- in: path
|
||||
name: apointementId
|
||||
schema:
|
||||
type: integer
|
||||
required: true
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/pdf:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
'400':
|
||||
description: Bad request
|
||||
'401':
|
||||
description: Authorization information is missing or invalid
|
||||
/shared/appointments/request:
|
||||
post:
|
||||
summary: demander un rendez-vous
|
||||
description: will add an appointement request request by the applicant to have an appointment to be confirmed or denied by the specified participants of the appointement.
|
||||
tags:
|
||||
- Shared API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-entrepreneur
|
||||
- MyINPulse-admin
|
||||
requestBody:
|
||||
description: \"participants\" property is an array containing userids of the participants in the appointement
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
title:
|
||||
type: string
|
||||
start_time:
|
||||
type: string
|
||||
end_time:
|
||||
type: string
|
||||
place:
|
||||
type: string
|
||||
applicantId:
|
||||
type: integer
|
||||
participants:
|
||||
type: array
|
||||
items:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
'400':
|
||||
description: Bad request
|
||||
'401':
|
||||
description: Authorization information is missing or invalid
|
||||
/entrepreneur/projects/request:
|
||||
post:
|
||||
summary: demander la création et validation d'un projet
|
||||
tags:
|
||||
- Entrepreneurs API
|
||||
description: Adds project to pending projects to then be accepted or rejected by an admin
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-entrepreneur
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
founder:
|
||||
$ref: '#/components/schemas/user-entrepreneur'
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
'400':
|
||||
description: Bad request
|
||||
'401':
|
||||
description: Authorization information is missing or invalid
|
||||
/entrepreneur/sectionCell/add:
|
||||
post:
|
||||
summary: ajouter une sections au LC
|
||||
description: Adds input data to the user's LC with a specified title.
|
||||
tags:
|
||||
- Entrepreneurs API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-entrepreneur
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/sectionCell'
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
'400':
|
||||
description: Bad request
|
||||
'401':
|
||||
description: Authorization information is missing or invalid
|
||||
/entrepreneur/sectionCell/modify:
|
||||
put:
|
||||
summary: modifier les données d'une section LC
|
||||
description: Modifies input Lean Canvas section by changing it to the information in the request body and changes the time stamp.
|
||||
tags:
|
||||
- Entrepreneurs API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-entrepreneur
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/sectionCell'
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
'400':
|
||||
description: Bad request
|
||||
'401':
|
||||
description: Authorization information is missing or invalid
|
||||
'/entrepreneur/sectionCell/remove/{sectionCellId}':
|
||||
delete:
|
||||
summary: supprimer une section LC.
|
||||
description: Deletes section from Lean Canvas
|
||||
tags:
|
||||
- Entrepreneurs API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-entrepreneur
|
||||
parameters:
|
||||
- in: path
|
||||
name: sectionCellId
|
||||
schema:
|
||||
type: integer
|
||||
required: true
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
'400':
|
||||
description: Bad request
|
||||
'401':
|
||||
description: Authorization information is missing or invalid
|
@ -1,121 +1,197 @@
|
||||
# _____ _ _ _____ ____ _____ ____ ____ _____ _ _ _____ _ _ ____
|
||||
# | ____| \ | |_ _| _ \| ____| _ \| _ \| ____| \ | | ____| | | | _ \
|
||||
# | _| | \| | | | | |_) | _| | |_) | |_) | _| | \| | _| | | | | |_) |
|
||||
# | |___| |\ | | | | _ <| |___| __/| _ <| |___| |\ | |___| |_| | _ <
|
||||
# |_____|_|_\_| |_| |_| \_\_____|_| |_| \_\_____|_| \_|_____|\___/|_| \_\
|
||||
# / \ | _ \_ _|
|
||||
# / _ \ | |_) | |
|
||||
# / ___ \| __/| |
|
||||
# /_/ \_\_| |___|
|
||||
#
|
||||
|
||||
# Entrepreneur API Endpoints
|
||||
paths:
|
||||
/entrepreneur/projects/request:
|
||||
post:
|
||||
summary: demander la création et validation d'un projet
|
||||
operationId: requestProjectCreation
|
||||
summary: Request creation and validation of a new project
|
||||
tags:
|
||||
- Entrepreneurs API
|
||||
description:
|
||||
Adds project to pending projects
|
||||
to then be accepted or rejected by
|
||||
an admin
|
||||
description: |-
|
||||
Submits a request for a new project. The project details are provided in the request body.
|
||||
The requesting entrepreneur (identified by the token) will be associated to it.
|
||||
The project is created with a 'pending' status, awaiting admin approval.
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-entrepreneur
|
||||
- MyINPulse: [MyINPulse-entrepreneur]
|
||||
requestBody:
|
||||
required: true
|
||||
description: Project details for the request. `status`, `creationDate` are required by the model when being sent but is ignored by the server;
|
||||
primarily expects a valid `projectId`, `name`, `logo`.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
founder:
|
||||
$ref: "main.yaml#/components/schemas/user-entrepreneur"
|
||||
$ref: "./main.yaml#/components/schemas/project"
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
"200":
|
||||
description: Accepted - Project creation request received and is pending validation.
|
||||
"400":
|
||||
description: Bad request
|
||||
description: Bad Request - Invalid input (e.g., missing name).
|
||||
"401":
|
||||
description: Authorization information is
|
||||
missing or invalid
|
||||
|
||||
|
||||
/entrepreneur/sectionCell/add:
|
||||
description: Unauthorized.
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
|
||||
/entrepreneur/sectionCells: # Base path
|
||||
post:
|
||||
summary: ajouter une sections au LC
|
||||
description:
|
||||
Adds input data to the user's LC
|
||||
with a specified title.
|
||||
operationId: addSectionCell
|
||||
summary: Add a cell to a Lean Canvas section
|
||||
description: Adds a new cell (like a sticky note) with the provided content to a specific section of the entrepreneur's project's Lean Canvas. Assumes project context is known based on user's token.
|
||||
`idSectionCell` and `modificationDate` are server-generated so they're values in the request are ignored by the server.
|
||||
tags:
|
||||
- Entrepreneurs API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-entrepreneur
|
||||
- MyINPulse: [MyINPulse-entrepreneur]
|
||||
requestBody:
|
||||
required: true
|
||||
description: Section cell details. `idSectionCell` and `modificationDate` will be ignored if sent.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "main.yaml#/components/schemas/sectionCell"
|
||||
$ref: "./main.yaml#/components/schemas/sectionCell"
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
"200":
|
||||
description: Created - Section cell added successfully. Returns the created cell.
|
||||
"400":
|
||||
description: Bad request
|
||||
description: Bad Request - Invalid input (e.g., missing content or sectionId).
|
||||
"401":
|
||||
description: Authorization information is
|
||||
missing or invalid
|
||||
/entrepreneur/sectionCell/modify:
|
||||
description: Unauthorized.
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
|
||||
/entrepreneur/sectionCells/{sectionCellId}:
|
||||
put:
|
||||
summary: modifier les données d'une section LC
|
||||
description:
|
||||
Modifies input Lean Canvas section by changing it to
|
||||
the information in the request body and changes the
|
||||
time stamp.
|
||||
operationId: modifySectionCell
|
||||
summary: Modify data in a Lean Canvas section cell
|
||||
description: Updates the content of an existing Lean Canvas section cell specified by `sectionCellId`. The server "updates" (it keeps a record of the previous version to keep a history of all the sectionCells and creates a new ones with the specified modifications) the `modificationDate`.
|
||||
tags:
|
||||
- Entrepreneurs API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-entrepreneur
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "main.yaml#/components/schemas/sectionCell"
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
"400":
|
||||
description: Bad request
|
||||
"401":
|
||||
description: Authorization information is
|
||||
missing or invalid
|
||||
|
||||
/entrepreneur/sectionCell/remove/{sectionCellId}:
|
||||
delete:
|
||||
summary: supprimer une section LC.
|
||||
description:
|
||||
Deletes section from Lean Canvas
|
||||
tags:
|
||||
- Entrepreneurs API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-entrepreneur
|
||||
- MyINPulse: [MyINPulse-entrepreneur]
|
||||
parameters:
|
||||
- in: path
|
||||
name: sectionCellId
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
required: true
|
||||
description: The ID of the section cell to modify.
|
||||
example: 508
|
||||
requestBody:
|
||||
required: true
|
||||
description: Updated section cell details. `sectionCellId` "the path parameter" is the only id that's consideredn the `sectionCellId` id in the request body is ignored. `modificationDate` should be updated by the server.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "./main.yaml#/components/schemas/sectionCell"
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
"400":
|
||||
description: Bad request
|
||||
"200":
|
||||
description: OK - Section cell updated successfully. Returns the updated cell.
|
||||
"404":
|
||||
description: Bad Request - Invalid input or ID mismatch.
|
||||
"401":
|
||||
description: Authorization information is
|
||||
missing or invalid
|
||||
description: Unauthorized.
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
|
||||
delete:
|
||||
operationId: removeSectionCell
|
||||
summary: Remove a Lean Canvas section cell
|
||||
description: Deletes the Lean Canvas section cell specified by `sectionCellId`.
|
||||
tags:
|
||||
- Entrepreneurs API
|
||||
security:
|
||||
- MyINPulse: [MyINPulse-entrepreneur]
|
||||
parameters:
|
||||
- in: path
|
||||
name: sectionCellId
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
description: The ID of the section cell to remove.
|
||||
example: 509
|
||||
responses:
|
||||
"200":
|
||||
description: No Content - Section cell removed successfully.
|
||||
"400":
|
||||
description: Bad Request - Invalid ID format.
|
||||
"404":
|
||||
description: Bad Request - sectionCell not found.
|
||||
"401":
|
||||
description: Unauthorized.
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
|
||||
|
||||
/entrepreneur/projects:
|
||||
get:
|
||||
summary: gets the projectId of the project associated with the entrepreneur
|
||||
description: returns a list of projectIds of the projects associated with the entrepreneur
|
||||
tags:
|
||||
- Entrepreneurs API
|
||||
security:
|
||||
- MyINPulse: [MyINPulse-entrepreneur]
|
||||
parameters:
|
||||
responses:
|
||||
"200":
|
||||
description: OK - Section cell updated successfully. Returns the updated cell.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "./main.yaml#/components/schemas/project"
|
||||
"404":
|
||||
description: Bad Request - Invalid input or ID mismatch.
|
||||
"401":
|
||||
description: Unauthorized or identity not found
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
|
||||
|
||||
/entrepreneur/projects/project-is-active:
|
||||
get:
|
||||
summary: checks if the project associated with an entrepreneur is active
|
||||
description: returns a boolean if the project associated with an entrepreneur has an active status
|
||||
(i.e has been validated by an admin). The user should be routed to LeanCanvas. any other response code
|
||||
should be treated as false
|
||||
tags:
|
||||
- Entrepreneurs API
|
||||
security:
|
||||
- MyINPulse: [MyINPulse-entrepreneur]
|
||||
parameters:
|
||||
responses:
|
||||
"200":
|
||||
description: OK - got the value successfully any other response code should be treated as false.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: boolean
|
||||
"404":
|
||||
description: Bad Request - Invalid input or ID mismatch.
|
||||
"401":
|
||||
description: Unauthorized or identity not found
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
|
||||
/entrepreneur/projects/has-pending-request:
|
||||
get:
|
||||
summary: checks if the user has a pending projectRequest
|
||||
description: returns a boolean if the project associated with an entrepreneur has a pending status
|
||||
(i.e has not yet been validated by an admin). The user should be routed to a page telling him that he should
|
||||
wait for admin validation. any other response code should be treated as false.
|
||||
tags:
|
||||
- Entrepreneurs API
|
||||
security:
|
||||
- MyINPulse: [MyINPulse-entrepreneur]
|
||||
parameters:
|
||||
responses:
|
||||
"200":
|
||||
description: OK - got the value successfully any other response code should be treated as false.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: boolean
|
||||
"404":
|
||||
description: Bad Request - Invalid input or ID mismatch.
|
||||
"401":
|
||||
description: Unauthorized or identity not found
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
@ -1,17 +1,18 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: MyInpulse Backend Api
|
||||
description: this document servers as a documentation for the backend api.
|
||||
version: 0.2.0
|
||||
title: MyInpulse Backend API
|
||||
description: This serves as an OpenAPI documentation for the MyInpulse backend service, covering operations for Entrepreneurs, Admins, and shared functionalities.
|
||||
version: 0.2.1
|
||||
|
||||
tags:
|
||||
- name: Entrepreneurs API
|
||||
description: La partie de l'api dédiée aux entrepreneurs
|
||||
description: API endpoints primarily for Entrepreneur users.
|
||||
- name: Admin API
|
||||
description: La partie de l'api dédiée aux entrepreneurs
|
||||
description: API endpoints restricted to Admin users for management tasks.
|
||||
- name: Shared API
|
||||
description: La partie de l'api dédiée aux entrepreneurs et admins
|
||||
|
||||
description: API endpoints accessible by both Entrepreneurs and Admins.
|
||||
- name: Unauth API
|
||||
description: API endpoints related to user account management.
|
||||
|
||||
components:
|
||||
schemas:
|
||||
@ -27,88 +28,132 @@ components:
|
||||
$ref: "models.yaml#/project"
|
||||
report:
|
||||
$ref: "models.yaml#/report"
|
||||
appointement:
|
||||
$ref: "models.yaml#/appointement"
|
||||
|
||||
appointment:
|
||||
$ref: "models.yaml#/appointment"
|
||||
joinRequest:
|
||||
$ref: "models.yaml#/joinRequest"
|
||||
projectDecision:
|
||||
$ref: "models.yaml#/projectDecision"
|
||||
joinRequestDecision:
|
||||
$ref: "models.yaml#/joinRequestDecision"
|
||||
|
||||
securitySchemes:
|
||||
MyINPulse:
|
||||
type: oauth2
|
||||
description: OAuth2 authentication using Keycloak.
|
||||
flows:
|
||||
implicit:
|
||||
authorizationUrl: http://localhost:7080
|
||||
authorizationUrl: '{keycloakBaseUrl}/realms/{keycloakRealm}/protocol/openid-connect/auth'
|
||||
scopes:
|
||||
MyINPulse-admin: Administrateur
|
||||
MyINPulse-entrepreneur: Utilisateur
|
||||
MyINPulse-admin: Grants administrator access.
|
||||
MyINPulse-entrepreneur: Grants standard entrepreneur user access.
|
||||
|
||||
servers:
|
||||
- url: http://localhost:8081/
|
||||
description: Backend server
|
||||
|
||||
|
||||
- url: '{serverProtocol}://{serverHost}:{serverPort}'
|
||||
description: API Server
|
||||
variables:
|
||||
serverProtocol:
|
||||
enum: [http, https]
|
||||
default: http
|
||||
serverHost:
|
||||
default: localhost
|
||||
serverPort:
|
||||
enum: ['8081']
|
||||
default: '8081'
|
||||
keycloakBaseUrl:
|
||||
default: http://localhost:7080
|
||||
description: Base URL for the Keycloak server.
|
||||
keycloakRealm:
|
||||
default: MyInpulseRealm
|
||||
description: Keycloak realm name.
|
||||
|
||||
paths:
|
||||
# _ ____ __ __ ___ _ _ _ ____ ___
|
||||
# _ _ _ _ _ _
|
||||
# | | | |_ __ __ _ _ _| |_| |__ / \ _ __ (_)
|
||||
# | | | | '_ \ / _` | | | | __| '_ \ / _ \ | '_ \| |
|
||||
# | |_| | | | | (_| | |_| | |_| | | |/ ___ \| |_) | |
|
||||
# \___/|_| |_|\__,_|\__,_|\__|_| |_/_/ \_\ .__/|_|
|
||||
# |_|
|
||||
|
||||
/unauth/finalize:
|
||||
$ref: "./unauthApi.yaml#/paths/~1unauth~1finalize"
|
||||
/unauth/request-join/{projectId}:
|
||||
$ref: "./unauthApi.yaml#/paths/~1unauth~1request-join~1{projectId}"
|
||||
/unauth/request-admin-role:
|
||||
$ref: "./unauthApi.yaml#/paths/~1unauth~1request-admin-role"
|
||||
/unauth/check-if-not-pending:
|
||||
$ref: "./unauthApi.yaml#/paths/~1unauth~1check-if-not-pending"
|
||||
|
||||
# _ ____ __ __ ___ _ _ _ ____ ___
|
||||
# / \ | _ \| \/ |_ _| \ | | / \ | _ \_ _|
|
||||
# / _ \ | | | | |\/| || || \| | / _ \ | |_) | |
|
||||
# / ___ \| |_| | | | || || |\ | / ___ \| __/| |
|
||||
# / _ \ | | | | |\/| || || \| | / _ \ | |_) | |
|
||||
# / ___ \| |_| | | | || || |\ | / ___ \| __/| |
|
||||
# /_/ \_\____/|_| |_|___|_| \_| /_/ \_\_| |___|
|
||||
#
|
||||
/admin/pending-accounts:
|
||||
$ref: "./adminApi.yaml#/paths/~1admin~1pending-accounts"
|
||||
/admin/accounts/validate/{userId}:
|
||||
$ref: "./adminApi.yaml#/paths/~1admin~1accounts~1validate~1{userId}"
|
||||
/admin/request-join:
|
||||
$ref: "./adminApi.yaml#/paths/~1admin~1request-join"
|
||||
/admin/request-join/decision/{joinRequestId}:
|
||||
$ref: "./adminApi.yaml#/paths/~1admin~1request-join~1decision~1{joinRequestId}"
|
||||
/admin/projects:
|
||||
$ref: "./adminApi.yaml#/paths/~1admin~1projects"
|
||||
/admin/projects/pending/decision:
|
||||
$ref: "./adminApi.yaml#/paths/~1admin~1projects~1pending~1decision"
|
||||
/admin/projects/add:
|
||||
$ref: "./adminApi.yaml#/paths/~1admin~1projects~1add"
|
||||
/admin/appointments/report/{appointmentId}:
|
||||
$ref: "./adminApi.yaml#/paths/~1admin~1appointments~1report~1{appointmentId}"
|
||||
/admin/projects/remove/{projectId}:
|
||||
$ref: "./adminApi.yaml#/paths/~1admin~1projects~1remove~1{projectId}"
|
||||
/admin/projects/pending:
|
||||
$ref: "./adminApi.yaml#/paths/~1admin~1projects~1pending"
|
||||
/admin/projects/pending/decision:
|
||||
$ref: "./adminApi.yaml#/paths/~1admin~1projects~1pending~1decision"
|
||||
/admin/appointments/report/{appointmentId}:
|
||||
$ref: "./adminApi.yaml#/paths/~1admin~1appointments~1report~1{appointmentId}"
|
||||
/admin/appointments/upcoming:
|
||||
$ref: "./adminApi.yaml#/paths/~1admin~1appointments~1upcoming"
|
||||
/admin/projects/{projectId}:
|
||||
$ref: "./adminApi.yaml#/paths/~1admin~1projects~1{projectId}"
|
||||
/admin/make-admin/{userId}:
|
||||
$ref: "./adminApi.yaml#/paths/~1admin~1make-admin~1{userId}"
|
||||
/admin/create-account:
|
||||
$ref: "./adminApi.yaml#/paths/~1admin~1create-account"
|
||||
|
||||
# ____ _ _ _ ____ ___
|
||||
# ____ _ _ _ ____ ___
|
||||
# / ___|| |__ __ _ _ __ ___ __| | / \ | _ \_ _|
|
||||
# \___ \| '_ \ / _` | '__/ _ \/ _` | / _ \ | |_) | |
|
||||
# ___) | | | | (_| | | | __/ (_| | / ___ \| __/| |
|
||||
# \___ \| '_ \ / _` | '__/ _ \/ _` | / _ \ | |_) | |
|
||||
# ___) | | | | (_| | | | __/ (_| | / ___ \| __/| |
|
||||
# |____/|_| |_|\__,_|_| \___|\__,_| /_/ \_\_| |___|
|
||||
#
|
||||
/shared/appointments/upcoming:
|
||||
$ref: "./sharedApi.yaml#/paths/~1shared~1appointments~1upcoming"
|
||||
/shared/projects/sectionCell/{projectId}/{sectionId}/{date}:
|
||||
$ref: "./sharedApi.yaml#/paths/~1shared~1projects~1sectionCell~1{projectId}~1{sectionId}~1{date}"
|
||||
#
|
||||
/shared/projects/sectionCells/{projectId}/{sectionId}/{date}:
|
||||
$ref: "./sharedApi.yaml#/paths/~1shared~1projects~1sectionCells~1{projectId}~1{sectionId}~1{date}"
|
||||
/shared/projects/entrepreneurs/{projectId}:
|
||||
$ref: "./sharedApi.yaml#/paths/~1shared~1projects~1entrepreneurs~1{projectId}"
|
||||
/shared/projects/admin/{projectId}:
|
||||
$ref: "./sharedApi.yaml#/paths/~1shared~1projects~1admin~1{projectId}"
|
||||
/shared/projects/appointments/{projectId}:
|
||||
$ref: "./sharedApi.yaml#/paths/~1shared~1projects~1appointments~1{projectId}"
|
||||
/shared/projects/appointments/report/{appointmentId}:
|
||||
$ref: "./sharedApi.yaml#/paths/~1shared~1projects~1appointments~1report~1{appointmentId}"
|
||||
|
||||
/shared/appointments/report/{appointmentId}:
|
||||
$ref: "./sharedApi.yaml#/paths/~1shared~1appointments~1report~1{appointmentId}"
|
||||
/shared/appointments/request:
|
||||
$ref: "./sharedApi.yaml#/paths/~1shared~1appointments~1request"
|
||||
|
||||
# _____ _ _ _____ ____ _____ ____ ____ _____ _ _ _____ _ _ ____
|
||||
# | ____| \ | |_ _| _ \| ____| _ \| _ \| ____| \ | | ____| | | | _ \
|
||||
# _____ _ _ _____ ____ _____ ____ ____ _____ _ _ _____ _ _ ____
|
||||
# | ____| \ | |_ _| _ \| ____| _ \| _ \| ____| \ | | ____| | | | _ \
|
||||
# | _| | \| | | | | |_) | _| | |_) | |_) | _| | \| | _| | | | | |_) |
|
||||
# | |___| |\ | | | | _ <| |___| __/| _ <| |___| |\ | |___| |_| | _ <
|
||||
# | |___| |\ | | | | _ <| |___| __/| _ <| |___| |\ | |___| |_| | _ <
|
||||
# |_____|_|_\_| |_| |_| \_\_____|_| |_| \_\_____|_| \_|_____|\___/|_| \_\
|
||||
# / \ | _ \_ _|
|
||||
# / _ \ | |_) | |
|
||||
# / ___ \| __/| |
|
||||
# /_/ \_\_| |___|
|
||||
# / \ | _ \_ _|
|
||||
# / _ \ | |_) | |
|
||||
# / ___ \| __/| |
|
||||
# /_/ \_\_| |___|
|
||||
#
|
||||
|
||||
/entrepreneur/projects:
|
||||
$ref: "./entrepreneurApi.yaml#/paths/~1entrepreneur~1projects"
|
||||
/entrepreneur/projects/request:
|
||||
$ref: "./entrepreneurApi.yaml#/paths/~1entrepreneur~1projects~1request"
|
||||
/entrepreneur/sectionCell/add:
|
||||
$ref: "./entrepreneurApi.yaml#/paths/~1entrepreneur~1sectionCell~1add"
|
||||
/entrepreneur/sectionCell/modify:
|
||||
$ref: "./entrepreneurApi.yaml#/paths/~1entrepreneur~1sectionCell~1modify"
|
||||
/entrepreneur/sectionCell/remove/{sectionCellId}:
|
||||
$ref: "./entrepreneurApi.yaml#/paths/~1entrepreneur~1sectionCell~1remove~1{sectionCellId}"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/entrepreneur/sectionCells:
|
||||
$ref: "./entrepreneurApi.yaml#/paths/~1entrepreneur~1sectionCells"
|
||||
/entrepreneur/sectionCells/{sectionCellId}:
|
||||
$ref: "./entrepreneurApi.yaml#/paths/~1entrepreneur~1sectionCells~1{sectionCellId}"
|
||||
/entrepreneur/projects/project-is-active:
|
||||
$ref: "./entrepreneurApi.yaml#/paths/~1entrepreneur~1projects~1project-is-active"
|
||||
/entrepreneur/projects/has-pending-request:
|
||||
$ref: "./entrepreneurApi.yaml#/paths/~1entrepreneur~1projects~1has-pending-request"
|
@ -1,92 +1,210 @@
|
||||
# models.yaml
|
||||
|
||||
user:
|
||||
type: object
|
||||
properties:
|
||||
idUser:
|
||||
type: integer
|
||||
userSurname:
|
||||
type: string
|
||||
userName:
|
||||
type: string
|
||||
primaryMail:
|
||||
type: string
|
||||
example: "example@exmaple.com"
|
||||
secondaryMail:
|
||||
type: string
|
||||
example: "example@exmaple.com"
|
||||
phoneNumber:
|
||||
type: string
|
||||
example: "0612345678"
|
||||
|
||||
type: object
|
||||
properties:
|
||||
idUser:
|
||||
type: integer
|
||||
description: Unique identifier for the user.
|
||||
#readOnly: true # Typically generated by the server
|
||||
example: 101
|
||||
userSurname:
|
||||
type: string
|
||||
description: User's surname (last name).
|
||||
example: "Doe"
|
||||
userName:
|
||||
type: string
|
||||
description: User's given name (first name).
|
||||
example: "John"
|
||||
primaryMail:
|
||||
type: string
|
||||
format: email
|
||||
description: User's primary email address.
|
||||
example: "john.doe@example.com"
|
||||
secondaryMail:
|
||||
type: string
|
||||
format: email
|
||||
description: User's secondary email address (optional).
|
||||
example: "j.doe@personal.com"
|
||||
phoneNumber:
|
||||
type: string
|
||||
description: User's phone number.
|
||||
example: "+33612345678" # Example using international format
|
||||
|
||||
user-entrepreneur:
|
||||
allOf:
|
||||
- $ref: "#/user"
|
||||
- type: object
|
||||
properties:
|
||||
school:
|
||||
properties:
|
||||
school:
|
||||
type: string
|
||||
example: "enseirb"
|
||||
description: The school the entrepreneur attends/attended.
|
||||
example: "ENSEIRB-MATMECA"
|
||||
course:
|
||||
type: string
|
||||
example: "info"
|
||||
description: The specific course or program of study.
|
||||
example: "Electronics"
|
||||
sneeStatus:
|
||||
type: boolean
|
||||
example: false
|
||||
description: Indicates if the user has SNEE status (Statut National d'Étudiant-Entrepreneur).
|
||||
example: true
|
||||
example: # Added full object example
|
||||
idUser: 101
|
||||
userSurname: "Doe"
|
||||
userName: "John"
|
||||
primaryMail: "john.doe@example.com"
|
||||
secondaryMail: "j.doe@personal.com"
|
||||
phoneNumber: "+33612345678"
|
||||
school: "ENSEIRB-MATMECA"
|
||||
course: "Electronics"
|
||||
sneeStatus: true
|
||||
|
||||
user-admin:
|
||||
allOf:
|
||||
- $ref: "#/user"
|
||||
# No additional properties needed for this example
|
||||
example: # Added full object example
|
||||
idUser: 55
|
||||
userSurname: "Admin"
|
||||
userName: "Super"
|
||||
primaryMail: "admin@myinpulse.com"
|
||||
phoneNumber: "+33512345678"
|
||||
|
||||
sectionCell:
|
||||
type: object
|
||||
description: Represents a cell (like a sticky note) within a specific section of a project's Lean Canvas.
|
||||
properties:
|
||||
idSectionCell:
|
||||
type: integer
|
||||
example: this the cell (postit id)
|
||||
description: Unique identifier for the section cell.
|
||||
#readOnly: true # Generated by server
|
||||
example: 508
|
||||
sectionId:
|
||||
type: integer
|
||||
description: Identifier of the Lean Canvas section this cell belongs to (e.g., 1 for Problem, 2 for Solution).
|
||||
example: 1
|
||||
contentSectionCell:
|
||||
type: string
|
||||
description: The text content of the section cell.
|
||||
example: "Users find it hard to track project progress."
|
||||
modificationDate:
|
||||
type: string
|
||||
example: "02-05-2025"
|
||||
format: date # Using Java LocalDate -> YYYY-MM-DD
|
||||
description: The date when this cell was last modified.
|
||||
#readOnly: true # Typically updated by the server on modification
|
||||
example: "yyyy-MM-dd HH:mm"
|
||||
|
||||
project:
|
||||
type: object
|
||||
description: Represents a project being managed or developed.
|
||||
properties:
|
||||
idProject:
|
||||
type: integer
|
||||
description: Unique identifier for the project.
|
||||
#readOnly: true # Generated by server
|
||||
example: 12
|
||||
projectName:
|
||||
type: string
|
||||
description: The name of the project.
|
||||
example: "MyInpulse Mobile App"
|
||||
creationDate:
|
||||
type: string
|
||||
example: "02-05-2025"
|
||||
format: date # Using Java LocalDate -> YYYY-MM-DD
|
||||
description: The date when the project was created in the system.
|
||||
#readOnly: true # Set by server
|
||||
example: "yyyy-MM-dd HH:mm"
|
||||
logo:
|
||||
example: to be discussed not yet fixed
|
||||
type: string
|
||||
format: binary
|
||||
format: byte
|
||||
description: Base64 encoded string representing the project logo image.
|
||||
example: "/*Base64 encoded string representing the project logo image*/"
|
||||
status:
|
||||
type: string
|
||||
enum: [PENDING, ACTIVE, ENDED, ABORTED, REJECTED]
|
||||
description: Corresponds to a status enum internal to the backend, it's value in in requests
|
||||
incoming to the server should be ignored as the client shouldn't be specifying them.
|
||||
example: "NaN"
|
||||
|
||||
joinRequest:
|
||||
type: object
|
||||
description: Represents a request from an entrepreneur to join an already existing project.
|
||||
properties:
|
||||
idProject:
|
||||
type: integer
|
||||
description: the ID of the project the entrepreneur wants to join.
|
||||
example: 42
|
||||
entrepreneur:
|
||||
$ref: "#/user-entrepreneur"
|
||||
|
||||
|
||||
report:
|
||||
type: object
|
||||
description: Represents a report associated with an appointment.
|
||||
properties:
|
||||
idReport:
|
||||
type: integer
|
||||
description: Unique identifier for the report.
|
||||
#readOnly: true # Generated by server
|
||||
example: 987
|
||||
reportContent:
|
||||
type: string
|
||||
|
||||
appointement:
|
||||
description: The textual content of the report. Could be plain text or Markdown (specify if known).
|
||||
example: "Discussed roadmap milestones for Q3. Agreed on preliminary UI mockups."
|
||||
|
||||
appointment: # Corrected typo
|
||||
type: object
|
||||
description: Represents a scheduled meeting or appointment.
|
||||
properties:
|
||||
idAppointment: # Assuming there's an ID
|
||||
type: integer
|
||||
description: Unique identifier for the appointment.
|
||||
#readOnly: true
|
||||
example: 303
|
||||
appointmentDate:
|
||||
type: string
|
||||
example: "02-05-2025"
|
||||
format: date # Using Java LocalDate -> YYYY-MM-DD
|
||||
description: The date of the appointment.
|
||||
example: "2025-05-10"
|
||||
appointmentTime:
|
||||
type: string
|
||||
example: "10:15:30"
|
||||
format: time # Using Java LocalTime -> HH:mm:ss
|
||||
description: The time of the appointment (local time).
|
||||
example: "14:30:00"
|
||||
appointmentDuration:
|
||||
type: string
|
||||
description: Duration of the appointment in ISO 8601 duration format (e.g., PT1H30M for 1 hour 30 minutes).
|
||||
example: "PT1H" # Example for 1 hour
|
||||
appointmentPlace:
|
||||
type: string
|
||||
description: Location or meeting link for the appointment.
|
||||
example: "Meeting Room 3 / https://meet.example.com/abc-def-ghi"
|
||||
appointmentSubject:
|
||||
type: string
|
||||
type: string
|
||||
description: The main topic or subject of the appointment.
|
||||
example: "Q3 Roadmap Planning"
|
||||
# Consider adding project ID or user IDs if relevant association exists
|
||||
|
||||
projectDecision:
|
||||
type: object
|
||||
description: Represents a decision from an admin to accept a pending project.
|
||||
properties:
|
||||
projectId:
|
||||
type: integer
|
||||
description: The ID of the project the entrepreneur wants to join.
|
||||
example: 12
|
||||
adminId:
|
||||
type: integer
|
||||
description: The ID of the project the admin who will supervise the project in case of admission.
|
||||
example: 2
|
||||
isAccepted:
|
||||
type: boolean
|
||||
description: The boolean value of the decision.
|
||||
example: "true"
|
||||
|
||||
joinRequestDecision:
|
||||
type: object
|
||||
description: Represents a decision from an admin to accept a pending project join request.
|
||||
properties:
|
||||
isAccepted:
|
||||
type: boolean
|
||||
description: The boolean value of the decision.
|
||||
example: "true"
|
@ -1,254 +1,193 @@
|
||||
# ____ _ _ _ ____ ___
|
||||
# / ___|| |__ __ _ _ __ ___ __| | / \ | _ \_ _|
|
||||
# \___ \| '_ \ / _` | '__/ _ \/ _` | / _ \ | |_) | |
|
||||
# ___) | | | | (_| | | | __/ (_| | / ___ \| __/| |
|
||||
# |____/|_| |_|\__,_|_| \___|\__,_| /_/ \_\_| |___|
|
||||
#
|
||||
paths:
|
||||
/shared/appointments/upcoming:
|
||||
get:
|
||||
summary: Retourne la list des prochains rendez-vous de l'utilisateur
|
||||
tags:
|
||||
- Shared API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
- MyINPulse-entrepreneur
|
||||
description:
|
||||
JSON array of upcoming appointment data (name, date, time etc..) for a user.
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "main.yaml#/components/schemas/appointement"
|
||||
"400":
|
||||
description: Bad request
|
||||
"401":
|
||||
description: Authorization information is missing or invalid
|
||||
# Shared API Endpoints
|
||||
paths:
|
||||
|
||||
/shared/projects/sectionCell/{projectId}/{sectionId}/{date}:
|
||||
get:
|
||||
summary: Retourne la liste de sections de LC avec un titre donné
|
||||
tags:
|
||||
- Shared API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
- MyINPulse-entrepreneur
|
||||
description:
|
||||
JSON array containing Lean Canvas
|
||||
section data with a title for the
|
||||
current date (or given date if the
|
||||
date parameter is passed)
|
||||
parameters:
|
||||
- in: path
|
||||
required: true
|
||||
name: projectId
|
||||
schema:
|
||||
type: integer
|
||||
- in: path
|
||||
required: true
|
||||
description: this number can be 1, 2,...,8. It is associated with the title of the sectionCell
|
||||
name: sectionId
|
||||
schema:
|
||||
type: integer
|
||||
enum: [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
- in: path
|
||||
required: true
|
||||
name: date
|
||||
description: the date corresponding to the wanted version of lc section. "Nan" for the latest version
|
||||
example: "NaN"
|
||||
schema:
|
||||
type: string
|
||||
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "main.yaml#/components/schemas/sectionCell"
|
||||
"400":
|
||||
description: Bad request
|
||||
"401":
|
||||
description: Authorization information is missing or invalid
|
||||
|
||||
/shared/projects/entrepreneurs/{projectId}:
|
||||
/shared/projects/sectionCells/{projectId}/{sectionId}/{date}:
|
||||
get:
|
||||
summary: Retourne la liste d'entrepreneurs associée à un projet donné
|
||||
tags:
|
||||
- Shared API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
- MyINPulse-entrepreneur
|
||||
description:
|
||||
JSON array of entrepreneur
|
||||
names associated with a project
|
||||
parameters:
|
||||
- in: path
|
||||
name: projectId
|
||||
schema:
|
||||
type: integer
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "main.yaml#/components/schemas/user-entrepreneur"
|
||||
"400":
|
||||
description: Bad request
|
||||
"401":
|
||||
description: Authorization information is missing or invalid
|
||||
|
||||
/shared/projects/admin/{projectId}:
|
||||
get:
|
||||
summary: Retourne les informations de l'admin qui accompagne le projet
|
||||
tags:
|
||||
- Shared API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
- MyINPulse-entrepreneur
|
||||
description:
|
||||
JSON object containing information (name, gmail, tel, etc..)
|
||||
the admin supervising the project with id projectID.
|
||||
parameters:
|
||||
- in: path
|
||||
name: projectId
|
||||
schema:
|
||||
type: integer
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "main.yaml#/components/schemas/user-admin"
|
||||
"400":
|
||||
description: Bad request
|
||||
"401":
|
||||
description: Authorization information is
|
||||
missing or invalid
|
||||
|
||||
/shared/projects/appointments/{projectId}:
|
||||
get:
|
||||
summary: Retourne les rendez-vous du projet
|
||||
tags:
|
||||
- Shared API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
- MyINPulse-entrepreneur
|
||||
description:
|
||||
JSON array of upcoming and past appointment
|
||||
data for the project with id projectID.
|
||||
parameters:
|
||||
- in: path
|
||||
name: projectId
|
||||
schema:
|
||||
type: integer
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "main.yaml#/components/schemas/appointement"
|
||||
"400":
|
||||
description: Bad request
|
||||
"401":
|
||||
description: Authorization information is
|
||||
missing or invalid
|
||||
/shared/projects/appointments/report/{appointmentId}:
|
||||
get:
|
||||
summary: Retourne le rapport pdf du rendez-vous
|
||||
tags:
|
||||
- Shared API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-admin
|
||||
- MyINPulse-entrepreneur
|
||||
description:
|
||||
PDF file containing the ap-
|
||||
pointment report
|
||||
parameters:
|
||||
- in: path
|
||||
name: apointementId
|
||||
schema:
|
||||
type: integer
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/pdf:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
"400":
|
||||
description: Bad request
|
||||
"401":
|
||||
description: Authorization information is
|
||||
missing or invalid
|
||||
|
||||
/shared/appointments/request:
|
||||
post:
|
||||
summary: demander un rendez-vous
|
||||
description:
|
||||
will add an appointement request request by the applicant
|
||||
to have an appointment to be confirmed or denied by the
|
||||
specified participants of the appointement.
|
||||
operationId: getSectionCellsByDate
|
||||
summary: Get project section cells modified on a specific date
|
||||
tags:
|
||||
- Shared API
|
||||
security:
|
||||
- MyINPulse:
|
||||
- MyINPulse-entrepreneur
|
||||
- MyINPulse-admin
|
||||
- MyINPulse: [MyINPulse-entrepreneur, MyINPulse-admin]
|
||||
description: Retrieves section cells belonging to a specific section of a project, filtered by the last modification date. Requires user to have access to the project.
|
||||
parameters:
|
||||
- in: path
|
||||
name: projectId
|
||||
required: true
|
||||
schema: { type: integer }
|
||||
description: ID of the project.
|
||||
- in: path
|
||||
name: sectionId
|
||||
required: true
|
||||
schema: { type: integer }
|
||||
description: ID of the Lean Canvas section.
|
||||
- in: path
|
||||
name: date
|
||||
required: true
|
||||
schema: { type: string, format: date } # Expect YYYY-MM-DD
|
||||
description: The modification date to filter by (YYYY-MM-DD HH:mm).
|
||||
responses:
|
||||
"200":
|
||||
description: OK - List of section cells matching the criteria.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "./main.yaml#/components/schemas/sectionCell"
|
||||
"400":
|
||||
description: Bad Request - Invalid parameter format.
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
"401":
|
||||
description: Unauthorized.
|
||||
|
||||
|
||||
/shared/projects/entrepreneurs/{projectId}:
|
||||
get:
|
||||
operationId: getProjectEntrepreneurs
|
||||
summary: Get entrepreneurs associated with a project
|
||||
tags:
|
||||
- Shared API
|
||||
security:
|
||||
- MyINPulse: [MyINPulse-entrepreneur, MyINPulse-admin]
|
||||
description: Retrieves a list of entrepreneur users associated with the specified project. Requires access to the project.
|
||||
parameters:
|
||||
- in: path
|
||||
name: projectId
|
||||
required: true
|
||||
schema: { type: integer }
|
||||
description: ID of the project.
|
||||
responses:
|
||||
"200":
|
||||
description: OK - List of entrepreneurs.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "./main.yaml#/components/schemas/user-entrepreneur"
|
||||
"401":
|
||||
description: Unauthorized.
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
"404":
|
||||
description: Not Found - Project not found.
|
||||
|
||||
/shared/projects/admin/{projectId}: # Path updated
|
||||
get:
|
||||
operationId: getProjectAdmin
|
||||
summary: Get admin associated with a project
|
||||
tags:
|
||||
- Shared API
|
||||
security:
|
||||
- MyINPulse: [MyINPulse-entrepreneur, MyINPulse-admin]
|
||||
description: Retrieves a list of admin users associated with the specified project. Requires access to the project.
|
||||
parameters:
|
||||
- in: path
|
||||
name: projectId
|
||||
required: true
|
||||
schema: { type: integer }
|
||||
description: ID of the project.
|
||||
responses:
|
||||
"200":
|
||||
description: OK - admin.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "./main.yaml#/components/schemas/user-admin"
|
||||
"401":
|
||||
description: Unauthorized.
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
"404":
|
||||
description: Not Found - Project not found.
|
||||
|
||||
|
||||
/shared/projects/appointments/{projectId}:
|
||||
get:
|
||||
operationId: getProjectAppointments
|
||||
summary: Get appointments related to a project
|
||||
tags:
|
||||
- Shared API
|
||||
security:
|
||||
- MyINPulse: [MyINPulse-entrepreneur, MyINPulse-admin]
|
||||
description: Retrieves a list of appointments associated with the specified project. Requires access to the project.
|
||||
parameters:
|
||||
- in: path
|
||||
name: projectId
|
||||
required: true
|
||||
schema: { type: integer }
|
||||
description: ID of the project.
|
||||
responses:
|
||||
"200":
|
||||
description: OK - List of appointments.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "./main.yaml#/components/schemas/appointment"
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
"401":
|
||||
description: Unauthorized.
|
||||
|
||||
|
||||
/shared/appointments/report/{appointmentId}: # Path updated
|
||||
get:
|
||||
operationId: getAppointmentReport # Shared endpoint implies read-only access might be possible
|
||||
summary: Get the report for an appointment
|
||||
tags:
|
||||
- Shared API
|
||||
security:
|
||||
- MyINPulse: [MyINPulse-entrepreneur, MyINPulse-admin]
|
||||
description: Retrieves the report associated with a specific appointment. Requires user to have access to the appointment/project.
|
||||
parameters:
|
||||
- in: path
|
||||
name: appointmentId
|
||||
required: true
|
||||
schema: { type: integer }
|
||||
description: ID of the appointment.
|
||||
responses:
|
||||
"200":
|
||||
description: OK - Report PDF returned.
|
||||
content:
|
||||
application/pdf:
|
||||
schema:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
"401":
|
||||
description: Unauthorized.
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
|
||||
|
||||
/shared/appointments/request:
|
||||
post:
|
||||
operationId: requestAppointment
|
||||
summary: Request a new appointment
|
||||
tags:
|
||||
- Shared API
|
||||
security:
|
||||
- MyINPulse: [MyINPulse-entrepreneur, MyINPulse-admin]
|
||||
description: Allows a user (entrepreneur or admin) to request a new appointment, potentially with another user or regarding a project. Details in the body. The request might need confirmation or create a pending appointment.
|
||||
requestBody:
|
||||
description: \"participants\" property is an array containing userids of the participants in the appointement
|
||||
required: true
|
||||
description: Details of the appointment request.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
title:
|
||||
type: string
|
||||
start_time:
|
||||
type: string
|
||||
end_time:
|
||||
type: string
|
||||
place:
|
||||
type: string
|
||||
applicantId:
|
||||
type: integer
|
||||
participants:
|
||||
#/* */
|
||||
type: array
|
||||
items:
|
||||
type: integer
|
||||
|
||||
$ref: "./main.yaml#/components/schemas/appointment" # Assuming request uses same model structure
|
||||
# Potentially add projectId or targetUserId here
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
"200": # Accepted seems appropriate for a request
|
||||
description: Accepted - Appointment request submitted.
|
||||
"400":
|
||||
description: Bad request
|
||||
description: Bad Request - Invalid appointment details.
|
||||
"401":
|
||||
description: Authorization information is
|
||||
missing or invalid
|
||||
|
||||
description: Unauthorized.
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
|
90
documentation/openapi/src/unauthApi.yaml
Normal file
90
documentation/openapi/src/unauthApi.yaml
Normal file
@ -0,0 +1,90 @@
|
||||
|
||||
# _ _ _ _ _ _
|
||||
# | | | |_ __ __ _ _ _| |_| |__ / \ _ __ (_)
|
||||
# | | | | '_ \ / _` | | | | __| '_ \ / _ \ | '_ \| |
|
||||
# | |_| | | | | (_| | |_| | |_| | | |/ ___ \| |_) | |
|
||||
# \___/|_| |_|\__,_|\__,_|\__|_| |_/_/ \_\ .__/|_|
|
||||
# |_|
|
||||
|
||||
paths:
|
||||
/unauth/finalize:
|
||||
post:
|
||||
summary: Finalize account setup using authentication token
|
||||
description: |-
|
||||
Completes the user account creation/setup process in the MyInpulse system.
|
||||
This endpoint requires the user to be authenticated via Keycloak (e.g., after initial login).
|
||||
User details (name, email, etc.) are extracted from the authenticated user's token (e.g., Keycloak JWT).
|
||||
No request body is needed. The account is marked as pending admin validation upon successful finalization.
|
||||
tags:
|
||||
- Unauth API
|
||||
responses:
|
||||
"200":
|
||||
description: Created - Account finalized and pending admin validation. Returns the user profile.
|
||||
"400":
|
||||
description: Bad Request - Problem processing the token or user data derived from it.
|
||||
"401":
|
||||
description: Unauthorized - Valid authentication token required.
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
/unauth/request-join/{projectId}:
|
||||
post:
|
||||
summary: Request to join an existing project
|
||||
description: Submits a request for the authenticated user (keycloack authenticated) to join the project specified by projectId. Their role is then changed to entrepreneur in server and Keycloak. This requires approval from a project admin.
|
||||
tags:
|
||||
- Unauth API
|
||||
parameters:
|
||||
- in: path
|
||||
name: projectId
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
description: The ID of the project to request joining.
|
||||
example: 15
|
||||
responses: # Moved responses block to correct level
|
||||
"200":
|
||||
description: Accepted - Join request submitted and pending approval.
|
||||
"400":
|
||||
description: Bad Request - Invalid project ID format
|
||||
"409":
|
||||
description: Already member/request pending.
|
||||
"401":
|
||||
description: Unauthorized.
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
/unauth/request-admin-role:
|
||||
post:
|
||||
summary: Request to become an admin
|
||||
description: Submits a request for the authenticated user (keycloack authenticated) to become an admin. Their role is then changed to admin in server and Keycloak. This requires approval from a project admin.
|
||||
tags:
|
||||
- Unauth API
|
||||
responses:
|
||||
"200":
|
||||
description: Accepted - Become admin request submitted and pending approval.
|
||||
"400":
|
||||
description: Bad Request - Invalid project ID format or already member/request pending.
|
||||
"401":
|
||||
description: Unauthorized.
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
||||
|
||||
/unauth/check-if-not-pending:
|
||||
get:
|
||||
summary: Returns a boolean of whether the user's account is not pending
|
||||
description: Returns a boolean with value `true` if the user's account is not pending and `false` if it is.
|
||||
tags:
|
||||
- Unauth API
|
||||
responses:
|
||||
"200":
|
||||
description: Accepted - Become admin request submitted and pending approval.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: boolean
|
||||
"400":
|
||||
description: Bad Request - Invalid project ID format or already member/request pending.
|
||||
"401":
|
||||
description: Unauthorized.
|
||||
"404":
|
||||
description: Bad Request - User not found in database.
|
||||
"403":
|
||||
description: Bad Token - Invalid Keycloack configuration.
|
@ -1947,7 +1947,6 @@
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/swagger-cli/-/swagger-cli-4.0.4.tgz",
|
||||
"integrity": "sha512-Cp8YYuLny3RJFQ4CvOBTaqmOOgYsem52dPx1xM5S4EUWFblIh2Q8atppMZvXKUr1e9xH5RwipYpmdUzdPcxWcA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@apidevtools/swagger-cli": "4.0.4"
|
||||
},
|
||||
|
0
front/Dockerfile
Normal file → Executable file
0
front/Dockerfile
Normal file → Executable file
@ -1,13 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Vite App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
10
front/MyINPulse-front/package-lock.json
generated
10
front/MyINPulse-front/package-lock.json
generated
@ -10,6 +10,7 @@
|
||||
"dependencies": {
|
||||
"axios": "^1.7.9",
|
||||
"cors": "^2.8.5",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"keycloak-js": "^26.1.0",
|
||||
"pinia": "^2.3.1",
|
||||
"pinia-plugin-persistedstate": "^4.2.0",
|
||||
@ -3588,6 +3589,15 @@
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/jwt-decode": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
|
||||
"integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/keycloak-js": {
|
||||
"version": "26.1.0",
|
||||
"resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-26.1.0.tgz",
|
||||
|
@ -18,7 +18,8 @@
|
||||
"pinia": "^2.3.1",
|
||||
"pinia-plugin-persistedstate": "^4.2.0",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.0"
|
||||
"vue-router": "^4.5.0",
|
||||
"jwt-decode": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.49.1",
|
||||
|
79
front/MyINPulse-front/src/ApiClasses/Appointment.ts
Normal file
79
front/MyINPulse-front/src/ApiClasses/Appointment.ts
Normal file
@ -0,0 +1,79 @@
|
||||
// appointment.ts
|
||||
class Appointment {
|
||||
private _idAppointment?: number;
|
||||
private _appointmentDate?: string;
|
||||
private _appointmentTime?: string;
|
||||
private _appointmentDuration?: string;
|
||||
private _appointmentPlace?: string;
|
||||
private _appointmentSubject?: string;
|
||||
|
||||
constructor(data: Partial<Appointment> = {}) {
|
||||
this._idAppointment = data.idAppointment;
|
||||
this._appointmentDate = data.appointmentDate;
|
||||
this._appointmentTime = data.appointmentTime;
|
||||
this._appointmentDuration = data.appointmentDuration;
|
||||
this._appointmentPlace = data.appointmentPlace;
|
||||
this._appointmentSubject = data.appointmentSubject;
|
||||
}
|
||||
|
||||
get idAppointment(): number | undefined {
|
||||
return this._idAppointment;
|
||||
}
|
||||
|
||||
set idAppointment(value: number | undefined) {
|
||||
this._idAppointment = value;
|
||||
}
|
||||
|
||||
get appointmentDate(): string | undefined {
|
||||
return this._appointmentDate;
|
||||
}
|
||||
|
||||
set appointmentDate(value: string | undefined) {
|
||||
this._appointmentDate = value;
|
||||
}
|
||||
|
||||
get appointmentTime(): string | undefined {
|
||||
return this._appointmentTime;
|
||||
}
|
||||
|
||||
set appointmentTime(value: string | undefined) {
|
||||
this._appointmentTime = value;
|
||||
}
|
||||
|
||||
get appointmentDuration(): string | undefined {
|
||||
return this._appointmentDuration;
|
||||
}
|
||||
|
||||
set appointmentDuration(value: string | undefined) {
|
||||
this._appointmentDuration = value;
|
||||
}
|
||||
|
||||
get appointmentPlace(): string | undefined {
|
||||
return this._appointmentPlace;
|
||||
}
|
||||
|
||||
set appointmentPlace(value: string | undefined) {
|
||||
this._appointmentPlace = value;
|
||||
}
|
||||
|
||||
get appointmentSubject(): string | undefined {
|
||||
return this._appointmentSubject;
|
||||
}
|
||||
|
||||
set appointmentSubject(value: string | undefined) {
|
||||
this._appointmentSubject = value;
|
||||
}
|
||||
|
||||
toObject() {
|
||||
return {
|
||||
idAppointment: this.idAppointment,
|
||||
appointmentDate: this.appointmentDate,
|
||||
appointmentTime: this.appointmentTime,
|
||||
appointmentDuration: this.appointmentDuration,
|
||||
appointmentPlace: this.appointmentPlace,
|
||||
appointmentSubject: this.appointmentSubject,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default Appointment;
|
39
front/MyINPulse-front/src/ApiClasses/JoinRequest.ts
Normal file
39
front/MyINPulse-front/src/ApiClasses/JoinRequest.ts
Normal file
@ -0,0 +1,39 @@
|
||||
// joinRequest.ts
|
||||
import UserEntrepreneur from "./UserEntrepreneur";
|
||||
|
||||
class JoinRequest {
|
||||
private _idProject?: number;
|
||||
private _entrepreneur?: UserEntrepreneur;
|
||||
|
||||
constructor(data: Partial<JoinRequest> = {}) {
|
||||
this._idProject = data.idProject;
|
||||
this._entrepreneur = data.entrepreneur
|
||||
? new UserEntrepreneur(data.entrepreneur)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
get idProject(): number | undefined {
|
||||
return this._idProject;
|
||||
}
|
||||
|
||||
set idProject(value: number | undefined) {
|
||||
this._idProject = value;
|
||||
}
|
||||
|
||||
get entrepreneur(): UserEntrepreneur | undefined {
|
||||
return this._entrepreneur;
|
||||
}
|
||||
|
||||
set entrepreneur(value: UserEntrepreneur | undefined) {
|
||||
this._entrepreneur = value;
|
||||
}
|
||||
|
||||
toObject() {
|
||||
return {
|
||||
idProject: this.idProject,
|
||||
entrepreneur: this.entrepreneur,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default JoinRequest;
|
24
front/MyINPulse-front/src/ApiClasses/JoinRequestDecision.ts
Normal file
24
front/MyINPulse-front/src/ApiClasses/JoinRequestDecision.ts
Normal file
@ -0,0 +1,24 @@
|
||||
// joinRequestDecision.ts
|
||||
class JoinRequestDecision {
|
||||
private _isAccepted?: boolean;
|
||||
|
||||
constructor(data: Partial<JoinRequestDecision> = {}) {
|
||||
this._isAccepted = data.isAccepted;
|
||||
}
|
||||
|
||||
get isAccepted(): boolean | undefined {
|
||||
return this._isAccepted;
|
||||
}
|
||||
|
||||
set isAccepted(value: boolean | undefined) {
|
||||
this._isAccepted = value;
|
||||
}
|
||||
|
||||
toObject() {
|
||||
return {
|
||||
isAccepted: this._isAccepted,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default JoinRequestDecision;
|
89
front/MyINPulse-front/src/ApiClasses/Project.ts
Normal file
89
front/MyINPulse-front/src/ApiClasses/Project.ts
Normal file
@ -0,0 +1,89 @@
|
||||
// project.ts
|
||||
class Project {
|
||||
private _idProject?: number;
|
||||
private _projectName?: string;
|
||||
private _creationDate?: string;
|
||||
private _logo?: string;
|
||||
private _status?: "PENDING" | "ACTIVE" | "ENDED" | "ABORTED" | "REJECTED";
|
||||
|
||||
constructor(data: Partial<Project> = {}) {
|
||||
this._idProject = data.idProject;
|
||||
this._projectName = data.projectName;
|
||||
this._creationDate = data.creationDate;
|
||||
this._logo = data.logo;
|
||||
this._status = data.status;
|
||||
}
|
||||
|
||||
get idProject(): number | undefined {
|
||||
return this._idProject;
|
||||
}
|
||||
|
||||
set idProject(value: number | undefined) {
|
||||
this._idProject = value;
|
||||
}
|
||||
|
||||
get projectName(): string {
|
||||
return this._projectName ?? "";
|
||||
}
|
||||
|
||||
set projectName(value: string | undefined) {
|
||||
this._projectName = value;
|
||||
}
|
||||
|
||||
get creationDate(): string {
|
||||
return this._creationDate ?? "";
|
||||
}
|
||||
|
||||
set creationDate(value: string | undefined) {
|
||||
this._creationDate = value;
|
||||
}
|
||||
|
||||
get logo(): string | undefined {
|
||||
return this._logo;
|
||||
}
|
||||
|
||||
set logo(value: string | undefined) {
|
||||
this._logo = value;
|
||||
}
|
||||
|
||||
get status():
|
||||
| "PENDING"
|
||||
| "ACTIVE"
|
||||
| "ENDED"
|
||||
| "ABORTED"
|
||||
| "REJECTED"
|
||||
| undefined {
|
||||
return this._status;
|
||||
}
|
||||
|
||||
set status(
|
||||
value:
|
||||
| "PENDING"
|
||||
| "ACTIVE"
|
||||
| "ENDED"
|
||||
| "ABORTED"
|
||||
| "REJECTED"
|
||||
| undefined
|
||||
) {
|
||||
this._status = value;
|
||||
}
|
||||
|
||||
toObject() {
|
||||
return {
|
||||
idProject: this.idProject,
|
||||
projectName: this.projectName,
|
||||
creationDate: this.creationDate,
|
||||
logo: this.logo,
|
||||
status: this.status,
|
||||
};
|
||||
}
|
||||
|
||||
toCreatePayload() {
|
||||
return {
|
||||
projectName: this.projectName,
|
||||
logo: this.logo,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default Project;
|
46
front/MyINPulse-front/src/ApiClasses/ProjectDecision.ts
Normal file
46
front/MyINPulse-front/src/ApiClasses/ProjectDecision.ts
Normal file
@ -0,0 +1,46 @@
|
||||
// projectDecision.ts
|
||||
class ProjectDecision {
|
||||
private _projectId?: number;
|
||||
private _adminId?: number;
|
||||
private _isAccepted?: boolean;
|
||||
|
||||
constructor(data: Partial<ProjectDecision> = {}) {
|
||||
this._projectId = data.projectId;
|
||||
this._adminId = data.adminId;
|
||||
this._isAccepted = data.isAccepted;
|
||||
}
|
||||
|
||||
get projectId(): number | undefined {
|
||||
return this._projectId;
|
||||
}
|
||||
|
||||
set projectId(value: number | undefined) {
|
||||
this._projectId = value;
|
||||
}
|
||||
|
||||
get adminId(): number | undefined {
|
||||
return this._adminId;
|
||||
}
|
||||
|
||||
set adminId(value: number | undefined) {
|
||||
this._adminId = value;
|
||||
}
|
||||
|
||||
get isAccepted(): boolean | undefined {
|
||||
return this._isAccepted;
|
||||
}
|
||||
|
||||
set isAccepted(value: boolean | undefined) {
|
||||
this._isAccepted = value;
|
||||
}
|
||||
|
||||
toObject() {
|
||||
return {
|
||||
projectId: this._projectId,
|
||||
adminId: this._adminId,
|
||||
isAccepted: this._isAccepted,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default ProjectDecision;
|
35
front/MyINPulse-front/src/ApiClasses/Repport.ts
Normal file
35
front/MyINPulse-front/src/ApiClasses/Repport.ts
Normal file
@ -0,0 +1,35 @@
|
||||
// report.ts
|
||||
class Report {
|
||||
private _idReport?: number;
|
||||
private _reportContent?: string;
|
||||
|
||||
constructor(data: Partial<Report> = {}) {
|
||||
this._idReport = data.idReport;
|
||||
this._reportContent = data.reportContent;
|
||||
}
|
||||
|
||||
get idReport(): number | undefined {
|
||||
return this._idReport;
|
||||
}
|
||||
|
||||
set idReport(value: number | undefined) {
|
||||
this._idReport = value;
|
||||
}
|
||||
|
||||
get reportContent(): string | undefined {
|
||||
return this._reportContent;
|
||||
}
|
||||
|
||||
set reportContent(value: string | undefined) {
|
||||
this._reportContent = value;
|
||||
}
|
||||
|
||||
toObject() {
|
||||
return {
|
||||
idReport: this._idReport,
|
||||
reportContent: this._reportContent,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default Report;
|
57
front/MyINPulse-front/src/ApiClasses/SectionCell.ts
Normal file
57
front/MyINPulse-front/src/ApiClasses/SectionCell.ts
Normal file
@ -0,0 +1,57 @@
|
||||
// sectionCell.ts
|
||||
class SectionCell {
|
||||
private _idSectionCell?: number;
|
||||
private _sectionId?: number;
|
||||
private _contentSectionCell?: string;
|
||||
private _modificationDate?: string;
|
||||
|
||||
constructor(data: Partial<SectionCell> = {}) {
|
||||
this._idSectionCell = data.idSectionCell;
|
||||
this._sectionId = data.sectionId;
|
||||
this._contentSectionCell = data.contentSectionCell;
|
||||
this._modificationDate = data.modificationDate;
|
||||
}
|
||||
|
||||
get idSectionCell(): number | undefined {
|
||||
return this._idSectionCell;
|
||||
}
|
||||
|
||||
set idSectionCell(value: number | undefined) {
|
||||
this._idSectionCell = value;
|
||||
}
|
||||
|
||||
get sectionId(): number | undefined {
|
||||
return this._sectionId;
|
||||
}
|
||||
|
||||
set sectionId(value: number | undefined) {
|
||||
this._sectionId = value;
|
||||
}
|
||||
|
||||
get contentSectionCell(): string | undefined {
|
||||
return this._contentSectionCell;
|
||||
}
|
||||
|
||||
set contentSectionCell(value: string | undefined) {
|
||||
this._contentSectionCell = value;
|
||||
}
|
||||
|
||||
get modificationDate(): string | undefined {
|
||||
return this._modificationDate;
|
||||
}
|
||||
|
||||
set modificationDate(value: string | undefined) {
|
||||
this._modificationDate = value;
|
||||
}
|
||||
|
||||
toObject() {
|
||||
return {
|
||||
idSectionCell: this._idSectionCell,
|
||||
sectionId: this._sectionId,
|
||||
contentSectionCell: this._contentSectionCell,
|
||||
modificationDate: this._modificationDate,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default SectionCell;
|
78
front/MyINPulse-front/src/ApiClasses/User.ts
Normal file
78
front/MyINPulse-front/src/ApiClasses/User.ts
Normal file
@ -0,0 +1,78 @@
|
||||
class User {
|
||||
private _idUser?: number;
|
||||
private _userSurname?: string;
|
||||
private _userName?: string;
|
||||
private _primaryMail?: string;
|
||||
private _secondaryMail?: string;
|
||||
private _phoneNumber?: string;
|
||||
|
||||
constructor(data: Partial<User> = {}) {
|
||||
this._idUser = data.idUser;
|
||||
this._userSurname = data.userSurname;
|
||||
this._userName = data.userName;
|
||||
this._primaryMail = data.primaryMail;
|
||||
this._secondaryMail = data.secondaryMail;
|
||||
this._phoneNumber = data.phoneNumber;
|
||||
}
|
||||
|
||||
get idUser(): number | undefined {
|
||||
return this._idUser;
|
||||
}
|
||||
|
||||
set idUser(value: number | undefined) {
|
||||
this._idUser = value;
|
||||
}
|
||||
|
||||
get userSurname(): string | undefined {
|
||||
return this._userSurname;
|
||||
}
|
||||
|
||||
set userSurname(value: string | undefined) {
|
||||
this._userSurname = value;
|
||||
}
|
||||
|
||||
get userName(): string | undefined {
|
||||
return this._userName;
|
||||
}
|
||||
|
||||
set userName(value: string | undefined) {
|
||||
this._userName = value;
|
||||
}
|
||||
|
||||
get primaryMail(): string | undefined {
|
||||
return this._primaryMail;
|
||||
}
|
||||
|
||||
set primaryMail(value: string | undefined) {
|
||||
this._primaryMail = value;
|
||||
}
|
||||
|
||||
get secondaryMail(): string | undefined {
|
||||
return this._secondaryMail;
|
||||
}
|
||||
|
||||
set secondaryMail(value: string | undefined) {
|
||||
this._secondaryMail = value;
|
||||
}
|
||||
|
||||
get phoneNumber(): string | undefined {
|
||||
return this._phoneNumber;
|
||||
}
|
||||
|
||||
set phoneNumber(value: string | undefined) {
|
||||
this._phoneNumber = value;
|
||||
}
|
||||
|
||||
toObject() {
|
||||
return {
|
||||
idUser: this._idUser,
|
||||
userSurname: this._userSurname,
|
||||
userName: this._userName,
|
||||
primaryMail: this._primaryMail,
|
||||
secondaryMail: this._secondaryMail,
|
||||
phoneNumber: this._phoneNumber,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default User;
|
14
front/MyINPulse-front/src/ApiClasses/UserAdmin.ts
Normal file
14
front/MyINPulse-front/src/ApiClasses/UserAdmin.ts
Normal file
@ -0,0 +1,14 @@
|
||||
// user-admin.ts
|
||||
import User from "./User";
|
||||
|
||||
class UserAdmin extends User {
|
||||
constructor(data: Partial<UserAdmin> = {}) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
get idUser(): number | undefined {
|
||||
return super.idUser;
|
||||
}
|
||||
}
|
||||
|
||||
export default UserAdmin;
|
50
front/MyINPulse-front/src/ApiClasses/UserEntrepreneur.ts
Normal file
50
front/MyINPulse-front/src/ApiClasses/UserEntrepreneur.ts
Normal file
@ -0,0 +1,50 @@
|
||||
// user-entrepreneur.ts
|
||||
import User from "./User";
|
||||
|
||||
class UserEntrepreneur extends User {
|
||||
private _school?: string;
|
||||
private _course?: string;
|
||||
private _sneeStatus?: boolean;
|
||||
|
||||
constructor(data: Partial<UserEntrepreneur> = {}) {
|
||||
super(data);
|
||||
this._school = data.school;
|
||||
this._course = data.course;
|
||||
this._sneeStatus = data.sneeStatus;
|
||||
}
|
||||
|
||||
get school(): string | undefined {
|
||||
return this._school;
|
||||
}
|
||||
|
||||
set school(value: string | undefined) {
|
||||
this._school = value;
|
||||
}
|
||||
|
||||
get course(): string | undefined {
|
||||
return this._course;
|
||||
}
|
||||
|
||||
set course(value: string | undefined) {
|
||||
this._course = value;
|
||||
}
|
||||
|
||||
get sneeStatus(): boolean | undefined {
|
||||
return this._sneeStatus;
|
||||
}
|
||||
|
||||
set sneeStatus(value: boolean | undefined) {
|
||||
this._sneeStatus = value;
|
||||
}
|
||||
|
||||
toObject() {
|
||||
return {
|
||||
...super.toObject(),
|
||||
school: this._school,
|
||||
course: this._course,
|
||||
sneeStatus: this._sneeStatus,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default UserEntrepreneur;
|
@ -1,47 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { RouterView } from "vue-router";
|
||||
import { /*RouterLink,*/ RouterView } from "vue-router";
|
||||
import ErrorWrapper from "@/views/errorWrapper.vue";
|
||||
import ProjectComponent from "@/components/ProjectComponent.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<HeaderComponent />
|
||||
<error-wrapper></error-wrapper>
|
||||
<div id="main">
|
||||
<ProjectComponent
|
||||
v-for="(project, index) in projects"
|
||||
:key="index"
|
||||
:project-name="project.name"
|
||||
/>
|
||||
</div>
|
||||
<Header />
|
||||
<ErrorWrapper />
|
||||
<!--<RouterLink to="/">Home</RouterLink> | -->
|
||||
<!--<RouterLink to="/canvas">Canvas</RouterLink> -->
|
||||
<RouterView />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import HeaderComponent from "@/components/HeaderComponent.vue";
|
||||
|
||||
export default {
|
||||
name: "App",
|
||||
components: {
|
||||
HeaderComponent,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
projects: [
|
||||
{
|
||||
name: "Projet Alpha",
|
||||
//link: './project-alpha.html',
|
||||
//members: ['Alice', 'Bob', 'Charlie'],
|
||||
},
|
||||
{
|
||||
name: "Projet Beta",
|
||||
//link: './project-beta.html',
|
||||
//members: ['David', 'Eve', 'Frank'],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
98
front/MyINPulse-front/src/components/AddProjectForm.vue
Normal file
98
front/MyINPulse-front/src/components/AddProjectForm.vue
Normal file
@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<form class="add-project-form" @submit.prevent="submitProject">
|
||||
<h2>Ajouter un projet</h2>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="projectName">Nom du projet</label>
|
||||
<input
|
||||
id="projectName"
|
||||
v-model="project.projectName"
|
||||
type="text"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="logo">Logo</label>
|
||||
<input
|
||||
id="logo"
|
||||
v-model="project.logo"
|
||||
type="text"
|
||||
placeholder="(Description)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button type="submit">Ajouter</button>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import Project from "@/ApiClasses/Project";
|
||||
import { addProjectManually } from "@/services/Apis/Admin";
|
||||
|
||||
const project = ref(new Project({}));
|
||||
|
||||
function submitProject() {
|
||||
addProjectManually(
|
||||
project.value.toCreatePayload(),
|
||||
(res) => console.log("Success:", res.data),
|
||||
(err) => console.error("Error:", err)
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
color: #333;
|
||||
margin-bottom: 1.2rem;
|
||||
border-bottom: 2px solid #ddd;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.add-project-form {
|
||||
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 {
|
||||
margin-bottom: 20px;
|
||||
font-size: 24px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
input {
|
||||
padding: 8px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #4caf50;
|
||||
color: white;
|
||||
padding: 10px 15px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
</style>
|
192
front/MyINPulse-front/src/components/AdminAppointments.vue
Normal file
192
front/MyINPulse-front/src/components/AdminAppointments.vue
Normal file
@ -0,0 +1,192 @@
|
||||
<template>
|
||||
<div>
|
||||
<h2>Ajouter / Modifier un Rapport pour un Rendez-vous</h2>
|
||||
|
||||
<form @submit.prevent="submitReport">
|
||||
<div>
|
||||
<label for="appointmentId">ID du rendez-vous :</label>
|
||||
<div style="display: flex; align-items: center; gap: 0.5rem">
|
||||
<button type="button" @click="decrementId">-</button>
|
||||
<input
|
||||
id="appointmentId"
|
||||
v-model.number="appointmentId"
|
||||
type="number"
|
||||
min="1"
|
||||
style="width: 60px; text-align: center"
|
||||
/>
|
||||
<button type="button" @click="incrementId">+</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="reportContent">Contenu du rapport :</label>
|
||||
<textarea
|
||||
id="reportContent"
|
||||
v-model="reportContent"
|
||||
required
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<button type="submit">
|
||||
{{ isUpdate ? "Mettre à jour" : "Créer" }} le rapport
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<p v-if="responseMessage">{{ responseMessage }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
createAppointmentReport,
|
||||
updateAppointmentReport,
|
||||
} from "@/services/Apis/Admin";
|
||||
|
||||
export default {
|
||||
name: "AdminAppointmentsComponent",
|
||||
data() {
|
||||
return {
|
||||
appointmentId: 1,
|
||||
reportContent: "",
|
||||
responseMessage: "",
|
||||
isUpdate: false,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
appointmentId(newVal) {
|
||||
if (newVal) {
|
||||
this.isUpdate = true;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
incrementId() {
|
||||
this.appointmentId++;
|
||||
},
|
||||
decrementId() {
|
||||
if (this.appointmentId > 1) {
|
||||
this.appointmentId--;
|
||||
}
|
||||
},
|
||||
submitReport() {
|
||||
const reportData = { content: this.reportContent };
|
||||
const onSuccess = (response) => {
|
||||
if (response.status === 201 || response.status === 200) {
|
||||
this.responseMessage =
|
||||
"Rapport " +
|
||||
(this.isUpdate ? "mis à jour" : "créé") +
|
||||
" avec succès.";
|
||||
}
|
||||
};
|
||||
const onError = (error) => {
|
||||
console.error(
|
||||
"Erreur lors de l'envoi du rapport :",
|
||||
error.response || error.message
|
||||
);
|
||||
if (error.response && error.response.status === 400) {
|
||||
this.responseMessage =
|
||||
"Requête invalide. Vérifiez les informations.";
|
||||
} else if (error.response && error.response.status === 401) {
|
||||
this.responseMessage =
|
||||
"Vous n'êtes pas autorisé à effectuer cette action.";
|
||||
} else {
|
||||
this.responseMessage =
|
||||
"Une erreur est survenue. Veuillez réessayer.";
|
||||
}
|
||||
};
|
||||
|
||||
if (this.isUpdate) {
|
||||
updateAppointmentReport(
|
||||
this.appointmentId,
|
||||
reportData,
|
||||
onSuccess,
|
||||
onError
|
||||
);
|
||||
} else {
|
||||
createAppointmentReport(
|
||||
this.appointmentId,
|
||||
reportData,
|
||||
onSuccess,
|
||||
onError
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Centrer le formulaire */
|
||||
div {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 1rem;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Titre */
|
||||
h2 {
|
||||
text-align: center;
|
||||
color: #333;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
/* Formulaire */
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
/* Boutons */
|
||||
button {
|
||||
padding: 0.5rem 1rem;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
/* Input et Textarea */
|
||||
input[type="number"],
|
||||
textarea {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
textarea {
|
||||
height: 120px;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
/* Message de réponse */
|
||||
p {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
color: green;
|
||||
}
|
||||
|
||||
/* Boutons d'incrémentation/décrémentation */
|
||||
div[style*="display: flex"] button {
|
||||
background-color: #6c757d;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
div[style*="display: flex"] button:hover {
|
||||
background-color: #5a6268;
|
||||
}
|
||||
</style>
|
93
front/MyINPulse-front/src/components/AgendaComponent.vue
Normal file
93
front/MyINPulse-front/src/components/AgendaComponent.vue
Normal file
@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<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>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineProps } from "vue";
|
||||
|
||||
interface rendezVous {
|
||||
projectName: string;
|
||||
date: string;
|
||||
lieu: string;
|
||||
}
|
||||
|
||||
defineProps<{
|
||||
projectRDV: rendezVous[];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
color: #333;
|
||||
margin-bottom: 1.2rem;
|
||||
border-bottom: 2px solid #ddd;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
#agenda {
|
||||
padding: 20px;
|
||||
background-color: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
/* Table Styling */
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-family: Arial, sans-serif;
|
||||
text-align: left;
|
||||
margin-top: 20px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #f0f2f5;
|
||||
padding: 12px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* Table Body Rows */
|
||||
tbody tr {
|
||||
border-bottom: 1px solid #ddd;
|
||||
transition: background-color 0.2s ease; /* Smooth hover effect */
|
||||
}
|
||||
|
||||
tbody tr:hover {
|
||||
background-color: #f9f9f9; /* Highlight row on hover */
|
||||
}
|
||||
|
||||
/* 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 {
|
||||
text-align: center;
|
||||
width: 50px; /* Adjust width as needed */
|
||||
}
|
||||
</style>
|
77
front/MyINPulse-front/src/components/AllEntrep.vue
Normal file
77
front/MyINPulse-front/src/components/AllEntrep.vue
Normal file
@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<div class="entrepreneur-list">
|
||||
<button @click="fetchEntrepreneurs">Afficher les entrepreneurs</button>
|
||||
|
||||
<ul v-if="entrepreneurs.length">
|
||||
<li
|
||||
v-for="entrepreneur in entrepreneurs"
|
||||
:key="entrepreneur.idUser"
|
||||
>
|
||||
{{ entrepreneur.userName }} {{ entrepreneur.userSurname }} -
|
||||
{{ entrepreneur.primaryMail }}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p v-else-if="loading">Chargement...</p>
|
||||
<p v-else-if="errorMessage">{{ errorMessage }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import type { AxiosResponse, AxiosError } from "axios";
|
||||
import { getAllEntrepreneurs } from "@/services/Apis/Unauth"; // ajuste ce chemin selon ton projet
|
||||
|
||||
// Types
|
||||
interface Entrepreneur {
|
||||
idUser: number;
|
||||
userName: string;
|
||||
userSurname: string;
|
||||
primaryMail: string;
|
||||
}
|
||||
|
||||
// Refs
|
||||
const entrepreneurs = ref<Entrepreneur[]>([]);
|
||||
const loading = ref(false);
|
||||
const errorMessage = ref("");
|
||||
|
||||
// Méthode
|
||||
function fetchEntrepreneurs() {
|
||||
loading.value = true;
|
||||
errorMessage.value = "";
|
||||
getAllEntrepreneurs(
|
||||
(response: AxiosResponse) => {
|
||||
if (Array.isArray(response.data)) {
|
||||
entrepreneurs.value = response.data;
|
||||
} else {
|
||||
console.error("Format inattendu :", response.data);
|
||||
errorMessage.value = "Réponse inattendue du serveur.";
|
||||
}
|
||||
loading.value = false;
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
errorMessage.value = "Erreur lors du chargement des entrepreneurs.";
|
||||
console.error(error);
|
||||
loading.value = false;
|
||||
}
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
button {
|
||||
margin-bottom: 1rem;
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
</style>
|
90
front/MyINPulse-front/src/components/GetAppointments.vue
Normal file
90
front/MyINPulse-front/src/components/GetAppointments.vue
Normal file
@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div class="appointment-list">
|
||||
<h2>Liste des rendez-vous</h2>
|
||||
<h3>de {{ store.user.username }}</h3>
|
||||
<ul v-if="appointments.length">
|
||||
<li v-for="appt in appointments" :key="appt.idAppointment">
|
||||
<strong>Sujet :</strong> {{ appt.appointmentSubject }}<br />
|
||||
<strong>Date :</strong> {{ appt.appointmentDate }}<br />
|
||||
<strong>Heure :</strong> {{ appt.appointmentTime }}<br />
|
||||
<strong>Durée :</strong> {{ appt.appointmentDuration }}<br />
|
||||
<strong>Lieu :</strong> {{ appt.appointmentPlace }}
|
||||
<hr />
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p v-else>Aucun rendez-vous trouvé.</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from "vue";
|
||||
import { callApi } from "@/services/api";
|
||||
import Appointment from "@/ApiClasses/Appointment";
|
||||
import { store } from "@/main.ts";
|
||||
|
||||
// Define the interface for the API response
|
||||
interface AppointmentResponse {
|
||||
idAppointment: number;
|
||||
appointmentSubject: string;
|
||||
appointmentDate: string;
|
||||
appointmentTime: string;
|
||||
appointmentDuration: string;
|
||||
appointmentPlace: string;
|
||||
}
|
||||
|
||||
const appointments = ref<Appointment[]>([]);
|
||||
|
||||
function loadAppointments() {
|
||||
if (!store.user) {
|
||||
console.error(
|
||||
"L'utilisateur n'est pas connecté ou les données utilisateur ne sont pas disponibles."
|
||||
);
|
||||
return;
|
||||
}
|
||||
//console.log("username :", store.user.username);
|
||||
//console.log("token :", store.user.token);
|
||||
callApi(
|
||||
"/admin/appointments/upcoming",
|
||||
(response) => {
|
||||
appointments.value = response.data.map(
|
||||
(item: AppointmentResponse) => new Appointment(item)
|
||||
);
|
||||
},
|
||||
(error) => {
|
||||
console.error(
|
||||
"Erreur lors de la récupération des rendez-vous :",
|
||||
error
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadAppointments();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.appointment-list {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background: #fdfdfd;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
border-bottom: 2px solid #ddd;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 15px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
@ -1,26 +1,64 @@
|
||||
<template>
|
||||
<header>
|
||||
<img src="./icons/logo inpulse.png" alt="INPulse" />
|
||||
<header class="header">
|
||||
<a
|
||||
href="https://www.bordeaux-inp.fr/fr/lincubateur-bordeaux-inpulse"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<img
|
||||
src="./icons/logo inpulse.png"
|
||||
alt="INPulse Logo"
|
||||
class="logo"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<div class="header-actions">
|
||||
<button class="logout" @click="store.logout">Logout</button>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: "HeaderComponent",
|
||||
};
|
||||
<script setup lang="ts">
|
||||
import { store } from "@/main.ts";
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
header img {
|
||||
width: 100px;
|
||||
}
|
||||
@import "@/components/canvas/style-project.css";
|
||||
|
||||
header {
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px 20px;
|
||||
background-color: #fff;
|
||||
border-bottom: 2px solid #ddd;
|
||||
padding: 15px 30px;
|
||||
background-color: #f9f9f9;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.logout {
|
||||
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;
|
||||
}
|
||||
|
||||
.logout:hover {
|
||||
background-color: #007bad;
|
||||
}
|
||||
</style>
|
||||
|
229
front/MyINPulse-front/src/components/LoginComponent.vue
Normal file
229
front/MyINPulse-front/src/components/LoginComponent.vue
Normal file
@ -0,0 +1,229 @@
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { jwtDecode } from "jwt-decode"; // i hope this doesn't break the code later
|
||||
import { store } from "../main.ts";
|
||||
import { checkPending } from "@/services/Apis/Unauth";
|
||||
import Header from "@/components/HeaderComponent.vue";
|
||||
const router = useRouter();
|
||||
|
||||
type TokenPayload = {
|
||||
realm_access?: {
|
||||
roles?: string[];
|
||||
};
|
||||
};
|
||||
|
||||
//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 (roles.includes("MyINPulse-admin")) {
|
||||
router.push("/admin");
|
||||
return;
|
||||
}
|
||||
|
||||
if (roles.includes("MyINPulse-entrepreneur")) {
|
||||
router.push("/canvas");
|
||||
return;
|
||||
}
|
||||
|
||||
checkPending(
|
||||
(response) => {
|
||||
const isValidated = response.data === true;
|
||||
if (
|
||||
isValidated &&
|
||||
roles.includes("MyINPulse-entrepreneur")
|
||||
) {
|
||||
router.push("/canvas");
|
||||
//router.push("/JorCproject");
|
||||
} else {
|
||||
router.push("/JorCproject");
|
||||
//router.push("/finalize");
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
if (error.response?.status === 403) {
|
||||
router.push("/finalize");
|
||||
} else {
|
||||
console.error(
|
||||
"Unexpected error during checkPending",
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
console.error("Failed to decode token", err);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Header />
|
||||
<error-wrapper></error-wrapper>
|
||||
<div class="auth-container">
|
||||
<div class="auth-card">
|
||||
<h1>Bienvenue à MyINPulse</h1>
|
||||
|
||||
<div
|
||||
class="status"
|
||||
:class="store.authenticated ? 'success' : 'error'"
|
||||
>
|
||||
<p>
|
||||
{{
|
||||
store.authenticated
|
||||
? "✅ Authenticated"
|
||||
: "❌ Not Authenticated"
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button @click="store.login">Login</button>
|
||||
<button @click="store.logout">Logout</button>
|
||||
<!--<button @click="store.signup">Signup-admin</button>
|
||||
<button @click="store.signup">Signup-Entrepreneur</button>
|
||||
<button @click="store.refreshUserToken">Refresh Token</button>-->
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div v-if="store.authenticated" class="token-section">
|
||||
<p><strong>Access Token:</strong></p>
|
||||
<pre>{{ store.user.token }}</pre>
|
||||
|
||||
<p><strong>Refresh Token:</strong></p>
|
||||
<pre>{{ store.user.refreshToken }}</pre>
|
||||
</div>
|
||||
|
||||
<div class="api-calls">
|
||||
<h2>Test API Calls</h2>
|
||||
<button @click="callApi('random')">
|
||||
Call Entrepreneur API
|
||||
</button>
|
||||
<button @click="callApi('random2')">Call Admin API</button>
|
||||
<button @click="callApi('unauth/dev')">Call Unauth API</button>
|
||||
|
||||
<div class="custom-call">
|
||||
<input
|
||||
v-model="customRequest"
|
||||
placeholder="Custom endpoint"
|
||||
/>
|
||||
<button @click="callApi(customRequest)">Call</button>
|
||||
</div>
|
||||
</div>-->
|
||||
</div>
|
||||
</div>
|
||||
</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;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin-bottom: 1rem;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.status {
|
||||
text-align: center;
|
||||
margin-bottom: 1.5rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
.success {
|
||||
color: green;
|
||||
}
|
||||
.error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.actions {
|
||||
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;
|
||||
}
|
||||
.actions button:hover {
|
||||
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;
|
||||
}
|
||||
|
||||
.api-calls {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
.api-calls h2 {
|
||||
margin-bottom: 1rem;
|
||||
color: #444;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
.api-calls button {
|
||||
margin-right: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.custom-call {
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.custom-call input {
|
||||
flex: 1;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 6px;
|
||||
}
|
||||
/*
|
||||
.status {
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 8px;
|
||||
display: inline-block;
|
||||
background-color: #e0f7e9;
|
||||
color: #2e7d32;
|
||||
}
|
||||
*/
|
||||
.status.error {
|
||||
background-color: #ffe2e2;
|
||||
color: #c62828;
|
||||
}
|
||||
</style>
|
132
front/MyINPulse-front/src/components/PendingProjectComponent.vue
Normal file
132
front/MyINPulse-front/src/components/PendingProjectComponent.vue
Normal file
@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<div class="project">
|
||||
<div class="project-header">
|
||||
<div class="project-title">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineProps } from "vue";
|
||||
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 sendDecision = (isAccepted: boolean) => {
|
||||
const decision = new ProjectDecision({
|
||||
projectId: props.projectId,
|
||||
adminId: props.adminId,
|
||||
isAccepted,
|
||||
});
|
||||
|
||||
decidePendingProject(
|
||||
decision,
|
||||
() => {
|
||||
addNewMessage(
|
||||
`Projet ${props.projectName} ${isAccepted ? "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;
|
||||
}
|
||||
|
||||
.project:hover {
|
||||
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;
|
||||
}
|
||||
|
||||
.project-title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.project-title h2 {
|
||||
font-size: 1.25rem;
|
||||
color: #222;
|
||||
margin: 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.project-title p {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.project-buttons {
|
||||
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;
|
||||
}
|
||||
|
||||
#accept {
|
||||
background-color: #4caf50;
|
||||
}
|
||||
|
||||
#accept:hover {
|
||||
background-color: #3e8e41;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
#refus {
|
||||
background-color: #e74c3c;
|
||||
}
|
||||
|
||||
#refus:hover {
|
||||
background-color: #c0392b;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
</style>
|
228
front/MyINPulse-front/src/components/PendingRequestsManager.vue
Normal file
228
front/MyINPulse-front/src/components/PendingRequestsManager.vue
Normal 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>
|
@ -1,21 +1,306 @@
|
||||
<template>
|
||||
<div class="project">
|
||||
<div class="project-header">
|
||||
<h2>{{ projectName }}</h2>
|
||||
<h2 @click="goToLink">{{ projectName }}</h2>
|
||||
<div class="header-actions">
|
||||
<div ref="dropdownRef" class="dropdown-wrapper">
|
||||
<button class="contact-button" @click.stop="toggleDropdown">
|
||||
Contact
|
||||
</button>
|
||||
<div
|
||||
v-if="entrepreneurEmails.length > 0"
|
||||
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>
|
||||
|
||||
<!-- Toute cette partie est cliquable -->
|
||||
<div class="project-body" @click="goToLink">
|
||||
<ul>
|
||||
<li v-for="(name, index) in listName" :key="index">
|
||||
{{ name }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import type { PropType } from "vue";
|
||||
<script setup lang="ts">
|
||||
import { defineProps, ref, onMounted, onBeforeUnmount } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { getProjectEntrepreneurs } from "@/services/Apis/Shared.ts";
|
||||
import UserEntrepreneur from "@/ApiClasses/UserEntrepreneur.ts";
|
||||
|
||||
export default {
|
||||
name: "ProjectComponent",
|
||||
props: {
|
||||
projectName: {
|
||||
type: Object as PropType<string>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
const IS_MOCK_MODE = false;
|
||||
const dropdownRef = ref<HTMLElement | null>(null);
|
||||
|
||||
const props = defineProps<{
|
||||
projectName: string;
|
||||
listName: string[];
|
||||
projectLink: string;
|
||||
projectId: number;
|
||||
}>();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const isDropdownOpen = ref(false);
|
||||
const entrepreneurEmails = ref<string[]>([]);
|
||||
const entrepreneurs = ref<UserEntrepreneur[]>([]);
|
||||
|
||||
const goToLink = () => {
|
||||
if (props.projectLink) {
|
||||
router.push(props.projectLink);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleDropdown = () => {
|
||||
isDropdownOpen.value = !isDropdownOpen.value;
|
||||
};
|
||||
|
||||
const fetchMockEntrepreneurs = () => {
|
||||
const mockData = [
|
||||
{
|
||||
userName: "Doe",
|
||||
userSurname: "John",
|
||||
primaryMail: "john.doe@example.com",
|
||||
},
|
||||
{
|
||||
userName: "Smith",
|
||||
userSurname: "Anna",
|
||||
primaryMail: "anna.smith@example.com",
|
||||
},
|
||||
{
|
||||
userName: "Mock",
|
||||
userSurname: "User",
|
||||
primaryMail: undefined,
|
||||
},
|
||||
];
|
||||
|
||||
entrepreneurs.value = mockData.map((item) => new UserEntrepreneur(item));
|
||||
entrepreneurEmails.value = entrepreneurs.value
|
||||
.map((e) => e.primaryMail)
|
||||
.filter((mail): mail is string => !!mail);
|
||||
|
||||
console.log("Mock entrepreneurs chargés :", entrepreneurs.value);
|
||||
};
|
||||
|
||||
const fetchEntrepreneurs = (projectId: number, useMock = false) => {
|
||||
if (useMock) {
|
||||
fetchMockEntrepreneurs();
|
||||
} else {
|
||||
getProjectEntrepreneurs(
|
||||
projectId,
|
||||
(response) => {
|
||||
const rawData = response.data as Partial<UserEntrepreneur>[];
|
||||
entrepreneurs.value = rawData.map(
|
||||
(item) => new UserEntrepreneur(item)
|
||||
);
|
||||
entrepreneurEmails.value = entrepreneurs.value
|
||||
.map((e) => e.primaryMail)
|
||||
.filter((mail): mail is string => !!mail);
|
||||
},
|
||||
(error) => {
|
||||
console.error(
|
||||
"Erreur lors de la récupération des entrepreneurs :",
|
||||
error
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
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 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));
|
||||
};
|
||||
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (
|
||||
isDropdownOpen.value &&
|
||||
dropdownRef.value &&
|
||||
!dropdownRef.value.contains(event.target as Node)
|
||||
) {
|
||||
isDropdownOpen.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
fetchEntrepreneurs(props.projectId, IS_MOCK_MODE);
|
||||
document.addEventListener("click", handleClickOutside);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener("click", handleClickOutside);
|
||||
});
|
||||
</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;
|
||||
}
|
||||
|
||||
.project:hover {
|
||||
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;
|
||||
}
|
||||
|
||||
.project-header h2 {
|
||||
font-size: 1.25rem;
|
||||
color: #222;
|
||||
margin: 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.project-buttons {
|
||||
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;
|
||||
}
|
||||
|
||||
.contact-btn:hover {
|
||||
background-color: #0056b3;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.project-body {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.project-body ul {
|
||||
list-style-type: disc;
|
||||
padding-left: 1.25rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.project-body ul li {
|
||||
font-size: 0.95rem;
|
||||
color: #555;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
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;
|
||||
}
|
||||
|
||||
.return-button:hover,
|
||||
.contact-button:hover {
|
||||
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;
|
||||
}
|
||||
|
||||
.contact-dropdown button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
text-align: left;
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.contact-dropdown button:hover {
|
||||
background-color: #009cde;
|
||||
}
|
||||
|
||||
.contact-dropdown.dropdown-visible {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
|
687
front/MyINPulse-front/src/components/canvas/CanvasItem.vue
Executable file
687
front/MyINPulse-front/src/components/canvas/CanvasItem.vue
Executable file
@ -0,0 +1,687 @@
|
||||
<template>
|
||||
<div :class="['cell', { expanded }]" @click="handleClick">
|
||||
<h3 class="fs-5 fw-medium">{{ titleText }}</h3>
|
||||
|
||||
<div class="tooltip-explain">{{ description }}</div>
|
||||
|
||||
<template v-if="expanded">
|
||||
<div class="explain">
|
||||
<p>{{ description }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="description-wrapper custom-flow">
|
||||
<div
|
||||
v-for="(desc, index) in currentDescriptions"
|
||||
:key="desc.idSectionCell || index"
|
||||
:class="[
|
||||
'section-bloc',
|
||||
index % 2 === 0 ? 'from-left' : 'from-right',
|
||||
]"
|
||||
>
|
||||
<!-- ADMIN -------------------------------------------------------------------------------------------->
|
||||
<template v-if="IS_ADMIN">
|
||||
<div class="description">
|
||||
<p class="m-0">{{ desc.contentSectionCell }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- ENTREP ------------------------------------------------------------------------------------------->
|
||||
<template v-else>
|
||||
<!-- Mode affichage -->
|
||||
<template v-if="!isEditing[index]">
|
||||
<template v-if="expanded">
|
||||
<button
|
||||
class="delete-button"
|
||||
title="Supprimer"
|
||||
@click.stop="
|
||||
deleteSectionCell(desc.idSectionCell, index)
|
||||
"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<div class="description">
|
||||
<p class="m-0">{{ desc.contentSectionCell }}</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>
|
||||
<div class="edit-row">
|
||||
<textarea
|
||||
v-model="
|
||||
editedDescriptions[index].contentSectionCell
|
||||
"
|
||||
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>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<template v-if="expanded">
|
||||
<div class="canvas-exit-hint">
|
||||
Cliquez n'importe où pour quitter le canvas (terminez
|
||||
d'abord vos modifications)
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, defineProps, onMounted } from "vue";
|
||||
import { getSectionCellsByDate } from "@/services/Apis/Shared.ts";
|
||||
import { addSectionCell } from "@/services/Apis/Entrepreneurs.ts";
|
||||
import SectionCell from "@/ApiClasses/SectionCell";
|
||||
import { removeSectionCell } from "@/services/Apis/Entrepreneurs.ts";
|
||||
import type { AxiosResponse, AxiosError } from "axios";
|
||||
|
||||
const props = defineProps<{
|
||||
projectId: number;
|
||||
title: number;
|
||||
titleText: string;
|
||||
description: string;
|
||||
isAdmin: boolean;
|
||||
}>();
|
||||
|
||||
const IS_MOCK_MODE = true;
|
||||
const IS_ADMIN = props.isAdmin;
|
||||
|
||||
const expanded = ref(false);
|
||||
const currentDescriptions = ref<SectionCell[]>([]);
|
||||
const editedDescriptions = ref<SectionCell[]>([]);
|
||||
const isEditing = ref<boolean[]>([]);
|
||||
|
||||
const startEditing = (index: number) => {
|
||||
isEditing.value[index] = true;
|
||||
};
|
||||
|
||||
const cancelEdit = (index: number) => {
|
||||
editedDescriptions.value[index].contentSectionCell =
|
||||
currentDescriptions.value[index].contentSectionCell;
|
||||
isEditing.value[index] = false;
|
||||
};
|
||||
|
||||
const saveEdit = (index: number) => {
|
||||
currentDescriptions.value[index].contentSectionCell =
|
||||
editedDescriptions.value[index].contentSectionCell;
|
||||
isEditing.value[index] = false;
|
||||
|
||||
if (!IS_MOCK_MODE) {
|
||||
addSectionCell(
|
||||
currentDescriptions.value[index],
|
||||
(response) => {
|
||||
console.log(
|
||||
"Modification enregistrée avec succès :",
|
||||
response.data
|
||||
);
|
||||
},
|
||||
(error) => {
|
||||
console.error("Erreur lors de l'enregistrement :", error);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const deleteSectionCell = (id: number | undefined, index: number): void => {
|
||||
if (id === -1) {
|
||||
window.confirm("Êtes-vous sûr de vouloir supprimer cet élément ?");
|
||||
console.error("Delete ignored");
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmed = window.confirm(
|
||||
"Êtes-vous sûr de vouloir supprimer cet élément ?"
|
||||
);
|
||||
|
||||
if (!confirmed) return;
|
||||
|
||||
if (id === undefined) {
|
||||
console.error("ID de la cellule non défini");
|
||||
return;
|
||||
}
|
||||
|
||||
removeSectionCell(
|
||||
id,
|
||||
() => {
|
||||
currentDescriptions.value.splice(index, 1);
|
||||
editedDescriptions.value.splice(index, 1);
|
||||
isEditing.value.splice(index, 1);
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
console.error("Erreur lors de la suppression :", error);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
if (expanded.value) {
|
||||
const editingInProgress = isEditing.value.some((edit) => edit);
|
||||
if (!editingInProgress) {
|
||||
expanded.value = false;
|
||||
}
|
||||
} else {
|
||||
expanded.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
const handleFetchSuccess = (sectionCells: SectionCell[]) => {
|
||||
currentDescriptions.value = sectionCells;
|
||||
editedDescriptions.value = sectionCells.map(
|
||||
(cell) =>
|
||||
new SectionCell({
|
||||
idSectionCell: cell.idSectionCell,
|
||||
sectionId: cell.sectionId,
|
||||
contentSectionCell: cell.contentSectionCell,
|
||||
modificationDate: cell.modificationDate,
|
||||
})
|
||||
);
|
||||
isEditing.value = Array(sectionCells.length).fill(false);
|
||||
};
|
||||
|
||||
const handleFetchError = (error: unknown) => {
|
||||
console.error("Erreur lors de la récupération des données :", error);
|
||||
|
||||
const errorCell = new SectionCell({
|
||||
idSectionCell: -1,
|
||||
sectionId: -1,
|
||||
contentSectionCell: "Échec du chargement des données.",
|
||||
modificationDate: new Date().toISOString(),
|
||||
});
|
||||
|
||||
currentDescriptions.value = [errorCell];
|
||||
editedDescriptions.value = [errorCell];
|
||||
isEditing.value = [false];
|
||||
};
|
||||
|
||||
const fetchData = async (
|
||||
projectId: number,
|
||||
title: number,
|
||||
date: string,
|
||||
useMock = false
|
||||
) => {
|
||||
try {
|
||||
if (useMock) {
|
||||
const responseData = await mockFetch(projectId, title, date);
|
||||
handleFetchSuccess(responseData);
|
||||
} else {
|
||||
if (projectId == -1) {
|
||||
const errorCell = new SectionCell({
|
||||
idSectionCell: -1,
|
||||
sectionId: -1,
|
||||
contentSectionCell: "Échec du chargement des données.",
|
||||
modificationDate: new Date().toISOString(),
|
||||
});
|
||||
|
||||
currentDescriptions.value = [errorCell];
|
||||
editedDescriptions.value = [errorCell];
|
||||
isEditing.value = [false];
|
||||
|
||||
console.error(
|
||||
"No sections to show because no project was found."
|
||||
);
|
||||
return;
|
||||
}
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
getSectionCellsByDate(
|
||||
projectId,
|
||||
title,
|
||||
date,
|
||||
(response: AxiosResponse) => {
|
||||
const data = response.data;
|
||||
|
||||
if (Array.isArray(data) && data.length > 0) {
|
||||
const sectionCells = data.map(
|
||||
(cellData) =>
|
||||
new SectionCell({
|
||||
idSectionCell: cellData.idSectionCell,
|
||||
sectionId: cellData.sectionId,
|
||||
contentSectionCell:
|
||||
cellData.contentSectionCell,
|
||||
modificationDate:
|
||||
cellData.modificationDate,
|
||||
})
|
||||
);
|
||||
handleFetchSuccess(sectionCells);
|
||||
} else {
|
||||
console.warn(
|
||||
"Aucune donnée reçue ou format inattendu :",
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
resolve();
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
handleFetchError(error);
|
||||
reject(error);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
handleFetchError(error);
|
||||
}
|
||||
};
|
||||
|
||||
const mockFetch = async (
|
||||
projectId: number,
|
||||
title: number,
|
||||
date: string
|
||||
): Promise<SectionCell[]> => {
|
||||
console.log(
|
||||
`Mock fetch pour projectId: ${projectId}, title: ${title}, date: ${date}`
|
||||
);
|
||||
|
||||
const leanCanvasData: Record<number, string[]> = {
|
||||
1: [
|
||||
"Les clients ont du mal à trouver des produits écoresponsables abordables.",
|
||||
"Le processus d'achat en ligne est trop complexe.",
|
||||
"Manque de transparence sur l’origine des produits.",
|
||||
"Peu d’alternatives locales et durables sur le marché.",
|
||||
],
|
||||
2: [
|
||||
"Jeunes urbains engagés dans la cause écologique.",
|
||||
"Familles à revenu moyen voulant consommer responsable.",
|
||||
"Entreprises soucieuses de leur empreinte carbone.",
|
||||
],
|
||||
3: [
|
||||
"Une plateforme centralisée avec des produits écologiques certifiés.",
|
||||
"Un service client humain et réactif.",
|
||||
"Livraison éco-responsable avec suivi.",
|
||||
],
|
||||
4: [
|
||||
"Application intuitive avec suggestions personnalisées.",
|
||||
"Emballages recyclables et réutilisables.",
|
||||
],
|
||||
5: [
|
||||
"Algorithme exclusif de recommandations durables.",
|
||||
"Forte communauté engagée sur les réseaux.",
|
||||
],
|
||||
6: [
|
||||
"Canaux digitaux : réseaux sociaux, SEO.",
|
||||
"Partenariats avec influenceurs écoresponsables.",
|
||||
"Boutique physique en pop-up stores.",
|
||||
],
|
||||
7: [
|
||||
"Taux de rétention client mensuel.",
|
||||
"Taux de satisfaction utilisateur (NPS).",
|
||||
],
|
||||
8: [
|
||||
"Coût du développement logiciel initial.",
|
||||
"Campagnes publicitaires et communication.",
|
||||
"Frais logistiques (emballages, transport).",
|
||||
],
|
||||
9: [
|
||||
"Ventes directes sur la plateforme.",
|
||||
"Abonnement mensuel premium pour livraison gratuite.",
|
||||
"Revenus via partenariats de marque.",
|
||||
],
|
||||
};
|
||||
|
||||
const section = leanCanvasData[title] || ["Aucune donnée disponible."];
|
||||
|
||||
const result = section.map(
|
||||
(txt, index) =>
|
||||
new SectionCell({
|
||||
idSectionCell: index + 1,
|
||||
sectionId: title,
|
||||
contentSectionCell: txt,
|
||||
modificationDate: date,
|
||||
})
|
||||
);
|
||||
|
||||
return new Promise<SectionCell[]>((resolve) => {
|
||||
setTimeout(() => resolve(result), 500);
|
||||
});
|
||||
};
|
||||
|
||||
function getCurrentFormattedDate(): string {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, "0"); // +1 car janvier = 0
|
||||
const day = String(now.getDate()).padStart(2, "0");
|
||||
const hours = String(now.getHours()).padStart(2, "0");
|
||||
const minutes = String(now.getMinutes()).padStart(2, "0");
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchData(
|
||||
props.projectId,
|
||||
props.title,
|
||||
getCurrentFormattedDate(),
|
||||
IS_MOCK_MODE
|
||||
);
|
||||
});
|
||||
</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);
|
||||
}
|
||||
|
||||
.expanded-content {
|
||||
justify-content: flex-start !important;
|
||||
}
|
||||
|
||||
.tooltip-explain {
|
||||
position: absolute;
|
||||
bottom: 101%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
padding: 6px 12px;
|
||||
font-size: 13px;
|
||||
border-radius: 6px;
|
||||
white-space: nowrap;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.3s;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.cell:not(.expanded):hover .tooltip-explain {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.cell:not(.expanded):hover {
|
||||
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;
|
||||
}
|
||||
|
||||
.p {
|
||||
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);
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 5px;
|
||||
color: #333;
|
||||
word-break: break-word;
|
||||
width: 90%;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.description + .p {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.edit-input {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 100px;
|
||||
padding: 10px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
margin-top: 10px;
|
||||
box-sizing: border-box;
|
||||
margin-left: 2%;
|
||||
max-height: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.button-container {
|
||||
display: block;
|
||||
margin-top: 20px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding-right: 1%;
|
||||
}
|
||||
|
||||
.section-bloc {
|
||||
background-color: #f3f3f3;
|
||||
border-radius: 8px;
|
||||
padding: 10px 12px;
|
||||
font-family: "Arial", sans-serif;
|
||||
color: #333;
|
||||
word-break: break-word;
|
||||
flex-shrink: 0;
|
||||
cursor: default;
|
||||
|
||||
max-width: 100%;
|
||||
width: fit-content;
|
||||
overflow-wrap: break-word;
|
||||
box-sizing: border-box;
|
||||
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.description p {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.cancel-button {
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.edit-button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
.save-button:hover {
|
||||
background-color: #218838;
|
||||
}
|
||||
|
||||
.cancel-button:hover {
|
||||
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;
|
||||
}
|
||||
|
||||
.description-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
|
||||
padding: 10px;
|
||||
overflow: hidden;
|
||||
max-height: 100%;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
max-height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.custom-flow {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
padding: 10px;
|
||||
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
|
||||
grid-auto-rows: min-content;
|
||||
grid-auto-flow: dense;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.float-up {
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
.float-left {
|
||||
transform: translateX(-10px);
|
||||
}
|
||||
.float-right {
|
||||
transform: translateX(10px);
|
||||
}
|
||||
.wiggle {
|
||||
transform: rotate(1deg);
|
||||
}
|
||||
.tilt {
|
||||
transform: rotate(-1deg);
|
||||
}
|
||||
|
||||
.from-left {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.from-right {
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.section-bloc.from-left {
|
||||
margin-right: auto;
|
||||
margin-left: 20%;
|
||||
}
|
||||
|
||||
.section-bloc.from-right {
|
||||
margin-left: auto;
|
||||
margin-right: 20%;
|
||||
}
|
||||
|
||||
.explain {
|
||||
font-size: 16px;
|
||||
color: #444;
|
||||
background-color: #f9f9f9;
|
||||
padding: 7px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 20px;
|
||||
margin-top: 20px;
|
||||
line-height: 1.6;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
}
|
||||
|
||||
.delete-button {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 10px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #a00;
|
||||
font-size: 1.2rem;
|
||||
cursor: pointer;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.section-bloc {
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
239
front/MyINPulse-front/src/components/canvas/HeaderCanvas.vue
Normal file
239
front/MyINPulse-front/src/components/canvas/HeaderCanvas.vue
Normal file
@ -0,0 +1,239 @@
|
||||
<template>
|
||||
<header class="header">
|
||||
<a
|
||||
href="https://www.bordeaux-inp.fr/fr/lincubateur-bordeaux-inpulse"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<img
|
||||
src="../icons/logo inpulse.png"
|
||||
alt="INPulse Logo"
|
||||
class="logo"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<div class="header-actions">
|
||||
<button class="return-button" @click="store.logout">Logout</button>
|
||||
|
||||
<div v-if="props.isAdmin">
|
||||
<div ref="dropdownRef" class="dropdown-wrapper">
|
||||
<button class="contact-button" @click.stop="toggleDropdown">
|
||||
Contact
|
||||
</button>
|
||||
<div
|
||||
v-if="entrepreneurEmails.length > 0"
|
||||
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>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onBeforeUnmount } from "vue";
|
||||
import { store } from "@/main.ts";
|
||||
import { getProjectEntrepreneurs } from "@/services/Apis/Shared.ts";
|
||||
import UserEntrepreneur from "@/ApiClasses/UserEntrepreneur.ts";
|
||||
|
||||
const dropdownRef = ref<HTMLElement | undefined>(undefined);
|
||||
|
||||
const props = defineProps<{
|
||||
projectId: number;
|
||||
isAdmin: boolean;
|
||||
}>();
|
||||
|
||||
const isDropdownOpen = ref(false);
|
||||
const entrepreneurEmails = ref<string[]>([]);
|
||||
const entrepreneurs = ref<UserEntrepreneur[]>([]);
|
||||
const IS_MOCK_MODE = false;
|
||||
|
||||
const toggleDropdown = () => {
|
||||
isDropdownOpen.value = !isDropdownOpen.value;
|
||||
};
|
||||
|
||||
const fetchMockEntrepreneurs = () => {
|
||||
const mockData = [
|
||||
{
|
||||
userName: "Doe",
|
||||
userSurname: "John",
|
||||
primaryMail: "john.doe@example.com",
|
||||
},
|
||||
{
|
||||
userName: "Smith",
|
||||
userSurname: "Anna",
|
||||
primaryMail: "anna.smith@example.com",
|
||||
},
|
||||
{
|
||||
userName: "Mock",
|
||||
userSurname: "User",
|
||||
primaryMail: undefined,
|
||||
},
|
||||
];
|
||||
|
||||
entrepreneurs.value = mockData.map((item) => new UserEntrepreneur(item));
|
||||
entrepreneurEmails.value = entrepreneurs.value
|
||||
.map((e) => e.primaryMail)
|
||||
.filter((mail): mail is string => !!mail);
|
||||
|
||||
console.log("Mock entrepreneurs chargés :", entrepreneurs.value);
|
||||
};
|
||||
|
||||
const fetchEntrepreneurs = (projectId: number, useMock = false) => {
|
||||
if (useMock) {
|
||||
fetchMockEntrepreneurs();
|
||||
} else {
|
||||
if (projectId == -1) {
|
||||
console.error("No contact to show because no project was found.");
|
||||
return;
|
||||
}
|
||||
getProjectEntrepreneurs(
|
||||
projectId,
|
||||
(response) => {
|
||||
const rawData = response.data as Partial<UserEntrepreneur>[];
|
||||
entrepreneurs.value = rawData.map(
|
||||
(item) => new UserEntrepreneur(item)
|
||||
);
|
||||
entrepreneurEmails.value = entrepreneurs.value
|
||||
.map((e) => e.primaryMail)
|
||||
.filter((mail): mail is string => !!mail);
|
||||
},
|
||||
(error) => {
|
||||
console.error(
|
||||
"Erreur lors de la récupération des entrepreneurs :",
|
||||
error
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
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 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));
|
||||
};
|
||||
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (
|
||||
isDropdownOpen.value &&
|
||||
dropdownRef.value &&
|
||||
!dropdownRef.value.contains(event.target as Node)
|
||||
) {
|
||||
isDropdownOpen.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (props.isAdmin) {
|
||||
fetchEntrepreneurs(props.projectId, IS_MOCK_MODE);
|
||||
}
|
||||
document.addEventListener("click", handleClickOutside);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener("click", handleClickOutside);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@import "@/components/canvas/style-project.css";
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px 20px;
|
||||
background-color: #f9f9f9;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
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;
|
||||
}
|
||||
|
||||
.return-button:hover,
|
||||
.contact-button:hover {
|
||||
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;
|
||||
}
|
||||
|
||||
.contact-dropdown button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
text-align: left;
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.contact-dropdown button:hover {
|
||||
background-color: #009cde;
|
||||
}
|
||||
|
||||
.contact-dropdown.dropdown-visible {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
174
front/MyINPulse-front/src/components/canvas/LeanCanvas.vue
Normal file
174
front/MyINPulse-front/src/components/canvas/LeanCanvas.vue
Normal file
@ -0,0 +1,174 @@
|
||||
<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="props.projectId"
|
||||
:class="['canvas-item', item.class, 'card', 'shadow', 'p-3']"
|
||||
:is-admin="props.isAdmin"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import CanvasItem from "@/components/canvas/CanvasItem.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
projectId: number;
|
||||
isAdmin: boolean;
|
||||
}>();
|
||||
|
||||
const items = ref([
|
||||
{
|
||||
title: 1,
|
||||
title_text: "1. Problème",
|
||||
description: "3 problèmes essentiels à résoudre pour le client",
|
||||
class: "Probleme",
|
||||
},
|
||||
{
|
||||
title: 2,
|
||||
title_text: "2. Segments",
|
||||
description: "Les segments de clientèle visés",
|
||||
class: "Segments",
|
||||
},
|
||||
{
|
||||
title: 3,
|
||||
title_text: "3. Valeur",
|
||||
description: "La proposition de valeur",
|
||||
class: "Valeur",
|
||||
},
|
||||
{
|
||||
title: 4,
|
||||
title_text: "4. Solution",
|
||||
description: "Les solutions proposées",
|
||||
class: "Solution",
|
||||
},
|
||||
{
|
||||
title: 5,
|
||||
title_text: "5. Avantage",
|
||||
description: "Les avantages concurrentiels",
|
||||
class: "Avantage",
|
||||
},
|
||||
{
|
||||
title: 6,
|
||||
title_text: "6. Canaux",
|
||||
description: "Les canaux de distribution",
|
||||
class: "Canaux",
|
||||
},
|
||||
{
|
||||
title: 7,
|
||||
title_text: "7. Indicateurs",
|
||||
description: "Les indicateurs clés de performance",
|
||||
class: "Indicateurs",
|
||||
},
|
||||
{
|
||||
title: 8,
|
||||
title_text: "8. Coûts",
|
||||
description: "Les coûts associés",
|
||||
class: "Couts",
|
||||
},
|
||||
{
|
||||
title: 9,
|
||||
title_text: "9. Revenus",
|
||||
description: "Les sources de revenus",
|
||||
class: "Revenus",
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@import "@/components/canvas/style-project.css";
|
||||
|
||||
.canvas {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(10, minmax(0, 1fr));
|
||||
grid-auto-rows: min-content;
|
||||
gap: 12px;
|
||||
padding: 30px;
|
||||
position: relative;
|
||||
height: auto;
|
||||
max-height: none;
|
||||
box-sizing: border-box;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.canvas {
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.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 {
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.Probleme {
|
||||
background-color: #ffdddd;
|
||||
}
|
||||
.Segments {
|
||||
background-color: #ddffdd;
|
||||
}
|
||||
.Valeur {
|
||||
background-color: #ddddff;
|
||||
}
|
||||
.Solution {
|
||||
background-color: #fff0b3;
|
||||
}
|
||||
.Avantage {
|
||||
background-color: #d1c4e9;
|
||||
}
|
||||
.Canaux {
|
||||
background-color: #b2ebf2;
|
||||
}
|
||||
.Indicateurs {
|
||||
background-color: #ffe082;
|
||||
}
|
||||
.Couts {
|
||||
background-color: #ffcdd2;
|
||||
}
|
||||
.Revenus {
|
||||
background-color: #c8e6c9;
|
||||
}
|
||||
</style>
|
156
front/MyINPulse-front/src/components/canvas/style-project.css
Normal file
156
front/MyINPulse-front/src/components/canvas/style-project.css
Normal file
@ -0,0 +1,156 @@
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.cell {
|
||||
flex: 1;
|
||||
border: 1px solid #ddd;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
.produit {
|
||||
background-color: #f9e4e4;
|
||||
}
|
||||
|
||||
.marche {
|
||||
background-color: #e4f1f9;
|
||||
}
|
||||
|
||||
.valeur {
|
||||
background-color: #f9f4e4;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 5px 0 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
h1 img {
|
||||
height: 80px;
|
||||
margin: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#ade {
|
||||
max-width: 1200px;
|
||||
margin: 20px auto;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
background-color: #e8f5e9;
|
||||
border: 2px solid #4caf50;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
#ade h3 {
|
||||
color: #2e7d32;
|
||||
}
|
||||
|
||||
#ade p {
|
||||
margin: 10px 0;
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
}
|
||||
header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px 20px;
|
||||
background-color: #fff;
|
||||
border-bottom: 2px solid #ddd;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
header .contact-menu {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.contact-button,
|
||||
.return {
|
||||
padding: 10px 15px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
background-color: #2196f3;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.contact-button:hover,
|
||||
.return:hover {
|
||||
background-color: #1976d2;
|
||||
}
|
||||
|
||||
/* Dropdown styling */
|
||||
.contact-dropdown {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50px;
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
padding: 15px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.contact-dropdown button {
|
||||
padding: 8px 12px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
background-color: #4caf50;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.contact-dropdown button:hover {
|
||||
background-color: #388e3c;
|
||||
}
|
||||
|
||||
.return {
|
||||
background-color: #f44336;
|
||||
}
|
||||
|
||||
.return:hover {
|
||||
background-color: #d32f2f;
|
||||
}
|
||||
|
||||
.header-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: white;
|
||||
}
|
0
front/MyINPulse-front/src/components/contact.ts
Normal file
0
front/MyINPulse-front/src/components/contact.ts
Normal file
Binary file not shown.
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 39 KiB |
@ -28,4 +28,46 @@ keycloakService.CallInit(() => {
|
||||
}
|
||||
});
|
||||
|
||||
// this shit made by me so i can run the canva vue app
|
||||
//createApp(App).use(router).mount('#app');
|
||||
|
||||
// TODO: fix the comment
|
||||
/*
|
||||
function tokenInterceptor () {
|
||||
axios.interceptors.request.use(config => {
|
||||
const keycloak = useKeycloak()
|
||||
if (keycloak.authenticated) {
|
||||
// Note that this is a simple example.
|
||||
// you should be careful not to leak tokens to third parties.
|
||||
// in this example the token is added to all usage of axios.
|
||||
config.headers.Authorization = `Bearer ${keycloak.token}`
|
||||
}
|
||||
return config
|
||||
}, error => {
|
||||
console.error("tokenInterceptor: Rejected")
|
||||
return Promise.reject(error)
|
||||
})
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
app.use(VueKeyCloak,{
|
||||
onReady: (keycloak) => {
|
||||
console.log("Ready !")
|
||||
tokenInterceptor()
|
||||
},
|
||||
init: {
|
||||
onLoad: 'login-required',
|
||||
checkLoginIframe: false,
|
||||
|
||||
},
|
||||
|
||||
config: {
|
||||
realm: 'test',
|
||||
url: 'http://localhost:7080',
|
||||
clientId: 'myinpulse'
|
||||
}
|
||||
} );
|
||||
*/
|
||||
|
||||
export { store };
|
||||
|
16
front/MyINPulse-front/src/plugins/authStore.ts
Normal file
16
front/MyINPulse-front/src/plugins/authStore.ts
Normal file
@ -0,0 +1,16 @@
|
||||
// file: src/plugins/authStore.js
|
||||
|
||||
import { useAuthStore } from "@/stores/authStore.ts";
|
||||
import keycloakService from "@/services/keycloak";
|
||||
import type { Pinia } from "pinia";
|
||||
import type { App } from "vue";
|
||||
// Setup auth store as a plugin so it can be accessed globally in our FE
|
||||
const authStorePlugin = {
|
||||
install(app: App, option: { pinia: Pinia }) {
|
||||
const store = useAuthStore(option.pinia);
|
||||
app.config.globalProperties.$store = store;
|
||||
keycloakService.CallInitStore(store);
|
||||
},
|
||||
};
|
||||
|
||||
export default authStorePlugin;
|
0
front/MyINPulse-front/src/router/index.ts
Normal file
0
front/MyINPulse-front/src/router/index.ts
Normal file
@ -11,6 +11,47 @@ const router = createRouter({
|
||||
// which is lazy-loaded when the route is visited.
|
||||
component: () => import("../views/testComponent.vue"),
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
name: "login",
|
||||
component: () => import("../components/LoginComponent.vue"),
|
||||
},
|
||||
{
|
||||
path: "/admin",
|
||||
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"),
|
||||
},
|
||||
|
||||
{
|
||||
path: "/signup",
|
||||
name: "signup",
|
||||
component: () => import("../views/EntrepSignUp.vue"),
|
||||
},
|
||||
|
||||
{
|
||||
path: "/JorCproject",
|
||||
name: "JorCproject",
|
||||
component: () => import("../views/JoinOrCreatProjectForEntrep.vue"),
|
||||
},
|
||||
|
||||
{
|
||||
path: "/finalize",
|
||||
name: "finalize",
|
||||
component: () => import("../views/FinalizeAccount.vue"),
|
||||
},
|
||||
|
||||
{
|
||||
path: "/pending-approval",
|
||||
name: "PendingApproval",
|
||||
component: () => import("@/views/PendingApproval.vue"),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
|
340
front/MyINPulse-front/src/services/Apis/Admin.ts
Normal file
340
front/MyINPulse-front/src/services/Apis/Admin.ts
Normal file
@ -0,0 +1,340 @@
|
||||
import { type AxiosError, type AxiosResponse } from "axios";
|
||||
import Report from "@/ApiClasses/Repport";
|
||||
import ProjectDecision from "@/ApiClasses/ProjectDecision";
|
||||
//import UserAdmin from "@/ApiClasses/UserAdmin";
|
||||
import {
|
||||
axiosInstance,
|
||||
defaultApiErrorHandler,
|
||||
defaultApiSuccessHandler,
|
||||
} from "@/services/api";
|
||||
|
||||
// Admin API
|
||||
function getPendingAccounts(
|
||||
onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.get("/admin/pending-accounts")
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function validateUserAccount(
|
||||
userId: number,
|
||||
onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.post(`/admin/accounts/validate/${userId}`)
|
||||
.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,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.get("/admin/projects")
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
type ProjectCreatePayload = {
|
||||
projectName: string;
|
||||
logo?: string;
|
||||
};
|
||||
|
||||
function addProjectManually(
|
||||
payload: ProjectCreatePayload,
|
||||
onSuccess?: (response: AxiosResponse) => void,
|
||||
onError?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.post("/admin/projects", payload)
|
||||
.then((response) => onSuccess?.(response))
|
||||
.catch((error: AxiosError) => onError?.(error));
|
||||
}
|
||||
|
||||
function getPendingProjects(
|
||||
onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.get("/admin/projects/pending")
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function decidePendingProject(
|
||||
decision: ProjectDecision,
|
||||
onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.post(`/admin/projects/pending/decision`, decision.toObject())
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createAppointmentReport(
|
||||
appointmentId: number,
|
||||
reportContent: Report, // Replace 'any' with a proper type for report content if available
|
||||
onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.post(`/admin/appointments/report/${appointmentId}`, reportContent)
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateAppointmentReport(
|
||||
appointmentId: number,
|
||||
reportContent: Report, // Replace 'any' with a proper type for report content if available
|
||||
onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.put(`/admin/appointments/report/${appointmentId}`, reportContent)
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getUpcomingAppointments(
|
||||
onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.get("/admin/appointments/upcoming")
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function removeProject(
|
||||
projectId: number,
|
||||
onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.delete(`/admin/projects/${projectId}`)
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function grantAdminRights(
|
||||
userId: number,
|
||||
onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.post(`/admin/make-admin/${userId}`)
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createAdmin(
|
||||
onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.post("/admin/create-account")
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
axiosInstance,
|
||||
//requestJoinProject, // Not yet implemented [cite: 4]
|
||||
getPendingAccounts,
|
||||
validateUserAccount,
|
||||
getPendingProjectJoinRequests, // Not yet implemented [cite: 3]
|
||||
decideProjectJoinRequest, // Not yet implemented [cite: 3]
|
||||
getAdminProjects,
|
||||
addProjectManually,
|
||||
getPendingProjects,
|
||||
decidePendingProject,
|
||||
createAppointmentReport,
|
||||
updateAppointmentReport,
|
||||
getUpcomingAppointments,
|
||||
removeProject,
|
||||
grantAdminRights,
|
||||
createAdmin,
|
||||
};
|
180
front/MyINPulse-front/src/services/Apis/Entrepreneurs.ts
Normal file
180
front/MyINPulse-front/src/services/Apis/Entrepreneurs.ts
Normal file
@ -0,0 +1,180 @@
|
||||
import { type AxiosError, type AxiosResponse } from "axios";
|
||||
import Project from "@/ApiClasses/Project";
|
||||
import SectionCell from "@/ApiClasses/SectionCell";
|
||||
import {
|
||||
axiosInstance,
|
||||
defaultApiErrorHandler,
|
||||
defaultApiSuccessHandler,
|
||||
} from "@/services/api";
|
||||
|
||||
// Entrepreneurs API
|
||||
function getEntrepreneurProjectId(
|
||||
onSuccessHandler?: (response: AxiosResponse<number[]>) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.get("/entrepreneur/projects")
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function requestProjectCreation(
|
||||
projectDetails: Project,
|
||||
onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.post("/entrepreneur/projects/request", projectDetails.toObject())
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addSectionCell(
|
||||
sectionCellDetails: SectionCell,
|
||||
onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.post("/entrepreneur/sectionCells", sectionCellDetails.toObject())
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function modifySectionCell(
|
||||
sectionCellId: number,
|
||||
sectionCellDetails: SectionCell,
|
||||
onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.put(`/entrepreneur/sectionCells/${sectionCellId}`, sectionCellDetails)
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function removeSectionCell(
|
||||
sectionCellId: number,
|
||||
onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.delete(`/entrepreneur/sectionCells/${sectionCellId}`)
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Checks if the entrepreneur has a pending project request
|
||||
function checkPendingProjectRequest(
|
||||
onSuccessHandler?: (response: AxiosResponse<boolean>) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.get("/entrepreneur/projects/has-pending-request")
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Checks if the entrepreneur has an active project
|
||||
function checkIfProjectIsActive(
|
||||
onSuccessHandler?: (response: AxiosResponse<boolean>) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.get("/entrepreneur/projects/project-is-active")
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
getEntrepreneurProjectId,
|
||||
requestProjectCreation,
|
||||
addSectionCell,
|
||||
modifySectionCell,
|
||||
removeSectionCell,
|
||||
checkPendingProjectRequest,
|
||||
checkIfProjectIsActive,
|
||||
};
|
157
front/MyINPulse-front/src/services/Apis/Shared.ts
Normal file
157
front/MyINPulse-front/src/services/Apis/Shared.ts
Normal file
@ -0,0 +1,157 @@
|
||||
import { type AxiosError, type AxiosResponse } from "axios";
|
||||
import Appointment from "@/ApiClasses/Appointment";
|
||||
import {
|
||||
axiosInstance,
|
||||
defaultApiErrorHandler,
|
||||
defaultApiSuccessHandler,
|
||||
} from "@/services/api";
|
||||
|
||||
// Shared API
|
||||
function getSectionCellsByDate(
|
||||
projectId: number,
|
||||
sectionId: number,
|
||||
date: string, // Use string for date in 'YYYY-MM-DD HH:mm' format
|
||||
onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.get(`/shared/projects/sectionCells/${projectId}/${sectionId}/${date}`)
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getProjectEntrepreneurs(
|
||||
projectId: number,
|
||||
onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.get(`/shared/projects/entrepreneurs/${projectId}`)
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getProjectAdmin(
|
||||
projectId: number,
|
||||
onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.get(`/shared/projects/admin/${projectId}`)
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getProjectAppointments(
|
||||
projectId: number,
|
||||
onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.get(`/shared/projects/appointments/${projectId}`)
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getAppointmentReport(
|
||||
appointmentId: number,
|
||||
onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.get(`/shared/appointments/report/${appointmentId}`)
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function requestAppointment(
|
||||
appointmentDetails: Appointment, // Replace 'any' with a proper type for appointment details if available
|
||||
onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.post("/shared/appointments/request", appointmentDetails)
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
getSectionCellsByDate,
|
||||
getProjectEntrepreneurs,
|
||||
getProjectAdmin,
|
||||
getProjectAppointments,
|
||||
getAppointmentReport,
|
||||
requestAppointment,
|
||||
};
|
103
front/MyINPulse-front/src/services/Apis/Unauth.ts
Normal file
103
front/MyINPulse-front/src/services/Apis/Unauth.ts
Normal file
@ -0,0 +1,103 @@
|
||||
import { type AxiosError, type AxiosResponse } from "axios";
|
||||
|
||||
import {
|
||||
axiosInstance,
|
||||
defaultApiErrorHandler,
|
||||
defaultApiSuccessHandler,
|
||||
} from "@/services/api";
|
||||
|
||||
// Unauth API
|
||||
function finalizeAccount(
|
||||
onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.post("/unauth/finalize")
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// function requestJoinProject( // Not yet implemented [cite: 4]
|
||||
// projectId: number,
|
||||
// onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
// onErrorHandler?: (error: AxiosError) => void
|
||||
// ): void {
|
||||
// axiosInstance
|
||||
// .post(`/unauth/request-join/${projectId}`)
|
||||
// .then((response) => {
|
||||
// if (onSuccessHandler) {
|
||||
// onSuccessHandler(response);
|
||||
// } else {
|
||||
// defaultApiSuccessHandler(response);
|
||||
// }
|
||||
// })
|
||||
// .catch((error: AxiosError) => {
|
||||
// if (onErrorHandler) {
|
||||
// onErrorHandler(error);
|
||||
// } else {
|
||||
// defaultApiErrorHandler(error);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
function getAllEntrepreneurs(
|
||||
onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.get("/unauth/getAllEntrepreneurs")
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function checkPending(
|
||||
onSuccessHandler?: (response: AxiosResponse<boolean>) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.get("/unauth/check-if-not-pending")
|
||||
.then((response) => {
|
||||
if (onSuccessHandler) {
|
||||
onSuccessHandler(response);
|
||||
} else {
|
||||
defaultApiSuccessHandler(response);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (onErrorHandler) {
|
||||
onErrorHandler(error);
|
||||
} else {
|
||||
defaultApiErrorHandler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
export {
|
||||
finalizeAccount,
|
||||
getAllEntrepreneurs,
|
||||
checkPending,
|
||||
// requestJoinProject, // Not yet implemented [cite: 4]
|
||||
};
|
@ -1,6 +1,7 @@
|
||||
import axios, { type AxiosError, type AxiosResponse } from "axios";
|
||||
import { store } from "@/main.ts";
|
||||
import { addNewMessage, color } from "@/services/popupDisplayer.ts";
|
||||
import router from "@/router/router";
|
||||
|
||||
const axiosInstance = axios.create({
|
||||
baseURL: import.meta.env.VITE_BACKEND_URL,
|
||||
@ -9,6 +10,19 @@ const axiosInstance = axios.create({
|
||||
},
|
||||
});
|
||||
|
||||
axiosInstance.interceptors.request.use(
|
||||
(config) => {
|
||||
const token = store.user?.token; // Récupérez le token depuis le store
|
||||
if (token) {
|
||||
config.headers["Authorization"] = `Bearer ${token}`; // Ajoutez le token dans l'en-tête
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
axiosInstance.interceptors.response.use(
|
||||
(response) => response, // Directly return successful responses.
|
||||
async (error) => {
|
||||
@ -19,19 +33,17 @@ axiosInstance.interceptors.response.use(
|
||||
!originalRequest._retry &&
|
||||
store.authenticated
|
||||
) {
|
||||
originalRequest._retry = true; // Mark the request as retried to avoid infinite loops.
|
||||
originalRequest._retry = true;
|
||||
try {
|
||||
await store.refreshUserToken();
|
||||
// Update the authorization header with the new access token.
|
||||
axiosInstance.defaults.headers.common["Authorization"] =
|
||||
`Bearer ${store.user.token}`;
|
||||
return axiosInstance(originalRequest); // Retry the original request with the new access token.
|
||||
return axiosInstance(originalRequest);
|
||||
} catch (refreshError) {
|
||||
// Handle refresh token errors by clearing stored tokens and redirecting to the login page.
|
||||
console.error("Token refresh failed:", refreshError);
|
||||
localStorage.removeItem("accessToken");
|
||||
localStorage.removeItem("refreshToken");
|
||||
window.location.href = "/login";
|
||||
router.push("/login");
|
||||
return Promise.reject(refreshError);
|
||||
}
|
||||
}
|
||||
@ -41,7 +53,9 @@ axiosInstance.interceptors.response.use(
|
||||
|
||||
// TODO: spawn a error modal
|
||||
function defaultApiErrorHandler(err: AxiosError) {
|
||||
addNewMessage(err.message, color.Red);
|
||||
const errorMessage =
|
||||
(err.response?.data as { message?: string })?.message ?? err.message;
|
||||
addNewMessage(errorMessage, color.Red);
|
||||
}
|
||||
|
||||
function defaultApiSuccessHandler(response: AxiosResponse) {
|
||||
@ -65,4 +79,36 @@ function callApi(
|
||||
);
|
||||
}
|
||||
|
||||
export { callApi };
|
||||
function postApi(
|
||||
endpoint: string,
|
||||
data: unknown, //to fix eslint issue, go back here if errors occurs later
|
||||
onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.post(endpoint, data)
|
||||
.then(onSuccessHandler ?? defaultApiSuccessHandler)
|
||||
.catch(onErrorHandler ?? defaultApiErrorHandler);
|
||||
}
|
||||
|
||||
function deleteApi(
|
||||
endpoint: string,
|
||||
onSuccessHandler?: (response: AxiosResponse) => void,
|
||||
onErrorHandler?: (error: AxiosError) => void
|
||||
): void {
|
||||
axiosInstance
|
||||
.delete(endpoint)
|
||||
.then(onSuccessHandler ?? defaultApiSuccessHandler)
|
||||
.catch(onErrorHandler ?? defaultApiErrorHandler);
|
||||
}
|
||||
|
||||
//export { axiosInstance, callApi, postApi, deleteApi };
|
||||
|
||||
export {
|
||||
axiosInstance,
|
||||
defaultApiErrorHandler,
|
||||
defaultApiSuccessHandler,
|
||||
callApi,
|
||||
postApi,
|
||||
deleteApi,
|
||||
};
|
||||
|
24
front/MyINPulse-front/src/services/tools.ts
Normal file
24
front/MyINPulse-front/src/services/tools.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { jwtDecode } from "jwt-decode";
|
||||
import { store } from "@/main";
|
||||
|
||||
type TokenPayload = {
|
||||
realm_access?: {
|
||||
roles?: string[];
|
||||
};
|
||||
};
|
||||
|
||||
function isAdmin(): boolean {
|
||||
if (store.authenticated && store.user.token) {
|
||||
const decoded = jwtDecode<TokenPayload>(store.user.token);
|
||||
const roles = decoded.realm_access?.roles || [];
|
||||
if (roles.includes("MyINPulse-admin")) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export { isAdmin };
|
@ -54,7 +54,7 @@ const useAuthStore = defineStore("storeAuth", {
|
||||
async logout() {
|
||||
try {
|
||||
await keycloakService.CallLogout(
|
||||
import.meta.env.VITE_APP_URL + "/test"
|
||||
import.meta.env.VITE_APP_URL + "/" //redirect to login page instead of test...
|
||||
);
|
||||
await this.clearUserData();
|
||||
} catch (error) {
|
||||
|
226
front/MyINPulse-front/src/views/AdminMain.vue
Normal file
226
front/MyINPulse-front/src/views/AdminMain.vue
Normal file
@ -0,0 +1,226 @@
|
||||
<template>
|
||||
<Header />
|
||||
<error-wrapper />
|
||||
|
||||
<div id="container">
|
||||
<div id="main">
|
||||
<h3>Projet en cours</h3>
|
||||
<ProjectComp
|
||||
v-for="(project, index) in projects"
|
||||
:key="index"
|
||||
:project-name="project.name"
|
||||
:list-name="project.members"
|
||||
:project-link="project.link"
|
||||
:project-id="0"
|
||||
/>
|
||||
|
||||
<!-- <AllEntrep /> -->
|
||||
|
||||
<h3>Projet en attente</h3>
|
||||
<PendingProjectComponent
|
||||
v-for="(project, index) in pendingProjects"
|
||||
:key="index"
|
||||
:project-name="project.projectName"
|
||||
:creation-date="project.creationDate"
|
||||
/>
|
||||
|
||||
<AddProjectForm />
|
||||
<PendingRequestsManager />
|
||||
</div>
|
||||
|
||||
<Agenda :project-r-d-v="rendezVous" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<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/AgendaComponent.vue";
|
||||
import ProjectComp from "../components/ProjectComponent.vue";
|
||||
import PendingProjectComponent from "@/components/PendingProjectComponent.vue";
|
||||
import AddProjectForm from "@/components/AddProjectForm.vue";
|
||||
import PendingRequestsManager from "@/components/PendingRequestsManager.vue";
|
||||
import { createAdmin } from "@/services/Apis/Admin";
|
||||
import Project from "@/ApiClasses/Project";
|
||||
import UserEntrepreneur from "@/ApiClasses/UserEntrepreneur";
|
||||
//import UserAdmin from "@/ApiClasses/UserAdmin";
|
||||
//import AllEntrep from "../components/AllEntrep.vue";
|
||||
const projects = ref<
|
||||
{
|
||||
name: string;
|
||||
link: string;
|
||||
members: string[];
|
||||
}[]
|
||||
>([]);
|
||||
|
||||
const fallbackProjects = [
|
||||
{
|
||||
name: "Projet Alpha",
|
||||
link: "/canvas",
|
||||
members: ["Alice", "Bob", "Charlie"],
|
||||
},
|
||||
{
|
||||
name: "Projet Beta",
|
||||
link: "./canvas",
|
||||
members: ["David", "Eve", "Frank"],
|
||||
},
|
||||
];
|
||||
|
||||
const createFirstAdmin = () => {
|
||||
createAdmin(
|
||||
(response) => {
|
||||
console.log("Admin créé avec succès :", response.data);
|
||||
},
|
||||
(error) => {
|
||||
console.error(
|
||||
"Erreur lors de la création de l'admin :",
|
||||
error.message
|
||||
);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
onMounted(createFirstAdmin);
|
||||
|
||||
const fetchProjects = () => {
|
||||
getAdminProjects(
|
||||
(response: AxiosResponse) => {
|
||||
const projectList = response.data.map(
|
||||
(p: Project) => new Project(p)
|
||||
);
|
||||
|
||||
const projectPromises = projectList.map(
|
||||
(project: Project) =>
|
||||
new Promise<void>((resolve) => {
|
||||
getProjectEntrepreneurs(
|
||||
project.idProject!,
|
||||
(memberResponse: AxiosResponse) => {
|
||||
const members = memberResponse.data.map(
|
||||
(m: UserEntrepreneur) =>
|
||||
new UserEntrepreneur(m).userName ||
|
||||
"Unknown"
|
||||
);
|
||||
projects.value.push({
|
||||
name:
|
||||
project.projectName ||
|
||||
"Unnamed Project",
|
||||
link: `/project/${project.idProject}`,
|
||||
members,
|
||||
});
|
||||
resolve();
|
||||
},
|
||||
() => {
|
||||
projects.value.push({
|
||||
name:
|
||||
project.projectName ||
|
||||
"Unnamed Project",
|
||||
link: `/project/${project.idProject}`,
|
||||
members: [],
|
||||
});
|
||||
resolve();
|
||||
}
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
Promise.all(projectPromises).catch(() => {
|
||||
projects.value = fallbackProjects;
|
||||
});
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
console.error("Error fetching admin projects:", error);
|
||||
projects.value = fallbackProjects;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
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(fetchProjects);
|
||||
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" },
|
||||
{ projectName: "Projet Beta", date: "2025-04-15", lieu: "Td10" },
|
||||
]);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 3fr 1fr;
|
||||
gap: 2rem;
|
||||
padding: 2rem;
|
||||
background-color: #f4f6f9;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#main {
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
#main > * + * {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
</style>
|
181
front/MyINPulse-front/src/views/CanvasView.vue
Normal file
181
front/MyINPulse-front/src/views/CanvasView.vue
Normal file
@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<div>
|
||||
<header>
|
||||
<HeaderCanvas :project-id="projectId" :is-admin="isAdmin_" />
|
||||
</header>
|
||||
</div>
|
||||
<div>
|
||||
<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 :project-id="projectId" :is-admin="isAdmin_" />
|
||||
<div class="info-box">
|
||||
<p v-if="admin">
|
||||
Responsable :
|
||||
<strong>{{ admin.userName }} {{ admin.userSurname }}</strong
|
||||
><br />
|
||||
Contact :
|
||||
<a :href="`mailto:${admin.primaryMail}`">{{
|
||||
admin.primaryMail
|
||||
}}</a>
|
||||
|
|
||||
<a :href="`tel:${admin.phoneNumber}`">{{
|
||||
admin.phoneNumber
|
||||
}}</a>
|
||||
</p>
|
||||
<p v-else>Chargement des informations du responsable...</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 } from "vue";
|
||||
import { isAdmin } from "@/services/tools.ts";
|
||||
import { getProjectAdmin } from "@/services/Apis/Shared.ts";
|
||||
import UserAdmin from "@/ApiClasses/UserAdmin.ts";
|
||||
import type { AxiosResponse, AxiosError } from "axios";
|
||||
import { getEntrepreneurProjectId } from "@/services/Apis/Entrepreneurs";
|
||||
|
||||
const IS_MOCK_MODE = false;
|
||||
const isAdmin_ = isAdmin();
|
||||
const admin = ref<UserAdmin | null>(null);
|
||||
const projectId = ref<number>(-1);
|
||||
|
||||
const mockAdminData = new UserAdmin({
|
||||
idUser: 1,
|
||||
userSurname: "ALAMI",
|
||||
userName: "Adnane",
|
||||
primaryMail: "mock.admin@example.com",
|
||||
secondaryMail: "admin.backup@example.com",
|
||||
phoneNumber: "0600000000",
|
||||
});
|
||||
|
||||
function getProjectId(): Promise<number> {
|
||||
return new Promise((resolve) => {
|
||||
getEntrepreneurProjectId(
|
||||
(response) => {
|
||||
const projectIds = response.data;
|
||||
if (!Array.isArray(projectIds) || projectIds.length === 0) {
|
||||
console.warn("Aucun projet trouvé pour cet entrepreneur.");
|
||||
resolve(-1);
|
||||
return;
|
||||
}
|
||||
resolve(projectIds[0]);
|
||||
},
|
||||
(error) => {
|
||||
console.error("Erreur API :", error);
|
||||
resolve(-1);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchProjectId() {
|
||||
try {
|
||||
projectId.value = await getProjectId();
|
||||
console.log("ProjectId :", projectId.value);
|
||||
} catch (error) {
|
||||
console.error("Erreur lors de la récupération du projet :", error);
|
||||
}
|
||||
}
|
||||
|
||||
const fetchAdminData = (projectId: number, useMock = IS_MOCK_MODE) => {
|
||||
if (useMock) {
|
||||
console.log("Utilisation des données mockées pour l'administrateur");
|
||||
admin.value = mockAdminData;
|
||||
return;
|
||||
}
|
||||
|
||||
if (projectId === -1) {
|
||||
admin.value = new UserAdmin({
|
||||
idUser: 0,
|
||||
userSurname: "Erreur",
|
||||
userName: "Chargement",
|
||||
primaryMail: "N/A",
|
||||
secondaryMail: "N/A",
|
||||
phoneNumber: "N/A",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
getProjectAdmin(
|
||||
projectId,
|
||||
(response: AxiosResponse) => {
|
||||
admin.value = new UserAdmin(response.data);
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
console.error(
|
||||
"Erreur lors de la récupération des données de l'administrateur :",
|
||||
error
|
||||
);
|
||||
|
||||
admin.value = new UserAdmin({
|
||||
idUser: 0,
|
||||
userSurname: "Erreur",
|
||||
userName: "Chargement",
|
||||
primaryMail: "N/A",
|
||||
secondaryMail: "N/A",
|
||||
phoneNumber: "N/A",
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
fetchProjectId();
|
||||
fetchAdminData(projectId.value);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-title {
|
||||
text-align: center;
|
||||
font-size: 2.5rem;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.canvas-help-text {
|
||||
text-align: center;
|
||||
font-size: 0.7rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.info-box {
|
||||
background-color: #f9f9f9;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
font-family: Arial, sans-serif;
|
||||
width: 30%;
|
||||
max-width: 600px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
.info-box p {
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.info-box a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.info-box a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.canvas-help-text {
|
||||
margin-top: 20px;
|
||||
margin-bottom: -10px;
|
||||
}
|
||||
|
||||
div:last-child {
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
</style>
|
185
front/MyINPulse-front/src/views/EntrepSignUp.vue
Normal file
185
front/MyINPulse-front/src/views/EntrepSignUp.vue
Normal file
@ -0,0 +1,185 @@
|
||||
<template>
|
||||
<form class="add-project-form" @submit.prevent="submitForm">
|
||||
<h2>Ajouter un projet</h2>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="name">Nom du projet</label>
|
||||
<input id="name" v-model="form.name" type="text" required />
|
||||
</div>
|
||||
|
||||
<h3>Entrepreneur</h3>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="founderName">Nom</label>
|
||||
<input
|
||||
id="founderName"
|
||||
v-model="form.founder.userName"
|
||||
type="text"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="founderSurname">Prénom</label>
|
||||
<input
|
||||
id="founderSurname"
|
||||
v-model="form.founder.userSurname"
|
||||
type="text"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="founderPrimaryMail">Email Principal</label>
|
||||
<input
|
||||
id="founderPrimaryMail"
|
||||
v-model="form.founder.primaryMail"
|
||||
type="email"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="founderSecondaryMail">Email Secondaire</label>
|
||||
<input
|
||||
id="founderSecondaryMail"
|
||||
v-model="form.founder.secondaryMail"
|
||||
type="email"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="founderPhoneNumber">Numéro de téléphone</label>
|
||||
<input
|
||||
id="founderPhoneNumber"
|
||||
v-model="form.founder.phoneNumber"
|
||||
type="tel"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="founderSchool">École</label>
|
||||
<input
|
||||
id="founderSchool"
|
||||
v-model="form.founder.school"
|
||||
type="text"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="founderCourse">Département</label>
|
||||
<input
|
||||
id="founderCourse"
|
||||
v-model="form.founder.course"
|
||||
type="text"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="founderSneeStatus">Statut étudiant entrepreneur</label>
|
||||
<input
|
||||
id="founderSneeStatus"
|
||||
v-model="form.founder.sneeStatus"
|
||||
type="checkbox"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button type="submit">Soumettre</button>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { postApi } from "@/services/api";
|
||||
|
||||
const form = ref({
|
||||
name: "",
|
||||
founder: {
|
||||
userSurname: "",
|
||||
userName: "",
|
||||
primaryMail: "",
|
||||
secondaryMail: "",
|
||||
phoneNumber: "",
|
||||
school: "",
|
||||
course: "",
|
||||
sneeStatus: false,
|
||||
},
|
||||
});
|
||||
|
||||
function submitForm() {
|
||||
postApi("/entrepreneur/projects/request", form.value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap");
|
||||
|
||||
.add-project-form {
|
||||
font-family: "Inter", sans-serif;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Le reste reste inchangé */
|
||||
h2 {
|
||||
margin-bottom: 20px;
|
||||
font-size: 24px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 20px;
|
||||
font-size: 20px;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: 500;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
input {
|
||||
padding: 8px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #ccc;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
width: auto;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #4caf50;
|
||||
color: white;
|
||||
padding: 10px 15px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 1em;
|
||||
width: 100%;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
|
||||
button:active {
|
||||
background-color: #388e3c;
|
||||
}
|
||||
|
||||
input[type="text"]:focus,
|
||||
input[type="email"]:focus,
|
||||
input[type="tel"]:focus {
|
||||
border-color: #4caf50;
|
||||
outline: none;
|
||||
}
|
||||
</style>
|
81
front/MyINPulse-front/src/views/FinalizeAccount.vue
Normal file
81
front/MyINPulse-front/src/views/FinalizeAccount.vue
Normal file
@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<Header />
|
||||
<div class="finalize-page">
|
||||
<div class="loader-container">
|
||||
<button class="return-button" @click="store.logout">Logout</button>
|
||||
<div class="spinner"></div>
|
||||
<p>Finalisation du compte en cours...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from "vue";
|
||||
//import { useRouter } from "vue-router";
|
||||
import { finalizeAccount } from "@/services/Apis/Unauth";
|
||||
import Header from "@/components/HeaderComponent.vue";
|
||||
import { store } from "@/main.ts";
|
||||
//const router = useRouter();
|
||||
|
||||
onMounted(() => {
|
||||
finalizeAccount(
|
||||
() => {
|
||||
console.log("finalize sended");
|
||||
},
|
||||
(error) => {
|
||||
console.error("Erreur lors de la finalisation :", error);
|
||||
}
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.finalize-page {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 80vh;
|
||||
background-color: #f9fbfd;
|
||||
}
|
||||
|
||||
.loader-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
color: #333;
|
||||
font-size: 1.1rem;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border: 5px solid #cfd8dc;
|
||||
border-top: 5px solid #3498db;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
185
front/MyINPulse-front/src/views/JoinOrCreatProjectForEntrep.vue
Normal file
185
front/MyINPulse-front/src/views/JoinOrCreatProjectForEntrep.vue
Normal file
@ -0,0 +1,185 @@
|
||||
<template>
|
||||
<Header />
|
||||
<header class="header">
|
||||
<img
|
||||
src="@/components/icons/logo inpulse.png"
|
||||
alt="INPulse Logo"
|
||||
class="logo"
|
||||
/>
|
||||
<button class="return-button" @click="store.logout">Logout</button>
|
||||
</header>
|
||||
|
||||
<div class="choix-projet">
|
||||
<h1>Bienvenue sur MyINPulse</h1>
|
||||
<p>
|
||||
Souhaitez-vous créer un nouveau projet ou joindre un projet existant
|
||||
?
|
||||
</p>
|
||||
|
||||
<div class="button-group">
|
||||
<button @click="choisir('creer')">Créer un projet</button>
|
||||
<button @click="choisir('joindre')">Joindre un projet</button>
|
||||
</div>
|
||||
|
||||
<div v-if="choix === 'creer'" class="form-creer">
|
||||
<h2>Créer un projet</h2>
|
||||
<label for="nomProjet">Nom du projet :</label>
|
||||
<input
|
||||
id="nomProjet"
|
||||
v-model="nomProjet"
|
||||
type="text"
|
||||
placeholder="Nom du projet"
|
||||
/>
|
||||
<button @click="validerCreation">Valider</button>
|
||||
</div>
|
||||
|
||||
<div v-if="choix === 'joindre'" class="message-indispo">
|
||||
<h2>Joindre un projet</h2>
|
||||
<p>Cette fonctionnalité n'est pas encore disponible.</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import Project from "@/ApiClasses/Project";
|
||||
import Header from "../components/HeaderComponent.vue";
|
||||
import { store } from "@/main.ts";
|
||||
import {
|
||||
requestProjectCreation,
|
||||
checkIfProjectIsActive,
|
||||
checkPendingProjectRequest,
|
||||
} from "@/services/Apis/Entrepreneurs";
|
||||
|
||||
const router = useRouter();
|
||||
const choix = ref<string | null>(null);
|
||||
const nomProjet = ref("");
|
||||
|
||||
const choisir = (option: "creer" | "joindre") => {
|
||||
choix.value = option;
|
||||
};
|
||||
|
||||
const validerCreation = () => {
|
||||
if (!nomProjet.value.trim()) {
|
||||
alert("Veuillez entrer un nom de projet.");
|
||||
return;
|
||||
}
|
||||
|
||||
const today = new Date();
|
||||
const yyyy = today.getFullYear();
|
||||
const mm = String(today.getMonth() + 1).padStart(2, "0");
|
||||
const dd = String(today.getDate()).padStart(2, "0");
|
||||
const formattedDate = `${yyyy}-${mm}-${dd}`;
|
||||
|
||||
const nouveauProjet = new Project({
|
||||
projectName: nomProjet.value.trim(),
|
||||
creationDate: formattedDate,
|
||||
status: "PENDING",
|
||||
});
|
||||
|
||||
requestProjectCreation(
|
||||
nouveauProjet,
|
||||
(response) => {
|
||||
console.log("Projet créé :", response.data);
|
||||
alert(`Projet "${nomProjet.value}" créé avec succès !`);
|
||||
},
|
||||
(error) => {
|
||||
console.error("Erreur lors de la création du projet :", error);
|
||||
alert("Une erreur est survenue lors de la création du projet.");
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
checkIfProjectIsActive(
|
||||
(response) => {
|
||||
if (response.data === true) {
|
||||
router.push("/canvas");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
checkPendingProjectRequest(
|
||||
(response) => {
|
||||
if (response.data === true) {
|
||||
router.push("/pending-approval");
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
console.warn("No active or pending project:", error);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@import "@/components/canvas/style-project.css";
|
||||
|
||||
.choix-projet {
|
||||
max-width: 500px;
|
||||
margin: auto;
|
||||
padding: 2rem;
|
||||
background-color: #fefefe;
|
||||
border-radius: 10px;
|
||||
font-family: "Inter", sans-serif;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
margin: 20px 0;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
font-size: 1em;
|
||||
background-color: #4caf50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #43a047;
|
||||
}
|
||||
|
||||
input {
|
||||
padding: 10px;
|
||||
margin-top: 10px;
|
||||
width: 80%;
|
||||
font-size: 1em;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
</style>
|
41
front/MyINPulse-front/src/views/PendingApproval.vue
Normal file
41
front/MyINPulse-front/src/views/PendingApproval.vue
Normal file
@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<Header />
|
||||
<div class="pending-container">
|
||||
<h1>Projet en attente de validation</h1>
|
||||
<p>
|
||||
Votre demande de création de projet a bien été reçue.<br />
|
||||
Un administrateur doit valider votre projet avant que vous puissiez
|
||||
continuer.
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Header from "@/components/HeaderComponent.vue";
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pending-container {
|
||||
max-width: 600px;
|
||||
margin: 100px auto;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
background-color: #fffdf8;
|
||||
border: 1px solid #ececec;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.1);
|
||||
font-family: "Inter", sans-serif;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #f57c00;
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1.1rem;
|
||||
color: #333;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
</style>
|
@ -18,7 +18,7 @@ import ErrorModal from "@/components/errorModal.vue";
|
||||
.error-wrapper {
|
||||
position: absolute;
|
||||
left: 70%;
|
||||
//background-color: blue;
|
||||
/*background-color: blue;*/
|
||||
height: 100%;
|
||||
width: 30%;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import { callApi } from "@/services/api.ts";
|
||||
import { ref } from "vue";
|
||||
|
||||
const CustomRequest = ref("");
|
||||
const USERID = ref("");
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -34,30 +35,7 @@ const CustomRequest = ref("");
|
||||
<td>Current refresh token</td>
|
||||
<td>{{ store.user.refreshToken }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Entrepreneur API call</td>
|
||||
<td>
|
||||
<button @click="callApi('random')">call</button>
|
||||
</td>
|
||||
<td>res</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Admin API call</td>
|
||||
<td>
|
||||
<button @click="callApi('random2')">call</button>
|
||||
</td>
|
||||
<td>res</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Unauth API call</td>
|
||||
<td>
|
||||
<button @click="callApi('unauth/dev')">call</button>
|
||||
</td>
|
||||
<td>res</td>
|
||||
<td id="3"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<input v-model="CustomRequest" placeholder="edit me" />
|
||||
@ -66,6 +44,83 @@ const CustomRequest = ref("");
|
||||
<button @click="callApi(CustomRequest)">call</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Create an account</td>
|
||||
<td>
|
||||
<button @click="callApi('unauth/create_account')">
|
||||
call
|
||||
</button>
|
||||
</td>
|
||||
<td>res</td>
|
||||
<td id="4"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Get Pending Accounts</td>
|
||||
<td>
|
||||
<button @click="callApi('/admin/pending-accounts')">
|
||||
call
|
||||
</button>
|
||||
</td>
|
||||
<td>res</td>
|
||||
<td id="6"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>admin/validate_user_account/{id}</td>
|
||||
<td>
|
||||
<button
|
||||
@click="
|
||||
callApi('admin/validate_user_account/' + USERID)
|
||||
"
|
||||
>
|
||||
call
|
||||
</button>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<input v-model="USERID" placeholder="user ID" />
|
||||
</td>
|
||||
|
||||
<td id="5"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>admin/setadmin/{uid}</td>
|
||||
<td>
|
||||
<button @click="callApi('admin/setadmin/' + USERID)">
|
||||
call
|
||||
</button>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<input v-model="USERID" placeholder="user ID" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Unauth API call</td>
|
||||
<td>
|
||||
<button @click="callApi('unauth/dev')">call</button>
|
||||
</td>
|
||||
<td>res</td>
|
||||
<td id="8"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Unauth API call</td>
|
||||
<td>
|
||||
<button @click="callApi('unauth/dev')">call</button>
|
||||
</td>
|
||||
<td>res</td>
|
||||
<td id="9"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Unauth API call</td>
|
||||
<td>
|
||||
<button @click="callApi('unauth/dev')">call</button>
|
||||
</td>
|
||||
<td>res</td>
|
||||
<td id="10"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
|
2638
keycloak/realm.json
Normal file
2638
keycloak/realm.json
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user