ownership change form draft

pull/705/head
gabrielburnworth 2018-03-07 21:08:00 -08:00
parent 7bafa4bc04
commit e48e32bc7c
12 changed files with 191 additions and 14 deletions

View File

@ -230,6 +230,23 @@ a {
}
}
.change-ownership-form {
p {
padding: 1rem;
margin-left: 0.5rem;
}
.row {
margin-bottom: 1rem;
}
label {
margin-top: 0.5rem;
}
button {
margin-top: 1rem;
margin-right: 1rem;
}
}
.pin-bindings-widget {
.fa-th-large {
color: $dark_gray;

View File

@ -24,17 +24,18 @@ describe("<Devices/>", () => {
images: [],
dispatch: jest.fn(),
resources: buildResourceIndex(FAKE_RESOURCES).index,
sourceFbosConfig: jest.fn()
sourceFbosConfig: jest.fn(),
shouldDisplay: jest.fn()
});
it("renders relevant panels", () => {
const el = shallow(<Devices {...p() } />);
const el = shallow(<Devices {...p()} />);
expect(el.find(FarmbotOsSettings).length).toBe(1);
});
it("Crashes when logged out", () => {
const props = p();
props.auth = undefined;
expect(() => render(<Devices {...props } />)).toThrow();
expect(() => render(<Devices {...props} />)).toThrow();
});
});

View File

