mirror of
https://github.com/strapi/strapi.git
synced 2025-09-06 07:12:26 +00:00
Merge pull request #5322 from strapi/single-types/uid-ctm
Add UID in CTM and fix Single types alignments
This commit is contained in:
commit
0bb347f79b
Binary file not shown.
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 352 KiB |
@ -26,7 +26,7 @@ const Wrapper = styled.div`
|
|||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
letter-spacing: 0.05rem;
|
letter-spacing: 0.05rem;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
color: ${props => props.theme.main.colors.strapi['gray-light']};
|
color: ${({ theme }) => theme.main.colors.strapi.grayLight};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import styled from 'styled-components';
|
|||||||
|
|
||||||
const A = styled.a`
|
const A = styled.a`
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-top: 0.8rem;
|
padding-top: 0.7rem;
|
||||||
padding-bottom: 0.2rem;
|
padding-bottom: 0.2rem;
|
||||||
padding-left: 1.6rem;
|
padding-left: 1.6rem;
|
||||||
min-height: 3.6rem;
|
min-height: 3.6rem;
|
||||||
|
@ -5,7 +5,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|||||||
|
|
||||||
const FaIcon = styled(({ small, ...props }) => <FontAwesomeIcon {...props} />)`
|
const FaIcon = styled(({ small, ...props }) => <FontAwesomeIcon {...props} />)`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: calc(50% - 0.9rem + 0.5rem);
|
top: calc(50% - 0.9rem + 0.3rem);
|
||||||
left: 1.6rem;
|
left: 1.6rem;
|
||||||
margin-right: 1.2rem;
|
margin-right: 1.2rem;
|
||||||
font-size: ${props => (props.small ? '1rem' : '1.4rem')};
|
font-size: ${props => (props.small ? '1rem' : '1.4rem')};
|
||||||
@ -14,9 +14,7 @@ const FaIcon = styled(({ small, ...props }) => <FontAwesomeIcon {...props} />)`
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const LeftMenuIcon = ({ icon }) => (
|
const LeftMenuIcon = ({ icon }) => <FaIcon small={icon === 'circle'} icon={icon} />;
|
||||||
<FaIcon small={icon === 'circle'} icon={icon} />
|
|
||||||
);
|
|
||||||
|
|
||||||
LeftMenuIcon.propTypes = {
|
LeftMenuIcon.propTypes = {
|
||||||
icon: PropTypes.string,
|
icon: PropTypes.string,
|
||||||
|
@ -2,9 +2,9 @@ import styled from 'styled-components';
|
|||||||
|
|
||||||
const Search = styled.input`
|
const Search = styled.input`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0 21px;
|
padding: 0 15px;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
font-size: 1.3rem;
|
font-size: 1.2rem;
|
||||||
color: ${props => props.theme.main.colors.white};
|
color: ${props => props.theme.main.colors.white};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -5,13 +5,14 @@ const Title = styled.div`
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding-left: 2rem;
|
padding-left: 2rem;
|
||||||
padding-right: 1.6rem;
|
padding-right: 1.6rem;
|
||||||
padding-top: 0.7rem;
|
padding-top: 1rem;
|
||||||
margin-bottom: 0.8rem;
|
margin-bottom: 0.8rem;
|
||||||
color: ${props => props.theme.main.colors.leftMenu['title-color']};
|
color: ${props => props.theme.main.colors.leftMenu['title-color']};
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
font-size: 1.1rem;
|
font-size: 1.2rem;
|
||||||
letter-spacing: 0.1rem;
|
letter-spacing: 0.1rem;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
|
max-height: 28px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
Title.defaultProps = {
|
Title.defaultProps = {
|
||||||
|
@ -6,7 +6,7 @@ const EmptyLinksList = styled.div`
|
|||||||
padding-right: 1.6rem;
|
padding-right: 1.6rem;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
min-height: 3.6rem;
|
min-height: 3.6rem;
|
||||||
padding-top: 0.2rem;
|
padding-top: 0.6rem;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
EmptyLinksList.defaultProps = {
|
EmptyLinksList.defaultProps = {
|
||||||
|
@ -10,12 +10,7 @@ import { FormattedMessage } from 'react-intl';
|
|||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
import { get } from 'lodash';
|
import { get } from 'lodash';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import {
|
import { ButtonDropdown, DropdownItem, DropdownMenu, DropdownToggle } from 'reactstrap';
|
||||||
ButtonDropdown,
|
|
||||||
DropdownItem,
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownToggle,
|
|
||||||
} from 'reactstrap';
|
|
||||||
import { auth } from 'strapi-helper-plugin';
|
import { auth } from 'strapi-helper-plugin';
|
||||||
import Wrapper from './components';
|
import Wrapper from './components';
|
||||||
|
|
||||||
@ -26,14 +21,14 @@ const Logout = ({ history: { push } }) => {
|
|||||||
const id = get(auth.getUserInfo(), 'id');
|
const id = get(auth.getUserInfo(), 'id');
|
||||||
|
|
||||||
push({
|
push({
|
||||||
pathname: `/plugins/content-manager/strapi::administrator/${id}`,
|
pathname: `/plugins/content-manager/collectionType/strapi::administrator/${id}`,
|
||||||
search:
|
search:
|
||||||
'?redirectUrl=/plugins/content-manager/strapi::administrator/&_page=0&_limit=0&_sort=id',
|
'?redirectUrl=/plugins/content-manager/collectionType/strapi::administrator/&_page=0&_limit=0&_sort=id',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const handleGoToAdministrator = () => {
|
const handleGoToAdministrator = () => {
|
||||||
push({
|
push({
|
||||||
pathname: '/plugins/content-manager/strapi::administrator',
|
pathname: '/plugins/content-manager/collectionType/strapi::administrator',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
|
@ -10,8 +10,9 @@ const colors = {
|
|||||||
pink: '#ff5b77',
|
pink: '#ff5b77',
|
||||||
purple: '#613d7c',
|
purple: '#613d7c',
|
||||||
gray: '#464a4c',
|
gray: '#464a4c',
|
||||||
|
border: '#E3E9F3',
|
||||||
'gray-dark': '#292b2c',
|
'gray-dark': '#292b2c',
|
||||||
'gray-light': '#636c72',
|
grayLight: '#636c72',
|
||||||
'gray-lighter': '#eceeef',
|
'gray-lighter': '#eceeef',
|
||||||
'gray-lightest': '#f7f7f9',
|
'gray-lightest': '#f7f7f9',
|
||||||
|
|
||||||
|
@ -12,9 +12,7 @@ export { default as Button } from './components/Button';
|
|||||||
export { default as ButtonModal } from './components/ButtonModal';
|
export { default as ButtonModal } from './components/ButtonModal';
|
||||||
export { default as CircleButton } from './components/CircleButton';
|
export { default as CircleButton } from './components/CircleButton';
|
||||||
export { default as ContainerFluid } from './components/ContainerFluid';
|
export { default as ContainerFluid } from './components/ContainerFluid';
|
||||||
export {
|
export { default as EmptyAttributesBlock } from './components/EmptyAttributesBlock';
|
||||||
default as EmptyAttributesBlock,
|
|
||||||
} from './components/EmptyAttributesBlock';
|
|
||||||
export { default as ErrorBoundary } from './components/ErrorBoundary';
|
export { default as ErrorBoundary } from './components/ErrorBoundary';
|
||||||
export { default as ExtendComponent } from './components/ExtendComponent';
|
export { default as ExtendComponent } from './components/ExtendComponent';
|
||||||
export { default as GlobalPagination } from './components/GlobalPagination';
|
export { default as GlobalPagination } from './components/GlobalPagination';
|
||||||
@ -24,54 +22,32 @@ export { default as HeaderModalTitle } from './components/HeaderModalTitle';
|
|||||||
export { default as IcoContainer } from './components/IcoContainer';
|
export { default as IcoContainer } from './components/IcoContainer';
|
||||||
export { default as InputAddon } from './components/InputAddon';
|
export { default as InputAddon } from './components/InputAddon';
|
||||||
|
|
||||||
export {
|
export { default as InputAddonWithErrors } from './components/InputAddonWithErrors';
|
||||||
default as InputAddonWithErrors,
|
|
||||||
} from './components/InputAddonWithErrors';
|
|
||||||
export { default as InputCheckbox } from './components/InputCheckbox';
|
export { default as InputCheckbox } from './components/InputCheckbox';
|
||||||
export {
|
export { default as InputCheckboxWithErrors } from './components/InputCheckboxWithErrors';
|
||||||
default as InputCheckboxWithErrors,
|
|
||||||
} from './components/InputCheckboxWithErrors';
|
|
||||||
export { default as InputDate } from './components/InputDate';
|
export { default as InputDate } from './components/InputDate';
|
||||||
export {
|
export { default as InputDateWithErrors } from './components/InputDateWithErrors';
|
||||||
default as InputDateWithErrors,
|
|
||||||
} from './components/InputDateWithErrors';
|
|
||||||
export { default as InputDescription } from './components/InputDescription';
|
export { default as InputDescription } from './components/InputDescription';
|
||||||
export { default as InputEmail } from './components/InputEmail';
|
export { default as InputEmail } from './components/InputEmail';
|
||||||
export {
|
export { default as InputEmailWithErrors } from './components/InputEmailWithErrors';
|
||||||
default as InputEmailWithErrors,
|
|
||||||
} from './components/InputEmailWithErrors';
|
|
||||||
export { default as InputErrors } from './components/InputErrors';
|
export { default as InputErrors } from './components/InputErrors';
|
||||||
export { default as InputFile } from './components/InputFile';
|
export { default as InputFile } from './components/InputFile';
|
||||||
export { default as InputNumber } from './components/InputNumber';
|
export { default as InputNumber } from './components/InputNumber';
|
||||||
export {
|
export { default as InputNumberWithErrors } from './components/InputNumberWithErrors';
|
||||||
default as InputNumberWithErrors,
|
|
||||||
} from './components/InputNumberWithErrors';
|
|
||||||
export { default as InputPassword } from './components/InputPassword';
|
export { default as InputPassword } from './components/InputPassword';
|
||||||
export {
|
export { default as InputPasswordWithErrors } from './components/InputPasswordWithErrors';
|
||||||
default as InputPasswordWithErrors,
|
|
||||||
} from './components/InputPasswordWithErrors';
|
|
||||||
export { default as InputSearch } from './components/InputSearch';
|
export { default as InputSearch } from './components/InputSearch';
|
||||||
export {
|
export { default as InputSearchWithErrors } from './components/InputSearchWithErrors';
|
||||||
default as InputSearchWithErrors,
|
|
||||||
} from './components/InputSearchWithErrors';
|
|
||||||
export { default as InputSelect } from './components/InputSelect';
|
export { default as InputSelect } from './components/InputSelect';
|
||||||
export {
|
export { default as InputSelectWithErrors } from './components/InputSelectWithErrors';
|
||||||
default as InputSelectWithErrors,
|
|
||||||
} from './components/InputSelectWithErrors';
|
|
||||||
export { default as InputsIndex } from './components/InputsIndex';
|
export { default as InputsIndex } from './components/InputsIndex';
|
||||||
export { default as InputSpacer } from './components/InputSpacer';
|
export { default as InputSpacer } from './components/InputSpacer';
|
||||||
export { default as InputText } from './components/InputText';
|
export { default as InputText } from './components/InputText';
|
||||||
export {
|
export { default as InputTextWithErrors } from './components/InputTextWithErrors';
|
||||||
default as InputTextWithErrors,
|
|
||||||
} from './components/InputTextWithErrors';
|
|
||||||
export { default as InputTextArea } from './components/InputTextArea';
|
export { default as InputTextArea } from './components/InputTextArea';
|
||||||
export {
|
export { default as InputTextAreaWithErrors } from './components/InputTextAreaWithErrors';
|
||||||
default as InputTextAreaWithErrors,
|
|
||||||
} from './components/InputTextAreaWithErrors';
|
|
||||||
export { default as InputToggle } from './components/InputToggle';
|
export { default as InputToggle } from './components/InputToggle';
|
||||||
export {
|
export { default as InputToggleWithErrors } from './components/InputToggleWithErrors';
|
||||||
default as InputToggleWithErrors,
|
|
||||||
} from './components/InputToggleWithErrors';
|
|
||||||
|
|
||||||
export { default as Label } from './components/Label';
|
export { default as Label } from './components/Label';
|
||||||
export { default as LeftMenu } from './components/LeftMenu';
|
export { default as LeftMenu } from './components/LeftMenu';
|
||||||
@ -86,9 +62,7 @@ export { default as ListTitle } from './components/ListTitle';
|
|||||||
|
|
||||||
export { default as LoadingBar } from './components/LoadingBar';
|
export { default as LoadingBar } from './components/LoadingBar';
|
||||||
export { default as LoadingIndicator } from './components/LoadingIndicator';
|
export { default as LoadingIndicator } from './components/LoadingIndicator';
|
||||||
export {
|
export { default as LoadingIndicatorPage } from './components/LoadingIndicatorPage';
|
||||||
default as LoadingIndicatorPage,
|
|
||||||
} from './components/LoadingIndicatorPage';
|
|
||||||
|
|
||||||
export { default as Modal } from './components/Modal';
|
export { default as Modal } from './components/Modal';
|
||||||
export { default as ModalBody } from './components/BodyModal';
|
export { default as ModalBody } from './components/BodyModal';
|
||||||
@ -105,11 +79,7 @@ export { default as TrashButton } from './components/TrashButton';
|
|||||||
export { default as ViewContainer } from './components/ViewContainer';
|
export { default as ViewContainer } from './components/ViewContainer';
|
||||||
|
|
||||||
// Contexts
|
// Contexts
|
||||||
export {
|
export { GlobalContext, GlobalContextProvider, useGlobalContext } from './contexts/GlobalContext';
|
||||||
GlobalContext,
|
|
||||||
GlobalContextProvider,
|
|
||||||
useGlobalContext,
|
|
||||||
} from './contexts/GlobalContext';
|
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
export { default as auth } from './utils/auth';
|
export { default as auth } from './utils/auth';
|
||||||
|
@ -58,12 +58,7 @@ const CustomTable = ({ data, headers, isBulkable }) => {
|
|||||||
handleGoTo(row.id);
|
handleGoTo(row.id);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Row
|
<Row isBulkable={isBulkable} headers={headers} row={row} goTo={handleGoTo} />
|
||||||
isBulkable={isBulkable}
|
|
||||||
headers={headers}
|
|
||||||
row={row}
|
|
||||||
goTo={handleGoTo}
|
|
||||||
/>
|
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
@ -73,9 +68,7 @@ const CustomTable = ({ data, headers, isBulkable }) => {
|
|||||||
<Table className="table">
|
<Table className="table">
|
||||||
<TableHeader headers={headers} isBulkable={isBulkable} />
|
<TableHeader headers={headers} isBulkable={isBulkable} />
|
||||||
<tbody>
|
<tbody>
|
||||||
{entriesToDelete.length > 0 && (
|
{entriesToDelete.length > 0 && <ActionCollapse colSpan={colSpanLength} />}
|
||||||
<ActionCollapse colSpan={colSpanLength} />
|
|
||||||
)}
|
|
||||||
{content}
|
{content}
|
||||||
</tbody>
|
</tbody>
|
||||||
</Table>
|
</Table>
|
||||||
@ -91,13 +84,6 @@ CustomTable.defaultProps = {
|
|||||||
CustomTable.propTypes = {
|
CustomTable.propTypes = {
|
||||||
data: PropTypes.array,
|
data: PropTypes.array,
|
||||||
headers: PropTypes.array,
|
headers: PropTypes.array,
|
||||||
history: PropTypes.shape({
|
|
||||||
location: PropTypes.shape({
|
|
||||||
pathname: PropTypes.string,
|
|
||||||
search: PropTypes.string,
|
|
||||||
}),
|
|
||||||
push: PropTypes.func.isRequired,
|
|
||||||
}).isRequired,
|
|
||||||
isBulkable: PropTypes.bool,
|
isBulkable: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
import { InputText } from '@buffetjs/core';
|
||||||
|
import { colors } from '@buffetjs/styles';
|
||||||
|
|
||||||
|
const InputUID = styled(InputText)`
|
||||||
|
width: 100%;
|
||||||
|
${({ error }) =>
|
||||||
|
error &&
|
||||||
|
`
|
||||||
|
> input {
|
||||||
|
border-color: ${colors.darkOrange};
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default InputUID;
|
@ -0,0 +1,21 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const Option = styled.div`
|
||||||
|
&:hover {
|
||||||
|
background-color: #e4f0fc;
|
||||||
|
.right-label {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cursor: pointer;
|
||||||
|
line-height: 2.6rem;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
padding: 5px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
.right-label {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default Option;
|
@ -0,0 +1,12 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const OptionsTitle = styled.div`
|
||||||
|
line-height: 2.1rem;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
padding: 5px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: ${({ theme }) => theme.main.colors.grayLight};
|
||||||
|
border-bottom: 1px solid ${({ theme }) => theme.main.colors.border};
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default OptionsTitle;
|
@ -0,0 +1,9 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const RightOptionLabel = styled.div`
|
||||||
|
color: ${({ theme }) => theme.main.colors.strapi.blue};
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: bold;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default RightOptionLabel;
|
@ -0,0 +1,35 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import getTrad from '../../../utils/getTrad'
|
||||||
|
import OptionsWrapper from './wrapper';
|
||||||
|
import Option from './Option';
|
||||||
|
import OptionsTitle from './OptionsTitle';
|
||||||
|
import RightOptionLabel from './RightOptionLabel';
|
||||||
|
|
||||||
|
const Options = ({ options, title }) => (
|
||||||
|
<OptionsWrapper>
|
||||||
|
{title && <OptionsTitle>{title}</OptionsTitle>}
|
||||||
|
{options.map(option => (
|
||||||
|
<Option key={option.id} onClick={option.onClick}>
|
||||||
|
<div>{option.label}</div>
|
||||||
|
|
||||||
|
<FormattedMessage id={getTrad('components.uid.apply')}>
|
||||||
|
{msg => <RightOptionLabel className="right-label">{msg}</RightOptionLabel>}
|
||||||
|
</FormattedMessage>
|
||||||
|
</Option>
|
||||||
|
))}
|
||||||
|
</OptionsWrapper>
|
||||||
|
);
|
||||||
|
|
||||||
|
Options.propTypes = {
|
||||||
|
options: PropTypes.array.isRequired,
|
||||||
|
title: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
Options.defaultProps = {
|
||||||
|
title: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Options;
|
@ -0,0 +1,13 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const wrapper = styled.div`
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 3px;
|
||||||
|
border: 1px solid ${props => props.theme.main.colors.border};
|
||||||
|
border-radius: 2px;
|
||||||
|
background-color: white;
|
||||||
|
z-index: 10;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default wrapper;
|
@ -0,0 +1,18 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const RegenerateButton = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 40px;
|
||||||
|
height: 32px;
|
||||||
|
background-color: #fafafb;
|
||||||
|
z-index: 10;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #aed4fb;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default RegenerateButton;
|
@ -0,0 +1,14 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const RightContent = styled.div`
|
||||||
|
display: flex;
|
||||||
|
z-index: 10;
|
||||||
|
background-color: ${({ theme }) => theme.main.colors.white};
|
||||||
|
align-items: center;
|
||||||
|
line-height: 32px;
|
||||||
|
right: 1px;
|
||||||
|
top: 1px;
|
||||||
|
position: absolute;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default RightContent;
|
@ -0,0 +1,69 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import { Success, Remove } from '@buffetjs/icons';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { useGlobalContext } from 'strapi-helper-plugin';
|
||||||
|
|
||||||
|
import pluginId from '../../pluginId';
|
||||||
|
import getTrad from '../../utils/getTrad';
|
||||||
|
|
||||||
|
// Note you don't need to create a specific file for this one
|
||||||
|
// as it will soon be replaced by the Text one so you can leave it in this file.
|
||||||
|
const RightContentLabel = styled.div`
|
||||||
|
padding: 0 5px;
|
||||||
|
text-transform: capitalize;
|
||||||
|
color: ${({ theme, color }) => theme.main.colors[color]};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const RightLabel = ({ label, availability }) => {
|
||||||
|
const { formatMessage } = useGlobalContext();
|
||||||
|
|
||||||
|
if (label) {
|
||||||
|
return (
|
||||||
|
<RightContentLabel color="blue">
|
||||||
|
{formatMessage({
|
||||||
|
id: getTrad('components.uid.regenerate'),
|
||||||
|
})}
|
||||||
|
</RightContentLabel>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (availability !== null) {
|
||||||
|
// This should be more generic in the futur.
|
||||||
|
return availability.isAvailable ? (
|
||||||
|
<>
|
||||||
|
<Success fill="#27b70f" width="20px" height="20px" />
|
||||||
|
<RightContentLabel color="green">
|
||||||
|
{formatMessage({
|
||||||
|
id: `${pluginId}.components.uid.available`,
|
||||||
|
})}
|
||||||
|
</RightContentLabel>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Remove fill="#ff203c" width="12px" height="12px" />
|
||||||
|
<RightContentLabel color="red">
|
||||||
|
{formatMessage({
|
||||||
|
id: getTrad('components.uid.unavailable'),
|
||||||
|
})}
|
||||||
|
</RightContentLabel>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
RightLabel.propTypes = {
|
||||||
|
label: PropTypes.string,
|
||||||
|
availability: PropTypes.shape({
|
||||||
|
isAvailable: PropTypes.bool,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
RightLabel.defaultProps = {
|
||||||
|
label: null,
|
||||||
|
availability: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RightLabel;
|
@ -0,0 +1,265 @@
|
|||||||
|
import React, { useEffect, useState, useRef } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Sync } from '@buffetjs/icons';
|
||||||
|
import { ErrorMessage as BaseErrorMessage } from '@buffetjs/styles';
|
||||||
|
import { Label, Error } from '@buffetjs/core';
|
||||||
|
import { useDebounce, useClickAwayListener } from '@buffetjs/hooks';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { request, LoadingIndicator } from 'strapi-helper-plugin';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
import { isEmpty } from 'lodash';
|
||||||
|
|
||||||
|
import pluginId from '../../pluginId';
|
||||||
|
import getRequestUrl from '../../utils/getRequestUrl';
|
||||||
|
import useDataManager from '../../hooks/useDataManager';
|
||||||
|
import RightLabel from './RightLabel';
|
||||||
|
import Options from './Options';
|
||||||
|
import RegenerateButton from './RegenerateButton';
|
||||||
|
import RightContent from './RightContent';
|
||||||
|
import Input from './InputUID';
|
||||||
|
|
||||||
|
// There is no need to create additional files for those little components.
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
position: relative;
|
||||||
|
padding-bottom: 23px;
|
||||||
|
`;
|
||||||
|
const InputContainer = styled.div`
|
||||||
|
position: relative;
|
||||||
|
`;
|
||||||
|
const ErrorMessage = styled(BaseErrorMessage)`
|
||||||
|
padding-top: 10px;
|
||||||
|
`;
|
||||||
|
const Name = styled(Label)`
|
||||||
|
display: block;
|
||||||
|
text-transform: capitalize;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
// This component should be in buffetjs. It will be used in the media lib.
|
||||||
|
// This component will be the strapi custom dropdown component.
|
||||||
|
// TODO : Make this component generic -> InputDropdown.
|
||||||
|
// TODO : Use the Compounds components pattern
|
||||||
|
// https://blog.bitsrc.io/understanding-compound-components-in-react-23c4b84535b5
|
||||||
|
const InputUID = ({
|
||||||
|
attribute,
|
||||||
|
contentTypeUID,
|
||||||
|
error: inputError,
|
||||||
|
name,
|
||||||
|
onChange,
|
||||||
|
required,
|
||||||
|
validations,
|
||||||
|
value,
|
||||||
|
}) => {
|
||||||
|
const { modifiedData, initialData } = useDataManager();
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [availability, setAvailability] = useState(null);
|
||||||
|
const [isSuggestionOpen, setIsSuggestionOpen] = useState(true);
|
||||||
|
const [isCustomized, setIsCustomized] = useState(false);
|
||||||
|
const [label, setLabel] = useState();
|
||||||
|
const debouncedValue = useDebounce(value, 300);
|
||||||
|
const debouncedTargetFieldValue = useDebounce(modifiedData[attribute.targetField], 300);
|
||||||
|
const wrapperRef = useRef(null);
|
||||||
|
const generateUid = useRef();
|
||||||
|
const initialValue = initialData[name];
|
||||||
|
const isCreation = isEmpty(initialData);
|
||||||
|
|
||||||
|
generateUid.current = async () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
const requestURL = getRequestUrl('explorer/uid/generate');
|
||||||
|
try {
|
||||||
|
const { data } = await request(requestURL, {
|
||||||
|
method: 'POST',
|
||||||
|
body: {
|
||||||
|
contentTypeUID,
|
||||||
|
field: name,
|
||||||
|
data: modifiedData,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
onChange({ target: { name, value: data, type: 'text' } });
|
||||||
|
setIsLoading(false);
|
||||||
|
} catch (err) {
|
||||||
|
console.error({ err });
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkAvailability = async () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
const requestURL = getRequestUrl('explorer/uid/check-availability');
|
||||||
|
try {
|
||||||
|
const data = await request(requestURL, {
|
||||||
|
method: 'POST',
|
||||||
|
body: {
|
||||||
|
contentTypeUID,
|
||||||
|
field: name,
|
||||||
|
value: value || null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
setAvailability(data);
|
||||||
|
|
||||||
|
if (data.suggestion) {
|
||||||
|
setIsSuggestionOpen(true);
|
||||||
|
}
|
||||||
|
setIsLoading(false);
|
||||||
|
} catch (err) {
|
||||||
|
console.error({ err });
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!value && required) {
|
||||||
|
generateUid.current();
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (debouncedValue && debouncedValue !== initialValue) {
|
||||||
|
checkAvailability();
|
||||||
|
}
|
||||||
|
if (!debouncedValue) {
|
||||||
|
setAvailability(null);
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [debouncedValue, initialValue]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let timer;
|
||||||
|
|
||||||
|
if (availability && availability.isAvailable) {
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
setAvailability(null);
|
||||||
|
}, 4000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (timer) {
|
||||||
|
clearTimeout(timer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [availability]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isCustomized && isCreation && debouncedTargetFieldValue !== null) {
|
||||||
|
generateUid.current();
|
||||||
|
}
|
||||||
|
}, [debouncedTargetFieldValue, isCustomized, isCreation]);
|
||||||
|
|
||||||
|
useClickAwayListener(wrapperRef, () => setIsSuggestionOpen(false));
|
||||||
|
|
||||||
|
const handleFocus = () => {
|
||||||
|
if (availability && availability.suggestion) {
|
||||||
|
setIsSuggestionOpen(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSuggestionClick = () => {
|
||||||
|
setIsSuggestionOpen(false);
|
||||||
|
onChange({ target: { name, value: availability.suggestion, type: 'text' } });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleGenerateMouseEnter = () => {
|
||||||
|
setLabel('regenerate');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleGenerateMouseLeave = () => {
|
||||||
|
setLabel(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange = (e, canCheck, dispatch) => {
|
||||||
|
if (!canCheck) {
|
||||||
|
dispatch({
|
||||||
|
type: 'SET_CHECK',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: 'SET_ERROR',
|
||||||
|
error: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (e.target.value && isCreation) {
|
||||||
|
setIsCustomized(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange(e);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Error name={name} inputError={inputError} type="text" validations={validations}>
|
||||||
|
{({ canCheck, onBlur, error, dispatch }) => {
|
||||||
|
const hasError = error && error !== null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper ref={wrapperRef}>
|
||||||
|
<Name htmlFor={name}>{name}</Name>
|
||||||
|
<InputContainer>
|
||||||
|
<Input
|
||||||
|
error={hasError}
|
||||||
|
onFocus={handleFocus}
|
||||||
|
name={name}
|
||||||
|
onChange={e => handleChange(e, canCheck, dispatch)}
|
||||||
|
type="text"
|
||||||
|
onBlur={onBlur}
|
||||||
|
// eslint-disable-next-line no-irregular-whitespace
|
||||||
|
value={value || ''}
|
||||||
|
/>
|
||||||
|
<RightContent>
|
||||||
|
<RightLabel availability={availability} label={label} />
|
||||||
|
<RegenerateButton
|
||||||
|
onMouseEnter={handleGenerateMouseEnter}
|
||||||
|
onMouseLeave={handleGenerateMouseLeave}
|
||||||
|
onClick={generateUid.current}
|
||||||
|
>
|
||||||
|
{isLoading ? (
|
||||||
|
<LoadingIndicator />
|
||||||
|
) : (
|
||||||
|
<Sync fill={label ? '#007EFF' : '#B5B7BB'} width="15px" height="15px" />
|
||||||
|
)}
|
||||||
|
</RegenerateButton>
|
||||||
|
</RightContent>
|
||||||
|
{availability && availability.suggestion && isSuggestionOpen && (
|
||||||
|
<FormattedMessage id={`${pluginId}.components.uid.suggested`}>
|
||||||
|
{msg => (
|
||||||
|
<Options
|
||||||
|
title={msg}
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
id: 'suggestion',
|
||||||
|
label: availability.suggestion,
|
||||||
|
onClick: handleSuggestionClick,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</FormattedMessage>
|
||||||
|
)}
|
||||||
|
</InputContainer>
|
||||||
|
{hasError && <ErrorMessage>{error}</ErrorMessage>}
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Error>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
InputUID.propTypes = {
|
||||||
|
attribute: PropTypes.object.isRequired,
|
||||||
|
contentTypeUID: PropTypes.string.isRequired,
|
||||||
|
error: PropTypes.string,
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
|
required: PropTypes.bool,
|
||||||
|
validations: PropTypes.object,
|
||||||
|
value: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
InputUID.defaultProps = {
|
||||||
|
error: null,
|
||||||
|
required: false,
|
||||||
|
validations: {},
|
||||||
|
value: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InputUID;
|
@ -9,6 +9,7 @@ import InputJSONWithErrors from '../InputJSONWithErrors';
|
|||||||
import InputFileWithErrors from '../InputFileWithErrors';
|
import InputFileWithErrors from '../InputFileWithErrors';
|
||||||
import SelectWrapper from '../SelectWrapper';
|
import SelectWrapper from '../SelectWrapper';
|
||||||
import WysiwygWithErrors from '../WysiwygWithErrors';
|
import WysiwygWithErrors from '../WysiwygWithErrors';
|
||||||
|
import InputUID from '../InputUID';
|
||||||
|
|
||||||
const getInputType = (type = '') => {
|
const getInputType = (type = '') => {
|
||||||
switch (toLower(type)) {
|
switch (toLower(type)) {
|
||||||
@ -42,32 +43,19 @@ const getInputType = (type = '') => {
|
|||||||
case 'WYSIWYG':
|
case 'WYSIWYG':
|
||||||
case 'richtext':
|
case 'richtext':
|
||||||
return 'wysiwyg';
|
return 'wysiwyg';
|
||||||
|
case 'uid':
|
||||||
|
return 'uid';
|
||||||
default:
|
default:
|
||||||
return 'text';
|
return 'text';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function Inputs({ autoFocus, keys, layout, name, onBlur }) {
|
function Inputs({ autoFocus, keys, layout, name, onBlur }) {
|
||||||
const {
|
const { didCheckErrors, formErrors, modifiedData, onChange } = useDataManager();
|
||||||
didCheckErrors,
|
const attribute = useMemo(() => get(layout, ['schema', 'attributes', name], {}), [layout, name]);
|
||||||
formErrors,
|
const metadatas = useMemo(() => get(layout, ['metadatas', name, 'edit'], {}), [layout, name]);
|
||||||
modifiedData,
|
const disabled = useMemo(() => !get(metadatas, 'editable', true), [metadatas]);
|
||||||
onChange,
|
|
||||||
} = useDataManager();
|
|
||||||
|
|
||||||
const attribute = useMemo(
|
|
||||||
() => get(layout, ['schema', 'attributes', name], {}),
|
|
||||||
[layout, name]
|
|
||||||
);
|
|
||||||
const metadatas = useMemo(
|
|
||||||
() => get(layout, ['metadatas', name, 'edit'], {}),
|
|
||||||
[layout, name]
|
|
||||||
);
|
|
||||||
const disabled = useMemo(() => !get(metadatas, 'editable', true), [
|
|
||||||
metadatas,
|
|
||||||
]);
|
|
||||||
const type = useMemo(() => get(attribute, 'type', null), [attribute]);
|
const type = useMemo(() => get(attribute, 'type', null), [attribute]);
|
||||||
|
|
||||||
const validations = omit(attribute, [
|
const validations = omit(attribute, [
|
||||||
'type',
|
'type',
|
||||||
'model',
|
'model',
|
||||||
@ -83,13 +71,13 @@ function Inputs({ autoFocus, keys, layout, name, onBlur }) {
|
|||||||
if (visible === false) {
|
if (visible === false) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const temporaryErrorIdUntilBuffetjsSupportsFormattedMessage =
|
const temporaryErrorIdUntilBuffetjsSupportsFormattedMessage = 'app.utils.defaultMessage';
|
||||||
'app.utils.defaultMessage';
|
|
||||||
const errorId = get(
|
const errorId = get(
|
||||||
formErrors,
|
formErrors,
|
||||||
[keys, 'id'],
|
[keys, 'id'],
|
||||||
temporaryErrorIdUntilBuffetjsSupportsFormattedMessage
|
temporaryErrorIdUntilBuffetjsSupportsFormattedMessage
|
||||||
);
|
);
|
||||||
|
const isRequired = get(validations, ['required'], false);
|
||||||
|
|
||||||
if (type === 'relation') {
|
if (type === 'relation') {
|
||||||
return (
|
return (
|
||||||
@ -131,12 +119,8 @@ function Inputs({ autoFocus, keys, layout, name, onBlur }) {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const isRequired = get(validations, ['required'], false);
|
|
||||||
const enumOptions = [
|
const enumOptions = [
|
||||||
<FormattedMessage
|
<FormattedMessage id="components.InputSelect.option.placeholder" key="__enum_option_null">
|
||||||
id="components.InputSelect.option.placeholder"
|
|
||||||
key="__enum_option_null"
|
|
||||||
>
|
|
||||||
{msg => (
|
{msg => (
|
||||||
<option disabled={isRequired} hidden={isRequired} value="">
|
<option disabled={isRequired} hidden={isRequired} value="">
|
||||||
{msg}
|
{msg}
|
||||||
@ -156,19 +140,21 @@ function Inputs({ autoFocus, keys, layout, name, onBlur }) {
|
|||||||
didCheckErrors={didCheckErrors}
|
didCheckErrors={didCheckErrors}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
error={
|
error={
|
||||||
isEmpty(error) ||
|
isEmpty(error) || errorId === temporaryErrorIdUntilBuffetjsSupportsFormattedMessage
|
||||||
errorId === temporaryErrorIdUntilBuffetjsSupportsFormattedMessage
|
|
||||||
? null
|
? null
|
||||||
: error
|
: error
|
||||||
}
|
}
|
||||||
inputDescription={description}
|
inputDescription={description}
|
||||||
description={description}
|
description={description}
|
||||||
|
contentTypeUID={layout.uid}
|
||||||
customInputs={{
|
customInputs={{
|
||||||
media: InputFileWithErrors,
|
media: InputFileWithErrors,
|
||||||
json: InputJSONWithErrors,
|
json: InputJSONWithErrors,
|
||||||
wysiwyg: WysiwygWithErrors,
|
wysiwyg: WysiwygWithErrors,
|
||||||
|
uid: InputUID,
|
||||||
}}
|
}}
|
||||||
multiple={get(attribute, 'multiple', false)}
|
multiple={get(attribute, 'multiple', false)}
|
||||||
|
attribute={attribute}
|
||||||
name={keys}
|
name={keys}
|
||||||
onBlur={onBlur}
|
onBlur={onBlur}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
|
@ -1,10 +1,4 @@
|
|||||||
import React, {
|
import React, { useCallback, useEffect, useMemo, useReducer, useState } from 'react';
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useReducer,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { cloneDeep, get, set } from 'lodash';
|
import { cloneDeep, get, set } from 'lodash';
|
||||||
@ -74,9 +68,9 @@ const EditSettingsView = ({
|
|||||||
}, [modifiedData]);
|
}, [modifiedData]);
|
||||||
|
|
||||||
const getForm = () =>
|
const getForm = () =>
|
||||||
Object.keys(
|
Object.keys(get(modifiedData, ['metadatas', metaToEdit, 'edit'], {})).filter(
|
||||||
get(modifiedData, ['metadatas', metaToEdit, 'edit'], {})
|
meta => meta !== 'visible'
|
||||||
).filter(meta => meta !== 'visible');
|
);
|
||||||
|
|
||||||
const getRelationsLayout = useCallback(() => {
|
const getRelationsLayout = useCallback(() => {
|
||||||
return get(modifiedData, ['layouts', 'editRelations'], []);
|
return get(modifiedData, ['layouts', 'editRelations'], []);
|
||||||
@ -94,10 +88,7 @@ const EditSettingsView = ({
|
|||||||
const getEditRemainingFields = () => {
|
const getEditRemainingFields = () => {
|
||||||
const attributes = getAttributes;
|
const attributes = getAttributes;
|
||||||
const metadatas = get(modifiedData, ['metadatas'], {});
|
const metadatas = get(modifiedData, ['metadatas'], {});
|
||||||
const displayedFields = getEditLayout().reduce(
|
const displayedFields = getEditLayout().reduce((acc, curr) => [...acc, ...curr.rowContent], []);
|
||||||
(acc, curr) => [...acc, ...curr.rowContent],
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
return Object.keys(attributes)
|
return Object.keys(attributes)
|
||||||
.filter(attr => get(attributes, [attr, 'type'], '') !== 'relation')
|
.filter(attr => get(attributes, [attr, 'type'], '') !== 'relation')
|
||||||
@ -114,11 +105,7 @@ const EditSettingsView = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const targetKey = formType === 'component' ? 'component' : 'targetModel';
|
const targetKey = formType === 'component' ? 'component' : 'targetModel';
|
||||||
const key = get(
|
const key = get(modifiedData, ['schema', 'attributes', metaToEdit, targetKey], '');
|
||||||
modifiedData,
|
|
||||||
['schema', 'attributes', metaToEdit, targetKey],
|
|
||||||
''
|
|
||||||
);
|
|
||||||
|
|
||||||
return get(componentsAndModelsMainPossibleMainFields, [key], []);
|
return get(componentsAndModelsMainPossibleMainFields, [key], []);
|
||||||
},
|
},
|
||||||
@ -129,13 +116,10 @@ const EditSettingsView = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const getData = async () => {
|
const getData = async () => {
|
||||||
try {
|
try {
|
||||||
const { data } = await request(
|
const { data } = await request(getRequestUrl(`${type}/${slug || componentSlug}`), {
|
||||||
getRequestUrl(`${type}/${slug || componentSlug}`),
|
|
||||||
{
|
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
signal,
|
signal,
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'GET_DATA_SUCCEEDED',
|
type: 'GET_DATA_SUCCEEDED',
|
||||||
@ -176,6 +160,7 @@ const EditSettingsView = ({
|
|||||||
const handleConfirm = async () => {
|
const handleConfirm = async () => {
|
||||||
try {
|
try {
|
||||||
const body = cloneDeep(modifiedData);
|
const body = cloneDeep(modifiedData);
|
||||||
|
|
||||||
// We need to send the unformated edit layout
|
// We need to send the unformated edit layout
|
||||||
set(body, 'layouts.edit', unformatLayout(body.layouts.edit));
|
set(body, 'layouts.edit', unformatLayout(body.layouts.edit));
|
||||||
|
|
||||||
@ -183,6 +168,7 @@ const EditSettingsView = ({
|
|||||||
delete body.uid;
|
delete body.uid;
|
||||||
delete body.isComponent;
|
delete body.isComponent;
|
||||||
delete body.category;
|
delete body.category;
|
||||||
|
delete body.apiID;
|
||||||
|
|
||||||
await request(getRequestUrl(`${type}/${slug || componentSlug}`), {
|
await request(getRequestUrl(`${type}/${slug || componentSlug}`), {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
@ -252,24 +238,15 @@ const EditSettingsView = ({
|
|||||||
getForm().map((meta, index) => {
|
getForm().map((meta, index) => {
|
||||||
const formType = get(getAttributes, [metaToEdit, 'type']);
|
const formType = get(getAttributes, [metaToEdit, 'type']);
|
||||||
|
|
||||||
if (
|
if (formType === 'dynamiczone' && !['label', 'description'].includes(meta)) {
|
||||||
formType === 'dynamiczone' &&
|
|
||||||
!['label', 'description'].includes(meta)
|
|
||||||
) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if ((formType === 'component' || formType === 'media') && meta !== 'label') {
|
||||||
(formType === 'component' || formType === 'media') &&
|
|
||||||
meta !== 'label'
|
|
||||||
) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if ((formType === 'json' || formType === 'boolean') && meta === 'placeholder') {
|
||||||
(formType === 'json' || formType === 'boolean') &&
|
|
||||||
meta === 'placeholder'
|
|
||||||
) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,11 +261,7 @@ const EditSettingsView = ({
|
|||||||
>
|
>
|
||||||
{description => (
|
{description => (
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id={get(
|
id={get(getInputProps(meta), 'label.id', 'app.utils.defaultMessage')}
|
||||||
getInputProps(meta),
|
|
||||||
'label.id',
|
|
||||||
'app.utils.defaultMessage'
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
{label => (
|
{label => (
|
||||||
<Input
|
<Input
|
||||||
|
@ -32,7 +32,7 @@ const Header = () => {
|
|||||||
|
|
||||||
const currentContentTypeMainField = get(layout, ['settings', 'mainField'], 'id');
|
const currentContentTypeMainField = get(layout, ['settings', 'mainField'], 'id');
|
||||||
const currentContentTypeName = get(layout, ['schema', 'info', 'name']);
|
const currentContentTypeName = get(layout, ['schema', 'info', 'name']);
|
||||||
const isCreatingEntry = id === 'create';
|
const isCreatingEntry = id === 'create' || (isSingleType && !initialData.created_at);
|
||||||
|
|
||||||
/* eslint-disable indent */
|
/* eslint-disable indent */
|
||||||
const entryHeaderTitle = isCreatingEntry
|
const entryHeaderTitle = isCreatingEntry
|
||||||
|
@ -13,11 +13,7 @@ import * as yup from 'yup';
|
|||||||
import { translatedErrors as errorsTrads } from 'strapi-helper-plugin';
|
import { translatedErrors as errorsTrads } from 'strapi-helper-plugin';
|
||||||
|
|
||||||
yup.addMethod(yup.mixed, 'defined', function() {
|
yup.addMethod(yup.mixed, 'defined', function() {
|
||||||
return this.test(
|
return this.test('defined', errorsTrads.required, value => value !== undefined);
|
||||||
'defined',
|
|
||||||
errorsTrads.required,
|
|
||||||
value => value !== undefined
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
yup.addMethod(yup.array, 'notEmptyMin', function(min) {
|
yup.addMethod(yup.array, 'notEmptyMin', function(min) {
|
||||||
@ -89,12 +85,9 @@ const createYupSchema = (model, { components }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (attribute.type === 'component') {
|
if (attribute.type === 'component') {
|
||||||
const componentFieldSchema = createYupSchema(
|
const componentFieldSchema = createYupSchema(components[attribute.component], {
|
||||||
components[attribute.component],
|
|
||||||
{
|
|
||||||
components,
|
components,
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
if (attribute.repeatable === true) {
|
if (attribute.repeatable === true) {
|
||||||
const { min, max, required } = attribute;
|
const { min, max, required } = attribute;
|
||||||
@ -129,9 +122,7 @@ const createYupSchema = (model, { components }) => {
|
|||||||
: componentFieldSchema.nullable();
|
: componentFieldSchema.nullable();
|
||||||
}
|
}
|
||||||
|
|
||||||
return attribute.required === true
|
return attribute.required === true ? yup.object().defined() : yup.object().nullable();
|
||||||
? yup.object().defined()
|
|
||||||
: yup.object().nullable();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
acc[current] = componentSchema;
|
acc[current] = componentSchema;
|
||||||
@ -178,11 +169,7 @@ const createYupSchema = (model, { components }) => {
|
|||||||
const createYupSchemaAttribute = (type, validations) => {
|
const createYupSchemaAttribute = (type, validations) => {
|
||||||
let schema = yup.mixed();
|
let schema = yup.mixed();
|
||||||
|
|
||||||
if (
|
if (['string', 'uid', 'text', 'richtext', 'email', 'password', 'enumeration'].includes(type)) {
|
||||||
['string', 'text', 'richtext', 'email', 'password', 'enumeration'].includes(
|
|
||||||
type
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
schema = yup.string();
|
schema = yup.string();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,12 +181,7 @@ const createYupSchemaAttribute = (type, validations) => {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (isNumber(value) || isNull(value) || isObject(value) || isArray(value)) {
|
||||||
isNumber(value) ||
|
|
||||||
isNull(value) ||
|
|
||||||
isObject(value) ||
|
|
||||||
isArray(value)
|
|
||||||
) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,9 +220,8 @@ const createYupSchemaAttribute = (type, validations) => {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
!!validationValue ||
|
!!validationValue ||
|
||||||
((!isBoolean(validationValue) &&
|
(!isBoolean(validationValue) && Number.isInteger(Math.floor(validationValue))) ||
|
||||||
Number.isInteger(Math.floor(validationValue))) ||
|
validationValue === 0
|
||||||
validationValue === 0)
|
|
||||||
) {
|
) {
|
||||||
switch (validation) {
|
switch (validation) {
|
||||||
case 'required':
|
case 'required':
|
||||||
@ -282,16 +263,12 @@ const createYupSchemaAttribute = (type, validations) => {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'positive':
|
case 'positive':
|
||||||
if (
|
if (['number', 'integer', 'bigint', 'float', 'decimal'].includes(type)) {
|
||||||
['number', 'integer', 'bigint', 'float', 'decimal'].includes(type)
|
|
||||||
) {
|
|
||||||
schema = schema.positive();
|
schema = schema.positive();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'negative':
|
case 'negative':
|
||||||
if (
|
if (['number', 'integer', 'bigint', 'float', 'decimal'].includes(type)) {
|
||||||
['number', 'integer', 'bigint', 'float', 'decimal'].includes(type)
|
|
||||||
) {
|
|
||||||
schema = schema.negative();
|
schema = schema.negative();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -50,6 +50,12 @@
|
|||||||
"components.TableEmpty.withSearch": "There is no {contentType} corresponding to the search ({search})...",
|
"components.TableEmpty.withSearch": "There is no {contentType} corresponding to the search ({search})...",
|
||||||
"components.TableEmpty.withoutFilter": "There is no {contentType}...",
|
"components.TableEmpty.withoutFilter": "There is no {contentType}...",
|
||||||
|
|
||||||
|
"components.uid.available": "available",
|
||||||
|
"components.uid.apply": "apply",
|
||||||
|
"components.uid.regenerate": "regenerate",
|
||||||
|
"components.uid.suggested": "suggested",
|
||||||
|
"components.uid.unavailable": "unavailable",
|
||||||
|
|
||||||
"containers.Edit.addAnItem": "Add an item...",
|
"containers.Edit.addAnItem": "Add an item...",
|
||||||
"containers.Edit.pluginHeader.title.new": "Create an entry",
|
"containers.Edit.pluginHeader.title.new": "Create an entry",
|
||||||
"containers.Edit.clickToJump": "Click to jump to the entry",
|
"containers.Edit.clickToJump": "Click to jump to the entry",
|
||||||
|
@ -47,6 +47,12 @@
|
|||||||
"components.TableEmpty.withSearch": "Aucun {contentType} n'a été trouvé avec cette recherche ({search})...",
|
"components.TableEmpty.withSearch": "Aucun {contentType} n'a été trouvé avec cette recherche ({search})...",
|
||||||
"components.TableEmpty.withoutFilter": "Aucun {contentType} n'a été trouvé...",
|
"components.TableEmpty.withoutFilter": "Aucun {contentType} n'a été trouvé...",
|
||||||
|
|
||||||
|
"components.uid.available": "disponible",
|
||||||
|
"components.uid.apply": "appliquer",
|
||||||
|
"components.uid.regenerate": "regénérer",
|
||||||
|
"components.uid.unavailable": "indisponible",
|
||||||
|
"components.uid.suggested": "suggéré",
|
||||||
|
|
||||||
"containers.Edit.addAnItem": "Ajouter un élément...",
|
"containers.Edit.addAnItem": "Ajouter un élément...",
|
||||||
"containers.Edit.pluginHeader.title.new": "Créer un document",
|
"containers.Edit.pluginHeader.title.new": "Créer un document",
|
||||||
"containers.Edit.clickToJump": "Cliquer pour voir l'entrée",
|
"containers.Edit.clickToJump": "Cliquer pour voir l'entrée",
|
||||||
|
@ -704,7 +704,7 @@ const forms = {
|
|||||||
|
|
||||||
if (type === 'uid') {
|
if (type === 'uid') {
|
||||||
const options = Object.keys(attributes)
|
const options = Object.keys(attributes)
|
||||||
.filter(key => attributes[key].type === 'string')
|
.filter(key => ['string', 'text'].includes(attributes[key].type))
|
||||||
.map(key => ({ id: key, value: key }));
|
.map(key => ({ id: key, value: key }));
|
||||||
|
|
||||||
items[0].push({
|
items[0].push({
|
||||||
|
@ -99,12 +99,10 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async getEnvironments(ctx) {
|
async getEnvironments(ctx) {
|
||||||
const environments = Object.keys(strapi.config.environments).map(
|
const environments = Object.keys(strapi.config.environments).map(environment => ({
|
||||||
environment => ({
|
|
||||||
name: environment,
|
name: environment,
|
||||||
active: strapi.config.environment === environment,
|
active: strapi.config.environment === environment,
|
||||||
})
|
}));
|
||||||
);
|
|
||||||
|
|
||||||
ctx.send({ environments });
|
ctx.send({ environments });
|
||||||
},
|
},
|
||||||
@ -143,18 +141,14 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async find(ctx) {
|
async find(ctx) {
|
||||||
const data = await strapi.plugins['upload'].services.upload.fetchAll(
|
const data = await strapi.plugins['upload'].services.upload.fetchAll(ctx.query);
|
||||||
ctx.query
|
|
||||||
);
|
|
||||||
|
|
||||||
// Send 200 `ok`
|
// Send 200 `ok`
|
||||||
ctx.send(data);
|
ctx.send(data);
|
||||||
},
|
},
|
||||||
|
|
||||||
async findOne(ctx) {
|
async findOne(ctx) {
|
||||||
const data = await strapi.plugins['upload'].services.upload.fetch(
|
const data = await strapi.plugins['upload'].services.upload.fetch(ctx.params);
|
||||||
ctx.params
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return ctx.notFound('file.notFound');
|
return ctx.notFound('file.notFound');
|
||||||
@ -164,9 +158,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async count(ctx) {
|
async count(ctx) {
|
||||||
const data = await strapi.plugins['upload'].services.upload.count(
|
const data = await strapi.plugins['upload'].services.upload.count(ctx.query);
|
||||||
ctx.query
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx.send({ count: data });
|
ctx.send({ count: data });
|
||||||
},
|
},
|
||||||
@ -208,9 +200,9 @@ const searchQueries = {
|
|||||||
return ({ id }) => {
|
return ({ id }) => {
|
||||||
return model
|
return model
|
||||||
.query(qb => {
|
.query(qb => {
|
||||||
qb.whereRaw('LOWER(hash) LIKE ?', [
|
qb.whereRaw('LOWER(hash) LIKE ?', [`%${id}%`]).orWhereRaw('LOWER(name) LIKE ?', [
|
||||||
`%${id}%`,
|
`%${id}%`,
|
||||||
]).orWhereRaw('LOWER(name) LIKE ?', [`%${id}%`]);
|
]);
|
||||||
})
|
})
|
||||||
.fetchAll()
|
.fetchAll()
|
||||||
.then(results => results.toJSON());
|
.then(results => results.toJSON());
|
||||||
|
@ -33,9 +33,7 @@ module.exports = {
|
|||||||
|
|
||||||
const createBuffer = async stream => {
|
const createBuffer = async stream => {
|
||||||
const parts = await toArray(fs.createReadStream(stream.path));
|
const parts = await toArray(fs.createReadStream(stream.path));
|
||||||
const buffers = parts.map(part =>
|
const buffers = parts.map(part => (_.isBuffer(part) ? part : Buffer.from(part)));
|
||||||
_.isBuffer(part) ? part : Buffer.from(part)
|
|
||||||
);
|
|
||||||
|
|
||||||
const buffer = Buffer.concat(buffers);
|
const buffer = Buffer.concat(buffers);
|
||||||
|
|
||||||
@ -44,10 +42,7 @@ module.exports = {
|
|||||||
name: stream.name,
|
name: stream.name,
|
||||||
sha256: niceHash(buffer),
|
sha256: niceHash(buffer),
|
||||||
hash: uuid().replace(/-/g, ''),
|
hash: uuid().replace(/-/g, ''),
|
||||||
ext:
|
ext: stream.name.split('.').length > 1 ? `.${_.last(stream.name.split('.'))}` : '',
|
||||||
stream.name.split('.').length > 1
|
|
||||||
? `.${_.last(stream.name.split('.'))}`
|
|
||||||
: '',
|
|
||||||
buffer,
|
buffer,
|
||||||
mime: stream.type,
|
mime: stream.type,
|
||||||
size: (stream.size / 1000).toFixed(2),
|
size: (stream.size / 1000).toFixed(2),
|
||||||
|
12
yarn.lock
12
yarn.lock
@ -3322,9 +3322,9 @@ acorn-globals@^4.1.0:
|
|||||||
acorn-walk "^6.0.1"
|
acorn-walk "^6.0.1"
|
||||||
|
|
||||||
acorn-jsx@^5.1.0:
|
acorn-jsx@^5.1.0:
|
||||||
version "5.1.0"
|
version "5.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384"
|
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe"
|
||||||
integrity sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==
|
integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==
|
||||||
|
|
||||||
acorn-walk@^6.0.1:
|
acorn-walk@^6.0.1:
|
||||||
version "6.2.0"
|
version "6.2.0"
|
||||||
@ -15555,9 +15555,9 @@ rsvp@^4.8.4:
|
|||||||
integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==
|
integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==
|
||||||
|
|
||||||
run-async@^2.2.0:
|
run-async@^2.2.0:
|
||||||
version "2.3.0"
|
version "2.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
|
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.0.tgz#e59054a5b86876cfae07f431d18cbaddc594f1e8"
|
||||||
integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA=
|
integrity sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==
|
||||||
dependencies:
|
dependencies:
|
||||||
is-promise "^2.1.0"
|
is-promise "^2.1.0"
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user