1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-27 12:36:14 +01:00

org settings and billing

This commit is contained in:
Kyle Spearrin 2017-04-06 16:52:25 -04:00
parent 7591843220
commit 80e4d2329a
8 changed files with 249 additions and 9 deletions

View File

@ -0,0 +1,8 @@
angular
.module('bit.filters')
.filter('jsonDate', function () {
return function (input) {
return input.split('T').join(' ');
};
});

View File

@ -1,6 +1,72 @@
angular angular
.module('bit.organization') .module('bit.organization')
.controller('organizationBillingController', function ($scope) { .controller('organizationBillingController', function ($scope, apiService, $state) {
$scope.charges = [];
$scope.paymentSource = null;
$scope.plan = null;
$scope.subscription = null;
$scope.loading = true;
$scope.$on('$viewContentLoaded', function () {
apiService.organizations.getBilling({ id: $state.params.orgId }, function (org) {
$scope.loading = false;
$scope.plan = {
name: org.Plan,
type: org.PlanType,
maxUsers: org.MaxUsers
};
$scope.subscription = {
trialEndDate: org.Subscription.TrialEndDate,
nextBillDate: org.Subscription.NextBillDate,
cancelNext: org.Subscription.CancelAtNextBillDate,
status: org.Subscription.Status
};
if (org.Subscription.Items) {
$scope.subscription.items = [];
for (var i = 0; i < org.Subscription.Items.length; i++) {
$scope.subscription.items.push({
amount: org.Subscription.Items[i].Amount,
name: org.Subscription.Items[i].Name,
interval: org.Subscription.Items[i].Interval,
qty: org.Subscription.Items[i].Quantity
});
}
}
if (org.PaymentSource) {
$scope.paymentSource = {
type: org.PaymentSource.Type,
description: org.PaymentSource.Description,
cardBrand: org.PaymentSource.CardBrand
};
}
var charges = [];
for (var i = 0; i < org.Charges.length; i++) {
charges.push({
date: org.Charges[i].CreatedDate,
paymentSource: org.Charges[i].PaymentSource ? org.Charges[i].PaymentSource.Description : '-',
amount: org.Charges[i].Amount,
status: org.Charges[i].Status,
failureMessage: org.Charges[i].FailureMessage,
refunded: org.Charges[i].Refunded,
partiallyRefunded: org.Charges[i].PartiallyRefunded,
refundedAmount: org.Charges[i].RefundedAmount
});
}
$scope.charges = charges;
});
});
$scope.changePayment = function () {
};
$scope.cancel = function () {
};
}); });

View File

@ -1,6 +1,23 @@
angular angular
.module('bit.organization') .module('bit.organization')
.controller('organizationSettingsController', function ($scope) { .controller('organizationSettingsController', function ($scope, $state, apiService, toastr, authService) {
$scope.model = {};
$scope.$on('$viewContentLoaded', function () {
apiService.organizations.get({ id: $state.params.orgId }, function (org) {
$scope.model = {
name: org.Name,
billingEmail: org.BillingEmail,
businessName: org.BusinessName
};
});
});
$scope.generalSave = function () {
$scope.generalPromise = apiService.organizations.put({ id: $state.params.orgId }, $scope.model, function (org) {
authService.updateProfileOrganization(org).then(function (updatedOrg) {
toastr.success('Organization has been updated.', 'Success!');
});
}).$promise;
};
}); });

View File

