1
0
forked from PGL/Clyde

Compare commits

..

3 Commits

Author SHA1 Message Date
b1ccae88ef
first version of the frontend for messages 2024-03-23 01:06:32 +01:00
ad81ee3647
base msg 2024-03-22 13:55:13 +01:00
b714aeb156
Make app use full space 2024-03-22 13:54:30 +01:00
15 changed files with 120 additions and 282 deletions

View File

@ -16,8 +16,6 @@ repositories {
} }
dependencies { dependencies {
compileOnly("org.projectlombok:lombok")
annotationProcessor("org.projectlombok:lombok")
implementation("org.springframework.boot:spring-boot-starter-jdbc") implementation("org.springframework.boot:spring-boot-starter-jdbc")
implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-mail") implementation("org.springframework.boot:spring-boot-starter-mail")

View File

@ -1,49 +0,0 @@
package ovh.herisson.Clyde.EndPoints.Msg;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import lombok.AllArgsConstructor;
import ovh.herisson.Clyde.Responses.UnauthorizedResponse;
import ovh.herisson.Clyde.Services.AuthenticatorService;
import ovh.herisson.Clyde.Services.Msg.DiscussionService;
import ovh.herisson.Clyde.Tables.User;
import ovh.herisson.Clyde.Tables.Msg.Discussion;
@RestController
@CrossOrigin(originPatterns = "*", allowCredentials = "true")
@AllArgsConstructor
public class MessagesController {
private AuthenticatorService authServ;
private DiscussionService discServ;
@GetMapping("/discussions")
public ResponseEntity<Iterable<Discussion>> getDiscussions(@RequestHeader("Authorization") String token ){
User user = authServ.getUserFromToken(token);
if(user == null){
return new UnauthorizedResponse<>(null);
}
Iterable<Discussion> mock = discServ.getOwned(authServ.getUserFromToken(token));
return new ResponseEntity<>(mock, HttpStatus.OK);
}
@GetMapping("/discussion/{id}")
public ResponseEntity<Discussion> getDiscussion(@RequestHeader("Authorization") String token, @PathVariable long id){
return null; // TODO
}
@PostMapping("/discussion")
public ResponseEntity<Discussion> createDiscussion(@RequestHeader("Authorization") String token, @RequestBody Discussion data){
return new ResponseEntity<>(discServ.create(data.getName(), authServ.getUserFromToken(token)), HttpStatus.OK);
}
}

View File

@ -1,14 +0,0 @@
package ovh.herisson.Clyde.Repositories.Msg;
import java.util.List;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import ovh.herisson.Clyde.Tables.Msg.Discussion;
public interface DiscussionRepository extends CrudRepository<Discussion, Long>{
@Query("SELECT d FROM Discussion d INNER JOIN FETCH d.members dm WHERE dm.id = ?1")
List<Discussion> findByMembership(long userid);
}

View File

@ -1,24 +0,0 @@
package ovh.herisson.Clyde.Services.Msg;
import org.springframework.stereotype.Service;
import lombok.AllArgsConstructor;
import ovh.herisson.Clyde.Repositories.Msg.DiscussionRepository;
import ovh.herisson.Clyde.Tables.User;
import ovh.herisson.Clyde.Tables.Msg.Discussion;
@Service
@AllArgsConstructor
public class DiscussionService {
private DiscussionRepository discRepo;
public Discussion create(String name, User author){
return discRepo.save(new Discussion(name, author));
}
public Iterable<Discussion> getOwned(User author){
return discRepo.findByMembership(author.getRegNo());
}
}

View File

@ -1,44 +0,0 @@
package ovh.herisson.Clyde.Tables.Msg;
import java.util.List;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import ovh.herisson.Clyde.Tables.User;
@Entity
@Getter
@Setter
@NoArgsConstructor
public class Discussion{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
@ManyToMany
@JoinTable(
name = "discussion_members",
joinColumns = @JoinColumn(name = "discussion_id"),
inverseJoinColumns = @JoinColumn(name = "user_id")
)
private List<User> members;
public Discussion(String name){
this.name = name;
}
public Discussion(String name, User user){
this.name = name;
this.members = List.of(user);
}
}

View File

@ -1,10 +1,7 @@
package ovh.herisson.Clyde.Tables; package ovh.herisson.Clyde.Tables;
import jakarta.persistence.*; import jakarta.persistence.*;
import ovh.herisson.Clyde.Tables.Msg.Discussion;
import java.util.Date; import java.util.Date;
import java.util.List;
@Entity @Entity
@ -23,10 +20,6 @@ public class User {
private String profilePictureUrl; private String profilePictureUrl;
private ovh.herisson.Clyde.Tables.Role role; private ovh.herisson.Clyde.Tables.Role role;
private String password; private String password;
@ManyToMany( mappedBy = "members" )
private List<Discussion> discussions;
public User(String lastName, String firstName, String email, String address, public User(String lastName, String firstName, String email, String address,
String country, Date birthDate, String profilePictureUrl, Role role, String password) String country, Date birthDate, String profilePictureUrl, Role role, String password)
{ {

View File

@ -16,9 +16,10 @@
<template> <template>
<div style='display:flex; justify-content:center; min-width:1140px;' v-for="item of requests"> <div v-for="item of requests">
<div class="bodu" v-if="item.state === 'Pending'"> <div class="bodu" v-if="item.state === 'Pending'">
<div class="container"> <div class="container">
<div class="id"><a>{{item.id}}</a></div> <div class="id"><a>{{item.id}}</a></div>
<div class="surname"><a>{{item.lastName}}</a></div> <div class="surname"><a>{{item.lastName}}</a></div>
<div class="firstname"><a>{{item.firstName}}</a></div> <div class="firstname"><a>{{item.firstName}}</a></div>
@ -36,9 +37,10 @@
height:100px; height:100px;
font-size:20px; font-size:20px;
display:grid; display:grid;
grid-template-columns:10% 14.2% 19% 14.2% 14.2% 14.2% 14.2%; grid-template-columns:[firstCol-start]100px[firstCol-end secondCol-start]150px[secondCol-end thirdCol-start]200px[thirdCol-end fourthCol-start]150px[fourthCol-end]150px[fifthCol-end]150px[sixthCol-end]150px[endCol];
grid-template-areas: grid-template-areas:
"id type surname firstname infos accept refuse"; "id type surname firstname infos accept refuse";
column-gap:10px;
} }
@ -98,8 +100,8 @@
} }
.bodu { .bodu {
margin-top:2%; width:100%;
width:66%; margin-bottom:10px;
border:2px solid black; border:2px solid black;
border-radius:9px; border-radius:9px;
background-color:rgb(50,50,50); background-color:rgb(50,50,50);

View File

@ -56,10 +56,10 @@
<template> <template>
<div class="setup"> <div class='loginBox'>
<div v-if="loginPage"> <div v-if="loginPage">
<div class='loginBox' style="margin-top:30%;"> <form @submit.prevent=" login(outputs.email,outputs.password);goBackHome();"class="form">
<form @submit.prevent="login(outputs.email,outputs.password);goBackHome();"class="form">
<h1 style="color:rgb(239,60,168); font-family: sans-serif;"> <h1 style="color:rgb(239,60,168); font-family: sans-serif;">
{{i18n("login.guest.signin")}} {{i18n("login.guest.signin")}}
</h1> </h1>
@ -74,14 +74,13 @@
<div class="register"> <div class="register">
<a @click="loginPage=!loginPage">{{i18n("login.guest.register")}}</a> <a @click="loginPage=!loginPage">{{i18n("login.guest.register")}}</a>
</div> </div>
<div class="inputBox" style="margin-bottom:35px;"> <div class="inputBox">
<input type="submit" v-model="submitValue"> <input type="submit" v-model="submitValue">
</div> </div>
</form> </form>
</div> </div>
</div>
<div v-else> <div v-else>
<div class='loginBox' style="margin-top:30%; margin-bottom:50%;">
<form class="form"> <form class="form">
<h1 style="color:rgb(239,60,168); font-family: sans-serif; text-align:center;"> <h1 style="color:rgb(239,60,168); font-family: sans-serif; text-align:center;">
{{i18n("login.guest.welcome")}} {{i18n("login.guest.welcome")}}
@ -129,12 +128,9 @@
<p>{{i18n("login.guest.country")}}</p> <p>{{i18n("login.guest.country")}}</p>
<input type="text" v-model="outputs.country"> <input type="text" v-model="outputs.country">
</div> </div>
<form class="inputBox"novalidate enctype="multipart/form-data"> <form novalidate enctype="multipart/form-data" class="inputBox">
<p>{{i18n("profile.picture").toUpperCase()}}</p> <p>{{i18n("profile.picture").toUpperCase()}}</p>
<label class="browser">
Parcourir . . .
<input type="file" :disabled="imageSaved" @change="ppData = uploadProfilePicture($event.target.files); imageSaved = true;" accept="image/*"> <input type="file" :disabled="imageSaved" @change="ppData = uploadProfilePicture($event.target.files); imageSaved = true;" accept="image/*">
</label>
</form> </form>
<div class="inputBox"> <div class="inputBox">
<p>{{i18n("Curriculum").toUpperCase()}}</p> <p>{{i18n("Curriculum").toUpperCase()}}</p>
@ -158,25 +154,34 @@
</form> </form>
</div> </div>
</div> </div>
</div>
</template> </template>
<style scoped> <style scoped>
.Home{
.setup { position:absolute;
margin-left: auto; display: flex;
margin-right:auto; z-index: 100;
min-width:400px; padding: 8px 16px;
color:rgb(255, 255, 255);
width:25%; text-decoration: none;
height:60%;
} }
.Home:hover{
width:40px;
background-color: black;
border-radius:6px;
color:white;
transform: translate(0px ,1px);
}
.loginBox { .loginBox {
background-color: rgb(24,24,24); background-color: rgb(24,24,24);
width: 400px;
display:flex; display:flex;
justify-content: center; justify-content: center;
border-radius: 5%; padding: 40px;
border-radius: 20px;
box-shadow:0 5px 25px #000000; box-shadow:0 5px 25px #000000;
} }
@ -185,8 +190,9 @@
width:100%; width:100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center;
align-items:center; align-items:center;
gap: 3%; gap: 15px;
} }
@ -194,12 +200,12 @@
width:100%; width:100%;
border: none; border: none;
margin-right: 12.5%; margin-right: 50px;
padding-left: 2.5%; padding-left: 10px;
padding-top:2.5%; padding-top:10px;
padding-bottom:2.5%; padding-bottom:10px;
outline:none; outline:none;
border-radius: 10px; border-radius: 4px;
font-size:1.35em; font-size:1.35em;
} }
@ -214,9 +220,8 @@
.register{ .register{
color:rgb(239,60,168); color:rgb(239,60,168);
width:70%; width: 100%;
margin-bottom:20px; display:flex;
margin-top:20px;
cursor: pointer; cursor: pointer;
} }
@ -245,21 +250,6 @@ input[type=submit],button,select{
} }
input[type=file]{
display:none;
}
.browser{
display:inline-block;
cursor:pointer;
border-radius:20px;
background-color:rgb(239,60,168);
padding:5%;
font-size:1.35em;
font-family:sans-serif;
background:#FFFFFF;
}
button:active ,.switchpage:active{ button:active ,.switchpage:active{
opacity:0.8; opacity:0.8;

View File

@ -105,8 +105,7 @@
</button> </button>
</div> </div>
<div v-if="createMod"> <div v-if="createMod">
<form class="listElement" style="width:40%;margin-right:auto;margin-left:auto;"> <form class="listElement">
<div style="margin-bottom:20px;"> <div style="margin-bottom:20px;">
{{i18n("name")}} : {{i18n("name")}} :
<input v-model="toAdd.title"> <input v-model="toAdd.title">
@ -126,7 +125,7 @@
</form> </form>
</div> </div>
<div v-if="deleteMod"> <div v-if="deleteMod">
<form class="listElement" style="width:40%;margin-right:auto;margin-left:auto;"> <form class="listElement">
<div style="margin-bottom:20px;"> <div style="margin-bottom:20px;">
{{i18n("courses.toDelete")}} : {{i18n("courses.toDelete")}} :
<select style="max-width:200px;" class="teacher" v-model="toRemove"> <select style="max-width:200px;" class="teacher" v-model="toRemove">
@ -139,7 +138,7 @@
</form> </form>
</div> </div>
<div v-if="!createMod && !deleteMod" v-for="item in curriculum" :key="item.title" style="width:50%;margin-left:auto; margin-right:auto;"> <div v-if="!createMod && !deleteMod" v-for="item in curriculum" :key="item.title">
<div v-if="editElementID !== item.title" style ="padding:15px 15px 15px 15px;"> <div v-if="editElementID !== item.title" style ="padding:15px 15px 15px 15px;">
<button @click="editElementID = item.title; setModify(item); "> <button @click="editElementID = item.title; setModify(item); ">
{{i18n("courses.modify")}} {{i18n("courses.modify")}}
@ -150,7 +149,6 @@
<button @click="editElementID= '';"> {{i18n("courses.back")}} </button> <button @click="editElementID= '';"> {{i18n("courses.back")}} </button>
</div> </div>
<div class="listElement" > <div class="listElement" >
<div class="containerElement" v-if="editElementID !== item.title" > <div class="containerElement" v-if="editElementID !== item.title" >
<div class="name"> {{item.title}} </div> <div class="name"> {{item.title}} </div>
@ -174,27 +172,17 @@
<style scoped> <style scoped>
.body { .body {
width:100%; width:100%;
margin-top:3.5%; margin-bottom:10px;
} }
.infosContainer {
min-width:350px;
padding-bottom:50px;
border:2px solid black;
font-size:25px;
color:white;
padding:20px;
background-color:rgb(50,50,50);
border-radius:20px;
}
.containerElement{ .containerElement{
justify-content:center; justify-content:center;
display:grid; display:grid;
grid-template-columns:38.8% 38.8% 22.4%; grid-template-columns:350px 350px 200px;
grid-template-areas: grid-template-areas:
"name teacher credits"; "name teacher credits";
column-gap:10px; } column-gap:10px;
}
.name { .name {
grid-area:name; grid-area:name;
@ -212,7 +200,6 @@
} }
.listElement{ .listElement{
min-width:625px;
border:2px solid black; border:2px solid black;
font-size:25px; font-size:25px;
color:white; color:white;
@ -220,7 +207,6 @@
background-color:rgb(50,50,50); background-color:rgb(50,50,50);
border-radius:20px; border-radius:20px;
margin-bottom:10px; margin-bottom:10px;
} }
.modify{ .modify{
@ -269,11 +255,10 @@
} }
.listTitle{ .listTitle{
min-width:380px;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
width:25%; width:400px;
margin-left:auto; margin-left:auto;
margin-right:auto; margin-right:auto;
border:2px solid black; border:2px solid black;
@ -281,8 +266,7 @@
color:white; color:white;
padding:20px; padding:20px;
background-color:rgb(50,50,50); background-color:rgb(50,50,50);
border-radius:20px; border-radius:20px;margin-bottom:10px;
margin-bottom:10px;
button:hover{ button:hover{
opacity:0.8; opacity:0.8;

View File

@ -7,9 +7,9 @@
<script setup> <script setup>
import { ref, reactive } from 'vue' import { ref, reactive } from 'vue'
import { getDiscussions, currentDiscussion, fetchDiscussion, createDiscussion } from '@/rest/msg.js' import { getDiscussions, currentDiscussion, fetchDiscussion } from '@/rest/msg.js'
const discussionsList = ref(await getDiscussions()); const discussionsList = reactive(await getDiscussions());
</script> </script>
@ -17,7 +17,6 @@
<div id="msg"> <div id="msg">
<div id="discList"> <div id="discList">
<div @click="fetchDiscussion(discussion.id)" class="discItem" v-for="discussion in discussionsList" :key="discussion.id">{{ discussion.name }}</div> <div @click="fetchDiscussion(discussion.id)" class="discItem" v-for="discussion in discussionsList" :key="discussion.id">{{ discussion.name }}</div>
<button id="createDiscussion" @click="createDiscussion('New Discussion'); getDiscussions().then(e => { discussionsList = e; })">+</button>
</div> </div>
<div id="discussion"> <div id="discussion">
<h1 id=msgName >{{currentDiscussion.name}}</h1> <h1 id=msgName >{{currentDiscussion.name}}</h1>
@ -51,8 +50,6 @@ div#discList{
border-radius: 10px; border-radius: 10px;
overflow: hidden; overflow: hidden;
padding: 10px; padding: 10px;
display: flex;
flex-direction: column;
} }
@ -69,17 +66,6 @@ div#discList{
border: 1px solid darkorange; border: 1px solid darkorange;
} }
#createDiscussion{
height: 4vh;
margin: 5px;
color: white;
background-color: green;
border-radius: 0 30px 30px 0;
border: none;
font-weight: 900;
font-size: 2em;
}
div#discussion{ div#discussion{
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -210,11 +210,11 @@
<style scoped> <style scoped>
.container{ .container{
min-width:675px;
display:grid; display:grid;
grid-template-columns:10vw 50vw; grid-template-columns:200px 900px;
grid-template-rows:200px auto; grid-template-rows:200px auto;
column-gap:2.7%; column-gap:30px;
row-gap:45px; row-gap:45px;
grid-template-areas: grid-template-areas:
"profilPic globalInfos" "profilPic globalInfos"
@ -222,7 +222,6 @@
} }
.profilPic{ .profilPic{
width:100%;
grid-area:profilPic; grid-area:profilPic;
} }
@ -243,17 +242,13 @@
grid-area:minfos; grid-area:minfos;
} }
.body { .body {
min-width:960px;
width:100%; width:100%;
display:flex; margin-bottom:10px;
align-items:center;
justify-content:center;
margin-top:5%;
} }
.containerElement{ .containerElement{
justify-content:center; justify-content:center;
display:grid; display:grid;
grid-template-columns:38.8% 38.8% 22.4%; grid-template-columns:350px 350px 200px;
grid-template-areas: grid-template-areas:
"name teacher credits"; "name teacher credits";
column-gap:10px; column-gap:10px;
@ -276,11 +271,10 @@
} }
.listTitle{ .listTitle{
min-width:197px;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
width:8vw; width:200px;
margin-left:auto; margin-left:auto;
margin-right:auto; margin-right:auto;
border:2px solid black; border:2px solid black;
@ -292,7 +286,6 @@
} }
.listElement{ .listElement{
min-width:625px;
border:2px solid black; border:2px solid black;
font-size:25px; font-size:25px;
color:white; color:white;
@ -303,7 +296,6 @@
} }
.infosContainer { .infosContainer {
min-width:350px;
padding-bottom:50px; padding-bottom:50px;
border:2px solid black; border:2px solid black;
font-size:25px; font-size:25px;

View File

@ -5,8 +5,8 @@
const users = await getStudents(); const users = await getStudents();
</script> </script>
<template style="margin-top:5%;"> <template>
<div style="display:flex; justify-content:center; " v-for="item in users"> <div v-for="item in users">
<div class="bodu"> <div class="bodu">
<div class="container"> <div class="container">
<div class="status"><a style="margin-left:30px">{{item.status}}</a></div> <div class="status"><a style="margin-left:30px">{{item.status}}</a></div>
@ -25,9 +25,10 @@
height:100px; height:100px;
font-size:30px; font-size:30px;
display:grid; display:grid;
grid-template-columns:21.7% 21.7% 21.7% 21.7% 13.1%; grid-template-columns:250px 250px 250px 250px 150px;
grid-template-areas: grid-template-areas:
"status option surname firstname infos"; "status option surname firstname infos";
column-gap:10px;
} }
@ -41,6 +42,21 @@
align-self:center; align-self:center;
} }
.refuse{
grid-area:refuse;
align-self:center;
}
.titles {
grid-area:titles;
background-color:rgb(215,215,215);
}
.id{
grid-area:id;
margin-left:40px;
align-self:center;
}
.status{ .status{
grid-area:status; grid-area:status;
align-self:center; align-self:center;
@ -65,15 +81,15 @@
button{ button{
font-size:15px; font-size:15px;
height:50px; height:50px;
width:75%; width:100px;
border:none; border:none;
border-radius:20px; border-radius:20px;
} }
.bodu { .bodu {
margin-top:2%; width:100%;
width:66%; margin-bottom:10px;
border:2px solid black; border:2px solid black;
border-radius:9px; border-radius:9px;
background-color:rgb(50,50,50); background-color:rgb(50,50,50);

View File

@ -7,8 +7,8 @@
const users = await getAllUsers(); const users = await getAllUsers();
</script> </script>
<template style="margin-top:5%;"> <template>
<div style="display:flex; justify-content:center; min-width:1140px;" v-for="item in users"> <div v-for="item in users">
<div class="bodu"> <div class="bodu">
<div class="container"> <div class="container">
<div class="role"><a style="margin-left:30px">{{i18n(item.role)}}</a></div> <div class="role"><a style="margin-left:30px">{{i18n(item.role)}}</a></div>
@ -22,20 +22,23 @@
<style scoped> <style scoped>
.container{ .container{
justify-content:center;
align-items:center;
color:white; color:white;
height:100px; height:100px;
font-size:30px; font-size:30px;
display:grid; display:grid;
grid-template-columns:27.7% 27.7% 27.7% 16.9%; grid-template-columns:250px 250px 250px 150px;
grid-template-areas: grid-template-areas:
"role surname firstname infos"; "role surname firstname infos";
column-gap:10px;
} }
.infos { .infos {
grid-area:infos; grid-area:infos;
align-self:center; align-items:center;
} }
.role { .role {
@ -64,18 +67,20 @@
button{ button{
font-size:15px; font-size:15px;
height:50px; height:50px;
width:75%; width:100px;
border:none; border:none;
border-radius:20px; border-radius:20px;
} }
.bodu { .bodu {
margin-top:2%; width:100%;
width:66%; margin-bottom:10px;
border:2px solid black; border:2px solid black;
border-radius:9px; border-radius:9px;
background-color:rgb(50,50,50); background-color:rgb(50,50,50);
} }
</style> </style>

View File

@ -5,19 +5,30 @@
* Description: Messages frontend api consumer * Description: Messages frontend api consumer
*******************************************************/ *******************************************************/
import { restGet, restPost } from './restConsumer.js' import { restGet } from './restConsumer.js'
import { ref } from 'vue' import { ref } from 'vue'
export const currentDiscussion = ref({}); export const currentDiscussion = ref({});
/**
* @return array of
* - id
* - name
* - members
*/
export async function getDiscussions(){ export async function getDiscussions(){
return restGet("/discussions"); return [
{
id: 1,
name: "Discussion#1",
members: [1, 2, 3, 4],
},
{
id: 2,
name: "Discussion#2",
members: [1, 4],
},
{
id: 3,
name: "Discussion#3",
members: [1, 3],
}
]
// return restGet("/discussions");
} }
export async function fetchDiscussion(id){ export async function fetchDiscussion(id){
@ -72,11 +83,3 @@ export async function fetchDiscussion(id){
// currentDiscussion.value = restGet("/discussion/" + id); // currentDiscussion.value = restGet("/discussion/" + id);
} }
export async function createDiscussion(name){
restPost("/discussion", {name: name});
}
export async function invite(id, regNo){
restPost("/discussion/"+ id+ "/invite", {user: regNo});
}