Skip to content

Commit

Permalink
Launch referral
Browse files Browse the repository at this point in the history
  • Loading branch information
asennoussi committed Apr 19, 2023
1 parent d622b76 commit 4f9067f
Show file tree
Hide file tree
Showing 12 changed files with 229 additions and 12 deletions.
25 changes: 17 additions & 8 deletions accounts/forms.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.db import IntegrityError
from .tokens import token_generator

from django import forms
Expand Down Expand Up @@ -44,7 +45,6 @@ def send_activation_email(self, request, user):

def clean_referral_code(self):
referral_code = self.cleaned_data['referral_code']
print(referral_code)
if referral_code:
# Validate the referral code and retrieve the referral object
try:
Expand All @@ -54,14 +54,23 @@ def clean_referral_code(self):
return referral.referral_code
return None

def create_referral(self, referral_code, user):
referrer = CustomUser.objects.get(
referral_code=referral_code)
def create_referral(self, referral_code, new_user):
try:
inviter = CustomUser.objects.get(referral_code=referral_code)

referral, created = Referral.objects.get_or_create(user=referrer)
import pdb
pdb.set_trace()
return referral.referred_users.add(user)
# Check if the new user is not the inviter
if inviter == new_user:
raise ValueError("A user cannot invite themselves.")

referral = Referral.objects.create(
inviter=inviter, referred_user=new_user)
return referral

except CustomUser.DoesNotExist:
raise ValueError("Invalid referral code.")
except IntegrityError:
raise ValueError(
"This user has already been invited by the inviter.")


class LoginForm(AuthenticationForm):
Expand Down
10 changes: 10 additions & 0 deletions dashboard/templates/dashboard/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,16 @@ <h2 class="mb-2 font-small">{{ request.user.email }}</h2>
<span class="sidebar-text">Linked accounts</span>
</a>
</li>
<li class="nav-item {% if request.resolver_match.url_name == "user_referrals" %}active{% endif %} ">
<a href="{% url 'user_referrals' %}" class="nav-link">
<span class="sidebar-icon">
<svg class="icon icon-xs me-2" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v12m-3-2.818l.879.659c1.171.879 3.07.879 4.242 0 1.172-.879 1.172-2.303 0-3.182C13.536 12.219 12.768 12 12 12c-.725 0-1.45-.22-2.003-.659-1.106-.879-1.106-2.303 0-3.182s2.9-.879 4.006 0l.415.33M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</span>
<span class="sidebar-text">Refer and earn $$</span>
</a>
</li>
<li role="separator" class="dropdown-divider mt-4 mb-3 border-gray-700"></li>
{% block upgradable %}{% endblock %}

Expand Down
1 change: 1 addition & 0 deletions dashboard/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@
name='payment_canceled'),
path('linked-accounts/unlink/<int:pk>',
views.UnlinkAccountRedirectView.as_view(), name="unlink_account"),
path('referrals/', views.UserReferralsView.as_view(), name='user_referrals'),
]
10 changes: 10 additions & 0 deletions dashboard/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from googleapiclient.discovery import build

from dashboard.forms import UpdateLinkedAccountForm
from referral.models import Referral

from .models import FilteredEmails, Jobs

Expand Down Expand Up @@ -370,3 +371,12 @@ def catch_email(request):
# TODO(developer) - Handle errors from gmail API.
print(f'An error occurred: {error}')
return HttpResponse(status=200)


class UserReferralsView(LoginRequiredMixin, ListView):
template_name = 'user_referrals.html'
context_object_name = 'referrals'

def get_queryset(self):
user = self.request.user
return Referral.objects.filter(inviter=user).select_related('referred_user')
3 changes: 3 additions & 0 deletions onboarding/static/onboarding/css/onboarding.css
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ li {
border-radius: 2px;
box-shadow: 0 3px 4px 0 rgba(0,0,0,.25);
}
.google-icon-wrapper .google-icon{
width:21px;
}
.google-btn .google-icon-wrapper {
position: absolute;
margin-top: 1px;
Expand Down
3 changes: 3 additions & 0 deletions referral/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
class ReferralConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'referral'

def ready(self):
from . import signals
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Generated by Django 4.1.1 on 2023-04-19 07:17

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('referral', '0002_remove_referral_referral_code_and_more'),
]

