14 Commits

Author SHA1 Message Date
36e4967394 Feat: first implementation of postgres db for backend
All checks were successful
CI / build (push) Successful in 13s
2025-02-11 00:08:53 +01:00
c32eea8a40 Merge pull request 'Mise en place d'un linter, de formatteur et d'actions afin de vérifier que le code du frontend compile bien' (#3) from linter into main
All checks were successful
CI / build (push) Successful in 12s
Reviewed-on: #3
Reviewed-by: Theo <tlelez@enseirb-matmeca.fr>
Vu que vous ne voulez pas faire avancer le projet, je vais abuser de mes droits d'administrateur et le faire.
2025-02-10 22:44:34 +01:00
30344a60b7 fix: removed import
All checks were successful
CI / build (push) Successful in 13s
2025-02-09 16:12:43 +01:00
2465545b6b fix: removed temp modal
Some checks failed
CI / build (push) Failing after 10s
2025-02-09 16:11:54 +01:00
83cbeb7a2e feat: single workflow that check prettier, linter and build
Some checks failed
CI / build (push) Failing after 11s
2025-02-09 16:07:40 +01:00
645a10477d feat: now respect codestyle
All checks were successful
CI / build (push) Successful in 4s
2025-02-09 15:59:30 +01:00
a859871265 fix: prettier now fail instead of doing nothing
Some checks failed
CI / build (push) Failing after 5s
2025-02-09 15:57:40 +01:00
0eab9a8063 feat: implemented prettier
All checks were successful
CI / build (push) Successful in 4s
2025-02-09 15:54:19 +01:00
1dff7573ff feat: now compliant with eslint
All checks were successful
CI / build (push) Successful in 10s
2025-02-09 15:28:09 +01:00
8af40bfe50 fix: linter action: installed required modules
Some checks failed
CI / build (push) Failing after 11s
2025-02-09 14:04:12 +01:00
afa4d34ec8 fix: linter action
Some checks failed
CI / build (push) Failing after 7s
2025-02-09 14:02:34 +01:00
c5fc5b600e fix: linter action
Some checks failed
CI / build (push) Failing after 12s
2025-02-09 14:00:49 +01:00
a4939737fe feat: trying to setup linter
Some checks failed
CI / build (push) Failing after 35s
2025-02-09 13:57:17 +01:00
e7ebcc0d3a feat: created eslint config 2025-02-09 12:36:43 +01:00
49 changed files with 3060 additions and 357 deletions

View File

@ -0,0 +1,24 @@
name: CI
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Install everything
working-directory: ./front/MyINPulse-front
run: npm i
- name: Run ESLint
working-directory: ./front/MyINPulse-front
run: npx eslint
- name: Run prettier
working-directory: ./front/MyINPulse-front
run: npx prettier src --check
- name: Build frontend
working-directory: ./front/MyINPulse-front
run: npm run build

View File

@ -20,6 +20,10 @@ repositories {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation('org.springframework.boot:spring-boot-starter-validation')
implementation('org.springframework.boot:spring-boot-starter-data-rest')
implementation 'org.postgresql:postgresql'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

View File

@ -0,0 +1,39 @@
package enseirb.myinpulse.postgres_db.controller;
import enseirb.myinpulse.postgres_db.repository.AdministrateursRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import enseirb.myinpulse.postgres_db.model.Administrateurs;
import org.springframework.web.server.ResponseStatusException;
import java.util.Optional;
@RestController
public class AdministrateursController {
@Autowired
AdministrateursRepository administrateursRepository;
@GetMapping("/Administrateurs")
@ResponseBody
public Iterable<Administrateurs> allAdministrateurs() {
return this.administrateursRepository.findAll();
}
@GetMapping("/Administrateurs/{id}")
public Administrateurs getAdministrateursById(@PathVariable Long id)
{
Optional<Administrateurs> administrateur = this.administrateursRepository.findById(id);
if (administrateur.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Cet administrateur n'existe pas");
}
return administrateur.get();
}
@PostMapping("/Administrateurs")
public Administrateurs addAdministrateurs(@RequestBody Administrateurs administrateurs) {
return this.administrateursRepository.save(administrateurs);
}
}

View File

@ -0,0 +1,49 @@
package enseirb.myinpulse.postgres_db.controller;
import enseirb.myinpulse.postgres_db.repository.ComptesRendusRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import enseirb.myinpulse.postgres_db.model.ComptesRendus;
import org.springframework.web.server.ResponseStatusException;
import java.util.Optional;
@RestController
public class ComptesRendusController {
@Autowired
ComptesRendusRepository comptesRendusRepository;
@GetMapping("/ComptesRendus")
@ResponseBody
public Iterable<ComptesRendus> allComptesRendus() {
return this.comptesRendusRepository.findAll();
}
@GetMapping("/ComptesRendus/{id}")
public ComptesRendus getComptesRendusById(@PathVariable Long id) {
Optional<ComptesRendus> compteRendu = this.comptesRendusRepository.findById(id);
if (compteRendu.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Ce compte rendu n'existe pas");
}
return compteRendu.get();
}
@PostMapping("/ComptesRendus")
public ComptesRendus addComptesRendus(@RequestBody ComptesRendus comptesRendus) {
return this.comptesRendusRepository.save(comptesRendus);
}
@PostMapping("/ComptesRendus/{id}")
public ComptesRendus updateProjets(@PathVariable Long id, String contenu_compte_rendu) {
Optional<ComptesRendus> compteRendu = this.comptesRendusRepository.findById(id);
if (compteRendu.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Ce compte rendu n'existe pas");
}
if (contenu_compte_rendu != null) {
compteRendu.get().setContenu_compte_rendu(contenu_compte_rendu);
}
return compteRendu.get();
}
}

View File

@ -0,0 +1,59 @@
package enseirb.myinpulse.postgres_db.controller;
import enseirb.myinpulse.postgres_db.repository.EntrepreneursRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.web.bind.annotation.*;
import enseirb.myinpulse.postgres_db.model.Entrepreneurs;
import org.springframework.web.server.ResponseStatusException;
import java.util.Optional;
@RestController
public class EntrepreneursController {
@Autowired
EntrepreneursRepository entrepreneursRepository;
@GetMapping("/Entrepreneurs")
@ResponseBody
public Iterable<Entrepreneurs> allEntrepreneurs() {
return this.entrepreneursRepository.findAll();
}
@GetMapping("/Entrepreneurs/{id}")
public Entrepreneurs getEntrepreneursById(@PathVariable Long id)
{
Optional<Entrepreneurs> entrepreneur = entrepreneursRepository.findById(id);
if (entrepreneur.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Cet entrepreneur n'existe pas");
}
return entrepreneur.get();
}
@PostMapping("/Entrepreneurs")
public Entrepreneurs addEntrepreneurs(@RequestBody Entrepreneurs entrepreneurs) {
return this.entrepreneursRepository.save(entrepreneurs);
}
@PostMapping("/Entrepreneurs/{id}")
public Entrepreneurs updateEntrepreneurs(@PathVariable Long id, String ecole, String filiere, Boolean status_snee) {
Optional<Entrepreneurs> entrepreneur = entrepreneursRepository.findById(id);
if (entrepreneur.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Cet entrepreneur n'existe pas");
}
if (ecole != null) {
entrepreneur.get().setEcole(ecole);
}
if (filiere != null) {
entrepreneur.get().setFiliere(filiere);
}
if (status_snee != null) {
entrepreneur.get().setStatus_snee(status_snee);
}
return entrepreneur.get();
}
}

View File

@ -0,0 +1,61 @@
package enseirb.myinpulse.postgres_db.controller;
import enseirb.myinpulse.postgres_db.repository.ProjetsRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import enseirb.myinpulse.postgres_db.model.Projets;
import org.springframework.web.server.ResponseStatusException;
import java.time.LocalDate;
import java.util.Optional;
@RestController
public class ProjetsController {
@Autowired
ProjetsRepository projetsRepository;
@GetMapping("/Projets")
@ResponseBody
public Iterable<Projets> allProjets() {
return this.projetsRepository.findAll();
}
@GetMapping("/Projets/{id}")
public Projets getProjetsById(@PathVariable Long id)
{
Optional<Projets> projet = this.projetsRepository.findById(id);
if (projet.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Ce projet n'existe pas");
}
return projet.get();
}
@PostMapping("/Projets")
public Projets addProjets(@RequestBody Projets projet) {
return this.projetsRepository.save(projet);
}
@PostMapping("/Projets/{id}")
public Projets updateProjets(@PathVariable Long id, String nom_projet, Byte[] logo, LocalDate date_creation, String status_projet) {
Optional<Projets> projet = this.projetsRepository.findById(id);
if (projet.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Ce projet n'existe pas");
}
if (nom_projet != null) {
projet.get().setNom_projet(nom_projet);
}
if (logo != null) {
projet.get().setLogo(logo);
}
if (date_creation != null) {
projet.get().setDate_creation(date_creation);
}
if (status_projet != null) {
projet.get().setStatus_projet(status_projet);
}
return projet.get();
}
}

View File

@ -0,0 +1,64 @@
package enseirb.myinpulse.postgres_db.controller;
import enseirb.myinpulse.postgres_db.repository.RendezVousRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import enseirb.myinpulse.postgres_db.model.RendezVous;
import org.springframework.web.server.ResponseStatusException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Optional;
@RestController
public class RendezVousController {
@Autowired
RendezVousRepository rendezVousRepository;
@GetMapping("/RendezVous")
@ResponseBody
public Iterable<RendezVous> allRendezVous() {
return this.rendezVousRepository.findAll();
}
@GetMapping("/RendezVous/{id}")
public RendezVous getRendezVousById(@PathVariable Long id)
{
Optional<RendezVous> rendezVous = this.rendezVousRepository.findById(id);
if (rendezVous.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Ce rendez vous n'existe pas");
}
return rendezVous.get();
}
@PostMapping("/RendezVous")
public RendezVous addRendezVous(@RequestBody RendezVous rendezVous) {
return this.rendezVousRepository.save(rendezVous);
}
@PostMapping("/RendezVous/{id}")
public RendezVous updateRendezVous(@PathVariable Long id, LocalDate date_rdv, LocalDateTime heure_rdv, LocalDateTime duree_rdv, String lieu_rdv, String sujet_rdv) {
Optional<RendezVous> rendezVous = this.rendezVousRepository.findById(id);
if (rendezVous.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Ce rendez vous n'existe pas");
}
if (date_rdv != null) {
rendezVous.get().setDate_rdv(date_rdv);
}
if (heure_rdv != null) {
rendezVous.get().setHeure_rdv(heure_rdv);
}
if (duree_rdv != null) {
rendezVous.get().setDuree_rdv(duree_rdv);
}
if (lieu_rdv != null) {
rendezVous.get().setLieu_rdv(lieu_rdv);
}
if (sujet_rdv != null) {
rendezVous.get().setSujet_rdv(sujet_rdv);
}
return rendezVous.get();
}
}

View File

@ -0,0 +1,58 @@
package enseirb.myinpulse.postgres_db.controller;
import enseirb.myinpulse.postgres_db.repository.SectionsRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import enseirb.myinpulse.postgres_db.model.Sections;
import org.springframework.web.server.ResponseStatusException;
import java.time.LocalDateTime;
import java.util.Optional;
@RestController
public class SectionsController {
@Autowired
SectionsRepository sectionsRepository;
@GetMapping("/Sections")
@ResponseBody
public Iterable<Sections> allSections() {
return this.sectionsRepository.findAll();
}
@GetMapping("/Sections/{id}")
public Sections getSectionsById(@PathVariable Long id)
{
Optional<Sections> section = this.sectionsRepository.findById(id);
if (section.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Cette section n'extise pas");
}
return section.get();
}
@PostMapping("/Sections")
public Sections addSections(@RequestBody Sections sections) {
return this.sectionsRepository.save(sections);
}
@PostMapping("/Sections/{id}")
public Sections updateSections(@PathVariable Long id, String titre, String contenu_section, LocalDateTime date_modification) {
Optional<Sections> section = this.sectionsRepository.findById(id);
if (section.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Cette section n'extise pas");
}
if (titre != null) {
section.get().setTitre(titre);
}
if (contenu_section != null) {
section.get().setContenu_section(contenu_section);
}
if (date_modification != null) {
section.get().setDate_modification(date_modification);
}
return section.get();
}
}

View File

@ -0,0 +1,61 @@
package enseirb.myinpulse.postgres_db.controller;
import enseirb.myinpulse.postgres_db.repository.UtilisateursRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import enseirb.myinpulse.postgres_db.model.Utilisateurs;
import org.springframework.web.server.ResponseStatusException;
import java.util.Optional;
@RestController
public class UtilisateursController {
@Autowired
UtilisateursRepository utilisateursRepository;
@GetMapping("/Utilisateurs")
@ResponseBody
public Iterable<Utilisateurs> allUtilisateurs() {
return this.utilisateursRepository.findAll();
}
@GetMapping("/Utilisateurs/{id}")
public Utilisateurs getUtilisateursById(@PathVariable Long id) {
Optional<Utilisateurs> utilisateur = utilisateursRepository.findById(id);
if (utilisateur.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Cet utilisateur n'existe pas");
}
return utilisateur.get();
}
@PostMapping("/Utilisateurs")
public Utilisateurs addUtilisateurs(@RequestBody Utilisateurs utilisateurs) {
return this.utilisateursRepository.save(utilisateurs);
}
@PostMapping("/Utilisateurs/{id}")
public Utilisateurs updateUtilisateurs(@PathVariable Long id, String nom_utilisateur, String prenom_utilisateur, String mail_principal, String mail_secondaire, String numero_telephone) {
Optional<Utilisateurs> utilisateur = utilisateursRepository.findById(id);
if (utilisateur.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Cet utilisateur n'existe pas");
}if (nom_utilisateur != null) {
utilisateur.get().setNom_utilisateur(nom_utilisateur);
}
if (prenom_utilisateur != null) {
utilisateur.get().setPrenom_utilisateur(prenom_utilisateur);
}
if (mail_principal != null) {
utilisateur.get().setMail_principal(mail_principal);
}
if (mail_secondaire != null) {
utilisateur.get().setMail_secondaire(mail_secondaire);
}
if (numero_telephone != null) {
utilisateur.get().setNumero_telephone(numero_telephone);
}
return utilisateur.get();
}
}

View File

@ -0,0 +1,34 @@
package enseirb.myinpulse.postgres_db.model;
import jakarta.persistence.*;
import jakarta.persistence.PrimaryKeyJoinColumn;
import jakarta.persistence.Table;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "administrateurs")
@PrimaryKeyJoinColumn(name = "id_administrateur")
public class Administrateurs extends Utilisateurs {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "Projets.id_projets")
private Projets projets;
@OneToMany(mappedBy = "administrateurs", fetch = FetchType.LAZY, orphanRemoval = true)
private List<Sections> ListSections = new ArrayList<>();
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "RendezVous.id_rdv")
private RendezVous rendezVous;
public Administrateurs() {
}
public Administrateurs(String nom_utilisateur, Long id_utilisateur, String prenom_utilisateur, String mail_principal, String mail_secondaire, String numero_telephone) {
super(nom_utilisateur, id_utilisateur, prenom_utilisateur, mail_principal, mail_secondaire, numero_telephone);
}
}

