Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
225 changes: 225 additions & 0 deletions app/components/AddAccount.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
<template>
<Page actionBarHidden="false" @loaded="onPageLoaded">
<ScrollView>
<FlexboxLayout class="page login-page" justifyContent="spaceBetween">
<Image class="logo" src="~/images/fieldkit-logo-blue.png"></Image>
<StackLayout class="form">
<GridLayout rows="auto, auto, auto, auto">
<StackLayout row="1" class="input-field">
<GridLayout rows="auto" columns="*">
<TextField
row="0"
id="email-field"
class="input"
:hint="_L('email')"
ref="email"
horizontalAlignment="left"
:isEnabled="!processing"
keyboardType="email"
autocorrect="false"
autocapitalizationType="none"
v-model="user.email"
automationText="loginEmailInput"
returnKeyType="next"
@focus="showActive"
@returnPress="focusPassword"
@blur="checkEmail"
></TextField>
<Image row="0" width="25" class="bottom-pad" horizontalAlignment="right" src="~/images/Icon_Email_login.png"></Image>
</GridLayout>
<StackLayout class="spacer-top" id="email-field-spacer" v-show="!noEmail && !emailNotValid"></StackLayout>
<Label class="validation-error" id="no-email" :text="_L('emailRequired')" textWrap="true" v-show="noEmail"></Label>
<Label class="validation-error" id="email-not-valid" :text="_L('emailNotValid')" textWrap="true" v-show="emailNotValid"></Label>
</StackLayout>

<StackLayout row="2" class="input-field">
<GridLayout rows="auto" columns="*">
<TextField
id="password-field"
class="input"
:hint="_L('password')"
secure="true"
ref="password"
horizontalAlignment="left"
:isEnabled="!processing"
v-model="user.password"
automationText="loginPasswordInput"
:returnKeyType="'done'"
@focus="showActive"
@blur="checkPassword"
></TextField>
<Image row="0" width="25" class="bottom-pad" horizontalAlignment="right" src="~/images/Icon_Password_login.png"></Image>
</GridLayout>
<StackLayout class="spacer-top" id="password-field-spacer" v-show="!noPassword"></StackLayout>
<Label class="validation-error" id="no-password" :text="_L('passwordRequired')" textWrap="true" v-show="noPassword"></Label>
</StackLayout>
<ActivityIndicator rowSpan="4" :busy="processing"></ActivityIndicator>
</GridLayout>

<Button class="btn btn-primary btn-padded m-t-20" :text="_L('addAccount')" :isEnabled="!processing" @tap="submit"></Button>
</StackLayout>
</FlexboxLayout>
</ScrollView>
</Page>
</template>

<script>
import routes from "../routes";
import Services from "../services/services";

