import * as React from "react"; import axios from "axios"; import { error as log, success, init as logInit } from "../toast/toast"; import { AuthState } from "../auth/interfaces"; import { prettyPrintApiErrors, attachToRoot } from "../util"; import { API } from "../api"; import { Session } from "../session"; import { FrontPageState, SetterCB } from "./interfaces"; import { Row, Col } from "../ui/index"; import { LoginProps, Login } from "./login"; import { ForgotPassword, ForgotPasswordProps } from "./forgot_password"; import { ResendVerification } from "./resend_verification"; import { CreateAccount } from "./create_account"; import { Content } from "../constants"; import { LaptopSplash } from "./laptop_splash"; import { TermsCheckbox } from "./terms_checkbox"; import { get } from "lodash"; import { t } from "../i18next_wrapper"; export const attachFrontPage = () => attachToRoot(FrontPage, {}); const showFor = (size: string[], extraClass?: string): string => { const ALL_SIZES = ["xs", "sm", "md", "lg", "xl"]; const HIDDEN_SIZES = ALL_SIZES.filter(x => !size.includes(x)); const classNames = HIDDEN_SIZES.map(x => "hidden-" + x); if (extraClass) { classNames.push(extraClass); } return classNames.join(" "); }; export interface PartialFormEvent { currentTarget: { checked: boolean; defaultValue: string; value: string; } } /** Set value for front page state field (except for "activePanel"). */ export const setField = (name: keyof Omit, cb: SetterCB) => (event: PartialFormEvent) => { const state: Partial = {}; switch (name) { // Booleans case "agreeToTerms": case "registrationSent": state[name] = event.currentTarget.checked; break; // all others (string) default: state[name] = event.currentTarget.value; } cb(state); }; export class FrontPage extends React.Component<{}, Partial> { constructor(props: {}) { super(props); this.state = { registrationSent: false, regEmail: "", regName: "", regPassword: "", regConfirmation: "", email: "", loginPassword: "", agreeToTerms: false, activePanel: "login" }; } componentDidMount() { if (Session.fetchStoredToken()) { window.location.assign("/app/controls"); } logInit(); API.setBaseUrl(API.fetchBrowserLocation()); this.setState({}); } submitLogin = (e: React.FormEvent<{}>) => { e.preventDefault(); const { email, loginPassword } = this.state; const payload = { user: { email, password: loginPassword } }; API.setBaseUrl(API.fetchBrowserLocation()); axios.post(API.current.tokensPath, payload) .then(resp => { Session.replaceToken(resp.data); window.location.assign("/app/controls"); }).catch((error: Error) => { switch (get(error, "response.status")) { case 451: // TOS was updated; User must agree to terms. window.location.assign("/tos_update"); break; case 403: // User did not click verification email link. log(t("Account Not Verified")); this.setState({ activePanel: "resendVerificationEmail" }); break; default: log(prettyPrintApiErrors(error as {})); } this.setState({ loginPassword: "" }); }); } submitRegistration = (e: React.FormEvent<{}>) => { e.preventDefault(); const { regEmail, regName, regPassword, regConfirmation, agreeToTerms } = this.state; const form = { user: { name: regName, email: regEmail, password: regPassword, password_confirmation: regConfirmation, agree_to_terms: agreeToTerms } }; axios.post(API.current.usersPath, form).then(() => { const m = "Almost done! Check your email for the verification link."; success(t(m), t("Success")); this.setState({ registrationSent: true }); }).catch(error => { log(prettyPrintApiErrors(error)); }); } toggleForgotPassword = () => this.setState({ activePanel: "forgotPassword" }); submitForgotPassword = (e: React.FormEvent) => { e.preventDefault(); const { email } = this.state; const data = { email }; axios.post(API.current.passwordResetPath, data) .then(() => { success(t("Email has been sent."), t("Forgot Password")); this.setState({ activePanel: "login" }); }).catch(error => { let errorMessage = prettyPrintApiErrors(error); if (errorMessage.toLowerCase().includes("not found")) { errorMessage = `That email address is not associated with an account.`; } log(t(errorMessage)); }); } handleFormUpdate: SetterCB = (state) => this.setState(state); maybeRenderTos = () => { if (globalConfig.TOS_URL) { return ; } } loginPanel = () => { const props: LoginProps = { email: this.state.email || "", onEmailChange: setField("email", this.handleFormUpdate), onLoginPasswordChange: setField("loginPassword", this.handleFormUpdate), onToggleForgotPassword: this.toggleForgotPassword, onSubmit: this.submitLogin, }; return ; } forgotPasswordPanel = () => { const goBack = () => this.setState({ activePanel: "login" }); const props: ForgotPasswordProps = { onGoBack: goBack, onSubmit: this.submitForgotPassword, email: this.state.email || "", onEmailChange: setField("email", this.handleFormUpdate), }; return ; } resendVerificationPanel = () => { const goBack = () => this.setState({ activePanel: "login" }); return { success(t(Content.VERIFICATION_EMAIL_RESENT)); goBack(); }} no={() => { log(t(Content.VERIFICATION_EMAIL_RESEND_ERROR)); goBack(); }} email={this.state.email || ""} />; } activePanel = () => { switch (this.state.activePanel) { case "forgotPassword": return this.forgotPasswordPanel(); case "resendVerificationEmail": return this.resendVerificationPanel(); case "login": default: return this.loginPanel(); } } defaultContent() { return

{t("Welcome to the")}
  {t("FarmBot Web App")}

{t("Setup, customize, and control FarmBot from your")}   {t("computer")} {t("tablet")} {t("smartphone")}

this.state[key]} set={(key, val) => this.setState({ [key]: val })}> {this.maybeRenderTos()}
; } render() { return Session.fetchStoredToken() ?
: this.defaultContent(); } }