View File

@ -0,0 +1,48 @@
package enseirb.myinpulse.postgres_db.model;
import jakarta.persistence.Entity;
import jakarta.persistence.*;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
@Entity
@Table(name = "comptes_rendus")
public class ComptesRendus {
@Id
@NotNull
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id_compte_rendu;
private String contenu_compte_rendu;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "RendezVous.id_rdv")
private RendezVous rendezVous;
public ComptesRendus() {
}
public ComptesRendus(Long id_compte_rendu, String contenu_compte_rendu) {
this.id_compte_rendu = id_compte_rendu;
this.contenu_compte_rendu = contenu_compte_rendu;
}
public Long getId_compte_rendu() {
return id_compte_rendu;
}
public void setId_compte_rendu(Long id_compte_rendu) {
this.id_compte_rendu = id_compte_rendu;
}
public String getContenu_compte_rendu() {
return contenu_compte_rendu;
}
public void setContenu_compte_rendu(String contenu_compte_rendu) {
this.contenu_compte_rendu = contenu_compte_rendu;
}
}

View File

@ -0,0 +1,66 @@
package enseirb.myinpulse.postgres_db.model;
import jakarta.persistence.Entity;
import jakarta.persistence.*;
import jakarta.persistence.Table;
@Entity
@Table(name = "entrepreneurs")
@PrimaryKeyJoinColumn(name = "id_entrepreneur")
public class Entrepreneurs extends Utilisateurs {
@Column(length=255)
private String ecole;
@Column(length=255)
private String filiere;
private boolean status_snee;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "Projets.id_projets")
private Projets projets_participation;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "Projets.id_projets")
private Projets projets_propose;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "RendezVous.id_rdv")
private RendezVous rendezVous;
public Entrepreneurs() {
}
public Entrepreneurs(String nom_utilisateur, Long id_utilisateur, String prenom_utilisateur, String mail_principal, String mail_secondaire, String numero_telephone, String ecole, boolean status_snee, String filiere) {
super(nom_utilisateur, id_utilisateur, prenom_utilisateur, mail_principal, mail_secondaire, numero_telephone);
this.ecole = ecole;
this.status_snee = status_snee;
this.filiere = filiere;
}
public String getEcole() {
return ecole;
}
public void setEcole(String ecole) {
this.ecole = ecole;
}
public String getFiliere() {
return filiere;
}
public void setFiliere(String filiere) {
this.filiere = filiere;
}
public boolean isStatus_snee() {
return status_snee;
}
public void setStatus_snee(boolean status_snee) {
this.status_snee = status_snee;
}
}

