Skip to content

Commit 4fb6a42

Browse files
committed
referrals MVP
1 parent 5b9f097 commit 4fb6a42

File tree

15 files changed

+1882
-23
lines changed

15 files changed

+1882
-23
lines changed

app/assets/v2/css/quests.css

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,20 @@
1+
#announce{
2+
width: 100%;
3+
display: block;
4+
background-color: #fef3c0;
5+
border-radius: 5px;
6+
text-align: center;
7+
margin-top: 5px;
8+
padding-top: 0px;
9+
}
110
.demo {
211
max-width: 600px;
312
cursor: pointer;
413
}
14+
.difficulty_tab{
15+
display: block;
16+
width: 100%;
17+
}
518
.card_head {
619
height: 140px;
720
}
@@ -149,10 +162,16 @@ body.quest_battle p{
149162
.quest-card:hover .card_head img{
150163
filter: grayscale(0%);
151164
}
152-
165+
#announce img{
166+
margin: 0px auto;
167+
margin-top: -150px;
168+
margin-bottom: -50px;
169+
width: 200px;
170+
height: 200px;
171+
}
153172
#leaderboard img {
154-
height: 80px;
155-
width: 80px;
173+
height: 40px;
174+
width: 40px;
156175
border-radius: 50px;
157176
}
158177
#enemy img,
@@ -170,7 +189,12 @@ body.quest_battle p{
170189
height: 250px;
171190
margin-left: -75px;
172191
}
173-
192+
.reward_schedule tr{
193+
border-radius: 5px;
194+
}
195+
.reward_schedule tr td{
196+
padding: 3px 5px;
197+
}
174198
.character.harm h3,
175199
.character.harm p{
176200
background-color: #F9006C;
@@ -410,8 +434,8 @@ body.quest_battle{
410434
width: 100%;
411435
}
412436
#leaderboard .inner_row {
413-
height: 120px;
414-
padding: 20px;
437+
height: 70px;
438+
padding: 10px;
415439
width: 100%;
416440
}
417441

50.7 KB
Loading

app/assets/v2/images/quests/enemies/samurai.svg

Lines changed: 1563 additions & 0 deletions
Loading
24.1 KB
Loading

app/assets/v2/js/pages/quests.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,16 @@ $(document).ready(function() {
306306
}
307307
}, 100);
308308

