diff --git a/app/assets/v2/js/cart-data.js b/app/assets/v2/js/cart-data.js
index 61a0328cd96..c017bff66ca 100644
--- a/app/assets/v2/js/cart-data.js
+++ b/app/assets/v2/js/cart-data.js
@@ -56,6 +56,8 @@ class CartData {
accptedTokenName = 'DAI';
}
+ grantData.uuid = get_UUID();
+
if (acceptsAllTokens || 'DAI' == accptedTokenName) {
grantData.grant_donation_amount = 5;
grantData.grant_donation_currency = 'DAI';
@@ -71,14 +73,22 @@ class CartData {
cartList.push(grantData);
this.setCart(cartList);
+
+ fetchData(`/grants/${grantData.grant_id}/activity`, 'POST', {
+ action: 'ADD_ITEM',
+ metadata: JSON.stringify(cartList)
+ }, {'X-CSRFToken': $("input[name='csrfmiddlewaretoken']").val()});
}
static removeIdFromCart(grantId) {
let cartList = this.loadCart();
- const newList = cartList.filter(grant => {
- return (grant.grant_id !== grantId);
- });
+ const newList = cartList.filter(grant => grant.grant_id !== grantId);
+
+ fetchData(`/grants/${grantId}/activity`, 'POST', {
+ action: 'REMOVE_ITEM',
+ metadata: JSON.stringify(newList)
+ }, {'X-CSRFToken': $("input[name='csrfmiddlewaretoken']").val()});
this.setCart(newList);
}
@@ -106,6 +116,21 @@ class CartData {
this.setCart(cartList);
}
+ static clearCart() {
+ let cartList = this.loadCart();
+
+ cartList.map(grant => {
+ fetchData('/grants/activity', 'POST', {
+ action: 'REMOVE_ITEM',
+ metadata: JSON.stringify(cartList),
+ bulk: true
+ }, {'X-CSRFToken': $("input[name='csrfmiddlewaretoken']").val()});
+ });
+
+ localStorage.setItem('grants_cart', JSON.stringify([]));
+ applyCartMenuStyles();
+ }
+
static loadCart() {
const cartList = localStorage.getItem('grants_cart');
diff --git a/app/assets/v2/js/cart.js b/app/assets/v2/js/cart.js
index 5e7fedbbd2a..8071f5fe972 100644
--- a/app/assets/v2/js/cart.js
+++ b/app/assets/v2/js/cart.js
@@ -345,7 +345,7 @@ Vue.component('grants-cart', {
},
clearCart() {
- CartData.setCart([]);
+ CartData.clearCart();
this.grantData = [];
update_cart_title();
},
diff --git a/app/assets/v2/js/shared.js b/app/assets/v2/js/shared.js
index 348537d0a08..dd9fd2a3853 100644
--- a/app/assets/v2/js/shared.js
+++ b/app/assets/v2/js/shared.js
@@ -1244,3 +1244,15 @@ const fetchIssueDetailsFromGithub = issue_url => {
});
});
};
+
+const get_UUID = () => {
+ var dt = new Date().getTime();
+ const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
+ const r = (dt + Math.random() * 16) % 16 | 0;
+
+ dt = Math.floor(dt / 16);
+ return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
+ });
+
+ return uuid;
+};
diff --git a/app/grants/admin.py b/app/grants/admin.py
index 1601112f807..a639ef122bd 100644
--- a/app/grants/admin.py
+++ b/app/grants/admin.py
@@ -22,7 +22,7 @@
from django.utils.html import format_html
from django.utils.safestring import mark_safe
-from grants.models import CLRMatch, Contribution, Flag, Grant, MatchPledge, PhantomFunding, Subscription
+from grants.models import CLRMatch, Contribution, Flag, Grant, MatchPledge, PhantomFunding, Subscription, CartActivity
class GeneralAdmin(admin.ModelAdmin):
@@ -147,7 +147,7 @@ def subscriptions_links(self, instance):
def contributions_links(self, instance):
"""Define the logo image tag to be displayed in the admin."""
- eles = []
+ eles = []
for i in [True, False]:
html = f"
Success {i}
"
@@ -308,6 +308,12 @@ def from_ip_address(self, instance):
return " , ".join(visits)
+class CartActivityAdmin(admin.ModelAdmin):
+ list_display = ['id', 'grant', 'profile', 'action', 'bulk', 'created_on']
+ raw_id_fields = ['grant', 'profile']
+ search_fields = ['bulk', 'action', 'grant']
+
+
admin.site.register(PhantomFunding, PhantomFundingAdmin)
admin.site.register(MatchPledge, MatchPledgeAdmin)
admin.site.register(Grant, GrantAdmin)
@@ -315,3 +321,4 @@ def from_ip_address(self, instance):
admin.site.register(CLRMatch, CLRMatchAdmin)
admin.site.register(Subscription, SubscriptionAdmin)
admin.site.register(Contribution, ContributionAdmin)
+admin.site.register(CartActivity, CartActivityAdmin)
diff --git a/app/grants/migrations/0059_auto_20200617_1508.py b/app/grants/migrations/0059_auto_20200617_1508.py
new file mode 100644
index 00000000000..3fefba4a745
--- /dev/null
+++ b/app/grants/migrations/0059_auto_20200617_1508.py
@@ -0,0 +1,48 @@
+# Generated by Django 2.2.4 on 2020-06-17 15:08
+
+import django.contrib.postgres.fields.jsonb
+from django.db import migrations, models
+import django.db.models.deletion
+import economy.models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('dashboard', '0122_auto_20200615_1510'),
+ ('grants', '0058_auto_20200615_1226'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='contribution',
+ name='tx_id',
+ field=models.CharField(blank=True, default='0x0', help_text='The transaction ID of the Contribution.', max_length=255),
+ ),
+ migrations.AlterField(
+ model_name='grant',
+ name='grant_type',
+ field=models.CharField(choices=[('tech', 'tech'), ('health', 'health'), ('Community', 'media'), ('change', 'change'), ('matic', 'matic')], default='tech', help_text='Grant CLR category', max_length=15),
+ ),
+ migrations.AlterField(
+ model_name='matchpledge',
+ name='pledge_type',
+ field=models.CharField(choices=[('tech', 'tech'), ('media', 'media'), ('health', 'health'), ('change', 'change')], default='tech', help_text='CLR pledge type', max_length=15),
+ ),
+ migrations.CreateModel(
+ name='CartActivity',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('created_on', models.DateTimeField(db_index=True, default=economy.models.get_time)),
+ ('modified_on', models.DateTimeField(default=economy.models.get_time)),
+ ('action', models.CharField(choices=[('ADD_ITEM', 'Add item to cart'), ('REMOVE_ITEM', 'Remove item to cart')], help_text='Type of activity', max_length=20)),
+ ('metadata', django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=dict, help_text='Related data to the action')),
+ ('bulk', models.BooleanField(default=False)),
+ ('grant', models.ForeignKey(help_text='Related Grant Activity ', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='cart_actions', to='grants.Grant')),
+ ('profile', models.ForeignKey(help_text='User Cart Activity', on_delete=django.db.models.deletion.CASCADE, related_name='cart_activity', to='dashboard.Profile')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ ]
diff --git a/app/grants/models.py b/app/grants/models.py
index d5331c24719..9d4a9cfb726 100644
--- a/app/grants/models.py
+++ b/app/grants/models.py
@@ -1401,3 +1401,20 @@ def to_mock_contribution(self):
context['tx_cleared'] = True
context['success'] = True
return context
+
+
+class CartActivity(SuperModel):
+ ACTIONS = (
+ ('ADD_ITEM', 'Add item to cart'),
+ ('REMOVE_ITEM', 'Remove item to cart')
+ )
+ grant = models.ForeignKey(Grant, null=True, on_delete=models.CASCADE, related_name='cart_actions',
+ help_text=_('Related Grant Activity '))
+ profile = models.ForeignKey('dashboard.Profile', on_delete=models.CASCADE, related_name='cart_activity',
+ help_text=_('User Cart Activity'))
+ action = models.CharField(max_length=20, choices=ACTIONS, help_text=_('Type of activity'))
+ metadata = JSONField(default=dict, blank=True, help_text=_('Related data to the action'))
+ bulk = models.BooleanField(default=False)
+
+ def __str__(self):
+ return f'{self.action} {self.grant.id if self.grant else "bulk"} from the cart {self.profile.handle}'
diff --git a/app/grants/templates/grants/index.html b/app/grants/templates/grants/index.html
index d3d0f005472..22d4b5477f0 100644
--- a/app/grants/templates/grants/index.html
+++ b/app/grants/templates/grants/index.html
@@ -47,7 +47,7 @@
+
Add to Cart
Want to contribute to stability of ecosystem funding? You funded {{prev_grants.count}} grant{{ prev_grants.count|pluralize }} last time. Fund
- them again?
+ them again?
{% for prev_grant in prev_grants %}
{{prev_grant.title}}
{% if not forloop.last %}{% ifequal forloop.revcounter 2 %} and {% else %}, {% endifequal %}{% else %}{% endif %}
@@ -89,6 +89,7 @@
+ {% csrf_token %}
{% include 'grants/detail/side-cart.html' %}
@@ -126,4 +127,4 @@
{% endif %}
-