View File

@ -0,0 +1,95 @@
package enseirb.myinpulse.postgres_db.model;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "projets")
public class Projets {
@Id
@NotNull
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id_projet;
@Column(length=255)
private String nom_projet;
private Byte[] logo;
private LocalDate date_creation;
@Column(length=255)
private String status_projet;
@OneToMany(mappedBy = "projets", fetch = FetchType.LAZY, orphanRemoval = true)
private List<Administrateurs> listAdministrateurs = new ArrayList<>();
@OneToMany(mappedBy = "projets", fetch = FetchType.LAZY, orphanRemoval = true)
private List<Entrepreneurs> ListEntrepreneursParticipation = new ArrayList<>();
@OneToOne(mappedBy = "projets", fetch = FetchType.LAZY, orphanRemoval = true)
private Entrepreneurs entrepreneurs_propose;
@OneToMany(mappedBy = "projets", fetch = FetchType.LAZY, orphanRemoval = true)
private List<Sections> ListSections = new ArrayList<>();
// Hibernate expects entities to have a no-arg constructor,
// though it does not necessarily have to be public.
public Projets() {
}
public Projets(Long id_projet, String nom_projet, Byte[] logo, LocalDate date_creation, String status_projet) {
this.id_projet = id_projet;
this.nom_projet = nom_projet;
this.logo = logo;
this.date_creation = date_creation;
this.status_projet = status_projet;
}
public Long getId_projet() {
return id_projet;
}
public void setId_projet(Long id_projet) {
this.id_projet = id_projet;
}
public String getNom_projet() {
return nom_projet;
}
public void setNom_projet(String nom_projet) {
this.nom_projet = nom_projet;
}
public Byte[] getLogo() {
return logo;
}
public void setLogo(Byte[] logo) {
this.logo = logo;
}
public LocalDate getDate_creation() {
return date_creation;
}
public void setDate_creation(LocalDate date_creation) {
this.date_creation = date_creation;
}
public String getStatus_projet() {
return status_projet;
}
public void setStatus_projet(String status_projet) {
this.status_projet = status_projet;
}
}

View File

@ -0,0 +1,107 @@
package enseirb.myinpulse.postgres_db.model;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "rendez_vous")
public class RendezVous {
@Id
@NotNull
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id_rdv;
private LocalDate date_rdv;
private LocalDateTime heure_rdv;
private LocalDateTime duree_rdv;
@Column(length=255)
private String lieu_rdv;
private String sujet_rdv;
@OneToMany(mappedBy = "rendez_vous", fetch = FetchType.LAZY, orphanRemoval = true)
private List<Entrepreneurs> ListEntrepreneurs = new ArrayList<>();
@OneToMany(mappedBy = "rendez_vous", fetch = FetchType.LAZY, orphanRemoval = true)
private List<Administrateurs> ListAdministrateurs = new ArrayList<>();
@OneToMany(mappedBy = "rendez_vous", fetch = FetchType.LAZY, orphanRemoval = true)
private List<ComptesRendus> ListComptesRendus = new ArrayList<>();
@ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.ALL })
@JoinTable(
name = "concerner",
joinColumns = @JoinColumn(name = "id_rdv"),
inverseJoinColumns = @JoinColumn(name = "id_sections"))
List<Sections> ListSections = new ArrayList<>();
public RendezVous() {
}
public RendezVous(Long id_rdv, LocalDate date_rdv, LocalDateTime heure_rdv, LocalDateTime duree_rdv, String lieu_rdv, String sujet_rdv) {
this.id_rdv = id_rdv;
this.date_rdv = date_rdv;
this.heure_rdv = heure_rdv;
this.duree_rdv = duree_rdv;
this.lieu_rdv = lieu_rdv;
this.sujet_rdv = sujet_rdv;
}
public Long getId_rdv() {
return id_rdv;
}
public void setId_rdv(Long id_rdv) {
this.id_rdv = id_rdv;
}
public LocalDate getDate_rdv() {
return date_rdv;
}
public void setDate_rdv(LocalDate date_rdv) {
this.date_rdv = date_rdv;
}
public LocalDateTime getHeure_rdv() {
return heure_rdv;
}
public void setHeure_rdv(LocalDateTime heure_rdv) {
this.heure_rdv = heure_rdv;
}
public LocalDateTime getDuree_rdv() {
return duree_rdv;
}
public void setDuree_rdv(LocalDateTime duree_rdv) {
this.duree_rdv = duree_rdv;
}
public String getLieu_rdv() {
return lieu_rdv;
}
public void setLieu_rdv(String lieu_rdv) {
this.lieu_rdv = lieu_rdv;
}
public String getSujet_rdv() {
return sujet_rdv;
}
public void setSujet_rdv(String sujet_rdv) {
this.sujet_rdv = sujet_rdv;
}
}

