efcsydney/efcsydney-roster

View on GitHub
client/src/modules/index/components/QuarterView/Desktop.js

Summary

Maintainability
A
1 hr
Test Coverage
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import ICalLink from 'react-icalendar-link';
import { connect } from 'react-redux';
import styled from 'styled-components';
import { media } from 'styled';
import moment from 'moment';
import { getCalData } from 'utils';
import { isHighlighted } from './utils';
import i18n from 'i18n';
import InlineSelect from './InlineSelect';
import IconCalendar from 'react-icons/lib/fa/calendar-plus-o';

const mapStateToProps = state => {
  const services = _.get(state, 'resource.data.services', {});
  const selectedServiceName = _.get(state, 'core.meta.category', 'english');
  const selectedService = _.find(services, { name: selectedServiceName }) || {};
  const positions = selectedService.positions || [];

  return {
    events: _.get(state.index, 'data', []),
    positions,
    selectedData: _.get(state.index, 'meta.selectedData', null),
    selectedService
  };
};
export default connect(mapStateToProps)(
  class Desktop extends Component {
    displayName = 'Desktop';
    static propTypes = {
      events: PropTypes.array,
      frequency: PropTypes.string,
      members: PropTypes.array,
      selectedData: PropTypes.object,
      selectedService: PropTypes.object
    };
    static defaultProps = {
      events: PropTypes.array,
      members: [],
      selectedData: {},
      selectedService: {}
    };
    handleCalClick = e => {
      e.stopPropagation();
    };
    handleDayClick = (e, dateString, serviceInfo) => {
      this.props.onDayClick(dateString, serviceInfo);
    };
    getTrans(key) {
      return i18n.t(`${this.displayName}.${key}`);
    }
    renderNameCells(date, members, serviceInfo) {
      const { positions, onRoleClick, selectedData } = this.props;
      const names = members.map(member => member.name);
      const isSkipService = serviceInfo.skipService || false;
      const id = serviceInfo.id || null;
      if (isSkipService) {
        const skipReason = serviceInfo.skipReason || '';
        return (
          <NameCell
            colSpan={names.length}
            onClick={e => this.handleDayClick(e, date, serviceInfo)}>
            {skipReason}
          </NameCell>
        );
      }

      const selectedDateString = selectedData
        ? moment(selectedData.day).format('YYYY-MM-DD')
        : '';
      const selectedRole = _.get(selectedData, 'role', null);
      const isSelectedDay = date === selectedDateString;
      return _.orderBy(positions, 'order', 'asc').map((position, i) => {
        const member = _.find(members, { role: position.name }) || {};
        const name = _.get(member, 'name', '');
        const roleName = _.get(member, 'role', '');
        const isSelected = isSelectedDay && selectedRole === roleName;
        return (
          <NameCell
            isSelected={isSelected}
            key={i}
            onClick={() => onRoleClick(date, position.name, name)}>
            <Text>{name}</Text>
            {isSelected && (
              <InlineSelect
                id={id}
                serviceInfo={serviceInfo}
                date={selectedDateString}
                role={selectedRole}
                names={this.props.members}
                value={name}
              />
            )}
          </NameCell>
        );
      });
    }
    renderCalendar(day, members) {
      const event = getCalData(day, members);

      return (
        <CalLink event={event}>
          <IconCalendar />
        </CalLink>
      );
    }
    renderDayRow(date, positions, matchedEvent) {
      const {
        selectedService: { frequency }
      } = this.props;
      const serviceInfo = _.get(matchedEvent, 'serviceInfo', {});
      const members = _.get(matchedEvent, 'members', []);

      positions = _.orderBy(positions, 'order', 'asc').map(({ id, name }) => {
        const member = _.find(members, { role: name }) || {};
        return {
          id: id,
          role: name,
          name: member.name || ''
        };
      });

      return (
        <Row key={date} highlighted={isHighlighted(date, frequency)}>
          <DayCell onClick={e => this.handleDayClick(e, date, serviceInfo)}>
            {moment(date).format(this.getTrans('dateFormat'))}
            {this.renderCalendar(date, members)}
          </DayCell>
          <NoteCell onClick={e => this.handleDayClick(e, date, serviceInfo)}>
            {_.get(serviceInfo, 'footnote', '')}
          </NoteCell>
          {this.renderNameCells(date, positions, serviceInfo)}
        </Row>
      );
    }
    render() {
      const { days, events, selectedService } = this.props;
      const footnoteLabel = _.get(selectedService, 'footnoteLabel', '');
      const positions = _.get(selectedService, 'positions', []);

      return (
        <Grid role="grid">
          <thead>
            <Row>
              <Header>
                <Text>{this.getTrans('gridCanton')}</Text>
              </Header>
              <Header>
                <Text>{footnoteLabel}</Text>
              </Header>
              {_.orderBy(positions, 'order', 'asc').map((position, i) => (
                <Header key={i}>
                  <Text>{position.name}</Text>
                </Header>
              ))}
            </Row>
          </thead>
          <tbody>
            {days.map(day => {
              const matchedEvent = _.find(events, { date: day });
              return this.renderDayRow(day, positions, matchedEvent);
            })}
          </tbody>
        </Grid>
      );
    }
  }
);

