Teams work.

master
Nick Sergeant 2015-10-15 22:37:54 -04:00
parent 002fbc45c5
commit ae1d532c06
7 changed files with 155 additions and 9 deletions

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('teams', '0005_auto_20150930_2124'),
]
operations = [
migrations.AddField(
model_name='team',
name='plan',
field=models.CharField(default=b'snipt-teams-25-monthly', max_length=100, choices=[(b'snipt-teams-25-monthly', b'25 users, monthly'), (b'snipt-teams-100-monthly', b'100 users, monthly'), (b'snipt-teams-250-monthly', b'250 users, monthly'), (b'snipt-teams-unlimited-monthly', b'Unlimited users, monthly'), (b'snipt-teams-25-yearly', b'25 users, yearly'), (b'snipt-teams-100-yearly', b'100 users, yearly'), (b'snipt-teams-250-yearly', b'250 users, yearly'), (b'snipt-teams-unlimited-yearly', b'Unlimited users, yearly')]),
),
]

View File

@ -4,6 +4,18 @@ from snipts.utils import slugify_uniquely
class Team(models.Model):
PLANS = (
('snipt-teams-25-monthly', '25 users, monthly'),
('snipt-teams-100-monthly', '100 users, monthly'),
('snipt-teams-250-monthly', '250 users, monthly'),
('snipt-teams-unlimited-monthly', 'Unlimited users, monthly'),
('snipt-teams-25-yearly', '25 users, yearly'),
('snipt-teams-100-yearly', '100 users, yearly'),
('snipt-teams-250-yearly', '250 users, yearly'),
('snipt-teams-unlimited-yearly', 'Unlimited users, yearly'),
)
email = models.EmailField(max_length=255)
members = models.ManyToManyField(User, related_name='member', blank=True)
name = models.CharField(max_length=30)
@ -11,6 +23,8 @@ class Team(models.Model):
slug = models.SlugField(max_length=255, blank=True)
stripe_id = models.CharField(max_length=100, null=True, blank=True)
user = models.OneToOneField(User, blank=True, null=True)
plan = models.CharField(max_length=100, default='snipt-teams-25-monthly',
choices=PLANS)
created = models.DateTimeField(auto_now_add=True, editable=False)
modified = models.DateTimeField(auto_now=True, editable=False)
@ -22,3 +36,21 @@ class Team(models.Model):
def __unicode__(self):
return self.name
@property
def member_limit(self):
plan_map = {
'snipt-teams-25-monthly': 25,
'snipt-teams-100-monthly': 100,
'snipt-teams-250-monthly': 250,
'snipt-teams-unlimited-monthly': float('inf'),
'snipt-teams-25-yearly': 25,
'snipt-teams-100-yearly': 100,
'snipt-teams-250-yearly': 250,
'snipt-teams-unlimited-yearly': float('inf')
}
if plan_map[self.plan] == float('inf'):
return 'Unlimited'
else:
return plan_map[self.plan]

View File

@ -37,7 +37,7 @@
<li>Public team posts are public to the world, as they are now.</li>
<li>Private team posts are editable by all team members.</li>
<li>All team members are automatically granted personal <a href="/pro/"><span class="pro">Pro</span></a> accounts.</li>
<li>Plans from $49/month, all with a 7-day free trial.</li>
<li>Plans from $49/month, all with a 14-day free trial.</li>
</ul>
</div>
{% if not request.user.is_authenticated %}
@ -61,7 +61,7 @@
<div class="control-group">
<label class="control-label" for="email">Team email:</label>
<div class="controls">
<input required type="text" class="input-xlarge" name="email" id="email" />
<input required type="email" class="input-xlarge" name="email" id="email" />
<p class="sub" style="margin-top: 3px; color: #999999;">
For billing and your team's Gravatar. Will remain private.
</p>
@ -72,12 +72,12 @@
<div class="controls">
<select name="plan" type="text" class="input-xlarge" id="plan">
<option value="snipt-teams-25-monthly">25 users - $49/month</option>
<option value="snipt-teams-25-yearly">25 users - $588/year</option>
<option value="snipt-teams-100-monthly">100 users - $149/month</option>
<option value="snipt-teams-100-yearly">100 users - $1,788/year</option>
<option value="snipt-teams-250-monthly">250 users - $299/month</option>
<option value="snipt-teams-250-yearly">250 users - $3,588/year</option>
<option value="snipt-teams-unlimited-monthly">Unlimited users - $499/month</option>
<option value="snipt-teams-25-yearly">25 users - $588/year</option>
<option value="snipt-teams-100-yearly">100 users - $1,788/year</option>
<option value="snipt-teams-250-yearly">250 users - $3,588/year</option>
<option value="snipt-teams-unlimited-yearly">Unlimited users - $5,988/year</option>
</select>
</div>

