Check in all new plan manager directive and add a nice donut chart for the repository usage by the user/org
This commit is contained in:
parent
934acce6d4
commit
a6a225dd5f
6 changed files with 377 additions and 167 deletions
|
@ -1358,6 +1358,41 @@ p.editable:hover i {
|
|||
stroke-width: 1.5px;
|
||||
}
|
||||
|
||||
#repository-usage-chart {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
#repository-usage-chart .count-text {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
#repository-usage-chart path.warning-0 {
|
||||
fill: #c09853;
|
||||
}
|
||||
|
||||
#repository-usage-chart path.error-0 {
|
||||
fill: #b94a48;
|
||||
}
|
||||
|
||||
#repository-usage-chart path.warning-1 {
|
||||
fill: #fcf8e3;
|
||||
}
|
||||
|
||||
#repository-usage-chart path.error-1 {
|
||||
fill: #f2dede;
|
||||
}
|
||||
|
||||
.plan-manager-element .usage-caption {
|
||||
display: inline-block;
|
||||
color: #aaa;
|
||||
font-size: 26px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
|
||||
/* Overrides for the markdown editor. */
|
||||
|
||||
.wmd-panel .btn-toolbar {
|
||||
|
|
58
static/directives/plan-manager.html
Normal file
58
static/directives/plan-manager.html
Normal file
|
@ -0,0 +1,58 @@
|
|||
<div class="plan-manager-element">
|
||||
<!-- Loading/Changing -->
|
||||
<i class="fa fa-spinner fa-spin fa-3x" ng-show="planLoading"></i>
|
||||
|
||||
<!-- Alerts -->
|
||||
<div class="alert alert-danger" ng-show="overLimit && !planLoading">
|
||||
You are using more private repositories than your plan allows, please
|
||||
upgrade your subscription to avoid disruptions in your organization's service.
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning" ng-show="nearLimit && !planLoading">
|
||||
You are nearing the number of allowed private repositories. It might be time to think about
|
||||
upgrading your subscription to avoid future disruptions in your organization's service.
|
||||
</div>
|
||||
|
||||
<!-- Chart -->
|
||||
<div>
|
||||
<div id="repository-usage-chart"></div>
|
||||
<span class="usage-caption" ng-show="chart">Repository Usage</span>
|
||||
</div>
|
||||
|
||||
<!-- Plans Table -->
|
||||
<table class="table table-hover plans-table" ng-show="!planLoading">
|
||||
<thead>
|
||||
<td>Plan</td>
|
||||
<td>Private Repositories</td>
|
||||
<td style="min-width: 64px">Price</td>
|
||||
<td></td>
|
||||
</thead>
|
||||
|
||||
<tr ng-repeat="plan in plans" ng-class="(subscribedPlan.stripeId === plan.stripeId) ? getActiveSubClass() : ''">
|
||||
<td>{{ plan.title }}</td>
|
||||
<td>{{ plan.privateRepos }}</td>
|
||||
<td><div class="plan-price">${{ plan.price / 100 }}</div></td>
|
||||
<td class="controls">
|
||||
<div ng-switch='plan.stripeId'>
|
||||
<div ng-switch-when='bus-free'>
|
||||
<button class="btn button-hidden">Hidden!</button>
|
||||
</div>
|
||||
<div ng-switch-default>
|
||||
<button class="btn" ng-show="subscribedPlan.stripeId !== plan.stripeId"
|
||||
ng-class="subscribedPlan.price == 0 ? 'btn-primary' : 'btn-default'"
|
||||
ng-click="changeSubscription(plan.stripeId)">
|
||||
<i class="fa fa-spinner fa-spin" ng-show="planChanging"></i>
|
||||
<span ng-show="!planChanging && subscribedPlan.price != 0">Change</span>
|
||||
<span ng-show="!planChanging && subscribedPlan.price == 0">Subscribe</span>
|
||||
</button>
|
||||
<button class="btn btn-danger" ng-show="subscription.plan === plan.stripeId"
|
||||
ng-click="cancelSubscription()">
|
||||
<i class="fa fa-spinner fa-spin" ng-show="planChanging"></i>
|
||||
<span ng-show="!planChanging">Cancel</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
171
static/js/app.js
171
static/js/app.js
|
@ -48,7 +48,7 @@ function getMarkedDown(string) {
|
|||
|
||||
// Start the application code itself.
|
||||
quayApp = angular.module('quay', ['restangular', 'angularMoment', 'angulartics', 'angulartics.mixpanel', '$strap.directives'], function($provide) {
|
||||
$provide.factory('UserService', ['Restangular', function(Restangular) {
|
||||
$provide.factory('UserService', ['Restangular', 'PlanService', function(Restangular, PlanService) {
|
||||
var userResponse = {
|
||||
verified: false,
|
||||
anonymous: true,
|
||||
|
@ -85,9 +85,7 @@ quayApp = angular.module('quay', ['restangular', 'angularMoment', 'angulartics',
|
|||
|
||||
userService.getCurrentSubscription = function(callback, failure) {
|
||||
if (currentSubscription) { callback(currentSubscription); }
|
||||
|
||||
var getSubscription = Restangular.one('user/plan');
|
||||
getSubscription.get().then(function(sub) {
|
||||
PlanService.getSubscription(null, function(sub) {
|
||||
currentSubscription = sub;
|
||||
callback(sub);
|
||||
}, failure);
|
||||
|
@ -171,23 +169,47 @@ quayApp = angular.module('quay', ['restangular', 'angularMoment', 'angulartics',
|
|||
});
|
||||
};
|
||||
|
||||
planService.showSubscribeDialog = function($scope, planId, orgname, started, success, failed) {
|
||||
var submitToken = function(token) {
|
||||
$scope.$apply(function() {
|
||||
started();
|
||||
});
|
||||
planService.getSubscription = function(organization, success, failure) {
|
||||
var url = planService.getSubscriptionUrl(organization);
|
||||
var getSubscription = Restangular.one(url);
|
||||
getSubscription.get().then(success, failure);
|
||||
};
|
||||
|
||||
planService.getSubscriptionUrl = function(orgname) {
|
||||
return orgname ? getRestUrl('organization', orgname, 'plan') : 'user/plan';
|
||||
};
|
||||
|
||||
planService.setSubscription = function(orgname, planId, success, failure, opt_token) {
|
||||
var subscriptionDetails = {
|
||||
plan: planId
|
||||
};
|
||||
|
||||
if (opt_token) {
|
||||
subscriptionDetails['token'] = opt_token.id;
|
||||
}
|
||||
|
||||
var url = planService.getSubscriptionUrl(orgname);
|
||||
var createSubscriptionRequest = Restangular.one(url);
|
||||
createSubscriptionRequest.customPUT(subscriptionDetails).then(success, failure);
|
||||
};
|
||||
|
||||
planService.changePlan = function($scope, orgname, planId, hasExistingSubscription, started, success, failure) {
|
||||
if (!hasExistingSubscription) {
|
||||
planService.showSubscribeDialog($scope, orgname, planId, started, success, failure);
|
||||
return;
|
||||
}
|
||||
|
||||
started();
|
||||
planService.setSubscription(orgname, planId, success, failure);
|
||||
};
|
||||
|
||||
planService.showSubscribeDialog = function($scope, orgname, planId, started, success, failure) {
|
||||
var submitToken = function(token) {
|
||||
mixpanel.track('plan_subscribe');
|
||||
|
||||
var subscriptionDetails = {
|
||||
token: token.id,
|
||||
plan: planId,
|
||||
};
|
||||
|
||||
var url = orgname ? getRestUrl('organization', orgname, 'plan') : 'user/plan';
|
||||
var createSubscriptionRequest = Restangular.one(url);
|
||||
$scope.$apply(function() {
|
||||
createSubscriptionRequest.customPUT(subscriptionDetails).then(success, failed);
|
||||
started();
|
||||
planService.setSubscription(orgname, planId, success, failure);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -485,6 +507,121 @@ quayApp.directive('roleGroup', function () {
|
|||
});
|
||||
|
||||
|
||||
quayApp.directive('planManager', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
templateUrl: '/static/directives/plan-manager.html',
|
||||
replace: false,
|
||||
transclude: false,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'user': '=user',
|
||||
'organization': '=organization'
|
||||
},
|
||||
controller: function($scope, $element, PlanService, Restangular) {
|
||||
var hasSubscription = false;
|
||||
|
||||
$scope.getActiveSubClass = function() {
|
||||
return 'active';
|
||||
};
|
||||
|
||||
$scope.changeSubscription = function(planId) {
|
||||
if ($scope.planChanging) { return; }
|
||||
|
||||
PlanService.changePlan($scope, $scope.organization, planId, hasSubscription, function() {
|
||||
// Started.
|
||||
$scope.planChanging = true;
|
||||
}, function(sub) {
|
||||
// Success.
|
||||
subscribedToPlan(sub);
|
||||
}, function() {
|
||||
// Failure.
|
||||
$scope.planChanging = false;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.cancelSubscription = function() {
|
||||
$scope.changeSubscription(getFreePlan());
|
||||
};
|
||||
|
||||
var subscribedToPlan = function(sub) {
|
||||
$scope.subscription = sub;
|
||||
|
||||
if (sub.plan != getFreePlan()) {
|
||||
hasSubscription = true;
|
||||
}
|
||||
|
||||
PlanService.getPlan(sub.plan, function(subscribedPlan) {
|
||||
$scope.subscribedPlan = subscribedPlan;
|
||||
$scope.planUsagePercent = sub.usedPrivateRepos * 100 / $scope.subscribedPlan.privateRepos;
|
||||
|
||||
if (sub.usedPrivateRepos > $scope.subscribedPlan.privateRepos) {
|
||||
$scope.overLimit = true;
|
||||
} else if (sub.usedPrivateRepos >= $scope.subscribedPlan.privateRepos * 0.7) {
|
||||
$scope.nearLimit = true;
|
||||
} else {
|
||||
$scope.overLimit = false;
|
||||
$scope.nearLimit = false;
|
||||
}
|
||||
|
||||
if (!$scope.chart) {
|
||||
$scope.chart = new RepositoryUsageChart();
|
||||
$scope.chart.draw('repository-usage-chart');
|
||||
}
|
||||
|
||||
$scope.chart.update(sub.usedPrivateRepos || 0, $scope.subscribedPlan.privateRepos || 0);
|
||||
|
||||
$scope.planChanging = false;
|
||||
$scope.planLoading = false;
|
||||
});
|
||||
};
|
||||
|
||||
var getFreePlan = function() {
|
||||
for (var i = 0; i < $scope.plans.length; ++i) {
|
||||
if ($scope.plans[i].price == 0) {
|
||||
return $scope.plans[i].stripeId;
|
||||
}
|
||||
}
|
||||
return 'free';
|
||||
};
|
||||
|
||||
var update = function() {
|
||||
$scope.planLoading = true;
|
||||
|
||||
if (!$scope.plans) { return; }
|
||||
if (!$scope.user && !$scope.organization) { return; }
|
||||
|
||||
PlanService.getSubscription($scope.organization, subscribedToPlan, function() {
|
||||
// User/Organization has no subscription.
|
||||
subscribedToPlan({ 'plan': getFreePlan() });
|
||||
});
|
||||
};
|
||||
|
||||
var loadPlans = function() {
|
||||
PlanService.getPlans(function(plans) {
|
||||
$scope.plans = plans[$scope.organization ? 'business' : 'user'];
|
||||
update();
|
||||
});
|
||||
};
|
||||
|
||||
// Start the initial download.
|
||||
loadPlans();
|
||||
update();
|
||||
|
||||
$scope.$watch('organization', function() {
|
||||
update();
|
||||
});
|
||||
|
||||
$scope.$watch('user', function() {
|
||||
update();
|
||||
});
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
});
|
||||
|
||||
|
||||
|
||||
quayApp.directive('namespaceSelector', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
|
|
|
@ -1191,75 +1191,6 @@ function OrgAdminCtrl($rootScope, $scope, Restangular, $routeParams, UserService
|
|||
});
|
||||
};
|
||||
|
||||
var subscribedToPlan = function(sub) {
|
||||
$scope.planChanging = false;
|
||||
$scope.subscription = sub;
|
||||
PlanService.getPlan(sub.plan, function(subscribedPlan) {
|
||||
$scope.subscribedPlan = subscribedPlan;
|
||||
$scope.planUsagePercent = sub.usedPrivateRepos * 100 / $scope.subscribedPlan.privateRepos;
|
||||
|
||||
if (sub.usedPrivateRepos > $scope.subscribedPlan.privateRepos) {
|
||||
$scope.overLimit = true;
|
||||
} else if (sub.usedPrivateRepos >= $scope.subscribedPlan.privateRepos * 0.7) {
|
||||
$scope.nearLimit = true;
|
||||
} else {
|
||||
$scope.overLimit = false;
|
||||
$scope.nearLimit = false;
|
||||
}
|
||||
|
||||
$scope.planLoading = false;
|
||||
});
|
||||
};
|
||||
|
||||
var loadSubscription = function() {
|
||||
$scope.planLoading = true;
|
||||
|
||||
var getSubscription = Restangular.one(getRestUrl('organization', orgname, 'plan'));
|
||||
getSubscription.get().then(subscribedToPlan, function() {
|
||||
// Organization has no subscription.
|
||||
subscribedToPlan({'plan': 'bus-free'});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.getActiveSubClass = function() {
|
||||
if ($scope.overLimit) { return 'danger'; }
|
||||
if ($scope.nearLimit) { return 'warning'; }
|
||||
return 'success';
|
||||
};
|
||||
|
||||
$scope.subscribe = function(planId) {
|
||||
$scope.planChanging = true;
|
||||
PlanService.showSubscribeDialog($scope, planId, orgname, function() {
|
||||
// Subscribing.
|
||||
}, function(plan) {
|
||||
// Subscribed.
|
||||
subscribedToPlan(plan);
|
||||
}, function() {
|
||||
// Failure.
|
||||
$scope.planChanging = false;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.changeSubscription = function(planId) {
|
||||
$scope.planChanging = true;
|
||||
$scope.errorMessage = undefined;
|
||||
|
||||
var subscriptionDetails = {
|
||||
plan: planId,
|
||||
};
|
||||
|
||||
var changeSubscriptionRequest = Restangular.one(getRestUrl('organization', orgname, 'plan'));
|
||||
changeSubscriptionRequest.customPUT(subscriptionDetails).then(subscribedToPlan, function() {
|
||||
// Failure
|
||||
$scope.planChanging = false;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.cancelSubscription = function() {
|
||||
$scope.changeSubscription('bus-free');
|
||||
};
|
||||
|
||||
loadSubscription();
|
||||
loadOrganization();
|
||||
}
|
||||
|
||||
|
|
|
@ -1130,4 +1130,130 @@ ImageFileChangeTree.prototype.toggle_ = function(d) {
|
|||
d.children = d._children;
|
||||
d._children = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Based off of http://bl.ocks.org/mbostock/1346410
|
||||
*/
|
||||
function RepositoryUsageChart() {
|
||||
this.total_ = null;
|
||||
this.count_ = null;
|
||||
this.drawn_ = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates the chart with the given count and total of number of repositories.
|
||||
*/
|
||||
RepositoryUsageChart.prototype.update = function(count, total) {
|
||||
if (!this.g_) { return; }
|
||||
this.total_ = total;
|
||||
this.count_ = count;
|
||||
this.drawInternal_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Conducts the actual draw or update (if applicable).
|
||||
*/
|
||||
RepositoryUsageChart.prototype.drawInternal_ = function() {
|
||||
// If the total is null, then we have not yet set the proper counts.
|
||||
if (this.total_ === null) { return; }
|
||||
|
||||
var duration = 750;
|
||||
|
||||
var arc = this.arc_;
|
||||
var pie = this.pie_;
|
||||
var arcTween = this.arcTween_;
|
||||
|
||||
var color = d3.scale.category20();
|
||||
var count = this.count_;
|
||||
var total = this.total_;
|
||||
|
||||
var data = [count, Math.max(0, total - count)];
|
||||
|
||||
var getClass = function(i) {
|
||||
if (total > 0 && (count / total) >= 0.7) {
|
||||
return 'warning-' + i;
|
||||
}
|
||||
|
||||
if (count >= total) {
|
||||
return 'error-' + i;
|
||||
}
|
||||
|
||||
return 'normal';
|
||||
};
|
||||
|
||||
var arcTween = function(a) {
|
||||
var i = d3.interpolate(this._current, a);
|
||||
this._current = i(0);
|
||||
return function(t) {
|
||||
return arc(i(t));
|
||||
};
|
||||
};
|
||||
|
||||
if (!this.drawn_) {
|
||||
var text = this.g_.append("svg:text")
|
||||
.attr("dy", 10)
|
||||
.attr("dx", 0)
|
||||
.attr('dominant-baseline', 'auto')
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('class', 'count-text')
|
||||
.text(this.count_ + ' / ' + this.total_);
|
||||
|
||||
var path = this.g_.datum(data).selectAll("path")
|
||||
.data(pie)
|
||||
.enter().append("path")
|
||||
.attr("fill", function(d, i) { return color(i); })
|
||||
.attr("class", function(d, i) { return getClass(i); })
|
||||
.attr("d", arc)
|
||||
.each(function(d) { this._current = d; }); // store the initial angles
|
||||
|
||||
this.path_ = path;
|
||||
this.text_ = text;
|
||||
} else {
|
||||
pie.value(function(d, i) { return data[i]; }); // change the value function
|
||||
this.path_ = this.path_.data(pie); // compute the new angles
|
||||
|
||||
this.path_.transition().duration(duration).attrTween("d", arcTween); // redraw the arcs
|
||||
this.path_.attr("class", function(d, i) { return getClass(i); });
|
||||
|
||||
// Update the text.
|
||||
this.text_.text(this.count_ + ' / ' + this.total_);
|
||||
}
|
||||
|
||||
this.drawn_ = true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Draws the chart in the given container.
|
||||
*/
|
||||
RepositoryUsageChart.prototype.draw = function(container) {
|
||||
var cw = document.getElementById(container).clientWidth;
|
||||
var ch = document.getElementById(container).clientHeight;
|
||||
var radius = Math.min(cw, ch) / 2;
|
||||
|
||||
var pie = d3.layout.pie().sort(null);
|
||||
|
||||
var arc = d3.svg.arc()
|
||||
.innerRadius(radius - 50)
|
||||
.outerRadius(radius - 25);
|
||||
|
||||
var svg = d3.select("#" + container).append("svg:svg")
|
||||
.attr("width", cw)
|
||||
.attr("height", ch);
|
||||
|
||||
var g = svg.append("g")
|
||||
.attr("transform", "translate(" + cw / 2 + "," + ch / 2 + ")");
|
||||
|
||||
this.svg_ = svg;
|
||||
this.g_ = g;
|
||||
this.pie_ = pie;
|
||||
this.arc_ = arc;
|
||||
this.width_ = cw;
|
||||
this.drawInternal_();
|
||||
};
|
|
@ -13,94 +13,17 @@
|
|||
<!-- Side tabs -->
|
||||
<div class="col-md-2">
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
<li class="active"><a href="javascript:void(0)" data-toggle="tab" data-target="#usage">Current Usage</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#plan">Plan/Billing</a></li>
|
||||
<li class="active"><a href="javascript:void(0)" data-toggle="tab" data-target="#plan">Plan and Usage</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#members">Members</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="col-md-10">
|
||||
<div class="tab-content">
|
||||
<!-- Usage tab -->
|
||||
<div id="usage" class="tab-pane active">
|
||||
<div class="alert alert-danger" ng-show="overLimit">
|
||||
You are using more private repositories than your plan allows, please
|
||||
<a href="javascript:void(0)" data-toggle="tab" data-target="#plan">upgrade your subscription</a>
|
||||
to avoid disruptions in your organization's service.
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning" ng-show="nearLimit">
|
||||
You are nearing the number of allowed private repositories. It might be time to think about
|
||||
<a href="javascript:void(0)" data-toggle="tab" data-target="#plan">upgrading your subscription</a>
|
||||
to avoid future disruptions in your organization's service.
|
||||
</div>
|
||||
|
||||
<!-- Subscription -->
|
||||
<i class="fa fa-spinner fa-spin fa-3x" ng-show="planLoading"></i>
|
||||
<div class="row" ng-show="!planLoading && subscription">
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
Current Usage
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="used-description">
|
||||
<b>{{ subscription.usedPrivateRepos }}</b> of <b>{{ subscribedPlan.privateRepos }}</b> private repositories used
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div ng-class="'progress-bar ' + (planUsagePercent > 90 ? 'progress-bar-danger' : '')" role="progressbar" aria-valuenow="{{ subscription.usedPrivateRepos }}" aria-valuemin="0" aria-valuemax="{{ subscribedPlan.privateRepos }}" style="width: {{ planUsagePercent }}%;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-content">
|
||||
<!-- Plans tab -->
|
||||
<div id="plan" class="tab-pane">
|
||||
<div class="alert alert-danger" ng-show="overLimit">
|
||||
You are using more private repositories than your plan allows, please
|
||||
upgrade your subscription to avoid disruptions in your organization's service.
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning" ng-show="nearLimit">
|
||||
You are nearing the number of allowed private repositories. It might be time to think about
|
||||
upgrading your subscription to avoid future disruptions in your organization's service.
|
||||
</div>
|
||||
|
||||
<i class="fa fa-spinner fa-spin fa-3x" ng-show="planChanging"></i>
|
||||
|
||||
<table class="table table-hover plans-table" ng-show="!planChanging">
|
||||
<thead>
|
||||
<td>Plan</td>
|
||||
<td>Private Repositories</td>
|
||||
<td style="min-width: 64px">Price</td>
|
||||
<td></td>
|
||||
</thead>
|
||||
|
||||
<tr ng-repeat="plan in plans" ng-class="(subscription.plan === plan.stripeId) ? getActiveSubClass() : ''">
|
||||
<td>{{ plan.title }}</td>
|
||||
<td>{{ plan.privateRepos }}</td>
|
||||
<td><div class="plan-price">${{ plan.price / 100 }}</div></td>
|
||||
<td class="controls">
|
||||
<div ng-switch='plan.stripeId'>
|
||||
<div ng-switch-when='bus-free'>
|
||||
<button class="btn button-hidden">Hidden!</button>
|
||||
</div>
|
||||
<div ng-switch-default>
|
||||
<button class="btn btn-primary" ng-show="subscription.plan === 'bus-free'" ng-click="subscribe(plan.stripeId)">Subscribe</button>
|
||||
<button class="btn btn-default" ng-hide="subscription.plan === 'bus-free' || subscription.plan === plan.stripeId"
|
||||
ng-click="changeSubscription(plan.stripeId)">
|
||||
Change
|
||||
</button>
|
||||
<button class="btn btn-danger" ng-show="subscription.plan === plan.stripeId" ng-click="cancelSubscription()">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div id="plan" class="tab-pane active">
|
||||
<div class="plan-manager" organization="organization.name"></div>
|
||||
</div>
|
||||
|
||||
<!-- Members tab -->
|
||||
|
|
Reference in a new issue