1
0
mirror of https://github.com/bitwarden/desktop.git synced 2024-09-06 00:28:04 +02:00

login form styling. server validation errors displayed as sweet alerts

This commit is contained in:
Kyle Spearrin 2016-09-20 16:17:31 -04:00
parent 2fefdf8f6c
commit 47b500d7c0
11 changed files with 91 additions and 111 deletions

View File

@ -2,7 +2,10 @@
.module('bit.accounts') .module('bit.accounts')
.controller('accountsLoginController', function ($scope, $state, loginService, userService) { .controller('accountsLoginController', function ($scope, $state, loginService, userService) {
$scope.login = function (model) { popupUtils.initListSectionItemListeners();
$scope.loginPromise = null;
$scope.login = function (model, form) {
$scope.loginPromise = loginService.logIn(model.email, model.masterPassword); $scope.loginPromise = loginService.logIn(model.email, model.masterPassword);
$scope.loginPromise.then(function () { $scope.loginPromise.then(function () {

View File

@ -1,22 +1,33 @@
<ion-view view-title="bitwarden"> <form name="theForm" ng-submit="theForm.$valid && login(model, theForm)" bit-form="loginPromise">
<ion-content> <div class="header">
<div class="left">
<a ng-click="close()" href="">Cancel</a>
</div>
<div class="right">
<button type="submit" class="btn btn-link" ng-show="!theForm.$loading">Log In</button>
<i class="fa fa-spinner fa-lg" ng-show="theForm.$loading" ng-class="{'fa-spin' : theForm.$loading}"></i>
</div>
<div class="title">bitwarden</div>
</div>
<div class="content">
<div class="list"> <div class="list">
<label class="item item-input"> <div class="list-section">
<i class="icon ion-android-mail placeholder-icon"></i> <div class="list-section-items">
<input type="text" placeholder="Email address" ng-model="model.email"> <div class="list-section-item list-section-item-icon-input">
</label> <i class="fa fa-envelope fa-lg fa-fw"></i>
<label class="item item-input"> <label for="email" class="sr-only">Email Address</label>
<i class="icon ion-locked placeholder-icon"></i> <input id="email" type="email" name="Email" placeholder="Email Address" ng-model="model.email">
<input type="password" placeholder="Master password" ng-model="model.masterPassword"> </div>
</label> <div class="list-section-item list-section-item-icon-input">
<i class="fa fa-lock fa-lg fa-fw"></i>
<label for="master-password" class="sr-only">Master Password</label>
<input id="master-password" type="password" name="MasterPassword" placeholder="Master Password" ng-model="model.masterPassword">
</div>
</div>
</div>
</div> </div>
<div class="padding"> <p class="text-center">
<button class="button button-block button-positive" ng-click="login(model)"> <a href="#/hint">Get master password hint</a>
Log In </p>
</button> </div>
<p class="text-center"> </form>
<a href="#/hint">Get master password hint</a>
</p>
</div>
</ion-content>
</ion-view>

View File

@ -2,7 +2,7 @@
<div class="right"> <div class="right">
<a href="" ng-click="addSite()"><i class="fa fa-plus fa-lg"></i></a> <a href="" ng-click="addSite()"><i class="fa fa-plus fa-lg"></i></a>
</div> </div>
<div class="title">Current Tab Sites</div> <div class="title">Current Tab</div>
</div> </div>
<div class="content content-tabs"> <div class="content content-tabs">
<div ng-if="sites.length"> <div ng-if="sites.length">

View File

@ -1,30 +0,0 @@
angular
.module('bit.directives')
.directive('bitField', function () {
var linkFn = function (scope, element, attrs, ngModel) {
ngModel.$registerError = registerError;
ngModel.$validators.validate = validator;
function validator() {
ngModel.$setValidity('bit', true);
return true;
}
function registerError() {
ngModel.$setValidity('bit', false);
}
};
return {
require: 'ngModel',
restrict: 'A',
compile: function (elem, attrs) {
if (!attrs.name || attrs.name === '') {
throw 'bit-field element does not have a valid name attribute';
}
return linkFn;
}
};
});

View File

@ -18,9 +18,6 @@
return; return;
} }
// reset errors
form.$errors = null;
// start loading // start loading
form.$loading = true; form.$loading = true;
@ -28,8 +25,7 @@
form.$loading = false; form.$loading = false;
}, function failure(reason) { }, function failure(reason) {
form.$loading = false; form.$loading = false;
validationService.addErrors(form, reason); validationService.showError(reason);
scope.$broadcast('show-errors-check-validity');
}); });
} }
}); });

