Farmbot-Web-App/frontend/messages/cards.tsx

355 lines
12 KiB
TypeScript

import React from "react";
import { t } from "../i18next_wrapper";
import {
AlertCardProps, AlertCardTemplateProps, FirmwareMissingProps,
SeedDataMissingProps, SeedDataMissingState, TourNotTakenProps,
CommonAlertCardProps,
DismissAlertProps,
Bulletin,
BulletinAlertComponentState
} from "./interfaces";
import { formatLogTime } from "../logs";
import {
FlashFirmwareBtn
} from "../devices/components/fbos_settings/firmware_hardware_status";
import { DropDownItem, Row, Col, FBSelect, docLink, Markdown } from "../ui";
import { Content } from "../constants";
import { TourList } from "../help/tour_list";
import { splitProblemTag } from "./alerts";
import { destroy } from "../api/crud";
import {
isFwHardwareValue, FIRMWARE_CHOICES_DDI, getFirmwareChoices
} from "../devices/components/firmware_hardware_support";
import { updateConfig } from "../devices/actions";
import { fetchBulletinContent, seedAccount } from "./actions";
import { startCase } from "lodash";
import { DevSettings } from "../account/dev/dev_support";
import { Session } from "../session";
export const AlertCard = (props: AlertCardProps) => {
const { alert, timeSettings, findApiAlertById, dispatch } = props;
const commonProps = { alert, timeSettings, findApiAlertById, dispatch };
switch (alert.problem_tag) {
case "farmbot_os.firmware.missing":
return <FirmwareMissing {...commonProps}
apiFirmwareValue={props.apiFirmwareValue} />;
case "api.seed_data.missing":
return <SeedDataMissing {...commonProps}
dispatch={dispatch} />;
case "api.tour.not_taken":
return <TourNotTaken {...commonProps}
dispatch={dispatch} />;
case "api.user.not_welcomed":
return <UserNotWelcomed {...commonProps} />;
case "api.documentation.unread":
return <DocumentationUnread {...commonProps} />;
case "api.bulletin.unread":
return <BulletinAlert {...commonProps} />;
case "api.demo_account.in_use":
return <DemoAccount {...commonProps} />;
default:
return <UnknownAlert {...commonProps} />;
}
};
const dismissAlert = (props: DismissAlertProps) => () =>
(props.id && props.findApiAlertById && props.dispatch) &&
props.dispatch(destroy(props.findApiAlertById(props.id)));
const timeOk = (timestamp: number) => timestamp > 1550000000;
const AlertCardTemplate = (props: AlertCardTemplateProps) => {
const { alert, findApiAlertById, dispatch } = props;
return <div className={
`problem-alert ${props.className} priority-${props.alert.priority}`}>
<div className="problem-alert-title">
<i className={`fa fa-${props.iconName || "exclamation-triangle"}`} />
<h3>{t(props.title)}</h3>
{timeOk(alert.created_at) &&
<p>{formatLogTime(alert.created_at, props.timeSettings)}</p>}
{alert.id && <i className="fa fa-times"
onClick={dismissAlert({ id: alert.id, findApiAlertById, dispatch })} />}
</div>
<div className="problem-alert-content">
<Markdown>{t(props.message)}</Markdown>
{props.children}
</div>
</div>;
};
const ICON_LOOKUP: { [x: string]: string } = {
"info": "info-circle",
"success": "check-square",
"warn": "exclamation-triangle",
};
class BulletinAlert
extends React.Component<CommonAlertCardProps, BulletinAlertComponentState> {
state: BulletinAlertComponentState = { bulletin: undefined, no_content: false };
componentDidMount() {
fetchBulletinContent(this.props.alert.slug)
.then(bulletin => bulletin
? this.setState({ bulletin })
: this.setState({ no_content: true }));
}
get bulletinData(): Bulletin {
return this.state.bulletin || {
content: this.state.no_content ? t("Unable to load content.")
: t("Loading..."),
href: undefined,
href_label: undefined,
type: "info",
slug: this.props.alert.slug,
title: undefined,
};
}
render() {
const { content, href, href_label, type, title } = this.bulletinData;
return <AlertCardTemplate
alert={this.props.alert}
className={"bulletin-alert"}
title={title || startCase(this.props.alert.slug)}
iconName={ICON_LOOKUP[type] || "info-circle"}
message={t(content)}
timeSettings={this.props.timeSettings}
dispatch={this.props.dispatch}
findApiAlertById={this.props.findApiAlertById}>
{href && <a className="link-button fb-button green"
href={href} target="_blank"
title={t("Open link in a new tab")}>
{href_label || t("Find out more")}
</a>}
</AlertCardTemplate>;
}
}
const UnknownAlert = (props: CommonAlertCardProps) => {
const { problem_tag, slug, priority } = props.alert;
const { author, noun, verb } = splitProblemTag(problem_tag);
return <AlertCardTemplate
alert={props.alert}
className={"unknown-alert"}
title={`${t(noun)}: ${t(verb)} (${t(author)})`}
message={t("Unknown problem of priority {{priority}} ({{slug}}).",
{ priority, slug })}
timeSettings={props.timeSettings}
dispatch={props.dispatch}
findApiAlertById={props.findApiAlertById} />;
};
const FirmwareChoiceTable = () =>
<table className="firmware-hardware-choice-table">
<thead>
<tr>
<th>{t("FarmBot Version")}</th>
<th>{t("Electronics Board")}</th>
<th>{t("Firmware Name")}</th>
</tr>
</thead>
<tbody>
<tr>
<td>{"Genesis v1.2"}</td>
<td>{"RAMPS"}</td>
<td><code>{FIRMWARE_CHOICES_DDI["arduino"].label}</code></td>
</tr>
<tr>
<td>{"Genesis v1.3"}</td>
<td>{"Farmduino"}</td>
<td><code>{FIRMWARE_CHOICES_DDI["farmduino"].label}</code></td>
</tr>
<tr>
<td>{"Genesis v1.4"}</td>
<td>{"Farmduino"}</td>
<td><code>{FIRMWARE_CHOICES_DDI["farmduino_k14"].label}</code></td>
</tr>
<tr>
<td>{"Genesis v1.5"}</td>
<td>{"Farmduino"}</td>
<td><code>{FIRMWARE_CHOICES_DDI["farmduino_k15"].label}</code></td>
</tr>
<tr>
<td>{"Express v1.0"}</td>
<td>{"Farmduino"}</td>
<td><code>{FIRMWARE_CHOICES_DDI["express_k10"].label}</code></td>
</tr>
</tbody>
</table>;
export const changeFirmwareHardware = (dispatch: Function | undefined) =>
(ddi: DropDownItem) => {
if (isFwHardwareValue(ddi.value)) {
dispatch?.(updateConfig({ firmware_hardware: ddi.value }));
}
};
const FirmwareMissing = (props: FirmwareMissingProps) =>
<AlertCardTemplate
alert={props.alert}
className={"firmware-missing-alert"}
title={t("Your device has no firmware")}
message={t(Content.FIRMWARE_MISSING)}
timeSettings={props.timeSettings}
dispatch={props.dispatch}
findApiAlertById={props.findApiAlertById}>
<Row>
<FirmwareChoiceTable />
<Col xs={4}>
<label>{t("Choose Firmware")}</label>
</Col>
<Col xs={5}>
<FBSelect
key={props.apiFirmwareValue}
list={getFirmwareChoices()}
customNullLabel={t("Select one")}
selectedItem={props.apiFirmwareValue
? FIRMWARE_CHOICES_DDI[props.apiFirmwareValue] : undefined}
onChange={changeFirmwareHardware(props.dispatch)} />
</Col>
<Col xs={3} hidden={true}>
<FlashFirmwareBtn
apiFirmwareValue={props.apiFirmwareValue}
botOnline={true} />
</Col>
</Row>
</AlertCardTemplate>;
const SEED_DATA_OPTIONS = (): DropDownItem[] => [
{ label: "Genesis v1.2", value: "genesis_1.2" },
{ label: "Genesis v1.3", value: "genesis_1.3" },
{ label: "Genesis v1.4", value: "genesis_1.4" },
{ label: "Genesis v1.4 XL", value: "genesis_xl_1.4" },
...(DevSettings.futureFeaturesEnabled() ? [
{ label: "Express v1.0", value: "express_1.0" },
{ label: "Express v1.0 XL", value: "express_xl_1.0" },
] : []),
{ label: "Custom Bot", value: "none" },
];
const SEED_DATA_OPTIONS_DDI: Record<string, DropDownItem> = {};
SEED_DATA_OPTIONS().map(ddi => SEED_DATA_OPTIONS_DDI[ddi.value] = ddi);
class SeedDataMissing
extends React.Component<SeedDataMissingProps, SeedDataMissingState> {
state: SeedDataMissingState = { selection: "" };
get dismiss() {
const { alert, findApiAlertById, dispatch } = this.props;
return dismissAlert({ id: alert.id, findApiAlertById, dispatch });
}
render() {
return <AlertCardTemplate
alert={this.props.alert}
className={"seed-data-missing-alert"}
title={t("Choose your FarmBot")}
message={t(Content.SEED_DATA_SELECTION)}
timeSettings={this.props.timeSettings}
dispatch={this.props.dispatch}
findApiAlertById={this.props.findApiAlertById}
iconName={"check-square"}>
<Row>
<Col xs={4}>
<label>{t("Choose your FarmBot")}</label>
</Col>
<Col xs={5}>
<FBSelect
key={this.state.selection}
list={SEED_DATA_OPTIONS()}
selectedItem={SEED_DATA_OPTIONS_DDI[this.state.selection]}
onChange={seedAccount(this.dismiss)} />
</Col>
</Row>
</AlertCardTemplate>;
}
}
const TourNotTaken = (props: TourNotTakenProps) =>
<AlertCardTemplate
alert={props.alert}
className={"tour-not-taken-alert"}
title={t("Take a guided tour")}
message={t(Content.TAKE_A_TOUR)}
timeSettings={props.timeSettings}
dispatch={props.dispatch}
findApiAlertById={props.findApiAlertById}
iconName={"info-circle"}>
<p>{t("Choose a tour to begin")}:</p>
<TourList dispatch={props.dispatch} />
</AlertCardTemplate>;
const UserNotWelcomed = (props: CommonAlertCardProps) =>
<AlertCardTemplate
alert={props.alert}
className={"user-not-welcomed-alert"}
title={t("Welcome to the FarmBot Web App")}
message={t(Content.WELCOME)}
timeSettings={props.timeSettings}
dispatch={props.dispatch}
findApiAlertById={props.findApiAlertById}
iconName={"info-circle"}>
<p>
{t("You're currently viewing the")} <b>{t("Message Center")}</b>.
{" "}{t(Content.MESSAGE_CENTER_WELCOME)}
</p>
<p>
{t(Content.MESSAGE_DISMISS)}
</p>
</AlertCardTemplate>;
const DocumentationUnread = (props: CommonAlertCardProps) =>
<AlertCardTemplate
alert={props.alert}
className={"documentation-unread-alert"}
title={t("Learn more about the app")}
message={t(Content.READ_THE_DOCS)}
timeSettings={props.timeSettings}
dispatch={props.dispatch}
findApiAlertById={props.findApiAlertById}
iconName={"info-circle"}>
<p>
{t("Head over to")}
&nbsp;<a href={docLink()} target="_blank"
title={t("Open documentation in a new tab")}>
{t("software.farm.bot")}
</a>
&nbsp;{t("to get started")}.
</p>
<a className="link-button fb-button green"
href={docLink()} target="_blank"
title={t("Open documentation in a new tab")}>
{t("Read the docs")}
</a>
</AlertCardTemplate>;
const DemoAccount = (props: CommonAlertCardProps) =>
<AlertCardTemplate
alert={props.alert}
className={"demo-account-alert"}
title={t("You're currently using a demo account")}
message={t(Content.DEMO_ACCOUNT)}
timeSettings={props.timeSettings}
dispatch={props.dispatch}
findApiAlertById={props.findApiAlertById}
iconName={"info-circle"}>
<p>
<i>{t("Please note:")}</i>&nbsp;
{t(Content.DEMO_NOTE)}
</p>
<p>
{t(Content.MAKE_A_REAL_ACCOUNT)}&nbsp;
<a href={"https://my.farm.bot"} target="_blank"
onClick={() => Session.clear()}
title={"my.farm.bot"}>
{"my.farm.bot"}
</a>.
</p>
<a className="link-button fb-button green"
href={"https://my.farm.bot"} target="_blank"
onClick={() => Session.clear()}
title={t("Make a real account")}>
{t("Make a real account")}
</a>
</AlertCardTemplate>;