diff --git a/package.json b/package.json
index 91c907d..1a56e50 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,7 @@
"@fortawesome/free-brands-svg-icons": "^6.1.1",
"@fortawesome/free-solid-svg-icons": "^6.1.1",
"@fortawesome/react-fontawesome": "^0.1.18",
- "@reduxjs/toolkit": "^1.9.7",
+ "@reduxjs/toolkit": "^2.2.3",
"@rjsf/core": "^4.1.1",
"babel-runtime": "^6.26.0",
"bootstrap": "^5.1.3",
@@ -48,6 +48,7 @@
"react-fullscreenable": "^2.5.1-0",
"react-json-view": "^1.21.3",
"react-notification-system": "^0.4.0",
+ "react-redux": "^7.2.8",
"react-rnd": "^10.3.7",
"react-router-dom": "^5.3.1",
"react-svg-pan-zoom": "^3.11.0",
diff --git a/src/index.js b/src/index.js
index c7a7018..a899895 100644
--- a/src/index.js
+++ b/src/index.js
@@ -22,7 +22,7 @@ import Router from "./router";
// Redux
import { Provider } from "react-redux";
-import store from "./redux/store";
+import {store} from "./store/index";
import "bootstrap/dist/css/bootstrap.css";
import "./styles/index.css";
diff --git a/src/pages/login/login-form.js b/src/pages/login/login-form.js
new file mode 100644
index 0000000..77283ac
--- /dev/null
+++ b/src/pages/login/login-form.js
@@ -0,0 +1,110 @@
+/**
+ * This file is part of VILLASweb.
+ *
+ * VILLASweb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * VILLASweb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with VILLASweb. If not, see .
+ ******************************************************************************/
+
+import React, { useState, useEffect } from 'react';
+import { Form, Button, Col } from 'react-bootstrap';
+import RecoverPassword from '../../user/recover-password'
+import { useDispatch } from 'react-redux'
+import { login } from '../../store/userSlice';
+import _ from 'lodash';
+
+import "./login.css";
+
+const LoginForm = (props) => {
+ const [username, setUsername] = useState('');
+ const [password, setPassword] = useState('');
+ const [forgottenPassword, setForgottenPassword] = useState(false)
+
+ //this variable is used to disable login button if either username or password is empty
+ const isInputValid = username !== '' && password !== '';
+
+ const dispatch = useDispatch();
+
+ const loginEvent = (event) => {
+ event.preventDefault();
+ dispatch(login({username, password}))
+ }
+
+ const villasLogin = (
+
+
+
+
+
+
+
+
+ setForgottenPassword(false)} sessionToken={props.sessionToken} />
+
+ );
+
+ if (props.config) {
+ let externalLogin = _.get(props.config, ['authentication', 'external', 'enabled'])
+ let provider = _.get(props.config, ['authentication', 'external', 'provider_name'])
+ let url = _.get(props.config, ['authentication', 'external', 'authorize_url']) + "?rd=/login/complete"
+
+ if (externalLogin && provider && url) {
+ return [
+ villasLogin,
+ ,
+
+ ];
+ }
+ }
+
+ return villasLogin;
+}
+
+export default LoginForm;
diff --git a/src/pages/login/login.css b/src/pages/login/login.css
new file mode 100644
index 0000000..74b8e69
--- /dev/null
+++ b/src/pages/login/login.css
@@ -0,0 +1,42 @@
+/**
+ * This file is part of VILLASweb.
+ *
+ * VILLASweb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * VILLASweb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with VILLASweb. If not, see .
+ ******************************************************************************/
+
+.login-parent {
+ display: flex;
+ max-width: 800px;
+ margin: 30px auto;
+}
+
+.login-welcome {
+ float: right;
+ max-width: 400px;
+ padding: 15px 20px;
+ border-radius: var(--borderradius) 0px 0px var(--borderradius);
+ background-color: #fff;
+ box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
+ 0 9px 18px 0 rgba(0, 0, 0, 0.1);
+}
+
+.login-container {
+ float: left;
+ max-width: 400px;
+ border-radius: 0px var(--borderradius) var(--borderradius) 0px;
+ padding: 15px 20px;
+ background-color: #fff;
+ box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
+ 0 9px 18px 0 rgba(0, 0, 0, 0.1);
+}
\ No newline at end of file
diff --git a/src/pages/login/login.js b/src/pages/login/login.js
new file mode 100644
index 0000000..59af39a
--- /dev/null
+++ b/src/pages/login/login.js
@@ -0,0 +1,80 @@
+/**
+ * This file is part of VILLASweb.
+ *
+ * VILLASweb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * VILLASweb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with VILLASweb. If not, see .
+ ******************************************************************************/
+
+import React, {useEffect, useRef} from 'react';
+import { NavbarBrand } from 'react-bootstrap';
+import NotificationSystem from 'react-notification-system';
+import { Redirect } from 'react-router-dom';
+
+import { useSelector } from 'react-redux';
+
+import LoginForm from './login-form';
+import Header from '../../common/header';
+import NotificationsDataManager from '../../common/data-managers/notifications-data-manager';
+import AppDispatcher from '../../common/app-dispatcher';
+import branding from '../../branding/branding';
+
+import './login.css';
+
+const Login = (props) => {
+
+ const notificationSystem = useRef()
+
+
+ useEffect(() => {
+ NotificationsDataManager.setSystem(notificationSystem);
+
+ console.log("redirected to login", currentUser)
+
+ // load config in case the user goes directly to /login
+ // otherwise it will be loaded in app constructor
+ AppDispatcher.dispatch({
+ type: 'config/load',
+ });
+ }, []);
+
+ const config = null
+
+ const currentUser = useSelector(state => state.user.currentUser);
+ const loginMessage = useSelector(state => state.user.loginMessage);
+
+ return currentUser == null ?
+ (
+
+
+
+
+
+
+ {branding.getWelcome()}
+
+
+
+ Login
+
+
+
+
+
+ {branding.getFooter()}
+
+ )
+ :
+ ();
+}
+
+export default Login
\ No newline at end of file
diff --git a/src/user/logout.js b/src/pages/login/logout.js
similarity index 69%
rename from src/user/logout.js
rename to src/pages/login/logout.js
index 36a49b5..ac5dae6 100644
--- a/src/user/logout.js
+++ b/src/pages/login/logout.js
@@ -15,25 +15,26 @@
* along with VILLASweb. If not, see .
******************************************************************************/
-import React from 'react';
+import React, {useEffect} from 'react';
import { Redirect } from 'react-router-dom';
-import AppDispatcher from '../common/app-dispatcher';
-class Logout extends React.Component {
+import { useDispatch } from 'react-redux'
+import { logout } from '../../store/userSlice';
- componentDidMount() {
- AppDispatcher.dispatch({
- type: 'users/logout'
- });
+const Logout = () => {
- // The Login Store and local storage are deleted automatically
- }
+ const dispatch = useDispatch();
- render() {
- return (
-
- );
- }
+ useEffect(() => {
+ let isMounted = true;
+ if(isMounted) dispatch(logout());
+
+ return () => {isMounted = false};
+ }, []);
+
+ return (
+
+ )
}
export default Logout;
diff --git a/src/router.js b/src/router.js
index a303e20..798093b 100644
--- a/src/router.js
+++ b/src/router.js
@@ -18,23 +18,25 @@
import React from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import App from "./app";
-import Login from "./user/login";
-import Logout from "./user/logout";
+import Login from "./pages/login/login";
+import Logout from "./pages/login/logout";
import LoginComplete from "./user/login-complete";
class Root extends React.Component {
render() {
return (
-
-
-
+
+
+
+
+
+
-
);
}
}
diff --git a/src/store/index.js b/src/store/index.js
new file mode 100644
index 0000000..106544b
--- /dev/null
+++ b/src/store/index.js
@@ -0,0 +1,26 @@
+/**
+ * This file is part of VILLASweb.
+ *
+ * VILLASweb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * VILLASweb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with VILLASweb. If not, see .
+ ******************************************************************************/
+
+import { configureStore } from "@reduxjs/toolkit"
+import userReducer from './userSlice'
+
+export const store = configureStore({
+ reducer: {
+ user: userReducer
+ },
+ devTools: true
+})
\ No newline at end of file
diff --git a/src/store/userSlice.js b/src/store/userSlice.js
new file mode 100644
index 0000000..ce346d3
--- /dev/null
+++ b/src/store/userSlice.js
@@ -0,0 +1,100 @@
+/**
+ * This file is part of VILLASweb.
+ *
+ * VILLASweb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * VILLASweb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with VILLASweb. If not, see .
+ ******************************************************************************/
+
+import {createSlice, createAsyncThunk} from '@reduxjs/toolkit'
+
+import RestAPI from '../common/api/rest-api';
+import ICDataDataManager from '../ic/ic-data-data-manager';
+
+const userSlice = createSlice({
+ name: 'login',
+ initialState: {currentUser: null, currentToken: null, isLoading: false, loginMessage: ''},
+ extraReducers: (builder) => {
+ builder
+ .addCase(login.pending, (state, action) => {
+ state.isLoading = true
+ })
+ .addCase(login.fulfilled, (state, action) => {
+ state.isLoading = false
+ state.currentUser = action.payload.user
+ state.currentToken = action.payload.token
+
+ localStorage.setItem('currentUser', JSON.stringify(action.payload.user));
+ localStorage.setItem('token', action.payload.token);
+ })
+ .addCase(login.rejected, (state, action) => {
+ state.loginMessage = 'Wrong credentials! Please try again.'
+ })
+ .addCase(logout.pending, (state) => {
+ state.currentUser = null
+ state.currentToken = null
+ })
+ .addCase(loginExternal.pending, (state, action) => {
+ state.isLoading = true
+ })
+ .addCase(loginExternal.fulfilled, (state, action) => {
+ state.isLoading = false
+ state.currentUser = action.payload.user
+ state.currentToken = action.payload.token
+
+ localStorage.setItem('currentUser', JSON.stringify(action.payload.user));
+ localStorage.setItem('token', action.payload.token);
+ })
+ }
+})
+
+export const login = createAsyncThunk(
+ 'user/login',
+ async (userData, thunkAPI) => {
+ try {
+ const res = await RestAPI.post('/api/v2/authenticate/internal', userData)
+
+ return {user: res.user, token: res.token}
+ } catch(error) {
+ console.log('Error while trying to log in: ', error)
+ return thunkAPI.rejectWithValue(error)
+ }
+ }
+)
+
+export const loginExternal = createAsyncThunk(
+ 'user/loginExternal',
+ async (data, thunkAPI) => {
+ try {
+ const res = await RestAPI.post(this.makeURL('/authenticate/external'), null, null, 60000)
+
+ return {user: res.user, token: res.token}
+ } catch(error) {
+ console.log('Error while trying to log in externally: ', error)
+ return thunkAPI.rejectWithValue(error)
+ }
+ }
+)
+
+export const logout = createAsyncThunk(
+ 'user/logout',
+ async () => {
+ // disconnect from all infrastructure components
+ ICDataDataManager.closeAll();
+ //remove token and current user from local storage
+ localStorage.clear();
+
+ console.log("logged out")
+ }
+)
+
+export default userSlice.reducer
\ No newline at end of file
diff --git a/src/styles/app.css b/src/styles/app.css
index ccf111e..e8dcefb 100644
--- a/src/styles/app.css
+++ b/src/styles/app.css
@@ -266,38 +266,6 @@ a:active {
background-color: white;
}
-/**
- * Login form
- */
- .login-parent {
- display: flex;
- max-width: 800px;
-
- margin: 30px auto;
- }
-
- .login-welcome {
- float: right;
- max-width: 400px;
- padding: 15px 20px;
- border-radius: var(--borderradius) 0px 0px var(--borderradius);
-
- background-color: #fff;
- box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
- 0 9px 18px 0 rgba(0, 0, 0, 0.1);
-}
-
-.login-container {
- float: left;
- max-width: 400px;
- border-radius: 0px var(--borderradius) var(--borderradius) 0px;
- padding: 15px 20px;
-
- background-color: #fff;
- box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
- 0 9px 18px 0 rgba(0, 0, 0, 0.1);
-}
-
hr {
margin-top: 1rem;
margin-bottom: 1rem;
diff --git a/src/user/login-form.js b/src/user/login-form.js
deleted file mode 100644
index c54bf6f..0000000
--- a/src/user/login-form.js
+++ /dev/null
@@ -1,129 +0,0 @@
-/**
- * This file is part of VILLASweb.
- *
- * VILLASweb is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * VILLASweb is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with VILLASweb. If not, see .
- ******************************************************************************/
-
-import React, { Component } from 'react';
-import { Form, Button, Col } from 'react-bootstrap';
-import RecoverPassword from './recover-password'
-import AppDispatcher from '../common/app-dispatcher';
-import _ from 'lodash';
-
-
-class LoginForm extends Component {
- constructor(props) {
- super(props);
-
- this.state = {
- username: '',
- password: '',
- forgottenPassword: false,
- disableLogin: true
- }
- }
-
- login(event) {
- // prevent from submitting the form since we send an action
- event.preventDefault();
-
- // send login action
- AppDispatcher.dispatch({
- type: 'users/login',
- username: this.state.username,
- password: this.state.password
- });
- }
-
- handleChange(event) {
- let disableLogin = this.state.disableLogin;
-
- if (event.target.id === 'username') {
- disableLogin = this.state.password.length === 0 || event.target.value.length === 0;
- } else if (event.target.id === 'password') {
- disableLogin = this.state.username.length === 0 || event.target.value.length === 0;
- }
-
- this.setState({ [event.target.id]: event.target.value, disableLogin });
- }
-
- openRecoverPassword() {
- this.setState({ forgottenPassword: true });
- }
-
- closeRecoverPassword() {
- this.setState({ forgottenPassword: false });
- }
-
- villaslogin() {
- return (
-
- Username
-
-
-
-
-
-
-
-
- this.closeRecoverPassword()} sessionToken={this.props.sessionToken} />
-
- );
- }
-
- render() {
- let villasLogin = this.villaslogin();
-
- if (this.props.config) {
- let externalLogin = _.get(this.props.config, ['authentication', 'external', 'enabled'])
- let provider = _.get(this.props.config, ['authentication', 'external', 'provider_name'])
- let url = _.get(this.props.config, ['authentication', 'external', 'authorize_url']) + "?rd=/login/complete"
-
- if (externalLogin && provider && url) {
- return [
- villasLogin,
- ,
-
- ];
- }
- }
-
- return villasLogin;
- }
-}
-
-export default LoginForm;
diff --git a/src/user/login.js b/src/user/login.js
deleted file mode 100644
index a91fa89..0000000
--- a/src/user/login.js
+++ /dev/null
@@ -1,93 +0,0 @@
-/**
- * This file is part of VILLASweb.
- *
- * VILLASweb is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * VILLASweb is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with VILLASweb. If not, see .
- ******************************************************************************/
-
-import React from 'react';
-import { Container } from 'flux/utils';
-import { NavbarBrand } from 'react-bootstrap';
-import NotificationSystem from 'react-notification-system';
-import { Redirect } from 'react-router-dom';
-
-import LoginForm from './login-form';
-import Header from '../common/header';
-import NotificationsDataManager from '../common/data-managers/notifications-data-manager';
-import LoginStore from './login-store'
-import AppDispatcher from '../common/app-dispatcher';
-import branding from '../branding/branding';
-
-
-class Login extends React.Component {
- constructor(props) {
- super(props);
-
- }
-
- static getStores() {
- return [LoginStore]
- }
-
- static calculateState(prevState, props) {
- // We need to work with the login store here to trigger the re-render upon state change after login
- // Upon successful login, the token and currentUser are stored in the local storage as strings
- return {
- loginMessage: LoginStore.getState().loginMessage,
- token: LoginStore.getState().token,
- currentUser: LoginStore.getState().currentUser,
- config: LoginStore.getState().config,
- }
- }
-
- componentDidMount() {
- NotificationsDataManager.setSystem(this.refs.notificationSystem);
-
- // load config in case the user goes directly to /login
- // otherwise it will be loaded in app constructor
- AppDispatcher.dispatch({
- type: 'config/load',
- });
- }
-
- render() {
-
- if (this.state.currentUser !== null && this.state.currentUser !== "") {
- return ();
- }
-
- return (
-