first commit
This commit is contained in:
86
src/components/Connection/index.js
Normal file
86
src/components/Connection/index.js
Normal file
@@ -0,0 +1,86 @@
|
||||
/* global document, window */
|
||||
|
||||
const axios = require('axios');
|
||||
|
||||
const Instance = () => {
|
||||
const apiUrl = process.env.NODE_ENV === 'production'
|
||||
? 'https://portal-api.bcbsinstitute.com'
|
||||
: 'https://portal-api.dev.bcbsinstitute.com';
|
||||
window.localStorage.setItem('App', '8a266a40-ed2e-4be2-bdfc-459a507bf02e');
|
||||
|
||||
let instance = axios.create({
|
||||
baseURL: apiUrl,
|
||||
timeout: 60000,
|
||||
headers: { App: window.localStorage.getItem('App') },
|
||||
});
|
||||
|
||||
const setCookie = (cname, cvalue, date) => {
|
||||
const d = new Date(date * 1000);
|
||||
const expires = `expires=${d.toUTCString()}`;
|
||||
document.cookie = `${cname}=${cvalue};${expires};path=/`;
|
||||
};
|
||||
|
||||
const getCookie = (cname) => {
|
||||
const name = `${cname}=`;
|
||||
const decodedCookie = decodeURIComponent(document.cookie);
|
||||
const ca = decodedCookie.split(';');
|
||||
for (let i = 0; i < ca.length; i += 1) {
|
||||
let c = ca[i];
|
||||
while (c.charAt(0) === ' ') {
|
||||
c = c.substring(1);
|
||||
}
|
||||
|
||||
if (c.indexOf(name) === 0) {
|
||||
return c.substring(name.length, c.length);
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
const setToken = (token) => {
|
||||
if (token && token !== null) {
|
||||
instance = axios.create({
|
||||
baseURL: apiUrl,
|
||||
timeout: 60000,
|
||||
headers: { App: window.localStorage.getItem('App'), Token: `Bearer ${token}` },
|
||||
});
|
||||
} else {
|
||||
instance = axios.create({
|
||||
baseURL: apiUrl,
|
||||
timeout: 60000,
|
||||
headers: { App: window.localStorage.getItem('App') },
|
||||
});
|
||||
}
|
||||
|
||||
return instance;
|
||||
};
|
||||
|
||||
const getConnection = () => {
|
||||
const token = getCookie('token');
|
||||
return setToken(token);
|
||||
};
|
||||
|
||||
const getRawConn = () => {
|
||||
const token = getCookie('token');
|
||||
if (token && token !== null && token !== '') {
|
||||
return instance;
|
||||
}
|
||||
window.location.href = '/#/login';
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
const token = getCookie('token');
|
||||
instance = setToken(token);
|
||||
|
||||
return {
|
||||
getCookie,
|
||||
setCookie,
|
||||
getConnection,
|
||||
setToken,
|
||||
getRawConn,
|
||||
};
|
||||
};
|
||||
|
||||
export default Instance();
|
||||
22
src/components/Footer/index.js
Normal file
22
src/components/Footer/index.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
import APPCONFIG from 'constants/Config';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
class Footer extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<section className="app-footer">
|
||||
<div className="container-fluid">
|
||||
<span className="float-left">
|
||||
<span>Copyright©{APPCONFIG.year} <a className="brand" target="_blank" href={APPCONFIG.productLink}>{APPCONFIG.brand}</a></span>
|
||||
</span>
|
||||
<span className="float-right">
|
||||
<span></span>
|
||||
</span>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Footer;
|
||||
33
src/components/Header/NavLeftList.js
Normal file
33
src/components/Header/NavLeftList.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import IconMenu from 'material-ui/IconMenu';
|
||||
import MenuItem from 'material-ui/MenuItem';
|
||||
import IconButton from 'material-ui/IconButton/IconButton';
|
||||
import { hashHistory } from 'react-router';
|
||||
import Divider from 'material-ui/Divider';
|
||||
import Badge from 'material-ui/Badge';
|
||||
import NotificationsIcon from 'material-ui/svg-icons/social/notifications';
|
||||
|
||||
|
||||
const HeaderIconButtonStyle = {
|
||||
width: '60px',
|
||||
height: '60px'
|
||||
};
|
||||
|
||||
const listItemStyle = {
|
||||
paddingLeft: '40px' // 36 + 16, algin with sub list
|
||||
};
|
||||
|
||||
class NavLeftList extends React.Component {
|
||||
|
||||
handleChange = (event, value) => {
|
||||
hashHistory.push(value);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div> </div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = NavLeftList;
|
||||
80
src/components/Header/NavRightList.js
Normal file
80
src/components/Header/NavRightList.js
Normal file
@@ -0,0 +1,80 @@
|
||||
import React from 'react';
|
||||
import IconMenu from 'material-ui/IconMenu';
|
||||
import MenuItem from 'material-ui/MenuItem';
|
||||
import IconButton from 'material-ui/IconButton/IconButton';
|
||||
import { hashHistory } from 'react-router';
|
||||
|
||||
const ImgIconButtonStyle = {
|
||||
width: '60px',
|
||||
height: '60px'
|
||||
};
|
||||
|
||||
const listItemStyle = {
|
||||
paddingLeft: '50px' // 36 + 16, algin with sub list
|
||||
};
|
||||
|
||||
class NavRightList extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.props = props;
|
||||
this.state = {
|
||||
name: '',
|
||||
useruuid: ''
|
||||
}
|
||||
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
}
|
||||
|
||||
handleChange = (event, value) => {
|
||||
hashHistory.push(value);
|
||||
}
|
||||
componentDidMount() {
|
||||
const user = JSON.parse(localStorage.getItem('loggedUser'));
|
||||
if (user) {
|
||||
this.setState(Object.assign(this.state, user));
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ul className="list-unstyled float-right">
|
||||
<li>
|
||||
|
||||
<IconMenu
|
||||
|
||||
iconButtonElement={<IconButton style={ImgIconButtonStyle}><img src="assets/images/ic_account_circle_white_48dp_1x.png" alt="" className="rounded-circle img30_30" /></IconButton>}
|
||||
onChange={this.handleChange}
|
||||
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
|
||||
targetOrigin={{ horizontal: 'right', vertical: 'top' }}
|
||||
menuStyle={{ minWidth: '150px' }}
|
||||
>
|
||||
<MenuItem
|
||||
onTouchTap={(e) => this.handleChange(e, `/app/authorizedusers/${this.state.useruuid}`)}
|
||||
primaryText="Profile"
|
||||
style={{ fontSize: '14px', lineHeight: '48px' }}
|
||||
innerDivStyle={listItemStyle}
|
||||
leftIcon={<i className="material-icons">account_circle</i>}
|
||||
/>
|
||||
<MenuItem
|
||||
onTouchTap={(e) => this.handleChange(e, `/app/form/steppers/${this.state.useruuid}`)}
|
||||
primaryText="Book Ride"
|
||||
innerDivStyle={listItemStyle}
|
||||
style={{ fontSize: '14px', lineHeight: '48px' }}
|
||||
leftIcon={<i className="material-icons">mode_edit</i>}
|
||||
/>
|
||||
<MenuItem
|
||||
onTouchTap={(e) => this.handleChange(e, `/login`)}
|
||||
primaryText="Log Out"
|
||||
innerDivStyle={listItemStyle}
|
||||
style={{ fontSize: '14px', lineHeight: '48px' }}
|
||||
leftIcon={<i className="material-icons">forward</i>}
|
||||
/>
|
||||
</IconMenu>
|
||||
</li>
|
||||
<li style={{ marginRight: '10px' }}><h6>{this.state.name}</h6></li>
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = NavRightList;
|
||||
70
src/components/Header/index.js
Normal file
70
src/components/Header/index.js
Normal file
@@ -0,0 +1,70 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import classnames from 'classnames';
|
||||
import { Link } from 'react-router';
|
||||
import APPCONFIG from 'constants/Config';
|
||||
import NavLeftList from './NavLeftList';
|
||||
import NavRightList from './NavRightList';
|
||||
|
||||
|
||||
class Header extends React.Component {
|
||||
componentDidMount() {
|
||||
const sidebarToggler = this.sidebarBtn;
|
||||
const $sidebarToggler = $(sidebarToggler);
|
||||
const $body = $('#body');
|
||||
|
||||
$sidebarToggler.on('click', (e) => {
|
||||
// _sidebar.scss, _page-container.scss
|
||||
$body.toggleClass('sidebar-mobile-open');
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isFixedHeader, colorOption } = this.props;
|
||||
|
||||
return (
|
||||
<section className="app-header">
|
||||
<div
|
||||
className={classnames('app-header-inner', {
|
||||
'bg-color-light': ['11', '12', '13', '14', '15', '16', '21'].indexOf(colorOption) >= 0,
|
||||
'bg-color-dark': colorOption === '31',
|
||||
'bg-color-primary': ['22', '32'].indexOf(colorOption) >= 0,
|
||||
'bg-color-success': ['23', '33'].indexOf(colorOption) >= 0,
|
||||
'bg-color-info': ['24', '34'].indexOf(colorOption) >= 0,
|
||||
'bg-color-warning': ['25', '35'].indexOf(colorOption) >= 0,
|
||||
'bg-color-danger': ['26', '36'].indexOf(colorOption) >= 0
|
||||
})}
|
||||
>
|
||||
<div className="hidden-lg-up float-left">
|
||||
<a href="javascript:;" className="md-button header-icon toggle-sidebar-btn" ref={(c) => { this.sidebarBtn = c; }}>
|
||||
<i className="material-icons">menu</i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="brand hidden-md-down">
|
||||
<h2><Link to="/">{APPCONFIG.brand}</Link></h2>
|
||||
</div>
|
||||
|
||||
<div className="top-nav-left hidden-md-down">
|
||||
<NavLeftList />
|
||||
</div>
|
||||
|
||||
<div className="top-nav-right">
|
||||
<NavRightList />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
colorOption: state.settings.colorOption,
|
||||
isFixedHeader: state.settings.isFixedHeader
|
||||
});
|
||||
|
||||
module.exports = connect(
|
||||
mapStateToProps
|
||||
)(Header);
|
||||
|
||||
245
src/components/Notifications/index.js
Normal file
245
src/components/Notifications/index.js
Normal file
@@ -0,0 +1,245 @@
|
||||
import 'jquery-slimscroll/jquery.slimscroll.min';
|
||||
import React from 'react';
|
||||
import APPCONFIG from 'constants/Config';
|
||||
import Badge from 'material-ui/Badge';
|
||||
import NotificationsIcon from 'material-ui/svg-icons/social/notifications';
|
||||
import { Card, CardActions, CardHeader, CardText } from 'material-ui/Card';
|
||||
import FlatButton from 'material-ui/FlatButton';
|
||||
import { List, ListItem } from 'material-ui/List';
|
||||
import Divider from 'material-ui/Divider';
|
||||
import Subheader from 'material-ui/Subheader';
|
||||
import MapsDirectionsCar from 'material-ui/svg-icons/maps/directions-car';
|
||||
import ContactList from '../../routes/app/components/ContactList';
|
||||
import Avatar from 'material-ui/Avatar';
|
||||
import { grey400, darkBlack, lightBlack } from 'material-ui/styles/colors';
|
||||
import IconButton from 'material-ui/IconButton';
|
||||
import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert';
|
||||
import IconMenu from 'material-ui/IconMenu';
|
||||
import MenuItem from 'material-ui/MenuItem';
|
||||
import NavigationChevronRight from 'material-ui/svg-icons/navigation/more-vert';
|
||||
|
||||
// SocialNotificationsActive
|
||||
|
||||
class Notifications extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const socketURL = process.env.NODE_ENV === 'production'
|
||||
? 'wss://portal-api.bcbsinstitute.com/v1/notification/ws?id='
|
||||
: 'wss://portal-api.dev.bcbsinstitute.com/v1/notification/ws?id='
|
||||
|
||||
this.props = props;
|
||||
this.state = {
|
||||
user: {
|
||||
useruuid: '',
|
||||
name: ''
|
||||
},
|
||||
socket_url: socketURL,
|
||||
notifications: {},
|
||||
totalNotifications: 0,
|
||||
notificationList: [],
|
||||
};
|
||||
this.socket = null;
|
||||
|
||||
this.socketMessageListener = this.socketMessageListener.bind(this);
|
||||
this.socketOpenListener = this.socketOpenListener.bind(this);
|
||||
this.socketCloseListener = this.socketCloseListener.bind(this);
|
||||
this.socketCloseListener = this.socketCloseListener.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setState(Object.assign(this.state, { user: this.props.user, notifications: [], totalNotifications: 0 }));
|
||||
if (this.state.user.useruuid && this.state.user.useruuid != '') {
|
||||
this.setState(Object.assign(this.state, { socket_url: this.state.socket_url + this.state.user.useruuid }));
|
||||
this.socket = new WebSocket(this.state.socket_url);
|
||||
|
||||
this.socketMessageListener = this.socketMessageListener.bind(this);
|
||||
this.socketOpenListener = this.socketOpenListener.bind(this);
|
||||
this.socketCloseListener = this.socketCloseListener.bind(this);
|
||||
|
||||
this.socket.addEventListener('open', this.socketOpenListener);
|
||||
this.socket.addEventListener('message', this.socketMessageListener);
|
||||
this.socket.addEventListener('close', this.socketCloseListener);
|
||||
}
|
||||
// }
|
||||
// const quickviewInner = this.quickview;
|
||||
// $(quickviewInner).slimscroll({
|
||||
// height: '100%'
|
||||
// });
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.user.useruuid !== this.state.user.useruuid) {
|
||||
if (this.socket) {
|
||||
this.socket.close();
|
||||
}
|
||||
this.setState(Object.assign(this.state, { user: nextProps.user, notifications: [], totalNotifications: 0 }));
|
||||
if (this.state.user.useruuid && this.state.user.useruuid != '') {
|
||||
this.setState(Object.assign(this.state, { socket_url: this.state.socket_url + this.state.user.useruuid }));
|
||||
this.socket = new WebSocket(this.state.socket_url);
|
||||
|
||||
this.socketMessageListener = this.socketMessageListener.bind(this);
|
||||
this.socketOpenListener = this.socketOpenListener.bind(this);
|
||||
this.socketCloseListener = this.socketCloseListener.bind(this);
|
||||
|
||||
this.socket.addEventListener('open', this.socketOpenListener);
|
||||
this.socket.addEventListener('message', this.socketMessageListener);
|
||||
this.socket.addEventListener('close', this.socketCloseListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
disableNotification(n) {
|
||||
n.content.type = "disable-notification";
|
||||
this.socket.send(JSON.stringify(n));
|
||||
this.setState(Object.assign(this.state, { totalNotifications: this.state.totalNotifications-- }));
|
||||
}
|
||||
|
||||
socketMessageListener(event) {
|
||||
let n = JSON.parse(event.data);
|
||||
let notifications = this.state.notifications;
|
||||
if (notifications[n.nid] === null || notifications[n.nid] === undefined) {
|
||||
notifications[n.nid] = n;
|
||||
let notificationList = []
|
||||
Object.keys(notifications).map((k) => {
|
||||
const n = notifications[k];
|
||||
switch (n.content.type) {
|
||||
case 'ride':
|
||||
const ride = n.content.payload;
|
||||
const driverPhoneNumber = (ride.driver && ride.driver.phone_number) ? ride.driver.phone_number : '';
|
||||
|
||||
const rideContactList = {
|
||||
driverMobile: driverPhoneNumber,
|
||||
memberMobile: ride.user.phonenumber,
|
||||
memberEmail: ride.user.email,
|
||||
schedulerEmail: ride.created_user.email,
|
||||
schedulerMobile: ride.created_user.phonenumber,
|
||||
rideID: ride.ride_uuid,
|
||||
};
|
||||
|
||||
|
||||
|
||||
const iconButtonElement = (
|
||||
<IconButton
|
||||
touch={true}
|
||||
tooltip="Actions"
|
||||
tooltipPosition="bottom-left"
|
||||
>
|
||||
<NavigationChevronRight color={grey400} />
|
||||
|
||||
{/* <MoreVertIcon color={grey400} /> */}
|
||||
</IconButton>
|
||||
);
|
||||
|
||||
const rightIconMenu = (
|
||||
<IconMenu iconButtonElement={iconButtonElement}>
|
||||
<MenuItem> <a href={"/#/app/member/" + n.content.payload.user.useruuid}>Member</a> </MenuItem>
|
||||
<MenuItem> <a href="/#/app/table/visits/" > Visits </a> </MenuItem>
|
||||
<MenuItem> <a href={"/#/ride/" + n.content.payload.ride_uuid + "/" + n.content.payload.user.useruuid}> Ride</a> </MenuItem>
|
||||
<MenuItem><a href={"/#/app/page/map/" + n.content.payload.ride_uuid}> Ride Details </a> </MenuItem>
|
||||
<MenuItem> <ContactList data={rideContactList} /> </MenuItem>
|
||||
</IconMenu>
|
||||
);
|
||||
|
||||
notificationList.push(
|
||||
<div>
|
||||
<ListItem
|
||||
leftAvatar={<i className="nav-icon material-icons">directions_car</i>}
|
||||
rightIconButton={rightIconMenu}
|
||||
primaryText={n.content.subject}
|
||||
secondaryText={
|
||||
<p>
|
||||
<span>{ride.user.name}</span><br />
|
||||
{n.content.body}
|
||||
</p>
|
||||
}
|
||||
secondaryTextLines={2}
|
||||
/>
|
||||
<Divider inset={true} />
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case 'message':
|
||||
return
|
||||
notificationList.push(
|
||||
<Card id={n.id}>
|
||||
<CardHeader
|
||||
title={n.content.subject}
|
||||
subtitle={ride.user.name + " | " + "Time Here"}
|
||||
avatar={<Message />}
|
||||
/>
|
||||
<CardText>
|
||||
{n.content.body}
|
||||
</CardText>
|
||||
<CardActions>
|
||||
<ContactList data={rideContactList} />
|
||||
</CardActions>
|
||||
</Card>)
|
||||
break;
|
||||
}
|
||||
})
|
||||
this.setState(Object.assign(this.state, { notifications: notifications, totalNotifications: this.state.totalNotifications + 1, notificationList: notificationList }));
|
||||
|
||||
if (this.props.onNotificationUpdate) {
|
||||
this.props.onNotificationUpdate(n, this.state.totalNotifications);
|
||||
}
|
||||
|
||||
this.forceUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
socketOpenListener(event) {
|
||||
console.log('Connected for the ID: ' + this.state.user.useruuid);
|
||||
}
|
||||
|
||||
socketCloseListener(event) {
|
||||
if (this.socket) {
|
||||
console.error('Disconnected.');
|
||||
}
|
||||
this.socket = new WebSocket(this.state.socket_url);
|
||||
|
||||
this.socketMessageListener = this.socketMessageListener.bind(this);
|
||||
this.socketOpenListener = this.socketOpenListener.bind(this);
|
||||
this.socketCloseListener = this.socketCloseListener.bind(this);
|
||||
|
||||
this.socket.addEventListener('open', this.socketOpenListener);
|
||||
this.socket.addEventListener('message', this.socketMessageListener);
|
||||
this.socket.addEventListener('close', this.socketCloseListener);
|
||||
}
|
||||
|
||||
toggleCustomizer = () => {
|
||||
const $body = $('#body');
|
||||
$body.toggleClass('quickview-open-customizer');
|
||||
}
|
||||
|
||||
closeCustomizer = () => {
|
||||
const $body = $('#body');
|
||||
$body.removeClass('quickview-open-customizer');
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<section
|
||||
className="quickview-wrapper customizer theme-light"
|
||||
id="quickview-customizer"
|
||||
>
|
||||
<a className="customizer-toggle" href="javascript:;" onClick={this.toggleCustomizer}>
|
||||
<Badge
|
||||
badgeStyle={{ top: 22, right: 16 }}
|
||||
badgeContent={this.state.totalNotifications}
|
||||
secondary={true}
|
||||
primary={false}
|
||||
>
|
||||
<NotificationsIcon />
|
||||
</Badge>
|
||||
</a>
|
||||
<List>
|
||||
<Subheader>Today</Subheader>
|
||||
{this.state.notificationList}
|
||||
</List>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Notifications;
|
||||
106
src/components/ReactECharts/index.js
Normal file
106
src/components/ReactECharts/index.js
Normal file
@@ -0,0 +1,106 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import echarts from 'echarts';
|
||||
import 'echarts/theme/macarons';
|
||||
|
||||
import elementResizeEvent from 'element-resize-event';
|
||||
|
||||
class ReactEcharts extends React.Component {
|
||||
propTypes: {
|
||||
option: PropTypes.object.isRequired,
|
||||
notMerge: PropTypes.bool,
|
||||
lazyUpdate: PropTypes.bool,
|
||||
style: PropTypes.object,
|
||||
className: PropTypes.string,
|
||||
theme: PropTypes.string,
|
||||
onChartReady: PropTypes.func,
|
||||
showLoading: PropTypes.bool,
|
||||
loadingOption: PropTypes.object,
|
||||
onEvents: PropTypes.object
|
||||
}
|
||||
|
||||
// first add
|
||||
componentDidMount() {
|
||||
const echartObj = this.renderEchartDom();
|
||||
const onEvents = this.props.onEvents || {};
|
||||
let reizeEvent;
|
||||
|
||||
this.bindEvents(echartObj, onEvents);
|
||||
// on chart ready
|
||||
if (typeof this.props.onChartReady === 'function') this.props.onChartReady(echartObj);
|
||||
|
||||
function resize() {
|
||||
clearTimeout(reizeEvent);
|
||||
reizeEvent = setTimeout(() => { echartObj.resize(); }, 200);
|
||||
}
|
||||
|
||||
// on resize
|
||||
elementResizeEvent(this.echartsDom, () => {
|
||||
resize();
|
||||
});
|
||||
}
|
||||
|
||||
// update
|
||||
componentDidUpdate() {
|
||||
this.renderEchartDom();
|
||||
this.bindEvents(this.getEchartsInstance(), this.props.onEvents || []);
|
||||
}
|
||||
|
||||
// remove
|
||||
componentWillUnmount() {
|
||||
echarts.dispose(this.echartsDom);
|
||||
}
|
||||
|
||||
// bind the events
|
||||
bindEvents(instance, events) {
|
||||
const loop = function loop(eventName) {
|
||||
// ignore the event config which not satisfy
|
||||
if (typeof eventName === 'string' && typeof events[eventName] === 'function') {
|
||||
// binding event
|
||||
instance.off(eventName);
|
||||
instance.on(eventName, (param) => {
|
||||
events[eventName](param, instance);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
for (const eventName in events) {
|
||||
loop(eventName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// render the dom
|
||||
renderEchartDom() {
|
||||
// init the echart object
|
||||
const echartObj = this.getEchartsInstance();
|
||||
// set the echart option
|
||||
echartObj.setOption(this.props.option, this.props.notMerge || false, this.props.lazyUpdate || false);
|
||||
// set loading mask
|
||||
if (this.props.showLoading) echartObj.showLoading(this.props.loadingOption || null);
|
||||
else echartObj.hideLoading();
|
||||
|
||||
return echartObj;
|
||||
}
|
||||
|
||||
getEchartsInstance() {
|
||||
// return the echart object
|
||||
const theme = this.props.theme ? this.props.theme : 'macarons';
|
||||
return echarts.getInstanceByDom(this.echartsDom) || echarts.init(this.echartsDom, theme);
|
||||
}
|
||||
|
||||
render() {
|
||||
const style = this.props.style || {
|
||||
height: '350px'
|
||||
};
|
||||
// for render
|
||||
return (
|
||||
<div
|
||||
ref={(c) => { this.echartsDom = c; }}
|
||||
className={this.props.className}
|
||||
style={style} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ReactEcharts;
|
||||
297
src/components/Shared/Notification.js
Normal file
297
src/components/Shared/Notification.js
Normal file
@@ -0,0 +1,297 @@
|
||||
import React, { Component } from 'react'
|
||||
import Message from 'material-ui/svg-icons/communication/message';
|
||||
import ReactMaterialUiNotifications from 'react-materialui-notifications';
|
||||
import moment from 'moment';
|
||||
import { deepOrange500 } from 'material-ui/styles/colors';
|
||||
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
|
||||
import getMuiTheme from 'material-ui/styles/getMuiTheme';
|
||||
import lightTheme from '../../containers/themes/lightTheme';
|
||||
import darkTheme from '../../containers/themes/darkTheme';
|
||||
import grayTheme from '../../containers/themes/grayTheme';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import FontIcon from 'material-ui/FontIcon';
|
||||
import { indigo500 } from 'material-ui/styles/colors';
|
||||
import Paper from 'material-ui/Paper';
|
||||
import MapsDirectionsCar from 'material-ui/svg-icons/maps/directions-car';
|
||||
|
||||
import ContactList from '../../routes/app/components/ContactList';
|
||||
|
||||
const nearbyIcon = <FontIcon className="material-icons">perm phone msg</FontIcon>;
|
||||
|
||||
const paperStyle = {
|
||||
height: 240,
|
||||
width: 270,
|
||||
margin: 10,
|
||||
textAlign: 'center',
|
||||
display: 'inline-block',
|
||||
position: 'relative'
|
||||
};
|
||||
|
||||
const styles = {
|
||||
appbar: {
|
||||
textAlign: 'left'
|
||||
},
|
||||
buttonContainer: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-around'
|
||||
},
|
||||
footer: {
|
||||
marginTop: 15,
|
||||
width: 'calc(100% - 30px)',
|
||||
fontSize: 16,
|
||||
padding: 15,
|
||||
backgroundColor: '#fff'
|
||||
},
|
||||
logo: {
|
||||
width: 48
|
||||
},
|
||||
paper: {
|
||||
width: '100%',
|
||||
textAlign: 'left',
|
||||
marginBottom: 15,
|
||||
padding: 15,
|
||||
fontSize: 18
|
||||
},
|
||||
rightIcon: {
|
||||
width: 36,
|
||||
height: 36,
|
||||
fill: '#fff'
|
||||
},
|
||||
table: {
|
||||
marginTop: 15
|
||||
},
|
||||
headerStyle: {
|
||||
tableLayout: 'auto'
|
||||
},
|
||||
table1Col1: {
|
||||
width: 137
|
||||
},
|
||||
table1Col2: {
|
||||
width: 72
|
||||
},
|
||||
table1Col3: {
|
||||
width: 128
|
||||
},
|
||||
table1Col2C: {
|
||||
width: 72,
|
||||
},
|
||||
table2Col3: {
|
||||
width: 36
|
||||
}
|
||||
};
|
||||
|
||||
export default class Notification extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const socketURL = process.env.NODE_ENV === 'production'
|
||||
? 'wss://portal-api.bcbsinstitute.com/v1/notification/ws?id='
|
||||
: 'wss://portal-api.dev.bcbsinstitute.com/v1/notification/ws?id='
|
||||
|
||||
this.props = props;
|
||||
this.state = {
|
||||
user: {
|
||||
useruuid: '',
|
||||
name: ''
|
||||
},
|
||||
socket_url: socketURL,
|
||||
notifications: {},
|
||||
totalNotifications: 0
|
||||
};
|
||||
this.socket = null;
|
||||
|
||||
this.socketMessageListener = this.socketMessageListener.bind(this);
|
||||
this.socketOpenListener = this.socketOpenListener.bind(this);
|
||||
this.socketCloseListener = this.socketCloseListener.bind(this);
|
||||
this.socketCloseListener = this.socketCloseListener.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setState(Object.assign(this.state, { user: this.props.user, notifications: [], totalNotifications: 0 }));
|
||||
if (this.state.user.useruuid && this.state.user.useruuid != '') {
|
||||
this.setState(Object.assign(this.state, { socket_url: this.state.socket_url + this.state.user.useruuid }));
|
||||
this.socket = new WebSocket(this.state.socket_url);
|
||||
|
||||
this.socketMessageListener = this.socketMessageListener.bind(this);
|
||||
this.socketOpenListener = this.socketOpenListener.bind(this);
|
||||
this.socketCloseListener = this.socketCloseListener.bind(this);
|
||||
|
||||
this.socket.addEventListener('open', this.socketOpenListener);
|
||||
this.socket.addEventListener('message', this.socketMessageListener);
|
||||
this.socket.addEventListener('close', this.socketCloseListener);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.user.useruuid !== this.state.user.useruuid) {
|
||||
if (this.socket) {
|
||||
this.socket.close();
|
||||
}
|
||||
this.setState(Object.assign(this.state, { user: nextProps.user, notifications: [], totalNotifications: 0 }));
|
||||
if (this.state.user.useruuid && this.state.user.useruuid != '') {
|
||||
this.setState(Object.assign(this.state, { socket_url: this.state.socket_url + this.state.user.useruuid }));
|
||||
this.socket = new WebSocket(this.state.socket_url);
|
||||
|
||||
this.socketMessageListener = this.socketMessageListener.bind(this);
|
||||
this.socketOpenListener = this.socketOpenListener.bind(this);
|
||||
this.socketCloseListener = this.socketCloseListener.bind(this);
|
||||
|
||||
this.socket.addEventListener('open', this.socketOpenListener);
|
||||
this.socket.addEventListener('message', this.socketMessageListener);
|
||||
this.socket.addEventListener('close', this.socketCloseListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
disableNotification(n) {
|
||||
n.content.type = "disable-notification";
|
||||
this.socket.send(JSON.stringify(n));
|
||||
this.setState(Object.assign(this.state, { totalNotifications: this.state.totalNotifications-- }));
|
||||
}
|
||||
|
||||
|
||||
socketMessageListener(event) {
|
||||
let n = JSON.parse(event.data);
|
||||
|
||||
let notifications = this.state.notifications;
|
||||
if (notifications[n.nid] === null || notifications[n.nid] === undefined) {
|
||||
switch (n.content.type) {
|
||||
case 'ride':
|
||||
const ride = n.content.payload;
|
||||
const driverPhoneNumber = (ride.driver && ride.driver.phone_number) ? ride.driver.phone_number : '';
|
||||
|
||||
const rideContactList = {
|
||||
driverMobile: driverPhoneNumber,
|
||||
memberMobile: ride.user.phonenumber,
|
||||
memberEmail: ride.user.email,
|
||||
schedulerEmail: ride.created_user.email,
|
||||
schedulerMobile: ride.created_user.phonenumber,
|
||||
rideID: ride.ride_uuid,
|
||||
}
|
||||
|
||||
ReactMaterialUiNotifications.showNotification({
|
||||
title: n.content.subject,
|
||||
additionalText: n.content.payload.user.name,
|
||||
priority: true,
|
||||
icon: <MapsDirectionsCar />,
|
||||
// icon: <Message />,
|
||||
iconBadgeColor: indigo500,
|
||||
personalized: true,
|
||||
timestamp: moment(n.date).format('ddd, MMMM DD, hh:mm A'),
|
||||
onClick: () => { this.disableNotification(n); },
|
||||
overflowContent:
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
{n.content.body}
|
||||
<p>
|
||||
<a href={"/#/app/member/" + n.content.payload.user.useruuid}>Member</a> |
|
||||
<a href="/#/app/table/visits/" > Visits </a> |
|
||||
<a href={"/#/ride/" + n.content.payload.ride_uuid + "/" + n.content.payload.user.useruuid}> Ride</a> |
|
||||
<a href={"/#/app/page/map/" + n.content.payload.ride_uuid}> Ride Details | </a>
|
||||
|
||||
<ContactList data={rideContactList} />
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div >
|
||||
</div>
|
||||
</div>,
|
||||
});
|
||||
if (this.props.onRideUpdate) {
|
||||
this.props.onRideUpdate(n.content.payload);
|
||||
}
|
||||
break;
|
||||
case 'message':
|
||||
ReactMaterialUiNotifications.showNotification({
|
||||
title: `Message Received`,
|
||||
additionalText: n.content.payload.user.name,
|
||||
priority: true,
|
||||
icon: <Message />,
|
||||
iconBadgeColor: deepOrange500,
|
||||
personalized: true,
|
||||
avatar: '/assets/images/ic_account_circle_white_48dp_1x.png',
|
||||
timestamp: moment(n.date).format('ddd, MMMM DD, hh:mm A'),
|
||||
onClick: () => { this.disableNotification(n); },
|
||||
overflowContent:
|
||||
<div>
|
||||
<div>
|
||||
{n.content.body}
|
||||
</div>
|
||||
<div>
|
||||
<Paper style={paperStyle} zDepth={1} />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
});
|
||||
break;
|
||||
}
|
||||
notifications[n.nid] = n;
|
||||
this.setState(Object.assign(this.state, { notifications: notifications, totalNotifications: this.state.totalNotifications++ }));
|
||||
|
||||
if (this.props.onNotificationUpdate) {
|
||||
this.props.onNotificationUpdate(n, this.state.totalNotifications);
|
||||
}
|
||||
|
||||
this.forceUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
socketOpenListener(event) {
|
||||
console.log('Connected for the ID: ' + this.state.user.useruuid);
|
||||
}
|
||||
|
||||
socketCloseListener(event) {
|
||||
if (this.socket) {
|
||||
console.error('Disconnected.');
|
||||
}
|
||||
this.socket = new WebSocket(this.state.socket_url);
|
||||
|
||||
this.socketMessageListener = this.socketMessageListener.bind(this);
|
||||
this.socketOpenListener = this.socketOpenListener.bind(this);
|
||||
this.socketCloseListener = this.socketCloseListener.bind(this);
|
||||
|
||||
this.socket.addEventListener('open', this.socketOpenListener);
|
||||
this.socket.addEventListener('message', this.socketMessageListener);
|
||||
this.socket.addEventListener('close', this.socketCloseListener);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { layoutBoxed, navCollapsed, navBehind, fixedHeader, sidebarWidth, theme } = this.props;
|
||||
let materialUITheme;
|
||||
switch (theme) {
|
||||
case 'gray':
|
||||
materialUITheme = grayTheme;
|
||||
break;
|
||||
case 'dark':
|
||||
materialUITheme = darkTheme;
|
||||
break;
|
||||
default:
|
||||
materialUITheme = lightTheme;
|
||||
}
|
||||
|
||||
return (
|
||||
<MuiThemeProvider muiTheme={getMuiTheme(materialUITheme)}>
|
||||
<ReactMaterialUiNotifications
|
||||
desktop={false}
|
||||
transitionName={{
|
||||
leave: 'dummy',
|
||||
leaveActive: 'fadeOut',
|
||||
appear: 'dummy',
|
||||
appearActive: 'zoomInUp'
|
||||
}}
|
||||
transitionAppear={true}
|
||||
transitionLeave={true}
|
||||
/>
|
||||
</MuiThemeProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Notification.propTypes = {
|
||||
user: PropTypes.object.isRequired,
|
||||
onRideUpdate: PropTypes.func,
|
||||
onNotificationUpdate: PropTypes.func,
|
||||
}
|
||||
0
src/components/Shared/index.js
Normal file
0
src/components/Shared/index.js
Normal file
155
src/components/Sidenav/SidenavContent.js
Normal file
155
src/components/Sidenav/SidenavContent.js
Normal file
@@ -0,0 +1,155 @@
|
||||
import React from 'react';
|
||||
import { Link, hashHistory } from 'react-router';
|
||||
import FlatButton from 'material-ui/FlatButton';
|
||||
import 'jquery-slimscroll/jquery.slimscroll.min';
|
||||
|
||||
import {
|
||||
loggedUser,
|
||||
planScheduler,
|
||||
providerScheduler,
|
||||
} from 'utils/authorization';
|
||||
|
||||
class SidebarContent extends React.Component {
|
||||
state = {
|
||||
user: {
|
||||
user_uuid: '',
|
||||
},
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const nav = this.nav;
|
||||
const $nav = $(nav);
|
||||
|
||||
// scroll
|
||||
$nav.slimscroll({
|
||||
height: '100%'
|
||||
});
|
||||
|
||||
// Append icon to submenu
|
||||
// Append to child `div`
|
||||
$nav.find('.prepend-icon').children('div').prepend('<i class="material-icons">keyboard_arrow_right</i>');
|
||||
|
||||
// AccordionNav
|
||||
const slideTime = 250;
|
||||
const $lists = $nav.find('ul').parent('li');
|
||||
$lists.append('<i class="material-icons icon-has-ul">arrow_drop_down</i>');
|
||||
const $As = $lists.children('a');
|
||||
|
||||
// Disable A link that has ul
|
||||
$As.on('click', event => event.preventDefault());
|
||||
|
||||
// Accordion nav
|
||||
$nav.on('click', (e) => {
|
||||
const target = e.target;
|
||||
const $parentLi = $(target).closest('li'); // closest, insead of parent, so it still works when click on i icons
|
||||
if (!$parentLi.length) return; // return if doesn't click on li
|
||||
const $subUl = $parentLi.children('ul');
|
||||
|
||||
// let depth = $subUl.parents().length; // but some li has no sub ul, so...
|
||||
const depth = $parentLi.parents().length + 1;
|
||||
|
||||
// filter out all elements (except target) at current depth or greater
|
||||
const allAtDepth = $nav.find('ul').filter(function () {
|
||||
if ($(this).parents().length >= depth && this !== $subUl.get(0)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
allAtDepth.slideUp(slideTime).closest('li').removeClass('open');
|
||||
|
||||
// Toggle target
|
||||
if ($parentLi.has('ul').length) {
|
||||
$parentLi.toggleClass('open');
|
||||
}
|
||||
|
||||
$subUl.stop().slideToggle(slideTime);
|
||||
});
|
||||
|
||||
|
||||
// HighlightActiveItems
|
||||
const $links = $nav.find('a');
|
||||
const currentLocation = hashHistory.getCurrentLocation();
|
||||
function highlightActive(pathname) {
|
||||
const path = `#${pathname}`;
|
||||
|
||||
$links.each((i, link) => {
|
||||
const $link = $(link);
|
||||
const $li = $link.parent('li');
|
||||
const href = $link.attr('href');
|
||||
// console.log(href);
|
||||
|
||||
if ($li.hasClass('active')) {
|
||||
$li.removeClass('active');
|
||||
}
|
||||
if (path.indexOf(href) === 0) {
|
||||
$li.addClass('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
highlightActive(currentLocation.pathname);
|
||||
hashHistory.listen((location) => {
|
||||
highlightActive(location.pathname);
|
||||
});
|
||||
|
||||
const user = JSON.parse(localStorage.getItem('loggedUser'));
|
||||
if (user) {
|
||||
this.setState(Object.assign(this.state, { user: user }));
|
||||
}
|
||||
}
|
||||
render() {
|
||||
|
||||
return (
|
||||
<ul className="nav" ref={(c) => { this.nav = c; }}>
|
||||
<li>
|
||||
<FlatButton href="#/app/form"><i className="nav-icon material-icons cyan-text text-lighter-4">directions_car</i><span className="nav-text">Rides</span></FlatButton>
|
||||
<ul>
|
||||
<li><FlatButton className="prepend-icon" href={"#/app/form/steppers/" + this.state.user.useruuid}><span>Book Rides</span></FlatButton></li>
|
||||
<li><FlatButton className="prepend-icon" href="#/app/table/rides"><span>Manage Rides</span></FlatButton></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<FlatButton href="#/app/chart"><i className="nav-icon material-icons">people_outline</i><span className="nav-text">Members</span></FlatButton>
|
||||
<ul>
|
||||
<li><FlatButton className="prepend-icon" href="#/app/page/eligibility"><span>Verify Eligibility</span></FlatButton></li>
|
||||
<li><FlatButton className="prepend-icon" href="#/app/table/members"><span>Manage Members</span></FlatButton></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<FlatButton href="#/app/table/visits"><i className="nav-icon material-icons">schedule</i><span className="nav-text">Visits</span></FlatButton>
|
||||
</li>
|
||||
{/* <li>
|
||||
<FlatButton href="#/app/table/message"><i className="nav-icon material-icons">mail_outline</i><span className="nav-text">Message Center</span></FlatButton>
|
||||
</li> */}
|
||||
|
||||
<li>
|
||||
<FlatButton href="#/app/chart"><i className="nav-icon material-icons">settings</i><span className="nav-text">Manage</span></FlatButton>
|
||||
<ul>
|
||||
{!loggedUser.anyOf(planScheduler, providerScheduler) &&
|
||||
<li>
|
||||
<FlatButton href="#/app/table/authorizedusers"><i className="nav-icon material-icons">people</i><span className="nav-text">Authorized Users</span></FlatButton>
|
||||
</li>
|
||||
}
|
||||
<li>
|
||||
<FlatButton href="#/app/table/organizations"><i className="nav-icon material-icons">assignment</i><span className="nav-text">Organizations</span></FlatButton>
|
||||
</li>
|
||||
<li>
|
||||
<FlatButton href="#/app/table/provider"><i className="nav-icon material-icons">local_hospital</i><span className="nav-text">Participating Providers</span></FlatButton>
|
||||
</li>
|
||||
<li>
|
||||
<FlatButton href={"#/app/authorizedusers/" + this.state.user.useruuid}><i className="nav-icon material-icons">account_circle</i><span className="nav-text">Profile</span></FlatButton>
|
||||
</li>
|
||||
{/* <li>
|
||||
<FlatButton href="#/app/dashboard"><i className="nav-icon material-icons">equalizer</i><span className="nav-text">Reports</span></FlatButton>
|
||||
</li> */}
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li className="nav-divider" />
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SidebarContent;
|
||||
107
src/components/Sidenav/index.js
Normal file
107
src/components/Sidenav/index.js
Normal file
@@ -0,0 +1,107 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import classnames from 'classnames';
|
||||
import { Link, hashHistory } from 'react-router';
|
||||
import APPCONFIG from 'constants/Config';
|
||||
import {
|
||||
toggleCollapsedNav
|
||||
} from '../../actions';
|
||||
import SidenavContent from './SidenavContent';
|
||||
|
||||
class Sidebar extends React.Component {
|
||||
|
||||
componentDidMount() {
|
||||
// AutoCloseMobileNav
|
||||
const $body = $('#body');
|
||||
|
||||
if (APPCONFIG.AutoCloseMobileNav) {
|
||||
hashHistory.listen((location) => {
|
||||
setTimeout(() => {
|
||||
$body.removeClass('sidebar-mobile-open');
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onToggleCollapsedNav = (e) => {
|
||||
const val = !this.props.navCollapsed;
|
||||
const { handleToggleCollapsedNav } = this.props;
|
||||
handleToggleCollapsedNav(val);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { navCollapsed, colorOption } = this.props;
|
||||
let toggleIcon = null;
|
||||
if (navCollapsed) {
|
||||
toggleIcon = <i className="material-icons">radio_button_unchecked</i>;
|
||||
} else {
|
||||
toggleIcon = <i className="material-icons">radio_button_checked</i>;
|
||||
}
|
||||
|
||||
return (
|
||||
<nav
|
||||
className={classnames('app-sidebar', {
|
||||
'bg-color-light': ['31', '32', '33', '34', '35', '36'].indexOf(colorOption) >= 0,
|
||||
'bg-color-dark': ['31', '32', '33', '34', '35', '36'].indexOf(colorOption) < 0
|
||||
})}
|
||||
>
|
||||
<section
|
||||
className={classnames('sidebar-header', {
|
||||
'bg-color-dark': ['11', '31'].indexOf(colorOption) >= 0,
|
||||
'bg-color-light': colorOption === '21',
|
||||
'bg-color-primary': ['12', '22', '32'].indexOf(colorOption) >= 0,
|
||||
'bg-color-success': ['13', '23', '33'].indexOf(colorOption) >= 0,
|
||||
'bg-color-info': ['14', '24', '34'].indexOf(colorOption) >= 0,
|
||||
'bg-color-warning': ['15', '25', '35'].indexOf(colorOption) >= 0,
|
||||
'bg-color-danger': ['16', '26', '36'].indexOf(colorOption) >= 0
|
||||
})}
|
||||
>
|
||||
<svg className="logo-img logo-react" viewBox="0 0 3925 3525" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle className="react-dot" stroke="none" cx="1960" cy="1760" r="355" />
|
||||
<g className="react-curve" strokeWidth="170" fill="none">
|
||||
<ellipse cx="2575" cy="545" rx="715" ry="1875" transform="rotate(30)" />
|
||||
<ellipse cx="1760" cy="-1960" rx="715" ry="1875" transform="rotate(90)" />
|
||||
<ellipse cx="-815" cy="-2505" rx="715" ry="1875" transform="rotate(-210)" />
|
||||
</g>
|
||||
</svg>
|
||||
<Link to="/" className="brand">{APPCONFIG.brand}</Link>
|
||||
<a href="javascript:;" className="collapsednav-toggler" onClick={this.onToggleCollapsedNav}>
|
||||
{toggleIcon}
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<section className="sidebar-content">
|
||||
<SidenavContent />
|
||||
</section>
|
||||
|
||||
<section className="sidebar-footer">
|
||||
<ul className="nav">
|
||||
<li>
|
||||
<a target="_blank" href={APPCONFIG.productLink}>
|
||||
<i className="nav-icon material-icons">help</i>
|
||||
<span className="nav-text"><span>Help</span> & <span>Support</span></span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
navCollapsed: state.settings.navCollapsed,
|
||||
colorOption: state.settings.colorOption
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
handleToggleCollapsedNav: (isNavCollapsed) => {
|
||||
dispatch(toggleCollapsedNav(isNavCollapsed));
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(Sidebar);
|
||||
|
||||
Reference in New Issue
Block a user