View File

@ -0,0 +1,56 @@
{% extends "base.html" %}
{% block page-title %}Team Billing{% endblock %}
{% block body-class %}account {{ block.super }}{% endblock %}
{% block breadcrumb %}
<li><a href="/{{ team.user.username }}/">{{ team.user.username }}</a></li>
<span class="prompt">/</span> <li><a href="/{{ team.user.username }}/billing/">Billing</a></li>
{% endblock %}
{% block content %}
<section class="snipts" id="snipts"></section>
<section class="profile group">
<aside>
<ul class="nav nav-list ng-cloak" ng-cloak>
<li class="nav-header">Team: {{ team.name }}</li>
<li>
<a href="/{{ team.slug }}/members/">Members</a>
</li>
<li class="active">
<a href="/{{ team.slug }}/billing/">Billing</a>
</li>
</ul>
</aside>
<section class="content">
<div class="def" data-title="Plan">
25 users
</div>
<div class="def" data-title="Price">
$49.00 USD / month
</div>
<div class="def" data-title="Card">
xxxx-xxxx-xxxx-4242
</div>
<div class="def" data-title="Status">
Active
</div>
<div class="def" data-title="Team since">
August 29, 2015
</div>
<div class="def" data-title="Next bill date" ng-show="user.stripeAccount.status != 'inactive'">
November 29, 2015
</div>
<p class="alert alert-info group" style="padding-right: 8px;">
<a href="/{{ team.slug }}/billing/cancel/" class="btn btn-danger pull-right">Cancel subscription</a>
</p>
</section>
</section>
{% endblock %}
{% block analytics %}
{% if not debug %}
window.ll('tagScreen', 'Team billing view');
{% endif %}
{% endblock %}

View File

@ -2,7 +2,7 @@
{% block page-title %}Team Members{% endblock %}
{% block body-class %}{{ block.super }}{% endblock %}
{% block body-class %}account {{ block.super }}{% endblock %}
{% block breadcrumb %}
<li><a href="/{{ team.user.username }}/">{{ team.user.username }}</a></li>
@ -10,7 +10,32 @@
{% endblock %}
{% block content %}
<section class="snipts" id="snipts"></section>
<section class="profile group">
<aside>
<ul class="nav nav-list ng-cloak" ng-cloak>
<li class="nav-header">Team: {{ team.name }}</li>
<li class="active">
<a href="/{{ team.slug }}/members/">Members</a>
</li>
<li>
<a href="/{{ team.slug }}/billing/">Billing</a>
</li>
</ul>
</aside>
<section class="content">
<div class="def" data-title="Owner">
{{ team.owner }}
</div>
<div class="def" data-title="Members ({{ team.members.all|length }} of {{ team.member_limit }})">
<ul style="margin-bottom: 0">
{% for member in team.members.all %}
<li>{{ member }}</li>
{% endfor %}
</ul>
</div>
</section>
</section>
{% endblock %}
{% block analytics %}

View File

@ -8,4 +8,7 @@ urlpatterns = \
url(r'^for-teams/complete/$', views.for_teams_complete),
url(r'^(?P<username>[^/]+)/members/$',
views.team_members,
name='team-members'))
name='team-members'),
url(r'^(?P<username>[^/]+)/billing/$',
views.team_billing,
name='team-billing'))

View File

@ -5,7 +5,7 @@ import uuid
from annoying.decorators import render_to
from django.conf import settings
from django.contrib.auth.models import User
from django.http import HttpResponseRedirect, HttpResponseBadRequest
from django.http import Http404, HttpResponseRedirect, HttpResponseBadRequest
from django.shortcuts import get_object_or_404
from teams.models import Team
@ -19,9 +19,19 @@ def for_teams(request):
return {}
@render_to('teams/team-billing.html')
def team_billing(request, username):
team = get_object_or_404(Team, slug=username)
return {
'team': team
}
@render_to('teams/team-members.html')
def team_members(request, username):
team = get_object_or_404(Team, slug=username)
if team.owner != request.user:
raise Http404
return {
'team': team
}
@ -49,6 +59,7 @@ def for_teams_complete(request):
team = Team(name=request.POST['team-name'],
email=request.POST['email'],
plan=plan,
owner=request.user)
team.stripe_id = customer.id
team.save()