Initial commit

This commit is contained in:
Theal0 2021-04-29 14:18:26 +02:00
parent 3611d14ac1
commit 5a44ae90ca
16 changed files with 1650 additions and 134 deletions

3
.gitignore vendored
View File

@ -21,3 +21,6 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.idea/
node_modules/

1170
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -6,8 +6,14 @@
"@testing-library/jest-dom": "^5.11.10",
"@testing-library/react": "^11.2.6",
"@testing-library/user-event": "^12.8.3",
"antd": "^4.15.2",
"bootstrap": "^4.6.0",
"js-cookie": "^2.2.1",
"js-cookies": "^1.0.4",
"react": "^17.0.2",
"react-bootstrap": "^1.5.2",
"react-dom": "^17.0.2",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
"web-vitals": "^1.1.1"
},
@ -34,5 +40,13 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"babel-plugin-import": "^1.13.3",
"css-loader": "^5.2.4",
"less": "^4.1.1",
"less-loader": "^8.1.1",
"less-vars-to-js": "^1.3.0",
"style-loader": "^2.0.0"
}
}

View File

@ -9,6 +9,13 @@
name="description"
content="Web site created using create-react-app"
/>
<!-- Including the bootstrap css via CDN -->
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
crossorigin="anonymous"
>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
@ -24,7 +31,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>Scodoc Mobile</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

View File

@ -1,38 +0,0 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

View File

@ -1,25 +0,0 @@
import logo from './logo.svg';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;

View File

@ -1,8 +0,0 @@
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

25
src/ScoDoc/ChoixDept.js Normal file
View File

@ -0,0 +1,25 @@
import {Link} from "react-router-dom";
import React, {Component} from "react";
import './Style.css'
class ChoixDept extends Component {
render() {
return (
<div className="wrapper">
<h1>Choix du département</h1>
<div id="wrapDept">
<Link to="/Scolarite">
Département Réseaux et Télécommunications
</Link>
</div>
<div id="wrapDept">
<Link to="/Scolarite">
Département Génie Civil
</Link>
</div>
</div>
);
}
}
export default ChoixDept

75
src/ScoDoc/Login.js Normal file
View File

@ -0,0 +1,75 @@
import React, {Component} from "react";
import {Link} from "react-router-dom";
import './Style.css'
import ChoixDept from "./ChoixDept";
class Login extends Component {
constructor(props) {
super(props);
this.state = {
login: "",
pass: "",
status: 0
};
this.handleChangeLogin = this.handleChangeLogin.bind(this);
this.handleChangePass = this.handleChangePass.bind(this);
this.checkCredentials = this.checkCredentials.bind(this)
}
handleChangeLogin(e) {
this.setState({ login: e.target.value });
}
handleChangePass(e) {
this.setState({ pass: e.target.value });
}
checkCredentials(e) {
let login = this.state.login
let pass = this.state.pass
fetch('https://scodoc.dev.net/ScoDoc/RT/Scolarite', {
method: 'GET',
verify: false,
credentials: 'include',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic ' + btoa(login + ":" + pass)
},
})
.then(res => {
this.setState({ status: res["status"] });
console.log("Cookie (get): " + res.headers.get('Set-Cookie'));
console.log("Cookie (doc): " + document.cookie);
})
.catch(console.log)
e.preventDefault()
}
render() {
return (
<div className="wrapper">
{(this.state.status !== 0 && this.state.status !==200) &&
<div id="errorMsg">
<h2 id="loginTitle">{ "⚠️" } Login ou mot de passe incorrect</h2>
</div>
}
{this.state.status !== 200 &&
<div id="formContent">
<h2 id="loginTitle">Connexion a ScoDoc</h2>
<form>
<input type="text" id="login" placeholder="Identifiant" onChange={this.handleChangeLogin}/>
<input type="password" id="password" placeholder="Mot de passe" onChange={this.handleChangePass}/>
<button type="submit" value="Log In" onClick={this.checkCredentials}>Log in</button>
</form>
</div>
}{this.state.status === 200 &&
<ChoixDept/>
}
</div>
)
}
}
export default Login

39
src/ScoDoc/ScoNavBar.js Normal file
View File

