Finitions de la v0

Mise a jour du Readme
This commit is contained in:
Theal0 2021-05-04 16:32:08 +02:00
parent 926c9c1a41
commit 52e54e1c60
12 changed files with 340 additions and 45 deletions

View File

@ -7,24 +7,21 @@ Version mobile de l'application web ScoDoc (v0)
### Fonctionnalités:
- Login
- Choix de département / formation
#### En cours:
- Recherche d'élèves
#### TODO:
- Gestion de semestre
- Affichage des profils étudiants
- Affichage des bulletins de notes
- Recherche d'élèves
- Affichage des absences
## Usage
Modifier le fichier index.js (ligne 8) afin de mettre l'endpoint de l'API ScoDoc
`npm run build` > Crée un dossier `build` avec le contenu du site en prod
#### Dans le cadre d'un serveur web Apache
### Dans le cadre d'un serveur web Apache
Le contenu du dossier `build` doit etre la racine du site web.
Pour éviter des erreurs 404 liées à l'arborescence React:
Pour éviter des erreurs 404 liées à l'arborescence dynamique de React:
```
<Directory ...>
...
@ -38,11 +35,14 @@ Pour éviter des erreurs 404 liées à l'arborescence React:
## Arborescence
### /
### `/`
Pages de login et choix du département
### /Scolarité
### `/DEPT/Scolarité`
Choix de la formation et barre de recherche d'élèves
### /Scolarité/GestionSem
Page principale de la gestion d'un semestre
### `/DEPT/Scolarité/SEMID/GestionSem`
Page principale de la gestion d'un semestre (Liste des élèves, présentation du département, gestion des absences)
### `/DEPT/Scolarite/Etudiant/ETUDID`
Profil de l'étudiant

100
package-lock.json generated
View File

@ -1203,6 +1203,74 @@
"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz",
"integrity": "sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ=="
},
"@emotion/cache": {
"version": "11.1.3",
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.1.3.tgz",
"integrity": "sha512-n4OWinUPJVaP6fXxWZD9OUeQ0lY7DvtmtSuqtRWT0Ofo/sBLCVSgb4/Oa0Q5eFxcwablRKjUXqXtNZVyEwCAuA==",
"requires": {
"@emotion/memoize": "^0.7.4",
"@emotion/sheet": "^1.0.0",
"@emotion/utils": "^1.0.0",
"@emotion/weak-memoize": "^0.2.5",
"stylis": "^4.0.3"
}
},
"@emotion/hash": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
"integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="
},
"@emotion/memoize": {
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz",
"integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ=="
},
"@emotion/react": {
"version": "11.1.5",
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.1.5.tgz",
"integrity": "sha512-xfnZ9NJEv9SU9K2sxXM06lzjK245xSeHRpUh67eARBm3PBHjjKIZlfWZ7UQvD0Obvw6ZKjlC79uHrlzFYpOB/Q==",
"requires": {
"@babel/runtime": "^7.7.2",
"@emotion/cache": "^11.1.3",
"@emotion/serialize": "^1.0.0",
"@emotion/sheet": "^1.0.1",
"@emotion/utils": "^1.0.0",
"@emotion/weak-memoize": "^0.2.5",
"hoist-non-react-statics": "^3.3.1"
}
},
"@emotion/serialize": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz",
"integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==",
"requires": {
"@emotion/hash": "^0.8.0",
"@emotion/memoize": "^0.7.4",
"@emotion/unitless": "^0.7.5",
"@emotion/utils": "^1.0.0",
"csstype": "^3.0.2"
}
},
"@emotion/sheet": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.0.1.tgz",
"integrity": "sha512-GbIvVMe4U+Zc+929N1V7nW6YYJtidj31lidSmdYcWozwoBIObXBnaJkKNDjZrLm9Nc0BR+ZyHNaRZxqNZbof5g=="
},
"@emotion/unitless": {
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
},
"@emotion/utils": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.0.0.tgz",
"integrity": "sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA=="
},
"@emotion/weak-memoize": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz",
"integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA=="
},
"@eslint/eslintrc": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz",
@ -10243,6 +10311,11 @@
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
"memoize-one": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
},
"memory-fs": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
@ -13247,6 +13320,14 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz",
"integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew=="
},
"react-input-autosize": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-3.0.0.tgz",
"integrity": "sha512-nL9uS7jEs/zu8sqwFE5MAPx6pPkNAriACQ2rGLlqmKr2sPGtN7TXTyDdQt4lbNXVx7Uzadb40x8qotIuru6Rhg==",
"requires": {
"prop-types": "^15.5.8"
}
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@ -13464,6 +13545,20 @@
}
}
},
"react-select": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/react-select/-/react-select-4.3.0.tgz",
"integrity": "sha512-SBPD1a3TJqE9zoI/jfOLCAoLr/neluaeokjOixr3zZ1vHezkom8K0A9J4QG9IWDqIDE9K/Mv+0y1GjidC2PDtQ==",
"requires": {
"@babel/runtime": "^7.12.0",
"@emotion/cache": "^11.0.0",
"@emotion/react": "^11.1.1",
"memoize-one": "^5.0.0",
"prop-types": "^15.6.0",
"react-input-autosize": "^3.0.0",
"react-transition-group": "^4.3.0"
}
},
"react-transition-group": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz",
@ -15201,6 +15296,11 @@
}
}
},
"stylis": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.10.tgz",
"integrity": "sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg=="
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",

View File

@ -15,6 +15,7 @@
"react-dom": "^17.0.2",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
"react-select": "^4.3.0",
"web-vitals": "^1.1.1"
},
"scripts": {

View File

@ -12,9 +12,8 @@ class ChoixDept extends Component {
}
componentWillMount() {
let dept = window.location.href.split('/')[3]
let BASE_URL = window.$api_url
fetch(BASE_URL + dept + '/Scolarite/Notes/formsemestre_list?format=json', {
fetch(BASE_URL + 'list_depts?format=json', {
method: 'GET',
verify: false,
credentials: 'include',
@ -26,7 +25,6 @@ class ChoixDept extends Component {
})
).then(res => {
this.setState({ depts: res.data })
console.log(res.data);
}));
}
@ -34,20 +32,15 @@ class ChoixDept extends Component {
return (
<div className="wrapper">
<h1>Choix du département</h1>
<div id="wrapDept">
<Link to="/RT/Scolarite">
Département Réseaux et Télécommunications
</Link>
</div>
<div id="wrapDept">
<Link to="/GC/Scolarite">
Département Génie Civil
</Link>
</div>
<div id="wrapDept">
<h3>Recherche d'élèves</h3>
<SearchStudent/>
</div>
{this.state.depts.map((dept, index) => {
return (
<div id="wrapDept">
<Link to={`/${dept}/Scolarite`}>
Département {dept}
</Link>
</div>
)
},)}
</div>
);
}

View File

@ -1,13 +1,131 @@
import {Component} from "react";
import React, {Component} from "react";
import './Style.css'
import ScoNavBar from "./ScoNavBar";
import {Link} from "react-router-dom";
import {Button} from "react-bootstrap";
class Etudiant extends Component {
constructor(props) {
super(props);
this.state = {
etud: {},
semestres: [],
formation: [],
loaded: false
};
}
componentWillMount() {
let dept = window.location.href.split('/')[3]
let etudid = window.location.href.split('/')[6]
let BASE_URL = window.$api_url
fetch(BASE_URL + dept + '/Scolarite/Notes/etud_info?format=json&etudid=' + etudid, {
method: 'GET',
verify: false,
credentials: 'include',
})
.then(response =>
response.json().then(data => ({
data: data,
status: response.status
})
).then(res => {
// Utilisation de '???' en cas de données vides pour le moment}
for (let propName in res.data) {
if (res.data[propName] === null || res.data[propName] === undefined || res.data[propName] === "") {
res.data[propName] = "???";
}
}
this.setState({ etud: res.data })
this.setState({ formation: res.data.insemestre })
res.data.insemestre.map((sem, index) => {
fetch(BASE_URL + dept + '/Scolarite/Notes/formsemestre_list?format=json&formsemestre_id=' + sem.formsemestre_id, {
method: 'GET',
verify: false,
credentials: 'include',
})
.then(response =>
response.json().then(data => ({
data: data,
status: response.status
}))
).then(res => {
var joined = this.state.semestres.concat(res.data[0]);
this.setState({ semestres: joined, loaded: true })
})
})
})
);
}
render() {
return(
<div>
<ScoNavBar/>
<div>TODO</div>
<div className="wrapper">
<div id="wrapDept">
<h1>{this.state.etud.nomprenom}</h1>
<img
alt={`${this.state.etud.nomprenom}`}
src={`https://scodoc.dev.net/ScoDoc/RT/Scolarite/Notes/${this.state.etud.photo_url}`}
width="102"
height="128"
className="d-inline-block align-top"
/>{' '}
<div id="wrapDept" className="col-sm">
<h3>Informations personnelles</h3>
<div className="col-sm">
<h4>Contact</h4>
Téléphone: {this.state.etud.telephone}<br/>
Mobile: {this.state.etud.telephonemobile}<br/>
Mail étudiant: {this.state.etud.email}<br/>
Mail perso: {this.state.etud.emailperso}<br/>
</div>
<div className="col-sm">
<h4>Lieu de résidence</h4>
Domicile: {this.state.etud.domicile} -
{" " + this.state.etud.codepostaldomicile} {this.state.etud.villedomicile}<br/>
</div>
</div>
<div id="wrapDept" className="col-sm">
<div className="col-sm">
<h4>Parcours</h4>
Bac {this.state.etud.bac} (Spécialité {this.state.etud.specialite}) -
{" " + this.state.etud.nomlycee} ({this.state.etud.codepostallycee} {this.state.etud.villelycee})<br/>
</div>
{this.state.loaded === true &&
<div className="col-sm">
<h4>Formation actuelle</h4>
{this.state.semestres.map((sem, index) => {
return (
<div>
<b>{sem.titreannee}</b><br/>
{sem.date_debut} - {sem.date_fin}
</div>
)
})}
</div>
}
</div>
{/* TODO: Lien vers la gestion des absences
<div id="wrapDept" className="col-sm">
<Link to="">
<div className="col-sm">
Gestion des absences
</div>
</Link>
</div>
*/}
</div>
</div>
<footer className="fixed-bottom">
<div style={{background: "#cccccc", padding: "5px"}}>
<Link to={`/${window.location.href.split('/')[3]}/Scolarite`}>
<Button type="button" className="btn waves-effect waves-light btn-primary">Retour au choix de semestre</Button>
</Link>
</div>
</footer>
</div>
)
}

