mirror of
https://github.com/elyby/accounts-frontend.git
synced 2024-11-30 10:42:34 +05:30
Merge branch 'profile'
This commit is contained in:
commit
7ab387fb71
83
src/components/profile/Profile.jsx
Normal file
83
src/components/profile/Profile.jsx
Normal file
@ -0,0 +1,83 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { FormattedMessage as Message, FormattedRelative as Relative, FormattedHTMLMessage as HTMLMessage } from 'react-intl';
|
||||
import Helmet from 'react-helmet';
|
||||
|
||||
import { userShape } from 'components/user/User';
|
||||
|
||||
import ProfileField from './ProfileField';
|
||||
import styles from './profile.scss';
|
||||
import messages from './Profile.messages';
|
||||
|
||||
export class Profile extends Component {
|
||||
static displayName = 'Profile';
|
||||
static propTypes = {
|
||||
user: userShape
|
||||
};
|
||||
|
||||
render() {
|
||||
const { user } = this.props;
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Message {...messages.accountPreferencesTitle}>
|
||||
{(pageTitle) => (
|
||||
<h2 className={styles.title}>
|
||||
<Helmet title={pageTitle} />
|
||||
{pageTitle}
|
||||
</h2>
|
||||
)}
|
||||
</Message>
|
||||
|
||||
<div className={styles.content}>
|
||||
<div className={styles.description}>
|
||||
<Message {...messages.accountDescription} />
|
||||
</div>
|
||||
|
||||
<div className={styles.options}>
|
||||
<div className={styles.item}>
|
||||
<h3 className={styles.optionsTitle}>
|
||||
<Message {...messages.personalData} />
|
||||
</h3>
|
||||
<p className={styles.optionsDescription}>
|
||||
<Message {...messages.preferencesDescription} />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ProfileField
|
||||
label={<Message {...messages.nickname} />}
|
||||
value={user.username}
|
||||
warningMessage={<Message {...messages.mojangPriorityWarning} />}
|
||||
/>
|
||||
|
||||
<ProfileField
|
||||
label={'E-mail'}
|
||||
value={user.email}
|
||||
/>
|
||||
|
||||
<ProfileField
|
||||
label={<Message {...messages.password} />}
|
||||
value={<Message {...messages.changedAt} values={{
|
||||
at: (<Relative value={user.passwordChangedAt * 1000} />)
|
||||
}} />}
|
||||
warningMessage={user.shouldChangePassword ? (
|
||||
<HTMLMessage {...messages.oldHashingAlgoWarning} />
|
||||
) : ''}
|
||||
/>
|
||||
|
||||
<ProfileField
|
||||
label={<Message {...messages.twoFactorAuth} />}
|
||||
value={<Message {...messages.disabled} />}
|
||||
/>
|
||||
|
||||
<ProfileField
|
||||
label={'UUID'}
|
||||
value={user.uuid}
|
||||
readonly
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
59
src/components/profile/Profile.messages.js
Normal file
59
src/components/profile/Profile.messages.js
Normal file
@ -0,0 +1,59 @@
|
||||
import { defineMessages } from 'react-intl';
|
||||
|
||||
export default defineMessages({
|
||||
accountPreferencesTitle: {
|
||||
id: 'accountPreferencesTitle',
|
||||
defaultMessage: 'Ely.by account preferences'
|
||||
// defaultMessage: 'Настройки аккаунта Ely.by'
|
||||
},
|
||||
personalData: {
|
||||
id: 'personalData',
|
||||
defaultMessage: 'Personal data'
|
||||
// defaultMessage: 'Персональные данные'
|
||||
},
|
||||
accountDescription: {
|
||||
id: 'accountDescription',
|
||||
defaultMessage: 'Ely.by account allows you to get access to many Minecraft resources. Please, take care of your account safety. Use secure password and change it regularly.'
|
||||
// defaultMessage: 'Благодаря аккаунту Ely.by вы можете получить доступ ко многим ресурсам, связанным с Minecraft. Берегите свой аккаунт, используйте надёжный пароль и регулярно его меняйте.'
|
||||
},
|
||||
preferencesDescription: {
|
||||
id: 'preferencesDescription',
|
||||
defaultMessage: 'Here you can change the key preferences of your account. Please note that all actions must be confirmed by entering a password.'
|
||||
// defaultMessage: 'Здесь вы можете сменить ключевые параметры вашего аккаунта. Обратите внимание, что для всех действий необходимо подтверждение при помощи ввода пароля.'
|
||||
},
|
||||
mojangPriorityWarning: {
|
||||
id: 'mojangPriorityWarning',
|
||||
defaultMessage: 'A Mojang account with the same nickname was found. According to project rules, account owner has the right to demand the restoration of control over nickname.'
|
||||
// defaultMessage: 'Найден аккаунт Mojang с таким же ником и, по правилам проекта, его владелец вправе потребовать восстановления контроля над ником.'
|
||||
},
|
||||
oldHashingAlgoWarning: {
|
||||
id: 'oldHashingAlgoWarning',
|
||||
defaultMessage: 'Your was hashed with an old hashing algorithm.<br />Please, change password.'
|
||||
// defaultMessage: 'Для пароля применяется старый алгоритм хэширования<br />Пожалуйста, смените пароль.'
|
||||
},
|
||||
changedAt: {
|
||||
id: 'changedAt',
|
||||
defaultMessage: 'Changed {at}'
|
||||
// defaultMessage: 'Изменен {at}'
|
||||
},
|
||||
disabled: {
|
||||
id: 'disabled',
|
||||
defaultMessage: 'Disabled'
|
||||
// defaultMessage: 'Не включена'
|
||||
},
|
||||
nickname: {
|
||||
id: 'nickname',
|
||||
defaultMessage: 'Nickname'
|
||||
// defaultMessage: 'Ник'
|
||||
},
|
||||
password: {
|
||||
id: 'password',
|
||||
defaultMessage: 'Password'
|
||||
// defaultMessage: 'Пароль'
|
||||
},
|
||||
twoFactorAuth: {
|
||||
id: 'twoFactorAuth',
|
||||
defaultMessage: 'Two factor auth'
|
||||
// defaultMessage: 'Двухфакторная аутентификация'
|
||||
}
|
||||
});
|
40
src/components/profile/ProfileField.jsx
Normal file
40
src/components/profile/ProfileField.jsx
Normal file
@ -0,0 +1,40 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import styles from './profile.scss';
|
||||
|
||||
export default class ProfileField extends Component {
|
||||
static displayName = 'ProfileField';
|
||||
static propTypes = {
|
||||
label: React.PropTypes.oneOfType([PropTypes.string, PropTypes.element]).isRequired,
|
||||
value: React.PropTypes.oneOfType([PropTypes.string, PropTypes.element]).isRequired,
|
||||
warningMessage: React.PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
|
||||
readonly: PropTypes.bool
|
||||
};
|
||||
|
||||
render() {
|
||||
const {label, value, warningMessage, readonly} = this.props;
|
||||
|
||||
return (
|
||||
<div className={styles.paramItem}>
|
||||
<div className={styles.paramRow}>
|
||||
<div className={styles.paramName}>{label}:</div>
|
||||
<div className={styles.paramValue}>{value}</div>
|
||||
|
||||
{readonly ? '' : (
|
||||
<div className={styles.paramAction}>
|
||||
<a href="#">
|
||||
<span className={styles.paramEditIcon} />
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{warningMessage ? (
|
||||
<div className={styles.paramMessage}>
|
||||
{warningMessage}
|
||||
</div>
|
||||
) : ''}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
126
src/components/profile/profile.scss
Normal file
126
src/components/profile/profile.scss
Normal file
@ -0,0 +1,126 @@
|
||||
@import '~components/ui/fonts.scss';
|
||||
@import '~components/ui/colors.scss';
|
||||
|
||||
.container {
|
||||
margin-top: 55px;
|
||||
}
|
||||
|
||||
|
||||
.title {
|
||||
font-family: $font-family-title;
|
||||
font-size: 30px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
color: #9a9a9a;
|
||||
width: 340px;
|
||||
padding: 12px 20px 0 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.options {
|
||||
background: #fff;
|
||||
flex-grow: 1;
|
||||
max-width: 416px;
|
||||
border-bottom: 10px solid #ddd8ce;
|
||||
}
|
||||
|
||||
.optionsTitle {
|
||||
position: relative;
|
||||
font-size: 24px;
|
||||
font-family: $font-family-title;
|
||||
padding-bottom: 9px;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
height: 3px;
|
||||
width: 86px;
|
||||
|
||||
background: $green;
|
||||
}
|
||||
}
|
||||
|
||||
.optionsDescription {
|
||||
font-size: 13px;
|
||||
color: #9a9a9a;
|
||||
line-height: 1.25;
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 30px;
|
||||
border-bottom: 1px solid #eee;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.paramItem {
|
||||
composes: item;
|
||||
|
||||
$padding: 20px;
|
||||
|
||||
padding-top: $padding;
|
||||
padding-bottom: $padding;
|
||||
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.paramRow {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.paramName {
|
||||
width: 125px;
|
||||
font-family: $font-family-title;
|
||||
}
|
||||
|
||||
.paramValue {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.uuidValue {
|
||||
composes: paramName;
|
||||
composes: paramValue;
|
||||
}
|
||||
|
||||
.paramAction {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.paramEditIcon {
|
||||
composes: pencil from 'components/ui/icons.scss';
|
||||
|
||||
color: $light;
|
||||
transition: .4s;
|
||||
|
||||
a:hover & {
|
||||
color: #444;
|
||||
}
|
||||
}
|
||||
|
||||
.paramMessage {
|
||||
padding: 10px 40px 0 0;
|
||||
|
||||
color: $red;
|
||||
font-size: 11px;
|
||||
line-height: 1.2;
|
||||
}
|
@ -17,6 +17,7 @@ export default class User {
|
||||
|
||||
const defaults = {
|
||||
id: null,
|
||||
uuid: null,
|
||||
token: '',
|
||||
username: '',
|
||||
email: '',
|
||||
@ -24,7 +25,8 @@ export default class User {
|
||||
goal: null, // the goal with wich user entered site
|
||||
isGuest: true,
|
||||
isActive: true,
|
||||
shouldChangePassword: false
|
||||
shouldChangePassword: false, // TODO: нужно ещё пробросить причину необходимости смены
|
||||
passwordChangedAt: null
|
||||
};
|
||||
|
||||
const user = Object.keys(defaults).reduce((user, key) => {
|
||||
@ -51,10 +53,12 @@ export default class User {
|
||||
|
||||
export const userShape = PropTypes.shape({
|
||||
id: PropTypes.number,
|
||||
uuid: PropTypes.string,
|
||||
token: PropTypes.string,
|
||||
username: PropTypes.string,
|
||||
email: PropTypes.string,
|
||||
avatar: PropTypes.string,
|
||||
isGuest: PropTypes.bool.isRequired,
|
||||
isActive: PropTypes.bool.isRequired
|
||||
isActive: PropTypes.bool.isRequired,
|
||||
passwordChangedAt: PropTypes.number
|
||||
});
|
||||
|
@ -27,7 +27,7 @@ export default class LoggedInPanel extends Component {
|
||||
|
||||
return (
|
||||
<div className={buttonGroups.horizontalGroup}>
|
||||
<Link to="/profile" className={classNames(buttons.green, buttonGroups.item)}>
|
||||
<Link to="/" className={classNames(buttons.green, buttonGroups.item)}>
|
||||
<span className={styles.userIcon} />
|
||||
<span className={styles.userName}>{user.username}</span>
|
||||
</Link>
|
||||
|
6
src/icons/webfont/pencil.svg
Normal file
6
src/icons/webfont/pencil.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
|
||||
<path d="M13.5 0c1.381 0 2.5 1.119 2.5 2.5 0 0.563-0.186 1.082-0.5 1.5l-1 1-3.5-3.5 1-1c0.418-0.314 0.937-0.5 1.5-0.5zM1 11.5l-1 4.5 4.5-1 9.25-9.25-3.5-3.5-9.25 9.25zM11.181 5.681l-7 7-0.862-0.862 7-7 0.862 0.862z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 543 B |
@ -2,20 +2,13 @@ import React, { Component } from 'react';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { Profile } from 'components/profile/Profile';
|
||||
|
||||
class IndexPage extends Component {
|
||||
displayName = 'IndexPage';
|
||||
|
||||
render() {
|
||||
const {user, children} = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>
|
||||
Hello {user.username}!
|
||||
</h1>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
return (<Profile {...this.props} />);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user