export default {
data() {
return {
processing: false,
noName: false,
nameTooLong: false,
noEmail: false,
emailNotValid: false,
noPassword: false,
navigatedAway: false,
user: {
name: "",
email: "",
password: "",
},
};
},
methods: {
onPageLoaded(args) {
// logging out sends resetUser = true
this.page = args.object;
},

showActive(event) {
let spacer = this.page.getViewById(event.object.id + "-spacer");
spacer.className = "spacer-top active";
},

checkEmail(event) {
let spacer = this.page.getViewById("email-field-spacer");
spacer.className = "spacer-top";
this.noEmail = !this.user.email || this.user.email.length == 0;
if (this.noEmail) {
return;
}
let emailPattern = /^([a-zA-Z0-9_+\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/;
this.emailNotValid = !emailPattern.test(this.user.email);
},

checkPassword(event) {
let spacer = this.page.getViewById("password-field-spacer");
spacer.className = "spacer-top";
this.noPassword = !this.user.password || this.user.password.length == 0;
if (this.noPassword) {
return;
}
this.passwordTooShort = this.user.password.length < 10;
},

submit() {
if (!this.user.email || !this.user.password) {
return this.alert(_L("provideBoth"));
}

this.processing = true;

return Services.PortalInterface()
.login(this.user)
.then(token => {
this.processing = false;

return this.$navigateTo(routes.appSettings, {});
})
.catch(error => {
this.processing = false;

if (!this.navigatedAway) {
return this.alert(_L("loginFailed"));
}
});
},

focusEmail() {
this.$refs.email.nativeView.focus();
},
focusPassword() {
this.$refs.password.nativeView.focus();
},

alert(message) {
return alert({
title: "FieldKit",
okButtonText: _L("ok"),
message: message,
});
},
},
};
</script>

<style scoped lang="scss">
// Start custom common variables
@import "../app-variables";
// End custom common variables

.login-page {
font-size: 16;
flex-direction: column;
height: 100%;
}

.form {
margin-left: 5;
margin-right: 5;
flex-grow: 2;
}

.logo {
margin-top: 50;
height: 47;
}

.spacer-top {
border-top-color: $fk-gray-lighter;
border-top-width: 2;
}

.active {
border-top-color: $fk-secondary-blue;
}

.input-field {
margin-bottom: 15;
}

.input {
width: 100%;
font-size: 16;
color: $fk-primary-black;
placeholder-color: $fk-gray-hint;
}

.input:disabled {
opacity: 0.5;
}

.btn-primary {
margin: 20 5 15 5;
}

.bottom-pad {
margin-bottom: 8;
}

.sign-up-label {
horizontal-align: center;
margin-bottom: 10;
}

.validation-error {
color: $fk-tertiary-red;
border-top-color: $fk-tertiary-red;
border-top-width: 2;
padding-top: 5;
}
</style>
31 changes: 28 additions & 3 deletions app/components/AppSettingsView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,22 @@
<ScreenHeader row="0" :title="_L('fieldkitSettings')" :canNavigateBack="false" :canNavigateSettings="false" class="m-t-10" />
<ScrollView row="1">
<FlexboxLayout flexDirection="column" class="p-t-10">
<StackLayout class="m-x-10 m-y-20" orientation="vertical">
<StackLayout>
<Label :text="_L('accounts')" class="size-16 m-b-10" textWrap="true" />
</StackLayout>
<StackLayout v-for="account in accounts" orientation="horizontal" class="v-middle">
<Label :text="account.email" class="size-14 m-10 v-middle"/>
<Image v-if="account.email == currentUser.email" width="10" class="v-middle" src="~/images/Icon_Save.png"></Image>
</StackLayout>
</StackLayout>
<StackLayout class="m-x-20 m-y-20 v-middle" @tap="addAccount" orientation="horizontal">
<Image width="16" class="m-r-10" src="~/images/Icon_Add_Button.png"></Image>
<Label :text="_L('addAccount')" class="v-middle"></Label>
</StackLayout>
<StackLayout>
<Button v-if="loggedIn" class="btn btn-secondary" :text="_L('logOut')" @tap="logout"></Button>
<Button v-if="loggedIn && accounts.length > 1" class="btn btn-secondary" :text="_L('logOutAll')" @tap="logout"></Button>
<Button v-if="loggedIn && accounts.length === 1" class="btn btn-secondary" :text="_L('logOut')" @tap="logout"></Button>
<Button v-if="!loggedIn" class="btn btn-primary" :text="_L('logIn')" @tap="goToLogin"></Button>
</StackLayout>
<StackLayout class="m-x-10 m-y-20">
Expand All @@ -26,16 +40,21 @@
import routes from "../routes";
import ScreenHeader from "./ScreenHeader";
import ScreenFooter from "./ScreenFooter";
import { hexStringToByteWiseString } from "../utilities";
import { Build } from "../config";

export default {
data() {
return {
loggedIn: this.$portalInterface.isLoggedIn(),
versions: Build,
currentUser: this.$portalInterface._currentUser
};
},
computed: {
accounts() {
return this.$store.state.portal.accounts;
},
},
components: {
ScreenHeader,
ScreenFooter,
Expand All @@ -56,6 +75,10 @@ export default {
goToLogin() {
this.$navigateTo(routes.login);
},

addAccount() {
this.$navigateTo(routes.addAccount);
}
},
};
</script>
Expand All @@ -64,6 +87,8 @@ export default {
// Start custom common variables
@import "../app-variables";
// End custom common variables

.v-middle {
vertical-align: middle;
}
// Custom styles
</style>
5 changes: 4 additions & 1 deletion app/i18n/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -407,5 +407,8 @@ module.exports = {
daysHrsMin: "days hrs min",
hrsMinSec: "hrs min sec",
downloadFirmware: "Download Firmware",
upgradeFirmware: "Upgrade Firmware"
upgradeFirmware: "Upgrade Firmware",
addAccount: "Add Account",
logOutAll: "Log Out All Accounts",
accounts: "Accounts"
};
5 changes: 4 additions & 1 deletion app/i18n/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -407,5 +407,8 @@ module.exports = {
daysHrsMin: "días hrs min",
hrsMinSec: "hrs min seg",
downloadFirmware: "Descargar Firmware",
upgradeFirmware: "Actualización de Firmware"
upgradeFirmware: "Actualización de Firmware",
addAccount: "Add Account",
logOutAll: "Log Out All Accounts",
accounts: "Accounts"
};
15 changes: 15 additions & 0 deletions app/migrations/20200731_093924_add_accounts_table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Migration from "./Migration";

export class AddAccounts_20200731_093924 extends Migration {
up(db) {
return db.batch([
`CREATE TABLE IF NOT EXISTS accounts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
email TEXT,
portal_id TEXT,
token TEXT
)`,
]);
}
}
1 change: 1 addition & 0 deletions app/migrations/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export * from "./20200624_193105_add_sensor_trend.js";
export * from "./20200708_162023_add_notes_table.js";
export * from "./20200715_075044_add_module_flags.js";
export * from "./20200723_093924_add_portal_updated_time.js";
export * from "./20200731_093924_add_accounts_table.js";
2 changes: 2 additions & 0 deletions app/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import DeployNotes from "../components/deploy/DeployNotesView";
import DeployReview from "../components/deploy/DeployReviewView";

import { Route, RouteState } from "./navigate";
import AddAccount from "~/components/AddAccount";

const routes = {
appSettings: new Route(AppSettings, {}),
Expand All @@ -36,6 +37,7 @@ const routes = {
deployReview: new Route(DeployReview, {}),
developerMenu: new Route(DeveloperMenu, { developer: true }),
login: new Route(Login, { login: true }),
addAccount: new Route(AddAccount, {}),
module: new Route(Module, {}),
assembleStation: new Route(AssembleStation, {}),
stations: new Route(StationListView, { listing: true }),
Expand Down
32 changes: 31 additions & 1 deletion app/services/db-interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import _ from "lodash";
import Config from "../config";
import { sqliteToJs } from "../utilities";
import { Download, FileTypeUtils } from "../store/types";
import { DownloadTableRow, NotesTableRow } from "../store/row-types";
import {AccountsTableRow, DownloadTableRow, NotesTableRow} from "../store/row-types";

const log = Config.logger("DbInterface");

Expand Down Expand Up @@ -869,4 +869,34 @@ export default class DatabaseInterface {
)
.catch((err) => Promise.reject(new Error(`error fetching notes: ${err}`)));
}

public getAllAccounts(): Promise<AccountsTableRow[]> {
return this.getDatabase()
.then((db) => db.query("SELECT * FROM accounts"))
.then((rows) => sqliteToJs(rows))
.then((rows) => {
return rows;
})
.catch((err) => Promise.reject(new Error(`error fetching accounts: ${err}`)));
}

public addOrUpdateAccounts(account: any): Promise<AccountsTableRow> {
return this.getDatabase()
.then((db) =>
db.query(`SELECT id FROM accounts WHERE portal_id = ?`, [account.portalId]).then((maybeId) => {
console.log('maybeId', maybeId);
if (maybeId.length == 0) {
return db.execute(`INSERT INTO accounts (name, email, portal_id, token) VALUES (?, ?, ?, ?)`, [account.name, account.email, account.portalId, account.token]);
}
const values = [account.name, account.email, account.portalId, account.token, maybeId[0].id];
return db.execute(`UPDATE accounts SET name = ?, email = ?, portal_id = ?, token = ? WHERE id = ?`, values);
})
)
.catch((err) => Promise.reject(new Error(`error fetching accounts: ${err}`)));
}

public deleteAllAccounts(): Promise<AccountsTableRow[]> {
return this.getDatabase()
.then((db) => db.query("DELETE FROM accounts"))
}
}
Loading