diff --git a/package.json b/package.json index f0ca223..4191c69 100644 --- a/package.json +++ b/package.json @@ -57,8 +57,9 @@ "react": "^15.5.4", "react-cookie": "^2.1.2", "react-data-components": "^1.1.1", + "react-dialog": "^1.0.2", "react-dom": "^15.5.4", - "react-draggable": "^3.0.5", + "react-draggable": "^2.2.6", "react-geosuggest": "^2.5.0", "react-geosuggest-sw": "^1.4.13", "react-google-maps": "^7.2.0", @@ -67,8 +68,10 @@ "react-imgix": "^7.1.1", "react-jquery-datatables": "^0.7.1", "react-materialui-notifications": "^0.5.1", + "react-onclickoutside": "^5.10.0", "react-places-autocomplete": "^5.4.2", "react-redux": "^5.0.5", + "react-resizable": "^1.7.1", "react-router": "^3.0.4", "react-router-redux": "^4.0.8", "react-tap-event-plugin": "^2.0.1", diff --git a/src/client.js b/src/client.js index 4cbe069..7487e93 100644 --- a/src/client.js +++ b/src/client.js @@ -8,10 +8,6 @@ import { Router, hashHistory, browserHistory } from 'react-router'; import { syncHistoryWithStore, routerMiddleware } from 'react-router-redux'; import reducers from './reducers'; import Instance from './components/Connection'; -import { - loggedUser, - visitReporter, -} from 'utils/authorization'; const middleware = routerMiddleware(hashHistory); const store = createStore( @@ -59,15 +55,7 @@ const rootRoute = { onChange: requireAuth, onEnter: requireAuth, component: require('./containers/App'), - indexRoute: { - onEnter: (nextState, replace) => { - if (loggedUser.anyOf(visitReporter)) { - replace('/app/form/visit/' + loggedUser.useruuid) - } else { - replace('/app/table/rides'); - } - } - }, + indexRoute: { onEnter: (nextState, replace) => replace('/app/table/rides') }, childRoutes: [ require('./routes/app'), require('./routes/404'), diff --git a/src/components/Header/NavRightList.js b/src/components/Header/NavRightList.js index 1e1f6c1..3caf7e9 100644 --- a/src/components/Header/NavRightList.js +++ b/src/components/Header/NavRightList.js @@ -6,11 +6,10 @@ import { hashHistory } from 'react-router'; import { loggedUser, - planScheduler, - providerScheduler, visitReporter, } from 'utils/authorization'; + const ImgIconButtonStyle = { width: '60px', height: '60px' @@ -35,7 +34,6 @@ class NavRightList extends React.Component { handleChange = (event, value) => { hashHistory.push(value); } - componentDidMount() { const user = JSON.parse(localStorage.getItem('loggedUser')); if (user) { @@ -48,8 +46,6 @@ class NavRightList extends React.Component { - ); - } - } - - module.exports = NavRightList; + ); + } +} + +module.exports = NavRightList; diff --git a/src/components/Shared/ValidationErrorsInfoDialog.js b/src/components/Shared/ValidationErrorsInfoDialog.js index c3ed35a..de1193d 100644 --- a/src/components/Shared/ValidationErrorsInfoDialog.js +++ b/src/components/Shared/ValidationErrorsInfoDialog.js @@ -1,68 +1,68 @@ import React, { Component } from 'react'; -import Dialog from 'material-ui/Dialog'; import FlatButton from 'material-ui/FlatButton'; +import Dialog from './draggable-dialog'; + +import './draggable-dialog/css/index.css'; + +const defaultDialogHeight = 100; //px +const dialogHeightPerLine = 25; //px export class ValidationErrorsInfoDialog extends React.Component { - constructor(props) { - super(props) - this.props = props; - this.state = { + + state = { open: this.props.open, - title: "Errors", + } + + componentWillReceiveProps(newProps){ + this.setState({open: newProps.open}); + } + + handleOpen = () => { + this.setState({ open: true }); + }; + + handleClose = () => { + this.setState({ open: false }); + this.props.onDismiss(); + }; + + render() { + + const actions = [ + , + ]; + + return ( +
+ { + this.state.open && + + {this.props.errorMessages.map(errorMessage=>{ + const oneValidationMessage = ({errorMessage.message}
); + const oneValidationMessageWithTab = (
  • {errorMessage.message}
  • ); + + if (errorMessage.field_name === "password-tab"){ + return oneValidationMessageWithTab; + } else{ + return oneValidationMessage; + } + })} +
    + } +
    + ); } } - componentWillReceiveProps(newProps) { - let title = this.state.title; - if (newProps.errorTitle) { - title = newProps.errorTitle; - } - - this.setState({ open: newProps.open, title: title }); - } - - handleOpen = () => { - this.setState({ open: true }); - }; - - handleClose = () => { - this.setState({ open: false }); - this.props.onDismiss(); - }; - - render() { - - const actions = [ - , - ]; - - return ( -
    - - {this.props.errorMessages.map(errorMessage => { - return ( -
    - {errorMessage.message} -
    -
    - ); - })} -
    -
    - ); - } -} - -module.exports = ValidationErrorsInfoDialog; \ No newline at end of file + module.exports = ValidationErrorsInfoDialog; diff --git a/src/components/Shared/draggable-dialog/DialogBody.js b/src/components/Shared/draggable-dialog/DialogBody.js new file mode 100644 index 0000000..455e04a --- /dev/null +++ b/src/components/Shared/draggable-dialog/DialogBody.js @@ -0,0 +1,18 @@ +import React from "react"; +import PropTypes from "prop-types"; + +class DialogBody extends React.Component { + render() { + return ( +
    + {this.props.children} +
    + ); + } +} + +DialogBody.propTypes = { + children: PropTypes.oneOfType([PropTypes.array, PropTypes.object]) +}; + +export default DialogBody; \ No newline at end of file diff --git a/src/components/Shared/draggable-dialog/DialogFooter.js b/src/components/Shared/draggable-dialog/DialogFooter.js new file mode 100644 index 0000000..6fe70d5 --- /dev/null +++ b/src/components/Shared/draggable-dialog/DialogFooter.js @@ -0,0 +1,43 @@ +import React from "react"; +import PropTypes from "prop-types"; +import cs from "classnames"; + +const DialogFooter = (props) => { + const buttons = props.buttons; + if (!buttons || buttons.length == 0) { + return false; + } + + const dialogButtons = buttons.map(function (button, index) { + if (React.isValidElement(button)) { + return button; + } + + const { text, onClick, className } = button; + + return ( + + ); + }, this); + + return ( +
    +
    + {dialogButtons} +
    +
    + ); +}; + +DialogFooter.propTypes = { + buttons: PropTypes.array, + onClose: PropTypes.func.isRequired +}; + +export default DialogFooter; \ No newline at end of file diff --git a/src/components/Shared/draggable-dialog/DialogTitle.js b/src/components/Shared/draggable-dialog/DialogTitle.js new file mode 100644 index 0000000..66c7f0b --- /dev/null +++ b/src/components/Shared/draggable-dialog/DialogTitle.js @@ -0,0 +1,65 @@ +import React from "react"; +import PropTypes from "prop-types"; + +const DialogTitle = ({ title, hasCloseIcon, onClose, allowMinimize, isMinimized, onMinimize, allowMaximize, isMaximized, onMaximize, onRestore }) => { + let closeIcon; + if (hasCloseIcon !== false) { + closeIcon = ( + + ); + } + + let minimizeIcon; + if (allowMinimize) { + if (isMinimized) { + minimizeIcon = ( + + ); + } else { + minimizeIcon = ( + + ); + } + } + + let maximizeIcon; + if (allowMaximize) { + if (isMaximized) { + maximizeIcon = ( + + ); + } else { + maximizeIcon = ( + + ); + } + } + + return ( +
    +
    + {title} +
    +
    + {minimizeIcon} + {maximizeIcon} + {closeIcon} +
    +
    + ); +}; + +DialogTitle.propTypes = { + hasCloseIcon: PropTypes.bool, + allowMinimize: PropTypes.bool, + allowMaximize: PropTypes.bool, + isMinimized: PropTypes.bool, + isMaximized: PropTypes.bool, + title: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), + onClose: PropTypes.func.isRequired, + onMinimize: PropTypes.func, + onMaximize: PropTypes.func, + onRestore: PropTypes.func +}; + +export default DialogTitle; \ No newline at end of file diff --git a/src/components/Shared/draggable-dialog/css/img/close.png b/src/components/Shared/draggable-dialog/css/img/close.png new file mode 100644 index 0000000..c06b094 Binary files /dev/null and b/src/components/Shared/draggable-dialog/css/img/close.png differ diff --git a/src/components/Shared/draggable-dialog/css/img/maximize.png b/src/components/Shared/draggable-dialog/css/img/maximize.png new file mode 100644 index 0000000..1749537 Binary files /dev/null and b/src/components/Shared/draggable-dialog/css/img/maximize.png differ diff --git a/src/components/Shared/draggable-dialog/css/img/minimize.png b/src/components/Shared/draggable-dialog/css/img/minimize.png new file mode 100644 index 0000000..a763cbb Binary files /dev/null and b/src/components/Shared/draggable-dialog/css/img/minimize.png differ diff --git a/src/components/Shared/draggable-dialog/css/img/restore.png b/src/components/Shared/draggable-dialog/css/img/restore.png new file mode 100644 index 0000000..6823ec3 Binary files /dev/null and b/src/components/Shared/draggable-dialog/css/img/restore.png differ diff --git a/src/components/Shared/draggable-dialog/css/index.css b/src/components/Shared/draggable-dialog/css/index.css new file mode 100644 index 0000000..fc9dcd8 --- /dev/null +++ b/src/components/Shared/draggable-dialog/css/index.css @@ -0,0 +1,167 @@ +body { + width: 100%; + min-height: 700px; +} + +a { + cursor: pointer; + text-decoration: underline; +} + +.ui-dialog-overlay { + background: #aaaaaa; + opacity: .3; + filter: Alpha(Opacity=30); + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 100; +} + +.ui-dialog { + position: fixed; + top: 50%; + left: 50%; + outline: 0 none; + padding: 0 !important;; + z-index: 101; + background-color: white; + border: 1px solid #f6f6f6; +} + +.ui-dialog.maximized{ + position: fixed; + top: 0; + left: 0; + width: 100% !important; + height: 100% !important; +} + +.ui-dialog.minimized{ + position: fixed; + bottom: 0; + right: 0; +} + +.ui-dialog .ui-dialog-titlebar { + position: relative; + font-size: 1em; + border-radius: 3px; + padding: 0.5em; + height: 35px; + /*border-bottom: 1px solid #f6f6f6;*/ +} + +.ui-dialog.react-draggable .ui-dialog-titlebar{ + cursor: move; +} + +.ui-dialog.react-draggable .ui-dialog-content{ + cursor: move; +} + +.ui-dialog.react-draggable .ui-dialog-buttonpane { + cursor: move; +} + + +.ui-dialog .ui-dialog-titlebar .action-items { + float: right; + position: relative; +} + +.ui-dialog .ui-dialog-titlebar .title { + float: left; + margin-right: .5em; + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-size: 1.3125em; + font-weight: 500; + line-height: 0.2em; + padding : 24px 0px 0px 19px; +} + +.icon { + width: 24px; + height: 24px; + display: block; + float: left; + margin: 5px; + cursor: pointer; + background-size: cover; +} + +.icon.icon-close { + width: 20px; + height: 20px; + background-image: url("./img/close.png"); +} + +.icon.icon-minimize { + background-image: url("./img/minimize.png"); +} + +.icon.icon-maximize { + background-image: url("./img/maximize.png"); +} + +.icon.icon-restore { + background-image: url("./img/restore.png"); +} + +.ui-dialog .ui-dialog-content { + background: none repeat scroll 0 0 transparent; + border: 0 none; + overflow: auto; + position: relative; + font-size: 1rem; + font-weight: 400; + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + line-height: 1.5em; + color:rgba(0, 0, 0, 0.54); + padding : 24px 24px 24px 24px; +} + +.ui-dialog .ui-dialog-buttonpane { + position: absolute; + width: 100%; + bottom: 0; + text-align: right; + border-width: 1px 0 0 0; + /*border-top: 1px solid #f6f6f6;*/ +} + +.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{ + padding: 0.5em; +} + +.ui-dialog .ui-dialog-buttonpane button { + margin: 0 .5em 0 .5em; + cursor: pointer; + background-color: #f6f6f6; + padding: 0.5em 1em; + outline: none; + border: 1px solid #CCCCCC; + border-radius: 3px; +} + +.ui-dialog .ui-dialog-buttonpane button:hover{ + background-color: #CCCCCC; + border: 1px solid #BBBBBB; +} + +.ui-dialog .react-resizable-handle { + position: absolute; + width: 20px; + height: 20px; + bottom: 0; + right: 0; + background: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2IDYiIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiNmZmZmZmYwMCIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iNnB4Ij48ZyBvcGFjaXR5PSIwLjMwMiI+PHBhdGggZD0iTSA2IDYgTCAwIDYgTCAwIDQuMiBMIDQgNC4yIEwgNC4yIDQuMiBMIDQuMiAwIEwgNiAwIEwgNiA2IEwgNiA2IFoiIGZpbGw9IiMwMDAwMDAiLz48L2c+PC9zdmc+'); + background-position: bottom right; + padding: 0 3px 3px 0; + background-repeat: no-repeat; + background-origin: content-box; + box-sizing: border-box; + cursor: se-resize; +} \ No newline at end of file diff --git a/src/components/Shared/draggable-dialog/index.js b/src/components/Shared/draggable-dialog/index.js new file mode 100644 index 0000000..98789d9 --- /dev/null +++ b/src/components/Shared/draggable-dialog/index.js @@ -0,0 +1,171 @@ +import React from "react"; +import PropTypes from "prop-types"; +import cs from "classnames"; +import Draggable from "react-draggable"; +import { Resizable } from "react-resizable"; +import DialogTitle from "./DialogTitle"; +import DialogBody from "./DialogBody"; +import DialogFooter from "./DialogFooter"; +import EventStack from "active-event-stack"; + +class Dialog extends React.Component { + constructor(props) { + super(props); + + this.state = { + height: props.height, + width: props.width, + isMinimized: false, + isMaximized: false + }; + } + + componentWillMount() { + /** + * This is done in the componentWillMount instead of the componentDidMount + * because this way, a modal that is a child of another will have register + * for events after its parent + */ + this.eventToken = EventStack.addListenable([ + ["keydown", this.handleGlobalKeydown] + ]); + } + + componentWillUnmount = () => { + EventStack.removeListenable(this.eventToken); + } + + handleGlobalKeydown = (e) => { + if (this.props.closeOnEscape && e.keyCode == 27) { + e.stopPropagation(); + this.onClose(); + } + + return false; + } + + onClose = () => { + if (this.props.onClose) { + this.props.onClose(); + } + } + + onMinimize = () => { + this.setState({ isMinimized: true, isMaximized: false }); + } + + onMaximize = () => { + this.setState({ isMinimized: false, isMaximized: true }); + } + + onRestore = () => { + this.setState({ isMinimized: false, isMaximized: false }); + } + + onResize = (event, { element, size }) => { + this.setState({ width: size.width, height: size.height }); + } + + getDialogTitle = () => { + return ( + + ); + } + + render() { + const { height, width, isMinimized, isMaximized } = this.state; + const { modal, isDraggable, isResizable, buttons, children, position } = this.props; + const { x = -width / 2, y = -height / 2 } = position; + + let dialog = ( +
    + {this.getDialogTitle()} + { + !isMinimized && {children} + } + { + !isMinimized && + } + +
    + ); + + if (!isMinimized && !isMaximized && isResizable) { + dialog = ( + + {dialog} + + ); + } + + if (!isMinimized && !isMaximized && isDraggable !== false) { + dialog = ( + + {dialog} + + ); + } + + return ( +
    + {dialog} + {modal &&
    } +
    + ); + } +} + +Dialog.propTypes = { + height: PropTypes.number, + width: PropTypes.number, + modal: PropTypes.bool, + position: PropTypes.shape({ + x: PropTypes.number, + y: PropTypes.number + }), + hasCloseIcon: PropTypes.bool, + allowMinimize: PropTypes.bool, + allowMaximize: PropTypes.bool, + isResizable: PropTypes.bool, + title: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), + closeOnEscape: PropTypes.bool, + onClose: PropTypes.func, + children: PropTypes.oneOfType([PropTypes.string, PropTypes.array, PropTypes.element]).isRequired, + buttons: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.shape({ + text: PropTypes.string, + onClick: PropTypes.func + })), + PropTypes.arrayOf(PropTypes.element) + ]) +}; + +Dialog.defaultProps = { + height: 200, + width: 600, + modal: false, + closeOnEscape: true, + isDraggable: false, + isResizable: false, + title: '', + hasCloseIcon: true, + allowMinimize: false, + allowMaximize: false, + onClose: null, + buttons: null, + position: { x: -250, y: -150 } +}; + +export default Dialog; \ No newline at end of file diff --git a/src/components/Sidenav/SidenavContent.js b/src/components/Sidenav/SidenavContent.js index cc0bfc5..5a4becf 100644 --- a/src/components/Sidenav/SidenavContent.js +++ b/src/components/Sidenav/SidenavContent.js @@ -102,17 +102,9 @@ class SidebarContent extends React.Component { return (
      { this.nav = c; }}> {loggedUser.anyOf(visitReporter) && -
    • - scheduleVisits -
        -
      • Add Visit
      • -
      -
    • +
    • Create Visit
    • } - - {!loggedUser.anyOf(visitReporter) && -
    • directions_carRides
        @@ -166,8 +158,6 @@ class SidebarContent extends React.Component { }
      • -
      • Log Out
      • -
      ); } diff --git a/src/routes/app/components/MainApp.js b/src/routes/app/components/MainApp.js index acdbfd4..6b79472 100644 --- a/src/routes/app/components/MainApp.js +++ b/src/routes/app/components/MainApp.js @@ -5,10 +5,6 @@ import Footer from 'components/Footer'; import Notifications from 'components/Notifications'; import Notification from 'components/Shared/Notification'; import GeolocationService from './Geolocation'; -import { - loggedUser, - visitReporter, -} from 'utils/authorization'; class MainApp extends React.Component { constructor(props) { @@ -57,9 +53,8 @@ class MainApp extends React.Component {