client/src/modules/index/components/QuarterView/Mobile.js
import _ from 'lodash';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import ICalLink from 'react-icalendar-link';
import moment from 'moment';
import { getCalData } from 'utils';
import IconEdit from 'react-icons/lib/fa/pencil';
import i18n from 'i18n';
import EditRole from '../EditRole';
import { isHighlighted } from './utils';
import IconCalendar from 'react-icons/lib/fa/calendar-plus-o';
const mapStateToProps = state => {
const {
meta: { isEditingRole }
} = state.index;
const services = _.get(state, 'resource.data.services', {});
const selectedServiceName = _.get(state, 'core.meta.category', 'english');
const selectedService = _.find(services, { name: selectedServiceName }) || {};
return {
events: _.get(state.index, 'data', []),
isEditingRole,
selectedData: _.get(state.index, 'meta.selectedData', null),
selectedService
};
};
export default connect(mapStateToProps)(
class Mobile extends Component {
displayName = 'Mobile';
static propTypes = {
events: PropTypes.array,
frequency: PropTypes.string,
members: PropTypes.array,
onDayClick: PropTypes.func,
onRoleClick: PropTypes.func,
selectedData: PropTypes.object,
selectedService: PropTypes.object
};
static defaultProps = {
events: [],
members: [],
onDayClick: () => {},
onRoleClick: () => {},
selectedData: {},
selectedService: {}
};
getTrans(key) {
return i18n.t(`${this.displayName}.${key}`);
}
scrollToThisWeek = () => {
const highlightedEl = document.getElementById('highlighted');
if (highlightedEl) {
highlightedEl.scrollIntoView();
}
};
handleBackTopClick = e => {
e.preventDefault();
const navbarEl = document.getElementById('navbar');
if (navbarEl) {
navbarEl.scrollIntoView();
}
};
componentWillReceiveProps(nextProps) {
const isInitialLoad =
!this.props.events.length && nextProps.events.length;
if (isInitialLoad) {
setTimeout(() => {
this.scrollToThisWeek();
}, 500);
}
}
renderRolesList(day, roles, members, serviceInfo) {
const { onDayClick, onRoleClick, selectedService } = this.props;
const positions = _.get(selectedService, 'positions', []);
const formattedDate = moment(day).format('YYYY-MM-DD');
if (serviceInfo.skipService) {
return (
<Row>
<ExcludeReason
onClick={() => onDayClick(formattedDate, serviceInfo)}>
{serviceInfo.skipReason}
</ExcludeReason>
</Row>
);
}
return _.orderBy(positions, 'order', 'asc').map((position, i) => {
const role = position.name;
const member = _.find(members, { role }) || {};
const name = member.name || '';
return (
<Row key={i}>
<Role>{role}</Role>
<Name onClick={() => onRoleClick(day, role, name)}>{name}</Name>
</Row>
);
});
}
render() {
const {
days,
events,
selectedService: { frequency },
isEditingRole,
onDayClick
} = this.props;
return (
<Grid>
{days.map(day => {
const matchedEvent = _.find(events, { date: day });
const members = _.get(matchedEvent, 'members', []);
const serviceInfo = _.get(matchedEvent, 'serviceInfo', {});
const roles = members.map(member => member.role);
const calData = getCalData(day, roles, members);
const formattedDate = day;
const highlighted = isHighlighted(day, frequency);
return (
<Day
key={day}
highlighted={highlighted}
id={highlighted ? 'highlighted' : undefined}>
<Header>
<Label
onClick={() => {
onDayClick(formattedDate, serviceInfo);
}}>
{moment(day).format(this.getTrans('dateFormat'))}
</Label>
<SettingLink
onClick={() => {
onDayClick(formattedDate, serviceInfo);
}}>
<IconEdit />
</SettingLink>
{serviceInfo.footnote && (
<Footnote
onClick={() => {
onDayClick(formattedDate, serviceInfo);
}}>
{serviceInfo.footnote}
</Footnote>
)}
{ICalLink.isSupported && (
<Action>
<CalLink event={calData}>
<IconCalendar /> Add to Calendar
</CalLink>
</Action>
)}
</Header>
{this.renderRolesList(day, roles, members, serviceInfo)}
</Day>
);
})}
{isEditingRole && (
<EditRole isOpen={true} members={this.props.members} />
)}
<BackTopLink onClick={this.handleBackTopClick}>Top</BackTopLink>
<BottomDateBarSpace />
</Grid>
);
}
}
);
const Grid = styled.div`
border-left: solid 1px #f0f3f8;
border-radius: 0 0 4px 4px;
flex-wrap: wrap;
margin: 0;
padding: 0;
width: 100%;
`;
const Cell = styled.span`
color: #666;
flex-grow: 1;
overflow: hidden;
padding: 0;
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
width: auto;
`;
const ExcludeReason = styled(Cell)`
cursor: pointer;
color: #ef4b3c;
padding: 10px;
`;
const Header = styled(Cell)`
cursor: pointer;
align-items: center;
background-color: #eee;
border: solid 1px #dadada;
border-width: 1px 0;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
display: flex;
flex-wrap: nowrap;
font-weight: bold;
justify-content: space-between;
overflow: visible;
padding: 0;
width: auto;
a {
display: inline-block;
}
`;
const Role = styled(Cell)`
background: #eee;
border-right: solid 1px #dadada;
font-weight: bold;
overflow: hidden;
padding: 10px;
text-align: right;
text-overflow: ellipsis;
width: 30%;
`;
const Name = styled(Cell)`
cursor: pointer;
padding: 10px;
text-align: left;
width: 70%;
`;
const Row = styled.div`
display: flex;
flex-direction: row;
&:nth-child(odd) {
background-color: #f8f8f8;
}
&:nth-child(odd) ${Role} {
background-color: #eee;
}
&:nth-child(even) ${Role} {
background-color: #f8f8f8;
}
&:last-child {
border-radius: 0 0 8px 8px;
}
&:last-child ${Role} {
border-bottom: none;
}
`;
const Day = styled.div`
background: #fff;
cursor: pointer;
color: ${props => (props.highlighted ? '#333' : '#666')};
display: flex;
flex-wrap: no-wrap;
flex-direction: column;
${Name}, ${ExcludeReason} {
background-color: ${props => (props.highlighted ? '#ffc' : 'transparent')};
border-bottom: solid 1px #eee;
}
&:last-child {
border-radius: 0 0 8px 8px;
}
&:last-child ${Row}:last-child ${Role} {
border-bottom: none;
border-bottom-left-radius: 8px;
}
&:last-child ${Row}:last-child ${Name} {
border-bottom: none;
border-bottom-right-radius: 8px;
}
`;
const Label = styled.span`
cursor: pointer;
color: #000;
flex: 0;
font-size: 15px;
line-height: 1.2;
padding: 10px 5px 10px 10px;
text-align: left;
`;
const SettingLink = styled.span`
color: #333;
cursor: pointer;
font-size: 13px;
line-height: 0;
padding: 10px 5px 10px 0;
flex: 0;
`;
const Footnote = styled.span`
cursor: pointer;
flex: 2;
font-size: 11px;
margin-left: 5px;
overflow: hidden;
text-align: left;
text-overflow: ellipsis;
vertical-align: baseline;
`;
const Action = styled.span`
flex: 0;
margin-left: auto;
margin-right: 5px;
`;
const BottomDateBarSpace = styled.div`
position: absolute;
height: 50px;
width: 100%;
`;
const BackTopLink = styled.a.attrs({ href: '#top' })`
border-radius: 50%;
display: flex;
font-size: 16px;
font-weight: bold;
align-items: center;
justify-content: center;
position: fixed;
z-index: 1;
bottom: 50px;
right: 10px;
background: #4285f4;
&:link,
&:visited {
color: #fff;
}
width: 48px;
height: 48px;
`;
const CalLink = styled(ICalLink)`
margin-left: 5px;
&:link,
&:visited {
color: #666;
}
* {
vertical-align: middle;
}
`;