Skip to content

Commit da4bc8d

Browse files
authored
feat(device): add transfer capabilties (#446)
* feat(device): add transfer capabilties Add transfer view and functionality to edit device page * Add basic claim functionality to dashboard * Refresh devices after claiming * Remove unused variables * Add form and use pattern for validation * Wrap input-group in form-group * Add alert on errors * Reorganize dashboard styling * Style transfer settings page * Add translation keys
1 parent cb78b8e commit da4bc8d

File tree

9 files changed

+273
-5
lines changed

9 files changed

+273
-5
lines changed

app/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,7 @@
423423
<script src="scripts/controllers/account.box.edit.script.js"></script>
424424
<script src="scripts/controllers/account.box.edit.mqtt.js"></script>
425425
<script src="scripts/controllers/account.box.edit.ttn.js"></script>
426+
<script src="scripts/controllers/account.box.edit.transfer.js"></script>
426427
<script src="scripts/controllers/account.box.dataupload.js"></script>
427428
<script src="scripts/services/account.js"></script>
428429
<script src="scripts/services/directus.js"></script>

app/scripts/app.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,16 @@
347347
},
348348
},
349349
})
350+
.state('account.edit.transfer', {
351+
url: '/transfer',
352+
views: {
353+
edit: {
354+
controller: 'EditBoxTransferController',
355+
controllerAs: 'transfer',
356+
templateUrl: 'views/account.box.edit.transfer.html',
357+
},
358+
},
359+
})
350360
.state('account.dashboard', {
351361
url: '',
352362
views: {
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
(function () {
2+
'use strict';
3+
4+
angular
5+
.module('openSenseMapApp')
6+
.controller('EditBoxTransferController', EditBoxTransferController);
7+
8+
EditBoxTransferController.$inject = ['boxData', 'moment', 'AccountService'];
9+
10+
function EditBoxTransferController (boxData, moment, AccountService) {
11+
var vm = this;
12+
vm.device = {};
13+
vm.deviceName = '';
14+
vm.data = {};
15+
vm.tokenExists = false;
16+
vm.expiration = '1';
17+
18+
vm.transferDevice = transferDevice;
19+
vm.revokeToken = revokeToken;
20+
21+
activate();
22+
23+
////
24+
25+
function activate () {
26+
if (angular.isDefined(boxData)) {
27+
angular.copy(boxData, vm.device);
28+
29+
return AccountService.getTransferToken(vm.device._id)
30+
.then(function (response) {
31+
if (response.data) {
32+
angular.copy(response.data, vm.data);
33+
vm.tokenExists = true;
34+
}
35+
})
36+
.catch(function (error) {
37+
console.log(error);
38+
});
39+
}
40+
}
41+
42+
function transferDevice () {
43+
44+
var payload = {
45+
boxId: vm.device._id,
46+
date: moment.utc().add(vm.expiration, 'd')
47+
.toDate()
48+
};
49+
50+
return AccountService.transferDevice(payload)
51+
.then(function (response) {
52+
angular.copy(response.data, vm.data);
53+
vm.tokenExists = true;
54+
})
55+
.catch(function (error) {
56+
console.log(error);
57+
});
58+
}
59+
60+
function revokeToken () {
61+
var payload = {
62+
boxId: vm.device._id,
63+
token: vm.data.token
64+
};
65+
66+
return AccountService.revokeToken(payload)
67+
.then(function (response) {
68+
if (response.status === 204) {
69+
angular.copy({}, vm.data);
70+
vm.tokenExists = false;
71+
}
72+
})
73+
.catch(function (error) {
74+
console.log(error);
75+
});
76+
}
77+
78+
}
79+
})();

app/scripts/controllers/account.dashboard.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@
1515
vm.boxes = [];
1616
vm.listStyle = 'tiles';
1717
vm.orderByProperty = 'createdAt';
18+
vm.claimToken = '';
19+
vm.claimPattern = /^[a-z0-9]*$/;
20+
vm.errorMessage = '';
21+
22+
vm.claimDevice = claimDevice;
23+
vm.closeAlert = closeAlert;
1824

1925
activate();
2026

@@ -47,6 +53,34 @@
4753
});
4854
}
4955

56+
function claimDevice () {
57+
var payload = {
58+
token: vm.claimToken
59+
};
60+
61+
62+
63+
return AccountService.claimDevice(payload)
64+
.then(function () {
65+
66+
// Clear token field
67+
vm.claimToken = '';
68+
69+
return getUsersBoxes()
70+
.then(function () {
71+
// console.log('refreshed boxes');
72+
});
73+
})
74+
.catch(function (error) {
75+
console.log(error);
76+
vm.errorMessage = error.message;
77+
});
78+
}
79+
80+
function closeAlert () {
81+
vm.errorMessage = '';
82+
}
83+
5084
$scope.$watch('dashboard.listStyle', function (value) {
5185
LocalStorageService.setValue(localStorageKey, value);
5286
});

