Release v1.1
Optimisations mineures Documentation, réorganisation et nettoyage du code
This commit is contained in:
parent
7569934ce8
commit
65cb895a1c
16
README.md
16
README.md
@ -2,7 +2,7 @@
|
||||
|
||||
## Description
|
||||
|
||||
Version mobile de l'application web ScoDoc (v1)
|
||||
Version mobile de l'application web ScoDoc (v1.1)
|
||||
|
||||
### Fonctionnalités:
|
||||
- Login
|
||||
@ -12,15 +12,15 @@ Version mobile de l'application web ScoDoc (v1)
|
||||
- Affichage des bulletins de notes
|
||||
- Gestion des absences
|
||||
|
||||
### Installation
|
||||
|
||||
Le contenu du dossier `build` devrait se situer dans `Scodoc\static\mobile`.
|
||||
|
||||
Le dossier `build` est disponible dans les [releases](https://scodoc.org/git/theal/ScoDocMobile/releases) du projet ou à générer depuis les sources.
|
||||
|
||||
## Usage
|
||||
|
||||
Modifier le fichier index.js (ligne 8) afin de mettre l'endpoint de l'API ScoDoc
|
||||
|
||||
`npm install` > Crée un dossier `build` avec le contenu du site en prod
|
||||
|
||||
### Dans le cadre d'un serveur web Apache
|
||||
|
||||
Le contenu du dossier `build` doit etre la racine du site web.
|
||||
`npm run build` > Génère un dossier `build` avec le contenu du site en prod.
|
||||
|
||||
## Arborescence
|
||||
|
||||
|
2424
package-lock.json
generated
2424
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -8,12 +8,17 @@
|
||||
"@testing-library/react": "^11.2.6",
|
||||
"@testing-library/user-event": "^12.8.3",
|
||||
"bootstrap": "^4.6.0",
|
||||
"i": "^0.3.6",
|
||||
"js-cookie": "^2.2.1",
|
||||
"js-cookies": "^1.0.4",
|
||||
"jsdoc": "^3.6.7",
|
||||
"jsdoc-to-markdown": "^7.0.1",
|
||||
"postcss": "^8.2.15",
|
||||
"react": "^17.0.2",
|
||||
"react-bootstrap": "^1.5.2",
|
||||
"react-device-detect": "^1.17.0",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-lazy-load-image-component": "^1.5.1",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "4.0.3",
|
||||
"react-select": "^4.3.0",
|
||||
@ -23,7 +28,8 @@
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
"eject": "react-scripts eject",
|
||||
"docs": "jsdoc2md ./src > docs.md"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 5.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.4 KiB |
@ -1,7 +1,9 @@
|
||||
import React, {Component} from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import './Style.css'
|
||||
import {getJson} from './Request'
|
||||
|
||||
/** Page de choix du département */
|
||||
class ChoixDept extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@ -12,21 +14,18 @@ class ChoixDept extends Component {
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.getData()
|
||||
}
|
||||
|
||||
/**
|
||||
* Recupère la liste des départements depuis l'API
|
||||
*/
|
||||
getData() {
|
||||
let BASE_URL = window.$api_url
|
||||
fetch(BASE_URL + 'list_depts?format=json', {
|
||||
method: 'GET',
|
||||
verify: false,
|
||||
credentials: 'include',
|
||||
})
|
||||
.then(response =>
|
||||
// Traitement des données JSON
|
||||
response.json().then(data => ({
|
||||
data: data,
|
||||
status: response.status
|
||||
})
|
||||
).then(res => {
|
||||
this.setState({ depts: res.data })
|
||||
}));
|
||||
getJson(BASE_URL + 'list_depts?format=json')
|
||||
.then(res => {
|
||||
this.setState({ depts: res.data })
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -38,7 +37,7 @@ class ChoixDept extends Component {
|
||||
{this.state.depts.map((dept, index) => {
|
||||
return (
|
||||
<div className="col-sm" key={index} id="wrapDept">
|
||||
<Link to={`/ScoDoc/static/mobile/${dept}/Scolarite`}>
|
||||
<Link to={`/${dept}/Scolarite`}>
|
||||
Département {dept}
|
||||
</Link>
|
||||
</div>
|
||||
|
@ -1,7 +1,9 @@
|
||||
import React, {Component} from "react";
|
||||
import './Style.css'
|
||||
import ScoNavBar from "./ScoNavBar";
|
||||
import {getJson} from "./Request";
|
||||
|
||||
/** Page d'information d'un étudiant' */
|
||||
class Etudiant extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@ -17,43 +19,28 @@ class Etudiant extends Component {
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
let dept = window.location.href.split('/')[6]
|
||||
let etudid = window.location.href.split('/')[9]
|
||||
this.getData()
|
||||
}
|
||||
|
||||
/**
|
||||
* Recupère les données de l'étudiant depuis l'API
|
||||
*/
|
||||
getData() {
|
||||
let dept = window.location.href.split('/')[7]
|
||||
let etudid = window.location.href.split('/')[10]
|
||||
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 =>
|
||||
// Traitement des données JSON
|
||||
response.json().then(data => ({
|
||||
data: data,
|
||||
status: response.status
|
||||
})
|
||||
).then(res => {
|
||||
this.setState({ etud: res.data })
|
||||
this.setState({ formation: res.data.insemestre })
|
||||
// Recuperation des données de semestres pour la formation d'un étudiant
|
||||
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',
|
||||
getJson(BASE_URL + dept + '/Scolarite/Notes/etud_info?format=json&etudid=' + etudid)
|
||||
.then(res => {
|
||||
this.setState({ etud: res.data, formation: res.data.insemestre })
|
||||
// Recuperation des données de semestres pour la formation d'un étudiant
|
||||
res.data.insemestre.map((sem) => {
|
||||
getJson(BASE_URL + dept + '/Scolarite/Notes/formsemestre_list?format=json&formsemestre_id=' + sem.formsemestre_id)
|
||||
.then(res => {
|
||||
let joined = this.state.semestres.concat(res.data[0]);
|
||||
this.setState({ semestres: joined, loaded: true })
|
||||
})
|
||||
.then(response =>
|
||||
response.json().then(data => ({
|
||||
// Traitement des données JSON
|
||||
data: data,
|
||||
status: response.status
|
||||
}))
|
||||
).then(res => {
|
||||
let joined = this.state.semestres.concat(res.data[0]);
|
||||
this.setState({ semestres: joined, loaded: true })
|
||||
})
|
||||
})
|
||||
})
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -65,7 +52,7 @@ class Etudiant extends Component {
|
||||
<h1>{this.state.etud.nomprenom}</h1>
|
||||
<img
|
||||
alt={`${this.state.etud.nomprenom}`}
|
||||
src={`/ScoDoc/${window.location.href.split('/')[6]}/Scolarite/Notes/${this.state.etud.photo_url}`}
|
||||
src={`/ScoDoc/${window.location.href.split('/')[7]}/Scolarite/Notes/${this.state.etud.photo_url}`}
|
||||
width="102"
|
||||
height="128"
|
||||
className="d-inline-block align-top"
|
||||
@ -114,7 +101,7 @@ class Etudiant extends Component {
|
||||
{this.state.loaded === true &&
|
||||
<div className="col-sm">
|
||||
<h4>Formation actuelle</h4>
|
||||
{this.state.semestres.map((sem, index) => {
|
||||
{this.state.semestres.map((sem) => {
|
||||
return (
|
||||
<div>
|
||||
<b>{sem.titreannee}</b><br/>
|
||||
@ -125,15 +112,6 @@ class Etudiant extends Component {
|
||||
</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>
|
||||
</div>
|
||||
|
@ -2,11 +2,13 @@ import React, {Component} from "react";
|
||||
import {Tabs, Tab} from "react-bootstrap"
|
||||
import Accueil from "./GestionSemestre/Accueil";
|
||||
import Absences from "./GestionSemestre/Absences";
|
||||
import Eleves from "./GestionSemestre/Eleves";
|
||||
import Etudiants from "./GestionSemestre/Etudiants";
|
||||
import ScoNavBar from "./ScoNavBar";
|
||||
import Bulletin from "./GestionSemestre/Bulletin";
|
||||
import Select from "react-select";
|
||||
import {getJson} from "./Request";
|
||||
|
||||
/** Page de gestion du semestre */
|
||||
class GestionSemestre extends Component {
|
||||
constructor(props){
|
||||
super(props)
|
||||
@ -18,29 +20,24 @@ class GestionSemestre extends Component {
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
// Recuperation de la liste des semestres
|
||||
let dept = window.location.href.split('/')[6]
|
||||
let sem = window.location.href.split('/')[8]
|
||||
this.getData()
|
||||
}
|
||||
|
||||
/**
|
||||
* Recupère la liste des étudiants inscrits au semestre pour le Select depuis l'API
|
||||
*/
|
||||
getData() {
|
||||
let dept = window.location.href.split('/')[7]
|
||||
let sem = window.location.href.split('/')[9]
|
||||
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 =>
|
||||
// Traitement des données JSON
|
||||
response.json().then(data => ({
|
||||
data: data,
|
||||
})
|
||||
).then(res => {
|
||||
// Création d'une liste pour le select
|
||||
res.data.map((student, index) => {
|
||||
let joined = this.state.selectOptions.concat({label: student.nom_disp + " " + student.prenom, value: student.etudid});
|
||||
this.setState({selectOptions: joined})
|
||||
})
|
||||
getJson(BASE_URL + dept + '/Scolarite/Notes/groups_view?with_codes=1&format=json&formsemestre_id=' + sem)
|
||||
.then(res => {
|
||||
// Création d'une liste pour le select
|
||||
res.data.map((student) => {
|
||||
let joined = this.state.selectOptions.concat({label: student.nom_disp + " " + student.prenom, value: student.etudid});
|
||||
this.setState({selectOptions: joined})
|
||||
})
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
handleSelectChange(e){
|
||||
@ -71,8 +68,8 @@ class GestionSemestre extends Component {
|
||||
<Tab eventKey="Bulletin" title="Bulletins">
|
||||
<Bulletin id={this.state.id} name={this.state.name}/>
|
||||
</Tab>
|
||||
<Tab eventKey="Eleves" title="Eleves">
|
||||
<Eleves />
|
||||
<Tab eventKey="Etud" title="Etudiants">
|
||||
<Etudiants />
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
@ -4,7 +4,9 @@ import '../Style.css'
|
||||
import SaisieAbs from "./Absences/SaisieAbs";
|
||||
import SupprAbs from "./Absences/SupprAbs";
|
||||
import JustAbs from "./Absences/JustAbs";
|
||||
import {getJson} from "../Request";
|
||||
|
||||
/** Page de gestion des absences */
|
||||
class Absences extends Component {
|
||||
constructor(props){
|
||||
super(props)
|
||||
@ -24,7 +26,7 @@ class Absences extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
// Recuperation des données en cas de changement de props (dans notre cas, changement d'élève)
|
||||
// Recuperation des données en cas de changement de props (dans notre cas, changement d'étudiant.e)
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.id !== this.props.id) {
|
||||
this.getData();
|
||||
@ -36,6 +38,11 @@ class Absences extends Component {
|
||||
if (this.props.id !== "") {this.getData()}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gère l'ouverture des Modal
|
||||
* @param key {String} - Correspond au type de modal [isOpen, isDelOpen, isJustOpen]
|
||||
* @param data {Object} - Objet contenant les données à transmettre
|
||||
*/
|
||||
openModal(key, data) {
|
||||
this.setState({[key]: true}, () => setTimeout(() => {
|
||||
this.setState({[key]: false})
|
||||
@ -43,52 +50,19 @@ class Absences extends Component {
|
||||
if (data) {this.setState({data: data})}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recupère les données d'absences depuis l'API
|
||||
*/
|
||||
getData() {
|
||||
this.getAbs()
|
||||
this.getAbsJust()
|
||||
}
|
||||
|
||||
getAbs() {
|
||||
let dept = window.location.href.split('/')[6]
|
||||
let dept = window.location.href.split('/')[7]
|
||||
let BASE_URL = window.$api_url
|
||||
if (this.state.id !== "") {
|
||||
fetch(BASE_URL + dept + "/Scolarite/Absences/ListeAbsEtud?format=json&absjust_only=0&etudid=" + this.props.id, {
|
||||
method: 'GET',
|
||||
verify: false,
|
||||
credentials: 'include',
|
||||
})
|
||||
.then(response =>
|
||||
// Traitement des données JSON
|
||||
response.json().then(data => ({
|
||||
data: data,
|
||||
})
|
||||
).then(res => {
|
||||
// Recuperation de la liste des absences
|
||||
this.setState({abs: res.data})
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
getAbsJust() {
|
||||
let dept = window.location.href.split('/')[6]
|
||||
let BASE_URL = window.$api_url
|
||||
if (this.state.id !== "") {
|
||||
fetch(BASE_URL + dept + "/Scolarite/Absences/ListeAbsEtud?format=json&absjust_only=1&etudid=" + this.props.id, {
|
||||
method: 'GET',
|
||||
verify: false,
|
||||
credentials: 'include',
|
||||
})
|
||||
.then(response =>
|
||||
// Traitement des données JSON
|
||||
response.json().then(data => ({
|
||||
data: data,
|
||||
})
|
||||
).then(res => {
|
||||
// Recuperation de la liste des absences
|
||||
this.setState({absjust: res.data})
|
||||
})
|
||||
);
|
||||
// Recuperation des absences non-justifiées
|
||||
getJson(BASE_URL + dept + "/Scolarite/Absences/ListeAbsEtud?format=json&absjust_only=0&etudid=" + this.props.id)
|
||||
.then(res => this.setState({abs: res.data}));
|
||||
// Recuperation des absences justifiées
|
||||
getJson(BASE_URL + dept + "/Scolarite/Absences/ListeAbsEtud?format=json&absjust_only=1&etudid=" + this.props.id)
|
||||
.then(res => this.setState({absjust: res.data}));
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,9 +93,9 @@ class Absences extends Component {
|
||||
</Button>
|
||||
</h4>
|
||||
{(this.state.abs.length === 0 && this.state.absjust.length === 0 && this.props.name !== "") &&
|
||||
<h6>Aucune absence de l'élève</h6>
|
||||
<h6>Aucune absence de l'étudiant.e</h6>
|
||||
}
|
||||
{this.state.abs.map((abs, index) => {
|
||||
{this.state.abs.map((abs) => {
|
||||
return (
|
||||
<div className="col-sm" id="wrapDept">
|
||||
<Col>
|
||||
@ -153,7 +127,7 @@ class Absences extends Component {
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
{this.state.absjust.map((abs, index) => {
|
||||
{this.state.absjust.map((abs) => {
|
||||
return (
|
||||
<div className="col-sm" id="wrapDept">
|
||||
<Col>
|
||||
|
@ -1,6 +1,8 @@
|
||||
import React, {Component} from "react";
|
||||
import {Button, Col, Form, Modal} from "react-bootstrap";
|
||||
import {post} from "../../Request";
|
||||
|
||||
/** Module de justification des absences */
|
||||
class JustAbs extends Component {
|
||||
constructor(props){
|
||||
super(props)
|
||||
@ -28,20 +30,22 @@ class JustAbs extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie une requête POST a l'API
|
||||
* @param data {String} - Données à envoyer sous la forme param1=val1¶m2=val2...
|
||||
*/
|
||||
postData(data) {
|
||||
let dept = window.location.href.split('/')[6]
|
||||
let dept = window.location.href.split('/')[7]
|
||||
let BASE_URL = window.$api_url
|
||||
fetch(BASE_URL + dept + "/Scolarite/Absences/doJustifAbsence", {
|
||||
method: 'POST',
|
||||
verify: false,
|
||||
credentials: 'include',
|
||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
body: data
|
||||
})
|
||||
post(BASE_URL + dept + "/Scolarite/Absences/doJustifAbsence", data)
|
||||
// Fermeture du modal
|
||||
this.setState({isOpen: false})
|
||||
}
|
||||
|
||||
/**
|
||||
* Gestion des données du formulaire
|
||||
* @param e {Event}
|
||||
*/
|
||||
onFormSubmit = e => {
|
||||
// Traitement du formulaire
|
||||
// Empeche le bouton de rediriger ou actualiser la page
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React, {Component} from "react";
|
||||
import {Button, Col, Form, Modal} from "react-bootstrap";
|
||||
import Absences from '../Absences'
|
||||
import {post} from "../../Request";
|
||||
|
||||
/** Module de saisie des absences */
|
||||
class SaisieAbs extends Component {
|
||||
constructor(props){
|
||||
super(props)
|
||||
@ -25,6 +26,10 @@ class SaisieAbs extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gestion des données du formulaire
|
||||
* @param e {Event}
|
||||
*/
|
||||
onFormSubmit = e => {
|
||||
// Traitement du formulaire
|
||||
// Empeche le bouton de rediriger ou actualiser la page
|
||||
@ -57,16 +62,14 @@ class SaisieAbs extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie une requête POST a l'API
|
||||
* @param data {String} - Données à envoyer sous la forme param1=val1¶m2=val2...
|
||||
*/
|
||||
postData(data) {
|
||||
let dept = window.location.href.split('/')[6]
|
||||
let dept = window.location.href.split('/')[7]
|
||||
let BASE_URL = window.$api_url
|
||||
fetch(BASE_URL + dept + "/Scolarite/Absences/doSignaleAbsence", {
|
||||
method: 'POST',
|
||||
verify: false,
|
||||
credentials: 'include',
|
||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
body: data
|
||||
})
|
||||
post(BASE_URL + dept + "/Scolarite/Absences/doSignaleAbsence", data)
|
||||
.then(response => {
|
||||
if (response.status === 200) {
|
||||
// Fermeture du modal
|
||||
|
@ -1,6 +1,8 @@
|
||||
import React, {Component} from "react";
|
||||
import {Button, Modal} from "react-bootstrap";
|
||||
import {post} from "../../Request";
|
||||
|
||||
/** Module de suppression des absences */
|
||||
class SupprAbs extends Component {
|
||||
constructor(props){
|
||||
super(props)
|
||||
@ -22,18 +24,16 @@ class SupprAbs extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie une requête POST a l'API
|
||||
* @param data {String} - Données à envoyer sous la forme param1=val1¶m2=val2...
|
||||
*/
|
||||
postData() {
|
||||
let dept = window.location.href.split('/')[6]
|
||||
let dept = window.location.href.split('/')[7]
|
||||
let BASE_URL = window.$api_url
|
||||
let data = "datedebut=" + this.props.data.date + "&datefin=" + this.props.data.date +
|
||||
"&demijournee=" + this.props.data.demijournee + "&etudid=" + this.state.etudid
|
||||
fetch(BASE_URL + dept + "/Scolarite/Absences/doAnnuleAbsence", {
|
||||
method: 'POST',
|
||||
verify: false,
|
||||
credentials: 'include',
|
||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
body: data
|
||||
})
|
||||
post(BASE_URL + dept + "/Scolarite/Absences/doAnnuleAbsence", data)
|
||||
// Fermeture du modal
|
||||
this.setState({isOpen: false})
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import React, {Component} from "react";
|
||||
import '../Style.css'
|
||||
import {getJson} from "../Request";
|
||||
|
||||
/** Page d'accueil de la gestion du semestre */
|
||||
class Accueil extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@ -10,22 +12,20 @@ class Accueil extends Component {
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
let dept = window.location.href.split('/')[6]
|
||||
let sem = window.location.href.split('/')[8]
|
||||
this.getData()
|
||||
}
|
||||
|
||||
/**
|
||||
* Recupère les données du semestre selectionné depuis l'API
|
||||
*/
|
||||
getData() {
|
||||
let dept = window.location.href.split('/')[7]
|
||||
let sem = window.location.href.split('/')[9]
|
||||
let BASE_URL = window.$api_url
|
||||
fetch(BASE_URL + dept +
|
||||
'/Scolarite/Notes/formsemestre_list?format=json&formsemestre_id=' + sem, {
|
||||
method: 'GET',
|
||||
verify: false,
|
||||
credentials: 'include',
|
||||
})
|
||||
.then(response =>
|
||||
response.json().then(data => ({
|
||||
data: data,
|
||||
})
|
||||
).then(res => {
|
||||
this.setState({ semestre: res.data[0]});
|
||||
}));
|
||||
getJson(BASE_URL + dept + '/Scolarite/Notes/formsemestre_list?format=json&formsemestre_id=' + sem)
|
||||
.then(res => {
|
||||
this.setState({ semestre: res.data[0]});
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -1,7 +1,9 @@
|
||||
import React, {Component} from "react";
|
||||
import {Table, Button, Dropdown} from "react-bootstrap"
|
||||
import '../Style.css'
|
||||
import {get, getJson} from "../Request";
|
||||
|
||||
/** Page de présentation des bulletins étudiants */
|
||||
class Bulletin extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@ -13,58 +15,49 @@ class Bulletin extends Component {
|
||||
this.getData = this.getData.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recupère les données de bulletin depuis l'API
|
||||
*/
|
||||
getData() {
|
||||
let dept = window.location.href.split('/')[6]
|
||||
let sem = window.location.href.split('/')[8]
|
||||
let dept = window.location.href.split('/')[7]
|
||||
let sem = window.location.href.split('/')[9]
|
||||
let BASE_URL = window.$api_url
|
||||
fetch(BASE_URL + dept + '/Scolarite/Notes/formsemestre_bulletinetud?formsemestre_id=' +
|
||||
sem +'&etudid=' + this.props.id +'&format=json', {
|
||||
method: 'GET',
|
||||
verify: false,
|
||||
credentials: 'include',
|
||||
})
|
||||
.then(response =>
|
||||
response.json().then(data => ({
|
||||
data: data,
|
||||
status: response.status
|
||||
})
|
||||
).then(res => {
|
||||
// Recuperation des données du bulletin
|
||||
this.setState({ bltn: res.data }, () => {
|
||||
// Recuperation d'un tableau CodeUE | NomUE
|
||||
let ls = {}
|
||||
for (let elm in this.state.bltn.decision_ue) {
|
||||
elm = this.state.bltn.decision_ue[elm]
|
||||
ls[elm.acronyme] = elm.titre
|
||||
}
|
||||
this.setState({datue: ls}, () => {
|
||||
// Marquage du bulletin comme "chargé"
|
||||
this.setState({loaded: true})
|
||||
})
|
||||
getJson(BASE_URL + dept + '/Scolarite/Notes/formsemestre_bulletinetud?formsemestre_id=' + sem +'&etudid=' +
|
||||
this.props.id +'&format=json')
|
||||
.then(res => {
|
||||
// Recuperation des données du bulletin
|
||||
this.setState({ bltn: res.data }, () => {
|
||||
// Recuperation d'un tableau CodeUE | NomUE
|
||||
let ls = {}
|
||||
for (let elm in this.state.bltn.decision_ue) {
|
||||
elm = this.state.bltn.decision_ue[elm]
|
||||
ls[elm.acronyme] = elm.titre
|
||||
}
|
||||
this.setState({datue: ls}, () => {
|
||||
// Marquage du bulletin comme "chargé"
|
||||
this.setState({loaded: true})
|
||||
})
|
||||
})
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
// Recuperation du bulletin au format PDF
|
||||
/**
|
||||
* Recupère les données de bulletin en tant que "blob" pour un PDF depuis l'API
|
||||
*/
|
||||
getPdf() {
|
||||
let BASE_URL = window.$api_url
|
||||
let dept = window.location.href.split('/')[6]
|
||||
let sem = window.location.href.split('/')[8]
|
||||
fetch( BASE_URL + dept + "/Scolarite/Notes/formsemestre_bulletinetud?" +
|
||||
"formsemestre_id=" + sem + "&etudid=" + this.props.id + "&format=pdf&version=selectedevals", {
|
||||
method: 'GET',
|
||||
verify: false,
|
||||
credentials: 'include',
|
||||
})
|
||||
.then( res => res.blob() )
|
||||
.then( blob => {
|
||||
get(BASE_URL + dept + "/Scolarite/Notes/formsemestre_bulletinetud?formsemestre_id=" + sem +
|
||||
"&etudid=" + this.props.id + "&format=pdf&version=selectedevals")
|
||||
.then(res => res.blob())
|
||||
.then(blob => {
|
||||
let file = window.URL.createObjectURL(blob);
|
||||
window.location.assign(file);
|
||||
});
|
||||
}
|
||||
|
||||
// Recuperation des données en cas de changement de props (dans notre cas, changement d'élève)
|
||||
// Recuperation des données en cas de changement de props (dans notre cas, changement d'étudiant.e)
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.id !== this.props.id) {
|
||||
this.getData();
|
||||
@ -107,7 +100,7 @@ class Bulletin extends Component {
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{this.state.bltn.ue.map((ue, index) => {
|
||||
{this.state.bltn.ue.map((ue) => {
|
||||
return (
|
||||
<tbody>
|
||||
<tr className="ueRow">
|
||||
@ -126,7 +119,7 @@ class Bulletin extends Component {
|
||||
</Dropdown>
|
||||
</td>
|
||||
</tr>
|
||||
{ue.module.map((mod, index) => {
|
||||
{ue.module.map((mod) => {
|
||||
return (
|
||||
<tr>
|
||||
<td colSpan="3">{mod.titre.replace("'", "'")}</td>
|
||||
|
@ -1,57 +1,57 @@
|
||||
import React, {Component} from "react";
|
||||
import {LazyLoadImage} from 'react-lazy-load-image-component';
|
||||
import '../Style.css'
|
||||
import {Link} from "react-router-dom";
|
||||
import {getJson} from "../Request";
|
||||
|
||||
class Eleves extends Component {
|
||||
/** Page de présentation des étudiants inscrits au semestre */
|
||||
class Etudiants extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
// Liste des élèves inscrits au semestre
|
||||
// Liste des étudiants inscrits au semestre
|
||||
students: [],
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
let dept = window.location.href.split('/')[6]
|
||||
let sem = window.location.href.split('/')[8]
|
||||
this.getData()
|
||||
}
|
||||
|
||||
/**
|
||||
* Recupère la liste des étudiants inscrits au semestre depuis l'API
|
||||
*/
|
||||
getData() {
|
||||
let dept = window.location.href.split('/')[7]
|
||||
let sem = window.location.href.split('/')[9]
|
||||
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 =>
|
||||
// Traitement des données JSON
|
||||
response.json().then(data => ({
|
||||
data: data,
|
||||
})
|
||||
).then(res => {
|
||||
// Gestion des données sous forme de tableau a deux colonnes
|
||||
const dat = res.data.map((x,i) => {
|
||||
return i % 2 === 0 ? res.data.slice(i, i+2) : null;
|
||||
}).filter(x => x != null);
|
||||
this.setState({ students: dat});
|
||||
}));
|
||||
getJson(BASE_URL + dept + '/Scolarite/Notes/groups_view?with_codes=1&format=json&formsemestre_id=' + sem)
|
||||
.then(res => {
|
||||
// Gestion des données sous forme de tableau a deux colonnes
|
||||
const dat = res.data.map((x,i) => {
|
||||
return i % 2 === 0 ? res.data.slice(i, i+2) : null;
|
||||
}).filter(x => x != null);
|
||||
this.setState({ students: dat});
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="wrapper">
|
||||
<h1 id="pageTitle">Liste des élèves</h1>
|
||||
<h1 id="pageTitle">Liste des étudiants</h1>
|
||||
<div className="container">
|
||||
{this.state.students.map((students, index) => {
|
||||
{this.state.students.map((students) => {
|
||||
// Creation du tableau de deux colonnes
|
||||
return (
|
||||
<div className="row justify-content-center">
|
||||
{students.map((etud, index) => {
|
||||
return (
|
||||
<div className="col" key={index} id="wrapDept">
|
||||
<Link to={`/ScoDoc/static/mobile/${window.location.href.split('/')[6]}/Scolarite/Etudiant/${etud.etudid}`}>
|
||||
<Link to={`/${window.location.href.split('/')[7]}/Scolarite/Etudiant/${etud.etudid}`}>
|
||||
{/* Recuperation de la photo de l'etudiant */}
|
||||
<img
|
||||
<LazyLoadImage
|
||||
alt={`${etud.nom_disp} ${etud.prenom}`}
|
||||
src={`/ScoDoc/RT/Scolarite/Notes/get_photo_image?etudid=${etud.etudid}`}
|
||||
src={`/ScoDoc/${window.location.href.split('/')[7]}/Scolarite/Notes/get_photo_image?etudid=${etud.etudid}`}
|
||||
width="102"
|
||||
height="128"
|
||||
className="d-inline-block align-top"
|
||||
@ -70,4 +70,4 @@ class Eleves extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
export default Eleves
|
||||
export default Etudiants
|
@ -3,7 +3,9 @@ import { isMobile } from 'react-device-detect';
|
||||
import './Style.css'
|
||||
import ChoixDept from "./ChoixDept";
|
||||
import ScoNavBar from "./ScoNavBar";
|
||||
import {getLogin} from "./Request";
|
||||
|
||||
/** Page de Login */
|
||||
class Login extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@ -25,6 +27,10 @@ class Login extends Component {
|
||||
this.setState({ pass: e.target.value });
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifie la validité des identifiants depuis l'API
|
||||
* @param e {event}
|
||||
*/
|
||||
checkCredentials(e) {
|
||||
e.preventDefault();
|
||||
|
||||
@ -33,15 +39,7 @@ class Login extends Component {
|
||||
|
||||
let BASE_URL = window.$api_url
|
||||
|
||||
fetch(BASE_URL, {
|
||||
method: 'GET',
|
||||
verify: false,
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Authorization': 'Basic ' + btoa(login + ":" + pass)
|
||||
},
|
||||
})
|
||||
getLogin(BASE_URL, login, pass)
|
||||
.then(res => {
|
||||
this.setState({ status: res["status"] });
|
||||
})
|
||||
|
67
src/ScoDoc/Request.js
Normal file
67
src/ScoDoc/Request.js
Normal file
@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Lance une requête GET a l'URL donnée en paramètre et retourne une Promise.
|
||||
* @param url {String} - URL de la requête
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export function get(url) {
|
||||
return (
|
||||
fetch(url, {
|
||||
method: 'GET',
|
||||
verify: false,
|
||||
credentials: 'include',
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Lance une requête GET a l'URL donnée en paramètre et retourne une Promise.
|
||||
* Dans ce cas particulier, on ajoute un header d'authentification.
|
||||
* @param url {String} - URL de la requête
|
||||
* @param login {String} - Identifiant
|
||||
* @param pass {String} - Mot de passe
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export function getLogin(url, login, pass) {
|
||||
return (
|
||||
fetch(url, {
|
||||
method: 'GET',
|
||||
verify: false,
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Authorization': 'Basic ' + btoa(login + ":" + pass)
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Lance une requête GET a l'URL donnée en paramètre et retourne les données JSON d'une Promise.
|
||||
* @param url {String} - URL de la requête
|
||||
* @returns {Promise<{data: any}>}
|
||||
*/
|
||||
export function getJson(url) {
|
||||
return get(url)
|
||||
.then(response => response.json()
|
||||
.then(data => ({data: data}))
|
||||
.then(res => {return res})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lance une requête POST a l'URL donnée en paramètre et retourne une Promise.
|
||||
* @param url {String} - URL de la requête
|
||||
* @param data {String} - Données de la requête au format "param1=val1¶m2=val2..."
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export function post(url, data) {
|
||||
return (
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
verify: false,
|
||||
credentials: 'include',
|
||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
body: data
|
||||
})
|
||||
)
|
||||
}
|
@ -3,6 +3,7 @@ import {Nav, Navbar, Button, Container} from 'react-bootstrap'
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import './Style.css'
|
||||
|
||||
/** Barre de navigation */
|
||||
class ScoNavBar extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@ -42,7 +43,7 @@ class ScoNavBar extends Component {
|
||||
</Navbar.Collapse>
|
||||
</Container>
|
||||
{this.state.logout === true &&
|
||||
<Redirect push to={window.$api_url + 'static/mobile/'}/>
|
||||
<Redirect push to="/"/>
|
||||
}
|
||||
</Navbar>
|
||||
)
|
||||
|
@ -4,7 +4,9 @@ import './Style.css'
|
||||
import ScoNavBar from "./ScoNavBar";
|
||||
import SearchStudent from './SearchStudent'
|
||||
import {Accordion, Card, Button} from 'react-bootstrap'
|
||||
import {getJson} from "./Request";
|
||||
|
||||
/** Page de choix du semestre */
|
||||
class Scolarite extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@ -14,34 +16,25 @@ class Scolarite extends Component {
|
||||
toast: false
|
||||
};
|
||||
this.dismissToast = this.dismissToast.bind(this);
|
||||
this.getData = this.getData.bind(this);
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
let dept = window.location.href.split('/')[6]
|
||||
let BASE_URL = window.$api_url
|
||||
fetch(BASE_URL + dept + '/Scolarite/Notes/formsemestre_list?format=json', {
|
||||
method: 'GET',
|
||||
verify: false,
|
||||
credentials: 'include',
|
||||
})
|
||||
.then(response =>
|
||||
response.json().then(data => ({
|
||||
data: data,
|
||||
status: response.status
|
||||
})
|
||||
).then(res => {
|
||||
this.setState({ semestres: res.data });
|
||||
}));
|
||||
this.getData()
|
||||
}
|
||||
|
||||
/**
|
||||
* Recupère la liste des semestres depuis l'API
|
||||
*/
|
||||
getData () {
|
||||
return this.state.semestres
|
||||
let dept = window.location.href.split('/')[7]
|
||||
let BASE_URL = window.$api_url
|
||||
getJson(BASE_URL + dept + '/Scolarite/Notes/formsemestre_list?format=json')
|
||||
.then(res => {
|
||||
this.setState({ semestres: res.data });
|
||||
})
|
||||
}
|
||||
|
||||
dismissToast() {
|
||||
this.setState({toast: false})
|
||||
}
|
||||
dismissToast = () => this.setState({toast: false})
|
||||
|
||||
render() {
|
||||
return (
|
||||
@ -63,15 +56,16 @@ class Scolarite extends Component {
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
{this.state.semestres.map((sem, index) => {
|
||||
if(sem.etat === "1")
|
||||
return (
|
||||
<div className="col-sm" key={index} id="wrapDept">
|
||||
<Link to={`/ScoDoc/static/mobile/${window.location.href.split('/')[6]}/Scolarite/${sem.formsemestre_id}/GestionSem`}>
|
||||
<h4>{sem.titre} [{sem.modalite}]</h4>
|
||||
<p>Semestre {sem.semestre_id} - Année {sem.anneescolaire} [{sem.date_debut} - {sem.date_fin}]</p>
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
if (sem.etat === "1") {
|
||||
return (
|
||||
<div className="col-sm" key={index} id="wrapDept">
|
||||
<Link to={`/${window.location.href.split('/')[7]}/Scolarite/${sem.formsemestre_id}/GestionSem`}>
|
||||
<h4>{sem.titre} [{sem.modalite}]</h4>
|
||||
<p>Semestre {sem.semestre_id} - Année {sem.anneescolaire} [{sem.date_debut} - {sem.date_fin}]</p>
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
@ -80,22 +74,23 @@ class Scolarite extends Component {
|
||||
</Card>
|
||||
<Card>
|
||||
<Card.Header>
|
||||
<Accordion.Toggle as={Button} variant="link" eventKey="0">
|
||||
<Accordion.Toggle as={Button} variant="link" eventKey="1">
|
||||
Semestres passés
|
||||
</Accordion.Toggle>
|
||||
</Card.Header>
|
||||
<Accordion.Collapse eventKey="1">
|
||||
<Card.Body>
|
||||
{this.state.semestres.map((sem, index) => {
|
||||
if(sem.etat !== "1")
|
||||
if (sem.etat !== "1") {
|
||||
return (
|
||||
<div className="col-12" key={index} id="wrapDept">
|
||||
<Link to={`/ScoDoc/static/mobile/${window.location.href.split('/')[6]}/Scolarite/${sem.formsemestre_id}/GestionSem`}>
|
||||
<Link to={`/${window.location.href.split('/')[7]}/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>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})}
|
||||
</Card.Body>
|
||||
</Accordion.Collapse>
|
||||
|
@ -1,7 +1,9 @@
|
||||
import React, {Component} from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Row, Col} from "react-bootstrap"
|
||||
import {getJson} from "./Request";
|
||||
|
||||
/** Module de recherche d'étudiant */
|
||||
class SearchStudent extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@ -20,46 +22,47 @@ class SearchStudent extends Component {
|
||||
this.setState({ search: e.target.value });
|
||||
}
|
||||
|
||||
searchStudent() {
|
||||
let dept = window.location.href.split('/')[6]
|
||||
/**
|
||||
* Lance une recherche de l'étudiant depuis l'API
|
||||
* @param search {String} - Texte recherché
|
||||
*/
|
||||
searchStudent(search) {
|
||||
let dept = window.location.href.split('/')[7]
|
||||
let BASE_URL = window.$api_url
|
||||
fetch(BASE_URL + dept +
|
||||
'/Scolarite/Notes/search_etud_by_name?term=' + this.state.search +'&format=json', {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
})
|
||||
.then(response =>
|
||||
response.json().then(data => ({data: data}))
|
||||
.then(res => {
|
||||
this.setState({ students: res.data });
|
||||
|
||||
if (this.state.students.length === 0) {
|
||||
this.setState({search_status: 1, toast: true});
|
||||
} else {
|
||||
this.setState({search_status: 2, toast: false});
|
||||
}
|
||||
}))
|
||||
getJson(BASE_URL + dept + '/Scolarite/Notes/search_etud_by_name?term=' + search +'&format=json')
|
||||
.then(res => {
|
||||
this.setState({ students: res.data });
|
||||
if (this.state.students.length === 0) {
|
||||
this.setState({search_status: 1, toast: true});
|
||||
} else {
|
||||
this.setState({search_status: 2, toast: false});
|
||||
}
|
||||
})
|
||||
this.setState({searched: true})
|
||||
}
|
||||
|
||||
/**
|
||||
* Presentation du résultat
|
||||
* @returns {JSX.Element} - Resultat au format JSX
|
||||
*/
|
||||
result() {
|
||||
if (this.state.toast === true) {
|
||||
return (
|
||||
<div id="wrapDept">
|
||||
Aucun élève trouvé
|
||||
Aucun étudiant trouvé
|
||||
</div>
|
||||
)
|
||||
} else if (this.state.search_status === 2) {
|
||||
return (
|
||||
<Col>
|
||||
{this.state.students.map((student, index) => {
|
||||
{this.state.students.map((student) => {
|
||||
return (
|
||||
<Row id="wrapDept">
|
||||
<Link to={`/ScoDoc/static/mobile/${window.location.href.split('/')[6]}/Scolarite/Etudiant/${student.value}`}>
|
||||
<Link to={`/${window.location.href.split('/')[7]}/Scolarite/Etudiant/${student.value}`}>
|
||||
<span>{student.label}</span>
|
||||
</Link>
|
||||
</Row>
|
||||
);
|
||||
)
|
||||
})}
|
||||
</Col>
|
||||
)
|
||||
@ -72,7 +75,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(this.state.search)}}>
|
||||
Rechercher
|
||||
</button>
|
||||
</div>
|
||||
|
12
src/main.js
12
src/main.js
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import {Switch, Route} from 'react-router-dom';
|
||||
import {HashRouter, Route} from 'react-router-dom';
|
||||
import Scolarite from './ScoDoc/Scolarite.js'
|
||||
import Login from './ScoDoc/Login'
|
||||
import GestionSemestre from "./ScoDoc/GestionSemestre";
|
||||
@ -8,12 +8,18 @@ import Etudiant from "./ScoDoc/Etudiant";
|
||||
|
||||
const Main = () => {
|
||||
return (
|
||||
<Switch>
|
||||
/*<HashRouter>
|
||||
<Route exact path='/ScoDoc/static/mobile' component={Login}/>
|
||||
<Route exact path='/ScoDoc/static/mobile/:DEPT/Scolarite' component={Scolarite}/>
|
||||
<Route exact path='/ScoDoc/static/mobile/:DEPT/Scolarite/Etudiant/:EtudId' component={Etudiant}/>
|
||||
<Route exact path='/ScoDoc/static/mobile/:DEPT/Scolarite/:SEM/GestionSem' component={GestionSemestre}/>
|
||||
</Switch>
|
||||
</HashRouter>*/
|
||||
<HashRouter>
|
||||
<Route exact path='/' component={Login}/>
|
||||
<Route exact path='/:DEPT/Scolarite' component={Scolarite}/>
|
||||
<Route exact path='/:DEPT/Scolarite/Etudiant/:EtudId' component={Etudiant}/>
|
||||
<Route exact path='/:DEPT/Scolarite/:SEM/GestionSem' component={GestionSemestre}/>
|
||||
</HashRouter>
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user