View File

@ -26,7 +26,7 @@ class GestionSemestre extends Component {
</div>
<footer className="fixed-bottom">
<div style={{background: "#cccccc", padding: "5px"}}>
<Link to="..">
<Link to={`/${window.location.href.split('/')[3]}/Scolarite`}>
<Button type="button" className="btn waves-effect waves-light btn-primary">Retour au choix de semestre</Button>
</Link>
</div>

View File

@ -1,11 +1,88 @@
import React, {Component} from "react";
import '../Style.css'
import Select from 'react-select'
class Absences extends Component {
constructor(props){
super(props)
this.state = {
selectOptions: [],
id: "",
name: '',
abs: []
}
}
componentWillMount() {
let dept = window.location.href.split('/')[3]
let sem = window.location.href.split('/')[5]
let BASE_URL = window.$api_url
fetch(BASE_URL + dept +
'/Scolarite/Notes/groups_view?with_codes=1&format=json&formsemestre_id=' + sem, {
method: 'GET',
verify: false,
credentials: 'include',
})
.then(response =>
response.json().then(data => ({
data: data,
})
).then(res => {
res.data.map((student, index) => {
let joined = this.state.selectOptions.concat({label: student.nom_disp + " " + student.prenom, value: student.etudid});
this.setState({selectOptions: joined})
})
})
);
}
handleSelectChange(e){
this.setState({id:e.value, name:e.label}, () => {this.getData()})
}
getData() {
let dept = window.location.href.split('/')[3]
let BASE_URL = window.$api_url
if (this.state.id !== "") {
fetch(BASE_URL + dept + "/Scolarite/Absences/ListeAbsEtud?format=json&etudid=" + this.state.id, {
method: 'GET',
verify: false,
credentials: 'include',
})
.then(response =>
response.json().then(data => ({
data: data,
})
).then(res => {
this.setState({abs: res.data})
})
);
}
}
render() {
return (
<div className="wrapper">
<h1 id="pageTitle">Gestion des absences</h1>
<Select className="mySelect" options={this.state.selectOptions} onChange={this.handleSelectChange.bind(this)}/>
<div className="col-sm" id="wrapDept">
{this.state.name !== "" && <h4>Absences de {this.state.name}</h4>}
{(this.state.abs.length === 0 && this.state.name !== "") &&
<h6>Aucune absence de l'élève</h6>
}
{this.state.abs.map((abs, index) => {
return (
<div className="col-sm" id="wrapDept">
<h6>{abs.datedmy} | {abs.matin}</h6>
{abs.motif !== "" &&
<span>Motif: {abs.motif }</span>
} {abs.exams !== "" &&
<span>Exam a rattraper: {abs.exams}</span>
}
</div>
)
})}
</div>
</div>
)
}

View File

@ -27,7 +27,6 @@ class Eleves extends Component {
})
).then(res => {
this.setState({ students: res.data});
console.log(res.data)
}));
}

