From a41df6f8d017941b006fe22f3905d3fd8f31ce70 Mon Sep 17 00:00:00 2001
From: Victor VOGT <v.vogt@etu.unistra.fr>
Date: Mon, 1 Nov 2021 17:39:55 +0100
Subject: [PATCH] :sparkles: profile editing fonctionne + debut transactions

---
 frontend/src/App.js                          |   7 ++
 frontend/src/actions/Bank.actions.js         |  83 ++++++++++++++
 frontend/src/containers/Bank.container.js    | 108 +++++++++++++++++++
 frontend/src/containers/Profile.container.js |  29 ++---
 frontend/src/containers/Users.container.js   |   2 -
 frontend/src/icons/transaction.png           | Bin 0 -> 3808 bytes
 frontend/src/reducers/Bank.reducer.js        |  27 +++++
 7 files changed, 241 insertions(+), 15 deletions(-)
 create mode 100644 frontend/src/actions/Bank.actions.js
 create mode 100644 frontend/src/containers/Bank.container.js
 create mode 100644 frontend/src/icons/transaction.png
 create mode 100644 frontend/src/reducers/Bank.reducer.js

diff --git a/frontend/src/App.js b/frontend/src/App.js
index ad122e1..0e3278b 100644
--- a/frontend/src/App.js
+++ b/frontend/src/App.js
@@ -6,6 +6,7 @@ import Users from './containers/Users.container';
 import SignUp from './containers/SignUp.container';
 import Login from './containers/Login.container';
 import Profile from './containers/Profile.container';
+import Bank from './containers/Bank.container';
 import { Navbar, Nav } from 'react-bootstrap';
 import {userService} from "./services/authentication.service";
 import * as loginActions from './actions/Login.actions';
@@ -54,6 +55,11 @@ class App extends Component {
                       Profile
                     </Nav.Link>
                   }
