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