app/scripts/services/account.js

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@
2727
deleteMeasurement: deleteMeasurement,
2828
postNewBox: postNewBox,
2929
deleteAccount: deleteAccount,
30-
compileSketch: compileSketch
30+
compileSketch: compileSketch,
31+
transferDevice: transferDevice,
32+
revokeToken: revokeToken,
33+
getTransferToken: getTransferToken,
34+
claimDevice: claimDevice
3135
};
3236

3337
return service;
@@ -246,5 +250,51 @@
246250
})
247251
.catch(failed);
248252
}
253+
254+
function getTransferToken (boxId) {
255+
return $http
256+
.get(app.API_URL + '/boxes/transfer/' + boxId, {
257+
auth: true,
258+
})
259+
.then(function (response) {
260+
return response.data;
261+
})
262+
.catch(failed);
263+
}
264+
265+
function transferDevice (data) {
266+
return $http.post(app.API_URL + '/boxes/transfer', data, {
267+
auth: true,
268+
})
269+
.then(function (response) {
270+
return response.data;
271+
})
272+
.catch(failed);
273+
}
274+
275+
function revokeToken (data) {
276+
return $http
277+
.delete(app.API_URL + '/boxes/transfer', {
278+
auth: true,
279+
data: data,
280+
headers: {
281+
'Content-type': 'application/json;charset=utf-8',
282+
},
283+
})
284+
.then(function (response) {
285+
return response;
286+
})
287+
.catch(failed);
288+
}
289+
290+
function claimDevice (data) {
291+
return $http.post(app.API_URL + '/boxes/claim', data, {
292+
auth: true
293+
})
294+
.then(function (response) {
295+
return response.data;
296+
})
297+
.catch(failed);
298+
}
249299
}
250300
})();

app/scripts/services/authenticationInterceptor.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
});
5555

5656
return deferred.promise;
57+
case 400:
5758
case 404:
5859
return $q.reject(response);
5960
default:

app/views/account.box.edit.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
<li ng-class="{'active': edit.tabActive.script}" ng-click="edit.changeActiveTab('script')"><a ui-sref="account.edit.script"><i class="fa fa-file-text-o fa-fw"></i> {{'EDIT_SCRIPT'|translate}}</a></li>
2020
<li ng-class="{'active': edit.tabActive.mqtt}" ng-click="edit.changeActiveTab('mqtt')"><a ui-sref="account.edit.mqtt"><i class="fa fa-wifi fa-fw"></i> {{'EDIT_MQTTOPTIONS'|translate}}</a></li>
2121
<li ng-class="{'active': edit.tabActive.ttn}" ng-click="edit.changeActiveTab('ttn')"><a ui-sref="account.edit.ttn"><i class="fa fa-cloud-upload fa-fw"></i> {{'EDIT_TTN'|translate}}</a></li>
22+
<li ng-class="{'active': edit.tabActive.transfer}" ng-click="edit.changeActiveTab('transfer')"><a ui-sref="account.edit.transfer"><i class="fa fa-exchange fa-fw"></i> {{'EDIT_TRANSFER'|translate}}</a></li>
2223
</ul>
2324
</div>
2425
<div class="col-lg-9 col-md-9">
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<div class="transferSettings">
2+
<div class="row">
3+
<div class="col-md-6 col-lg-6">
4+
<h2 style="margin-top: 0px; margin-bottom: 0px">{{'EDIT_TRANSFER'|translate}}</h2>
5+
</div>
6+
</div>
7+
8+
<hr>
9+
10+
<div class="row">
11+
<div class="col-md-6 col-lg-6">
12+
13+
<div uib-alert class="alert-warning">
14+
<p>
15+
<span class="fa fa-exclamation-circle" aria-hidden="true"></span>
16+
{{'TRANSFER_NOTICE' | translate}}
17+
</p>
18+
<hr>
19+
<p ng-bind-html="'TRANSFER_INFORMATION'|translate"></p>
20+
</div>
21+
22+
<div ng-show="transfer.tokenExists" style="border: 1px solid grey; border-radius: 6px;">
23+
<div style="min-height: inherit; padding: 8px; box-sizing: border-box;">
24+
<div style="display: flex; float: right; box-sizing: border-box;">
25+
<button ng-click="transfer.revokeToken()" class="btn btn-danger">{{'REVOKE' | translate}}</button>
26+
</div>
27+
<small style="margin-right: 8px; float: right;">
28+
{{'CREATED_AT' | translate}}: {{transfer.data.createdAt | amUtc | amLocal | amDateFormat:'l LTS'}}
29+
</small>
30+
<span>Token:</span>
31+
<code>{{transfer.data.token}} <button osem-clipboard class="btn-toClipboard" text="transfer.data.token" title="Copy to Clipboard"><i class="fa fa-clipboard"></i></button></code>
32+
<div>
33+
<span>{{'EXPIRES_AT' | translate}}: {{transfer.data.expiresAt | amUtc | amLocal | amDateFormat:'l LTS'}}</span>
34+
</div>
35+
</div>
36+
</div>
37+
38+
<div ng-hide="transfer.tokenExists">
39+
<form name="tokenForm" novalidate>
40+
<div class="form-group">
41+
<label for="expiration">{{'EXPIRATION' | translate}}</label>
42+
<select class="form-control" ng-model="transfer.expiration" ng-change="transfer.changeExpiration()" name="expiration" id="expiration">
43+
<option value="1">1 {{'DAY' | translate}}</option>
44+
<option value="7">7 {{'DAYS' | translate}}</option>
45+
<option value="30">30 {{'DAYS' | translate}}</option>
46+
<option value="60">60 {{'DAYS' | translate}}</option>
47+
<option value="90">90 {{'DAYS' | translate}}</option>
48+
</select>
49+
</div>
50+
<div class="form-group" >
51+
<label class="control-label" translate="TRANSFER_INPUT_LABEL" translate-value-name="{{transfer.device.name}}"></label>
52+
<input type="text" name="name" id="name" class="form-control" ng-model="transfer.deviceName" required>
53+
</div>
54+
<div class="form-group">
55+
<button ng-disabled="transfer.deviceName !== transfer.device.name" ng-click="transfer.transferDevice()" class="btn btn-danger col-md-12">{{'TRANSFER_BUTTON_TEXT' | translate}}</button>
56+
</div>
57+
</form>
58+
</div>
59+
</div>
60+
</div>
61+
62+
</div>

