Skip to content

Spanish Translation Account form and New Password #1547

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Aug 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 13 additions & 16 deletions client/modules/User/components/APIKeyForm.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import PropTypes from 'prop-types';
import React from 'react';

import Button from '../../../common/Button';
import { PlusIcon } from '../../../common/icons';
import CopyableInput from '../../IDE/components/CopyableInput';
Expand All @@ -12,7 +11,7 @@ export const APIKeyPropType = PropTypes.shape({
token: PropTypes.object,
label: PropTypes.string.isRequired,
createdAt: PropTypes.string.isRequired,
lastUsedAt: PropTypes.string,
lastUsedAt: PropTypes.string
});

class APIKeyForm extends React.Component {
Expand All @@ -39,7 +38,7 @@ class APIKeyForm extends React.Component {
}

removeKey(key) {
const message = `Are you sure you want to delete "${key.label}"?`;
const message = this.props.t('APIKeyForm.ConfirmDelete', { key_label: key.label });

if (window.confirm(message)) {
this.props.removeApiKey(key.id);
Expand All @@ -51,10 +50,10 @@ class APIKeyForm extends React.Component {

if (hasApiKeys) {
return (
<APIKeyList apiKeys={this.props.apiKeys} onRemove={this.removeKey} />
<APIKeyList apiKeys={this.props.apiKeys} onRemove={this.removeKey} t={this.props.t} />
);
}
return <p>You have no exsiting tokens.</p>;
return <p>{this.props.t('APIKeyForm.NoTokens')}</p>;
}

render() {
Expand All @@ -63,20 +62,18 @@ class APIKeyForm extends React.Component {
return (
<div className="api-key-form">
<p className="api-key-form__summary">
Personal Access Tokens act like your password to allow automated
scripts to access the Editor API. Create a token for each script
that needs access.
{this.props.t('APIKeyForm.Summary')}
</p>

<div className="api-key-form__section">
<h3 className="api-key-form__title">Create new token</h3>
<h3 className="api-key-form__title">{this.props.t('APIKeyForm.CreateToken')}</h3>
<form className="form form--inline" onSubmit={this.addKey}>
<label htmlFor="keyLabel" className="form__label form__label--hidden ">What is this token for?</label>
<label htmlFor="keyLabel" className="form__label form__label--hidden ">{this.props.t('APIKeyForm.TokenLabel')}</label>
<input
className="form__input"
id="keyLabel"
onChange={(event) => { this.setState({ keyLabel: event.target.value }); }}
placeholder="What is this token for? e.g. Example import script"
placeholder={this.props.t('APIKeyForm.TokenPlaceholder')}
type="text"
value={this.state.keyLabel}
/>
Expand All @@ -86,17 +83,16 @@ class APIKeyForm extends React.Component {
label="Create new key"
type="submit"
>
Create
{this.props.t('APIKeyForm.CreateTokenSubmit')}
</Button>
</form>

{
keyWithToken && (
<div className="api-key-form__new-token">
<h4 className="api-key-form__new-token__title">Your new access token</h4>
<h4 className="api-key-form__new-token__title">{this.props.t('APIKeyForm.NewTokenTitle')}</h4>
<p className="api-key-form__new-token__info">
Make sure to copy your new personal access token now.
You won’t be able to see it again!
{this.props.t('APIKeyForm.NewTokenInfo')}
</p>
<CopyableInput label={keyWithToken.label} value={keyWithToken.token} />
</div>
Expand All @@ -105,7 +101,7 @@ class APIKeyForm extends React.Component {
</div>

<div className="api-key-form__section">
<h3 className="api-key-form__title">Existing tokens</h3>
<h3 className="api-key-form__title">{this.props.t('APIKeyForm.ExistingTokensTitle')}</h3>
{this.renderApiKeys()}
</div>
</div>
Expand All @@ -117,6 +113,7 @@ APIKeyForm.propTypes = {
apiKeys: PropTypes.arrayOf(PropTypes.shape(APIKeyPropType)).isRequired,
createApiKey: PropTypes.func.isRequired,
removeApiKey: PropTypes.func.isRequired,
t: PropTypes.func.isRequired
};

export default APIKeyForm;
15 changes: 8 additions & 7 deletions client/modules/User/components/APIKeyList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ import { APIKeyPropType } from './APIKeyForm';

import TrashCanIcon from '../../../images/trash-can.svg';

function APIKeyList({ apiKeys, onRemove }) {
function APIKeyList({ apiKeys, onRemove, t }) {
return (
<table className="api-key-list">
<thead>
<tr>
<th>Name</th>
<th>Created on</th>
<th>Last used</th>
<th>Actions</th>
<th>{t('APIKeyList.Name')}</th>
<th>{t('APIKeyList.Created')}</th>
<th>{t('APIKeyList.LastUsed')}</th>
<th>{t('APIKeyList.Actions')}</th>
</tr>
</thead>
<tbody>
{orderBy(apiKeys, ['createdAt'], ['desc']).map((key) => {
const lastUsed = key.lastUsedAt ?
distanceInWordsToNow(new Date(key.lastUsedAt), { addSuffix: true }) :
'Never';
t('APIKeyList.Never');

return (
<tr key={key.id}>
Expand All @@ -34,7 +34,7 @@ function APIKeyList({ apiKeys, onRemove }) {
<button
className="api-key-list__delete-button"
onClick={() => onRemove(key)}
aria-label="Delete API Key"
aria-label={t('APIKeyList.DeleteARIA')}
>
<TrashCanIcon focusable="false" aria-hidden="true" />
</button>
Expand All @@ -50,6 +50,7 @@ function APIKeyList({ apiKeys, onRemove }) {
APIKeyList.propTypes = {
apiKeys: PropTypes.arrayOf(PropTypes.shape(APIKeyPropType)).isRequired,
onRemove: PropTypes.func.isRequired,
t: PropTypes.func.isRequired
};

export default APIKeyList;
31 changes: 17 additions & 14 deletions client/modules/User/components/AccountForm.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import PropTypes from 'prop-types';
import React from 'react';
import { withTranslation } from 'react-i18next';
import { domOnlyProps } from '../../../utils/reduxFormUtils';
import Button from '../../../common/Button';

Expand All @@ -13,7 +14,8 @@ function AccountForm(props) {
initiateVerification,
submitting,
invalid,
pristine
pristine,
t
} = props;

const handleInitiateVerification = (evt) => {
Expand All @@ -24,10 +26,10 @@ function AccountForm(props) {
return (
<form className="form" onSubmit={handleSubmit(props.updateSettings)}>
<p className="form__field">
<label htmlFor="email" className="form__label">Email</label>
<label htmlFor="email" className="form__label">{t('AccountForm.Email')}</label>
<input
className="form__input"
aria-label="email"
aria-label={t('AccountForm.EmailARIA')}
type="text"
id="email"
{...domOnlyProps(email)}
Expand All @@ -38,27 +40,27 @@ function AccountForm(props) {
user.verified !== 'verified' &&
(
<p className="form__context">
<span className="form__status">Unconfirmed.</span>
<span className="form__status">{t('AccountForm.Unconfirmed')}</span>
{
user.emailVerificationInitiate === true ?
(
<span className="form__status"> Confirmation sent, check your email.</span>
<span className="form__status"> {t('AccountForm.EmailSent')}</span>
) :
(
<Button
onClick={handleInitiateVerification}
>Resend confirmation email
>{t('AccountForm.Resend')}
</Button>
)
}
</p>
)
}
<p className="form__field">
<label htmlFor="username" className="form__label">User Name</label>
<label htmlFor="username" className="form__label">{t('AccountForm.UserName')}</label>
<input
className="form__input"
aria-label="username"
aria-label={t('AccountForm.UserNameARIA')}
type="text"
id="username"
defaultValue={username}
Expand All @@ -67,10 +69,10 @@ function AccountForm(props) {
{username.touched && username.error && <span className="form-error">{username.error}</span>}
</p>
<p className="form__field">
<label htmlFor="current password" className="form__label">Current Password</label>
<label htmlFor="current password" className="form__label">{t('AccountForm.CurrentPassword')}</label>
<input
className="form__input"
aria-label="currentPassword"
aria-label={t('AccountForm.CurrentPasswordARIA')}
type="password"
id="currentPassword"
{...domOnlyProps(currentPassword)}
Expand All @@ -82,10 +84,10 @@ function AccountForm(props) {
}
</p>
<p className="form__field">
<label htmlFor="new password" className="form__label">New Password</label>
<label htmlFor="new password" className="form__label">{t('AccountForm.NewPassword')}</label>
<input
className="form__input"
aria-label="newPassword"
aria-label={t('AccountForm.NewPasswordARIA')}
type="password"
id="newPassword"
{...domOnlyProps(newPassword)}
Expand All @@ -95,7 +97,7 @@ function AccountForm(props) {
<Button
type="submit"
disabled={submitting || invalid || pristine}
>Save All Settings
>{t('AccountForm.SubmitSaveAllSettings')}
</Button>
</form>
);
Expand All @@ -118,6 +120,7 @@ AccountForm.propTypes = {
submitting: PropTypes.bool,
invalid: PropTypes.bool,
pristine: PropTypes.bool,
t: PropTypes.func.isRequired
};

AccountForm.defaultProps = {
Expand All @@ -126,4 +129,4 @@ AccountForm.defaultProps = {
invalid: false
};

export default AccountForm;
export default withTranslation()(AccountForm);
18 changes: 10 additions & 8 deletions client/modules/User/components/NewPasswordForm.jsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,33 @@
import PropTypes from 'prop-types';
import React from 'react';

import { withTranslation } from 'react-i18next';
import { domOnlyProps } from '../../../utils/reduxFormUtils';
import Button from '../../../common/Button';

function NewPasswordForm(props) {
const {
fields: { password, confirmPassword }, handleSubmit, submitting, invalid, pristine
fields: { password, confirmPassword }, handleSubmit, submitting, invalid, pristine,
t
} = props;
return (
<form className="form" onSubmit={handleSubmit(props.updatePassword.bind(this, props.params.reset_password_token))}>
<p className="form__field">
<label htmlFor="password" className="form__label">Password</label>
<label htmlFor="password" className="form__label">{t('NewPasswordForm.Title')}</label>
<input
className="form__input"
aria-label="password"
aria-label={t('NewPasswordForm.TitleARIA')}
type="password"
id="Password"
{...domOnlyProps(password)}
/>
{password.touched && password.error && <span className="form-error">{password.error}</span>}
</p>
<p className="form__field">
<label htmlFor="confirm password" className="form__label">Confirm Password</label>
<label htmlFor="confirm password" className="form__label">{t('NewPasswordForm.ConfirmPassword')}</label>
<input
className="form__input"
type="password"
aria-label="confirm password"
aria-label={t('NewPasswordForm.ConfirmPasswordARIA')}
id="confirm password"
{...domOnlyProps(confirmPassword)}
/>
Expand All @@ -36,7 +37,7 @@ function NewPasswordForm(props) {
<span className="form-error">{confirmPassword.error}</span>
}
</p>
<Button type="submit" disabled={submitting || invalid || pristine}>Set New Password</Button>
<Button type="submit" disabled={submitting || invalid || pristine}>{t('NewPasswordForm.SubmitSetNewPassword')}</Button>
</form>
);
}
Expand All @@ -54,6 +55,7 @@ NewPasswordForm.propTypes = {
params: PropTypes.shape({
reset_password_token: PropTypes.string,
}).isRequired,
t: PropTypes.func.isRequired
};

NewPasswordForm.defaultProps = {
Expand All @@ -62,4 +64,4 @@ NewPasswordForm.defaultProps = {
submitting: false
};

export default NewPasswordForm;
export default withTranslation()(NewPasswordForm);
22 changes: 13 additions & 9 deletions client/modules/User/pages/AccountView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { reduxForm } from 'redux-form';
import { bindActionCreators } from 'redux';
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
import { Helmet } from 'react-helmet';
import { withTranslation } from 'react-i18next';
import { updateSettings, initiateVerification, createApiKey, removeApiKey } from '../actions';
import AccountForm from '../components/AccountForm';
import apiClient from '../../../utils/apiClient';
Expand All @@ -16,9 +17,11 @@ function SocialLoginPanel(props) {
return (
<React.Fragment>
<AccountForm {...props} />
<h2 className="form-container__divider">Social Login</h2>
{/* eslint-disable-next-line react/prop-types */}
<h2 className="form-container__divider">{props.t('AccountView.SocialLogin')}</h2>
<p className="account__social-text">
Use your GitHub or Google account to log into the p5.js Web Editor.
{/* eslint-disable-next-line react/prop-types */}
{props.t('AccountView.SocialLoginDescription')}
</p>
<div className="account__social-stack">
<SocialAuthButton service={SocialAuthButton.services.github} />
Expand All @@ -39,21 +42,21 @@ class AccountView extends React.Component {
return (
<div className="account-settings__container">
<Helmet>
<title>p5.js Web Editor | Account Settings</title>
<title>{this.props.t('AccountView.Title')}</title>
</Helmet>

<Nav layout="dashboard" />

<main className="account-settings">
<header className="account-settings__header">
<h1 className="account-settings__title">Account Settings</h1>
<h1 className="account-settings__title">{this.props.t('AccountView.Settings')}</h1>
</header>
{accessTokensUIEnabled &&
<Tabs className="account__tabs">
<TabList>
<div className="tabs__titles">
<Tab><h4 className="tabs__title">Account</h4></Tab>
{accessTokensUIEnabled && <Tab><h4 className="tabs__title">Access Tokens</h4></Tab>}
<Tab><h4 className="tabs__title">{this.props.t('AccountView.AccountTab')}</h4></Tab>
{accessTokensUIEnabled && <Tab><h4 className="tabs__title">{this.props.t('AccountView.AccessTokensTab')}</h4></Tab>}
</div>
</TabList>
<TabPanel>
Expand Down Expand Up @@ -107,13 +110,14 @@ function asyncValidate(formProps, dispatch, props) {

AccountView.propTypes = {
previousPath: PropTypes.string.isRequired,
theme: PropTypes.string.isRequired
theme: PropTypes.string.isRequired,
t: PropTypes.func.isRequired
};

export default reduxForm({
export default withTranslation()(reduxForm({
form: 'updateAllSettings',
fields: ['username', 'email', 'currentPassword', 'newPassword'],
validate: validateSettings,
asyncValidate,
asyncBlurFields: ['username', 'email', 'currentPassword']
}, mapStateToProps, mapDispatchToProps)(AccountView);
}, mapStateToProps, mapDispatchToProps)(AccountView));
Loading