View File

@ -16,7 +16,6 @@ class ScoNavBar extends Component {
let BASE_URL = window.$api_url
fetch(BASE_URL + "acl_users/logout")
.then(res => console.log(res.body))
.then(res => {return <Redirect to="/" />})
.catch(console.log)
}

View File

@ -30,13 +30,13 @@ class Scolarite extends Component {
credentials: 'include',
})
.then(response =>
response.json().then(data => ({
data: data,
status: response.status
})
).then(res => {
this.setState({ semestres: res.data });
}));
response.json().then(data => ({
data: data,
status: response.status
})
).then(res => {
this.setState({ semestres: res.data });
}));
}
getData () {
@ -68,14 +68,14 @@ class Scolarite extends Component {
return (
<div className="col-12" key={index} id="wrapDept">
{sem.etat !== 0 ?
<Link to={`${sem.formsemestre_id}/GestionSem`}>
<Link to={`/${window.location.href.split('/')[3]}/Scolarite/${sem.formsemestre_id}/GestionSem`}>
<h3>{sem.titre} [{sem.modalite}]</h3>
<p>Semestre {sem.semestre_id} - Année {sem.anneescolaire} [{sem.date_debut} - {sem.date_fin}]</p>
</Link>
: null}
</div>
)
},)}
})}
</Card.Body>
</Accordion.Collapse>
</Card>

View File

@ -76,7 +76,7 @@ class SearchStudent extends Component {
<div className="input-group">
<input type="text" id="search" className="form-control" onChange={this.handleChangeSearch}/>
<div className="input-group-append">
<button type="button" className="btn waves-effect waves-light btn-primary" onClick={this.searchStudent}>
<button type="button" className="btn waves-effect waves-light btn-primary" onClick={() => {this.searchStudent()}}>
Rechercher
</button>
</div>

View File

@ -96,6 +96,10 @@ a {
border-radius: 0 0 10px 10px;
}
body {
/* Margin bottom by footer height */
margin-bottom: 100px;
}
/* FORM TYPOGRAPHY*/
@ -181,3 +185,7 @@ input[type=text]::placeholder, input[type=password]::placeholder {
#icon {
width:60%;
}
.mySelect {
min-width: 200px;
}