View File

@ -0,0 +1,78 @@
package enseirb.myinpulse.postgres_db.model;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "sections")
public class Sections {
@Id
@NotNull
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id_section;
@Column(length=255)
private String titre;
private String contenu_section;
private LocalDateTime date_modification;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "Projets.id_projets")
private Projets projets;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "Administrateurs.id_admnistrateur")
private Administrateurs administrateurs;
@ManyToMany(mappedBy = "sections")
private List<RendezVous> rendezVous = new ArrayList<>();
public Sections() {
}
public Sections(Long id_section, String titre, String contenu_section, LocalDateTime date_modification) {
this.id_section = id_section;
this.titre = titre;
this.contenu_section = contenu_section;
this.date_modification = date_modification;
}
public String getTitre() {
return titre;
}
public void setTitre(String titre) {
this.titre = titre;
}
public Long getId_section() {
return id_section;
}
public void setId_section(Long id_section) {
this.id_section = id_section;
}
public String getContenu_section() {
return contenu_section;
}
public void setContenu_section(String contenu_section) {
this.contenu_section = contenu_section;
}
public LocalDateTime getDate_modification() {
return date_modification;
}
public void setDate_modification(LocalDateTime date_modification) {
this.date_modification = date_modification;
}
}

View File

@ -0,0 +1,90 @@
package enseirb.myinpulse.postgres_db.model;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
@Entity
@Table(name = "utilisateurs")
@Inheritance(strategy = InheritanceType.JOINED)
public class Utilisateurs {
@Id
@NotNull
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id_utilisateur;
@Column(length=255)
private String nom_utilisateur;
@Column(length=255)
private String prenom_utilisateur;
@Column(length=255)
private String mail_principal;
@Column(length=255)
private String mail_secondaire;
@Column(length=15)
private String numero_telephone;
public Utilisateurs() {
}
public Utilisateurs(String nom_utilisateur, Long id_utilisateur, String prenom_utilisateur, String mail_principal, String mail_secondaire, String numero_telephone) {
this.nom_utilisateur = nom_utilisateur;
this.id_utilisateur = id_utilisateur;
this.prenom_utilisateur = prenom_utilisateur;
this.mail_principal = mail_principal;
this.mail_secondaire = mail_secondaire;
this.numero_telephone = numero_telephone;
}
public Long getId_utilisateur() {
return id_utilisateur;
}
public void setId_utilisateur(Long id_utilisateur) {
this.id_utilisateur = id_utilisateur;
}
public String getNom_utilisateur() {
return nom_utilisateur;
}
public void setNom_utilisateur(String nom_utilisateur) {
this.nom_utilisateur = nom_utilisateur;
}
public String getPrenom_utilisateur() {
return prenom_utilisateur;
}
public void setPrenom_utilisateur(String prenom_utilisateur) {
this.prenom_utilisateur = prenom_utilisateur;
}
public String getMail_principal() {
return mail_principal;
}
public void setMail_principal(String mail_principal) {
this.mail_principal = mail_principal;
}
public String getMail_secondaire() {
return mail_secondaire;
}
public void setMail_secondaire(String mail_secondaire) {
this.mail_secondaire = mail_secondaire;
}
public String getNumero_telephone() {
return numero_telephone;
}
public void setNumero_telephone(String numero_telephone) {
this.numero_telephone = numero_telephone;
}
}

View File

@ -0,0 +1,13 @@
package enseirb.myinpulse.postgres_db.repository;
import enseirb.myinpulse.postgres_db.model.Administrateurs;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource
public interface AdministrateursRepository extends JpaRepository<Administrateurs, Long> {
/* @Query("SELECT a from Administrateurs a")
Administrateurs findAllAdministrateurs(); */
}

View File

@ -0,0 +1,9 @@
package enseirb.myinpulse.postgres_db.repository;
import enseirb.myinpulse.postgres_db.model.ComptesRendus;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource
public interface ComptesRendusRepository extends JpaRepository<ComptesRendus, Long> {
}

View File

@ -0,0 +1,13 @@
package enseirb.myinpulse.postgres_db.repository;
import enseirb.myinpulse.postgres_db.model.Entrepreneurs;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource
public interface EntrepreneursRepository extends JpaRepository<Entrepreneurs, Long> {
/* @Query("SELECT e from Entrepreneurs e")
Entrepreneurs findAllEntrepreneurs(); */
}

View File

@ -0,0 +1,9 @@
package enseirb.myinpulse.postgres_db.repository;
import enseirb.myinpulse.postgres_db.model.Projets;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource
public interface ProjetsRepository extends JpaRepository<Projets, Long> {
}

View File

@ -0,0 +1,9 @@
package enseirb.myinpulse.postgres_db.repository;
import enseirb.myinpulse.postgres_db.model.RendezVous;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource
public interface RendezVousRepository extends JpaRepository<RendezVous, Long> {
}

View File

@ -0,0 +1,9 @@
package enseirb.myinpulse.postgres_db.repository;
import enseirb.myinpulse.postgres_db.model.Sections;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource
public interface SectionsRepository extends JpaRepository<Sections, Long> {
}

View File

@ -0,0 +1,13 @@
package enseirb.myinpulse.postgres_db.repository;
import enseirb.myinpulse.postgres_db.model.Utilisateurs;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource
public interface UtilisateursRepository extends JpaRepository<Utilisateurs, Long> {
/* @Query("SELECT u from Utilisateurs u")
Utilisateurs findAllUtilisateurs(); */
}

View File

@ -2,3 +2,10 @@ 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.level.org.springframework.security=DEBUG
spring.datasource.url=jdbc:postgresql://localhost:5432/${MyINPulse_DB}
spring.datasource.username=${POSTGRES_USER}
spring.datasource.password=${POSTGRES_PASSWORD}
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.data.rest.base-path=/my/base/path

View File