@ -27,12 +27,13 @@ describe("<FarmbotOsSettings/>", () => {
botToMqttStatus: "up",
sourceFbosConfig: (x) => {
return { value: bot.hardware.configuration[x], consistent: true };
}
},
shouldDisplay: jest.fn()
};
};
it("renders settings", () => {
const osSettings = mount(<FarmbotOsSettings {...fakeProps() } />);
const osSettings = mount(<FarmbotOsSettings {...fakeProps()} />);
expect(osSettings.find("input").length).toBe(1);
expect(osSettings.find("button").length).toBe(6);
["NAME", "TIME ZONE", "LAST SEEN", "FARMBOT OS", "CAMERA", "FIRMWARE"]
@ -41,7 +42,7 @@ describe("<FarmbotOsSettings/>", () => {
it("fetches OS release notes", async () => {
mockReleaseNoteData = { data: "intro\n\n# v6\n\n* note" };
const osSettings = await mount(<FarmbotOsSettings {...fakeProps() } />);
const osSettings = await mount(<FarmbotOsSettings {...fakeProps()} />);
await expect(axios.get).toHaveBeenCalledWith(
expect.stringContaining("RELEASE_NOTES.md"));
expect(osSettings.state().osReleaseNotes)
@ -50,7 +51,7 @@ describe("<FarmbotOsSettings/>", () => {
it("doesn't fetch OS release notes", async () => {
mockReleaseNoteData = { data: "empty notes" };
const osSettings = await mount(<FarmbotOsSettings {...fakeProps() } />);
const osSettings = await mount(<FarmbotOsSettings {...fakeProps()} />);
await expect(axios.get).toHaveBeenCalledWith(
expect.stringContaining("RELEASE_NOTES.md"));
expect(osSettings.state().osReleaseNotes)
@ -86,7 +87,7 @@ describe("<FbosDetails />", () => {
};
it("renders", () => {
const wrapper = mount(<FbosDetails {...fakeProps() } />);
const wrapper = mount(<FbosDetails {...fakeProps()} />);
["Environment: ---",
"Commit: ---",
"Target: ---",

View File

@ -157,7 +157,8 @@ export class FarmbotOsSettings
<PowerAndReset
controlPanelState={this.props.bot.controlPanelState}
dispatch={this.props.dispatch}
sourceFbosConfig={sourceFbosConfig} />
sourceFbosConfig={sourceFbosConfig}
shouldDisplay={this.props.shouldDisplay} />
</MustBeOnline>
</WidgetBody>
</form>

View File

@ -0,0 +1,41 @@
const mockDevice = {
send: jest.fn(() => { return Promise.resolve(); }),
};
jest.mock("../../../../device", () => ({
getDevice: () => (mockDevice)
}));
import * as React from "react";
import { ChangeOwnershipForm } from "../change_ownership_form";
import { mount } from "enzyme";
describe("<ChangeOwnershipForm/>", () => {
it("renders", () => {
const wrapper = mount(<ChangeOwnershipForm />);
["email", "password", "server"]
.map(string => expect(wrapper.text().toLowerCase()).toContain(string));
});
it("submits", () => {
const wrapper = mount(<ChangeOwnershipForm />);
wrapper.find("button").simulate("click");
expect(mockDevice.send).toHaveBeenCalledWith({
"kind": "rpc_request",
"args": { "label": expect.any(String) },
"body": [
{
"kind": "pair",
"args": { "label": "email", "value": "" }
},
{
"kind": "pair",
"args": { "label": "secret", "value": 0 }
},
{
"kind": "pair",
"args": { "label": "server", "value": "https://my.farm.bot" }
}
]
});
});
});

View File

@ -20,7 +20,8 @@ describe("<PowerAndReset/>", () => {
dispatch: jest.fn(x => x(jest.fn(), fakeState)),
sourceFbosConfig: (x) => {
return { value: bot.hardware.configuration[x], consistent: true };
}
},
shouldDisplay: jest.fn()
};
};

View File

@ -0,0 +1,95 @@
import * as React from "react";
import { Row, Col, BlurableInput } from "../../../ui/index";
import { t } from "i18next";
import { info, success } from "farmbot-toastr";
import { getDevice } from "../../../device";
import { rpcRequest } from "farmbot";
interface ChangeOwnershipFormState {
email: string;
password: string;
server: string;
}
export class ChangeOwnershipForm
extends React.Component<{}, ChangeOwnershipFormState> {
state = {
email: "",
password: "",
server: "https://my.farm.bot"
};
submitOwnershipChange = () => {
info(t("Sending change of ownership..."), t("Sending"));
getDevice()
.send(rpcRequest([
{ kind: "pair", args: { label: "email", value: this.state.email } },
{ kind: "pair", args: { label: "secret", value: 0 } },
{ kind: "pair", args: { label: "server", value: this.state.server } }
// tslint:disable-next-line:no-any
] as any))
.then(() => {
success(t("Received change of ownership."));
});
}
render() {
return <div className={"change-ownership-form"}>
<Row>
<p>
{t("Change the account FarmBot is connected to.")}
</p>
<Col xs={4}>
<label>
{t("Email")}
</label>
</Col>
<Col xs={8}>
<BlurableInput
allowEmpty={true}
onCommit={e => this.setState({ email: e.currentTarget.value })}
name="email"
value={this.state.email}
type="email" />
</Col>
</Row>
<Row>
<Col xs={4}>
<label>
{t("Password")}
</label>
</Col>
<Col xs={8}>
<BlurableInput
allowEmpty={true}
onCommit={e => this.setState({ password: e.currentTarget.value })}
name="password"
value={this.state.password}
type="password" />
</Col>
</Row>
<Row>
<Col xs={4}>
<label>
{t("Server")}
</label>
</Col>
<Col xs={8}>
<BlurableInput
allowEmpty={true}
onCommit={e => this.setState({ server: e.currentTarget.value })}
name="server"
value={this.state.server}
type="text" />
</Col>
</Row>
<Row>
<button
className={"fb-button gray"}
onClick={this.submitOwnershipChange}>
{t("submit")}
</button>
</Row>
</div>;
}
}

View File

@ -1,4 +1,5 @@
import { SourceFbosConfig, BotState, ControlPanelState } from "../../interfaces";
import { ShouldDisplay } from "../../../sequences/interfaces";
export interface AutoSyncRowProps {
dispatch: Function;
@ -20,6 +21,7 @@ export interface PowerAndResetProps {
controlPanelState: ControlPanelState;
dispatch: Function;
sourceFbosConfig: SourceFbosConfig;
shouldDisplay: ShouldDisplay;
}
export interface FactoryResetRowProps {

View File

@ -1,14 +1,15 @@
import * as React from "react";
import { Header } from "../hardware_settings/header";
import { Collapse } from "@blueprintjs/core";
import { Collapse, Popover, Position } from "@blueprintjs/core";
import { RestartRow } from "./restart_row";
import { ShutdownRow } from "./shutdown_row";
import { FactoryResetRow } from "./factory_reset_row";
import { PowerAndResetProps } from "./interfaces";
import { ChangeOwnershipForm } from "./change_ownership_form";
import { t } from "i18next";
export function PowerAndReset(props: PowerAndResetProps) {
const { dispatch, sourceFbosConfig } = props;
const { dispatch, sourceFbosConfig, shouldDisplay } = props;
const { power_and_reset } = props.controlPanelState;
return <section>
<div style={{ fontSize: "1px" }}>
@ -24,6 +25,14 @@ export function PowerAndReset(props: PowerAndResetProps) {
<FactoryResetRow
dispatch={dispatch}
sourceFbosConfig={sourceFbosConfig} />
{shouldDisplay("change_ownership") &&
<Popover position={Position.BOTTOM_LEFT}>
<p className={"release-notes-button"}>
{t("Change Ownership")}&nbsp;
<i className="fa fa-caret-down" />
</p>
<ChangeOwnershipForm />
</Popover>}
</Collapse>
</section>;
}

View File

@ -65,7 +65,8 @@ export class Devices extends React.Component<Props, {}> {
bot={this.props.bot}
botToMqttLastSeen={botToMqttLastSeen}
botToMqttStatus={botToMqttStatus}
sourceFbosConfig={this.props.sourceFbosConfig} />
sourceFbosConfig={this.props.sourceFbosConfig}
shouldDisplay={this.props.shouldDisplay} />
<ConnectivityPanel
status={this.props.deviceAccount.specialStatus}
onRefresh={this.refresh}

View File

@ -18,6 +18,7 @@ import { WD_ENV } from "../farmware/weed_detector/remote_env/interfaces";
import { ConnectionStatus, ConnectionState, NetworkState } from "../connectivity/interfaces";
import { IntegerSize } from "../util";
import { WebAppConfig } from "../config_storage/web_app_configs";
import { ShouldDisplay } from "../sequences/interfaces";
export interface Props {
userToApi: ConnectionStatus | undefined;
@ -30,6 +31,7 @@ export interface Props {
dispatch: Function;
resources: ResourceIndex;
sourceFbosConfig: SourceFbosConfig;
shouldDisplay: ShouldDisplay;
}
export type SourceFbosConfig = (config: ConfigurationName) =>
@ -121,6 +123,7 @@ export interface FarmbotOsProps {
botToMqttLastSeen: string;
dispatch: Function;
sourceFbosConfig: SourceFbosConfig;
shouldDisplay: ShouldDisplay;
}
export interface FarmbotOsState {

View File

@ -2,16 +2,20 @@ import { Everything } from "../interfaces";
import { Props } from "./interfaces";
import {
selectAllImages,
getDeviceAccountSettings
getDeviceAccountSettings,
maybeGetDevice
} from "../resources/selectors";
import { sourceFbosConfigValue } from "./components/source_fbos_config_value";
import { getFbosConfig } from "../resources/selectors_by_kind";
import { determineInstalledOsVersion, shouldDisplay } from "../util";
export function mapStateToProps(props: Everything): Props {
const conf = getFbosConfig(props.resources.index);
const { hardware } = props.bot;
const fbosConfig = (conf && conf.body && conf.body.api_migrated)
? conf.body : undefined;
const installedOsVersion = determineInstalledOsVersion(
props.bot, maybeGetDevice(props.resources.index));
return {
userToApi: props.bot.connectivity["user.api"],
userToMqtt: props.bot.connectivity["user.mqtt"],
@ -23,5 +27,6 @@ export function mapStateToProps(props: Everything): Props {
images: selectAllImages(props.resources.index),
resources: props.resources.index,
sourceFbosConfig: sourceFbosConfigValue(fbosConfig, hardware.configuration),
shouldDisplay: shouldDisplay(installedOsVersion, props.bot.minOsFeatureData),
};
}