NOTE: The plans page is still broken

- Change the subscribe method to allow for subscribing to the free plan, even when an org
- Change the frontend to no longer have different plan groups
- Change the frontend to display the proper plans (i.e. hide the deprecated plans unless it is the current plan, etc)
This commit is contained in:
Joseph Schorr 2013-12-19 21:51:46 -05:00
parent e3504b91de
commit 3f062ee602
4 changed files with 91 additions and 85 deletions

View file

@ -1540,7 +1540,7 @@ def subscribe(user, plan, token, require_business_plan):
if not plan_found: if not plan_found:
abort(404) abort(404)
if require_business_plan and not plan_found['bus_features']: if require_business_plan and not plan_found['bus_features'] and not plan_found['price'] == 0:
abort(404) abort(404)
private_repos = model.get_private_repo_count(user.username) private_repos = model.get_private_repo_count(user.username)
@ -1679,7 +1679,7 @@ def get_org_subscription(orgname):
return jsonify(subscription_view(cus.subscription, private_repos)) return jsonify(subscription_view(cus.subscription, private_repos))
return jsonify({ return jsonify({
'plan': 'bus-free', 'plan': 'free',
'usedPrivateRepos': private_repos, 'usedPrivateRepos': private_repos,
}) })

View file

@ -32,14 +32,15 @@
<td></td> <td></td>
</thead> </thead>
<tr ng-repeat="plan in plans" ng-class="(subscribedPlan.stripeId === plan.stripeId) ? getActiveSubClass() : ''"> <tr ng-repeat="plan in plans" ng-show="isPlanVisible(plan, subscribedPlan)"
ng-class="(subscribedPlan.stripeId === plan.stripeId) ? getActiveSubClass() : ''">
<td>{{ plan.title }}</td> <td>{{ plan.title }}</td>
<td>{{ plan.privateRepos }}</td> <td>{{ plan.privateRepos }}</td>
<td><div class="plan-price">${{ plan.price / 100 }}</div></td> <td><div class="plan-price">${{ plan.price / 100 }}</div></td>
<td class="controls"> <td class="controls">
<div ng-switch='plan.stripeId'> <div ng-switch='plan.deprecated'>
<div ng-switch-when='bus-free'> <div ng-switch-when='true'>
<button class="btn button-hidden">Hidden!</button> <b>Existing Plan</b>
</div> </div>
<div ng-switch-default> <div ng-switch-default>
<button class="btn" ng-show="subscribedPlan.stripeId !== plan.stripeId" <button class="btn" ng-show="subscribedPlan.stripeId !== plan.stripeId"

View file