const Text = styled.span`
  overflow: hidden;
  white-space: nowrap;
  &:empty:before {
    color: #ccc;
    content: '-';
  }
  ${media.print`
    white-space: normal;
    line-height: 1.2;
  `};
`;
const Grid = styled.table`
  border-collapse: collapse;
  border-left: solid 1px #f0f3f8;
  border-radius: 0 0 8px 8px;
  margin: 0;
  padding: 0;
  table-layout: fixed;
  min-width: 100%;
  ${media.print`
    font-size: 11px;
    min-width: 0;
    width: 100%;
  `};
`;
const Cell = styled.td`
  border: solid 1px #eee;
  border-width: 0 1px 0 0;
  padding: 10px;
  text-align: center;
  white-space: nowrap;
  width: ${props => props.width};
  &:empty:before {
    padding: 0;
    color: #ccc;
    content: '-';
  }
  ${media.print`
    font-size: 11px;
    line-height: 1.3;
    padding: 8px 4px;
    white-space: normal;
  `};
`;
const Header = styled(Cell)`
  background-color: #eee;
  border-bottom: solid 1px #dadada;
  border-top: solid 1px #dadada;
  color: #666;
  font-weight: bold;
  text-align: center;
  ${media.print`
    font-size: 11px;
  `};
`;
const DayCell = styled(Cell)`
  border-right: solid 1px #eee;
  color: #666;
  cursor: pointer;
  font-weight: bold;
  overflow: visible;
  text-align: right;
  ${media.print`
    text-align: center;
    white-space: nowrap;
  `};
`;
const NameCell = styled(Cell)`
  cursor: pointer;
  position: relative;
  &[colspan] {
    border-color: #eee;
    border-width: 1px 1px 1px 0;
  }
  ${props => props.isSelected && `z-index: 1`};
`;
const NoteCell = styled(NameCell)`
  line-height: 1.2;
  min-width: 75px;
  max-width: 120px;
  overflow: hidden;
  padding: 5px 8px;
  text-overflow: ellipsis;
  white-space: normal;
  font-size: 12px;
`;
const Row = styled.tr`
  width: 100%;
  &:nth-child(odd) {
    background-color: #f8f8f8;
  }
  &:nth-child(odd) ${DayCell} {
    background-color: #eee;
  }
  &:nth-child(even) ${DayCell} {
    background-color: #f8f8f8;
  }
  ${NameCell}, ${NoteCell} {
    background-color: ${props =>
      props.highlighted ? '#ffc !important' : 'transparent !important'};
    color: ${props => (props.highlighted ? '#333' : '#666')};
  }
  &:last-child {
    border-radius: 0 0 8px 8px;
  }
  &:last-child ${DayCell} {
    border-bottom: none;
    border-bottom-left-radius: 8px;
  }
  &:last-child ${NameCell}:last-child {
    border-bottom: none;
    border-bottom-right-radius: 8px;
  }
  ${media.print`
    ${NameCell}, ${NoteCell} {
      background-color: transparent;
      color: #666;
    }
  `};
`;
const CalLink = styled(ICalLink)`
  margin-left: 5px;
  &:link,
  &:visited {
    color: #666;
  }
`;