Commit 39c9d0de authored by Valeriy Panov's avatar Valeriy Panov Committed by Matija Obreza
Browse files

#238 Reordering descriptors in descriptor list

parent 268c1366
......@@ -153,6 +153,15 @@ export const addDescriptorsToDescriptorList = (descriptorList: DescriptorList, d
});
};
// Add a descriptor to the descriptor list
export const setDescriptorsToDescriptorList = (descriptorList: DescriptorList, descriptorUuids: string[]) => (dispatch, getState) => {
return DescriptorListService.setDescriptors(getState().login.access_token, descriptorList, descriptorUuids)
.then((descriptorList) => {
// receive updates
dispatch(receiveDescriptorList(descriptorList));
});
};
// Remove a descriptor to the descriptor list
export const removeDescriptorFromDescriptorList = (descriptorList: DescriptorList, descriptor: Descriptor) => (dispatch, getState) => {
return DescriptorListService.removeDescriptor(getState().login.access_token, descriptorList, [ descriptor.uuid ])
......
......@@ -26,6 +26,7 @@ export const CREATE_DESCRIPTORLIST_URL = `${DESCRIPTORLIST_API}/create`;
export const UPDATE_DESCRIPTORLIST_URL = `${DESCRIPTORLIST_API}/update`;
export const PUBLISH_DESCRIPTORLIST_URL = `${DESCRIPTORLIST_API}/publish`;
export const ADD_DESCRIPTORLIST_DESCRIPTOR_URL = `${DESCRIPTORLIST_API}/add-descriptors`;
export const SET_DESCRIPTORLIST_DESCRIPTORS_URL = `${DESCRIPTORLIST_API}/set-descriptors`;
export const REMOVE_DESCRIPTORLIST_DESCRIPTOR_URL = `${DESCRIPTORLIST_API}/remove-descriptors`;
// Descriptors API
......
......@@ -2,9 +2,11 @@ import authenticatedRequest from 'utilities/requestUtils';
import { dereferenceReferences } from 'utilities';
import {log} from 'utilities/debug';
import { MY_LIST_DESCRIPTORSLISTS_URL, GET_DESCRIPTORLIST_URL, REMOVE_DESCRIPTORLIST_URL,
CREATE_DESCRIPTORLIST_URL, UPDATE_DESCRIPTORLIST_URL, LIST_DESCRIPTORLISTS_URL, PUBLISH_DESCRIPTORLIST_URL,
ADD_DESCRIPTORLIST_DESCRIPTOR_URL, REMOVE_DESCRIPTORLIST_DESCRIPTOR_URL } from 'constants/apiURLS';
import {
MY_LIST_DESCRIPTORSLISTS_URL, GET_DESCRIPTORLIST_URL, REMOVE_DESCRIPTORLIST_URL,
CREATE_DESCRIPTORLIST_URL, UPDATE_DESCRIPTORLIST_URL, LIST_DESCRIPTORLISTS_URL, PUBLISH_DESCRIPTORLIST_URL,
ADD_DESCRIPTORLIST_DESCRIPTOR_URL, REMOVE_DESCRIPTORLIST_DESCRIPTOR_URL, SET_DESCRIPTORLIST_DESCRIPTORS_URL,
} from 'constants/apiURLS';
import { DescriptorList, IDescriptorListFilter } from 'model/descriptor.model';
import { Partner } from 'model/partner.model';
......@@ -121,6 +123,16 @@ export class DescriptorListService {
}).then(({ data }) => new DescriptorList(data));
}
public static setDescriptors(token: string, descriptorList: DescriptorList, descriptorUuids: string[]): Promise<DescriptorList> {
log('Set descriptors to descriptor list', descriptorUuids);
return authenticatedRequest(token, {
url: `${SET_DESCRIPTORLIST_DESCRIPTORS_URL}/${descriptorList.uuid},${descriptorList.version}`,
method: 'POST',
data: [ ...descriptorUuids ],
}).then(({ data }) => new DescriptorList(data));
}
public static removeDescriptor(token: string, descriptorList: DescriptorList, descriptorUuids: string[]): Promise<DescriptorList> {
log('Removing descriptor to descriptor list', descriptorUuids);
......
import * as React from 'react';
import {findDOMNode} from 'react-dom';
import {DragSource, DropTarget} from 'react-dnd';
import {
ListItem,
ListItemIcon,
ListItemText,
} from 'material-ui/List';
import ReorderIcon from 'material-ui-icons/Reorder';
import ItemTypes from './ItemTypes';
interface ICardProps extends React.ClassAttributes<any> {
connectDragSource: (func) => any;
connectDropTarget: (element) => any;
index: number;
isDragging: boolean;
id: any;
text: string;
moveCard: () => void;
}
const cardSource = {
beginDrag(props) {
return {
id: props.id,
index: props.index,
};
},
};
const cardTarget = {
hover(props, monitor, component) {
const dragIndex = monitor.getItem().index;
const hoverIndex = props.index;
// Don't replace items with themselves
if (dragIndex === hoverIndex) {
return;
}
// Determine rectangle on screen
const hoverBoundingRect = findDOMNode(component).getBoundingClientRect();
// Get vertical middle
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
// Determine mouse position
const clientOffset = monitor.getClientOffset();
// Get pixels to the top
const hoverClientY = clientOffset.y - hoverBoundingRect.top;
// Only perform the move when the mouse has crossed half of the items height
// When dragging downwards, only move when the cursor is below 50%
// When dragging upwards, only move when the cursor is above 50%
// Dragging downwards
if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
return;
}
// Dragging upwards
if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
return;
}
// Time to actually perform the action
props.moveCard(dragIndex, hoverIndex);
// Note: we're mutating the monitor item here!
// Generally it's better to avoid mutations,
// but it's good here for the sake of performance
// to avoid expensive index searches.
monitor.getItem().index = hoverIndex;
},
};
const Card = ({
text,
isDragging,
connectDragSource,
connectDropTarget,
}: ICardProps) => {
const opacity = isDragging ? 0 : 1;
return connectDragSource(
connectDropTarget(
<div>
<ListItem style={ {opacity} }>
<ListItemIcon>
<ReorderIcon/>
</ListItemIcon>
<ListItemText
primary={ text }
/>
</ListItem>
</div>,
),
);
};
const dropTargetWrap = DropTarget(ItemTypes.CARD, cardTarget, (connect) => ({
connectDropTarget: connect.dropTarget(),
}));
const dragSourceWrap = DragSource(ItemTypes.CARD, cardSource, (connect, monitor) => ({
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging(),
}));
export default dropTargetWrap(dragSourceWrap(Card));
import * as React from 'react';
import Paper from 'material-ui/Paper';
import Button from 'material-ui/Button';
import List from 'material-ui/List';
import { DragDropContext } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import * as update from 'immutability-helper';
import {Descriptor} from 'model/descriptor.model';
import Card from './Card';
interface IDescriptorOrderProps extends React.ClassAttributes<any> {
descriptors: Descriptor[];
onDone: (descriptors: Descriptor[]) => void;
onCancel?: () => void;
}
class DescriptorOrder extends React.PureComponent<IDescriptorOrderProps, any> {
constructor(props: IDescriptorOrderProps, context: any) {
super(props, context);
const { descriptors = [] } = props;
this.state = {
descriptors: [ ...descriptors ],
};
}
public componentWillReceiveProps(nextProps) {
const { descriptors = [] } = nextProps;
this.setState(() => ({ descriptors: [ ...descriptors ] }));
}
protected saveOrder = () => {
this.props.onDone(this.state.descriptors);
}
public moveCard = (dragIndex, hoverIndex) => {
const { descriptors } = this.state;
const dragCard = descriptors[dragIndex];
this.setState(
update(this.state, {
descriptors: {
$splice: [[dragIndex, 1], [hoverIndex, 0, dragCard]],
},
}),
);
}
public render() {
const {onCancel} = this.props;
const {descriptors} = this.state;
return (
<Paper className="p-20">
<List>
{ descriptors.map((descriptor, i) => (
<Card
key={ descriptor.id }
index={ i }
id={ descriptor.id }
text={ descriptor.title }
moveCard={ this.moveCard }
/>
)) }
</List>
<Button raised onClick={ this.saveOrder }>Save order</Button>
<Button onClick={ onCancel }>Cancel</Button>
</Paper>
);
}
}
export default DragDropContext(HTML5Backend)(DescriptorOrder);
export default {
CARD: 'card',
};
......@@ -12,7 +12,10 @@ import {listCrops} from 'actions/crop';
import {loadMyPartners} from 'actions/partner';
import {Partner} from 'model/partner.model';
import {loadDescriptors, importDescriptor} from 'actions/descriptors';
import {loadDescriptorList, saveDescriptorList, publishDescriptorList, addDescriptorsToDescriptorList, addDescriptorToDescriptorList, removeDescriptorFromDescriptorList} from 'actions/descriptorList';
import {
loadDescriptorList, saveDescriptorList, publishDescriptorList, addDescriptorsToDescriptorList,
addDescriptorToDescriptorList, removeDescriptorFromDescriptorList, setDescriptorsToDescriptorList,
} from 'actions/descriptorList';
import {DescriptorList, Descriptor, IDescriptorFilter} from 'model/descriptor.model';
import DescriptorListForm from './c/DescriptorListForm';
......@@ -25,6 +28,7 @@ import Paper from 'material-ui/Paper';
import Button from 'material-ui/Button';
import DescriptorUpload from 'ui/catalog/descriptor/DescriptorUpload';
import DescriptorListExtras from './c/Extras';
import DescriptorOrder from 'ui/common/reorderable/DescriptorOrder';
interface IDescriptorListEditPageProps extends React.ClassAttributes<any> {
classes: any;
......@@ -39,6 +43,7 @@ interface IDescriptorListEditPageProps extends React.ClassAttributes<any> {
addDescriptorsToDescriptorList: any;
addDescriptorToDescriptorList: any;
removeDescriptorFromDescriptorList: any;
setDescriptorsToDescriptorList: (descriptorList: DescriptorList, descriptorUuids: string[]) => any;
myPartners: Partner[];
loadMyPartners: any;
......@@ -62,7 +67,7 @@ class DescriptorListEditPage extends React.Component<IDescriptorListEditPageProp
public constructor(props: any) {
super(props);
this.state = { uploader: false, extras: false, csvDescriptors: '', uploadedDescriptors: null };
this.state = { uploader: false, extras: false, csvDescriptors: '', uploadedDescriptors: null, reorder: false };
}
public componentWillMount() {
......@@ -91,6 +96,16 @@ class DescriptorListEditPage extends React.Component<IDescriptorListEditPageProp
saveDescriptorList(descriptorList);
}
public onDone = (descriptors: Descriptor[]) => {
const {descriptorList, setDescriptorsToDescriptorList} = this.props;
console.log('Saving descriptors ', descriptors);
setDescriptorsToDescriptorList(descriptorList, descriptors.map((e) => e.uuid));
}
public onCancelReorder = () => {
this.setState({ ...this.state, reorder: ! this.state.reorder });
}
public onPublish = (e) => {
const {descriptorList, publishDescriptorList} = this.props;
......@@ -120,6 +135,10 @@ class DescriptorListEditPage extends React.Component<IDescriptorListEditPageProp
this.setState({ ...this.state, uploader: false, extras: ! this.state.extras });
}
public reorderDescriptors = () => {
this.setState({ ...this.state, reorder: ! this.state.reorder });
}
public importDescriptors = async (d: Descriptor[]) => {
const {descriptorList, importDescriptor, addDescriptorsToDescriptorList} = this.props;
const imported: string[] = [];
......@@ -167,6 +186,7 @@ class DescriptorListEditPage extends React.Component<IDescriptorListEditPageProp
<Link to="/dashboard/descriptorlists"><Button type="button">Back to dashboard</Button></Link>
{ descriptorList && descriptorList.uuid && <Button onClick={ this.addDescriptors }>{ this.state.uploader ? `No descriptors from Excel` : `Add descriptors from Excel` }</Button> }
{ descriptorList && descriptorList.uuid && <Button onClick={ this.editExtras }>{ this.state.extras ? `Cancel extras` : `Edit extras` }</Button> }
{ descriptorList && descriptorList.uuid && <Button onClick={ this.reorderDescriptors }>{ this.state.reorder ? `Cancel reorder` : `Edit reorder` }</Button> }
{ descriptorList && descriptorList.id > 0 && ! descriptorList.published && <Button type="button" onClick={ this.onPublish }>Approve and Publish</Button> }
</span>
) } />
......@@ -185,6 +205,12 @@ class DescriptorListEditPage extends React.Component<IDescriptorListEditPageProp
</Grid>
) }
{ this.state.reorder && descriptorList.uuid && (
<Grid item xs={ 12 } className="mb-20">
<DescriptorOrder descriptors={ descriptorList.descriptors } onDone={ this.onDone } onCancel={ this.onCancelReorder } />
</Grid>
) }
{ ! (this.state.uploader || this.state.extras) && descriptorList.uuid && (
<Grid item xs={ 12 } className="mb-20">
......@@ -232,6 +258,7 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({
removeDescriptorFromDescriptorList,
loadMyPartners,
listCrops,
setDescriptorsToDescriptorList,
}, dispatch);
export default connect(mapStateToProps, mapDispatchToProps)(DescriptorListEditPage);
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment