Init attribute form

This commit is contained in:
soupette 2019-11-18 19:03:24 +01:00 committed by Alexandre Bodin
parent af8b17da1b
commit 41f09d410f
12 changed files with 435 additions and 273 deletions

View File

@ -19,10 +19,19 @@ const Button = styled.button`
&:hover, &:hover,
&:active, &:active,
&:focus { &:focus {
background: #f7f7f7; background: #e6f0fb;
outline: 0; border-color: #aed4fb;
> div:after {
color: #0097f6; .attributeIcon {
background-color: #007eff;
> svg {
g {
path {
fill: #007eff;
}
}
}
} }
} }

View File

@ -6,33 +6,10 @@ const Card = styled.div`
align-items: center; align-items: center;
max-width: calc(100% - 18px); max-width: calc(100% - 18px);
&:after {
content: '\f05d';
position: absolute;
top: 7px;
right: 26px;
color: #e3e9f3;
font-size: 1.4rem;
font-family: 'FontAwesome';
-webkit-font-smoothing: antialiased;
}
&:hover {
background: none;
}
> img {
display: inline-block;
height: 20px;
width: 35px;
margin-right: 10px;
}
> span { > span {
white-space: nowrap; white-space: nowrap;
color: #9ea7b8; color: #9ea7b8;
font-size: 1.2rem; font-size: 1.2rem;
font-style: italic;
font-weight: 400; font-weight: 400;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
margin-top: auto; margin-top: auto;

View File

@ -4,81 +4,76 @@
* *
*/ */
import React from 'react'; import React, {
forwardRef,
useEffect,
useImperativeHandle,
useRef,
} from 'react';
import { AttributeIcon } from '@buffetjs/core';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { useHistory } from 'react-router-dom';
import attributeIcons from '../../utils/attributeIcons'; import getTrad from '../../utils/getTrad';
import pluginId from '../../pluginId'; import useQuery from '../../hooks/useQuery';
import Button from './Button'; import Button from './Button';
import Card from './Card'; import Card from './Card';
class AttributeOption extends React.Component { const AttributeOption = forwardRef(({ tabIndex, type }, ref) => {
componentDidUpdate(prevProps) { const buttonRef = useRef();
const { isDisplayed, nodeToFocus, tabIndex } = this.props; const tabRef = useRef();
const query = useQuery();
const { push } = useHistory();
tabRef.current = tabIndex;
if ( useImperativeHandle(ref, () => ({
prevProps.isDisplayed !== isDisplayed && focus: () => {
isDisplayed && buttonRef.current.focus();
nodeToFocus === tabIndex },
) { }));
this.focusNode();
useEffect(() => {
if (tabRef.current === 0) {
buttonRef.current.focus();
} }
}, []);
if (prevProps.nodeToFocus !== nodeToFocus && nodeToFocus === tabIndex) { const handleClick = () => {
this.focusNode(); const forTarget = query.get('for');
} const target = query.get('target');
}
button = React.createRef(); push({
search: `modalType=attribute&actionType=create&settingType=base&for=${forTarget}&target=${target}&attributeType=${type}`,
focusNode = () => { });
const { current } = this.button;
current.focus();
}; };
render() {
const { description, onClick, tabIndex, type } = this.props;
return ( return (
<div className="col-md-6"> <div className="col-6">
<Button <Button ref={buttonRef} type="button" onClick={handleClick}>
id={`attrCard${type}`}
onClick={() => onClick(type)}
type="button"
tabIndex={tabIndex + 1}
ref={this.button}
>
<Card> <Card>
<img src={attributeIcons[type]} alt="ico" /> <AttributeIcon
<FormattedMessage type={type}
id={`${pluginId}.popUpForm.attributes.${type}.name`} style={{ marginRight: 10 }}
> className="attributeIcon"
/>
<FormattedMessage id={getTrad(`attribute.${type}`)}>
{message => <span className="attributeType">{message}</span>} {message => <span className="attributeType">{message}</span>}
</FormattedMessage> </FormattedMessage>
<FormattedMessage id={description} /> <FormattedMessage id={getTrad(`attribute.${type}.description`)} />
</Card> </Card>
</Button> </Button>
</div> </div>
); );
} });
}
AttributeOption.displayName = 'AttributeOption';
AttributeOption.defaultProps = { AttributeOption.defaultProps = {
description: 'app.utils.defaultMessage',
isDisplayed: false,
nodeToFocus: -1,
onClick: () => {},
tabIndex: 0, tabIndex: 0,
type: 'string', type: 'text',
}; };
AttributeOption.propTypes = { AttributeOption.propTypes = {
description: PropTypes.string,
isDisplayed: PropTypes.bool,
nodeToFocus: PropTypes.number,
onClick: PropTypes.func,
tabIndex: PropTypes.number, tabIndex: PropTypes.number,
type: PropTypes.string, type: PropTypes.string,
}; };

View File

@ -2,15 +2,20 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { HeaderModalTitle } from 'strapi-helper-plugin'; import { HeaderModalTitle } from 'strapi-helper-plugin';
import { AttributeIcon } from '@buffetjs/core'; import { AttributeIcon } from '@buffetjs/core';
import pluginId from '../../pluginId';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { upperFirst } from 'lodash';
import pluginId from '../../pluginId';
const ModalHeader = ({ headerId, name, type }) => { const ModalHeader = ({ headerId, iconType, name }) => {
console.log({ iconType });
return ( return (
<section> <section>
<HeaderModalTitle style={{ textTransform: 'none' }}> <HeaderModalTitle style={{ textTransform: 'none' }}>
<AttributeIcon type={type} style={{ margin: 'auto 20px auto 0' }} /> <AttributeIcon type={iconType} style={{ margin: 'auto 20px auto 0' }} />
{headerId && (
<FormattedMessage id={`${pluginId}.${headerId}`} values={{ name }} /> <FormattedMessage id={`${pluginId}.${headerId}`} values={{ name }} />
)}
{!headerId && <span>{upperFirst(name)}</span>}
</HeaderModalTitle> </HeaderModalTitle>
</section> </section>
); );
@ -18,14 +23,14 @@ const ModalHeader = ({ headerId, name, type }) => {
ModalHeader.defaultProps = { ModalHeader.defaultProps = {
headerId: '', headerId: '',
iconType: 'contentType',
name: '', name: '',
type: 'contentType',
}; };
ModalHeader.propTypes = { ModalHeader.propTypes = {
headerId: PropTypes.string, headerId: PropTypes.string,
iconType: PropTypes.string,
name: PropTypes.string, name: PropTypes.string,
type: PropTypes.string,
}; };
export default ModalHeader; export default ModalHeader;

View File

@ -18,7 +18,6 @@ const DataManagerProvider = ({ children }) => {
isLoading, isLoading,
initialData, initialData,
modifiedData, modifiedData,
newSchema,
} = reducerState.toJS(); } = reducerState.toJS();
const contentTypeMatch = useRouteMatch( const contentTypeMatch = useRouteMatch(
@ -90,7 +89,6 @@ const DataManagerProvider = ({ children }) => {
createSchema, createSchema,
initialData, initialData,
modifiedData, modifiedData,
newSchema,
setModifiedData, setModifiedData,
}} }}
> >

View File

@ -6,16 +6,6 @@ const initialState = fromJS({
initialData: {}, initialData: {},
modifiedData: {}, modifiedData: {},
isLoading: true, isLoading: true,
newSchema: {
schemaType: '',
schema: {},
uid: '',
},
newSchemaClone: {
schemaType: '',
schema: {},
uid: '',
},
}); });
const reducer = (state, action) => { const reducer = (state, action) => {
@ -25,12 +15,21 @@ const reducer = (state, action) => {
.update('components', () => fromJS(action.components)) .update('components', () => fromJS(action.components))
.update('contentTypes', () => fromJS(action.contentTypes)) .update('contentTypes', () => fromJS(action.contentTypes))
.update('isLoading', () => false); .update('isLoading', () => false);
case 'CREATE_SCHEMA': case 'CREATE_SCHEMA': {
console.log({ action }); const newSchema = {
return state uid: action.uid,
.updateIn(['newSchema', 'schema'], () => fromJS(action.data)) isTemporary: true,
.updateIn(['newSchema', 'uid'], () => fromJS(action.uid)) schema: {
.updateIn(['newSchema', 'schemaType'], () => fromJS(action.schemaType)); ...action.data,
attributes: {},
},
};
const key =
action.schemaType === 'contentType' ? 'contentTypes' : 'components';
return state.updateIn([key, action.uid], () => fromJS(newSchema));
}
case 'SET_MODIFIED_DATA': case 'SET_MODIFIED_DATA':
return state return state
.update('initialData', () => OrderedMap(action.schemaToSet)) .update('initialData', () => OrderedMap(action.schemaToSet))

View File

@ -1,4 +1,4 @@
import React, { useEffect, useReducer, useState } from 'react'; import React, { useEffect, useReducer, useRef, useState } from 'react';
// import PropTypes from 'prop-types'; // import PropTypes from 'prop-types';
import { import {
ButtonModal, ButtonModal,
@ -18,15 +18,17 @@ import { get, isEmpty, upperFirst } from 'lodash';
import pluginId from '../../pluginId'; import pluginId from '../../pluginId';
import useQuery from '../../hooks/useQuery'; import useQuery from '../../hooks/useQuery';
import useDataManager from '../../hooks/useDataManager'; import useDataManager from '../../hooks/useDataManager';
import AttributeOption from '../../components/AttributeOption';
import ModalHeader from '../../components/ModalHeader'; import ModalHeader from '../../components/ModalHeader';
import HeaderModalNavContainer from '../../components/HeaderModalNavContainer'; import HeaderModalNavContainer from '../../components/HeaderModalNavContainer';
import HeaderNavLink from '../../components/HeaderNavLink'; import HeaderNavLink from '../../components/HeaderNavLink';
import getTrad from '../../utils/getTrad';
import getAttributes from './utils/attributes';
import forms from './utils/forms'; import forms from './utils/forms';
import { createUid } from './utils/createUid'; import { createUid } from './utils/createUid';
import init from './init'; import init from './init';
import reducer, { initialState } from './reducer'; import reducer, { initialState } from './reducer';
const getTrad = id => `${pluginId}.${id}`;
const NAVLINKS = [{ id: 'base' }, { id: 'advanced' }]; const NAVLINKS = [{ id: 'base' }, { id: 'advanced' }];
const FormModal = () => { const FormModal = () => {
@ -34,40 +36,77 @@ const FormModal = () => {
actionType: null, actionType: null,
modalType: null, modalType: null,
settingType: null, settingType: null,
// uid: null, for: null,
target: null,
attributeType: null,
}; };
const [state, setState] = useState(initialStateData); const [state, setState] = useState(initialStateData);
const [reducerState, dispatch] = useReducer(reducer, initialState, init); const [reducerState, dispatch] = useReducer(reducer, initialState, init);
const { push } = useHistory(); const { push } = useHistory();
const { search } = useLocation(); const { search } = useLocation();
const { formatMessage } = useGlobalContext(); const { formatMessage } = useGlobalContext();
const isOpen = !isEmpty(search);
const query = useQuery(); const query = useQuery();
const attributeOptionRef = useRef();
const { contentTypes, createSchema, initialData } = useDataManager(); const { contentTypes, createSchema, initialData } = useDataManager();
const { formErrors, modifiedData } = reducerState.toJS(); const { formErrors, modifiedData } = reducerState.toJS();
useEffect(() => { useEffect(() => {
if (isOpen) { if (!isEmpty(search)) {
setState({ setState({
actionType: query.get('actionType'), actionType: query.get('actionType'),
modalType: query.get('modalType'), modalType: query.get('modalType'),
settingType: query.get('settingType'), settingType: query.get('settingType'),
// uid: query.get('uid'), for: query.get('for'),
target: query.get('target'),
attributeType: query.get('attributeType'),
}); });
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [isOpen]); }, [search]);
const displayedAttributes = getAttributes(state.for);
const form = get(forms, [state.modalType, 'form', state.settingType], () => ({
items: [],
}));
const iconType = ['components', 'contentType'].includes(state.modalType)
? state.modalType
: state.for;
const isCreatingCT = state.modalType === 'contentType';
const isCreating = state.actionType === 'create'; const isCreating = state.actionType === 'create';
const headerId = isCreating const isOpen = !isEmpty(search);
const isPickingAttribute = state.modalType === 'chooseAttribute';
const name = get(initialData, ['schema', 'name'], '');
const uid = createUid(modifiedData.name || '');
let headerId = isCreating
? `modalForm.${state.modalType}.header-create` ? `modalForm.${state.modalType}.header-create`
: 'modalForm.header-edit'; : 'modalForm.header-edit';
const name = get(initialData, ['schema', 'name'], '');
if (!['contentType', 'component'].includes(state.modalType)) {
headerId = null;
}
const modalBodyStyle = isPickingAttribute
? { paddingTop: '0.5rem', paddingBottom: '3rem' }
: {};
const getModalTitleSubHeader = () => {
switch (state.modalType) {
case 'chooseAttribute':
return getTrad(`modalForm.sub-header.chooseAttribute.${state.for}`);
default:
return getTrad('configurations');
}
};
const getNextSearch = nextTab => { const getNextSearch = nextTab => {
const newSearch = Object.keys(state).reduce((acc, current) => { const newSearch = Object.keys(state).reduce((acc, current, index) => {
if (current !== 'settingType') { if (current !== 'settingType') {
acc = `${acc}&${current}=${state[current]}`; acc = `${acc}${index === 0 ? '' : '&'}${current}=${state[current]}`;
} else { } else {
acc = `${acc}&${current}=${nextTab}`; acc = `${acc}${index === 0 ? '' : '&'}${current}=${nextTab}`;
} }
return acc; return acc;
@ -87,16 +126,21 @@ const FormModal = () => {
e.preventDefault(); e.preventDefault();
try { try {
const schema = forms.contentType.schema(Object.keys(contentTypes)); const schema = forms[state.modalType].schema(Object.keys(contentTypes));
await schema.validate(modifiedData, { abortEarly: false }); await schema.validate(modifiedData, { abortEarly: false });
createSchema(modifiedData, state.modalType, createUid(modifiedData.name));
handleToggle(); createSchema(modifiedData, state.modalType, uid);
// push({ p}) const nextSlug = isCreatingCT ? 'content-types' : 'component-categories';
push({
pathname: `/plugins/${pluginId}/${nextSlug}/${uid}`,
search: `modalType=chooseAttribute&for=${state.modalType}&target=${modifiedData.name}`,
});
dispatch({
type: 'RESET_PROPS',
});
} catch (err) { } catch (err) {
const errors = getYupInnerErrors(err); const errors = getYupInnerErrors(err);
// TODO
console.log({ errors });
dispatch({ dispatch({
type: 'SET_ERRORS', type: 'SET_ERRORS',
errors, errors,
@ -112,23 +156,33 @@ const FormModal = () => {
type: 'RESET_PROPS', type: 'RESET_PROPS',
}); });
}; };
const form = get(forms, [state.modalType, 'form', state.settingType], () => ({
items: [], const onOpened = () => {
})); if (state.modalType === 'chooseAttribute') {
attributeOptionRef.current.focus();
}
};
return ( return (
<Modal isOpen={isOpen} onClosed={onClosed} onToggle={handleToggle}> <Modal
isOpen={isOpen}
onOpened={onOpened}
onClosed={onClosed}
onToggle={handleToggle}
>
<HeaderModal> <HeaderModal>
<ModalHeader <ModalHeader
name={name} name={state.target || name}
headerId={headerId} headerId={headerId}
type={state.modalType || 'contentType'} iconType={iconType || 'contentType'}
/> />
<section> <section>
<HeaderModalTitle> <HeaderModalTitle>
<FormattedMessage id={getTrad('configurations')}> <FormattedMessage id={getModalTitleSubHeader()}>
{msg => <span>{upperFirst(msg)}</span>} {msg => <span>{upperFirst(msg)}</span>}
</FormattedMessage> </FormattedMessage>
{!isPickingAttribute && (
<>
<div className="settings-tabs"> <div className="settings-tabs">
<HeaderModalNavContainer> <HeaderModalNavContainer>
{NAVLINKS.map((link, index) => { {NAVLINKS.map((link, index) => {
@ -138,24 +192,68 @@ const FormModal = () => {
key={link.id} key={link.id}
{...link} {...link}
onClick={() => { onClick={() => {
setState(prev => ({ ...prev, settingType: link.id })); setState(prev => ({
...prev,
settingType: link.id,
}));
push({ search: getNextSearch(link.id) }); push({ search: getNextSearch(link.id) });
}} }}
nextTab={index === NAVLINKS.length - 1 ? 0 : index + 1} nextTab={
index === NAVLINKS.length - 1 ? 0 : index + 1
}
/> />
); );
})} })}
</HeaderModalNavContainer> </HeaderModalNavContainer>
</div> </div>
<hr /> <hr />
</>
)}
</HeaderModalTitle> </HeaderModalTitle>
</section> </section>
</HeaderModal> </HeaderModal>
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<ModalForm> <ModalForm>
<ModalBody> <ModalBody style={modalBodyStyle}>
<div className="container-fluid"> <div className="container-fluid">
{form(modifiedData).items.map((row, index) => { {isPickingAttribute
? displayedAttributes.map((row, i) => {
return (
<div key={i} className="row">
{i === 1 && (
<hr
style={{
width: 'calc(100% - 30px)',
marginBottom: 7,
}}
/>
)}
{row.map((attr, index) => {
const tabIndex =
i === 0
? index
: displayedAttributes[0].length + index;
return (
<AttributeOption
key={attr}
tabIndex={tabIndex}
isDisplayed
onClick={() => {}}
ref={
i === 0 && index === 0
? attributeOptionRef
: null
}
type={attr}
/>
);
})}
</div>
);
})
: form(modifiedData, state.attributeType).items.map(
(row, index) => {
return ( return (
<div className="row" key={index}> <div className="row" key={index}>
{row.map(input => { {row.map(input => {
@ -196,10 +294,12 @@ const FormModal = () => {
})} })}
</div> </div>
); );
})} }
)}
</div> </div>
</ModalBody> </ModalBody>
</ModalForm> </ModalForm>
{!isPickingAttribute && (
<ModalFooter> <ModalFooter>
<section> <section>
<ButtonModal <ButtonModal
@ -210,6 +310,7 @@ const FormModal = () => {
<ButtonModal message="form.button.done" type="submit" /> <ButtonModal message="form.button.done" type="submit" />
</section> </section>
</ModalFooter> </ModalFooter>
)}
</form> </form>
</Modal> </Modal>
); );

View File

@ -0,0 +1,23 @@
const getAttributes = () => {
const defaultAttributes = [
[
'text',
'email',
'richtext',
'password',
'number',
'enumeration',
'date',
'media',
'boolean',
'json',
// 'uid',
'relation',
],
['component', 'dynamiczone'],
];
return defaultAttributes;
};
export default getAttributes;

View File

@ -1,6 +1,7 @@
import * as yup from 'yup'; import * as yup from 'yup';
import { translatedErrors as errorsTrads } from 'strapi-helper-plugin'; import { translatedErrors as errorsTrads } from 'strapi-helper-plugin';
import pluginId from '../../../pluginId'; import pluginId from '../../../pluginId';
import getTrad from '../../../utils/getTrad';
import { createUid, nameToSlug } from './createUid'; import { createUid, nameToSlug } from './createUid';
yup.addMethod(yup.mixed, 'defined', function() { yup.addMethod(yup.mixed, 'defined', function() {
@ -22,6 +23,57 @@ yup.addMethod(yup.string, 'unique', function(message, allReadyTakenValues) {
}); });
const forms = { const forms = {
attribute: {
schema() {
return yup.object();
},
form: {
advanced() {
return {
items: [[]],
};
},
base(data, type) {
const items = [
[
{
autoFocus: true,
name: 'name',
type: 'text',
label: {
id: getTrad('modalForm.attribute.form.base.name'),
},
description: {
id: getTrad('modalForm.attribute.form.base.name.description'),
},
validations: {
required: true,
},
},
],
];
if (type === 'text') {
items[0].push({
label: {
id: 'content-type-builder.form.attribute.item.number.type',
},
name: 'type',
type: 'select',
value: 'short text',
options: ['short text', 'long text'],
validations: {
required: true,
},
});
}
return {
items,
};
},
},
},
contentType: { contentType: {
schema(allReadyTakenValues) { schema(allReadyTakenValues) {
return yup.object().shape({ return yup.object().shape({

View File

@ -13,14 +13,19 @@ import CustomLink from '../../components/CustomLink';
import useDataManager from '../../hooks/useDataManager'; import useDataManager from '../../hooks/useDataManager';
import Wrapper from './Wrapper'; import Wrapper from './Wrapper';
// const displayNotificationCTNotSaved = () => { const displayNotificationCTNotSaved = () => {
// strapi.notification.info( strapi.notification.info(
// `${pluginId}.notification.info.contentType.creating.notSaved` `${pluginId}.notification.info.contentType.creating.notSaved`
// ); );
// }; };
function LeftMenu() { function LeftMenu() {
const { components, contentTypes, newSchema } = useDataManager(); const {
components,
contentTypes,
// initialData,
// modifiedData,
} = useDataManager();
const { currentEnvironment } = useGlobalContext(); const { currentEnvironment } = useGlobalContext();
const { push } = useHistory(); const { push } = useHistory();
const isProduction = currentEnvironment === 'production'; const isProduction = currentEnvironment === 'production';
@ -40,14 +45,24 @@ function LeftMenu() {
})), })),
obj => obj.title obj => obj.title
); );
const tempSchemaCT = const canOpenModalCreateCTorComponent = () => {
newSchema.schemaType === 'contentType' return (
? { !Object.keys(contentTypes).some(
name: newSchema.uid, ct => contentTypes[ct].isTemporary === true
title: newSchema.schema.name, ) &&
to: `/plugins/${pluginId}/content-types/${newSchema.uid}`, !Object.keys(components).some(
component => components[component].isTemporary === true
)
);
};
const handleClickOpenModal = type => {
if (canOpenModalCreateCTorComponent()) {
push({ search: `modalType=${type}&actionType=create&settingType=base` });
} else {
displayNotificationCTNotSaved();
} }
: null; };
const data = [ const data = [
{ {
name: 'models', name: 'models',
@ -61,10 +76,7 @@ function LeftMenu() {
disabled: isProduction, disabled: isProduction,
id: `${pluginId}.button.model.create`, id: `${pluginId}.button.model.create`,
onClick: () => { onClick: () => {
push({ handleClickOpenModal('contentType');
search:
'modalType=contentType&actionType=create&settingType=base',
});
}, },
}, },
}, },
@ -75,7 +87,6 @@ function LeftMenu() {
title: contentTypes[uid].schema.name, title: contentTypes[uid].schema.name,
to: `/plugins/${pluginId}/content-types/${uid}`, to: `/plugins/${pluginId}/content-types/${uid}`,
})) }))
.concat(tempSchemaCT)
.filter(obj => obj !== null), .filter(obj => obj !== null),
obj => obj.title obj => obj.title
), ),
@ -92,9 +103,7 @@ function LeftMenu() {
disabled: isProduction, disabled: isProduction,
id: `${pluginId}.button.component.create`, id: `${pluginId}.button.component.create`,
onClick: () => { onClick: () => {
push({ handleClickOpenModal('component');
search: 'modalType=component&actionType=create&settingType=base',
});
}, },
}, },
}, },

View File

@ -2,23 +2,7 @@
"model": "Content Type", "model": "Content Type",
"group": "Group", "group": "Group",
"attribute.WYSIWYG": "Text (WYSIWYG)", "attribute.WYSIWYG": "Text (WYSIWYG)",
"attribute.boolean": "Boolean",
"attribute.date": "Date",
"attribute.decimal": "Decimal",
"attribute.email": "Email",
"attribute.enumeration": "Enumeration",
"attribute.float": "Float",
"attribute.group": "Group",
"attribute.integer": "Integer",
"attribute.biginteger": "Big Integer",
"attribute.json": "JSON",
"attribute.media": "Media",
"attribute.password": "Password",
"attribute.relation": "Relation",
"attribute.richtext": "Rich text",
"attribute.string": "String",
"attribute.text": "Text",
"attribute.uuid": "Uuid",
"button.attributes.add": "Add New Field", "button.attributes.add": "Add New Field",
"button.attributes.add.another": "Add Another Field", "button.attributes.add.another": "Add Another Field",
"button.contentType.add": "Add a Content Type", "button.contentType.add": "Add a Content Type",
@ -146,34 +130,7 @@
"notification.success.message.contentType.edit": "Your Content Type has been updated", "notification.success.message.contentType.edit": "Your Content Type has been updated",
"plugin.description.long": "Modelize the data structure of your API. Create new fields and relations in just a minute. The files are automatically created and updated in your project.", "plugin.description.long": "Modelize the data structure of your API. Create new fields and relations in just a minute. The files are automatically created and updated in your project.",
"plugin.description.short": "Modelize the data structure of your API.", "plugin.description.short": "Modelize the data structure of your API.",
"popUpForm.attributes.boolean.description": "Yes or no, 1 or 0, true or false",
"popUpForm.attributes.boolean.name": "Boolean",
"popUpForm.attributes.date.description": "Event date, opening hours",
"popUpForm.attributes.date.name": "Date",
"popUpForm.attributes.email.description": "User's email...",
"popUpForm.attributes.email.name": "Email",
"popUpForm.attributes.enumeration.description": "List of choices",
"popUpForm.attributes.enumeration.name": "Enumeration",
"popUpForm.attributes.group.description": "Refers to a Group",
"popUpForm.attributes.group.name": "Group",
"popUpForm.attributes.json.description": "Data in JSON format",
"popUpForm.attributes.json.name": "JSON",
"popUpForm.attributes.media.description": "Images, videos, PDFs and other files",
"popUpForm.attributes.media.name": "Media",
"popUpForm.attributes.number.description": "Everything that is number",
"popUpForm.attributes.number.name": "Number",
"popUpForm.attributes.password.description": "User password...",
"popUpForm.attributes.password.name": "Password",
"popUpForm.attributes.relation.description": "Refers to a Content Type",
"popUpForm.attributes.relation.name": "Relation",
"popUpForm.attributes.richtext.description": "Formatting and creating text",
"popUpForm.attributes.richtext.name": "Rich text",
"popUpForm.attributes.string.description": "Titles, names, paragraphs, list of names",
"popUpForm.attributes.string.name": "String",
"popUpForm.attributes.text.description": "Descriptions, text paragraphs, articles",
"popUpForm.attributes.text.name": "Text",
"popUpForm.attributes.uuid.description": "Unique identifier",
"popUpForm.attributes.uuid.name": "Uuid",
"popUpForm.choose.attributes.header.title": "Add New Field", "popUpForm.choose.attributes.header.title": "Add New Field",
"popUpForm.choose.attributes.header.subtitle.model": "Select a field for your content type", "popUpForm.choose.attributes.header.subtitle.model": "Select a field for your content type",
"popUpForm.choose.attributes.header.subtitle.group": "Select a field for your group", "popUpForm.choose.attributes.header.subtitle.group": "Select a field for your group",
@ -221,13 +178,45 @@
"table.relations.title.singular": "including {number} relationship", "table.relations.title.singular": "including {number} relationship",
"prompt.content.unsaved": "Are you sure you want to leave this content type? All your modifications will be lost.", "prompt.content.unsaved": "Are you sure you want to leave this content type? All your modifications will be lost.",
"modalForm.contentType.header-create": "Create a content type", "attribute.boolean": "Boolean",
"modalForm.header-edit": "Edit {name}", "attribute.boolean.description": "Yes or no, 1 or 0, true or false",
"modalForm.component.header-create": "Create a component", "attribute.component": "Component",
"attribute.component.description": "Set of fields that you can repeat or reuse",
"attribute.date": "Date",
"attribute.date.description": "Event date, opening hours",
"attribute.dynamiczone": "Dynamic zone",
"attribute.dynamiczone.description": "Dynamically pick component when editing content",
"attribute.email": "Email",
"attribute.email.description": "User's email...",
"attribute.enumeration": "Enumeration",
"attribute.enumeration.description": "List of choices",
"attribute.json": "JSON",
"attribute.json.description": "Data in JSON format",
"attribute.media": "Media",
"attribute.media.description": "Images, videos, PDFs and other files",
"attribute.number": "Number",
"attribute.number.description": "Everything that is number",
"attribute.password": "Password",
"attribute.password.description": "User password...",
"attribute.relation": "Relation",
"attribute.relation.description": "Refers to a Content Type",
"attribute.richtext": "Rich text",
"attribute.richtext.description": "Formatting and creating text",
"attribute.text": "Text",
"attribute.text.description": "Small or long text like title or description",
"attribute.uid": "Uuid",
"attribute.uid.description": "Unique identifier",
"configurations": "configurations", "configurations": "configurations",
"contentType.displayName.label": "Display name", "contentType.displayName.label": "Display name",
"contentType.collectionName.description": "Useful when the name of your Content Type and your table name differ", "contentType.collectionName.description": "Useful when the name of your Content Type and your table name differ",
"contentType.collectionName.label": "Collection name", "contentType.collectionName.label": "Collection name",
"contentType.UID.description": "The UID is used to generate the API routes and databases tables/collections" "contentType.UID.description": "The UID is used to generate the API routes and databases tables/collections",
"modalForm.component.header-create": "Create a component",
"modalForm.contentType.header-create": "Create a content type",
"modalForm.attribute.form.base.name": "Name",
"modalForm.attribute.form.base.name.description": "No space is allowed for the name of the attribute",
"modalForm.header-edit": "Edit {name}",
"modalForm.sub-header.chooseAttribute.component": "Select a field for your component",
"modalForm.sub-header.chooseAttribute.contentType": "Select a field for your content type"
} }

View File

@ -0,0 +1,5 @@
import pluginId from '../pluginId';
const getTrad = id => `${pluginId}.${id}`;
export default getTrad;