@ -0,0 +1,63 @@
INSERT INTO projets (nom_projet, logo, date_creation, status_projet) VALUES
('Eau du robinet', decode('013d7d16d7ad4fefb61bd95b765c8ceb', 'hex'), TO_DATE('01-OCT-2023', 'DD-MON-YYYY'), 'En cours'),
('Air oxygéné', decode('150647a0984e8f228cd14b54', 'hex'), TO_DATE('04-APR-2024', 'DD-MON-YYYY'), 'En cours'),
('Débat concours', decode('022024abd5486e245c145dda65116f', 'hex'), TO_DATE('22-NOV-2023', 'DD-MON-YYYY'), 'Suspendu'),
('HDeirbMI', decode('ab548d6c1d595a2975e6476f544d14c55a', 'hex'), TO_DATE('07-DEC-2024', 'DD-MON-YYYY'), 'Lancement');
INSERT INTO utilisateurs (nom, prenom, mail_principal, mail_secondaire, numero_telephone) 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');
INSERT INTO entrepreneurs (ecole, filiere, status_snee) VALUES
('ENSEIRB-MATMECA', 'INFO', TRUE),
('ENSC', 'Cognitique', TRUE),
('ENSEIRB-MATMECA', 'MATMECA', FALSE),
('SupOptique', 'Classique', TRUE),
('ENSEGID', 'Géoscience', FALSE),
('ENSMAC', 'Matériaux composites - Mécanique', FALSE);
INSERT INTO sections (titre, contenu_section, date_modification) VALUES
("Problème", "les problèmes...", TO_DATE('15-JAN-2025', 'DD-MON-YYYY')),
("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_DATE('12-OCT-2022', 'DD-MON-YYYY')),
("Proposition de valeur unique", "'Son prix est de 2594€' 'Ah oui c'est unique en effet'", TO_DATE('25-MAY-2024', 'DD-MON-YYYY')),
("Solution", "Un problème ? Une solution", TO_DATE('08-FEB-2024', 'DD-MON-YYYY')),
("Canaux", "Ici nous avons la Seine, là-bas le Rhin, oh et plus loin le canal de Suez", TO_DATE('19-JUL-2023', 'DD-MON-YYYY')),
("Sources de revenus", "Y'en n'a pas on est pas payé. Enfin y'a du café quoi", TO_DATE('12-JAN-2025', 'DD-MON-YYYY')),
("Structure des coûts", "'Ah oui là ça va faire au moins 1000€ par mois', Eirbware", TO_DATE('06-FEB-2025', 'DD-MON-YYYY')),
("Indicateurs clés", "On apprend les clés comme des badges, ça se fait", TO_DATE('05-FEB-2025', 'DD-MON-YYYY')),
("Avantages concurrentiel", "On est meilleur", TO_DATE('23-APR-2024', 'DD-MON-YYYY'));
INSERT INTO rendez_vous (date_rdv, heure_rdv, duree_rdv, lieu_rdv, sujet_rdv) 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('29-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 comptes_rendus (contenu_compte_rendu) VALUES
("Ah oui ça c'est super, ah ouais j'aime bien, bien vu de penser à ça"),
("Bonne réunion"),
("Ouais, j'ai rien compris mais niquel on fait comme vous avez dit"),
("Non non ça va pas du tout ce que tu me proposes, faut tout refaire"),
("Réponse de la DSI : non"),
("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");

View File

@ -0,0 +1,122 @@
DROP TABLE IF EXISTS projets CASCADE;
DROP TABLE IF EXISTS utilisateurs CASCADE;
DROP TABLE IF EXISTS entrepreneurs CASCADE;
DROP TABLE IF EXISTS administrateurs CASCADE;
DROP TABLE IF EXISTS sections CASCADE;
DROP TABLE IF EXISTS rendez_vous CASCADE;
DROP TABLE IF EXISTS comptes_rendus CASCADE;
DROP TABLE IF EXISTS concerner CASCADE;
DROP TABLE IF EXISTS formes CASCADE;
CREATE TABLE projets
(
id_projet SERIAL NOT NULL,
nom_projet VARCHAR(255) ,
logo BYTEA ,
date_creation DATE ,
status_projet VARCHAR(255) ,
CONSTRAINT pk_projet PRIMARY KEY (id_projet) );
CREATE TABLE utilisateurs
(
id_utilisateur SERIAL NOT NULL,
nom_utilisateur VARCHAR(255) ,
prenom_utilisateur VARCHAR(255) ,
mail_principal VARCHAR(255) ,
mail_secondaire VARCHAR(255) ,
numero_telephone VARCHAR(15) ,
CONSTRAINT pk_utilisateur PRIMARY KEY (id_utilisateur) );
CREATE TABLE entrepreneurs
(
id_entrepreneur SERIAL REFERENCES utilisateurs (id_utilisateur),
ecole VARCHAR(255) ,
filiere VARCHAR(255) ,
status_snee BOOLEAN ,
CONSTRAINT pk_entrepreneur PRIMARY KEY (id_entrepreneur) );
CREATE TABLE administrateurs
(
id_administrateur SERIAL REFERENCES utilisateurs (id_utilisateur),
CONSTRAINT pk_administrateur PRIMARY KEY (id_administrateur) );
CREATE TABLE sections
(
id_section SERIAL NOT NULL,
titre VARCHAR(255) ,
contenu_section TEXT ,
date_modification TIMESTAMP ,
CONSTRAINT pk_section PRIMARY KEY (id_section) );
CREATE TABLE rendez_vous
(
id_rdv SERIAL NOT NULL,
date_rdv DATE ,
heure_rdv TIME ,
duree_rdv TIME ,
lieu_rdv VARCHAR(255) ,
sujet_rdv TEXT ,
CONSTRAINT pk_rdv PRIMARY KEY (id_rdv) );
CREATE TABLE comptes_rendus
(
id_compte_rendu SERIAL NOT NULL,
contenu_compte_rendu TEXT ,
CONSTRAINT pk_compte_rendu PRIMARY KEY (id_compte_rendu) );
CREATE TABLE concerner
(
id_section SERIAL REFERENCES sections (id_section),
id_rdv SERIAL REFERENCES sections (id_rdv),
CONSTRAINT pk_concerner PRIMARY KEY (id_section, id_rdv) );
ALTER TABLE projets
ADD CONSTRAINT fk1_projet FOREIGN KEY (id_administrateur)
REFERENCES administrateurs (id_administrateur)
ON DELETE CASCADE;
ALTER TABLE projets
ADD CONSTRAINT fk2_projet FOREIGN KEY (id_entrepreneur_participation)
REFERENCES entrepreneurs (id_entrepreneur)
ON DELETE CASCADE;
ALTER TABLE entrepreneurs
ADD CONSTRAINT fk1_entrepreneur FOREIGN KEY (id_projet_propose)
REFERENCES projets (id_projet)
ON DELETE CASCADE;
ALTER TABLE sections
ADD CONSTRAINT fk1_section FOREIGN KEY (id_projet)
REFERENCES projets (id_projet)
ON DELETE CASCADE;
ALTER TABLE sections
ADD CONSTRAINT fk2_section FOREIGN KEY (id_administrateur)
REFERENCES administrateurs (id_administrateur)
ON DELETE CASCADE;
ALTER TABLE rendez-vous
ADD CONSTRAINT fk1_rdv FOREIGN KEY (id_entrepreneur)
REFERENCES entrepreneurs (id_entrepreneur)
ON DELETE CASCADE;
ALTER TABLE rendez-vous
ADD CONSTRAINT fk2_rdv FOREIGN KEY (id_administrateur)
REFERENCES administrateurs (id_administrateur)
ON DELETE CASCADE;
ALTER TABLE comptes-rendus
ADD CONSTRAINT fk1_compte_rendu FOREIGN KEY (id_rdv)
REFERENCES rendez_vous (id_rdv)
ON DELETE CASCADE;

View File

@ -2,8 +2,8 @@ services:
postgres:
image: postgres:latest
container_name: MyINPulse-DB
#ports:
# - 5432:5432
ports:
- 5432:5432
volumes:
- ./postgres:/var/lib/postgresql/data
environment:

View File

@ -4,3 +4,7 @@ POSTGRES_PASSWORD=keycloak_db_user_password
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=admin
KEYCLOAK_HOSTNAME=localhost
MYINPULSE_DB=MyINPulse_db
MYINPULSE_DB_USER=MyINPulse_db_user
MYINPULSE_DB_PASS=MyINPulse_db_user_pass

View File

@ -36,4 +36,4 @@ playwright-report/
# Custom
.installed
package-lock.json
./package-lock.json

View File

@ -0,0 +1,7 @@
{
"useTabs":false,
"semi":true,
"trailingComma":"es5",
"arrowParens":"always",
"tabWidth":4
}

View File

@ -0,0 +1,29 @@
import eslint from "@eslint/js";
import eslintConfigPrettier from "eslint-config-prettier";
import eslintPluginVue from "eslint-plugin-vue";
import globals from "globals";
import typescriptEslint from "typescript-eslint";
export default typescriptEslint.config(
{ ignores: ["*.d.ts", "**/coverage", "**/dist"] },
{
extends: [
eslint.configs.recommended,
...typescriptEslint.configs.recommended,
...eslintPluginVue.configs["flat/recommended"],
],
files: ["**/*.{ts,vue}"],
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
globals: globals.browser,
parserOptions: {
parser: typescriptEslint.parser,
},
},
rules: {
// your rules
},
},
eslintConfigPrettier
);

File diff suppressed because it is too large Load Diff

View File

@ -26,8 +26,15 @@
"@types/node": "^22.10.7",
"@vitejs/plugin-vue": "^5.2.1",
"@vue/tsconfig": "^0.7.0",
"eslint": "^9.20.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-vue": "^9.32.0",
"globals": "^15.14.0",
"jiti": "^2.4.2",
"npm-run-all2": "^7.0.2",
"prettier": "3.5.0",
"typescript": "~5.7.3",
"typescript-eslint": "^8.23.0",
"vite": "^6.0.11",
"vite-plugin-vue-devtools": "^7.7.0",
"vue-tsc": "^2.2.0"

View File

@ -1,50 +1,47 @@
<script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router'
import { RouterView } from "vue-router";
import ErrorWrapper from "@/views/errorWrapper.vue";
import ProjectComponent from "@/components/ProjectComponent.vue";
</script>
<template>
<Header />
<error-wrapper></error-wrapper>
<div id="main">
<ProjectComp
v-for="(project, index) in projects"
:key="index"
:projectName="project.name"
/>
<HeaderComponent />
<error-wrapper></error-wrapper>
<div id="main">
<ProjectComponent
v-for="(project, index) in projects"
:key="index"
:project-name="project.name"
/>
</div>
<RouterView />
<RouterView />
</template>
<style scoped>
</style>
<script lang="ts">
import Header from "@/components/Header.vue";
import ProjectComp from "@/components/Project-comp.vue";
import HeaderComponent from "@/components/HeaderComponent.vue";
export default {
name: 'App',
components: {
Header,
ProjectComp,
},
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'],
},
],
};
},
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>