309+
$('#reflink').click(function() {
310+
$(this).focus();
311+
$(this).select();
312+
document.execCommand('copy');
313+
$(this).after('<div class=after_copy>Copied to clipboard</div>');
314+
setTimeout(function() {
315+
$('.after_copy').remove();
316+
}, 500);
317+
});
318+
309319
$('.demo').click(function(e) {
310320
e.preventDefault();
311321
$(this).fadeOut(function() {
@@ -316,6 +326,16 @@ $(document).ready(function() {
316326
});
317327
});
318328

329+
$('#tabs a').click(function(e) {
330+
e.preventDefault();
331+
var target = $(this).data('href');
332+
333+
$('.difficulty_tab').addClass('hidden');
334+
$('.nav-link').removeClass('active');
335+
$(this).addClass('active');
336+
$('.difficulty_tab.' + target).removeClass('hidden');
337+
});
338+
319339
$('.quest-card.available').click(function(e) {
320340
e.preventDefault();
321341
document.location.href = $(this).find('.btn').attr('href');

app/dashboard/admin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def recalculate_profile(modeladmin, request, queryset):
113113
recalculate_profile.short_description = "Recalculate Profile Frontend Info"
114114

115115
class ProfileAdmin(admin.ModelAdmin):
116-
raw_id_fields = ['user', 'preferred_kudos_wallet']
116+
raw_id_fields = ['user', 'preferred_kudos_wallet', 'referrer']
117117
ordering = ['-id']
118118
search_fields = ['email', 'data']
119119
list_display = ['handle', 'created_on']
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Generated by Django 2.2.3 on 2019-10-01 14:41
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('dashboard', '0054_auto_20190928_1936'),
11+
]
12+
13+
operations = [
14+
migrations.AddField(
15+
model_name='profile',
16+
name='referrer',
17+
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='referred', to='dashboard.Profile'),
18+
),
19+
]

app/dashboard/models.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2235,6 +2235,7 @@ class Profile(SuperModel):
22352235
rank_funder = models.IntegerField(default=0)
22362236
rank_org = models.IntegerField(default=0)
22372237
rank_coder = models.IntegerField(default=0)
2238+
referrer = models.ForeignKey('dashboard.Profile', related_name='referred', on_delete=models.CASCADE, null=True, db_index=True)
22382239

22392240
objects = ProfileQuerySet.as_manager()
22402241

@@ -2265,6 +2266,10 @@ def team(self):
22652266
return Profile.objects.none()
22662267
return Profile.objects.filter(organizations__icontains=self.handle)
22672268

2269+
@property
2270+
def ref_code(self):
2271+
return hex(self.pk).replace("0x",'')
2272+
22682273
@property
22692274
def get_org_kudos(self):
22702275
from kudos.models import Token

app/marketing/utils.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -312,16 +312,37 @@ def get_platform_wide_stats(since_last_n_days=90):
312312
}
313313

314314

315-
def handle_marketing_callback(key, request):
315+
def handle_marketing_callback(_input, request):
316+
#config
316317
from marketing.models import MarketingCallback
318+
from dashboard.models import Profile
319+
320+
#setup
321+
key = _input if not ':' in _input else _input.split(':')[0]
317322
callbacks = MarketingCallback.objects.filter(key=key)
318323
if callbacks.exists():
319324
callback_reference = callbacks.first().val
325+
#set user referrer
326+
if key == 'ref':
327+
if request.user.is_authenticated:
328+
from django.contrib.auth.models import User
329+
value = _input.split(':')[1]
330+
pk = int(value, 16)
331+
profs = Profile.objects.filter(pk=pk)
332+
if profs.exists():
333+
profile = profs.first()
334+
if profile.pk != request.user.profile.pk:
335+
target_profile = request.user.profile
336+
target_profile.referrer = profile
337+
target_profile.save()
338+
else:
339+
messages.info(request, "You have been selected to receive a $5.00 Gitcoin Grants voucher. Login to use it.")
340+
# add user to a group
320341
if callback_reference.split(':')[0] == 'add_to_group':
321342
if request.user.is_authenticated:
322343
from django.contrib.auth.models import Group
323-
messages.info(request, "You have redeemed your $5.00 Gitcoin Grants voucher. Browse grants on gitcoin.co/grants and click 'fund' to spend this voucher!")
324344
group_name = callback_reference.split(':')[1]
345+
messages.info(request, "You have redeemed your $5.00 Gitcoin Grants voucher. Browse grants on gitcoin.co/grants and click 'fund' to spend this voucher!")
325346
group = Group.objects.get(name=group_name)
326347
group.user_set.add(request.user)
327348
else:

app/quests/admin.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from django.contrib import admin
22

33
# Register your models here.
4-
from .models import Quest, QuestAttempt
4+
from .models import Quest, QuestAttempt, QuestPointAward
55

66

77
class QuestAdmin(admin.ModelAdmin):
@@ -14,5 +14,11 @@ class QuestAttemptAdmin(admin.ModelAdmin):
1414
ordering = ['-id']
1515
list_display = ['created_on', '__str__']
1616

17+
class QuestPointAwardAdmin(admin.ModelAdmin):
18+
raw_id_fields = ['questattempt', 'profile']
19+
ordering = ['-id']
20+
list_display = ['created_on', '__str__']
21+
22+
admin.site.register(QuestPointAward, QuestPointAwardAdmin)
1723
admin.site.register(Quest, QuestAdmin)
1824
admin.site.register(QuestAttempt, QuestAttemptAdmin)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Generated by Django 2.2.3 on 2019-10-02 00:11
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
import economy.models
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
('dashboard', '0055_profile_referrer'),
12+
('quests', '0007_quest_difficulty'),
13+
]
14+
15+
operations = [
16+
migrations.CreateModel(
17+
name='QuestPointAward',
18+
fields=[
19+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20+
('created_on', models.DateTimeField(db_index=True, default=economy.models.get_time)),
21+
('modified_on', models.DateTimeField(default=economy.models.get_time)),
22+
('value', models.FloatField()),
23+
('profile', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='questpointawards', to='dashboard.Profile')),
24+
('questattempt', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pointawards', to='quests.QuestAttempt')),
25+
],
26+
options={
27+
'abstract': False,
28+
},
29+
),
30+
]

