mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-01-01 21:21:50 +08:00
logic for MyProfile component is finished
This commit is contained in:
parent
a3d126b073
commit
086be89a5d
10 changed files with 10079 additions and 19 deletions
|
@ -10,3 +10,5 @@ export const SET_CURRENT_USER = "SET_CURRENT_USER";
|
|||
export const CHANGE_CURRENT_USER_FULL_NAME = "CHANGE_CURRENT_USER_FULL_NAME";
|
||||
export const CHANGE_CURRENT_USER_INITIALS = "CHANGE_CURRENT_USER_INITIALS";
|
||||
export const CHANGE_CURRENT_USER_EMAIL = "CHANGE_CURRENT_USER_EMAIL";
|
||||
export const CHANGE_CURRENT_USER_PASSWORD = "CHANGE_CURRENT_USER_PASSWORD";
|
||||
export const CHANGE_CURRENT_USER_AVATAR = "CHANGE_CURRENT_USER_AVATAR";
|
||||
|
|
|
@ -21,3 +21,8 @@ export const RELEASE_NOTES_LINK = "http://scinote.net/docs/release-notes/";
|
|||
export const PREMIUM_LINK = "http://scinote.net/premium/";
|
||||
export const CONTACT_US_LINK =
|
||||
"http://scinote.net/story-of-scinote/#contact-scinote";
|
||||
|
||||
// settings
|
||||
export const SETTINGS_ACCOUNT_PROFILE = "/settings/account/profile";
|
||||
export const SETTINGS_ACCOUNT_PREFERENCES = "/settings/account/preferences";
|
||||
export const SETTINGS_TEAMS = "/settings/teams";
|
||||
|
|
|
@ -4,7 +4,9 @@ import {
|
|||
SET_CURRENT_USER,
|
||||
CHANGE_CURRENT_USER_FULL_NAME,
|
||||
CHANGE_CURRENT_USER_INITIALS,
|
||||
CHANGE_CURRENT_USER_EMAIL
|
||||
CHANGE_CURRENT_USER_EMAIL,
|
||||
CHANGE_CURRENT_USER_PASSWORD,
|
||||
CHANGE_CURRENT_USER_AVATAR
|
||||
} from "../../app/action_types";
|
||||
|
||||
function addCurrentUser(data) {
|
||||
|
@ -47,3 +49,17 @@ export function changeEmail(email) {
|
|||
payload: email
|
||||
};
|
||||
}
|
||||
|
||||
export function changePassword(password) {
|
||||
return {
|
||||
type: CHANGE_CURRENT_USER_PASSWORD,
|
||||
payload: password
|
||||
};
|
||||
}
|
||||
|
||||
export function changeAvatar(avatarSrc) {
|
||||
return {
|
||||
type: CHANGE_CURRENT_USER_AVATAR,
|
||||
payload: avatarSrc
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@ import {
|
|||
SET_CURRENT_USER,
|
||||
CHANGE_CURRENT_USER_FULL_NAME,
|
||||
CHANGE_CURRENT_USER_INITIALS,
|
||||
CHANGE_CURRENT_USER_EMAIL
|
||||
CHANGE_CURRENT_USER_EMAIL,
|
||||
CHANGE_CURRENT_USER_PASSWORD,
|
||||
CHANGE_CURRENT_USER_AVATAR
|
||||
} from "../../app/action_types";
|
||||
|
||||
export function currentUser(
|
||||
|
@ -25,6 +27,12 @@ export function currentUser(
|
|||
return Object.assign({}, state, { initials: action.payload });
|
||||
case CHANGE_CURRENT_USER_EMAIL:
|
||||
return Object.assign({}, state, { email: action.payload });
|
||||
case CHANGE_CURRENT_USER_PASSWORD:
|
||||
console.log("handle sending password to the server");
|
||||
// return Object.assign({}, state, { password: action.payload });
|
||||
return state;
|
||||
case CHANGE_CURRENT_USER_AVATAR:
|
||||
return Object.assign({}, state, { avatar: action.payload });
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,29 @@
|
|||
import React from "react";
|
||||
import { string } from "prop-types";
|
||||
import PropTypes, { string } from "prop-types";
|
||||
import styled from "styled-components";
|
||||
|
||||
const Avatar = props => <img src={props.imgSource} alt="default avatar" />;
|
||||
const AvatarWrapper = styled.div`
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
position: relative;
|
||||
`;
|
||||
const EditAvatar = styled.span`
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background-color: silver;
|
||||
`;
|
||||
|
||||
const Avatar = props =>
|
||||
<AvatarWrapper onClick={props.enableEdit}>
|
||||
<img src={props.imgSource} alt="default avatar" />
|
||||
<EditAvatar>Edit Avatar</EditAvatar>
|
||||
</AvatarWrapper>;
|
||||
|
||||
Avatar.propTypes = {
|
||||
imgSource: string.isRequired
|
||||
imgSource: string.isRequired,
|
||||
enableEdit: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default Avatar;
|
||||
|
|
|
@ -1,16 +1,32 @@
|
|||
import React, { Component } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import styled from "styled-components";
|
||||
import { FormGroup, FormControl, ControlLabel, Button } from "react-bootstrap";
|
||||
|
||||
const StyledInputEnabled = styled.div`
|
||||
border: 1px solid black;
|
||||
padding: 10px;
|
||||
`;
|
||||
|
||||
const ErrorMsg = styled.div`color: red;`;
|
||||
|
||||
class InputEnabled extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
value: this.props.inputValue
|
||||
};
|
||||
if (props.inputType === "password") {
|
||||
this.state = {
|
||||
value: "",
|
||||
value2: ""
|
||||
};
|
||||
} else {
|
||||
this.state = {
|
||||
value: this.props.inputValue
|
||||
};
|
||||
}
|
||||
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.handleChange2 = this.handleChange2.bind(this);
|
||||
this.handleUpdate = this.handleUpdate.bind(this);
|
||||
}
|
||||
|
||||
|
@ -18,6 +34,10 @@ class InputEnabled extends Component {
|
|||
this.setState({ value: event.target.value });
|
||||
}
|
||||
|
||||
handleChange2(event) {
|
||||
this.setState({ value2: event.target.value });
|
||||
}
|
||||
|
||||
handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
@ -27,19 +47,74 @@ class InputEnabled extends Component {
|
|||
this.props.disableEdit();
|
||||
}
|
||||
|
||||
confirmationField() {
|
||||
let inputs;
|
||||
const type = this.props.inputType;
|
||||
|
||||
if (type === "email" || type === "password") {
|
||||
inputs = (
|
||||
<div>
|
||||
<p>
|
||||
Current password (we need your current password to confirm your
|
||||
changes)
|
||||
</p>
|
||||
<FormControl type="password" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return inputs;
|
||||
}
|
||||
|
||||
errorMsg() {
|
||||
return this.state.value !== this.state.value2
|
||||
? <ErrorMsg>Passwords do not match!</ErrorMsg>
|
||||
: "";
|
||||
}
|
||||
|
||||
inputField() {
|
||||
let input;
|
||||
|
||||
if (this.props.inputType === "password") {
|
||||
input = (
|
||||
<div>
|
||||
<FormControl
|
||||
type={this.props.inputType}
|
||||
value={this.state.value}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<p>New password confirmation</p>
|
||||
<FormControl
|
||||
type={this.props.inputType}
|
||||
value={this.state.value2}
|
||||
onChange={this.handleChange2}
|
||||
/>
|
||||
{this.errorMsg()}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
input = (
|
||||
<FormControl
|
||||
type={this.props.inputType}
|
||||
value={this.state.value}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<StyledInputEnabled>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<FormGroup>
|
||||
{this.confirmationField()}
|
||||
<ControlLabel>
|
||||
{this.props.labelValue}
|
||||
</ControlLabel>
|
||||
<FormControl
|
||||
type={this.props.inputType}
|
||||
value={this.state.value}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
{this.inputField()}
|
||||
<Button bsStyle="primary" onClick={this.props.disableEdit}>
|
||||
Cancel
|
||||
</Button>
|
||||
|
@ -48,7 +123,7 @@ class InputEnabled extends Component {
|
|||
</Button>
|
||||
</FormGroup>
|
||||
</form>
|
||||
</div>
|
||||
</StyledInputEnabled>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
39
app/javascript/packs/src/settings/components/MainNav.jsx
Normal file
39
app/javascript/packs/src/settings/components/MainNav.jsx
Normal file
|
@ -0,0 +1,39 @@
|
|||
import React, { Component } from "react";
|
||||
import { LinkContainer } from "react-router-bootstrap";
|
||||
import { Nav, NavItem } from "react-bootstrap";
|
||||
|
||||
import { SETTINGS_ACCOUNT_PROFILE, SETTINGS_TEAMS } from "../../../app/routes";
|
||||
|
||||
export default class MainNav extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
active: "1"
|
||||
};
|
||||
this.handleSelect = this.handleSelect.bind(this);
|
||||
}
|
||||
|
||||
handleSelect(eventKey) {
|
||||
event.preventDefault();
|
||||
this.setState({ active: eventKey });
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Nav bsStyle="tabs" activeKey="1" onSelect={this.handleSelect}>
|
||||
<LinkContainer
|
||||
active={this.state.active === "1"}
|
||||
to={SETTINGS_ACCOUNT_PROFILE}
|
||||
>
|
||||
<NavItem eventKey="1">Account</NavItem>
|
||||
</LinkContainer>
|
||||
<LinkContainer to={SETTINGS_TEAMS} active={this.state.active === "2"}>
|
||||
<NavItem eventKey="2">Team</NavItem>
|
||||
</LinkContainer>
|
||||
</Nav>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -9,7 +9,9 @@ import InputEnabled from "./InputEnabled";
|
|||
import {
|
||||
changeFullName,
|
||||
changeInitials,
|
||||
changeEmail
|
||||
changeEmail,
|
||||
changePassword,
|
||||
changeAvatar
|
||||
} from "../../../shared/actions/UsersActions";
|
||||
|
||||
class MyProfile extends Component {
|
||||
|
@ -19,7 +21,9 @@ class MyProfile extends Component {
|
|||
this.state = {
|
||||
isFullNameEditable: false,
|
||||
areInitialsEditable: false,
|
||||
isEmailEditable: false
|
||||
isEmailEditable: false,
|
||||
isPasswordEditable: false,
|
||||
isAvatarEditable: false
|
||||
};
|
||||
|
||||
this.toggleIsEditable = this.toggleIsEditable.bind(this);
|
||||
|
@ -34,9 +38,53 @@ class MyProfile extends Component {
|
|||
const areInitialsEditable = "areInitialsEditable";
|
||||
const isFullNameEditable = "isFullNameEditable";
|
||||
const isEmailEditable = "isEmailEditable";
|
||||
const isPasswordEditable = "isPasswordEditable";
|
||||
const isAvatarEditable = "isAvatarEditable";
|
||||
let fullNameField;
|
||||
let initialsField;
|
||||
let emailField;
|
||||
let passwordField;
|
||||
let avatarField;
|
||||
|
||||
if (this.state.isAvatarEditable) {
|
||||
avatarField = (
|
||||
<InputEnabled
|
||||
labelValue="Avatar"
|
||||
inputType="file"
|
||||
inputValue=""
|
||||
disableEdit={() => this.toggleIsEditable(isAvatarEditable)}
|
||||
saveData={avatarSrc => this.props.changeAvatar(avatarSrc)}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
avatarField = (
|
||||
<Avatar
|
||||
imgSource={this.props.avatarThumbPath}
|
||||
enableEdit={() => this.toggleIsEditable(isAvatarEditable)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.isPasswordEditable) {
|
||||
passwordField = (
|
||||
<InputEnabled
|
||||
labelValue="Change password"
|
||||
inputType="password"
|
||||
inputValue=""
|
||||
disableEdit={() => this.toggleIsEditable(isPasswordEditable)}
|
||||
saveData={newPassword => this.props.changePassword(newPassword)}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
passwordField = (
|
||||
<InputDisabled
|
||||
labelValue="Change password"
|
||||
inputType="password"
|
||||
inputValue=""
|
||||
enableEdit={() => this.toggleIsEditable(isPasswordEditable)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.isEmailEditable) {
|
||||
emailField = (
|
||||
|
@ -105,10 +153,11 @@ class MyProfile extends Component {
|
|||
<div>
|
||||
<h2>My Profile</h2>
|
||||
<h4>Avatar</h4>
|
||||
<Avatar imgSource={this.props.avatarThumbPath} />
|
||||
{avatarField}
|
||||
{fullNameField}
|
||||
{initialsField}
|
||||
{emailField}
|
||||
{passwordField}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -121,7 +170,9 @@ MyProfile.propTypes = {
|
|||
email: PropTypes.string.isRequired,
|
||||
changeFullName: PropTypes.func.isRequired,
|
||||
changeInitials: PropTypes.func.isRequired,
|
||||
changeEmail: PropTypes.func.isRequired
|
||||
changeEmail: PropTypes.func.isRequired,
|
||||
changePassword: PropTypes.func.isRequired,
|
||||
changeAvatar: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = state => state.current_user;
|
||||
|
@ -134,6 +185,12 @@ const mapDispatchToProps = dispatch => ({
|
|||
},
|
||||
changeEmail(email) {
|
||||
dispatch(changeEmail(email));
|
||||
},
|
||||
changePassword(password) {
|
||||
dispatch(changePassword(password));
|
||||
},
|
||||
changeAvatar(avatarSrc) {
|
||||
dispatch(changeAvatar(avatarSrc));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
import React, { Component } from "react";
|
||||
import { Nav, NavItem } from "react-bootstrap";
|
||||
import { LinkContainer } from "react-router-bootstrap";
|
||||
|
||||
import {
|
||||
SETTINGS_ACCOUNT_PROFILE,
|
||||
SETTINGS_ACCOUNT_PREFERENCES
|
||||
} from "../../../app/routes";
|
||||
|
||||
class SettingsAccountLeftTab extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleSelect = this.handleSelect.bind(this);
|
||||
}
|
||||
|
||||
handleSelect(selectedKey) {}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Nav bsStyle="pills" stacked activeKey={1} onSelect={this.handleSelect}>
|
||||
<LinkContainer to={SETTINGS_ACCOUNT_PROFILE}>
|
||||
<NavItem eventKey={1}>Profile</NavItem>
|
||||
</LinkContainer>
|
||||
<LinkContainer to={SETTINGS_ACCOUNT_PREFERENCES}>
|
||||
<NavItem eventKey={2}>Preferences</NavItem>
|
||||
</LinkContainer>
|
||||
</Nav>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SettingsAccountLeftTab;
|
9807
package-lock.json
generated
Normal file
9807
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue