mirror of
				https://github.com/elyby/accounts-frontend.git
				synced 2025-05-31 14:11:58 +05:30 
			
		
		
		
	Merge branch 'profile'
This commit is contained in:
		
							
								
								
									
										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} />); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user