mirror of
				https://github.com/strapi/strapi.git
				synced 2025-11-04 03:43:34 +00:00 
			
		
		
		
	Init attribute form
This commit is contained in:
		
							parent
							
								
									af8b17da1b
								
							
						
					
					
						commit
						41f09d410f
					
				@ -19,10 +19,19 @@ const Button = styled.button`
 | 
			
		||||
  &:hover,
 | 
			
		||||
  &:active,
 | 
			
		||||
  &:focus {
 | 
			
		||||
    background: #f7f7f7;
 | 
			
		||||
    outline: 0;
 | 
			
		||||
    > div:after {
 | 
			
		||||
      color: #0097f6;
 | 
			
		||||
    background: #e6f0fb;
 | 
			
		||||
    border-color: #aed4fb;
 | 
			
		||||
 | 
			
		||||
    .attributeIcon {
 | 
			
		||||
      background-color: #007eff;
 | 
			
		||||
 | 
			
		||||
      > svg {
 | 
			
		||||
        g {
 | 
			
		||||
          path {
 | 
			
		||||
            fill: #007eff;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -6,33 +6,10 @@ const Card = styled.div`
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  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 {
 | 
			
		||||
    white-space: nowrap;
 | 
			
		||||
    color: #9ea7b8;
 | 
			
		||||
    font-size: 1.2rem;
 | 
			
		||||
    font-style: italic;
 | 
			
		||||
    font-weight: 400;
 | 
			
		||||
    -webkit-font-smoothing: antialiased;
 | 
			
		||||
    margin-top: auto;
 | 
			
		||||
 | 
			
		||||
@ -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 { FormattedMessage } from 'react-intl';
 | 
			
		||||
 | 
			
		||||
import attributeIcons from '../../utils/attributeIcons';
 | 
			
		||||
import pluginId from '../../pluginId';
 | 
			
		||||
import { useHistory } from 'react-router-dom';
 | 
			
		||||
import getTrad from '../../utils/getTrad';
 | 
			
		||||
import useQuery from '../../hooks/useQuery';
 | 
			
		||||
import Button from './Button';
 | 
			
		||||
import Card from './Card';
 | 
			
		||||
 | 
			
		||||
class AttributeOption extends React.Component {
 | 
			
		||||
  componentDidUpdate(prevProps) {
 | 
			
		||||
    const { isDisplayed, nodeToFocus, tabIndex } = this.props;
 | 
			
		||||
const AttributeOption = forwardRef(({ tabIndex, type }, ref) => {
 | 
			
		||||
  const buttonRef = useRef();
 | 
			
		||||
  const tabRef = useRef();
 | 
			
		||||
  const query = useQuery();
 | 
			
		||||
  const { push } = useHistory();
 | 
			
		||||
  tabRef.current = tabIndex;
 | 
			
		||||
 | 
			
		||||
    if (
 | 
			
		||||
      prevProps.isDisplayed !== isDisplayed &&
 | 
			
		||||
      isDisplayed &&
 | 
			
		||||
      nodeToFocus === tabIndex
 | 
			
		||||
    ) {
 | 
			
		||||
      this.focusNode();
 | 
			
		||||
  useImperativeHandle(ref, () => ({
 | 
			
		||||
    focus: () => {
 | 
			
		||||
      buttonRef.current.focus();
 | 
			
		||||
    },
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (tabRef.current === 0) {
 | 
			
		||||
      buttonRef.current.focus();
 | 
			
		||||
    }
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
    if (prevProps.nodeToFocus !== nodeToFocus && nodeToFocus === tabIndex) {
 | 
			
		||||
      this.focusNode();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  const handleClick = () => {
 | 
			
		||||
    const forTarget = query.get('for');
 | 
			
		||||
    const target = query.get('target');
 | 
			
		||||
 | 
			
		||||
  button = React.createRef();
 | 
			
		||||
 | 
			
		||||
  focusNode = () => {
 | 
			
		||||
    const { current } = this.button;
 | 
			
		||||
 | 
			
		||||
    current.focus();
 | 
			
		||||
    push({
 | 
			
		||||
      search: `modalType=attribute&actionType=create&settingType=base&for=${forTarget}&target=${target}&attributeType=${type}`,
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const { description, onClick, tabIndex, type } = this.props;
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
      <div className="col-md-6">
 | 
			
		||||
        <Button
 | 
			
		||||
          id={`attrCard${type}`}
 | 
			
		||||
          onClick={() => onClick(type)}
 | 
			
		||||
          type="button"
 | 
			
		||||
          tabIndex={tabIndex + 1}
 | 
			
		||||
          ref={this.button}
 | 
			
		||||
        >
 | 
			
		||||
    <div className="col-6">
 | 
			
		||||
      <Button ref={buttonRef} type="button" onClick={handleClick}>
 | 
			
		||||
        <Card>
 | 
			
		||||
            <img src={attributeIcons[type]} alt="ico" />
 | 
			
		||||
            <FormattedMessage
 | 
			
		||||
              id={`${pluginId}.popUpForm.attributes.${type}.name`}
 | 
			
		||||
            >
 | 
			
		||||
          <AttributeIcon
 | 
			
		||||
            type={type}
 | 
			
		||||
            style={{ marginRight: 10 }}
 | 
			
		||||
            className="attributeIcon"
 | 
			
		||||
          />
 | 
			
		||||
          <FormattedMessage id={getTrad(`attribute.${type}`)}>
 | 
			
		||||
            {message => <span className="attributeType">{message}</span>}
 | 
			
		||||
          </FormattedMessage>
 | 
			
		||||
            <FormattedMessage id={description} />
 | 
			
		||||
          <FormattedMessage id={getTrad(`attribute.${type}.description`)} />
 | 
			
		||||
        </Card>
 | 
			
		||||
      </Button>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
AttributeOption.displayName = 'AttributeOption';
 | 
			
		||||
 | 
			
		||||
AttributeOption.defaultProps = {
 | 
			
		||||
  description: 'app.utils.defaultMessage',
 | 
			
		||||
  isDisplayed: false,
 | 
			
		||||
  nodeToFocus: -1,
 | 
			
		||||
  onClick: () => {},
 | 
			
		||||
  tabIndex: 0,
 | 
			
		||||
  type: 'string',
 | 
			
		||||
  type: 'text',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
AttributeOption.propTypes = {
 | 
			
		||||
  description: PropTypes.string,
 | 
			
		||||
  isDisplayed: PropTypes.bool,
 | 
			
		||||
  nodeToFocus: PropTypes.number,
 | 
			
		||||
  onClick: PropTypes.func,
 | 
			
		||||
  tabIndex: PropTypes.number,
 | 
			
		||||
  type: PropTypes.string,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -2,15 +2,20 @@ import React from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import { HeaderModalTitle } from 'strapi-helper-plugin';
 | 
			
		||||
import { AttributeIcon } from '@buffetjs/core';
 | 
			
		||||
import pluginId from '../../pluginId';
 | 
			
		||||
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 (
 | 
			
		||||
    <section>
 | 
			
		||||
      <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 }} />
 | 
			
		||||
        )}
 | 
			
		||||
        {!headerId && <span>{upperFirst(name)}</span>}
 | 
			
		||||
      </HeaderModalTitle>
 | 
			
		||||
    </section>
 | 
			
		||||
  );
 | 
			
		||||
@ -18,14 +23,14 @@ const ModalHeader = ({ headerId, name, type }) => {
 | 
			
		||||
 | 
			
		||||
ModalHeader.defaultProps = {
 | 
			
		||||
  headerId: '',
 | 
			
		||||
  iconType: 'contentType',
 | 
			
		||||
  name: '',
 | 
			
		||||
  type: 'contentType',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
ModalHeader.propTypes = {
 | 
			
		||||
  headerId: PropTypes.string,
 | 
			
		||||
  iconType: PropTypes.string,
 | 
			
		||||
  name: PropTypes.string,
 | 
			
		||||
  type: PropTypes.string,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default ModalHeader;
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,6 @@ const DataManagerProvider = ({ children }) => {
 | 
			
		||||
    isLoading,
 | 
			
		||||
    initialData,
 | 
			
		||||
    modifiedData,
 | 
			
		||||
    newSchema,
 | 
			
		||||
  } = reducerState.toJS();
 | 
			
		||||
 | 
			
		||||
  const contentTypeMatch = useRouteMatch(
 | 
			
		||||
@ -90,7 +89,6 @@ const DataManagerProvider = ({ children }) => {
 | 
			
		||||
        createSchema,
 | 
			
		||||
        initialData,
 | 
			
		||||
        modifiedData,
 | 
			
		||||
        newSchema,
 | 
			
		||||
        setModifiedData,
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
 | 
			
		||||
@ -6,16 +6,6 @@ const initialState = fromJS({
 | 
			
		||||
  initialData: {},
 | 
			
		||||
  modifiedData: {},
 | 
			
		||||
  isLoading: true,
 | 
			
		||||
  newSchema: {
 | 
			
		||||
    schemaType: '',
 | 
			
		||||
    schema: {},
 | 
			
		||||
    uid: '',
 | 
			
		||||
  },
 | 
			
		||||
  newSchemaClone: {
 | 
			
		||||
    schemaType: '',
 | 
			
		||||
    schema: {},
 | 
			
		||||
    uid: '',
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const reducer = (state, action) => {
 | 
			
		||||
@ -25,12 +15,21 @@ const reducer = (state, action) => {
 | 
			
		||||
        .update('components', () => fromJS(action.components))
 | 
			
		||||
        .update('contentTypes', () => fromJS(action.contentTypes))
 | 
			
		||||
        .update('isLoading', () => false);
 | 
			
		||||
    case 'CREATE_SCHEMA':
 | 
			
		||||
      console.log({ action });
 | 
			
		||||
      return state
 | 
			
		||||
        .updateIn(['newSchema', 'schema'], () => fromJS(action.data))
 | 
			
		||||
        .updateIn(['newSchema', 'uid'], () => fromJS(action.uid))
 | 
			
		||||
        .updateIn(['newSchema', 'schemaType'], () => fromJS(action.schemaType));
 | 
			
		||||
    case 'CREATE_SCHEMA': {
 | 
			
		||||
      const newSchema = {
 | 
			
		||||
        uid: action.uid,
 | 
			
		||||
        isTemporary: true,
 | 
			
		||||
        schema: {
 | 
			
		||||
          ...action.data,
 | 
			
		||||
          attributes: {},
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
      const key =
 | 
			
		||||
        action.schemaType === 'contentType' ? 'contentTypes' : 'components';
 | 
			
		||||
 | 
			
		||||
      return state.updateIn([key, action.uid], () => fromJS(newSchema));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case 'SET_MODIFIED_DATA':
 | 
			
		||||
      return state
 | 
			
		||||
        .update('initialData', () => OrderedMap(action.schemaToSet))
 | 
			
		||||
 | 
			
		||||
@ -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 {
 | 
			
		||||
  ButtonModal,
 | 
			
		||||
@ -18,15 +18,17 @@ import { get, isEmpty, upperFirst } from 'lodash';
 | 
			
		||||
import pluginId from '../../pluginId';
 | 
			
		||||
import useQuery from '../../hooks/useQuery';
 | 
			
		||||
import useDataManager from '../../hooks/useDataManager';
 | 
			
		||||
import AttributeOption from '../../components/AttributeOption';
 | 
			
		||||
import ModalHeader from '../../components/ModalHeader';
 | 
			
		||||
import HeaderModalNavContainer from '../../components/HeaderModalNavContainer';
 | 
			
		||||
import HeaderNavLink from '../../components/HeaderNavLink';
 | 
			
		||||
import getTrad from '../../utils/getTrad';
 | 
			
		||||
import getAttributes from './utils/attributes';
 | 
			
		||||
import forms from './utils/forms';
 | 
			
		||||
import { createUid } from './utils/createUid';
 | 
			
		||||
import init from './init';
 | 
			
		||||
import reducer, { initialState } from './reducer';
 | 
			
		||||
 | 
			
		||||
const getTrad = id => `${pluginId}.${id}`;
 | 
			
		||||
const NAVLINKS = [{ id: 'base' }, { id: 'advanced' }];
 | 
			
		||||
 | 
			
		||||
const FormModal = () => {
 | 
			
		||||
@ -34,40 +36,77 @@ const FormModal = () => {
 | 
			
		||||
    actionType: null,
 | 
			
		||||
    modalType: null,
 | 
			
		||||
    settingType: null,
 | 
			
		||||
    // uid: null,
 | 
			
		||||
    for: null,
 | 
			
		||||
    target: null,
 | 
			
		||||
    attributeType: null,
 | 
			
		||||
  };
 | 
			
		||||
  const [state, setState] = useState(initialStateData);
 | 
			
		||||
  const [reducerState, dispatch] = useReducer(reducer, initialState, init);
 | 
			
		||||
  const { push } = useHistory();
 | 
			
		||||
  const { search } = useLocation();
 | 
			
		||||
  const { formatMessage } = useGlobalContext();
 | 
			
		||||
  const isOpen = !isEmpty(search);
 | 
			
		||||
  const query = useQuery();
 | 
			
		||||
  const attributeOptionRef = useRef();
 | 
			
		||||
 | 
			
		||||
  const { contentTypes, createSchema, initialData } = useDataManager();
 | 
			
		||||
  const { formErrors, modifiedData } = reducerState.toJS();
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (isOpen) {
 | 
			
		||||
    if (!isEmpty(search)) {
 | 
			
		||||
      setState({
 | 
			
		||||
        actionType: query.get('actionType'),
 | 
			
		||||
        modalType: query.get('modalType'),
 | 
			
		||||
        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
 | 
			
		||||
  }, [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 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.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 newSearch = Object.keys(state).reduce((acc, current) => {
 | 
			
		||||
    const newSearch = Object.keys(state).reduce((acc, current, index) => {
 | 
			
		||||
      if (current !== 'settingType') {
 | 
			
		||||
        acc = `${acc}&${current}=${state[current]}`;
 | 
			
		||||
        acc = `${acc}${index === 0 ? '' : '&'}${current}=${state[current]}`;
 | 
			
		||||
      } else {
 | 
			
		||||
        acc = `${acc}&${current}=${nextTab}`;
 | 
			
		||||
        acc = `${acc}${index === 0 ? '' : '&'}${current}=${nextTab}`;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return acc;
 | 
			
		||||
@ -87,16 +126,21 @@ const FormModal = () => {
 | 
			
		||||
    e.preventDefault();
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      const schema = forms.contentType.schema(Object.keys(contentTypes));
 | 
			
		||||
      const schema = forms[state.modalType].schema(Object.keys(contentTypes));
 | 
			
		||||
 | 
			
		||||
      await schema.validate(modifiedData, { abortEarly: false });
 | 
			
		||||
      createSchema(modifiedData, state.modalType, createUid(modifiedData.name));
 | 
			
		||||
      handleToggle();
 | 
			
		||||
      // push({ p})
 | 
			
		||||
 | 
			
		||||
      createSchema(modifiedData, state.modalType, uid);
 | 
			
		||||
      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) {
 | 
			
		||||
      const errors = getYupInnerErrors(err);
 | 
			
		||||
      // TODO
 | 
			
		||||
      console.log({ errors });
 | 
			
		||||
      dispatch({
 | 
			
		||||
        type: 'SET_ERRORS',
 | 
			
		||||
        errors,
 | 
			
		||||
@ -112,23 +156,33 @@ const FormModal = () => {
 | 
			
		||||
      type: 'RESET_PROPS',
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
  const form = get(forms, [state.modalType, 'form', state.settingType], () => ({
 | 
			
		||||
    items: [],
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  const onOpened = () => {
 | 
			
		||||
    if (state.modalType === 'chooseAttribute') {
 | 
			
		||||
      attributeOptionRef.current.focus();
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Modal isOpen={isOpen} onClosed={onClosed} onToggle={handleToggle}>
 | 
			
		||||
    <Modal
 | 
			
		||||
      isOpen={isOpen}
 | 
			
		||||
      onOpened={onOpened}
 | 
			
		||||
      onClosed={onClosed}
 | 
			
		||||
      onToggle={handleToggle}
 | 
			
		||||
    >
 | 
			
		||||
      <HeaderModal>
 | 
			
		||||
        <ModalHeader
 | 
			
		||||
          name={name}
 | 
			
		||||
          name={state.target || name}
 | 
			
		||||
          headerId={headerId}
 | 
			
		||||
          type={state.modalType || 'contentType'}
 | 
			
		||||
          iconType={iconType || 'contentType'}
 | 
			
		||||
        />
 | 
			
		||||
        <section>
 | 
			
		||||
          <HeaderModalTitle>
 | 
			
		||||
            <FormattedMessage id={getTrad('configurations')}>
 | 
			
		||||
            <FormattedMessage id={getModalTitleSubHeader()}>
 | 
			
		||||
              {msg => <span>{upperFirst(msg)}</span>}
 | 
			
		||||
            </FormattedMessage>
 | 
			
		||||
            {!isPickingAttribute && (
 | 
			
		||||
              <>
 | 
			
		||||
                <div className="settings-tabs">
 | 
			
		||||
                  <HeaderModalNavContainer>
 | 
			
		||||
                    {NAVLINKS.map((link, index) => {
 | 
			
		||||
@ -138,24 +192,68 @@ const FormModal = () => {
 | 
			
		||||
                          key={link.id}
 | 
			
		||||
                          {...link}
 | 
			
		||||
                          onClick={() => {
 | 
			
		||||
                        setState(prev => ({ ...prev, settingType: link.id }));
 | 
			
		||||
                            setState(prev => ({
 | 
			
		||||
                              ...prev,
 | 
			
		||||
                              settingType: link.id,
 | 
			
		||||
                            }));
 | 
			
		||||
                            push({ search: getNextSearch(link.id) });
 | 
			
		||||
                          }}
 | 
			
		||||
                      nextTab={index === NAVLINKS.length - 1 ? 0 : index + 1}
 | 
			
		||||
                          nextTab={
 | 
			
		||||
                            index === NAVLINKS.length - 1 ? 0 : index + 1
 | 
			
		||||
                          }
 | 
			
		||||
                        />
 | 
			
		||||
                      );
 | 
			
		||||
                    })}
 | 
			
		||||
                  </HeaderModalNavContainer>
 | 
			
		||||
                </div>
 | 
			
		||||
                <hr />
 | 
			
		||||
              </>
 | 
			
		||||
            )}
 | 
			
		||||
          </HeaderModalTitle>
 | 
			
		||||
        </section>
 | 
			
		||||
      </HeaderModal>
 | 
			
		||||
      <form onSubmit={handleSubmit}>
 | 
			
		||||
        <ModalForm>
 | 
			
		||||
          <ModalBody>
 | 
			
		||||
          <ModalBody style={modalBodyStyle}>
 | 
			
		||||
            <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 (
 | 
			
		||||
                        <div className="row" key={index}>
 | 
			
		||||
                          {row.map(input => {
 | 
			
		||||
@ -196,10 +294,12 @@ const FormModal = () => {
 | 
			
		||||
                          })}
 | 
			
		||||
                        </div>
 | 
			
		||||
                      );
 | 
			
		||||
              })}
 | 
			
		||||
                    }
 | 
			
		||||
                  )}
 | 
			
		||||
            </div>
 | 
			
		||||
          </ModalBody>
 | 
			
		||||
        </ModalForm>
 | 
			
		||||
        {!isPickingAttribute && (
 | 
			
		||||
          <ModalFooter>
 | 
			
		||||
            <section>
 | 
			
		||||
              <ButtonModal
 | 
			
		||||
@ -210,6 +310,7 @@ const FormModal = () => {
 | 
			
		||||
              <ButtonModal message="form.button.done" type="submit" />
 | 
			
		||||
            </section>
 | 
			
		||||
          </ModalFooter>
 | 
			
		||||
        )}
 | 
			
		||||
      </form>
 | 
			
		||||
    </Modal>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
import * as yup from 'yup';
 | 
			
		||||
import { translatedErrors as errorsTrads } from 'strapi-helper-plugin';
 | 
			
		||||
import pluginId from '../../../pluginId';
 | 
			
		||||
import getTrad from '../../../utils/getTrad';
 | 
			
		||||
import { createUid, nameToSlug } from './createUid';
 | 
			
		||||
 | 
			
		||||
yup.addMethod(yup.mixed, 'defined', function() {
 | 
			
		||||
@ -22,6 +23,57 @@ yup.addMethod(yup.string, 'unique', function(message, allReadyTakenValues) {
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
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: {
 | 
			
		||||
    schema(allReadyTakenValues) {
 | 
			
		||||
      return yup.object().shape({
 | 
			
		||||
 | 
			
		||||
@ -13,14 +13,19 @@ import CustomLink from '../../components/CustomLink';
 | 
			
		||||
import useDataManager from '../../hooks/useDataManager';
 | 
			
		||||
import Wrapper from './Wrapper';
 | 
			
		||||
 | 
			
		||||
// const displayNotificationCTNotSaved = () => {
 | 
			
		||||
//   strapi.notification.info(
 | 
			
		||||
//     `${pluginId}.notification.info.contentType.creating.notSaved`
 | 
			
		||||
//   );
 | 
			
		||||
// };
 | 
			
		||||
const displayNotificationCTNotSaved = () => {
 | 
			
		||||
  strapi.notification.info(
 | 
			
		||||
    `${pluginId}.notification.info.contentType.creating.notSaved`
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function LeftMenu() {
 | 
			
		||||
  const { components, contentTypes, newSchema } = useDataManager();
 | 
			
		||||
  const {
 | 
			
		||||
    components,
 | 
			
		||||
    contentTypes,
 | 
			
		||||
    // initialData,
 | 
			
		||||
    // modifiedData,
 | 
			
		||||
  } = useDataManager();
 | 
			
		||||
  const { currentEnvironment } = useGlobalContext();
 | 
			
		||||
  const { push } = useHistory();
 | 
			
		||||
  const isProduction = currentEnvironment === 'production';
 | 
			
		||||
@ -40,14 +45,24 @@ function LeftMenu() {
 | 
			
		||||
    })),
 | 
			
		||||
    obj => obj.title
 | 
			
		||||
  );
 | 
			
		||||
  const tempSchemaCT =
 | 
			
		||||
    newSchema.schemaType === 'contentType'
 | 
			
		||||
      ? {
 | 
			
		||||
          name: newSchema.uid,
 | 
			
		||||
          title: newSchema.schema.name,
 | 
			
		||||
          to: `/plugins/${pluginId}/content-types/${newSchema.uid}`,
 | 
			
		||||
  const canOpenModalCreateCTorComponent = () => {
 | 
			
		||||
    return (
 | 
			
		||||
      !Object.keys(contentTypes).some(
 | 
			
		||||
        ct => contentTypes[ct].isTemporary === true
 | 
			
		||||
      ) &&
 | 
			
		||||
      !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 = [
 | 
			
		||||
    {
 | 
			
		||||
      name: 'models',
 | 
			
		||||
@ -61,10 +76,7 @@ function LeftMenu() {
 | 
			
		||||
          disabled: isProduction,
 | 
			
		||||
          id: `${pluginId}.button.model.create`,
 | 
			
		||||
          onClick: () => {
 | 
			
		||||
            push({
 | 
			
		||||
              search:
 | 
			
		||||
                'modalType=contentType&actionType=create&settingType=base',
 | 
			
		||||
            });
 | 
			
		||||
            handleClickOpenModal('contentType');
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
@ -75,7 +87,6 @@ function LeftMenu() {
 | 
			
		||||
            title: contentTypes[uid].schema.name,
 | 
			
		||||
            to: `/plugins/${pluginId}/content-types/${uid}`,
 | 
			
		||||
          }))
 | 
			
		||||
          .concat(tempSchemaCT)
 | 
			
		||||
          .filter(obj => obj !== null),
 | 
			
		||||
        obj => obj.title
 | 
			
		||||
      ),
 | 
			
		||||
@ -92,9 +103,7 @@ function LeftMenu() {
 | 
			
		||||
          disabled: isProduction,
 | 
			
		||||
          id: `${pluginId}.button.component.create`,
 | 
			
		||||
          onClick: () => {
 | 
			
		||||
            push({
 | 
			
		||||
              search: 'modalType=component&actionType=create&settingType=base',
 | 
			
		||||
            });
 | 
			
		||||
            handleClickOpenModal('component');
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
@ -2,23 +2,7 @@
 | 
			
		||||
  "model": "Content Type",
 | 
			
		||||
  "group": "Group",
 | 
			
		||||
  "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.another": "Add Another Field",
 | 
			
		||||
  "button.contentType.add": "Add a Content Type",
 | 
			
		||||
@ -146,34 +130,7 @@
 | 
			
		||||
  "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.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.subtitle.model": "Select a field for your content type",
 | 
			
		||||
  "popUpForm.choose.attributes.header.subtitle.group": "Select a field for your group",
 | 
			
		||||
@ -221,13 +178,45 @@
 | 
			
		||||
  "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.",
 | 
			
		||||
 | 
			
		||||
  "modalForm.contentType.header-create": "Create a content type",
 | 
			
		||||
  "modalForm.header-edit": "Edit {name}",
 | 
			
		||||
  "modalForm.component.header-create": "Create a component",
 | 
			
		||||
  "attribute.boolean": "Boolean",
 | 
			
		||||
  "attribute.boolean.description": "Yes or no, 1 or 0, true or false",
 | 
			
		||||
  "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",
 | 
			
		||||
  "contentType.displayName.label": "Display name",
 | 
			
		||||
  "contentType.collectionName.description": "Useful when the name of your Content Type and your table name differ",
 | 
			
		||||
  "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"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,5 @@
 | 
			
		||||
import pluginId from '../pluginId';
 | 
			
		||||
 | 
			
		||||
const getTrad = id => `${pluginId}.${id}`;
 | 
			
		||||
 | 
			
		||||
export default getTrad;
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user