View File

@ -1,26 +1,26 @@
<template>
<header>
<img src="./icons/logo inpulse.png" alt="INPulse" />
<img src="./icons/logo inpulse.png" alt="INPulse" />
</header>
</template>
</template>
<script lang="ts">
export default {
name: 'Header',
};
</script>
<script lang="ts">
export default {
name: "HeaderComponent",
};
</script>
<style scoped>
header img {
<style scoped>
header img {
width: 100px;
}
}
header{
header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 20px;
background-color: #fff;
border-bottom: 2px solid #ddd;
}
</style>
}
</style>

View File

@ -3,16 +3,19 @@
<div class="project-header">
<h2>{{ projectName }}</h2>
</div>
</div>
</template>
<script lang="ts">
import type { PropType } from "vue";
export default {
name: 'Project',
name: "ProjectComponent",
props: {
projectName: String,
}
}
projectName: {
type: Object as PropType<string>,
required: true,
},
},
};
</script>

View File

@ -1,17 +1,43 @@
<script setup lang="ts">
const props = defineProps(['data']);
import { color } from "@/services/popupDisplayer.ts";
import type { PropType } from "vue";
defineProps({
errorMessage: {
type: Object as PropType<string>,
required: true,
},
errorColor: {
type: Object as PropType<color>,
default: color.Red,
},
});
</script>
<template>
<div :class='["red", "yellow", "blue", "green"][data.type]' class="error-modal">
<p>{{["Erreur :(", "Warning :|", "Info :)", "Succes ;)"][data.type]}}</p>
<p>{{data.errorMessage}}</p>
<div class="loading" :class='["red-loader", "yellow-loader", "blue-loader", "green-loader"][data.type]'></div>
</div>
<div
:class="['red', 'yellow', 'blue', 'green'][errorColor]"
class="error-modal"
>
<p>
{{
["Erreur :(", "Warning :|", "Info :)", "Succes ;)"][errorColor]
}}
</p>
<p>{{ errorMessage }}</p>
<div
class="loading"
:class="
['red-loader', 'yellow-loader', 'blue-loader', 'green-loader'][
errorColor
]
"
></div>
</div>
</template>
<style scoped>
.error-modal{
.error-modal {
margin-bottom: 1em;
padding: 1em;
border-radius: 1em;
@ -19,41 +45,45 @@ const props = defineProps(['data']);
overflow: hidden;
position: relative;
animation: disappear 5s linear forwards;
}
}
.red{
.red {
background-color: #ee6055;
color: white;
}
.red-loader {
}
.red-loader {
background-color: #fa8383;
}
}
.yellow{
background-color: #FF9D23;
color: white;
}
.yellow-loader{
.yellow {
background-color: #ff9d23;
color: white;
}
.yellow-loader {
background-color: #ffbf81;
}
}
.blue {
.blue {
background-color: #809bce;
color: white;
}
.blue-loader{
background-color: #95b8d1;
}
}
.green {
.blue-loader {
background-color: #95b8d1;
}
.green {
background-color: green;
color: white;
}
.green-loader {
background-color: darkgreen;
}
}
.loading {
.green-loader {
background-color: darkgreen;
}
.loading {
box-sizing: border-box;
position: absolute;
padding: 0;
@ -62,33 +92,33 @@ const props = defineProps(['data']);
height: 1em;
width: 0;
animation: loading 4s linear forwards;
}
}
/* Animation for the loading bar */
@keyframes loading {
/* Animation for the loading bar */
@keyframes loading {
0% {
width: 100%;
width: 100%;
}
100% {
width: 0;
width: 0;
}
}
}
@keyframes disappear {
@keyframes disappear {
0% {
height: 100px;
padding: 1em;
margin: 1em;
height: 100px;
padding: 1em;
margin: 1em;
}
80% {
height: 100px;
padding: 1em;
margin: 1em;
height: 100px;
padding: 1em;
margin: 1em;
}
100% {
height: 0;
margin: 0;
padding: 0;
height: 0;
margin: 0;
padding: 0;
}
}
}
</style>

View File

@ -0,0 +1,18 @@
<script setup lang="ts">
import { addNewMessage } from "@/services/popupDisplayer.ts";
</script>
<template>
<button
@click="
addNewMessage(
'new error from another view',
Math.floor(Math.random() * 4)
)
"
>
Add an error
</button>
</template>
<style scoped></style>

View File