@ -261,6 +261,10 @@ quayApp = angular.module('quay', ['ngRoute', 'chieffancypants.loadingBar', 'rest
var planService = {}; var planService = {};
var listeners = []; var listeners = [];
planService.getFreePlan = function() {
return 'free';
};
planService.registerListener = function(obj, callback) { planService.registerListener = function(obj, callback) {
listeners.push({'obj': obj, 'callback': callback}); listeners.push({'obj': obj, 'callback': callback});
}; };
@ -278,6 +282,27 @@ quayApp = angular.module('quay', ['ngRoute', 'chieffancypants.loadingBar', 'rest
CookieService.putSession('quay.notedplan', planId); CookieService.putSession('quay.notedplan', planId);
}; };
planService.isOrgCompatible = function(plan) {
return plan['stripeId'] == planService.getFreePlan() || plan['bus_features'];
};
planService.getMatchingBusinessPlan = function(callback) {
planService.getPlans(function() {
planService.getSubscription(null, function(sub) {
var plan = planDict[sub.plan];
if (!plan) {
planService.getMinimumPlan(0, true, callback);
return;
}
var count = Math.max(sub.usedPrivateRepos, plan.privateRepos);
planService.getMinimumPlan(count, true, callback);
}, function() {
planService.getMinimumPlan(0, true, callback);
});
});
};
planService.handleNotedPlan = function() { planService.handleNotedPlan = function() {
var planId = planService.getAndResetNotedPlan(); var planId = planService.getAndResetNotedPlan();
if (!planId) { return; } if (!planId) { return; }
@ -288,15 +313,13 @@ quayApp = angular.module('quay', ['ngRoute', 'chieffancypants.loadingBar', 'rest
} }
planService.getPlan(planId, function(plan) { planService.getPlan(planId, function(plan) {
planService.isBusinessPlan(planId, function(bus) { if (planService.isOrgCompatible(plan)) {
if (bus) {
document.location = '/organizations/new/?plan=' + planId; document.location = '/organizations/new/?plan=' + planId;
} else { } else {
document.location = '/user?plan=' + planId; document.location = '/user?plan=' + planId;
} }
}); });
}); });
});
}; };
planService.getAndResetNotedPlan = function() { planService.getAndResetNotedPlan = function() {
@ -333,53 +356,36 @@ quayApp = angular.module('quay', ['ngRoute', 'chieffancypants.loadingBar', 'rest
var getPlans = Restangular.one('plans'); var getPlans = Restangular.one('plans');
getPlans.get().then(function(data) { getPlans.get().then(function(data) {
var i = 0; var i = 0;
for(i = 0; i < data.user.length; i++) { for(i = 0; i < data.plans.length; i++) {
planDict[data.user[i].stripeId] = data.user[i]; planDict[data.plans[i].stripeId] = data.plans[i];
} }
for(i = 0; i < data.business.length; i++) { plans = data.plans;
planDict[data.business[i].stripeId] = data.business[i];
}
plans = data;
callback(plans); callback(plans);
}, function() { callback([]); }); }, function() { callback([]); });
}; };
planService.getMatchingBusinessPlan = function(callback) { planService.getPlans = function(callback, opt_includePersonal) {
planService.getPlans(function() {
planService.getSubscription(null, function(sub) {
var plan = planDict[sub.plan];
if (!plan) {
planService.getMinimumPlan(0, true, callback);
return;
}
var count = Math.max(sub.usedPrivateRepos, plan.privateRepos);
planService.getMinimumPlan(count, true, callback);
}, function() {
planService.getMinimumPlan(0, true, callback);
});
});
};
planService.isBusinessPlan = function(planId, callback) {
planService.verifyLoaded(function() { planService.verifyLoaded(function() {
planSource = plans.business; var filtered = [];
for (var i = 0; i < planSource.length; i++) { for (var i = 0; i < plans.length; ++i) {
var plan = planSource[i]; var plan = plans[i];
if (plan.stripeId == planId) { if (plan['deprecated']) { continue; }
callback(true); if (!opt_includePersonal && !planService.isOrgCompatible(plan)) { continue; }
return; filtered.push(plan);
} }
} callback(filtered);
callback(false);
}); });
}; };
planService.getPlans = function(callback) {
planService.verifyLoaded(callback);
};
planService.getPlan = function(planId, callback) { planService.getPlan = function(planId, callback) {
planService.getPlanIncludingDeprecated(planId, function(plan) {
if (!plan['deprecated']) {
callback(plan);
}
});
};
planService.getPlanIncludingDeprecated = function(planId, callback) {
planService.verifyLoaded(function() { planService.verifyLoaded(function() {
if (planDict[planId]) { if (planDict[planId]) {
callback(planDict[planId]); callback(planDict[planId]);
@ -389,13 +395,12 @@ quayApp = angular.module('quay', ['ngRoute', 'chieffancypants.loadingBar', 'rest
planService.getMinimumPlan = function(privateCount, isBusiness, callback) { planService.getMinimumPlan = function(privateCount, isBusiness, callback) {
planService.verifyLoaded(function() { planService.verifyLoaded(function() {
var planSource = plans.user; for (var i = 0; i < plans.length; i++) {
if (isBusiness) { var plan = plans[i];
planSource = plans.business; if (isBusiness && !planService.isOrgCompatible(plan)) {
continue;
} }
for (var i = 0; i < planSource.length; i++) {
var plan = planSource[i];
if (plan.privateRepos >= privateCount) { if (plan.privateRepos >= privateCount) {
callback(plan); callback(plan);
return; return;
@ -453,6 +458,8 @@ quayApp = angular.module('quay', ['ngRoute', 'chieffancypants.loadingBar', 'rest
} }
planService.getPlan(planId, function(plan) { planService.getPlan(planId, function(plan) {
if (orgname && !planService.isOrgCompatible(plan)) { return; }
planService.getCardInfo(orgname, function(cardInfo) { planService.getCardInfo(orgname, function(cardInfo) {
if (plan.price > 0 && !cardInfo.last4) { if (plan.price > 0 && !cardInfo.last4) {
planService.showSubscribeDialog($scope, orgname, planId, callbacks); planService.showSubscribeDialog($scope, orgname, planId, callbacks);
@ -1843,6 +1850,18 @@ quayApp.directive('planManager', function () {
controller: function($scope, $element, PlanService, Restangular) { controller: function($scope, $element, PlanService, Restangular) {
var hasSubscription = false; var hasSubscription = false;
$scope.isPlanVisible = function(plan, subscribedPlan) {
if (plan['deprecated']) {
return plan == subscribedPlan;
}
if ($scope.organization && !PlanService.isOrgCompatible(plan)) {
return false;
}
return true;
};
$scope.getActiveSubClass = function() { $scope.getActiveSubClass = function() {
return 'active'; return 'active';
}; };
@ -1865,17 +1884,17 @@ quayApp.directive('planManager', function () {
}; };
$scope.cancelSubscription = function() { $scope.cancelSubscription = function() {
$scope.changeSubscription(getFreePlan()); $scope.changeSubscription(PlanService.getFreePlan());
}; };
var subscribedToPlan = function(sub) { var subscribedToPlan = function(sub) {
$scope.subscription = sub; $scope.subscription = sub;
if (sub.plan != getFreePlan()) { if (sub.plan != PlanService.getFreePlan()) {
hasSubscription = true; hasSubscription = true;
} }
PlanService.getPlan(sub.plan, function(subscribedPlan) { PlanService.getPlanIncludingDeprecated(sub.plan, function(subscribedPlan) {
$scope.subscribedPlan = subscribedPlan; $scope.subscribedPlan = subscribedPlan;
$scope.planUsagePercent = sub.usedPrivateRepos * 100 / $scope.subscribedPlan.privateRepos; $scope.planUsagePercent = sub.usedPrivateRepos * 100 / $scope.subscribedPlan.privateRepos;
@ -1905,22 +1924,13 @@ quayApp.directive('planManager', function () {
}); });
}; };
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() { var update = function() {
$scope.planLoading = true; $scope.planLoading = true;
if (!$scope.plans) { return; } if (!$scope.plans) { return; }
PlanService.getSubscription($scope.organization, subscribedToPlan, function() { PlanService.getSubscription($scope.organization, subscribedToPlan, function() {
// User/Organization has no subscription. // User/Organization has no subscription.
subscribedToPlan({ 'plan': getFreePlan() }); subscribedToPlan({ 'plan': PlanService.getFreePlan() });
}); });
}; };
@ -1929,13 +1939,13 @@ quayApp.directive('planManager', function () {
if (!$scope.user && !$scope.organization) { return; } if (!$scope.user && !$scope.organization) { return; }
$scope.loadingPlans = true; $scope.loadingPlans = true;
PlanService.getPlans(function(plans) { PlanService.verifyLoaded(function(plans) {
$scope.plans = plans[$scope.organization ? 'business' : 'user']; $scope.plans = plans;
update(); update();
if ($scope.readyForPlan) { if ($scope.readyForPlan) {
var planRequested = $scope.readyForPlan(); var planRequested = $scope.readyForPlan();
if (planRequested && planRequested != getFreePlan()) { if (planRequested && planRequested != PlanService.getFreePlan()) {
$scope.changeSubscription(planRequested); $scope.changeSubscription(planRequested);
} }
} }

View file

@ -22,7 +22,7 @@ function PlansCtrl($scope, $location, UserService, PlanService) {
// Load the list of plans. // Load the list of plans.
PlanService.getPlans(function(plans) { PlanService.getPlans(function(plans) {
$scope.plans = plans; $scope.plans = plans;
}); }, /* include the personal plan */ true);
// Monitor any user changes and place the current user into the scope. // Monitor any user changes and place the current user into the scope.
UserService.updateUserIn($scope); UserService.updateUserIn($scope);
@ -621,7 +621,7 @@ function UserAdminCtrl($scope, $timeout, $location, Restangular, PlanService, Us
}); });
PlanService.getPlans(function(plans) { PlanService.getPlans(function(plans) {
$scope.orgPlans = plans.business; $scope.orgPlans = plans;
}); });
$scope.convertStep = 1; $scope.convertStep = 1;
@ -1074,17 +1074,12 @@ function OrgAdminCtrl($rootScope, $scope, Restangular, $routeParams, UserService
// Load the list of plans. // Load the list of plans.
PlanService.getPlans(function(plans) { PlanService.getPlans(function(plans) {
$scope.plans = plans.business; $scope.plans = plans;
$scope.plan_map = {}; $scope.plan_map = {};
var addPlans = function(plans) {
for (var i = 0; i < plans.length; ++i) { for (var i = 0; i < plans.length; ++i) {
$scope.plan_map[plans[i].stripeId] = plans[i]; $scope.plan_map[plans[i].stripeId] = plans[i];
} }
};
addPlans(plans.user);
addPlans(plans.business);
}); });
$scope.orgname = orgname; $scope.orgname = orgname;
@ -1230,7 +1225,7 @@ function NewOrgCtrl($scope, $routeParams, $timeout, $location, UserService, Plan
// Load the list of plans. // Load the list of plans.
PlanService.getPlans(function(plans) { PlanService.getPlans(function(plans) {
$scope.plans = plans.business; $scope.plans = plans;
$scope.currentPlan = null; $scope.currentPlan = null;
if (requested) { if (requested) {
PlanService.getPlan(requested, function(plan) { PlanService.getPlan(requested, function(plan) {