@ -0,0 +1,39 @@
import React, {Component} from "react";
import {Link, Redirect} from "react-router-dom";
import './Style.css'
import {Nav, Navbar, Button} from 'react-bootstrap'
class ScoNavBar extends Component {
constructor(props) {
super(props);
this.state = {
login: {},
semestre: {},
};
}
logout() {
fetch("https://scodoc.dev.net/ScoDoc/acl_users/logout")
.then(res => console.log(res.body))
.then(res => {return <Redirect to="/" />})
.catch(console.log)
}
render() {
return (
<Navbar bg="light" expand="sm">
<Navbar.Brand>ScoDoc</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="ml-auto">
<Link to="/">
<Button variant="primary" onClick={() => {this.logout()}}>Déconnexion</Button>
</Link>
</Nav>
</Navbar.Collapse>
</Navbar>
)
}
}
export default ScoNavBar

154
src/ScoDoc/Scolarite.js Normal file
View File

@ -0,0 +1,154 @@
import React, {Component} from "react";
import {Link, Redirect} from "react-router-dom";
import './Style.css'
import ScoNavBar from "./ScoNavBar";
import {Accordion, Card, Button, Toast} from 'react-bootstrap'
class Scolarite extends Component {
constructor(props) {
super(props);
this.state = {
search: "",
// Status possibles:
// 0: Vide - 1: Pas de resultat - 2: Plusieurs resultats
search_status: 0,
semestres: [],
students: [],
toast: false
};
this.handleChangeSearch = this.handleChangeSearch.bind(this);
this.searchStudent = this.searchStudent.bind(this);
this.dismissToast = this.dismissToast.bind(this);
this.getData = this.getData.bind(this);
}
/* getJsonData () {
return require('..\\json\\sems.json');
} */
componentWillMount() {
fetch('https://scodoc.dev.net/ScoDoc/RT/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 });
}));
}
getData () {
return this.state.semestres
}
dismissToast() {
this.setState({toast: false})
}
handleChangeSearch(e) {
this.setState({ search: e.target.value });
}
searchStudent(e) {
fetch('https://scodoc.dev.net/ScoDoc/RT/Scolarite/Notes/search_etud_by_name?format=json', {
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({term: this.state.search})
})
.then(response =>
response.json().then(data => ({
data: data,
status: response.status
})
).then(res => {
this.setState({ students: res.data });
console.log(this.state.students)
}));
let result = 0
console.log(this.state.students)
if (result === 0) {
this.setState({search_status: 1, toast: true});
} else if (result === 1) {
// Redirection sur la page de l'étudiant
return <Redirect to="/" />
} else {
this.setState({search_status: 2});
// Liste des élèves trouvés avec liens
}
}
render() {
return (
<div>
<ScoNavBar/>
<section>
<h1 id="pageTitle">Scolarité</h1>
</section>
<Accordion defaultActiveKey="0">
<Card>
<Card.Header>
<Accordion.Toggle as={Button} variant="link" eventKey="0">
Choix du semestre
</Accordion.Toggle>
</Card.Header>
<Accordion.Collapse eventKey="0">
<Card.Body>
{this.state.semestres.map((sem, index) => (
<div className="col-12" key={index} id="wrapDept">
<Link to="/">
<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>
</Card>
<Card>
<Card.Header>
<Accordion.Toggle as={Button} variant="link" eventKey="1">
Recherche étudiant
</Accordion.Toggle>
</Card.Header>
<Accordion.Collapse eventKey="1">
<Card.Body>
<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}>
Rechercher
</button>
</div>
</div>
{this.state.toast === true &&
<Toast onClick={this.dismissToast} style={{opacity: "70%"}}>
<Toast.Body>Aucun élève trouvé</Toast.Body>
</Toast>
}
</Card.Body>
</Accordion.Collapse>
</Card>
</Accordion>
<footer className="fixed-bottom">
<div style={{background: "#cccccc", padding: "5px"}}>
<Link to="/">
<Button type="button" className="btn waves-effect waves-light btn-primary">Retour au choix de département</Button>
</Link>
</div>
</footer>
</div>
)
}
}
export default Scolarite;

View File

183
src/ScoDoc/Style.css Normal file
View File

@ -0,0 +1,183 @@
/* BASIC */
html {
background-color: #56baed;
}
body {
font-family: "Poppins", sans-serif;
height: 100vh;
}
a {
color: #92badd;
display:inline-block;
text-decoration: none;
font-weight: 400;
}
#pageTitle {
text-align: center;
font-size: 25px;
font-weight: 600;
color: #aaaaaa;
}
#loginTitle {
text-align: center;
font-size: 20px;
font-weight: 600;
text-transform: uppercase;
display:inline-block;
margin: 40px 8px 10px 8px;
color: #cccccc;
}
/* STRUCTURE */
.wrapper {
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
width: 100%;
min-height: 100%;
padding: 10px;
}
#formContent {
-webkit-border-radius: 10px 10px 10px 10px;
border-radius: 10px 10px 10px 10px;
background: #fff;
padding: 30px;
width: 90%;
max-width: 450px;
position: relative;
padding: 0px;
-webkit-box-shadow: 0 30px 60px 0 rgba(0,0,0,0.3);
box-shadow: 0 30px 60px 0 rgba(0,0,0,0.3);
text-align: center;
}
#errorMsg {
-webkit-border-radius: 10px 10px 10px 10px;
border-radius: 10px 10px 10px 10px;
background: #fff;
width: 90%;
max-width: 450px;
position: relative;
padding: 0px;
-webkit-box-shadow: 0 30px 60px 0 rgba(0,0,0,0.3);
box-shadow: 0 30px 60px 0 rgba(0,0,0,0.3);
text-align: center;
margin-bottom: 10px;
padding-bottom: 20px;
}
#wrapDept {
-webkit-border-radius: 10px 10px 10px 10px;
border-radius: 10px 10px 10px 10px;
background: #fff;
width: 90%;
max-width: 450px;
position: relative;
-webkit-box-shadow: 0 10px 20px 0 rgba(0,0,0,0.3);
box-shadow: 0 10px 20px 0 rgba(0,0,0,0.3);
text-align: center;
margin: 10px;
padding: 10px;
}
#formFooter {
background-color: #f6f6f6;
border-top: 1px solid #dce8f1;
padding: 25px;
text-align: center;
-webkit-border-radius: 0 0 10px 10px;
border-radius: 0 0 10px 10px;
}
/* FORM TYPOGRAPHY*/
input[type=button], button[type=submit], input[type=reset] {
background-color: #56baed;
border: none;
color: white;
padding: 15px 80px;
text-align: center;
text-decoration: none;
display: inline-block;
text-transform: uppercase;
font-size: 13px;
-webkit-box-shadow: 0 10px 30px 0 rgba(95,186,233,0.4);
box-shadow: 0 10px 30px 0 rgba(95,186,233,0.4);
-webkit-border-radius: 5px 5px 5px 5px;
border-radius: 5px 5px 5px 5px;
margin: 5px 20px 40px 20px;
-webkit-transition: all 0.3s ease-in-out;
-moz-transition: all 0.3s ease-in-out;
-ms-transition: all 0.3s ease-in-out;
-o-transition: all 0.3s ease-in-out;
transition: all 0.3s ease-in-out;
}
input[type=button]:hover, button[type=submit]:hover, input[type=reset]:hover {
background-color: #39ace7;
}
input[type=button]:active, button[type=submit]:active, input[type=reset]:active {
-moz-transform: scale(0.95);
-webkit-transform: scale(0.95);
-o-transform: scale(0.95);
-ms-transform: scale(0.95);
transform: scale(0.95);
}
input[type=text], input[type=password] {
background-color: #f6f6f6;
border: none;
color: #0d0d0d;
padding: 15px 32px;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 5px;
width: 85%;
border: 2px solid #f6f6f6;
-webkit-transition: all 0.5s ease-in-out;
-moz-transition: all 0.5s ease-in-out;
-ms-transition: all 0.5s ease-in-out;
-o-transition: all 0.5s ease-in-out;
transition: all 0.5s ease-in-out;
-webkit-border-radius: 5px 5px 5px 5px;
border-radius: 5px 5px 5px 5px;
}
input[type=text]:focus, input[type=password]:focus {
background-color: #fff;
border-bottom: 2px solid #5fbae9;
}
input[type=text]::placeholder, input[type=password]::placeholder {
color: #cccccc;
}
.underlineHover:hover {
color: #0d0d0d;
}
.underlineHover:hover:after{
width: 100%;
}
/* OTHERS */
*:focus {
outline: none;
}
#icon {
width:60%;
}

View File

@ -1,13 +0,0 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

View File

@ -1,13 +1,14 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import {BrowserRouter} from "react-router-dom";
import reportWebVitals from './reportWebVitals';
import Main from "./main";
import 'bootstrap/dist/css/bootstrap.min.css'
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
<BrowserRouter>
<Main />
</BrowserRouter>,
document.getElementById('root')
);

17
src/main.js Normal file
View File

@ -0,0 +1,17 @@
import React from 'react';
import {Switch, Route} from 'react-router-dom';
import Scolarite from './ScoDoc/Scolarite.js'
import ChoixDept from './ScoDoc/ChoixDept.js'
import Login from './ScoDoc/Login'
const Main = () => {
return (
<Switch>
<Route exact path='/' component={Login}/>
<Route exact path='/Scolarite' component={Scolarite}/>
</Switch>
);
}
export default Main;