operations = [
migrations.AddField(
model_name='referral',
name='created_at',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name='referral',
name='inviter',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='referrals_made', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='referral',
name='referred_user',
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='referred_by', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='referral',
name='successful',
field=models.BooleanField(default=False),
),
migrations.AlterUniqueTogether(
name='referral',
unique_together={('inviter', 'referred_user')},
),
migrations.RemoveField(
model_name='referral',
name='referred_users',
),
migrations.RemoveField(
model_name='referral',
name='user',
),
]
15 changes: 11 additions & 4 deletions referral/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@


class Referral(models.Model):
user = models.ForeignKey(
CustomUser, on_delete=models.CASCADE, related_name='referred_by')
referred_users = models.ManyToManyField(
CustomUser)
inviter = models.ForeignKey(
CustomUser, on_delete=models.CASCADE, related_name='referrals_made', null=True)
referred_user = models.OneToOneField(
CustomUser, on_delete=models.CASCADE, related_name='referred_by', null=True)

# Additional fields to track referral information
created_at = models.DateTimeField(auto_now_add=True)
successful = models.BooleanField(default=False)

class Meta:
unique_together = ('inviter', 'referred_user')
32 changes: 32 additions & 0 deletions referral/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from django.db.models.signals import post_save
from django.dispatch import receiver
from datetime import timedelta
from accounts.models import CustomUser
from .models import Referral


@receiver(post_save, sender=CustomUser)
def process_referral(sender, instance, **kwargs):
user = instance
print("Signal Referral received")

# Check if the user's subscription_status has become 'subscribed'
if user.subscription_status == 'subscribed':
try:
# Fetch the referral object where the user is the referred_user
referral = Referral.objects.get(referred_user=user)

# If the referral is not successful yet, process it
if not referral.successful:
inviter = referral.inviter

# Extend the inviter's payment due date by 14 days
inviter.expires_at += timedelta(days=14)
inviter.save()

# Mark the referral as successful
referral.successful = True
referral.save()
except Referral.DoesNotExist:
# No referral exists for this user, do nothing
pass
71 changes: 71 additions & 0 deletions referral/templates/user_referrals.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{% extends 'dashboard/base.html' %}
{% load referral_tags %}

{% block content %}
<div class="container">
<div class="row">
<div class="col">

<h5>Invite a friend and get $2 when they subscribe</h5>

<!-- Invite link field -->
<div class="row align-items-center mb-3">
<div class="col-auto">
<label for="inviteLink" class="form-label">Your Invite Link:</label>
</div>
<div class="col-4">
<div class="input-group">
<input type="text" class="form-control" id="inviteLink" value="{{ request.scheme }}://{{ request.get_host }}?ref={{ user.referral_code }}" readonly>
<button class="btn btn-primary" onclick="copyInviteLink()" title="Copy Link" type="button">
<svg fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="18" height="18">
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 01-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 011.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 00-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 01-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 00-3.375-3.375h-1.5a1.125 1.125 0 01-1.125-1.125v-1.5a3.375 3.375 0 00-3.375-3.375H9.75"></path>
</svg>
</button>
</div>
</div>
</div>

<h2>My Referrals</h2>

<!-- Referrals table -->
<table class="table table-striped">
<thead>
<tr>
<th scope="col">Email</th>
<th scope="col">Status</th>
</tr>
</thead>
<tbody>
{% for referral in referrals %}
<tr>
<td>{{ referral.referred_user.email|obfuscate_email }}</td>
<td>
{%if referral.successful %}
<svg class="icon icon-xs text-success" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path></svg></td>
{%else%}
<svg class="icon icon-xs text-danger" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path></svg>
{%endif%}
</tr>
{% empty %}
<tr>
<td colspan="2" class="text-center">No referrals found.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>

{% endblock %}

{% block extra_js %}
<script>
function copyInviteLink() {
const inviteLinkField = document.getElementById('inviteLink');
inviteLinkField.select();
document.execCommand('copy');
alert('Invite link copied to clipboard.');
}
</script>
{% endblock %}
Empty file.
21 changes: 21 additions & 0 deletions referral/templatetags/referral_tags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from django import template

register = template.Library()


@register.filter
def obfuscate_email(email):
email_parts = email.split('@')
if len(email_parts) != 2:
return email

username, domain = email_parts

if len(username) <= 3:
return email

obfuscated_username = username[:2] + '*' * \
(len(username) - 3) + username[-1]
obfuscated_email = obfuscated_username + '@' + domain

return obfuscated_email

0 comments on commit 4f9067f

Please sign in to comment.