mirror of
https://github.com/FlipsideCrypto/badger.git
synced 2026-02-06 10:57:46 +00:00
feat: add feedback to api and front-end
This commit is contained in:
parent
93cece08a4
commit
cff93359c3
@ -4,6 +4,6 @@ from .models import Feedback
|
||||
|
||||
@admin.register(Feedback)
|
||||
class FeedbackAdmin(admin.ModelAdmin):
|
||||
list_display = ('url', 'liked', 'comment', 'created_at', 'updated_at')
|
||||
list_display = ('feedback_url', 'liked', 'comment', 'created_at', 'updated_at')
|
||||
list_filter = ('created_at', 'updated_at')
|
||||
search_fields = ('url', 'comment')
|
||||
search_fields = ('feedback_url', 'comment')
|
||||
@ -1,13 +1,17 @@
|
||||
# Generated by Django 4.1.1 on 2022-10-15 21:06
|
||||
# Generated by Django 4.1.1 on 2022-10-15 21:16
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = []
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
@ -22,11 +26,19 @@ class Migration(migrations.Migration):
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("url", models.CharField(max_length=255)),
|
||||
("feedback_url", models.CharField(max_length=255)),
|
||||
("liked", models.BooleanField()),
|
||||
("comment", models.TextField(blank=True, null=True)),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
(
|
||||
"author",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="feedbacks",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
26
api/feedback/migrations/0002_alter_feedback_author.py
Normal file
26
api/feedback/migrations/0002_alter_feedback_author.py
Normal file
@ -0,0 +1,26 @@
|
||||
# Generated by Django 4.1.1 on 2022-10-15 22:31
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
("feedback", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="feedback",
|
||||
name="author",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="feedbacks",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
]
|
||||
26
api/feedback/migrations/0003_alter_feedback_author.py
Normal file
26
api/feedback/migrations/0003_alter_feedback_author.py
Normal file
@ -0,0 +1,26 @@
|
||||
# Generated by Django 4.1.1 on 2022-10-15 22:41
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
("feedback", "0002_alter_feedback_author"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="feedback",
|
||||
name="author",
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="feedbacks",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
]
|
||||
@ -1,7 +1,17 @@
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.db import models
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
class Feedback(models.Model):
|
||||
url = models.CharField(max_length=255)
|
||||
author = models.ForeignKey(
|
||||
'siwe_auth.Wallet',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='feedbacks',
|
||||
null=True
|
||||
)
|
||||
|
||||
feedback_url = models.CharField(max_length=255)
|
||||
liked = models.BooleanField()
|
||||
comment = models.TextField(blank=True, null=True)
|
||||
|
||||
|
||||
@ -1,20 +1,40 @@
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from .models import Feedback
|
||||
|
||||
class FeedbackSerializer(serializers.ModelSerializer):
|
||||
def validate_url(self, value):
|
||||
if not all([value.startswith('http://'), 'badger' in value]):
|
||||
raise serializers.ValidationError('Invalid URL')
|
||||
User = get_user_model()
|
||||
|
||||
return value
|
||||
|
||||
class FeedbackUserSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = (
|
||||
'url',
|
||||
'ethereum_address',
|
||||
'ens_name',
|
||||
'ens_avatar',
|
||||
)
|
||||
|
||||
|
||||
class FeedbackSerializer(serializers.ModelSerializer):
|
||||
author = FeedbackUserSerializer(read_only=True)
|
||||
|
||||
def create(self, validated_data):
|
||||
validated_data['author'] = self.context['request'].user
|
||||
return super().create(validated_data)
|
||||
|
||||
class Meta:
|
||||
model = Feedback
|
||||
fields = (
|
||||
'url',
|
||||
'liked',
|
||||
'comment',
|
||||
'created_at',
|
||||
'url',
|
||||
'id',
|
||||
'author',
|
||||
'feedback_url',
|
||||
'liked',
|
||||
'comment',
|
||||
'created_at',
|
||||
'updated_at'
|
||||
)
|
||||
)
|
||||
depth = 1
|
||||
|
||||
@ -5,4 +5,6 @@ from .views import (
|
||||
)
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
router.register(r'badges', FeedbackViewSet)
|
||||
router.register(r'feedback', FeedbackViewSet)
|
||||
|
||||
urlpatterns = router.urls
|
||||
@ -4,6 +4,8 @@ from rest_framework.permissions import (
|
||||
IsAdminUser
|
||||
)
|
||||
|
||||
from api.permissions import generator
|
||||
|
||||
from .models import Feedback
|
||||
from .serializers import FeedbackSerializer
|
||||
|
||||
@ -13,8 +15,15 @@ class FeedbackViewSet(viewsets.ModelViewSet):
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def get_permissions(self):
|
||||
if self.action != 'create':
|
||||
return [IsAdminUser]
|
||||
def get_queryset(self):
|
||||
if not self.request.user.is_admin:
|
||||
return self.queryset.filter(author=self.request.user)
|
||||
return self.queryset
|
||||
|
||||
return super().get_permissions()
|
||||
def get_permissions(self):
|
||||
permission_classes = []
|
||||
|
||||
if self.action in ['update', 'partial_update', 'destroy']:
|
||||
permission_classes = [IsAdminUser]
|
||||
|
||||
return generator(self.permission_classes + permission_classes)
|
||||
@ -40,7 +40,7 @@ services:
|
||||
env_file:
|
||||
- ./.env
|
||||
volumes:
|
||||
- .:/api/code
|
||||
- ./api:/code
|
||||
depends_on:
|
||||
- badger_db
|
||||
links:
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import { useState } from "react";
|
||||
|
||||
import { Button } from "@mui/material";
|
||||
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
|
||||
import { postFeedbackRequest } from "@utils/api_requests";
|
||||
|
||||
import StatusIndicators from './StatusIndicators/StatusIndicators';
|
||||
|
||||
import "@style/Dashboard/Sidebar/Sidebar.css";
|
||||
@ -14,8 +15,17 @@ const HelpSidebar = () => {
|
||||
|
||||
const collapseIcon = collapsed ? "chevron-left" : "chevron-right";
|
||||
|
||||
const toggleCollapsed = () => {
|
||||
setCollapsed(!collapsed);
|
||||
const onFeedbackSubmission = async ({ liked }) => {
|
||||
const feedbackObj = {
|
||||
feedback_url: window.location.href,
|
||||
liked,
|
||||
}
|
||||
|
||||
// TODO: Needs to be updated to support adding error when feedback is not added
|
||||
const response = await postFeedbackRequest(feedbackObj);
|
||||
if(response.status === 200) {
|
||||
console.log("Feedback submitted successfully");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
@ -23,7 +33,7 @@ const HelpSidebar = () => {
|
||||
<div className="sidebar__header">
|
||||
<h5>Help</h5>
|
||||
<div>
|
||||
<Button onClick={toggleCollapsed}>
|
||||
<Button onClick={() => { setCollapsed(!collapsed) }}>
|
||||
<FontAwesomeIcon icon={['fal', collapseIcon]} />
|
||||
</Button>
|
||||
</div>
|
||||
@ -44,11 +54,17 @@ const HelpSidebar = () => {
|
||||
<StatusIndicators />
|
||||
</div>
|
||||
|
||||
{/* <div className="sidebar__footer">
|
||||
<div className="sidebar__footer">
|
||||
<p>Do you like this page?</p>
|
||||
<FontAwesomeIcon icon={['fal', 'thumbs-up']} />
|
||||
<FontAwesomeIcon icon={['fal', 'thumbs-down']} />
|
||||
</div> */}
|
||||
|
||||
<Button onClick={() => { onFeedbackSubmission({ liked: true }) }}>
|
||||
<FontAwesomeIcon icon={['fal', 'thumbs-up']} />
|
||||
</Button>
|
||||
|
||||
<Button onClick={() => { onFeedbackSubmission({ liked: false }) }}>
|
||||
<FontAwesomeIcon icon={['fal', 'thumbs-down']} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -34,13 +34,16 @@
|
||||
|
||||
.sidebar.right .sidebar__footer {
|
||||
display: grid;
|
||||
grid-template-columns: 200px auto auto;
|
||||
grid-template-columns: auto auto auto;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sidebar.right .sidebar__footer p {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.sidebar.right .sidebar__footer svg {
|
||||
color:rgba(0, 0, 0, .35);
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.sidebar.right.collapsed {
|
||||
|
||||
@ -3,6 +3,36 @@ import { cleanAddresses, getCSRFToken } from "./helpers";
|
||||
|
||||
const API_URL = process.env.REACT_APP_API_URL;
|
||||
|
||||
export async function postFeedbackRequest(feedback) {
|
||||
let response;
|
||||
|
||||
try {
|
||||
await fetch(`${API_URL}/feedback/`, {
|
||||
method: 'POST',
|
||||
mode: "cors",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': getCSRFToken(),
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(feedback)
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if(!data?.id) throw new Error("Feedback POST request failed");
|
||||
response = data;
|
||||
})
|
||||
.catch(err => {
|
||||
throw new Error(err);
|
||||
})
|
||||
}
|
||||
catch(err) {
|
||||
response = { error: err };
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
export async function postOrgRequest(org) {
|
||||
let response;
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user