View File

@ -1,2 +1,2 @@
angular angular
.module('bit.services', ['angular-jwt']); .module('bit.services', ['angular-jwt', 'oitozero.ngSweetAlert']);

View File

@ -1,61 +1,47 @@
angular angular
.module('bit.services') .module('bit.services')
.factory('validationService', function () { .factory('validationService', function (SweetAlert) {
var _service = {}; var _service = {};
_service.addErrors = function (form, reason) { _service.showError = function (data) {
var data = reason.data;
var defaultErrorMessage = 'An unexpected error has occured.'; var defaultErrorMessage = 'An unexpected error has occured.';
form.$errors = []; var errors = [];
if (!data || !angular.isObject(data)) { if (!data || !angular.isObject(data)) {
form.$errors.push(defaultErrorMessage); errors.push(defaultErrorMessage);
return;
} }
else if (!data.validationErrors) {
if (!data.validationErrors) {
if (data.message) { if (data.message) {
form.$errors.push(data.message); errors.push(data.message);
} }
else { else {
form.$errors.push(defaultErrorMessage); errors.push(defaultErrorMessage);
}
return;
}
for (var key in data.validationErrors) {
if (!data.validationErrors.hasOwnProperty(key)) {
continue;
}
for (var i = 0; i < data.validationErrors[key].length; i++) {
_service.addError(form, key, data.validationErrors[key][i]);
} }
} }
}; else {
for (var key in data.validationErrors) {
if (!data.validationErrors.hasOwnProperty(key)) {
continue;
}
_service.addError = function (form, key, errorMessage, clearExistingErrors) { for (var i = 0; i < data.validationErrors[key].length; i++) {
if (clearExistingErrors || !form.$errors) { errors.push(data.validationErrors[key][i]);
form.$errors = []; }
}
var pushError = true;
for (var i = 0; i < form.$errors.length; i++) {
if (form.$errors[i] === errorMessage) {
pushError = false;
break;
} }
} }
if (pushError) { if (errors.length) {
form.$errors.push(errorMessage); SweetAlert.swal({
title: 'Error',
text: errors[0],
type: 'error',
showCancelButton: false,
confirmButtonText: 'Ok'
});
} }
if (key && key !== '' && form[key] && form[key].$registerError) { return errors;
form[key].$registerError();
}
}; };
return _service; return _service;

View File

@ -18,19 +18,19 @@
<div class="list-section-items"> <div class="list-section-items">
<div class="list-section-item"> <div class="list-section-item">
<label for="name" class="item-label">Name</label> <label for="name" class="item-label">Name</label>
<input id="name" type="text" name="Name" ng-model="site.name" bit-field> <input id="name" type="text" name="Name" ng-model="site.name">
</div> </div>
<div class="list-section-item"> <div class="list-section-item">
<label for="uri" class="item-label">URI</label> <label for="uri" class="item-label">URI</label>
<input id="uri" type="text" name="Uri" ng-model="site.uri" bit-field> <input id="uri" type="text" name="Uri" ng-model="site.uri">
</div> </div>
<div class="list-section-item"> <div class="list-section-item">
<label for="username" class="item-label">Username</label> <label for="username" class="item-label">Username</label>
<input id="username" type="text" name="Username" ng-model="site.username" bit-field> <input id="username" type="text" name="Username" ng-model="site.username">
</div> </div>
<div class="list-section-item"> <div class="list-section-item">
<label for="password" class="item-label">Password</label> <label for="password" class="item-label">Password</label>
<input id="password" type="password" name="Password" ng-model="site.password" bit-field> <input id="password" type="password" name="Password" ng-model="site.password">
</div> </div>
<a class="list-section-item" href="" ng-click="generatePassword()"> <a class="list-section-item" href="" ng-click="generatePassword()">
Generate Password Generate Password
@ -42,13 +42,13 @@
<div class="list-section-items"> <div class="list-section-items">
<div class="list-section-item"> <div class="list-section-item">
<label for="folder" class="item-label">Folder</label> <label for="folder" class="item-label">Folder</label>
<select id="folder" name="FolderId" ng-model="site.folderId" bit-field> <select id="folder" name="FolderId" ng-model="site.folderId">
<option ng-repeat="folder in folders | orderBy: ['name']" value="{{folder.id}}">{{folder.name}}</option> <option ng-repeat="folder in folders | orderBy: ['name']" value="{{folder.id}}">{{folder.name}}</option>
</select> </select>
</div> </div>
<div class="list-section-item list-section-item-checkbox"> <div class="list-section-item list-section-item-checkbox">
<label for="favorite">Favorite</label> <label for="favorite">Favorite</label>
<input id="favorite" name="Favorite" type="checkbox" ng-model="site.favorite" bit-field> <input id="favorite" name="Favorite" type="checkbox" ng-model="site.favorite">
</div> </div>
</div> </div>
</div> </div>
@ -58,7 +58,7 @@
</div> </div>
<div class="list-section-items"> <div class="list-section-items">
<div class="list-section-item"> <div class="list-section-item">
<textarea id="notes" name="Notes" rows="5" ng-model="site.notes" bit-field></textarea> <textarea id="notes" name="Notes" rows="5" ng-model="site.notes"></textarea>
</div> </div>
</div> </div>
</div> </div>

View File

@ -18,19 +18,19 @@
<div class="list-section-items"> <div class="list-section-items">
<div class="list-section-item"> <div class="list-section-item">
<label for="name" class="item-label">Name</label> <label for="name" class="item-label">Name</label>
<input id="name" type="text" name="Name" ng-model="site.name" bit-field> <input id="name" type="text" name="Name" ng-model="site.name">
</div> </div>
<div class="list-section-item"> <div class="list-section-item">
<label for="uri" class="item-label">URI</label> <label for="uri" class="item-label">URI</label>
<input id="uri" type="text" name="Uri" ng-model="site.uri" bit-field> <input id="uri" type="text" name="Uri" ng-model="site.uri">
</div> </div>
<div class="list-section-item"> <div class="list-section-item">
<label for="username" class="item-label">Username</label> <label for="username" class="item-label">Username</label>
<input id="username" type="text" name="Username" ng-model="site.username" bit-field> <input id="username" type="text" name="Username" ng-model="site.username">
</div> </div>
<div class="list-section-item"> <div class="list-section-item">
<label for="password" class="item-label">Password</label> <label for="password" class="item-label">Password</label>
<input id="password" type="password" name="Password" ng-model="site.password" bit-field> <input id="password" type="password" name="Password" ng-model="site.password">
</div> </div>
<a class="list-section-item" href="" ng-click="generatePassword()"> <a class="list-section-item" href="" ng-click="generatePassword()">
Generate Password Generate Password
@ -42,13 +42,13 @@
<div class="list-section-items"> <div class="list-section-items">
<div class="list-section-item"> <div class="list-section-item">
<label for="folder" class="item-label">Folder</label> <label for="folder" class="item-label">Folder</label>
<select id="folder" name="FolderId" ng-model="site.folderId" bit-field> <select id="folder" name="FolderId" ng-model="site.folderId">
<option ng-repeat="folder in folders | orderBy: ['name']" value="{{folder.id}}">{{folder.name}}</option> <option ng-repeat="folder in folders | orderBy: ['name']" value="{{folder.id}}">{{folder.name}}</option>
</select> </select>
</div> </div>
<div class="list-section-item list-section-item-checkbox"> <div class="list-section-item list-section-item-checkbox">
<label for="favorite">Favorite</label> <label for="favorite">Favorite</label>
<input id="favorite" name="Favorite" type="checkbox" ng-model="site.favorite" bit-field> <input id="favorite" name="Favorite" type="checkbox" ng-model="site.favorite">
</div> </div>
</div> </div>
</div> </div>
@ -58,7 +58,7 @@
</div> </div>
<div class="list-section-items"> <div class="list-section-items">
<div class="list-section-item"> <div class="list-section-item">
<textarea id="notes" name="Notes" rows="5" ng-model="site.notes" bit-field></textarea> <textarea id="notes" name="Notes" rows="5" ng-model="site.notes"></textarea>
</div> </div>
</div> </div>
</div> </div>

View File

@ -38,7 +38,6 @@
<script src="app/directives/directivesModule.js"></script> <script src="app/directives/directivesModule.js"></script>
<script src="app/directives/formDirective.js"></script> <script src="app/directives/formDirective.js"></script>
<script src="app/directives/fieldDirective.js"></script>
<script src="app/services/servicesModule.js"></script> <script src="app/services/servicesModule.js"></script>
<script src="app/services/backgroundService.js"></script> <script src="app/services/backgroundService.js"></script>

View File

@ -348,6 +348,21 @@
} }
} }
} }
&.list-section-item-icon-input {
padding: 15px 15px;
.fa {
float: left;
color: @list-icon-color;
margin-top: 3px;
}
input {
display: inline-block;
margin-left: 10px;
}
}
} }
&.list-no-selection { &.list-no-selection {