Teams work.
parent
002fbc45c5
commit
ae1d532c06
|
@ -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')]),
|
||||
),
|
||||
]
|
|
@ -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]
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
|
@ -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 %}
|
||||
|
|
|
@ -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'))
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue