misc bug fixes

pull/1060/head
gabrielburnworth 2018-12-03 13:36:43 -08:00
parent 9d8ffe46ee
commit 2bcb249e7a
20 changed files with 98 additions and 58 deletions

View File

@ -23,7 +23,7 @@ class GlobalConfig < ApplicationRecord
{
"NODE_ENV" => Rails.env || "development",
"LONG_REVISION" => LONG_REVISION,
"SHORT_REVISION" => LONG_REVISION.first(7),
"SHORT_REVISION" => LONG_REVISION.first(8),
}.map do |(key, value)|
self.find_or_create_by(key: key).update_attributes(key: key, value: value)
end

View File

@ -1,6 +1,7 @@
# A human
class User < ApplicationRecord
class AlreadyVerified < StandardError; end
# TODO: use GlobalConfig instead of ENV
ENFORCE_TOS = ENV.fetch("TOS_URL") { false }
SKIP_EMAIL_VALIDATION = ENV.fetch("NO_EMAILS") { false }
validates :email, uniqueness: true

View File

@ -58,8 +58,8 @@ namespace :coverage do
puts "=" * 37
puts "COVERAGE RESULTS"
puts "This build: #{build_percent.round(8)}% #{CURRENT_COMMIT[0,7]}"
puts "Staging build: #{staging_percent.round(8)}% #{latest_commit_staging[0,7]}"
puts "This build: #{build_percent.round(8)}% #{CURRENT_COMMIT[0,8]}"
puts "Staging build: #{staging_percent.round(8)}% #{latest_commit_staging[0,8]}"
puts "=" * 37
puts "Difference: #{diff.round(8)}%"
puts "Pass?: #{pass ? "yes" : "no"}"

View File

@ -94,6 +94,18 @@ var HelperNamespace = (function () {
console.dir(getAllTags());
}
/** For debugging. Replace all translations with a debug string. */
function replaceWithDebugString(key, debugString, debugStringOption) {
const debugChar = debugString[0];
switch (debugStringOption) {
case 'r': return debugString; // replace with: string as provided
case 's': return debugChar; // single character
case 'n': return key.replace(/\S/g, debugChar); // maintain whitespace
case 'l': return debugChar.repeat(key.length) // replace whitespace
default: return key;
}
}
/**
* Label a section of tags with a comment before the first tag in the section.
*/
@ -189,6 +201,7 @@ var HelperNamespace = (function () {
// For debugging
const debug = process.argv[3];
const debugOption = process.argv[4];
// merge new tags with existing translation
var result = {};
@ -198,14 +211,18 @@ var HelperNamespace = (function () {
// all current tags in English
Object.keys(jsonCurrentTagData).sort(localeSort).map(function (key) {
result[key] = jsonCurrentTagData[key];
if (debug) { result[key] = debug[0].repeat(key.length) }
if (debug) {
result[key] = replaceWithDebugString(key, debug, debugOption);
}
})
for (var key in ordered) {
// replace current tag with an existing translation
if (result.hasOwnProperty(key)) {
delete result[key];
result[key] = ordered[key];
if (debug) { result[key] = debug[0].repeat(key.length) }
if (debug) {
result[key] = replaceWithDebugString(key, debug, debugOption);
}
existing++;
if (key !== result[key]) { translated++; }
}

View File

@ -126,6 +126,7 @@
overflow: hidden;
text-overflow: ellipsis;
height: 2rem;
padding: 0.2rem;
}
.right-button {
float: right;

View File

@ -41,6 +41,8 @@ input:not([role="combobox"]) {
.week-row {
height: 30.5px;
width: 108%;
margin-left: -1rem;
}
select {

View File

@ -111,6 +111,11 @@ nav {
label {
color: $white;
}
p {
display: inline;
color: $gray;
font-size: 1.2rem;
}
}
}
.bp3-overlay-content {

View File

@ -5,7 +5,9 @@
}
.week-grid-meta-buttons {
margin-top: 1rem;
text-align: right;
button {
float: none;
margin-left: 1rem;
margin-bottom: 1rem;
}

View File

@ -74,9 +74,8 @@
margin-left: 1rem;
margin-bottom: 0.5rem;
}
}
img {
width: 55%;
margin-bottom: 2rem;
img {
width: 55%;
margin-bottom: 2rem;
}
}

View File

@ -18,17 +18,21 @@ describe("executableType", () => {
});
describe("OFSearch()", () => {
const START = expect.objectContaining({
type: Actions.OF_SEARCH_RESULTS_START
});
const NO = expect.objectContaining({ type: Actions.OF_SEARCH_RESULTS_NO });
it("searches: no image", async () => {
mockPromise = Promise.resolve({ data: { data: [{ attributes: {} }] } });
const dispatch = jest.fn();
await OFSearch("mint")(dispatch);
expect(dispatch).toHaveBeenCalledWith(START);
await expect(dispatch).toHaveBeenCalledWith({
type: Actions.OF_SEARCH_RESULTS_OK, payload: [
{ crop: {}, image: "/app-resources/img/generic-plant.svg" }]
});
await expect(dispatch).not.toHaveBeenCalledWith(expect.objectContaining({
type: Actions.OF_SEARCH_RESULTS_NO
}));
await expect(dispatch).not.toHaveBeenCalledWith(NO);
});
it("searches: image", async () => {
@ -43,24 +47,22 @@ describe("OFSearch()", () => {
});
const dispatch = jest.fn();
await OFSearch("mint")(dispatch);
expect(dispatch).toHaveBeenCalledWith(START);
await expect(dispatch).toHaveBeenCalledWith({
type: Actions.OF_SEARCH_RESULTS_OK, payload: [
{ crop: {}, image: "thumbnail_url" }]
});
await expect(dispatch).not.toHaveBeenCalledWith(expect.objectContaining({
type: Actions.OF_SEARCH_RESULTS_NO
}));
await expect(dispatch).not.toHaveBeenCalledWith(NO);
});
it("fails search", async () => {
mockPromise = Promise.reject();
const dispatch = jest.fn();
await OFSearch("mint")(dispatch);
expect(dispatch).toHaveBeenCalledWith(START);
await expect(dispatch).not.toHaveBeenCalledWith(expect.objectContaining({
type: Actions.OF_SEARCH_RESULTS_OK
}));
await expect(dispatch).toHaveBeenCalledWith({
type: Actions.OF_SEARCH_RESULTS_NO, payload: undefined
});
await expect(dispatch).toHaveBeenCalledWith(NO);
});
});

View File

@ -27,7 +27,6 @@ import {
fakeCropLiveSearchResult
} from "../../../__test_support__/fake_crop_search_result";
import { unselectPlant } from "../../actions";
import { Actions } from "../../../constants";
describe("<CropInfo />", () => {
const fakeProps = (): CropInfoProps => {
@ -118,8 +117,5 @@ describe("searchForCurrentCrop()", () => {
searchForCurrentCrop(fakeOFSearch)(dispatch);
expect(fakeOFSearch).toHaveBeenCalledWith("mint");
expect(unselectPlant).toHaveBeenCalled();
expect(dispatch).toHaveBeenCalledWith(expect.objectContaining({
type: Actions.OF_SEARCH_RESULTS_START
}));
});
});

View File

@ -55,6 +55,10 @@ export class CropCatalog extends React.Component<CropCatalogProps, {}> {
this.validSearchTerm;
}
componentDidMount() {
this.props.openfarmSearch(this.props.cropSearchQuery)(this.props.dispatch);
}
render() {
return <DesignerPanel panelName={"crop-catalog"} panelColor={"green"}>
<DesignerPanelHeader

View File

@ -209,12 +209,15 @@ export function mapStateToProps(props: Everything): CropInfoProps {
/** Get OpenFarm crop search results for crop info page contents. */
export const searchForCurrentCrop = (openfarmSearch: OpenfarmSearch) =>
(dispatch: Function) => {
dispatch({ type: Actions.OF_SEARCH_RESULTS_START, payload: true });
const crop = getPathArray()[5];
openfarmSearch(crop)(dispatch);
unselectPlant(dispatch)();
};
/** Clear the current crop search results. */
const clearCropSearchResults = (dispatch: Function) => () =>
dispatch({ type: Actions.OF_SEARCH_RESULTS_OK, payload: [] });
@connect(mapStateToProps)
export class CropInfo extends React.Component<CropInfoProps, {}> {
@ -233,6 +236,7 @@ export class CropInfo extends React.Component<CropInfoProps, {}> {
panelColor={"green"}
title={result.crop.name}
backTo={basePath}
onBack={clearCropSearchResults(this.props.dispatch)}
style={{ background: backgroundURL }}
description={result.crop.description}>
<AddToMapButton basePath={basePath} crop={crop} />

View File

@ -17,6 +17,7 @@ interface IdURL {
const FALLBACK: OpenFarm.Included[] = [];
export let OFSearch = (searchTerm: string) =>
(dispatch: Function) => {
dispatch({ type: Actions.OF_SEARCH_RESULTS_START, payload: undefined });
openFarmSearchQuery(searchTerm)
.then(resp => {
const images: { [key: string]: string } = {};

View File

@ -42,9 +42,9 @@ describe("<SyncButton/>", function () {
it("defaults to `disconnected` and `red` when uncertain", () => {
const p = fakeProps();
// tslint:disable-next-line:no-any
p.bot.hardware.informational_settings.sync_status = "mistake" as any;
p.bot.hardware.informational_settings.sync_status = "new" as any;
const result = shallow(<SyncButton {...p} />);
expect(result.text()).toContain("DISCONNECTED");
expect(result.text()).toContain("new");
expect(result.hasClass("red")).toBeTruthy();
});

View File

@ -3,6 +3,7 @@ import { t } from "i18next";
import { AccountMenuProps } from "./interfaces";
import { docLink } from "../ui/doc_link";
import { Link } from "../link";
import { shortRevision } from "../util";
export const AdditionalMenu = (props: AccountMenuProps) => {
return <div className="nav-additional-menu">
@ -34,7 +35,7 @@ export const AdditionalMenu = (props: AccountMenuProps) => {
<a
href="https://github.com/FarmBot/Farmbot-Web-App"
target="_blank">
{(globalConfig.SHORT_REVISION || "NONE").slice(0, 7)}
{shortRevision().slice(0, 7)}<p>{shortRevision().slice(7, 8)}</p>
</a>
</div>
</div>;

View File

@ -14,7 +14,7 @@ const COLOR_MAPPING: Record<SyncStatus, string> = {
"unknown": "red"
};
const TEXT_MAPPING: Record<SyncStatus, string> = {
const TEXT_MAPPING: () => Record<SyncStatus, string> = () => ({
"synced": t("SYNCED"),
"sync_now": t("SYNC NOW"),
"syncing": t("SYNCING"),
@ -22,7 +22,7 @@ const TEXT_MAPPING: Record<SyncStatus, string> = {
"booting": t("BOOTING"),
"unknown": t("DISCONNECTED"),
"maintenance": t("MAINTENANCE DOWNTIME")
};
});
/** Animation during syncing action */
const spinner = <span className="btn-spinner sync" />;
@ -30,15 +30,16 @@ const spinner = <span className="btn-spinner sync" />;
export function SyncButton({ user, bot, dispatch, consistent }: NavButtonProps) {
if (!user) {
return <span></span>;
return <span />;
}
let { sync_status } = bot.hardware.informational_settings;
sync_status = sync_status || "unknown";
const color = !consistent && sync_status === "sync_now"
const { sync_status } = bot.hardware.informational_settings;
const syncStatus = sync_status || "unknown";
const normalColor = COLOR_MAPPING[syncStatus] || "red";
const color = (!consistent && (syncStatus === "sync_now"))
? "gray"
: (COLOR_MAPPING[sync_status] || "red");
const text = TEXT_MAPPING[sync_status] || t("DISCONNECTED");
const spinnerEl = (sync_status === "syncing") ? spinner : "";
: normalColor;
const text = TEXT_MAPPING()[syncStatus] || syncStatus.replace("_", " ");
const spinnerEl = (syncStatus === "syncing") ? spinner : "";
return <button
className={`nav-sync ${color} fb-button`}

View File

@ -21,26 +21,30 @@ export function WeekGrid({ weeks, dispatch }: WeekGridProps) {
<Row>
<Col xs={12}>
<div className="week-grid-meta-buttons">
<button
className="green widget-control fb-button"
onClick={() => dispatch(pushWeek())}>
<i className="fa fa-plus" /> {t("Week")}
</button>
<button
className="red widget-control fb-button"
onClick={() => dispatch(popWeek())}>
<i className="fa fa-minus" /> {t("Week")}
</button>
<button
className="gray widget-control fb-button"
onClick={() => dispatch(deselectDays())}>
{t("Deselect all")}
</button>
<button
className="gray widget-control fb-button"
onClick={() => dispatch(selectDays())}>
{t("Select all")}
</button>
<div>
<button
className="green widget-control fb-button"
onClick={() => dispatch(pushWeek())}>
<i className="fa fa-plus" /> {t("Week")}
</button>
<button
className="red widget-control fb-button"
onClick={() => dispatch(popWeek())}>
<i className="fa fa-minus" /> {t("Week")}
</button>
</div>
<div>
<button
className="gray widget-control fb-button"
onClick={() => dispatch(deselectDays())}>
{t("Deselect all")}
</button>
<button
className="gray widget-control fb-button"
onClick={() => dispatch(selectDays())}>
{t("Select all")}
</button>
</div>
</div>
</Col>
</Row>

View File

@ -12,7 +12,7 @@ interface CenterProps {
}
export function CenterPanel(props: CenterProps) {
return <Col sm={props.width || 6}>
return <Col sm={props.width || 6} lg={6}>
<div className={props.className}>
<h3>
<i>{t(props.title)}</i>

View File

@ -12,7 +12,7 @@ interface RightPanelProps {
}
export function RightPanel(props: RightPanelProps) {
return <Col sm={props.width || 3}>
return <Col sm={props.width || 3} lg={3}>
{props.show &&
<div className={props.className}>
<h3>