+                  { this.props.loginState.loggedIn &&
+                    <Nav.Link as={Link} to="/bank/">
+                      Bank
+                    </Nav.Link>
+                  }
                   { this.props.loginState.loggedIn &&
                   <Nav.Link as={Link} to="/" onClick={(e) => {e.preventDefault(); this.props.logoutRequest()}} >
                     Log out
@@ -74,6 +80,7 @@ class App extends Component {
               <Route path="/signup/" component={SignUp} />
               <Route path="/login/" component={Login} />
               <PrivateRoute path="/profile/" component={Profile} />
+              <PrivateRoute path="/bank/" component={Bank} />
             </div>
           </Router>
     );
diff --git a/frontend/src/actions/Bank.actions.js b/frontend/src/actions/Bank.actions.js
new file mode 100644
index 0000000..f33ffc4
--- /dev/null
+++ b/frontend/src/actions/Bank.actions.js
@@ -0,0 +1,83 @@
+import {userService} from "../services/authentication.service";
+
+export const profileDataFetchSuccess = (data) => {
+    return {
+        type:'PROFILE_DATA_REQ_SUCCESS',
+        data
+    }
+}
+
+export const profileDataFetchFailure = () => {
+    return {
+        type:'PROFILE_DATA_REQ_FAILURE'
+    }
+}
+
+export const updateUserSuccess = () => {
+    return {
+        type:'UPDATE_USER_SUCCESS',
+    }
+}
+
+export const updateUserFailure = (error) => {
+    return {
+        type:'UPDATE_USER_FAILURE',
+        message:error,
+    }
+}
+
+export const fetchUserData = () => {
+    return async (dispatch) => {
+        const response = await fetch( "/api/me", {
+            method: 'GET',
+            headers: {
+                'Authorization': userService.getToken()
+            }
+        })
+
+        if(response.ok){
+            response.json().then(data => {
+                console.log(data);
+                dispatch(profileDataFetchSuccess(data));
+            }).catch(err => dispatch(profileDataFetchFailure(err)));
+        }
+        else{
+            response.json().then(error => {
+                dispatch(profileDataFetchFailure(error));
+            }).catch(err => dispatch(profileDataFetchFailure(err)));
+        }
+
+        return response;
+    }
+}
+
+export const changeUserData = (data) => {
+    return async (dispatch) => {
+        const response = await fetch( "/api/me/update", {
+            method: 'POST',
+            headers: {
+                'Authorization': userService.getToken(),
+                'Accept': 'application/json',
+                'Content-Type': 'application/json'
+            },
+            body: JSON.stringify(data),
+        })
+
+        if(response.ok){
+            dispatch(updateUserSuccess());
+        }
+        else{
+            response.json().then(error => {
+                dispatch(updateUserFailure(error));
+            }).catch(err => dispatch(updateUserFailure(err)));
+        }
+
+        return response;
+    }
+}
+
+export const reinitializeState = () => {
+    return {
+        type:'REINITIALIZE_STATE'
+    }
+}
diff --git a/frontend/src/containers/Bank.container.js b/frontend/src/containers/Bank.container.js
new file mode 100644
index 0000000..778d36e
--- /dev/null
+++ b/frontend/src/containers/Bank.container.js
@@ -0,0 +1,108 @@
+import { connect } from 'react-redux';
+import React from 'react';
+import logo from '../icons/transaction.png';
+import { withRouter } from 'react-router-dom'
+import { Table, Accordion, Button, Card, Image, Container, Row, Col } from "react-bootstrap";
+import * as bankActions from "../actions/Bank.actions";
+
+export class Bank extends React.Component {
+
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      readOnly: true
+    };
+
+    this.switchToEditionMode = this.switchToEditionMode.bind(this); 
+  }
+
+  componentWillMount(){
+    this.props.fetchUserData();
+    this.props.reinitializeState();
+  }
+
+  componentDidUpdate(prevProps) {
+    if (prevProps.state !== this.props.state) {
+      this.setState({
+        readOnly: this.props.state.updateUserSuccess
+      });
+    }
+  }
+
+  handleChange = event => {
+    this.setState({
+      [event.target.id]: event.target.value,
+    });
+  }
+
+  switchToEditionMode() {
+    this.setState({
+      readOnly: false
+    });
+  }
+
+  changeUserData() {
+    this.props.changeUserData(this.state);
+  }
+
+
+  render() {
+  	return (
+  		<Row style={{ width: '95%', margin: '0 auto', marginTop:'30px' }}>
+        <Col xs lg="4">
+          <Card style={{ width: '100%', margin: '0 auto', marginTop:'30px' }}>
+            <Card.Body>
+              <Card.Title>Balance</Card.Title>
+            </Card.Body>
+          </Card>
+        </Col>
+        <Col>
+          <Card style={{ width: '100%', margin: '0 auto', marginTop:'30px' }}>
+            <Card.Body>
+              <Card.Title>Transactions</Card.Title>
+            <Table hover>
+              <tbody>
+                <tr>
+                  <td><Image src={logo} width={30} height={30} /></td>
+                  <td colSpan="2">29/09/2021</td>
+                  <td>-8€</td>
+                </tr>
+                <tr>
+                  <td><Image src={logo} width={30} height={30} /></td>
+                  <td colSpan="2">29/09/2021</td>
+                  <td>-8€</td>
+                </tr>
+                <tr>
+                  <td><Image src={logo} width={30} height={30} /></td>
+                  <td colSpan="2">29/09/2021</td>
+                  <td>-8€</td>
+                </tr>
+              </tbody>
+            </Table>
+           </Card.Body>
+          </Card>
+        </Col>
+          {this.props.state.updateUserError && <div><br/>{JSON.stringify(this.props.state.updateUserErrorMessage.message)}</div>}
+          {this.props.state.updateUserSuccess && <div><br/>Success! You can now use your new password.</div>}
+      </Row>
+  	);
+  }
+}
+
+// map state from store to props
+const mapStateToProps = (state) => {
+  return {
+    state: state.profile
+  }
+}
+// map actions to props
+const mapDispatchToProps = (dispatch) => {
+  return {
+    fetchUserData: () => dispatch(bankActions.fetchUserData()),
+    changeUserData: (data) => dispatch(bankActions.changeUserData(data)),
+    reinitializeState: () => dispatch(bankActions.reinitializeState()),
+  }
+}
+
+export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Bank))
diff --git a/frontend/src/containers/Profile.container.js b/frontend/src/containers/Profile.container.js
index 4a2dcd2..22cd540 100644
--- a/frontend/src/containers/Profile.container.js
+++ b/frontend/src/containers/Profile.container.js
@@ -13,7 +13,7 @@ export class Profile extends React.Component {
       readOnly: true
     };
 