app/views/account.dashboard.html

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<div class="container">
2-
<div class="row">
3-
<div class="col-lg-12 col-sm-12 col-md-12" style="padding-top: 15px;">
4-
<div class="thumbnail">
2+
<!-- First row with stats and token transfer -->
3+
<div class="row" style="display: flex; flex-wrap: wrap;">
4+
<div class="col-lg-6 col-sm-12 col-md-12" style="margin-bottom: 30px;">
5+
<div class="thumbnail" style="height: 100%;">
56
<div class="caption">
67
<h3 translate="REGISTERED_BOXES" translate-values="{count: dashboard.boxes.length}"></h3>
78
<p>{{'REGISTERED_BOXES_INFO' | translate}}</p>
@@ -16,6 +17,35 @@ <h3 translate="REGISTERED_BOXES" translate-values="{count: dashboard.boxes.lengt
1617
</div>
1718
</div>
1819

20+
<div class="col-lg-6 col-sm-12 col-md-12" style="margin-bottom: 30px;">
21+
<div class="thumbnail" style="height: 100%;">
22+
<div class="caption">
23+
<h3>{{'CLAIM_HEADER' | translate}}</h3>
24+
<form name="claimForm" novalidate>
25+
<div class="form-group">
26+
<label for="token" ng-bind-html="'CLAIM_INFORMATION'|translate"></label>
27+
<div class="input-group">
28+
<input type="text" name="token" id="token" class="form-control" placeholder="Token" ng-model="dashboard.claimToken" ng-pattern="dashboard.claimPattern" maxlength="12">
29+
<span class="input-group-btn">
30+
<button class="btn btn-primary" type="button" ng-click="dashboard.claimDevice()" ng-disabled="dashboard.claimToken === ''">{{'CLAIM_DEVICE' | translate}}</button>
31+
</span>
32+
</div><!-- /input-group -->
33+
<span class="help-block" ng-show="claimForm.token.$error.pattern">{{'CLAIM_VALID_TOKEN' | translate}}</span><br>
34+
</div>
35+
</form>
36+
<div class="alert alert-danger alert-dismissible" role="alert" ng-show="dashboard.errorMessage">
37+
<button type="button" class="close" data-dismiss="alert" aria-label="Close" ng-click="dashboard.closeAlert()">
38+
<span aria-hidden="true">&times;</span>
39+
</button>
40+
{{dashboard.errorMessage}}
41+
</div>
42+
</div>
43+
</div>
44+
</div>
45+
</div>
46+
47+
<!-- Second row with options for device list -->
48+
<div class="row">
1949
<div class="col-sm-4 col-sm-offset-8 clearfix">
2050
<div class="btn-group pull-right">
2151
<label class="btn btn-default" ng-model="dashboard.listStyle" uib-btn-radio="'tiles'">
@@ -37,8 +67,8 @@ <h3 translate="REGISTERED_BOXES" translate-values="{count: dashboard.boxes.lengt
3767
</button>
3868
</div>
3969
</div>
40-
4170
</div>
71+
4272
<div class="row" ng-show="dashboard.listStyle==='tiles'">
4373
<div class="col-lg-6" ng-repeat="box in dashboard.boxes | orderBy: dashboard.orderByProperty"
4474
style="padding-top: 15px;">

0 commit comments

Comments
 (0)