app/quests/models.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,17 @@ class QuestAttempt(SuperModel):
137137
def __str__(self):
138138
"""Return the string representation of this obj."""
139139
return f'{self.pk}, {self.profile.handle} => {self.quest.title} state: {self.state} success: {self.success}'
140+
141+
class QuestPointAward(SuperModel):
142+
143+
questattempt = models.ForeignKey('quests.QuestAttempt', related_name='pointawards', on_delete=models.CASCADE)
144+
profile = models.ForeignKey(
145+
'dashboard.Profile',
146+
on_delete=models.CASCADE,
147+
related_name='questpointawards',
148+
)
149+
value = models.FloatField()
150+
151+
def __str__(self):
152+
"""Return the string representation of this obj."""
153+
return f'{self.value}, {self.profile.handle}'

app/quests/templates/quests/index.html

Lines changed: 85 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,24 @@ <h1 class="font-title-xl font-weight-semibold">A New Way to Explore the Web3 Eco
3939
</div>
4040
</div>
4141
<div class="row indent_on_mobile available_quests">
42+
<h1 class="mt-3" style="width: 100%;">Quests <span id="alpha">Alpha</span></h1>
43+
<div id="tabs">
44+
<div class="container">
45+
<ul class="nav nav-tabs mt-3" id="myTab" role="tablist">
46+
{% for difficulty_level, the_quests in quests%}
47+
<li class="nav-item">
48+
<a class="nav-link nav-line {% if difficulty_level == 'Beginner' %}active{%endif%}" href="#" data-href="{{difficulty_level}}">
49+
{{difficulty_level}} Quests
50+
</a>
51+
</li>
52+
{% endfor %}
53+
</ul>
54+
</div>
55+
</div>
4256
{% for difficulty_level, the_quests in quests%}
43-
<h1 class="mt-3" style="width: 100%;">{{difficulty_level}} Quests <span id="alpha">Alpha</span></h1>
44-
<div>
57+
<div class="difficulty_tab {{difficulty_level}} {% if difficulty_level != 'Beginner' %}hidden{%endif%}">
4558
{% for unlocked, beaten, cooldown, quest in the_quests %}
46-
<div style="{% cycle 'background-color: #fafafa;' '' %}; width: 250px; display: block; float: left; border-radius: 10px; height: 490px; text-align: center;" class="m-3 p-2 quest-card {%if not unlocked%}locked{%elif beaten%}beaten{%else%}available{%endif%} {%if cooldown%}cooldown{%endif%}">
59+
<div id="{{difficulty_level}}" style="{% cycle 'background-color: #fafafa;' '' %}; width: 250px; display: block; float: left; border-radius: 10px; height: 490px; text-align: center;" class="m-3 p-2 quest-card {%if not unlocked%}locked{%elif beaten%}beaten{%else%}available{%endif%} {%if cooldown%}cooldown{%endif%}">
4760
<div class="card_head">
4861
<span class="boss">#boss</span>
4962
<img class="card_img boss" src="/static/{{quest.game_metadata.enemy.art}}" title="{{quest.game_metadata.enemy.title}}">
@@ -93,22 +106,87 @@ <h2 style="margin-top: -50px; font-size: 20px;">
93106
</div>
94107
{% endfor %}
95108
</div>
109+
<div class="row indent_on_mobile pt-2" style="margin-top: 100px; background-color: #fafafa;">
110+
<h1 id=refer class="mt-3">Better with Friends</h1>
111+
</div>
112+
113+
<div class="row indent_on_mobile mt-0" style="background-color: #fafafa;">
114+
<div class="container-fluid py-0 text-center">
115+
<img src="{% static 'v2/images/grants/robots.png' %}" style="max-width: 200px; margin-top: -170px;" />
116+
</div>
117+
</div>
118+
119+
<div class="row indent_on_mobile" style="background-color: #fafafa;">
120+
<div class="container-fluid py-4">
121+
<div class="container w-50 float-left">
122+
<strong>Gitcoin Quests are a team sport!</strong>.
123+
You can climb the leaderboard, and earn points, faster, by referring friends to share tips & play together.
124+
<br>
125+
<br>
126+
After each quest you complete you will be invited to show off your quest completion + share a referral link. You will earn points for every user who join completes a quest after joining with your referral link.
127+
<br>
128+
{% if REFER_LINK %}
129+
<BR>
130+
Your personal referral link:
131+
<br>
132+
<input id=reflink type="text" value="{{REFER_LINK}}" class="w-100" readonly="readonly">
133+
<br>
134+
{% else %}
135+
<a href="{% url 'social:begin' 'github' %}?next={{ request.get_full_path }}">Login</a> to get your personal reflink.
136+
{% endif %}
137+
</div>
138+
<div class="container w-50 float-right reward_schedule">
139+
<table class="w-100">
140+
<tr style="font-weight: bold;">
141+
<td>
142+
Degree of Seperation From You
143+
</td>
144+
<td>
145+
Your Bonus Quest Point Reward When They Complete Quest
146+
</td>
147+
</tr>
148+
{% for row in rewards_schedule %}
149+
<tr style="{% cycle 'background-color: #fafafa;' 'background-color: #eaeaea;' %}">
150+
<td>
151+
{{row.layer}}
152+
</td>
153+
<td>
154+
{% if row.reward_denominator == 1 %}
155+
{{row.reward_denominator}}
156+
{% else %}
157+
1/{{row.reward_denominator}}
158+
{% endif %}
159+
point{{row.reward_denominator|pluralize}} + <img style="width:17px; height: 17px;" src="{% static "v2/images/quests/rando_kudos.gif" %}"> quest kudos
160+
</td>
161+
</tr>
162+
{% endfor %}
163+
</table>
164+
</div>
165+
</div>
166+
</div>
167+
96168

