From 58d6daa3606b9c5cca2f2b4174d74a3343cbb3a3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miguel=20Angel=20Gordi=C3=A1n?=
Date: Tue, 28 Jul 2020 05:48:43 -0500
Subject: [PATCH] Add dashboard for sponsors (#7064)
* Add dashboard for sponsors
* Add dashboard for sponsors
* Restrict one prize
* fix overflow text
* Toggle description
* Update styles
* Add pay button for sponsor dashboard
* Update title to being a link
Co-authored-by: Dan Lipert
---
app/app/urls.py | 4 +-
app/assets/v2/js/pages/dashboard-hackathon.js | 23 ++
app/assets/v2/js/pages/dashboard-sponsors.js | 136 +++++++++
app/assets/v2/js/vue-components.js | 4 +-
app/dashboard/models.py | 27 ++
.../templates/dashboard/index-vue.html | 3 +
.../templates/dashboard/sponsors.html | 281 ++++++++++++++++++
app/dashboard/views.py | 124 ++++++++
8 files changed, 600 insertions(+), 2 deletions(-)
create mode 100644 app/assets/v2/js/pages/dashboard-sponsors.js
create mode 100644 app/dashboard/templates/dashboard/sponsors.html
diff --git a/app/app/urls.py b/app/app/urls.py
index a1018c157e7..d602a5246bc 100644
--- a/app/app/urls.py
+++ b/app/app/urls.py
@@ -216,6 +216,7 @@
path('hackathon//new/', dashboard.views.new_hackathon_bounty, name='new_hackathon_bounty'),
path('hackathon//new', dashboard.views.new_hackathon_bounty, name='new_hackathon_bounty2'),
path('hackathon//', dashboard.views.hackathon, name='hackathon'),
+ path('hackathon/dashboard/', dashboard.views.dashboard_sponsors, name='sponsors-dashboard'),
path('hackathon/', dashboard.views.hackathon, name='hackathon2'),
path('hackathon//onboard/', dashboard.views.hackathon_onboard, name='hackathon_onboard2'),
path('hackathon///', dashboard.views.hackathon, name='hackathon'),
@@ -251,6 +252,7 @@
re_path(r'^hackathons/?$', dashboard.views.get_hackathons, name='get_hackathons4'),
url(r'^register_hackathon/', dashboard.views.hackathon_registration, name='hackathon_registration'),
path('api/v0.1/hackathon//save/', dashboard.views.save_hackathon, name='save_hackathon'),
+ path('api/v1/hackathon//prizes', dashboard.views.hackathon_prizes, name='hackathon_prizes'),
path('api/v0.1/hackathon//showcase/', dashboard.views.showcase, name='hackathon_showcase'),
# action URLs
@@ -688,7 +690,7 @@
url(settings.GITHUB_EVENT_HOOK_URL, gitcoinbot.views.payload, name='payload'),
url(r'^impersonate/', include('impersonate.urls')),
url(r'^api/v0.1/hackathon_project/set_winner/', dashboard.views.set_project_winner, name='project_winner'),
- url(r'^api/v0.1/hackathon_project/set_winner/', dashboard.views.set_project_winner, name='project_winner'),
+ url(r'^api/v0.1/hackathon_project/set_notes/', dashboard.views.set_project_notes, name='project_notes'),
# users
url(r'^api/v0.1/user_bounties/', dashboard.views.get_user_bounties, name='get_user_bounties'),
diff --git a/app/assets/v2/js/pages/dashboard-hackathon.js b/app/assets/v2/js/pages/dashboard-hackathon.js
index 82a2f11cfba..3eb7579b37c 100644
--- a/app/assets/v2/js/pages/dashboard-hackathon.js
+++ b/app/assets/v2/js/pages/dashboard-hackathon.js
@@ -928,11 +928,34 @@
}
},
+ computed: {
+ isSponsor: () => {
+ let vm = this;
+
+ if (document.contxt.is_staff) {
+ return true;
+ }
+
+ for (let i = 0; i < vm.hackathonSponsors.length; i++) {
+ if (vm.hackathonSponsors[i].org_name === document.contxt.github_handle) {
+ return true;
+ }
+ }
+
+ for (let i = 0; i < vm.prizeFounders.length; i++) {
+ if (vm.prizeFounders[i] === document.contxt.github_handle) {
+ return true;
+ }
+ }
+ return false;
+ }
+ },
data: () => ({
is_registered: document.is_registered,
activePanel: document.activePanel,
hackathonObj: document.hackathonObj,
hackathonSponsors: document.hackathonSponsors,
+ prizeFounders: document.prizeFounders,
hackathonProjects: [],
chatURL: document.chatURL,
hackHasEnded: document.displayShowcase
diff --git a/app/assets/v2/js/pages/dashboard-sponsors.js b/app/assets/v2/js/pages/dashboard-sponsors.js
new file mode 100644
index 00000000000..2e2188e614f
--- /dev/null
+++ b/app/assets/v2/js/pages/dashboard-sponsors.js
@@ -0,0 +1,136 @@
+(function($) {
+ $(function() {
+ window.hackathonApp = new Vue({
+ delimiters: [ '[[', ']]' ],
+ el: '#sponsors-app',
+ mounted() {
+ this.retrieveSponsorPrizes();
+ },
+ methods: {
+ markWinner: function($event, project, prizeIndex) {
+ let vm = this;
+ const url = '/api/v0.1/hackathon_project/set_winner/';
+ const markWinner = fetchData(url, 'POST', {
+ project_id: project.pk,
+ winner: $event ? 1 : 0
+ }, {'X-CSRFToken': vm.csrf});
+
+ vm.prizes[prizeIndex].submissions.forEach((submission, submissionIndex) => {
+ if (submission.pk !== project.pk && submission.winner) {
+ console.log(submission.pk);
+ console.log(project.pk);
+ const unmarkPreviousWinner = fetchData(url, 'POST', {
+ project_id: submission.pk,
+ winner: 0
+ }, {'X-CSRFToken': vm.csrf});
+
+ $.when(unmarkPreviousWinner).then(() => {
+ vm.$set(vm.prizes[prizeIndex].submissions[submissionIndex], 'winner', false);
+ });
+ }
+ });
+
+ $.when(markWinner).then(response => {
+ if (response.message) {
+ alert(response.message);
+ }
+ }).catch(err => {
+ console.log(err);
+ });
+ },
+ setNote: function($event, project) {
+ let vm = this;
+
+ const url = '/api/v0.1/hackathon_project/set_notes/';
+ const setNotes = fetchData(url, 'POST', {
+ project_id: project.pk,
+ notes: vm.comments[project.pk]
+ }, {'X-CSRFToken': vm.csrf});
+
+ $.when(setNotes).then(response => {
+ if (response.message) {
+ alert(response.message);
+ }
+ }).catch(err => {
+ console.log(err);
+ });
+ },
+ addNote: function(project) {
+ let vm = this;
+
+ vm.$set(vm.comments, project.pk, '');
+ },
+ getComment: function(project) {
+ let vm = this;
+
+ return vm.comments[project];
+ },
+ retrieveSponsorPrizes: function() {
+ const vm = this;
+ const hackathon = fetchData(`/api/v1/hackathon/${vm.hackathonObj['slug']}/prizes`);
+
+ $.when(hackathon).then((response) => {
+ for (let i = 0; i < response.prizes.length; i++) {
+ if (response.prizes[i].submissions.length) {
+ response.prizes[i].submissions.forEach((submission) => {
+ vm.$set(vm.comments, submission.pk, submission.extra.notes);
+ });
+ }
+ }
+ vm.prizes = response.prizes;
+ });
+ },
+ tabChange: function(input) {
+ let vm = this;
+
+ switch (input) {
+ default:
+ case 0:
+ newPathName = 'prizes';
+ break;
+ case 1:
+ newPathName = 'submissions';
+ break;
+ }
+ let newUrl = `/hackathon/dashboard/${vm.hackathonObj['slug']}/${newPathName}`;
+
+ history.pushState({}, `${vm.hackathonObj['slug']} - ${newPathName}`, newUrl);
+
+ },
+ start_and_end: function(str) {
+ if (str.length > 25) {
+ return str.substr(0, 8) + '...' + str.substr(str.length - 5, str.length);
+ }
+ return str;
+ },
+ getSummary: function(project) {
+ if (project.showDescription || project.summary.length < 177) {
+ return project.summary;
+ }
+ return `${project.summary.slice(0, 177)}...`;
+ },
+ toggleSummary: function(prizeIndex, submissionIndex) {
+ let vm = this;
+ const showDescription = !vm.prizes[prizeIndex].submissions[submissionIndex].showDescription;
+
+ vm.$set(vm.prizes[prizeIndex].submissions[submissionIndex], 'showDescription', showDescription);
+ }
+ },
+ computed: {
+ isMobileDevice() {
+ return this.windowWidth < 576;
+ }
+ },
+ data: () => ({
+ activePanel: document.activePanel,
+ hackathonObj: document.hackathonObj,
+ hackathonSponsors: document.hackathonSponsors,
+ hackathonProjects: [],
+ chatURL: document.chatURL,
+ prizes: [],
+ comments: [],
+ csrf: $("input[name='csrfmiddlewaretoken']").val() || ''
+ })
+ });
+ });
+})(jQuery);
diff --git a/app/assets/v2/js/vue-components.js b/app/assets/v2/js/vue-components.js
index 946d4c80caa..ef8f9cb24dd 100644
--- a/app/assets/v2/js/vue-components.js
+++ b/app/assets/v2/js/vue-components.js
@@ -9,9 +9,11 @@ Vue.mixin({
},
methods: {
chatWindow: function(channel, dm) {
+ let vm = this;
+
dm = dm || channel ? channel.indexOf('@') >= 0 : false;
channel = channel || 'town-square';
- let vm = this;
+
const hackathonTeamSlug = 'hackathons';
const gitcoinTeamSlug = 'gitcoin';
const isHackathon = (document.hackathon_id !== null);
diff --git a/app/dashboard/models.py b/app/dashboard/models.py
index e861d614136..aa519390aa8 100644
--- a/app/dashboard/models.py
+++ b/app/dashboard/models.py
@@ -4893,6 +4893,33 @@ def url(self):
def get_absolute_url(self):
return self.url()
+ def to_json(self):
+ profiles = [
+ {
+ 'handle': profile.handle,
+ 'name': profile.name,
+ 'email': profile.email,
+ 'payout_address': profile.preferred_payout_address,
+ 'url': profile.url,
+ 'avatar': profile.active_avatar.avatar_url if profile.active_avatar else ''
+ } for profile in self.profiles.all()
+ ]
+
+ return {
+ 'pk': self.pk,
+ 'name': self.name,
+ 'logo': self.logo.url,
+ 'badge': self.badge,
+ 'profiles': profiles,
+ 'work_url': self.work_url,
+ 'summary': self.summary,
+ 'status': self.status,
+ 'message': self.message,
+ 'chat_channel_id': self.chat_channel_id,
+ 'winner': self.winner,
+ 'extra': self.extra
+ }
+
class FeedbackEntry(SuperModel):
bounty = models.ForeignKey(
diff --git a/app/dashboard/templates/dashboard/index-vue.html b/app/dashboard/templates/dashboard/index-vue.html
index c1b21c72d95..a243cb53c75 100644
--- a/app/dashboard/templates/dashboard/index-vue.html
+++ b/app/dashboard/templates/dashboard/index-vue.html
@@ -124,6 +124,7 @@
Hackathon Over
{% endif %}
+ Sponsor dashboard
@@ -817,6 +818,7 @@
[[hackathon.name]] Wall of Fame<
{{orgs|json_script:"sponsor-list"}}
{{hackathon_obj|json_script:"hackathon-object"}}
+ {{prize_founders|json_script:"prize-founders"}}
+ {{orgs|json_script:"sponsor-list"}}
+ {{hackathon_obj|json_script:"hackathon-object"}}
+
+
+ {% include 'shared/activity_scripts.html' %}
+
+
+
+
+
+ {% include 'shared/current_profile.html' %}
+
+