@ -7,10 +7,110 @@
<section class="content"> <section class="content">
<div class="box"> <div class="box">
<div class="box-header with-border"> <div class="box-header with-border">
<h3 class="box-title"><i class="fa fa-server"></i> My Org</h3> <h3 class="box-title">Plan</h3>
</div> </div>
<div class="box-body"> <div class="box-body">
Some data <div class="row">
<div class="col-sm-6">
<dl>
<dt>Name</dt>
<dd>{{plan.name}}</dd>
<dt>Maximum Users</dt>
<dd>{{plan.maxUsers}}</dd>
</dl>
</div>
<div class="col-sm-6">
<dl>
<dt>Status</dt>
<dd style="text-transform: capitalize;">{{subscription.status}}</dd>
<dt>Next Bill Date</dt>
<dd>{{subscription.nextBillDate | jsonDate}}</dd>
</dl>
</div>
</div>
<div class="row">
<div class="col-md-6">
<strong>Details</strong>
<div ng-show="loading">
Loading...
</div>
<div class="table-responsive" style="margin: 0;" ng-show="!loading">
<table class="table" style="margin: 0;">
<tbody>
<tr ng-repeat="item in subscription.items">
<td>
{{item.name}} {{item.qty > 1 ? '&times;' + item.qty : ''}}
@ {{item.amount | currency:'$'}} /{{item.interval}}
</td>
<td class="text-right">{{(item.qty * item.amount) | currency:'$'}} /{{item.interval}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="box-footer">
<button type="button" class="btn btn-default btn-flat" ng-click="changePlan()">
Change Plan
</button>
<button type="button" class="btn btn-default btn-flat" ng-click="cancel()">
Cancel Plan
</button>
</div>
</div>
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Payment Method</h3>
</div>
<div class="box-body">
<div ng-show="loading">
Loading...
</div>
<div ng-show="!loading && !paymentMethod">
<i class="fa fa-credit-card"></i> No payment method on file.
</div>
<div ng-show="!loading && paymentMethod">
</div>
</div>
<div class="box-footer">
<button type="button" class="btn btn-default btn-flat" ng-click="changePayment()">
{{ paymentMethod ? 'Change Payment Method' : 'Add Payment Method' }}
</button>
</div>
</div>
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Charges</h3>
</div>
<div class="box-body">
<div ng-show="loading">
Loading...
</div>
<div ng-show="!loading && !charges.length">
No charges.
</div>
<div class="table-responsive" ng-show="charges.length">
<table class="table">
<tbody>
<tr ng-repeat="charge in charges">
<td style="width: 200px">
{{charge.date | jsonDate}}
</td>
<td>
{{charge.paymentSource}}
</td>
<td style="width: 150px; text-transform: capitalize;">
{{charge.status}}
</td>
<td class="text-right" style="width: 150px;">
{{charge.amount | currency:'$'}}
</td>
</tr>
</tbody>
</table>
</div>
</div> </div>
</div> </div>
</section> </section>

View File

@ -5,12 +5,48 @@
</h1> </h1>
</section> </section>
<section class="content"> <section class="content">
<div class="box"> <div class="box box-default">
<div class="box-header with-border"> <div class="box-header with-border">
<h3 class="box-title"><i class="fa fa-server"></i> My Org</h3> <h3 class="box-title">General</h3>
</div> </div>
<form role="form" name="generalForm" ng-submit="generalForm.$valid && generalSave()" api-form="generalPromise">
<div class="box-body"> <div class="box-body">
Some data <div class="row">
<div class="col-sm-9">
<div class="callout callout-danger validation-errors" ng-show="generalForm.$errors">
<h4>Errors have occured</h4>
<ul>
<li ng-repeat="e in generalForm.$errors">{{e}}</li>
</ul>
</div> </div>
<div class="form-group" show-errors>
<label for="name">Organization Name</label>
<input type="text" id="name" name="Name" ng-model="model.name" class="form-control"
required api-field />
</div>
<div class="form-group" show-errors>
<label for="name">Business Name</label>
<input type="text" id="businessName" name="BusinessName" ng-model="model.businessName"
class="form-control" api-field />
</div>
<div class="form-group" show-errors>
<label for="name">Billing Email</label>
<input type="email" id="billingEmail" name="BillingEmail" ng-model="model.billingEmail"
class="form-control" required api-field />
</div>
</div>
<div class="col-sm-3 settings-photo">
<letter-avatar data="{{model.name}}" round="false"
avclass="img-responsive img-rounded" avwidth="200" avheight="200"
fontsize="90"></letter-avatar>
</div>
</div>
</div>
<div class="box-footer">
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="generalForm.$loading">
<i class="fa fa-refresh fa-spin loading-icon" ng-show="generalForm.$loading"></i>Save
</button>
</div>
</form>
</div> </div>
</section> </section>

View File

@ -35,6 +35,7 @@
_service.organizations = $resource(_apiUri + '/organizations/:id', {}, { _service.organizations = $resource(_apiUri + '/organizations/:id', {}, {
get: { method: 'GET', params: { id: '@id' } }, get: { method: 'GET', params: { id: '@id' } },
getBilling: { url: _apiUri + '/organizations/:id/billing', method: 'GET', params: { id: '@id' } },
list: { method: 'GET', params: {} }, list: { method: 'GET', params: {} },
post: { method: 'POST', params: {} }, post: { method: 'POST', params: {} },
put: { method: 'POST', params: { id: '@id' } }, put: { method: 'POST', params: { id: '@id' } },

View File

@ -140,6 +140,17 @@ angular
}); });
}; };
_service.updateProfileOrganization = function (org) {
return _service.getUserProfile().then(function (profile) {
if (profile) {
if (profile.organizations && org.Id in profile.organizations) {
profile.organizations[org.Id].name = org.Name;
_userProfile = profile;
}
}
});
};
_service.isAuthenticated = function () { _service.isAuthenticated = function () {
return tokenService.getToken() !== null; return tokenService.getToken() !== null;
}; };

View File

@ -93,6 +93,7 @@
<script src="app/filters/filtersModule.js"></script> <script src="app/filters/filtersModule.js"></script>
<script src="app/filters/enumNameFilter.js"></script> <script src="app/filters/enumNameFilter.js"></script>
<script src="app/filters/enumLabelClassFilter.js"></script> <script src="app/filters/enumLabelClassFilter.js"></script>
<script src="app/filters/jsonDateFilter.js"></script>
<script src="app/services/servicesModule.js"></script> <script src="app/services/servicesModule.js"></script>
<script src="app/services/tokenService.js"></script> <script src="app/services/tokenService.js"></script>