diff --git a/src/assets/css/draggable.css b/src/assets/css/draggable.css index 35982e8fb..6287b07c5 100644 --- a/src/assets/css/draggable.css +++ b/src/assets/css/draggable.css @@ -93,3 +93,18 @@ height: 100%; float: left; } + +.draggable-icon-remove { + position: absolute; + top: 3px; + right: 3px; +} + +.dnd-widget { + position: relative; +} + +.dnd-placeholder { + width: 100%; + background-color: $brand-primary-light-color; +} diff --git a/src/assets/css/indicator.css b/src/assets/css/indicator.css index cf29162a6..e0d67f81a 100644 --- a/src/assets/css/indicator.css +++ b/src/assets/css/indicator.css @@ -2,15 +2,22 @@ border-radius: 3px; overflow: hidden; margin: .5rem; +} + +.indicators-wrapper { display: flex; + flex-direction: row; +} + +.indicators-wrapper > * { + flex:1; } .indicator { - width: 25%; border: 1px solid $brand-border-color; border-right: none; - flex-direction: column; display: flex; + flex-direction: column; align-items: center; height: 130px; padding: 10px; diff --git a/src/assets/css/styles.css b/src/assets/css/styles.css index 01fa11aec..1a48774e7 100644 --- a/src/assets/css/styles.css +++ b/src/assets/css/styles.css @@ -892,6 +892,10 @@ td.pulse-off input { padding-top: 10px; } +.dashboard-edit-mode { + width: 75%; +} + .dashboard-edit-bar { margin: 0 .5rem; } diff --git a/src/components/charts/RawChart.js b/src/components/charts/RawChart.js index cd20154c1..0fd231538 100644 --- a/src/components/charts/RawChart.js +++ b/src/components/charts/RawChart.js @@ -22,6 +22,26 @@ class RawChart extends Component { } } + componentDidUpdate(prevProps) { + if( + prevProps.isMaximize !== this.props.isMaximize || + prevProps.index !== this.props.index || + prevProps.id !== this.props.id + ) { + if(this.props.chartType === 'Indicator'){ + this.fetchData(); + }else{ + this.setState({ + forceChartReRender: true, + }, () => { + this.setState({ + forceChartReRender: false + }) + }) + } + } + } + componentDidMount(){ const { pollInterval } = this.props; @@ -85,10 +105,10 @@ class RawChart extends Component { renderChart() { const { - id, chartType, caption, fields, groupBy, reRender, height, + id, chartType, caption, fields, groupBy, height, isMaximize, chartTitle } = this.props; - const {chartData} = this.state; + const {chartData, forceChartReRender} = this.state; const data = chartData[0] && chartData[0].values; switch(chartType){ @@ -96,10 +116,11 @@ class RawChart extends Component { return( { - this.setState({ - forceChartReRender: false - }) - }) - } - } - handleClickOutside = () => { this.setState({ toggleWidgetMenu: false @@ -87,12 +32,7 @@ export class DraggableWidget extends Component { this.setState({ isMaximize: true, toggleWidgetMenu: false, - forceChartReRender: true, height: 500 - }, () => { - this.setState({ - forceChartReRender: false - }) }) } @@ -100,31 +40,24 @@ export class DraggableWidget extends Component { this.setState({ isMaximize: false, toggleWidgetMenu: false, - forceChartReRender: true, height: 400 - }, () => { - this.setState({ - forceChartReRender: false - }) }) } render() { const { - text, isDragging, connectDragSource, connectDropTarget, + text, hideWidgets, showWidgets, index, idMaximized, id, chartType, caption, fields, groupBy, pollInterval } = this.props; const { - toggleWidgetMenu, isMaximize, forceChartReRender, height + toggleWidgetMenu, isMaximize, height } = this.state; - return connectDragSource(connectDropTarget( + return (
- - )); + ); } } -DraggableWidget.propTypes = { - connectDragSource: PropTypes.func.isRequired, - connectDropTarget: PropTypes.func.isRequired, - index: PropTypes.number.isRequired, - isDragging: PropTypes.bool.isRequired, - id: PropTypes.any.isRequired, - text: PropTypes.string.isRequired, - moveCard: PropTypes.func.isRequired -}; - -DraggableWidget = - DragSource(ItemTypes.DRAGGABLE_CARD, cardSource, collect)( - DropTarget(ItemTypes.DRAGGABLE_CARD, cardTarget, connect)( - onClickOutside(DraggableWidget - ) - )); - -export default DraggableWidget; +export default ChartWidget; diff --git a/src/components/dashboard/DndWidget.js b/src/components/dashboard/DndWidget.js new file mode 100644 index 000000000..61119a305 --- /dev/null +++ b/src/components/dashboard/DndWidget.js @@ -0,0 +1,87 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import ItemTypes from '../../constants/ItemTypes'; +import { DragSource, DropTarget } from 'react-dnd'; + +const cardSource = { + beginDrag(props) { + return { + id: props.id, + index: props.index + }; + } +}; + +const cardTarget = { + hover(props, monitor) { + const dragIndex = monitor.getItem().index; + const hoverIndex = props.index; + + // Don't replace items with themselves + if (dragIndex === hoverIndex) { + return; + } + + // Time to actually perform the action + props.moveCard(props.entity, 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; + } +}; + +function collect(connect, monitor) { + return { + connectDragSource: connect.dragSource(), + isDragging: monitor.isDragging() + }; +} + +function connect(connect) { + return { + connectDropTarget: connect.dropTarget() + }; +} + +export class DndWidget extends Component { + constructor(props) { + super(props); + } + + render() { + const { + children, connectDragSource, connectDropTarget, isDragging, + className, transparent, removeCard, entity, id, placeholder + } = this.props; + + if(transparent) return
{children}
; + + return connectDragSource(connectDropTarget( +
+ {!placeholder && removeCard(entity, id)} + />} + {children} +
+ )); + } +} + +DndWidget = + DragSource(props => props.entity, cardSource, collect)( + DropTarget(props => props.entity, cardTarget, connect)( + DndWidget + ) + ); + +export default DndWidget; diff --git a/src/components/dashboard/DraggableWrapper.js b/src/components/dashboard/DraggableWrapper.js new file mode 100644 index 000000000..c3f704b1a --- /dev/null +++ b/src/components/dashboard/DraggableWrapper.js @@ -0,0 +1,210 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import {connect} from 'react-redux'; +import update from 'react/lib/update'; +import ChartWidget from './ChartWidget'; +import { DragDropContext } from 'react-dnd'; +import HTML5Backend from 'react-dnd-html5-backend'; +import logo from '../../assets/images/metasfresh_logo_green_thumb.png'; +import RawChart from '../charts/RawChart'; +import DndWidget from './DndWidget'; +import Sidenav from '../dashboard/Sidenav'; +import Indicator from '../charts/Indicator'; + +import { + getKPIsDashboard, + getTargetIndicatorsDashboard +} from '../../actions/AppActions'; + +export class DraggableWrapper extends Component { + constructor(props) { + super(props); + this.state = { + cards: [], + isVisible: true, + idMaximized: false, + indicators: [], + sidenav: null + }; + } + + componentDidMount = () => { + this.getDashboard(); + this.getIndicators(); + } + + getIndicators = () => { + getTargetIndicatorsDashboard().then(response => { + this.setState({ + indicators: response.data.items + }); + }); + } + + getDashboard = () => { + getKPIsDashboard().then(response => { + this.setState({ + cards: response.data.items + }); + }); + } + + moveCard = (entity, dragIndex, hoverIndex) => { + const draggedItem = this.state[entity][dragIndex]; + this.setState(prev => update(prev, { + [entity]: { + $splice: [ + [dragIndex, 1], + [hoverIndex, 0, draggedItem] + ] + } + })); + } + + removeCard = (entity, index) => { + this.setState(prev => update(prev, { + [entity]: { + $splice: [ + [index, 1] + ] + } + })); + } + + hideWidgets = (id) => { + this.setState({ + isVisible: false, + idMaximized: id + }) + } + + showWidgets = () => { + this.setState({ + isVisible: true, + idMaximized: false + }) + } + + renderIndicators = () => { + const {indicators, idMaximized, editmode} = this.state; + + if(!indicators.length && editmode) return ( +
+ + + +
+ ); + + if(!indicators.length) return false; + + return ( +
+ {indicators.map((indicator, id) => + + + + )} +
+ ) + } + + renderKpis = () => { + const {cards, idMaximized, editmode} = this.state; + + return ( +
+ {cards.length > 0 ? cards.map((item, id) => { + return ( + + + + ); + }) : +
+
+ +
+
} +
+ ) + } + + render() { + const {editmode} = this.state; + + return ( +
+
+
+ +
+ {this.renderIndicators()} + {this.renderKpis()} +
+ {editmode && + + } +
+ ); + } +} + +DraggableWrapper.propTypes = { + dispatch: PropTypes.func.isRequired +}; + +DraggableWrapper = connect()(DragDropContext(HTML5Backend)(DraggableWrapper)); + +export default DraggableWrapper; diff --git a/src/components/dashboard/Sidenav.js b/src/components/dashboard/Sidenav.js index 98617aac1..580372aca 100644 --- a/src/components/dashboard/Sidenav.js +++ b/src/components/dashboard/Sidenav.js @@ -1,6 +1,4 @@ import React, { Component } from 'react'; -import onClickOutside from 'react-onclickoutside'; -import InfiniteScroll from 'react-infinite-scroller'; import Loader from '../app/Loader'; import RawChart from '../charts/RawChart'; import Indicator from '../charts/Indicator'; @@ -21,21 +19,13 @@ class Sidenav extends Component { componentWillMount = () => { const {entity} = this.props; - getRequest('dashboard', entity, 'available').then(res => { + getRequest('dashboard', 'available').then(res => { this.setState({ view: res.data }) }) } - loadMore = (page) => { - } - - handleClickOutside = () => { - const {onClickOutside} = this.props; - onClickOutside(); - } - render() { const {view} = this.state; const {entity} = this.props; @@ -43,28 +33,24 @@ class Sidenav extends Component {
- } - hasMore={view.result && view.size >= view.result.length} - useWindow={false} - > -
- {entity === 'kpis' ? 'Add KPI widgets' : 'Add target indicators'} -
-
- {view && view.map((item, i) => - entity === 'kpis' ? - : - - )} -
-
+
+ Add widgets +
+
+ {view && view.map((item, i) => + item.chartType === 'kpis' ? + : + + )} +
); } } -export default onClickOutside(Sidenav); +export default Sidenav; diff --git a/src/components/widget/DraggableWrapper.js b/src/components/widget/DraggableWrapper.js deleted file mode 100644 index 113880fa9..000000000 --- a/src/components/widget/DraggableWrapper.js +++ /dev/null @@ -1,173 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import {connect} from 'react-redux'; -import update from 'react/lib/update'; -import DraggableWidget from './DraggableWidget'; -import { DragDropContext } from 'react-dnd'; -import HTML5Backend from 'react-dnd-html5-backend'; -import logo from '../../assets/images/metasfresh_logo_green_thumb.png'; -import RawChart from '../charts/RawChart'; -import Sidenav from '../dashboard/Sidenav'; - -import { - getKPIsDashboard, - getTargetIndicatorsDashboard -} from '../../actions/AppActions'; - -export class DraggableWrapper extends Component { - constructor(props) { - super(props); - this.moveCard = this.moveCard.bind(this); - this.state = { - cards: [], - isVisible: true, - idMaximized: false, - indicators: [], - sidenav: null - }; - } - - componentDidMount = () => { - this.getDashboard(); - this.getIndicators(); - } - - getIndicators = () => { - getTargetIndicatorsDashboard().then(response => { - this.setState(Object.assign({}, this.state, { - indicators: response.data.items - })); - }); - } - - getDashboard = () => { - getKPIsDashboard().then(response => { - this.setState(Object.assign({}, this.state, { - cards: response.data.items - })); - }); - } - - moveCard = (dragIndex, hoverIndex) => { - const { cards } = this.state; - const dragCard = cards[dragIndex]; - - this.setState(update(this.state, { - cards: { - $splice: [ - [dragIndex, 1], - [hoverIndex, 0, dragCard] - ] - } - }), () => { - // const changes = { - // 'jsonDashboardChanges': { - // 'dashboardItemIdsOrder': cards.map(item => item.id) - // } - // }; - // setUserDashboardWidgets(changes); - //TO DO: future implementation - }); - } - - hideWidgets = (id) => { - this.setState({ - isVisible: false, - idMaximized: id - }) - } - - showWidgets = () => { - this.setState({ - isVisible: true, - idMaximized: false - }) - } - - render() { - const { cards, idMaximized, indicators, sidenav} = this.state; - - return ( -
-
-
- -
- {indicators.length > 0 &&
- - {indicators.map((indicator, id) => - - )} -
} -
- -
- {cards.length > 0 ? - cards.map((item, id) => { - return ( - - ); - }): -
-
- -
-
- } -
- {sidenav && - - this.setState({sidenav: null})} - entity={sidenav} - /> - } -
- ); - } - } - - DraggableWrapper.propTypes = { - dispatch: PropTypes.func.isRequired - }; - - DraggableWrapper = connect()(DragDropContext(HTML5Backend)(DraggableWrapper)); - - export default DraggableWrapper; diff --git a/src/containers/Dashboard.js b/src/containers/Dashboard.js index dca457f71..ebf8102cd 100644 --- a/src/containers/Dashboard.js +++ b/src/containers/Dashboard.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import {connect} from 'react-redux'; import Container from '../components/Container'; -import DraggableWrapper from '../components/widget/DraggableWrapper'; +import DraggableWrapper from '../components/dashboard/DraggableWrapper'; export class Dashboard extends Component { constructor(props){ diff --git a/src/routes.js b/src/routes.js index 06175d60b..e0459e089 100644 --- a/src/routes.js +++ b/src/routes.js @@ -79,8 +79,6 @@ export const getRoutes = (store, auth) => { /> - -