97169
{% if leaderboard|length %}
98170
<div class="row indent_on_mobile">
99-
<h1 class="mt-3">Leaderboard</h1>
171+
<h1 style="margin-top: 100px;">Leaderboard</h1>
172+
<div id="announce">
173+
<img class="mr-1" src="{% static "v2/images/quests/enemies/samurai.svg" %}">
174+
<h4><strong>The Devcon 5 Challenge is Live!</strong></h4>
175+
<p>Whomever is in the top 3 of the <a href="#leaderboard">leaderboard</a> at end of day Sunday 10/6 will <strong>win a KeepKey hardware wallet.</strong>
176+
</div>
177+
100178
</div>
101179
<div class="row indent_on_mobile">
102180
<div id=leaderboard>
103181
{% for item in leaderboard %}
104182
<div class="inner_row" style="{% cycle 'background-color: #fafafa;' '' %}";>
105-
<div class="float-left mr-2 ml-2 pt-3">{{ forloop.counter }}</div>
106-
<div class="float-left mr-2 ml-2"><img src="/dynamic/avatar/{{item.0}}"><a style="margin-left: 10px; width: 150px;" href="/profile/{{item.0}}">@{{item.0}}</a></div>
183+
<div class="float-left mr-2 ml-2">{{ forloop.counter }}</div>
184+
<div class="float-left mr-2 ml-2"><img src="/dynamic/avatar/{{item.0}}"><a style="margin-left: 10px; width: 120px;" href="/profile/{{item.0}}">@{{item.0}}</a></div>
107185
<div style="float: right; width: 500px; text-align: right;">
108186
{% for btr in item.2%}
109187
<img src="{{btr.0}}" title="{{btr.1}}" style="height: 50px; width: 50px; border-radius: 0px;">
110188
{% endfor %}
111-
{{item.1}} quest{{item.1|pluralize}} beaten</div>
189+
{{item.1}} point{{item.1|pluralize}}</div>
112190
</div>
113191
{% endfor %}
114192
</div>

app/quests/templates/quests/nav.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
<div class="collapse navbar-collapse order-1" id="navbarSupportedContent">
4141
<nav class="navbar-nav ml-auto mr-3">
4242
<a class="nav-link selected interior" href="{% url 'quests_index' %}">{% trans "Quests Explorer" %}</a>
43+
<a class="nav-link interior" href="{% url 'quests_index' %}#refer">{% trans "Refer a Friend" %}</a>
4344
<a class="nav-link interior" href="{% url 'quests_index' %}#leaderboard">{% trans "Leaderboard" %}</a>
4445
<a class="nav-link interior" href="https://gitcoin.typeform.com/to/cfBRgm"><i class="fab fa-ethereum mr-1" role="aria-hidden"></i> {% trans "Create Quest" %}</a>
4546

0 commit comments

Comments
 (0)