@ -1,36 +1,31 @@
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/router.ts'
import {createPinia} from "pinia";
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
import AuthStorePlugin from './plugins/authStore';
import keycloakService from './services/keycloak';
import {useAuthStore} from "@/stores/authStore.ts";
let store: any;
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router/router.ts";
import { createPinia } from "pinia";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
import keycloakService from "./services/keycloak";
import { type AuthStore, useAuthStore } from "@/stores/authStore.ts";
let store: AuthStore;
keycloakService.CallInit(() => {
try {
const app = createApp(App)
const app = createApp(App);
// Setup pinia store, allowing user to keep logged in status after refresh
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
app.use(pinia);
app.use(AuthStorePlugin, { pinia });
store = useAuthStore();
app.use(router)
keycloakService.CallInitStore(store);
app.use(router);
app.mount('#app');
app.mount("#app");
} catch (e) {
console.error("Error while initiating Keycloak.")
console.error(e)
createApp(App).mount('#app');
console.error("Error while initiating Keycloak.");
console.error(e);
createApp(App).mount("#app");
}
});
})
export {store};
export { store };

View File

@ -1,14 +0,0 @@
// file: src/plugins/authStore.js
import { useAuthStore } from "@/stores/authStore.ts";
import keycloakService from '@/services/keycloak';
// Setup auth store as a plugin so it can be accessed globally in our FE
const authStorePlugin = {
install(app: any, option: any) {
const store = useAuthStore(option.pinia);
app.config.globalProperties.$store = store;
keycloakService.CallInitStore(store);
}
}
export default authStorePlugin;

View File

@ -1,17 +1,17 @@
import { createRouter, createWebHistory } from 'vue-router'
import { createRouter, createWebHistory } from "vue-router";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/test',
name: 'test',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/test.vue'),
},
],
})
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: "/test",
name: "test",
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import("../views/testComponent.vue"),
},
],
});
export default router
export default router;

View File

