# All files / composer/components SlotPicker.jsx

0% Statements 0/36
0% Branches 0/34
0% Functions 0/8
0% Lines 0/33

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121

     /**
     * Simple slot picker. Works with 12/24h formats.
     *
     * This component is controlled through the slot prop, and uses the onChange
     * callback in props to let its parent know of any change.
     */

    import React from 'react';
    import PropTypes from 'prop-types';
    import moment from 'moment-timezone';
    import Select from '../components/Select';

    class SlotPicker extends React.Component {
      static propTypes = {
        metaData: PropTypes.object,
        shouldUse24hTime: PropTypes.bool.isRequired,
        onChange: PropTypes.func.isRequired,
        slots: PropTypes.arrayOf(PropTypes.shape({
          isSlotFree: PropTypes.bool.isRequired,
          timestamp: PropTypes.number.isRequired,
        })).isRequired,
        metaData: PropTypes.object,
        slot: PropTypes.instanceOf(moment),
        timezone: PropTypes.string,
        className: PropTypes.string,
      };

      static defaultProps = {
        metaData: undefined,
      };

      onSlotChange = (e) => {
        const { timezone } = this.props;

        const slotTimestamp = e.target.value;
        let slotMoment = moment.unix(slotTimestamp);
        if (timezone) slotMoment = slotMoment.tz(timezone);

        this.props.onChange(slotMoment);
      };

      getHumanReadableTimeSlotLabel = (timestamp, isSlotFree) => {
        const { shouldUse24hTime, timezone } = this.props;

        let slotMoment = moment.unix(timestamp);
        if (timezone) slotMoment = slotMoment.tz(timezone);

        const humanReadableFormat = shouldUse24hTime ? 'H:mm' : 'h:mm a';
        const humanReadableTime = slotMoment.format(humanReadableFormat);

        const isTimeInFuture = slotMoment.isAfter();
        const slotPastLabel = isTimeInFuture ? '' : ' (past)';
        const slotOccupancyLabel =
          isSlotFree ? '' :
          this.isOriginalTimeOfEditedUpdate(timestamp) ? ' (occupied by this post)' :
          ' (occupied)';

        return `${humanReadableTime}${slotOccupancyLabel || slotPastLabel}`;
      };

      // Returns true if we're editing an existing update and the timestamp passed
      // matches the original scheduled time of that update (i.e. the scheduled time
      // before any changes were made to it during this MC session)
      isOriginalTimeOfEditedUpdate = (timestamp) => {
        const { metaData } = this.props;
        const isEditingUpdate = metaData && metaData.updateId !== null;
        return isEditingUpdate && timestamp === metaData.scheduledAt;
      };

      render() {
        const { slots, slot, className } = this.props;
        const EMPTY_OPTION_VALUE = 'empty';

        const slotTimestamp = slot.unix();
        const currentTimestampSeconds = Math.floor(Date.now() / 1000);
        const isSlotTimeInFuture = slotTimestamp > currentTimestampSeconds;

        const isSlotTimestampAvailable = (
          this.isOriginalTimeOfEditedUpdate(slotTimestamp) ||
          (isSlotTimeInFuture &&
            slots.some(({ timestamp, isSlotFree }) => timestamp === slotTimestamp && isSlotFree))
        );
        const selectedSlot = isSlotTimestampAvailable ? slotTimestamp : EMPTY_OPTION_VALUE;

        const hasSlots = slots.length > 0;

        const slotsWithDisabledInfo = slots.map(({ timestamp, isSlotFree }) => {
          const isTimeInFuture = timestamp > currentTimestampSeconds;
          const isDisabled =
            (!isSlotFree || !isTimeInFuture) && !this.isOriginalTimeOfEditedUpdate(timestamp);

          return { timestamp, isSlotFree, isDisabled };
        });

        const areAllOptionsDisabled = slotsWithDisabledInfo.every(({ isDisabled }) => isDisabled);

        return (
          <div className={className}>
            {hasSlots &&
              <Select value={selectedSlot} onChange={this.onSlotChange}>
                <option disabled value={EMPTY_OPTION_VALUE}>
                  {areAllOptionsDisabled ? 'Select a slot (all occupied)' : 'Select a slot' }
                </option>
                {slotsWithDisabledInfo.map(({ timestamp, isSlotFree, isDisabled }) => (
                  <option value={timestamp} disabled={isDisabled} key={timestamp}>
                    {this.getHumanReadableTimeSlotLabel(timestamp, isSlotFree)}
                  </option>
                ))}
              </Select>}

            {!hasSlots &&
              <Select disabled value="">
                <option value="">No slots this day</option>
              </Select>}
          </div>
        );
      }
    }

    export default SlotPicker;