mirror of
https://github.com/strapi/strapi.git
synced 2025-12-24 05:34:33 +00:00
Move auth views to the admin
This commit is contained in:
parent
b7bbca6bfc
commit
1cd7084a94
@ -9,6 +9,7 @@ import {
|
||||
ENABLE_GLOBAL_OVERLAY_BLOCKER,
|
||||
FREEZE_APP,
|
||||
GET_APP_PLUGINS_SUCCEEDED,
|
||||
GET_DATA_SUCCEEDED,
|
||||
LOAD_PLUGIN,
|
||||
PLUGIN_DELETED,
|
||||
PLUGIN_LOADED,
|
||||
@ -36,6 +37,13 @@ export function freezeApp(data) {
|
||||
};
|
||||
}
|
||||
|
||||
export function getDataSucceeded(hasAdminUser) {
|
||||
return {
|
||||
type: GET_DATA_SUCCEEDED,
|
||||
hasAdminUser,
|
||||
};
|
||||
}
|
||||
|
||||
export function getAppPluginsSucceeded(plugins) {
|
||||
return {
|
||||
type: GET_APP_PLUGINS_SUCCEEDED,
|
||||
|
||||
@ -16,3 +16,4 @@ export const DISABLE_GLOBAL_OVERLAY_BLOCKER =
|
||||
'app/App/OverlayBlocker/DISABLE_GLOBAL_OVERLAY_BLOCKER';
|
||||
export const ENABLE_GLOBAL_OVERLAY_BLOCKER =
|
||||
'app/App/OverlayBlocker/ENABLE_GLOBAL_OVERLAY_BLOCKER';
|
||||
export const GET_DATA_SUCCEEDED = 'app/App/OverlayBlocker/GET_DATA_SUCCEEDED';
|
||||
|
||||
@ -11,9 +11,12 @@
|
||||
* the linting exception.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Switch, Route } from 'react-router-dom';
|
||||
import { LoadingIndicatorPage } from 'strapi-helper-plugin';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators, compose } from 'redux';
|
||||
import { LoadingIndicatorPage, request } from 'strapi-helper-plugin';
|
||||
|
||||
import Admin from '../Admin';
|
||||
import NotFoundPage from '../NotFoundPage';
|
||||
@ -22,12 +25,32 @@ import AppLoader from '../AppLoader';
|
||||
import styles from './styles.scss';
|
||||
import AuthPage from '../AuthPage';
|
||||
|
||||
import { getDataSucceeded } from './actions';
|
||||
|
||||
function App(props) {
|
||||
const getDataRef = useRef();
|
||||
getDataRef.current = props.getDataSucceeded;
|
||||
|
||||
useEffect(() => {
|
||||
const getData = async () => {
|
||||
try {
|
||||
const requestURL = '/users-permissions/init';
|
||||
|
||||
const { hasAdmin } = await request(requestURL, { method: 'GET' });
|
||||
getDataRef.current(hasAdmin);
|
||||
} catch (err) {
|
||||
strapi.notification.error('app.containers.App.notification.error.init');
|
||||
}
|
||||
};
|
||||
|
||||
getData();
|
||||
}, [getDataRef]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<NotificationProvider />
|
||||
<AppLoader>
|
||||
{({ shouldLoad }) => {
|
||||
{({ hasAdminUser, shouldLoad }) => {
|
||||
if (shouldLoad) {
|
||||
return <LoadingIndicatorPage />;
|
||||
}
|
||||
@ -38,7 +61,11 @@ function App(props) {
|
||||
<Route
|
||||
path="/auth/:authType"
|
||||
render={routerProps => (
|
||||
<AuthPage {...props} {...routerProps} />
|
||||
<AuthPage
|
||||
{...props}
|
||||
{...routerProps}
|
||||
hasAdminUser={hasAdminUser}
|
||||
/>
|
||||
)}
|
||||
exact
|
||||
/>
|
||||
@ -56,6 +83,17 @@ function App(props) {
|
||||
);
|
||||
}
|
||||
|
||||
App.propTypes = {};
|
||||
App.propTypes = {
|
||||
getDataSucceeded: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default App;
|
||||
export function mapDispatchToProps(dispatch) {
|
||||
return bindActionCreators({ getDataSucceeded }, dispatch);
|
||||
}
|
||||
|
||||
const withConnect = connect(
|
||||
null,
|
||||
mapDispatchToProps
|
||||
);
|
||||
|
||||
export default compose(withConnect)(App);
|
||||
|
||||
@ -6,6 +6,7 @@ import {
|
||||
ENABLE_GLOBAL_OVERLAY_BLOCKER,
|
||||
FREEZE_APP,
|
||||
GET_APP_PLUGINS_SUCCEEDED,
|
||||
GET_DATA_SUCCEEDED,
|
||||
PLUGIN_DELETED,
|
||||
PLUGIN_LOADED,
|
||||
UNFREEZE_APP,
|
||||
@ -17,8 +18,11 @@ const initialState = fromJS({
|
||||
appPlugins: List([]),
|
||||
blockApp: false,
|
||||
overlayBlockerData: null,
|
||||
// TODO remove when new lifecycle
|
||||
hasUserPlugin: true,
|
||||
hasAdminUser: false,
|
||||
isAppLoading: true,
|
||||
isLoading: true,
|
||||
plugins: {},
|
||||
showGlobalAppBlocker: true,
|
||||
});
|
||||
@ -41,12 +45,16 @@ function appReducer(state = initialState, action) {
|
||||
return state
|
||||
.update('appPlugins', () => List(action.appPlugins))
|
||||
.update('isAppLoading', () => false);
|
||||
case GET_DATA_SUCCEEDED:
|
||||
return state
|
||||
.update('isLoading', () => false)
|
||||
.update('hasAdminUser', () => action.hasAdminUser);
|
||||
case PLUGIN_LOADED:
|
||||
return state.setIn(['plugins', action.plugin.id], fromJS(action.plugin));
|
||||
case UPDATE_PLUGIN:
|
||||
return state.setIn(
|
||||
['plugins', action.pluginId, action.updatedKey],
|
||||
fromJS(action.updatedValue),
|
||||
fromJS(action.updatedValue)
|
||||
);
|
||||
case PLUGIN_DELETED:
|
||||
return state.deleteIn(['plugins', action.plugin]);
|
||||
|
||||
@ -11,21 +11,27 @@ import makeSelectApp from '../App/selectors';
|
||||
|
||||
export class AppLoader extends React.Component {
|
||||
shouldLoad = () => {
|
||||
const { appPlugins, plugins: mountedPlugins } = this.props;
|
||||
const { appPlugins, isLoading, plugins: mountedPlugins } = this.props;
|
||||
|
||||
if (isLoading) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return appPlugins.length !== Object.keys(mountedPlugins).length;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { children } = this.props;
|
||||
const { children, hasAdminUser } = this.props;
|
||||
|
||||
return children({ shouldLoad: this.shouldLoad() });
|
||||
return children({ hasAdminUser, shouldLoad: this.shouldLoad() });
|
||||
}
|
||||
}
|
||||
|
||||
AppLoader.propTypes = {
|
||||
appPlugins: PropTypes.array.isRequired,
|
||||
children: PropTypes.func.isRequired,
|
||||
hasAdminUser: PropTypes.bool.isRequired,
|
||||
isLoading: PropTypes.bool.isRequired,
|
||||
plugins: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
@ -33,5 +39,5 @@ const mapStateToProps = makeSelectApp();
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
null,
|
||||
null
|
||||
)(AppLoader);
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import React, { memo } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import PropTypes from 'prop-types';
|
||||
import { get } from 'lodash';
|
||||
|
||||
import { InputsIndex as Inputs } from 'strapi-helper-plugin';
|
||||
import CustomLabel from './CustomLabel';
|
||||
@ -8,8 +9,11 @@ import CustomLabel from './CustomLabel';
|
||||
const Input = ({
|
||||
autoFocus,
|
||||
customBootstrapClass,
|
||||
didCheckErrors,
|
||||
errors,
|
||||
label,
|
||||
name,
|
||||
noErrorsDescription,
|
||||
onChange,
|
||||
placeholder,
|
||||
type,
|
||||
@ -58,10 +62,11 @@ const Input = ({
|
||||
<Inputs
|
||||
autoFocus={autoFocus}
|
||||
customBootstrapClass={customBootstrapClass || 'col-12'}
|
||||
didCheckErrors={false}
|
||||
errors={{}}
|
||||
didCheckErrors={didCheckErrors}
|
||||
errors={get(errors, name, [])}
|
||||
label={inputLabel}
|
||||
name={name}
|
||||
noErrorsDescription={noErrorsDescription}
|
||||
onChange={onChange}
|
||||
placeholder={placeholder}
|
||||
type={type}
|
||||
@ -74,8 +79,11 @@ const Input = ({
|
||||
Input.propTypes = {
|
||||
autoFocus: PropTypes.bool,
|
||||
customBootstrapClass: PropTypes.string,
|
||||
didCheckErrors: PropTypes.bool.isRequired,
|
||||
errors: PropTypes.object.isRequired,
|
||||
label: PropTypes.object,
|
||||
name: PropTypes.string.isRequired,
|
||||
noErrorsDescription: PropTypes.bool.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
placeholder: PropTypes.string,
|
||||
type: PropTypes.string.isRequired,
|
||||
|
||||
@ -40,7 +40,9 @@ const Wrapper = styled.div`
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
// padding: 13px 30px 0 30px;
|
||||
padding: 8px 30px 0 30px;
|
||||
padding: ${({ authType }) =>
|
||||
authType === 'register' ? '13px 30px 17px 30px' : '8px 30px 0 30px'};
|
||||
|
||||
line-height: 18px;
|
||||
color: #333740;
|
||||
}
|
||||
@ -85,7 +87,8 @@ const Wrapper = styled.div`
|
||||
}
|
||||
|
||||
.bordered {
|
||||
border-top: 2px solid #1c5de7;
|
||||
border-top: 2px solid
|
||||
${({ withSucessBorder }) => (withSucessBorder ? '#5a9e06' : '#1c5de7')};
|
||||
}
|
||||
|
||||
.borderedSuccess {
|
||||
|
||||
@ -3,7 +3,6 @@ import { translatedErrors } from 'strapi-helper-plugin';
|
||||
|
||||
const form = {
|
||||
'forgot-password': {
|
||||
endPoint: '/auth/forgot-password',
|
||||
inputs: [
|
||||
[
|
||||
{
|
||||
@ -24,7 +23,6 @@ const form = {
|
||||
}),
|
||||
},
|
||||
login: {
|
||||
endPoint: '/auth/local',
|
||||
inputs: [
|
||||
[
|
||||
{
|
||||
@ -62,7 +60,6 @@ const form = {
|
||||
}),
|
||||
},
|
||||
register: {
|
||||
endPoint: '/auth/local/register',
|
||||
inputs: [
|
||||
[
|
||||
{
|
||||
@ -126,26 +123,14 @@ const form = {
|
||||
passwordConfirmation: yup
|
||||
.string()
|
||||
.min(6, translatedErrors.minLength)
|
||||
.oneOf([yup.ref('password'), null])
|
||||
.required({ id: 'components.Input.error.password.noMatch' }),
|
||||
.oneOf(
|
||||
[yup.ref('password'), null],
|
||||
'components.Input.error.password.noMatch'
|
||||
)
|
||||
.required(translatedErrors.required),
|
||||
}),
|
||||
},
|
||||
'register-success': {
|
||||
inputs: [
|
||||
[
|
||||
{
|
||||
name: 'email',
|
||||
type: 'email',
|
||||
label: {
|
||||
id: 'Auth.form.register-success.email.label',
|
||||
},
|
||||
placeholder: 'Auth.form.register-success.email.placeholder',
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
'reset-password': {
|
||||
endPoint: '/auth/reset-password',
|
||||
inputs: [
|
||||
[
|
||||
{
|
||||
@ -175,8 +160,11 @@ const form = {
|
||||
passwordConfirmation: yup
|
||||
.string()
|
||||
.min(6, translatedErrors.required)
|
||||
.oneOf([yup.ref('password'), null])
|
||||
.required({ id: 'components.Input.error.password.noMatch' }),
|
||||
.oneOf(
|
||||
[yup.ref('password'), null],
|
||||
'components.Input.error.password.noMatch'
|
||||
)
|
||||
.required(translatedErrors.required),
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,9 +1,16 @@
|
||||
import React, { memo, useReducer } from 'react';
|
||||
import React, { memo, useEffect, useReducer, useRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { get, isEmpty, omit, set } from 'lodash';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { upperFirst } from 'lodash';
|
||||
import { Link, Redirect } from 'react-router-dom';
|
||||
import { Button, getYupInnerErrors } from 'strapi-helper-plugin';
|
||||
import {
|
||||
auth,
|
||||
Button,
|
||||
getQueryParameters,
|
||||
getYupInnerErrors,
|
||||
request,
|
||||
} from 'strapi-helper-plugin';
|
||||
import NavTopRightWrapper from '../../components/NavTopRightWrapper';
|
||||
import LogoStrapi from '../../assets/images/logo_strapi.png';
|
||||
import PageTitle from '../../components/PageTitle';
|
||||
@ -12,14 +19,43 @@ import Wrapper from './Wrapper';
|
||||
import Input from './Input';
|
||||
import forms from './forms';
|
||||
import reducer, { initialState } from './reducer';
|
||||
import formatErrorFromRequest from './utils/formatErrorFromRequest';
|
||||
|
||||
const AuthPage = ({
|
||||
hasAdminUser,
|
||||
history: { push },
|
||||
location: { search },
|
||||
match: {
|
||||
params: { authType },
|
||||
},
|
||||
}) => {
|
||||
const [reducerState, dispatch] = useReducer(reducer, initialState);
|
||||
const { modifiedData } = reducerState.toJS();
|
||||
const codeRef = useRef();
|
||||
codeRef.current = getQueryParameters(search, 'code');
|
||||
useEffect(() => {
|
||||
// Set the reset code provided by the url
|
||||
if (authType === 'reset-password') {
|
||||
dispatch({
|
||||
type: 'ON_CHANGE',
|
||||
keys: ['code'],
|
||||
value: codeRef.current,
|
||||
});
|
||||
} else {
|
||||
// Clean reducer upon navigation
|
||||
dispatch({
|
||||
type: 'RESET_PROPS',
|
||||
});
|
||||
}
|
||||
|
||||
return () => {};
|
||||
}, [authType, codeRef]);
|
||||
const {
|
||||
didCheckErrors,
|
||||
errors,
|
||||
modifiedData,
|
||||
submitSuccess,
|
||||
userEmail,
|
||||
} = reducerState.toJS();
|
||||
const handleChange = ({ target: { name, value } }) => {
|
||||
dispatch({
|
||||
type: 'ON_CHANGE',
|
||||
@ -27,20 +63,63 @@ const AuthPage = ({
|
||||
value,
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = async e => {
|
||||
e.preventDefault();
|
||||
const schema = forms[authType].schema;
|
||||
let errors = {};
|
||||
let formErrors = {};
|
||||
|
||||
try {
|
||||
await schema.validate(modifiedData, { abortEarly: false });
|
||||
|
||||
try {
|
||||
const requestEndPoint = authType === 'login' ? 'local' : authType;
|
||||
const requestURL = `/admin/auth/${requestEndPoint}`;
|
||||
const body = omit(modifiedData, 'news');
|
||||
|
||||
if (authType === 'forgot-password') {
|
||||
set(body, 'url', `${strapi.backendURL}/admin/auth/reset-password`);
|
||||
}
|
||||
|
||||
const { jwt, user, ok } = await request(requestURL, {
|
||||
method: 'POST',
|
||||
body,
|
||||
});
|
||||
|
||||
if (authType === 'forgot-password' && ok === true) {
|
||||
dispatch({
|
||||
type: 'SUBMIT_SUCCESS',
|
||||
email: modifiedData.email,
|
||||
});
|
||||
} else {
|
||||
auth.setToken(jwt, modifiedData.rememberMe);
|
||||
auth.setUserInfo(user, modifiedData.rememberMe);
|
||||
push('/');
|
||||
}
|
||||
} catch (err) {
|
||||
const formattedError = formatErrorFromRequest(err);
|
||||
|
||||
if (authType === 'login') {
|
||||
formErrors = {
|
||||
global: formattedError,
|
||||
identifier: formattedError,
|
||||
password: formattedError,
|
||||
};
|
||||
} else if (authType === 'forgot-password') {
|
||||
formErrors = { email: formattedError };
|
||||
} else {
|
||||
strapi.notification.error(
|
||||
get(formattedError, '0.id', 'notification.error')
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
errors = getYupInnerErrors(err);
|
||||
formErrors = getYupInnerErrors(err);
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: 'SET_ERRORS',
|
||||
errors,
|
||||
formErrors,
|
||||
});
|
||||
};
|
||||
|
||||
@ -49,13 +128,17 @@ const AuthPage = ({
|
||||
return <Redirect to="/" />;
|
||||
}
|
||||
|
||||
// TODO Remove temporary
|
||||
const hasErrors = false;
|
||||
if (hasAdminUser && authType === 'register') {
|
||||
return <Redirect to="/auth/login" />;
|
||||
}
|
||||
|
||||
const globalError = get(errors, 'global.0.id', '');
|
||||
const shouldShowFormErrors = !isEmpty(globalError);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageTitle title={upperFirst(authType)} />
|
||||
<Wrapper>
|
||||
<Wrapper authType={authType} withSucessBorder={submitSuccess}>
|
||||
<NavTopRightWrapper>
|
||||
<LocaleToggle isLogged className="localeDropdownMenuNotLogged" />
|
||||
</NavTopRightWrapper>
|
||||
@ -76,25 +159,36 @@ const AuthPage = ({
|
||||
<div className="formContainer bordered">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="container-fluid">
|
||||
{/* TODO ERROR CONTAINER */}
|
||||
{hasErrors && (
|
||||
{shouldShowFormErrors && (
|
||||
<div className="errorsContainer">
|
||||
{/* TODO DISPLAY ERRORS */}
|
||||
<FormattedMessage id={globalError} />
|
||||
</div>
|
||||
)}
|
||||
<div className="row" style={{ textAlign: 'start' }}>
|
||||
{forms[authType].inputs.map(row => {
|
||||
return row.map(input => {
|
||||
return (
|
||||
<Input
|
||||
{...input}
|
||||
key={input.name}
|
||||
onChange={handleChange}
|
||||
value={modifiedData[input.name]}
|
||||
/>
|
||||
);
|
||||
});
|
||||
})}
|
||||
{submitSuccess && (
|
||||
<div className="forgotSuccess">
|
||||
<FormattedMessage id="Auth.form.forgot-password.email.label.success" />
|
||||
<br />
|
||||
<p>{userEmail}</p>
|
||||
</div>
|
||||
)}
|
||||
{!submitSuccess &&
|
||||
forms[authType].inputs.map((row, index) => {
|
||||
return row.map(input => {
|
||||
return (
|
||||
<Input
|
||||
{...input}
|
||||
autoFocus={index === 0}
|
||||
didCheckErrors={didCheckErrors}
|
||||
errors={errors}
|
||||
key={input.name}
|
||||
noErrorsDescription={shouldShowFormErrors}
|
||||
onChange={handleChange}
|
||||
value={modifiedData[input.name]}
|
||||
/>
|
||||
);
|
||||
});
|
||||
})}
|
||||
<div
|
||||
className={`${
|
||||
authType === 'login'
|
||||
@ -103,12 +197,13 @@ const AuthPage = ({
|
||||
}`}
|
||||
>
|
||||
<Button
|
||||
className={submitSuccess ? 'buttonForgotSuccess' : ''}
|
||||
type="submit"
|
||||
label="Auth.form.button.login"
|
||||
primary
|
||||
style={
|
||||
authType === 'forgot-password' ? { width: '100%' } : {}
|
||||
}
|
||||
label={`Auth.form.button.${
|
||||
submitSuccess ? 'forgot-password.success' : authType
|
||||
}`}
|
||||
primary={!submitSuccess}
|
||||
style={authType === 'login' ? {} : { width: '100%' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -142,6 +237,13 @@ const AuthPage = ({
|
||||
};
|
||||
|
||||
AuthPage.propTypes = {
|
||||
hasAdminUser: PropTypes.bool.isRequired,
|
||||
history: PropTypes.shape({
|
||||
push: PropTypes.func.isRequired,
|
||||
}),
|
||||
location: PropTypes.shape({
|
||||
search: PropTypes.string.isRequired,
|
||||
}),
|
||||
match: PropTypes.shape({
|
||||
params: PropTypes.shape({
|
||||
authType: PropTypes.string,
|
||||
|
||||
@ -4,6 +4,8 @@ const initialState = fromJS({
|
||||
didCheckErrors: false,
|
||||
errors: {},
|
||||
modifiedData: {},
|
||||
userEmail: '',
|
||||
submitSuccess: false,
|
||||
});
|
||||
|
||||
const reducer = (state, action) => {
|
||||
@ -13,10 +15,16 @@ const reducer = (state, action) => {
|
||||
['modifiedData', ...action.keys],
|
||||
() => action.value
|
||||
);
|
||||
case 'RESET_PROPS':
|
||||
return initialState;
|
||||
case 'SET_ERRORS':
|
||||
return state
|
||||
.update('errors', () => action.errors)
|
||||
.update('errors', () => action.formErrors)
|
||||
.update('didCheckErrors', v => !v);
|
||||
case 'SUBMIT_SUCCESS':
|
||||
return state
|
||||
.update('userEmail', () => action.email)
|
||||
.update('submitSuccess', () => true);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
import { get } from 'lodash';
|
||||
|
||||
const formatErrorFromRequest = errorResponse => {
|
||||
const messages = get(errorResponse, ['response', 'payload', 'message'], []);
|
||||
|
||||
return messages.reduce((acc, current) => {
|
||||
const err = current.messages.reduce(
|
||||
(acc, key) => {
|
||||
acc.id = key.id;
|
||||
|
||||
return acc;
|
||||
},
|
||||
{ id: '' }
|
||||
);
|
||||
|
||||
acc.push(err);
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
};
|
||||
|
||||
export default formatErrorFromRequest;
|
||||
@ -181,5 +181,6 @@
|
||||
"Auth.form.register.username.placeholder": "اكتب اسمك هنا (مثل: خالد سالم)",
|
||||
"Auth.header.register.description": "لإنهاء الإعداد وتأمين تطبيقك ، يرجى إنشاء أول مستخدم (مسؤول أساسي) عن طريق إدخال المعلومات الضرورية أدناه.",
|
||||
"Auth.link.forgot-password": "هل نسيت كلمة السر الخاصة بك؟",
|
||||
"Auth.link.ready": "مستعد لتسجيل الدخول؟"
|
||||
"Auth.link.ready": "مستعد لتسجيل الدخول؟",
|
||||
"components.Input.error.password.noMatch": "كلمات السر لا تتطابق"
|
||||
}
|
||||
|
||||
@ -184,5 +184,6 @@
|
||||
"Auth.form.register.username.placeholder": "John Doe",
|
||||
"Auth.header.register.description": "Erstelle bitte einen Administrator durch Ausfüllen der folgenden Felder, um die Einrichtung deiner App abzuschließen.",
|
||||
"Auth.link.forgot-password": "Passwort vergessen?",
|
||||
"Auth.link.ready": "Bereit für den Login?"
|
||||
"Auth.link.ready": "Bereit für den Login?",
|
||||
"components.Input.error.password.noMatch": "Passwörter stimmen nicht überein"
|
||||
}
|
||||
|
||||
@ -210,5 +210,7 @@
|
||||
"Auth.form.register.username.placeholder": "John Doe",
|
||||
"Auth.header.register.description": "To finish setup and secure your app, please create the first user (root admin) by entering the necessary information below.",
|
||||
"Auth.link.forgot-password": "Forgot your password?",
|
||||
"Auth.link.ready": "Ready to sign in?"
|
||||
"Auth.link.ready": "Ready to sign in?",
|
||||
"app.containers.App.notification.error.init": "An error occured while requesting the API",
|
||||
"components.Input.error.password.noMatch": "Passwords do not match"
|
||||
}
|
||||
|
||||
@ -199,5 +199,6 @@
|
||||
"Auth.form.register.username.placeholder": "John Doe",
|
||||
"Auth.header.register.description": "Para terminar de configurar y asegurar su aplicación, por favor cree el primer usuario (administrador) ingresando a continuación la información necesaria.",
|
||||
"Auth.link.forgot-password": "¿Olvidó su contraseña?",
|
||||
"Auth.link.ready": "¿Listo para iniciar sesión?"
|
||||
"Auth.link.ready": "¿Listo para iniciar sesión?",
|
||||
"components.Input.error.password.noMatch": "Las contraseñas no coinciden"
|
||||
}
|
||||
|
||||
@ -210,5 +210,7 @@
|
||||
"Auth.form.register.username.placeholder": "John Doe",
|
||||
"Auth.header.register.description": "Pour finir le setup et sécuriser votre app, merci de créer le premier utilisateur (root admin) en remplissant le formulaire suivant.",
|
||||
"Auth.link.forgot-password": "Mot de passe oublié ?",
|
||||
"Auth.link.ready": "Prêt à vous connecter ?"
|
||||
"Auth.link.ready": "Prêt à vous connecter ?",
|
||||
"app.containers.App.notification.error.init": "Une erreur est survenue en requêtant l'API",
|
||||
"components.Input.error.password.noMatch": "Le mot de passe ne correspond pas"
|
||||
}
|
||||
|
||||
@ -181,5 +181,6 @@
|
||||
"Auth.form.register.username.placeholder": "John Doe",
|
||||
"Auth.header.register.description": "Per terminare l'installazione e mettere in sicurezza la tua applicazione è necessario creare il primo utente (root admin) inserendo le informazioni necessarie di seguito riportate",
|
||||
"Auth.link.forgot-password": "Password dimenticata?",
|
||||
"Auth.link.ready": "Sei pronto per accedere?"
|
||||
"Auth.link.ready": "Sei pronto per accedere?",
|
||||
"components.Input.error.password.noMatch": "La password non corrisponde"
|
||||
}
|
||||
|
||||
@ -184,5 +184,6 @@
|
||||
"Auth.form.register.username.placeholder": "John Doe",
|
||||
"Auth.header.register.description": "セットアップを完了してアプリを保護するには、以下の情報を入力して最初のユーザー(ルート管理者)を作成してください。",
|
||||
"Auth.link.forgot-password": "パスワードをお忘れですか?",
|
||||
"Auth.link.ready": "サインインする準備ができましたか?"
|
||||
"Auth.link.ready": "サインインする準備ができましたか?",
|
||||
"components.Input.error.password.noMatch": "パスワードが一致しません"
|
||||
}
|
||||
|
||||
@ -195,5 +195,6 @@
|
||||
"Auth.form.register.username.placeholder": "JohnDoe",
|
||||
"Auth.header.register.description": "애플리케이션 설정 및 보안을 위해 첫 번째 사용자(root 관리자)를 생성하세요.",
|
||||
"Auth.link.forgot-password": "패스워드 재설정",
|
||||
"Auth.link.ready": "로그인 하시겠습니까?"
|
||||
"Auth.link.ready": "로그인 하시겠습니까?",
|
||||
"components.Input.error.password.noMatch": "패스워드가 일치하지 않습니다."
|
||||
}
|
||||
|
||||
@ -184,5 +184,6 @@
|
||||
"Auth.form.register.username.placeholder": "John Doe",
|
||||
"Auth.header.register.description": "Om de set-up af te maken en je app te beveiligen, maak a.u.b. een eerste gebruiker (root admin) door alle velden hier beneden in te vullen.",
|
||||
"Auth.link.forgot-password": "Wachtwoord vergeten?",
|
||||
"Auth.link.ready": "Klaar om in te loggen?"
|
||||
"Auth.link.ready": "Klaar om in te loggen?",
|
||||
"components.Input.error.password.noMatch": "Wachtwoorden komen niet overeen"
|
||||
}
|
||||
|
||||
@ -183,5 +183,6 @@
|
||||
"Auth.form.register.username.placeholder": "Jan Nowak",
|
||||
"Auth.header.register.description": "Aby zakończyć konfigurację i zabezpieczyć swoją aplikację, utwórz pierwszego użytkownika (administratora root), wypełniając poniższe informacje.",
|
||||
"Auth.link.forgot-password": "Nie pamiętasz hasła?",
|
||||
"Auth.link.ready": "Zaczynamy?"
|
||||
"Auth.link.ready": "Zaczynamy?",
|
||||
"components.Input.error.password.noMatch": "Hasło nie pasuje"
|
||||
}
|
||||
|
||||
@ -181,5 +181,6 @@
|
||||
"Auth.form.register.username.placeholder": "John Doe",
|
||||
"Auth.header.register.description": "Para concluir a configuração e proteger seu aplicativo, crie o primeiro usuário (administrador root), digitando as informações necessárias abaixo.",
|
||||
"Auth.link.forgot-password": "Esqueceu sua senha?",
|
||||
"Auth.link.ready": "Pronto para entrar?"
|
||||
"Auth.link.ready": "Pronto para entrar?",
|
||||
"components.Input.error.password.noMatch": "As senhas não conferem"
|
||||
}
|
||||
|
||||
@ -183,5 +183,6 @@
|
||||
"Auth.form.register.username.placeholder": "John Doe",
|
||||
"Auth.header.register.description": "Para terminar a configuração e melhorar a segurança da sua aplicação, por favor crie o primeiro utilizador (root admin) inserindo a informação necessária abaixo.",
|
||||
"Auth.link.forgot-password": "Esqueceu a palavra-passe?",
|
||||
"Auth.link.ready": "Preparado para entrar?"
|
||||
"Auth.link.ready": "Preparado para entrar?",
|
||||
"components.Input.error.password.noMatch": "As passwords não coincidem"
|
||||
}
|
||||
|
||||
@ -199,5 +199,6 @@
|
||||
"Auth.form.register.username.placeholder": "John Doe",
|
||||
"Auth.header.register.description": "Для завершения установки и обеспечения безопасности приложения, создайте вашего первого пользователя (root admin), заполнив форму ниже.",
|
||||
"Auth.link.forgot-password": "Забыли пароль?",
|
||||
"Auth.link.ready": "Готовы войти?"
|
||||
"Auth.link.ready": "Готовы войти?",
|
||||
"components.Input.error.password.noMatch": "Пароль не совпадает"
|
||||
}
|
||||
|
||||
@ -184,5 +184,6 @@
|
||||
"Auth.form.register.username.placeholder": "John Doe",
|
||||
"Auth.header.register.description": "Kurulumu tamamlamak ve uygulamanızı güvence altına almak için, lütfen aşağıdaki gerekli bilgileri girerek ilk kullanıcıyı (root admin) oluşturun.",
|
||||
"Auth.link.forgot-password": "Parolanızı mı unuttunuz ?",
|
||||
"Auth.link.ready": "Zaten kayıtlı mısınız?"
|
||||
"Auth.link.ready": "Zaten kayıtlı mısınız?",
|
||||
"components.Input.error.password.noMatch": "Şifreler uyuşmuyor"
|
||||
}
|
||||
|
||||
@ -176,5 +176,6 @@
|
||||
"Auth.form.register.username.placeholder": "John Doe",
|
||||
"Auth.header.register.description": "要完成安装并保护您的应用程序,请通过输入必要的信息来创建第一个用户(root管理员)。",
|
||||
"Auth.link.forgot-password": "忘记密码了吗?",
|
||||
"Auth.link.ready": "准备好登陆?"
|
||||
"Auth.link.ready": "准备好登陆?",
|
||||
"components.Input.error.password.noMatch": "密码不匹配"
|
||||
}
|
||||
|
||||
@ -184,5 +184,6 @@
|
||||
"Auth.form.register.username.placeholder": "John Doe",
|
||||
"Auth.header.register.description": "為了完成設定,請填寫以下欄位以建立第一個最高權限管理者。",
|
||||
"Auth.link.forgot-password": "忘記密碼了嗎?",
|
||||
"Auth.link.ready": "準備登入了嗎?"
|
||||
"Auth.link.ready": "準備登入了嗎?",
|
||||
"components.Input.error.password.noMatch": "密碼不符"
|
||||
}
|
||||
|
||||
@ -89,7 +89,7 @@
|
||||
"PopUpForm.header.edit.email-templates": "تحرير قوالب البريد الإلكتروني",
|
||||
"PopUpForm.header.edit.providers": "تحرير موفر {provider}",
|
||||
"PopUpForm.inputSelect.providers.label": "اختر الموفر",
|
||||
"components.Input.error.password.noMatch": "كلمات السر لا تتطابق",
|
||||
|
||||
"notification.error.delete": "حدث خطأ أثناء محاولة حذف العنصر",
|
||||
"notification.error.fetch": "حدث خطأ أثناء محاولة جلب البيانات",
|
||||
"notification.error.fetchUser": "حدث خطأ أثناء محاولة جلب المستخدمين",
|
||||
|
||||
@ -96,7 +96,7 @@
|
||||
"PopUpForm.header.edit.email-templates": "E-Mail-Templates bearbeiten",
|
||||
"PopUpForm.header.edit.providers": "Methode {provider} bearbeiten",
|
||||
"PopUpForm.inputSelect.providers.label": "Wähle die Methode aus",
|
||||
"components.Input.error.password.noMatch": "Passwörter stimmen nicht überein",
|
||||
|
||||
"components.Input.error.password.length": "Passwort ist zu kurz",
|
||||
"notification.error.delete": "Beim Löschen des Objekts ist ein Fehler aufgetreten",
|
||||
"notification.error.fetch": "Beim Abruf von Daten ist ein Fehler aufgetreten",
|
||||
|
||||
@ -96,7 +96,7 @@
|
||||
"PopUpForm.header.edit.email-templates": "Edit Email Templates",
|
||||
"PopUpForm.header.edit.providers": "Edit {provider} Provider",
|
||||
"PopUpForm.inputSelect.providers.label": "Choose the provider",
|
||||
"components.Input.error.password.noMatch": "Passwords do not match",
|
||||
|
||||
"components.Input.error.password.length": "Password is too short",
|
||||
"notification.error.delete": "An error occurred while trying to delete the item",
|
||||
"notification.error.fetch": "An error occurred while trying to fetch data",
|
||||
|
||||
@ -95,7 +95,7 @@
|
||||
"PopUpForm.header.edit.email-templates": "Editar Plantillas de Email",
|
||||
"PopUpForm.header.edit.providers": "Editar Proveedor {provider}",
|
||||
"PopUpForm.inputSelect.providers.label": "Elija el proveedor",
|
||||
"components.Input.error.password.noMatch": "Las contraseñas no coinciden",
|
||||
|
||||
"components.Input.error.password.length": "Contraseña muy corta",
|
||||
"notification.error.delete": "Se ha producido un error al intentar borrar el elemento",
|
||||
"notification.error.fetch": "Se ha producido un error al intentar recuperar los datos",
|
||||
|
||||
@ -97,7 +97,7 @@
|
||||
"PopUpForm.header.edit.email-templates": "Editer E-mail Templates",
|
||||
"PopUpForm.header.edit.providers": "Editer {provider} Provider",
|
||||
"PopUpForm.inputSelect.providers.label": "Sélectionnez le provider",
|
||||
"components.Input.error.password.noMatch": "Le mot de passe ne correspond pas",
|
||||
|
||||
"components.Input.error.password.length": "Le mot de passe doit contenir au moins 6 caractères",
|
||||
"notification.error.delete": "Une erreur est survenue en essayant de supprimer cet élément",
|
||||
"notification.error.fetch": "Une erreur est survenue en essayant de récupérer les données",
|
||||
|
||||
@ -87,7 +87,7 @@
|
||||
"PopUpForm.header.edit.email-templates": "Modifica il template delle Email",
|
||||
"PopUpForm.header.edit.providers": "Modifica {provider} Provider",
|
||||
"PopUpForm.inputSelect.providers.label": "Scegli il provider",
|
||||
"components.Input.error.password.noMatch": "La password non corrisponde",
|
||||
|
||||
"eaderNav.link.advancedSettings": "Impostazioni avanzate",
|
||||
"notification.error.delete": "Si è verificato un errore mentre si stava cercando di eliminare l'elemento",
|
||||
"notification.error.fetch": "Si è verificato un errore mentre si stava cercando di recuperare i dati",
|
||||
|
||||
@ -95,7 +95,7 @@
|
||||
"PopUpForm.header.edit.email-templates": "メールテンプレートの編集",
|
||||
"PopUpForm.header.edit.providers": "{provider}プロバイダを編集",
|
||||
"PopUpForm.inputSelect.providers.label": "プロバイダの選択",
|
||||
"components.Input.error.password.noMatch": "パスワードが一致しません",
|
||||
|
||||
"components.Input.error.password.length": "パスワードが一致しません",
|
||||
"notification.error.delete": "アイテムの削除中にエラーが発生しました",
|
||||
"notification.error.fetch": "データの取得中にエラーが発生しました",
|
||||
|
||||
@ -89,7 +89,7 @@
|
||||
"PopUpForm.header.edit.email-templates": "이메일 템플릿 수정",
|
||||
"PopUpForm.header.edit.providers": "{provider} 프로바이더(Provider) 설정",
|
||||
"PopUpForm.inputSelect.providers.label": "프로바이더(provider) 선택",
|
||||
"components.Input.error.password.noMatch": "패스워드가 일치하지 않습니다.",
|
||||
|
||||
"notification.error.delete": "항목을 삭제하는데 에러가 발생했습니다.",
|
||||
"notification.error.fetch": "데이터를 가져오는데 에러가 발생했습니다.",
|
||||
"notification.error.fetchUser": "사용자를 가져오는데 에러가 발생했습니다.",
|
||||
|
||||
@ -97,7 +97,7 @@
|
||||
"PopUpForm.header.edit.providers": "Leverancier {provider} aanpassen",
|
||||
"PopUpForm.inputSelect.providers.label": "Kies een leverancier",
|
||||
"components.Input.error.password.length": "Wachtwoord is te kort",
|
||||
"components.Input.error.password.noMatch": "Wachtwoorden komen niet overeen",
|
||||
|
||||
"notification.error.delete": "Er is een fout opgetreden tijdens het verwijderen van dit item",
|
||||
"notification.error.fetch": "Er is een fout opgetreden tijdens het ophalen van de data",
|
||||
"notification.error.fetchUser": "Er is een fout opgetreden tijdens het ophalen van de gebruikers",
|
||||
|
||||
@ -96,7 +96,7 @@
|
||||
"PopUpForm.header.edit.email-templates": "Zmień szablony e-mail",
|
||||
"PopUpForm.header.edit.providers": "Edytuj dostawcę {provider}",
|
||||
"PopUpForm.inputSelect.providers.label": "Dostawca",
|
||||
"components.Input.error.password.noMatch": "Hasło nie pasuje",
|
||||
|
||||
"components.Input.error.password.length": "Hasło jest za krótkie",
|
||||
"notification.error.delete": "Wystąpił błąd podczas usuwania pozycji",
|
||||
"notification.error.fetch": "Wystąpił błąd podczas pobierania danych",
|
||||
|
||||
@ -89,7 +89,7 @@
|
||||
"PopUpForm.header.edit.email-templates": "Editar modelos de email",
|
||||
"PopUpForm.header.edit.providers": "Editar provedor {provider}",
|
||||
"PopUpForm.inputSelect.providers.label": "Escolher o provedor",
|
||||
"components.Input.error.password.noMatch": "As senhas não conferem",
|
||||
|
||||
"notification.error.delete": "Ocorreu um erro ao tentar eliminar o registro",
|
||||
"notification.error.fetch": "Ocorreu um erro ao tentar buscar dados",
|
||||
"notification.error.fetchUser": "Ocorreu um erro ao tentar buscar usuários",
|
||||
|
||||
@ -95,7 +95,7 @@
|
||||
"PopUpForm.header.edit.email-templates": "Editar Modelos de Email",
|
||||
"PopUpForm.header.edit.providers": "Editar o serviço de autenticação {provider}",
|
||||
"PopUpForm.inputSelect.providers.label": "Selecionar o serviço de autenticação",
|
||||
"components.Input.error.password.noMatch": "As passwords não coincidem",
|
||||
|
||||
"components.Input.error.password.length": "A password é demasiado curta",
|
||||
"notification.error.delete": "Ocorreu um erro a tentar eliminar o item",
|
||||
"notification.error.fetch": "Ocorreu um erro a tentar obter os dados",
|
||||
|
||||
@ -97,7 +97,7 @@
|
||||
"PopUpForm.header.edit.email-templates": "Редактировать шаблон письма",
|
||||
"PopUpForm.header.edit.providers": "Редактирование провайдера {provider}",
|
||||
"PopUpForm.inputSelect.providers.label": "Выбрать провайдера",
|
||||
"components.Input.error.password.noMatch": "Пароль не совпадает",
|
||||
|
||||
"notification.error.delete": "Возникла ошибка в процессе удаления",
|
||||
"notification.error.fetch": "Возникла ошибка при загрузке данных",
|
||||
"notification.error.fetchUser": "Возникла ошибка при загрузке пользователей",
|
||||
|
||||
@ -96,7 +96,7 @@
|
||||
"PopUpForm.header.edit.email-templates": "E-posta Şablonlarını Düzenle",
|
||||
"PopUpForm.header.edit.providers": "{sağlayıcı} sağlayıcını düzenle",
|
||||
"PopUpForm.inputSelect.providers.label": "Sağlayıcıyı seçin",
|
||||
"components.Input.error.password.noMatch": "Şifreler uyuşmuyor",
|
||||
|
||||
"components.Input.error.password.length": "Şifreniz çok kısa",
|
||||
"notification.error.delete": "Öğe silinirken bir hata oluştu",
|
||||
"notification.error.fetch": "Verileri getirmeye çalışılırken bir hata oluştu",
|
||||
|
||||
@ -89,7 +89,7 @@
|
||||
"PopUpForm.header.edit.email-templates": "编辑电子邮件模版",
|
||||
"PopUpForm.header.edit.providers": "编译 {provider} 供应商",
|
||||
"PopUpForm.inputSelect.providers.label": "选择供应商",
|
||||
"components.Input.error.password.noMatch": "密码不匹配",
|
||||
|
||||
"notification.error.delete": "删除数据时出错",
|
||||
"notification.error.fetch": "获取数据时出错",
|
||||
"notification.error.fetchUser": "获取用户时出错",
|
||||
|
||||
@ -95,7 +95,7 @@
|
||||
"PopUpForm.header.edit.email-templates": "編輯郵件範本",
|
||||
"PopUpForm.header.edit.providers": "編輯 {provider} 驗證方式",
|
||||
"PopUpForm.inputSelect.providers.label": "選擇驗證方式",
|
||||
"components.Input.error.password.noMatch": "密碼不符",
|
||||
|
||||
"components.Input.error.password.length": "密碼長度過短",
|
||||
"notification.error.delete": "刪除項目時發生錯誤",
|
||||
"notification.error.fetch": "讀取資料時發生錯誤",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user