mirror of
https://github.com/prymitive/karma
synced 2026-05-11 03:46:48 +00:00
Latest react-datepicker no longer accepts moment instances, it uses plain js Date objects instead. Migrate code to pass props as Date.
278 lines
8.2 KiB
JavaScript
278 lines
8.2 KiB
JavaScript
import React, { Component } from "react";
|
|
import PropTypes from "prop-types";
|
|
|
|
import { observable, action } from "mobx";
|
|
import { observer } from "mobx-react";
|
|
|
|
import moment from "moment";
|
|
|
|
import DatePicker from "react-datepicker";
|
|
|
|
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
|
import { Duration } from "./Duration";
|
|
import { HourMinute } from "./HourMinute";
|
|
|
|
import "./index.scss";
|
|
|
|
const OffsetBadge = ({ startDate, endDate, prefixLabel }) => {
|
|
const days = endDate.diff(startDate, "days");
|
|
const hours = endDate.diff(startDate, "hours") % 24;
|
|
const minutes = endDate.diff(startDate, "minutes") % 60;
|
|
|
|
return (
|
|
<span className="badge badge-light">
|
|
{days <= 0 && hours <= 0 && minutes <= 0 ? "now" : prefixLabel}
|
|
{days > 0 ? `${days}d ` : null}
|
|
{hours > 0 ? `${hours}h ` : null}
|
|
{minutes > 0 ? `${minutes}m ` : null}
|
|
</span>
|
|
);
|
|
};
|
|
OffsetBadge.propTypes = {
|
|
startDate: PropTypes.instanceOf(moment).isRequired,
|
|
endDate: PropTypes.instanceOf(moment).isRequired,
|
|
prefixLabel: PropTypes.string.isRequired
|
|
};
|
|
|
|
const Tab = ({ title, active, onClick }) => (
|
|
<li className="nav-item">
|
|
<span
|
|
className={`nav-link cursor-pointer ${active ? "active" : ""}`}
|
|
onClick={onClick}
|
|
>
|
|
{title}
|
|
</span>
|
|
</li>
|
|
);
|
|
Tab.propTypes = {
|
|
title: PropTypes.node.isRequired,
|
|
active: PropTypes.bool,
|
|
onClick: PropTypes.func.isRequired
|
|
};
|
|
|
|
const TabNames = Object.freeze({
|
|
Start: "start",
|
|
End: "end",
|
|
Duration: "duration"
|
|
});
|
|
|
|
const TabContentStart = observer(({ silenceFormStore }) => {
|
|
return (
|
|
<div className="d-flex flex-sm-row flex-column justify-content-around mx-3 mt-2 ">
|
|
<DatePicker
|
|
inline
|
|
todayButton={"Today"}
|
|
minDate={moment()
|
|
.second(0)
|
|
.toDate()}
|
|
selected={silenceFormStore.data.startsAt.toDate()}
|
|
onChange={val => {
|
|
silenceFormStore.data.startsAt = moment(val);
|
|
silenceFormStore.data.verifyStarEnd();
|
|
}}
|
|
/>
|
|
<HourMinute
|
|
dateValue={silenceFormStore.data.startsAt}
|
|
onHourInc={() => silenceFormStore.data.incStart(60)}
|
|
onHourDec={() => silenceFormStore.data.decStart(60)}
|
|
onMinuteInc={() => silenceFormStore.data.incStart(1)}
|
|
onMinuteDec={() => silenceFormStore.data.decStart(1)}
|
|
/>
|
|
</div>
|
|
);
|
|
});
|
|
|
|
const TabContentEnd = observer(({ silenceFormStore }) => {
|
|
return (
|
|
<div className="d-flex flex-sm-row flex-column justify-content-around mx-3 mt-2 ">
|
|
<DatePicker
|
|
inline
|
|
todayButton={"Today"}
|
|
minDate={moment()
|
|
.second(0)
|
|
.add(1, "minutes")
|
|
.toDate()}
|
|
selected={silenceFormStore.data.endsAt.toDate()}
|
|
onChange={val => {
|
|
silenceFormStore.data.endsAt = moment(val);
|
|
silenceFormStore.data.verifyStarEnd();
|
|
}}
|
|
/>
|
|
<HourMinute
|
|
dateValue={silenceFormStore.data.endsAt}
|
|
onHourInc={() => silenceFormStore.data.incEnd(60)}
|
|
onHourDec={() => silenceFormStore.data.decEnd(60)}
|
|
onMinuteInc={() => silenceFormStore.data.incEnd(1)}
|
|
onMinuteDec={() => silenceFormStore.data.decEnd(1)}
|
|
/>
|
|
</div>
|
|
);
|
|
});
|
|
|
|
// calculate value for duration increase button using a goal step
|
|
const CalculateChangeValueUp = (currentValue, step) => {
|
|
// if current value is less than step (but >0) then use 1
|
|
if (currentValue > 0 && currentValue < step) {
|
|
return 1;
|
|
}
|
|
// otherwise use step or a value that moves current value to the next step
|
|
return step - (currentValue % step);
|
|
};
|
|
|
|
// calculate value for duration decrease button using a goal step
|
|
const CalculateChangeValueDown = (currentValue, step) => {
|
|
// if current value is less than step (but >0) then use 1
|
|
if (currentValue > 0 && currentValue < step) {
|
|
return 1;
|
|
}
|
|
// otherwise use step or a value that moves current value to the next step
|
|
return currentValue % step || step;
|
|
};
|
|
|
|
const TabContentDuration = observer(({ silenceFormStore }) => {
|
|
return (
|
|
<div className="d-flex flex-sm-row flex-column justify-content-around mt-2 mx-3">
|
|
<Duration
|
|
label="days"
|
|
value={silenceFormStore.data.toDuration.days}
|
|
onInc={() => silenceFormStore.data.incEnd(60 * 24)}
|
|
onDec={() => silenceFormStore.data.decEnd(60 * 24)}
|
|
/>
|
|
<Duration
|
|
label="hours"
|
|
value={silenceFormStore.data.toDuration.hours}
|
|
onInc={() => silenceFormStore.data.incEnd(60)}
|
|
onDec={() => silenceFormStore.data.decEnd(60)}
|
|
/>
|
|
<Duration
|
|
label="minutes"
|
|
value={silenceFormStore.data.toDuration.minutes}
|
|
onInc={() =>
|
|
silenceFormStore.data.incEnd(
|
|
CalculateChangeValueUp(silenceFormStore.data.toDuration.minutes, 5)
|
|
)
|
|
}
|
|
onDec={() =>
|
|
silenceFormStore.data.decEnd(
|
|
CalculateChangeValueDown(
|
|
silenceFormStore.data.toDuration.minutes,
|
|
5
|
|
)
|
|
)
|
|
}
|
|
/>
|
|
</div>
|
|
);
|
|
});
|
|
TabContentDuration.propTypes = {
|
|
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired
|
|
};
|
|
|
|
const DateTimeSelect = observer(
|
|
class DateTimeSelect extends Component {
|
|
static propTypes = {
|
|
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired
|
|
};
|
|
|
|
tab = observable(
|
|
{
|
|
current: TabNames.Duration,
|
|
setStart() {
|
|
this.current = TabNames.Start;
|
|
},
|
|
setEnd() {
|
|
this.current = TabNames.End;
|
|
},
|
|
setDuration() {
|
|
this.current = TabNames.Duration;
|
|
},
|
|
timeNow: moment().seconds(0),
|
|
updateTimeNow() {
|
|
this.timeNow = moment().seconds(0);
|
|
}
|
|
},
|
|
{
|
|
setStart: action.bound,
|
|
setEnd: action.bound,
|
|
setDuration: action.bound,
|
|
updateTimeNow: action.bound
|
|
}
|
|
);
|
|
|
|
componentDidMount() {
|
|
this.tab.updateTimeNow();
|
|
this.nowUpdateTimer = setInterval(this.tab.updateTimeNow, 30 * 1000);
|
|
}
|
|
componentWillUnmount() {
|
|
clearInterval(this.nowUpdateTimer);
|
|
this.nowUpdateTimer = null;
|
|
}
|
|
|
|
render() {
|
|
const { silenceFormStore } = this.props;
|
|
|
|
return (
|
|
<React.Fragment>
|
|
<ul className="nav nav-tabs nav-fill">
|
|
<Tab
|
|
title={
|
|
<React.Fragment>
|
|
<span className="mr-1">Starts</span>
|
|
<OffsetBadge
|
|
prefixLabel="in "
|
|
startDate={this.tab.timeNow}
|
|
endDate={silenceFormStore.data.startsAt}
|
|
/>
|
|
</React.Fragment>
|
|
}
|
|
active={this.tab.current === TabNames.Start}
|
|
onClick={this.tab.setStart}
|
|
/>
|
|
<Tab
|
|
title={
|
|
<React.Fragment>
|
|
<span className="mr-1">Ends</span>
|
|
<OffsetBadge
|
|
prefixLabel="in "
|
|
startDate={this.tab.timeNow}
|
|
endDate={silenceFormStore.data.endsAt}
|
|
/>
|
|
</React.Fragment>
|
|
}
|
|
active={this.tab.current === TabNames.End}
|
|
onClick={this.tab.setEnd}
|
|
/>
|
|
<Tab
|
|
title={
|
|
<React.Fragment>
|
|
<span className="mr-1">Duration</span>
|
|
<OffsetBadge
|
|
prefixLabel=""
|
|
startDate={silenceFormStore.data.startsAt}
|
|
endDate={silenceFormStore.data.endsAt}
|
|
/>
|
|
</React.Fragment>
|
|
}
|
|
active={this.tab.current === TabNames.Duration}
|
|
onClick={this.tab.setDuration}
|
|
/>
|
|
</ul>
|
|
<div className="tab-content mb-3">
|
|
{this.tab.current === TabNames.Duration ? (
|
|
<TabContentDuration silenceFormStore={silenceFormStore} />
|
|
) : null}
|
|
{this.tab.current === TabNames.Start ? (
|
|
<TabContentStart silenceFormStore={silenceFormStore} />
|
|
) : null}
|
|
{this.tab.current === TabNames.End ? (
|
|
<TabContentEnd silenceFormStore={silenceFormStore} />
|
|
) : null}
|
|
</div>
|
|
</React.Fragment>
|
|
);
|
|
}
|
|
}
|
|
);
|
|
|
|
export { DateTimeSelect, TabContentStart, TabContentEnd, TabContentDuration };
|