/* eslint-disable sonarjs/no-duplicate-string */
/* eslint-disable sonarjs/cognitive-complexity */
/* eslint-disable no-underscore-dangle */
import React, { Component } from 'react';
import {
getDateForCalendar,
getDateForFilter,
getYesterday,
getDateFromString,
getDateForIntervalFilter,
getCountryNewDate,
} from '../utils/common';
import SVGComponent from './SVG/SVGComponent';
import { SVG_ICONS, MANIPULATIONS } from '../constants';
import Calendar from './Calendar';
import validateFields from '../utils/validation';
let ACTIVE_FILTER = [];
/**
* @module IntervalDates
*/
/**
* @typedef {object} props
* @property {Array} fields
* @property {object} filter
* @property {object} defaultFilter
* @property {Function} changeFilter
*/
export default class IntervalDates extends Component {
constructor(props) {
super(props);
/**
* @member {object}
* @description Properties has _ prefix to avoid matching filter keys
* with component properties.
* @property {boolean} isOpen
* @property {string} _fromDate
* @property {string} _toDate
* @property {string} _fromTime
* @property {string} _toTime
* @property {object} filter
* @property {number} activeFilter
* @property {boolean} isCustomOpen
*/
const yesterday = getDateForCalendar(getYesterday(this.props.selectedTimezone));
const today = getDateForCalendar(getCountryNewDate(this.props.selectedTimezone));
ACTIVE_FILTER = ['30#minutes ago', '5#hours ago', '24#hours ago', `Today#${today}`, `Yesterday#${yesterday}`];
const fromDate = props.filter[props.fields[0]];
const toDate = props.filter[props.fields[1]];
this.state = this.getInintalState(fromDate, toDate, props);
}
/**
* Update state on clear filter
*
* @param {object} prevProps
* @returns {void}
*/
componentDidUpdate(prevProps) {
if (
this.props.filter[this.props.fields[0]] !== prevProps.filter[prevProps.fields[0]] ||
this.props.filter[this.props.fields[1]] !== prevProps.filter[prevProps.fields[1]]
) {
const fromDate = this.props.filter[this.props.fields[0]];
const toDate = this.props.filter[this.props.fields[1]];
// eslint-disable-next-line react/no-did-update-set-state
this.setState(this.getInintalState(fromDate, toDate, this.props));
}
}
/**
* Calculate state for parsed interval date
*
* @function
* @param {string} from
* @param {string} to
* @param {object} props
* @returns {object}
*/
getInintalState = (from, to, props) => {
let splitChar = ' ';
if (from?.includes('T')) {
splitChar = 'T';
}
const fromDate = from ? from.split(splitChar) : null;
const toDate = to ? to.split(splitChar) : null;
return {
isOpen: false,
_fromDate: fromDate ? getCountryNewDate(this.props.selectedTimezone, fromDate[0]) : '',
_fromTime: fromDate ? fromDate[1] : '',
_toDate: toDate ? getCountryNewDate(this.props.selectedTimezone, toDate[0]) : '',
_toTime: toDate ? toDate[1] : '',
filter: {
_fromTime: fromDate ? fromDate[1] : '',
_toTime: toDate ? toDate[1] : '',
[props.fields[0]]: fromDate
? getDateForFilter(getCountryNewDate(this.props.selectedTimezone, fromDate[0]))
: '',
[props.fields[1]]: toDate ? getDateForFilter(getCountryNewDate(this.props.selectedTimezone, toDate[0])) : '',
},
activeFilter: 3,
isCustomOpen: false,
errorMgs: {},
};
};
/**
* Toggle calendar
*
* @function
* @returns {void}
*/
toggleCalendarPopup = () => {
this.setState({
isOpen: !this.state.isOpen,
isCustomOpen: false,
errorMgs: {},
});
};
/**
* Set default filter
*
* @function
* @returns {void}
*/
reset = () => {
const fromDate = this.props.defaultFilter[this.props.fields[0]];
const toDate = this.props.defaultFilter[this.props.fields[1]];
this.setState(this.getInintalState(fromDate, toDate, this.props));
this.applay(MANIPULATIONS.RESET_DATE_AND_TIME);
};
/**
* Discard changes
*
* @function
* @returns {void}
*/
cancel = () => {
if (this.state.errorMgs?.fromDate) return;
this.setState({
isOpen: false,
filter: {
_fromTime: this.state._fromTime,
_toTime: this.state._toTime,
[this.props.fields[0]]: this.state._fromDate ? getDateForFilter(this.state._fromDate) : '',
[this.props.fields[1]]: this.state._toDate ? getDateForFilter(this.state._toDate) : '',
},
activeFilter: 4,
isCustomOpen: false,
errorMgs: {},
});
};
/**
* Adjust Date and Time According to Custom Option
*
* @param {string} type
* @function
*/
adjustDateAndTimeAccordingToCustomOption = (type) => {
let fromDate = this.state.filter[this.props.fields[0]];
let toDate = this.state.filter[this.props.fields[1]];
if (type === MANIPULATIONS.RESET_DATE_AND_TIME) {
fromDate = '';
toDate = '';
}
this.props.changeFilter(fromDate ? `${fromDate} ${this.state.filter._fromTime}` : '', this.props.fields[0]);
this.props.changeFilter(toDate ? `${toDate} ${this.state.filter._toTime}` : '', this.props.fields[1]);
const validationParams = [
{
label: 'To date',
fieldName: 'toDate',
value: `${toDate} ${this.state.filter._toTime}`,
rules: ['mandatory', 'greaterThan'],
comparatorValue: `${fromDate} ${this.state.filter._fromTime}`,
},
];
// allow search by date for last 3 months only if this variable is present
if (this.props.restrictSearchTo3Months) {
const allowSearchFromDate = new Date();
allowSearchFromDate.setMonth(allowSearchFromDate.getMonth() - 3);
validationParams.push({
label: 'From date',
fieldName: 'fromDate',
value: `${fromDate} ${this.state.filter._fromTime}`,
rules: ['greaterThan'],
comparatorValue: `${getDateForFilter(allowSearchFromDate)}`,
});
}
validateFields(validationParams)
.then(() => {
this.setState({
isOpen: false,
_fromDate: fromDate ? getCountryNewDate(this.props.selectedTimezone, fromDate) : '',
_toDate: toDate ? getCountryNewDate(this.props.selectedTimezone, toDate) : '',
_fromTime: this.state.filter._fromTime,
_toTime: this.state.filter._toTime,
activeFilter: 4,
isCustomOpen: false,
});
})
.catch((errorMgs) => {
this.setState({
errorMgs,
});
});
};
/**
* Change dates
*
* @function
* @param {string} type
* @returns {void}
*/
adjustDateAndTimeAccordingToCalendarPopup = (type) => {
const filter = ACTIVE_FILTER[this.state.activeFilter].split('#');
let from = '';
let to = '';
let date;
if (filter[1] === 'minutes ago') {
const minutes = filter[0];
date = getCountryNewDate(this.props.selectedTimezone);
to = getDateForIntervalFilter(date);
date.setMinutes(date.getMinutes() - minutes);
from = getDateForIntervalFilter(date);
} else if (filter[1] === 'hours ago') {
const hours = filter[0];
date = getCountryNewDate(this.props.selectedTimezone);
to = getDateForIntervalFilter(date);
date.setHours(date.getHours() - hours);
from = getDateForIntervalFilter(date);
} else {
date = getDateFromString(filter[1]);
from = `${date} 00:00`;
to = `${date} 23:59`;
}
if (type === MANIPULATIONS.RESET_DATE_AND_TIME) {
from = '';
to = '';
}
this.props.changeFilter(from, this.props.fields[0]);
this.props.changeFilter(to, this.props.fields[1]);
this.setState(this.getInintalState(from, to, this.props));
};
/**
* Change dates
*
* @function
* @param {string} type
* @returns {void}
*/
applay = (type) => {
if (this.state.isCustomOpen) {
this.adjustDateAndTimeAccordingToCustomOption(type);
} else {
this.adjustDateAndTimeAccordingToCalendarPopup(type);
}
};
/**
* Change quick filter
*
* @function
* @param {number} index
* @returns {void}
*/
changeFilter = (index) => {
this.setState({
activeFilter: index,
});
};
/**
* Change custom date filter
*
* @function
* @param {string} fieldValue
* @param {string} field
* @returns {void}
*/
changeFilterDate = (fieldValue, field) => {
const temp = this.state.filter;
temp[field] = fieldValue;
this.setState({
filter: temp,
errorMgs: {},
});
};
/**
* Toggle custom filters
*
* @function
* @returns {void}
*/
toggleCustomFilter = () => {
if (!this.state.isCustomOpen && !this.state.filter._fromTime && !this.state.filter._toTime) {
const today = getDateFromString(getDateForCalendar(getCountryNewDate(this.props.selectedTimezone)));
const updatedFilters = { ...this.state.filter };
updatedFilters._fromTime = '00:00';
updatedFilters._toTime = '23:59';
if (this.props.dateFilterFields) {
this.props.dateFilterFields.forEach((field) => {
updatedFilters[field] = today;
});
}
this.setState({
isCustomOpen: !this.state.isCustomOpen,
filter: updatedFilters,
});
} else {
this.setState({
isCustomOpen: !this.state.isCustomOpen,
});
}
};
/**
* Enable / Disable Apply Button for date and time filter
*
* @function
* @returns {boolean}
*/
disableApplyBtn = () => {
const isDateEmpty = this.props.dateFilterFields?.some((l) => !this.state.filter[l]);
return this.state.isCustomOpen && (isDateEmpty || !this.state.filter._fromTime || !this.state.filter._toTime);
};
/**
* Render
*
* @returns {view}
*/
render() {
const fromDate = getDateForCalendar(this.state._fromDate);
const toDate = getDateForCalendar(this.state._toDate);
return (
<>
<div className="main calendar">
<div className="calendar__wrapper" onClick={this.toggleCalendarPopup}>
<div className="calendar__input-date">
<div className="calendar__input-date-label">From</div>
<div className="calendar__input-date-value">{fromDate || '-'}</div>
<div className="calendar__input-date-value">{this.state._fromTime}</div>
</div>
<div className="calendar__input-date">
<div className="calendar__input-date-label">To</div>
<div className="calendar__input-date-value">{toDate || '-'}</div>
<div className="calendar__input-date-value">{this.state._toTime}</div>
</div>
</div>
<i className="icon-calendar" onClick={this.toggleCalendarPopup} />
<div className={`main calendar__popup ${this.state.isOpen && 'open'} `}>
<div className="p-15">
<div className="calendar__popup-heading">
<h3>Select time range</h3>
<SVGComponent
className="icon-xxs calendar__popup-close"
src={`${SVG_ICONS.utility}#close`}
onClick={this.toggleCalendarPopup}
/>
</div>
</div>
<div className="d-flex">
<div>
<div className="calendar__popup-option">
{ACTIVE_FILTER.map((f, i) => {
const filter = f.split('#');
return (
<div
key={f}
className={`calendar__popup-row ${
this.state.activeFilter === i && !this.state.isCustomOpen ? 'active' : ''
}`}
onClick={this.changeFilter.bind(this, i)}
>
<div className="mr-4 w-30">{filter[0]}</div>
<span>{filter[1]}</span>
</div>
);
})}
</div>
<div className="calendar__popup-row mt-3 calendar-trigger" onClick={this.toggleCustomFilter}>
<span>Enter custom range</span>
<SVGComponent className="icon-xxs next-icon ml-4" src={`${SVG_ICONS.pagination}#arrow`} />
</div>
</div>
<div className={`calendar__popup-custom-field ${this.state.isCustomOpen && 'open'}`}>
<div className="mb-2">From</div>
<Calendar
field={this.props.fields[0]}
filter={this.state.filter}
defaultFilter={this.props.defaultFilter}
changeFilter={this.changeFilterDate}
className="calendar__popup-input-style has-calendar mt-2"
sub
selectedTimezone={this.props.selectedTimezone}
/>
{this.state.errorMgs.fromDate}
<input
type="time"
className="calendar__popup-input-style mt-2"
value={this.state.filter._fromTime}
onChange={(e) => {
this.changeFilterDate(e.target.value, '_fromTime');
}}
/>
<div className="mb-2 mt-2">To</div>
<Calendar
field={this.props.fields[1]}
filter={this.state.filter}
defaultFilter={this.props.defaultFilter}
changeFilter={this.changeFilterDate}
className="calendar__popup-input-style has-calendar mt-2"
sub
selectedTimezone={this.props.selectedTimezone}
/>
{this.state.errorMgs.toDate}
<input
type="time"
className="calendar__popup-input-style mt-2"
value={this.state.filter._toTime}
onChange={(e) => {
this.changeFilterDate(e.target.value, '_toTime');
}}
/>
</div>
</div>
<div className="p-15">
<div className="calendar__popup-button">
<button type="button" className="btn btn-secondary mr-auto" onClick={this.reset}>
Reset
</button>
<button type="button" className="btn btn-secondary ml-10" onClick={this.cancel}>
Cancel
</button>
<button
type="button"
className="btn btn-primary ml-10"
onClick={this.applay}
disabled={this.disableApplyBtn()}
>
Apply
</button>
</div>
</div>
</div>
</div>
{this.state.isOpen && <div className="overlay-mask bg-grey open" />}
</>
);
}
}