@ -1,31 +1,36 @@
import axios from "axios";
import {store} from "@/main.ts";
import {addNewMessage, color} from "@/services/popupDisplayer.ts";
import axios, { type AxiosError, type AxiosResponse } from "axios";
import { store } from "@/main.ts";
import { addNewMessage, color } from "@/services/popupDisplayer.ts";
const axiosInstance = axios.create({
baseURL: import.meta.env.VITE_BACKEND_URL,
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
},
});
axiosInstance.interceptors.response.use(
response => response, // Directly return successful responses.
async error => {
(response) => response, // Directly return successful responses.
async (error) => {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry && store.authenticated) {
if (
error.response.status === 401 &&
!originalRequest._retry &&
store.authenticated
) {
originalRequest._retry = true; // Mark the request as retried to avoid infinite loops.
try {
await store.refreshUserToken();
// Update the authorization header with the new access token.
axiosInstance.defaults.headers.common['Authorization'] = `Bearer ${store.user.token}`;
axiosInstance.defaults.headers.common["Authorization"] =
`Bearer ${store.user.token}`;
return axiosInstance(originalRequest); // Retry the original request with the new access token.
} 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';
console.error("Token refresh failed:", refreshError);
localStorage.removeItem("accessToken");
localStorage.removeItem("refreshToken");
window.location.href = "/login";
return Promise.reject(refreshError);
}
}
@ -34,23 +39,29 @@ axiosInstance.interceptors.response.use(
);
// TODO: spawn a error modal
function defaultApiErrorHandler(err: string){
addNewMessage(err, color.Red);
function defaultApiErrorHandler(err: AxiosError) {
addNewMessage(err.message, color.Red);
}
function defaultApiSuccessHandler(response: any){
addNewMessage(response.data, color.green)
}
function callApi(endpoint: string, onSuccessHandler?: any, onErrorHandler?: any): void {
axiosInstance.get(endpoint).then(
onSuccessHandler == null ? defaultApiSuccessHandler : onSuccessHandler
).catch(
(err) => {
onErrorHandler == null ? defaultApiErrorHandler(err): onErrorHandler(err);
throw err;
}
)
function defaultApiSuccessHandler(response: AxiosResponse) {
addNewMessage(response.data, color.Green);
}
function callApi(
endpoint: string,
onSuccessHandler?: (response: AxiosResponse) => void,
onErrorHandler?: (error: AxiosError) => void
): void {
axiosInstance
.get(endpoint)
.then(
onSuccessHandler == null
? defaultApiSuccessHandler
: onSuccessHandler
)
.catch(
onErrorHandler == null ? defaultApiErrorHandler : onErrorHandler
);
}
export {callApi}
export { callApi };

View File

@ -1,33 +1,31 @@
import Keycloak from 'keycloak-js';
import Keycloak from "keycloak-js";
import type { AuthStore } from "@/stores/authStore.ts";
const options = {
url: import.meta.env.VITE_KEYCLOAK_URL,
clientId: import.meta.env.VITE_KEYCLOAK_CLIENT_ID,
realm: import.meta.env.VITE_KEYCLOAK_REALM
}
realm: import.meta.env.VITE_KEYCLOAK_REALM,
};
const keycloak = new Keycloak(options);
let authenticated: boolean | undefined;
let store = null;
async function login(){
async function login() {
try {
await keycloak.login() // https://www.keycloak.org/securing-apps/javascript-adapter#:~:text=when%20initialization%20completes.-,login(options),-Redirects%20to%20login
await keycloak.login(); // https://www.keycloak.org/securing-apps/javascript-adapter#:~:text=when%20initialization%20completes.-,login(options),-Redirects%20to%20login
return keycloak;
} catch (error) {
console.log(error)
console.log(error);
}
}
async function signup(){
async function signup() {
try {
await keycloak.login(
{action: "register"}
) // https://www.keycloak.org/securing-apps/javascript-adapter#:~:text=when%20initialization%20completes.-,login(options),-Redirects%20to%20login
await keycloak.login({ action: "register" }); // https://www.keycloak.org/securing-apps/javascript-adapter#:~:text=when%20initialization%20completes.-,login(options),-Redirects%20to%20login
return keycloak;
} catch (error) {
console.log(error)
console.log(error);
}
}
@ -42,31 +40,33 @@ async function init(onInitCallback: () => void) {
onLoad: "check-sso",
silentCheckSsoRedirectUri: `${location.origin}/silent-check-sso.htm`,
responseMode: "query",
})
onInitCallback()
});
onInitCallback();
} catch (error) {
console.error("Keycloak init failed")
console.error(error)
console.error("Keycloak init failed");
console.error(error);
}
};
}
/**
* Initializes store with Keycloak user data
*
*/
async function initStore(storeInstance: any) {
async function initStore(storeInstance: AuthStore) {
try {
store = storeInstance
console.log(keycloak)
await store.initOauth(keycloak)
store = storeInstance;
console.log(keycloak);
await store.initOauth(keycloak);
// Show alert if user is not authenticated
if (!authenticated) { console.warn("not authenticated") }
if (!authenticated) {
console.warn("not authenticated");
}
} catch (error) {
console.error("Keycloak init failed")
console.error(error)
console.error("Keycloak init failed");
console.error(error);
}
};
}
/**
* Logout user
@ -83,7 +83,7 @@ async function refreshToken() {
await keycloak.updateToken(480);
return keycloak;
} catch (error) {
console.error('Failed to refresh token');
console.error("Failed to refresh token");
console.error(error);
}
}

View File

@ -1,19 +1,43 @@
import {ref} from "vue";
enum color {Red, Yellow, Blue, green}
import { ref, type Ref } from "vue";
function addNewMessage(errorMessage: string, type?: color, timeout?: number){
if (timeout == null){
enum color {
Red,
Yellow,
Blue,
Green,
}
type ErrorMessageContent = {
message: string;
color: color;
id: number;
timeout: number;
};
let id: number = 0;
const getId = () => {
id = id + 1;
return id;
};
function addNewMessage(errorMessage: string, type?: color, timeout?: number) {
if (timeout == null) {
timeout = 5000;
}
if (type == null){
if (type == null) {
type = color.Red;
}
const data = {errorMessage: errorMessage, timeout: timeout, type: type, uid: Math.random()*100000};
errorList.value.push(data)
setTimeout(() => errorList.value.slice(0, 1), timeout)
const data: ErrorMessageContent = {
message: errorMessage,
timeout: timeout,
color: type,
id: getId(),
};
errorList.value.push(data);
setTimeout(() => errorList.value.slice(0, 1), timeout);
}
const errorList: any= ref([])
const errorList: Ref<ErrorMessageContent[]> = ref([]);
export {addNewMessage, errorList, color}
export { addNewMessage, errorList, color, type ErrorMessageContent };

View File

@ -1,54 +1,61 @@
import { defineStore } from "pinia";
import keycloakService from '@/services/keycloak';
import keycloakService from "@/services/keycloak";
import type Keycloak from "keycloak-js";
export const useAuthStore = defineStore("storeAuth", {
const useAuthStore = defineStore("storeAuth", {
state: () => {
return {
testv: true,
authenticated: false,
user: {
token: "",
refreshToken: "",
username: "",
},
}
};
},
persist: true,
getters: {},
actions: {
// Initialize Keycloak OAuth
async initOauth(keycloak: Keycloak, clearData = true) {
if(clearData) { await this.clearUserData(); }
if (clearData) {
await this.clearUserData();
}
this.authenticated = !!keycloak.authenticated; // the !! removes undefined
if (this.authenticated && keycloak.token && keycloak.idTokenParsed && keycloak.refreshToken){
if (
this.authenticated &&
keycloak.token &&
keycloak.idTokenParsed &&
keycloak.refreshToken
) {
this.user.username = keycloak.idTokenParsed.given_name;
this.user.token = keycloak.token;
this.user.refreshToken = keycloak.refreshToken;
}
},
async login(){
async login() {
try {
const keycloak = await keycloakService.callLogin();
if (keycloak)
await this.initOauth(keycloak);
if (keycloak) await this.initOauth(keycloak);
} catch (error) {
console.log(error)
console.log(error);
}
},
async signup() {
try {
const keycloak = await keycloakService.callSignup();
if (keycloak)
await this.initOauth(keycloak);
if (keycloak) await this.initOauth(keycloak);
} catch (error) {
console.log(error)
console.log(error);
}
},
// Logout user
async logout() {
try {
await keycloakService.CallLogout(import.meta.env.VITE_APP_URL + "/test");
await keycloakService.CallLogout(
import.meta.env.VITE_APP_URL + "/test"
);
await this.clearUserData();
} catch (error) {
console.error(error);
@ -58,23 +65,23 @@ export const useAuthStore = defineStore("storeAuth", {
async refreshUserToken() {
try {
const keycloak = await keycloakService.CallTokenRefresh();
if (keycloak)
await this.initOauth(keycloak, false);
if (keycloak) await this.initOauth(keycloak, false);
} catch (error) {
console.error(error);
}
},
test() {
this.testv = !this.testv;
},
// Clear user's store data
clearUserData() {
this.authenticated = false;
this.user = {
token: "",
refreshToken: "",
username: "",
refreshToken: "",
username: "",
};
}
}
},
},
});
type AuthStore = ReturnType<typeof useAuthStore>;
export { useAuthStore, type AuthStore };

View File

@ -1,15 +0,0 @@
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>
<style>
@media (min-width: 1024px) {
.about {
min-height: 100vh;
display: flex;
align-items: center;
}
}
</style>

View File

@ -1,22 +1,25 @@
<script setup lang="ts">
import {errorList} from "@/services/popupDisplayer.ts";
import { errorList } from "@/services/popupDisplayer.ts";
import ErrorModal from "@/components/errorModal.vue";
</script>
<template>
<div class="error-wrapper">
<error-modal v-for="elm in errorList" :data=elm></error-modal>
</div>
<div class="error-wrapper">
<error-modal
v-for="elm in errorList"
:key="elm.id"
:error-message="elm.message"
:error-color="elm.color"
/>
</div>
</template>
<style scoped>
.error-wrapper{
position: absolute;
left: 70%;
//background-color: blue;
height: 100%;
width: 30%;
.error-wrapper {
position: absolute;
left: 70%;
//background-color: blue;
height: 100%;
width: 30%;
}
</style>

View File

@ -1,75 +0,0 @@
<script setup lang="ts">
import {store} from "../main.ts";
import {callApi} from "@/services/api.ts";
import ErrorModal from "@/components/errorModal.vue";
import {errorList} from "@/services/popupDisplayer.ts";
import TempModal from "@/components/temp-modal.vue";
import ErrorWrapper from "@/App.vue";
function addResToTable(id: any){
return (req: any) => {
console.log(req)
}
}
</script>
<template>
<h1>Test page</h1>
<table class="test" style="width:100%">
<tbody>
<tr>
<td>Is Currently Authenticated ? </td>
<td>{{store.authenticated}}</td>
<td><button @click="store.login">Login</button></td>
<td><button @click="store.logout">Logout</button></td>
<td><button @click="store.signup">Signup</button></td>
</tr>
<tr>
<td>current token</td>
<td>{{store.user.token}}</td>
<td><button @click="store.refreshUserToken">Refresh</button></td>
</tr>
<tr>
<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('random3')">call</button></td>
<td>res</td>
<td id="3"></td>
</tr>
</tbody>
</table>
<temp-modal></temp-modal>
</template>
<style scoped>
table {
width: 100px;
table-layout: fixed
}
tr {
width: 100%;
height: 100%;
}
td {
border: solid 1px black;
width: 20%;
height: 100%;
overflow: hidden;
}
</style>

View File

@ -0,0 +1,79 @@
<script lang="ts" setup>
import { store } from "../main.ts";
import { callApi } from "@/services/api.ts";
</script>
<template>
<h1>Test page</h1>
<table class="test" style="width: 100%">
<tbody>
<tr>
<td>Is Currently Authenticated ?</td>
<td>{{ store.authenticated }}</td>
<td>
<button @click="store.login">Login</button>
</td>
<td>
<button @click="store.logout">Logout</button>
</td>
<td>
<button @click="store.signup">Signup</button>
</td>
</tr>
<tr>
<td>current token</td>
<td>{{ store.user.token }}</td>
<td>
<button @click="store.refreshUserToken">Refresh</button>
</td>
</tr>
<tr>
<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('random3')">call</button>
</td>
<td>res</td>
<td id="3"></td>
</tr>
</tbody>
</table>
</template>
<style scoped>
table {
width: 100px;
table-layout: fixed;
}
tr {
width: 100%;
height: 100%;
}
td {
border: solid 1px black;
width: 20%;
height: 100%;
overflow: hidden;
}
</style>