-    this.switchEditionMode = this.switchEditionMode.bind(this); 
+    this.switchToEditionMode = this.switchToEditionMode.bind(this); 
   }
 
   componentWillMount(){
@@ -21,25 +21,28 @@ export class Profile extends React.Component {
     this.props.reinitializeState();
   }
 
+  componentDidUpdate(prevProps) {
+    if (prevProps.state !== this.props.state) {
+      this.setState({
+        readOnly: this.props.state.updateUserSuccess
+      });
+    }
+  }
+
   handleChange = event => {
     this.setState({
       [event.target.id]: event.target.value,
     });
   }
 
-  switchEditionMode() {
-    this.setState(prevState => ({
-      readOnly: !prevState.readOnly
-    }));
+  switchToEditionMode() {
+    this.setState({
+      readOnly: false
+    });
   }
 
   changeUserData() {
     this.props.changeUserData(this.state);
-    if(this.props.state.updateUserSuccess){
-      this.setState({
-        readOnly: true
-      });
-    }
   }
 
 
@@ -152,10 +155,10 @@ export class Profile extends React.Component {
                 Save
               </Button>
             }
-            { this.state.readOnly && 
+            { this.state.readOnly &&  
               <Button
                 block
-                onClick={this.switchEditionMode}
+                onClick={this.switchToEditionMode}
                 variant="primary"
               >
                 Edit
@@ -163,7 +166,7 @@ export class Profile extends React.Component {
               }
             </Form>
           {this.props.state.updateUserError && <div><br/>{JSON.stringify(this.props.state.updateUserErrorMessage.message)}</div>}
-          {this.props.state.updateUserSuccess && <div><br/>Success! You can now use your new password.</div>}
+          {this.props.state.updateUserSuccess && <div><br/>Profile changed with success.</div>}
         </Card.Body>
       </Card>
   	);
diff --git a/frontend/src/containers/Users.container.js b/frontend/src/containers/Users.container.js
index e9a68d7..1785e18 100644
--- a/frontend/src/containers/Users.container.js
+++ b/frontend/src/containers/Users.container.js
@@ -23,8 +23,6 @@ export class Users extends React.Component {
                 <thead>
                     <tr>
                         <td>Username</td>
-                        <td># likes</td>
-                        { this.props.loginState.loggedIn && <td></td> }
                     </tr>
                 </thead>
                 <tbody>
diff --git a/frontend/src/icons/transaction.png b/frontend/src/icons/transaction.png
new file mode 100644
index 0000000000000000000000000000000000000000..7782275daf26875be6d85341f96eb12fcd7e0699
GIT binary patch
literal 3808
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
M>?oTGHc}7&593nsqW}N^

literal 0
HcmV?d00001

diff --git a/frontend/src/reducers/Bank.reducer.js b/frontend/src/reducers/Bank.reducer.js
new file mode 100644
index 0000000..b3f1b31
--- /dev/null
+++ b/frontend/src/reducers/Bank.reducer.js
@@ -0,0 +1,27 @@
+const INITIAL_STATE = {
+	me: {},
+	updateUserError: false,
+	updateUserErrorMessage: {},
+	updateUserSuccess: false,
+	loading: false,
+	profileDataError: false,
+}
+
+const forgotpasswordReducer = (currentState = INITIAL_STATE, action) => {
+  switch (action.type) {
+	  case 'REINITIALIZE_STATE':
+	  	return {...currentState, updateUserError: false, updateUserErrorMessage: {}, loading:false, updateUserSuccess:false};
+	  case 'PROFILE_DATA_REQ_SUCCESS':
+	  	return {...currentState, me:action.data};
+	  case 'PROFILE_DATA_REQ_FAILURE':
+		  return {...currentState, profileDataError: true};
+	  case 'UPDATE_USER_FAILURE':
+		  return {...currentState, updateUserError:true, updateUserErrorMessage:action.message, loading: false, updateUserSuccess:false};
+	  case 'UPDATE_USER_SUCCESS':
+		  return {...currentState, updateUserError:false, updateUserErrorMessage:{}, updateUserSuccess: true, loading: false};
+	  default:
+	       return currentState;
+	}
+}
+
+export default forgotpasswordReducer;
-- 
GitLab