mirror of
https://github.com/strapi/strapi.git
synced 2025-09-03 05:39:36 +00:00
Update design, behavior and add link for selectMany
This commit is contained in:
parent
c9800a6581
commit
d8e5d0403b
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="8px" height="8px" viewBox="0 0 8 8" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 49.3 (51167) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Icon grab</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Pages" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Content-Manager---Settings-view---Single" transform="translate(-335.000000, -698.000000)" fill="#B3B5B9">
|
||||
<g id="Container" transform="translate(261.000000, 84.000000)">
|
||||
<g id="Forms" transform="translate(3.000000, 77.000000)">
|
||||
<g id="Settings">
|
||||
<g id="Attributes" transform="translate(4.000000, 456.000000)">
|
||||
<g id="Order-attributes" transform="translate(0.000000, 23.000000)">
|
||||
<g id="Link" transform="translate(0.000000, 19.000000)">
|
||||
<g id="Icon-grab" transform="translate(67.000000, 39.000000)">
|
||||
<rect id="Rectangle-4" x="0" y="0" width="2" height="2"></rect>
|
||||
<rect id="Rectangle-4" x="3" y="0" width="2" height="2"></rect>
|
||||
<rect id="Rectangle-4" x="6" y="0" width="2" height="2"></rect>
|
||||
<rect id="Rectangle-4" x="0" y="3" width="2" height="2"></rect>
|
||||
<rect id="Rectangle-4" x="3" y="3" width="2" height="2"></rect>
|
||||
<rect id="Rectangle-4" x="6" y="3" width="2" height="2"></rect>
|
||||
<rect id="Rectangle-4" x="0" y="6" width="2" height="2"></rect>
|
||||
<rect id="Rectangle-4" x="3" y="6" width="2" height="2"></rect>
|
||||
<rect id="Rectangle-4" x="6" y="6" width="2" height="2"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 49.3 (51167) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Icon remove</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Pages" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Content-Manager---List-view" transform="translate(-279.000000, -165.000000)">
|
||||
<g id="Container" transform="translate(234.000000, 0.000000)">
|
||||
<g id="Add-filters" transform="translate(0.000000, 60.000000)">
|
||||
<g id="Icon-remove" transform="translate(45.000000, 105.000000)">
|
||||
<rect id="Rectangle-12" stroke="#E3E9F3" x="0.5" y="0.5" width="19" height="19" rx="9.5"></rect>
|
||||
<path d="M6,10 L14,10" id="Line-4" stroke="#007EFF" stroke-width="2" stroke-linecap="round"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import PropTypes from 'prop-types';
|
||||
import { get, map } from 'lodash';
|
||||
|
||||
@ -26,9 +25,6 @@ const filterRelationsUpload = (data) => Object.keys(data).reduce((acc, current)
|
||||
function EditRelations(props) {
|
||||
return (
|
||||
<div className={styles.editFormRelations}>
|
||||
<FormattedMessage id="content-manager.EditRelations.title">
|
||||
{(message) => <h3>{message}</h3>}
|
||||
</FormattedMessage>
|
||||
{map(filterRelationsUpload(props.schema.relations), (relation, key) => {
|
||||
if (relation.nature.toLowerCase().includes('morph') && relation[key]) return '';
|
||||
|
||||
@ -43,6 +39,7 @@ function EditRelations(props) {
|
||||
schema={props.schema}
|
||||
setRecordAttribute={props.changeData}
|
||||
location={props.location}
|
||||
onRedirect={props.onRedirect}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@ -59,6 +56,7 @@ EditRelations.propTypes = {
|
||||
changeData: PropTypes.func.isRequired,
|
||||
currentModelName: PropTypes.string.isRequired,
|
||||
location: PropTypes.object.isRequired,
|
||||
onRedirect: PropTypes.func.isRequired,
|
||||
record: PropTypes.object,
|
||||
schema: PropTypes.object,
|
||||
};
|
||||
|
@ -1,13 +1,3 @@
|
||||
.editFormRelations { /* stylelint-disable */
|
||||
h3{
|
||||
height: 41px;
|
||||
margin: 0 -25px 14px;
|
||||
padding: 0 25px;
|
||||
background: #F3F3F3;
|
||||
line-height: 41px;
|
||||
border-radius: 2px 2px 0 0;
|
||||
letter-spacing: 0.03rem;
|
||||
font-size: 1.3rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
padding: 19px 0 20px;
|
||||
}
|
||||
|
@ -6,48 +6,41 @@
|
||||
|
||||
import React from 'react';
|
||||
import Select from 'react-select';
|
||||
import {SortableContainer, SortableElement, SortableHandle, arrayMove} from 'react-sortable-hoc';
|
||||
|
||||
import { SortableContainer, SortableElement, arrayMove } from 'react-sortable-hoc';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { cloneDeep, isArray, isNull, isUndefined, get, findIndex, includes } from 'lodash';
|
||||
|
||||
import IcoContainer from 'components/IcoContainer';
|
||||
import Ico from 'components/Ico';
|
||||
|
||||
// Utils.
|
||||
import request from 'utils/request';
|
||||
import templateObject from 'utils/templateObject';
|
||||
|
||||
// CSS.
|
||||
import 'react-select/dist/react-select.css';
|
||||
|
||||
// Icons.
|
||||
import IconRemove from '../../assets/images/icon_remove.svg';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
const DragHandle = SortableHandle(() => <span><Ico icoType="bars" /></span>);
|
||||
|
||||
const SortableItem = SortableElement(({idx, onRemove, value}) => {
|
||||
const icons = [
|
||||
{
|
||||
icoType: 'trash',
|
||||
onClick: () => onRemove(idx),
|
||||
},
|
||||
];
|
||||
const SortableItem = SortableElement(({idx, onRemove, item, onClick}) => {
|
||||
return (
|
||||
<li className={styles.sortableListItem}>
|
||||
<span>
|
||||
<DragHandle />
|
||||
<span className="sortable-item--value">{value}</span>
|
||||
</span>
|
||||
<span className={styles.sortableListItemActions}>
|
||||
<IcoContainer icons={icons} />
|
||||
</span>
|
||||
<div>
|
||||
<div className={styles.dragHandle}><span></span></div>
|
||||
<span className="sortable-item--value" onClick={() => onClick(item)}>{item.label}</span>
|
||||
</div>
|
||||
<div className={styles.sortableListItemActions}>
|
||||
<img src={IconRemove} alt="Remove Icon" onClick={() => onRemove(idx)} />
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
});
|
||||
|
||||
const SortableList = SortableContainer(({items, onRemove}) => {
|
||||
const SortableList = SortableContainer(({items, onRemove, onClick}) => {
|
||||
return (
|
||||
<ul className={styles.sortableList}>
|
||||
{items.map((item, index) => (
|
||||
<SortableItem key={`item-${index}`} index={index} idx={index} value={item.label} onRemove={onRemove} />
|
||||
<SortableItem key={`item-${index}`} index={index} idx={index} item={item} onRemove={onRemove} onClick={onClick} />
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
@ -178,6 +171,15 @@ class SelectMany extends React.Component {
|
||||
this.props.setRecordAttribute({ target });
|
||||
}
|
||||
|
||||
// Redirect to the edit page
|
||||
handleClick = (item = {}) => {
|
||||
this.props.onRedirect({
|
||||
model: this.props.relation.collection || this.props.relation.model,
|
||||
id: item.value.id || item.value._id,
|
||||
source: this.props.relation.plugin,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const description = this.props.relation.description ? (
|
||||
<p>{this.props.relation.description}</p>
|
||||
@ -217,7 +219,8 @@ class SelectMany extends React.Component {
|
||||
}
|
||||
onSortEnd={this.handleSortEnd}
|
||||
onRemove={this.handleRemove}
|
||||
useDragHandle
|
||||
distance={1}
|
||||
onClick={this.handleClick}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@ -226,6 +229,7 @@ class SelectMany extends React.Component {
|
||||
}
|
||||
|
||||
SelectMany.propTypes = {
|
||||
onRedirect: PropTypes.func.isRequired,
|
||||
record: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]).isRequired,
|
||||
relation: PropTypes.object.isRequired,
|
||||
setRecordAttribute: PropTypes.func.isRequired,
|
||||
|
@ -7,7 +7,7 @@
|
||||
}
|
||||
|
||||
label + div{
|
||||
margin: 5px 0 26px;
|
||||
margin: 3px 0 26px;
|
||||
|
||||
&:focus{
|
||||
outline: none;
|
||||
@ -27,7 +27,7 @@
|
||||
}
|
||||
|
||||
.sortableList {
|
||||
|
||||
margin-top: -21px;
|
||||
padding-left: 0 !important;
|
||||
list-style: none !important;
|
||||
|
||||
@ -36,14 +36,105 @@
|
||||
.sortableListItem {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-content: center;
|
||||
justify-content: space-between;
|
||||
> span {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
height: 27px;
|
||||
|
||||
|
||||
&:hover{
|
||||
cursor: pointer;
|
||||
|
||||
}
|
||||
|
||||
&:active{
|
||||
.dragHandle{
|
||||
cursor: pointer;
|
||||
|
||||
> span {
|
||||
background: #AED4FB;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dragHandle{
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
margin-top: -1px;
|
||||
|
||||
> span {
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 6px;
|
||||
height: 1px;
|
||||
padding: 0px !important;
|
||||
background: #B3B5B9;
|
||||
overflow: visible !important;
|
||||
transition: background .25s ease-out;
|
||||
|
||||
&:before, &:after{
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 6px;
|
||||
height: 1px;
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
&:before{
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&:after{
|
||||
position: absolute;
|
||||
bottom: -2px;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
> div {
|
||||
span {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&:first-of-type{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: color .25s ease-out;
|
||||
|
||||
&:hover{
|
||||
.dragHandle{
|
||||
> span {
|
||||
background: #007EFF;
|
||||
}
|
||||
}
|
||||
|
||||
color: #007EFF;
|
||||
}
|
||||
|
||||
span {
|
||||
&:last-of-type{
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:last-of-type{
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
line-height: 27px;
|
||||
text-align: right;
|
||||
padding-right: 0px;
|
||||
|
||||
img{
|
||||
display: inline-block;
|
||||
height: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -32,6 +32,7 @@ import injectSaga from 'utils/injectSaga';
|
||||
import getQueryParameters from 'utils/getQueryParameters';
|
||||
import { bindLayout } from 'utils/bindLayout';
|
||||
import inputValidations from 'utils/inputsValidations';
|
||||
import { generateRedirectURI } from 'containers/ListPage/utils';
|
||||
|
||||
import { checkFormValidity } from 'utils/formValidations';
|
||||
|
||||
@ -54,35 +55,15 @@ import styles from './styles.scss';
|
||||
|
||||
export class EditPage extends React.Component {
|
||||
componentDidMount() {
|
||||
this.props.initModelProps(this.getModelName(), this.isCreating(), this.getSource(), this.getModelAttributes());
|
||||
|
||||
if (!this.isCreating()) {
|
||||
const mainField = get(this.getModel(), 'info.mainField') || this.getModel().primaryKey;
|
||||
this.props.getData(this.props.match.params.id, this.getSource(), mainField);
|
||||
} else {
|
||||
this.props.getLayout(this.getSource());
|
||||
}
|
||||
|
||||
// Get all relations made with the upload plugin
|
||||
const fileRelations = Object.keys(get(this.getSchema(), 'relations', {})).reduce((acc, current) => {
|
||||
const association = get(this.getSchema(), ['relations', current], {});
|
||||
|
||||
if (association.plugin === 'upload' && association[association.type] === 'file') {
|
||||
const relation = {
|
||||
name: current,
|
||||
multiple: association.nature === 'manyToManyMorph',
|
||||
};
|
||||
|
||||
acc.push(relation);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
// Update the reducer so we can use it to create the appropriate FormData in the saga
|
||||
this.props.setFileRelations(fileRelations);
|
||||
this.initComponent(this.props);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.location.pathname !== this.props.location.pathname) {
|
||||
this.props.resetProps();
|
||||
this.initComponent(this.props);
|
||||
}
|
||||
|
||||
if (prevProps.editPage.submitSuccess !== this.props.editPage.submitSuccess) {
|
||||
if (!isEmpty(this.props.location.search) && includes(this.props.location.search, '?redirectUrl')) {
|
||||
const redirectUrl = this.props.location.search.split('?redirectUrl=')[1];
|
||||
@ -165,6 +146,38 @@ export class EditPage extends React.Component {
|
||||
*/
|
||||
getSource = () => getQueryParameters(this.props.location.search, 'source');
|
||||
|
||||
/**
|
||||
* Initialize component
|
||||
*/
|
||||
initComponent = (props) => {
|
||||
this.props.initModelProps(this.getModelName(), this.isCreating(), this.getSource(), this.getModelAttributes());
|
||||
|
||||
if (!this.isCreating()) {
|
||||
const mainField = get(this.getModel(), 'info.mainField') || this.getModel().primaryKey;
|
||||
this.props.getData(props.match.params.id, this.getSource(), mainField);
|
||||
} else {
|
||||
this.props.getLayout(this.getSource());
|
||||
}
|
||||
|
||||
// Get all relations made with the upload plugin
|
||||
const fileRelations = Object.keys(get(this.getSchema(), 'relations', {})).reduce((acc, current) => {
|
||||
const association = get(this.getSchema(), ['relations', current], {});
|
||||
|
||||
if (association.plugin === 'upload' && association[association.type] === 'file') {
|
||||
const relation = {
|
||||
name: current,
|
||||
multiple: association.nature === 'manyToManyMorph',
|
||||
};
|
||||
|
||||
acc.push(relation);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
// Update the reducer so we can use it to create the appropriate FormData in the saga
|
||||
this.props.setFileRelations(fileRelations);
|
||||
}
|
||||
|
||||
handleBlur = ({ target }) => {
|
||||
const defaultValue = get(this.getModelAttribute(target.name), 'default');
|
||||
|
||||
@ -209,6 +222,15 @@ export class EditPage extends React.Component {
|
||||
this.props.changeData({ target });
|
||||
}
|
||||
|
||||
handleRedirect = ({ model, id, source = 'content-manager'}) => {
|
||||
const pathname = `${this.props.match.path.replace(':slug', model).replace(':id', id)}`;
|
||||
|
||||
this.props.history.push({
|
||||
pathname,
|
||||
search: `?source=${source}&redirectURI=${generateRedirectURI({ model, search: `?source=${source}` })}`,
|
||||
});
|
||||
}
|
||||
|
||||
handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
const formErrors = checkFormValidity(this.generateFormFromRecord(), this.props.editPage.formValidations);
|
||||
@ -267,7 +289,7 @@ export class EditPage extends React.Component {
|
||||
|
||||
render() {
|
||||
const { editPage } = this.props;
|
||||
|
||||
console.log(this.props);
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
@ -309,6 +331,7 @@ export class EditPage extends React.Component {
|
||||
changeData={this.props.changeData}
|
||||
record={editPage.record}
|
||||
schema={this.getSchema()}
|
||||
onRedirect={this.handleRedirect}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@ -62,6 +62,7 @@ import {
|
||||
generateFiltersFromSearch,
|
||||
generateSearchFromFilters,
|
||||
generateSearchFromParams,
|
||||
generateRedirectURI,
|
||||
} from './utils';
|
||||
|
||||
import styles from './styles.scss';
|
||||
@ -157,9 +158,7 @@ export class ListPage extends React.Component {
|
||||
* Generate the redirect URI when editing an entry
|
||||
* @type {String}
|
||||
*/
|
||||
generateRedirectURI = () => (
|
||||
`?redirectUrl=/plugins/content-manager/${this.getCurrentModelName().toLowerCase()}${this.generateSearch()}`
|
||||
);
|
||||
generateRedirectURI = generateRedirectURI.bind(this);
|
||||
|
||||
generateSearch = () => {
|
||||
const {
|
||||
|
@ -50,8 +50,17 @@ const generateSearchFromParams = params =>
|
||||
return acc;
|
||||
}, '');
|
||||
|
||||
/**
|
||||
* Generate the redirect URI when editing an entry
|
||||
* @type {String}
|
||||
*/
|
||||
const generateRedirectURI = function ({ model, search } = {}) {
|
||||
return `?redirectUrl=/plugins/content-manager/${(model || this.getCurrentModelName()).toLowerCase()}${(search || this.generateSearch())}`;
|
||||
};
|
||||
|
||||
export {
|
||||
generateFiltersFromSearch,
|
||||
generateSearchFromFilters,
|
||||
generateSearchFromParams,
|
||||
generateRedirectURI,
|
||||
};
|
||||
|
@ -24,7 +24,7 @@
|
||||
"devDependencies": {
|
||||
"react-select": "^1.0.0-rc.5",
|
||||
"react-sortable-hoc": "^0.8.3",
|
||||
"strapi-helper-plugin": "3.0.0-alpha.12.5"
|
||||
"strapi-helper-plugin": "3.0.0-alpha.12.6"
|
||||
},
|
||||
"author": {
|
||||
"name": "Strapi team",
|
||||
|
@ -1,6 +1,8 @@
|
||||
module.exports = async (ctx, next) => {
|
||||
let role;
|
||||
|
||||
console.log("ljqskljdkls");
|
||||
|
||||
if (ctx.request && ctx.request.header && ctx.request.header.authorization) {
|
||||
try {
|
||||
const { _id, id } = await strapi.plugins['users-permissions'].services.jwt.getToken(ctx);
|
||||
|
@ -14,6 +14,14 @@ module.exports = strapi => {
|
||||
},
|
||||
|
||||
initialize: function(cb) {
|
||||
console.log();
|
||||
_.forEach(strapi.admin.config.routes, value => {
|
||||
if (_.get(value.config, 'policies')) {
|
||||
value.config.policies.unshift('plugins.users-permissions.permissions');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
_.forEach(strapi.config.routes, value => {
|
||||
if (_.get(value.config, 'policies')) {
|
||||
value.config.policies.unshift('plugins.users-permissions.permissions');
|
||||
|
@ -38,6 +38,10 @@
|
||||
"via": "users",
|
||||
"plugin": "users-permissions",
|
||||
"configurable": false
|
||||
},
|
||||
"products": {
|
||||
"collection": "product",
|
||||
"via": "users"
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user