165 lines
5.2 KiB
TypeScript
165 lines
5.2 KiB
TypeScript
import * as React from "react";
|
|
import { connect } from "react-redux";
|
|
import { Row } from "../../ui/index";
|
|
import { mapStateToProps } from "./map_state_to_props";
|
|
import {
|
|
FarmEventProps, CalendarOccurrence, FarmEventState
|
|
} from "../interfaces";
|
|
import moment from "moment";
|
|
import { Content } from "../../constants";
|
|
import { Panel, DesignerNavTabs } from "../panel_header";
|
|
import { Link } from "../../link";
|
|
import {
|
|
DesignerPanel, DesignerPanelContent, DesignerPanelTop
|
|
} from "../plants/designer_panel";
|
|
import {
|
|
EmptyStateWrapper, EmptyStateGraphic
|
|
} from "../../ui/empty_state_wrapper";
|
|
import { some, uniq, map, sortBy } from "lodash";
|
|
import { t } from "../../i18next_wrapper";
|
|
|
|
const filterSearch = (term: string) => (item: CalendarOccurrence) =>
|
|
item.heading.toLowerCase().includes(term)
|
|
|| (item.subheading && item.subheading.toLowerCase().includes(term));
|
|
|
|
export class PureFarmEvents
|
|
extends React.Component<FarmEventProps, FarmEventState> {
|
|
state: FarmEventState = { searchTerm: "" };
|
|
get searchTerm() { return this.state.searchTerm.toLowerCase(); }
|
|
|
|
resetCalendar = () => {
|
|
this.setState({ searchTerm: "" });
|
|
const panel = document.querySelector(".farm-events");
|
|
if (panel) { panel.scrollTo(0, 0); }
|
|
}
|
|
|
|
innerRows = (items: CalendarOccurrence[]) => {
|
|
|
|
return sortBy(items, x => x.sortKey)
|
|
.filter(filterSearch(this.searchTerm))
|
|
.map((occur, index) => {
|
|
const url = `/app/designer/events/`
|
|
+ (occur.id || "UNSAVED_EVENT").toString();
|
|
const heading = occur.subheading
|
|
? occur.subheading
|
|
: occur.heading;
|
|
const subHeading = occur.subheading
|
|
? <p style={{ color: "gray" }}> {occur.heading} </p>
|
|
: <p />;
|
|
|
|
return <div
|
|
className="farm-event-data-block"
|
|
key={`${occur.sortKey}.${index}`}>
|
|
<div className="farm-event-data-time">
|
|
{occur.timeStr}
|
|
</div>
|
|
<div className="farm-event-data-executable">
|
|
{heading}
|
|
{subHeading}
|
|
</div>
|
|
<Link to={url}>
|
|
<i className="fa fa-pencil-square-o edit-icon" />
|
|
</Link>
|
|
</div>;
|
|
});
|
|
}
|
|
|
|
renderCalendarRowsInYear(year: number) {
|
|
return this.props.calendarRows.filter((day) => {
|
|
return day.year == year;
|
|
})
|
|
.filter(item => !this.searchTerm ||
|
|
some(item.items.map(filterSearch(this.searchTerm))))
|
|
.map(item => {
|
|
return <div className="farm-event" key={item.sortKey}>
|
|
<div className="farm-event-date">
|
|
<div className="farm-event-date-month">
|
|
{item.month}
|
|
</div>
|
|
<div className="farm-event-date-day">
|
|
<b>{item.day}</b>
|
|
</div>
|
|
</div>
|
|
<div className="farm-event-data">
|
|
{this.innerRows(item.items)}
|
|
</div>
|
|
</div>;
|
|
});
|
|
}
|
|
|
|
renderCalendarRows() {
|
|
const years = uniq(map(this.props.calendarRows, "year"));
|
|
return years.map(year => {
|
|
return <div key={moment(year, "YY").unix()}
|
|
className="farm-event-calendar-rows">
|
|
<div className="farm-event-year">
|
|
20{year}
|
|
</div>
|
|
{this.renderCalendarRowsInYear(year)}
|
|
</div>;
|
|
});
|
|
}
|
|
|
|
/** FarmEvents will generate some very unexpected results if the user has
|
|
* not set a timezone for the bot (defaults to 0 UTC offset, which could be
|
|
* far from user's local time). */
|
|
tzwarning = () => {
|
|
return <DesignerPanelContent panelName={"farm-event"}>
|
|
<Row>
|
|
</Row>
|
|
|
|
<div className="farm-events">
|
|
<h2>Timezone Required</h2>
|
|
<p>
|
|
{t(Content.SET_TIMEZONE_HEADER)}
|
|
</p>
|
|
<p>
|
|
<Link to="/app/device">{t(Content.SET_TIMEZONE_BODY)}</Link>
|
|
</p>
|
|
</div>
|
|
</DesignerPanelContent>;
|
|
};
|
|
|
|
normalContent = () => {
|
|
return <div className="farm-event-panel-normal-content">
|
|
<DesignerPanelTop
|
|
panel={Panel.FarmEvents}
|
|
linkTo={"/app/designer/events/add"}
|
|
title={t("Add event")}
|
|
noIcon={true}>
|
|
<i className="fa fa-calendar" onClick={this.resetCalendar} />
|
|
<input
|
|
value={this.state.searchTerm}
|
|
onChange={e => this.setState({ searchTerm: e.currentTarget.value })}
|
|
placeholder={t("Search events...")} />
|
|
</DesignerPanelTop>
|
|
<DesignerPanelContent panelName={"farm-event"}>
|
|
|
|
<div className="farm-events">
|
|
<EmptyStateWrapper
|
|
notEmpty={this.props.calendarRows.length > 0}
|
|
title={t("No events scheduled.")}
|
|
text={t(Content.NOTHING_SCHEDULED)}
|
|
colorScheme="events"
|
|
graphic={EmptyStateGraphic.farm_events}>
|
|
{this.renderCalendarRows()}
|
|
</EmptyStateWrapper>
|
|
</div>
|
|
</DesignerPanelContent>
|
|
</div>;
|
|
};
|
|
|
|
render() {
|
|
return <DesignerPanel panelName={"farm-event"} panel={Panel.FarmEvents}>
|
|
<DesignerNavTabs />
|
|
{this.props.timezoneIsSet ? this.normalContent() : this.tzwarning()}
|
|
</DesignerPanel>;
|
|
}
|
|
}
|
|
|
|
/** This is intentional. It is not a hack or a work around.
|
|
* It avoids mocking `connect` in unit tests.
|
|
* See testing pattern noted here: https://github.com/airbnb/enzyme/issues/98
|
|
*/
|
|
export const FarmEvents = connect(mapStateToProps)(PureFarmEvents);
|