transactions
This commit is contained in:
@@ -1,5 +1,45 @@
|
||||
from django.contrib import admin
|
||||
from .models import TicketIssueType, TicketAttachment, Tickets
|
||||
from .models import TicketIssueType, TicketAttachment, Tickets, Feedback, ContactUs
|
||||
|
||||
|
||||
class FeedbackAdmin(admin.ModelAdmin):
|
||||
list_display = ("id", "principal", "email", "rating", "short_comment")
|
||||
list_filter = ("rating", "principal")
|
||||
search_fields = ("email", "comment")
|
||||
readonly_fields = ("email",)
|
||||
|
||||
def short_comment(self, obj):
|
||||
"""Create a shortened version of the comment for display in the list."""
|
||||
return obj.comment[:50] + "..." if len(obj.comment) > 50 else obj.comment
|
||||
|
||||
short_comment.short_description = "Comment"
|
||||
|
||||
|
||||
class ContactUsAdmin(admin.ModelAdmin):
|
||||
list_display = (
|
||||
"name",
|
||||
"email_address",
|
||||
"mobile_number",
|
||||
"subject",
|
||||
"short_message",
|
||||
"has_reply",
|
||||
)
|
||||
list_filter = ("subject",)
|
||||
search_fields = ("name", "email_address", "mobile_number", "message")
|
||||
readonly_fields = ("name", "email_address", "mobile_number", "subject", "message")
|
||||
|
||||
def short_message(self, obj):
|
||||
"""Create a shortened version of the message for display in the list."""
|
||||
return obj.message[:50] + "..." if len(obj.message) > 50 else obj.message
|
||||
|
||||
short_message.short_description = "Message"
|
||||
|
||||
def has_reply(self, obj):
|
||||
"""Indicate whether the contact request has been replied to."""
|
||||
return obj.reply is not None
|
||||
|
||||
has_reply.boolean = True
|
||||
has_reply.short_description = "Replied?"
|
||||
|
||||
|
||||
class TicketIssueTypeAdmin(admin.ModelAdmin):
|
||||
@@ -26,6 +66,8 @@ class TicketsAdmin(admin.ModelAdmin):
|
||||
filter_horizontal = ("attachments",) # For the many-to-many relationship
|
||||
|
||||
|
||||
admin.site.register(ContactUs, ContactUsAdmin)
|
||||
admin.site.register(Feedback, FeedbackAdmin)
|
||||
admin.site.register(TicketIssueType, TicketIssueTypeAdmin)
|
||||
admin.site.register(TicketAttachment, TicketAttachmentAdmin)
|
||||
admin.site.register(Tickets, TicketsAdmin)
|
||||
|
||||
@@ -9,7 +9,8 @@ from goodtimes import date_utils
|
||||
class ContactUsSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = ContactUs
|
||||
fields = "__all__"
|
||||
# Alternatively, you can specify fields explicitly:
|
||||
fields = ['name', 'email_address', 'mobile_number', 'subject', 'message']
|
||||
|
||||
class TicketIssueTypeSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
|
||||
@@ -4,12 +4,15 @@ from . import views
|
||||
app_name = "manage_communications"
|
||||
|
||||
urlpatterns = [
|
||||
path('contact_us/', views.ContactUsListView.as_view(), name='contact_us_list'),
|
||||
path('contact_us/reply/', views.ContactUsReplyView.as_view(), name='contact_us_reply'),
|
||||
|
||||
path('tickets/', views.TicketListView.as_view(), name='ticket_list'),
|
||||
|
||||
path('feedback/', views.FeedbackListView.as_view(), name='feedback_list'),
|
||||
path('feedback/delete/<int:pk>', views.FeedbackDeleteView.as_view(), name='feedback_delete'),
|
||||
|
||||
path("contact_us/", views.ContactUsListView.as_view(), name="contact_us_list"),
|
||||
path(
|
||||
"contact_us/reply/", views.ContactUsReplyView.as_view(), name="contact_us_reply"
|
||||
),
|
||||
path("tickets/", views.TicketListView.as_view(), name="ticket_list"),
|
||||
path("feedback/", views.FeedbackListView.as_view(), name="feedback_list"),
|
||||
path(
|
||||
"feedback/delete/<int:pk>",
|
||||
views.FeedbackDeleteView.as_view(),
|
||||
name="feedback_delete",
|
||||
),
|
||||
]
|
||||
|
||||
@@ -124,6 +124,7 @@ class FeedbackDeleteView(LoginRequiredMixin, generic.View):
|
||||
try:
|
||||
type_obj = self.model.objects.get(id=pk)
|
||||
type_obj.deleted = True
|
||||
type_obj.active = False
|
||||
type_obj.save()
|
||||
messages.success(request, self.success_message)
|
||||
except self.model.DoesNotExist:
|
||||
|
||||
@@ -6,6 +6,7 @@ from manage_events.models import (
|
||||
Event,
|
||||
EventCategory,
|
||||
EventImage,
|
||||
EventReview,
|
||||
Favorites,
|
||||
Venue,
|
||||
PrincipalPreference,
|
||||
@@ -44,6 +45,7 @@ class EventDetailSerializer(serializers.ModelSerializer):
|
||||
venue = VenueSerializer(read_only=True) # Use VenueSerializer for the venue field
|
||||
category = EventCategorySerializer(read_only=True)
|
||||
is_favorited = serializers.SerializerMethodField()
|
||||
reviews = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Event
|
||||
@@ -66,6 +68,7 @@ class EventDetailSerializer(serializers.ModelSerializer):
|
||||
"age_group",
|
||||
"images",
|
||||
"is_favorited",
|
||||
"reviews",
|
||||
]
|
||||
|
||||
def get_images(self, obj):
|
||||
@@ -81,6 +84,11 @@ class EventDetailSerializer(serializers.ModelSerializer):
|
||||
return Favorites.objects.filter(event=obj, principal=user).exists()
|
||||
return False
|
||||
|
||||
def get_reviews(self, obj):
|
||||
# related_name is 'reviews' as defined in EventReview model
|
||||
reviews = obj.reviews.all()
|
||||
return EventReviewSerializer(reviews, many=True, context=self.context).data
|
||||
|
||||
|
||||
class CreateEventSerializer(serializers.ModelSerializer):
|
||||
images = serializers.ListField(
|
||||
@@ -187,5 +195,20 @@ class EventMasterSerializer(serializers.ModelSerializer):
|
||||
|
||||
|
||||
class EventDateRangeSerializer(serializers.Serializer):
|
||||
start_date = serializers.DateField(format='%Y-%m-%d', input_formats=['%Y-%m-%d'])
|
||||
end_date = serializers.DateField(format='%Y-%m-%d', input_formats=['%Y-%m-%d'])
|
||||
start_date = serializers.DateField(format="%Y-%m-%d", input_formats=["%Y-%m-%d"])
|
||||
end_date = serializers.DateField(format="%Y-%m-%d", input_formats=["%Y-%m-%d"])
|
||||
|
||||
|
||||
class EventReviewSerializer(serializers.ModelSerializer):
|
||||
principal = ProfileSerializer(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = EventReview
|
||||
fields = ["id", "event", "principal", "review_text", "rating"]
|
||||
extra_kwargs = {
|
||||
"principal": {"read_only": True},
|
||||
}
|
||||
|
||||
def create(self, validated_data):
|
||||
validated_data["principal"] = self.context["request"].user
|
||||
return super().create(validated_data)
|
||||
|
||||
@@ -91,4 +91,10 @@ urlpatterns = [
|
||||
),
|
||||
# My Events
|
||||
path("my-events/", views.MyEventsAPIView.as_view(), name="my-events"),
|
||||
# Events Review Add and Edit
|
||||
path(
|
||||
"event-reviews/",
|
||||
views.EventReviewCreateAPIView.as_view(),
|
||||
name="event-review-create",
|
||||
),
|
||||
]
|
||||
|
||||
@@ -2,7 +2,7 @@ from datetime import timedelta
|
||||
from django.db import transaction
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils import timezone
|
||||
from rest_framework import status, generics
|
||||
from rest_framework import status, generics, mixins
|
||||
from rest_framework.views import APIView
|
||||
from django.conf import settings
|
||||
from accounts.models import IAmPrincipalLocation
|
||||
@@ -22,6 +22,7 @@ from manage_events.api.serializers import (
|
||||
CreateVenueSerializer,
|
||||
EventCategorySerializer,
|
||||
EventDetailSerializer,
|
||||
EventReviewSerializer,
|
||||
IAmPrincipalLocationSerializer,
|
||||
PrincipalPreferenceSerializer,
|
||||
VenueSerializer,
|
||||
@@ -32,6 +33,7 @@ from manage_events.models import (
|
||||
Event,
|
||||
EventCategory,
|
||||
EventPrincipalInteraction,
|
||||
EventReview,
|
||||
Favorites,
|
||||
PrincipalPreference,
|
||||
Venue,
|
||||
@@ -659,3 +661,25 @@ class EventDateRangeAPIView(APIView):
|
||||
message=constants.SUCCESS,
|
||||
status=status.HTTP_200_OK,
|
||||
)
|
||||
|
||||
|
||||
class EventReviewCreateAPIView(mixins.CreateModelMixin, generics.GenericAPIView):
|
||||
queryset = EventReview.objects.filter(active=True, deleted=False)
|
||||
serializer_class = EventReviewSerializer
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
self.perform_create(serializer)
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return ApiResponse.success(
|
||||
"Review created successfully.",
|
||||
data=serializer.data,
|
||||
status=status.HTTP_201_CREATED,
|
||||
)
|
||||
else:
|
||||
return ApiResponse.error(
|
||||
"Validation error.",
|
||||
errors=serializer.errors,
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
# Generated by Django 5.0.2 on 2024-03-10 14:49
|
||||
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("manage_events", "0003_alter_eventprincipalinteraction_unique_together"),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="favorites",
|
||||
name="active",
|
||||
field=models.BooleanField(default=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="favorites",
|
||||
name="created_by",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="%(class)s_created",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="favorites",
|
||||
name="created_on",
|
||||
field=models.DateTimeField(
|
||||
auto_now_add=True, default=django.utils.timezone.now
|
||||
),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="favorites",
|
||||
name="deleted",
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="favorites",
|
||||
name="modified_by",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="%(class)s_modified",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="favorites",
|
||||
name="modified_on",
|
||||
field=models.DateTimeField(auto_now=True),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="EventReview",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("active", models.BooleanField(default=True)),
|
||||
("deleted", models.BooleanField(default=False)),
|
||||
("created_on", models.DateTimeField(auto_now_add=True)),
|
||||
("modified_on", models.DateTimeField(auto_now=True)),
|
||||
("review_text", models.TextField()),
|
||||
(
|
||||
"rating",
|
||||
models.PositiveSmallIntegerField(
|
||||
choices=[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]
|
||||
),
|
||||
),
|
||||
(
|
||||
"created_by",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="%(class)s_created",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
(
|
||||
"event",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="reviews",
|
||||
to="manage_events.event",
|
||||
),
|
||||
),
|
||||
(
|
||||
"modified_by",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="%(class)s_modified",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
(
|
||||
"principal",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="event_reviews",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"ordering": ["-created_on"],
|
||||
"unique_together": {("event", "principal")},
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,17 @@
|
||||
# Generated by Django 5.0.2 on 2024-03-10 17:08
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("manage_events", "0004_favorites_active_favorites_created_by_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name="eventreview",
|
||||
unique_together=set(),
|
||||
),
|
||||
]
|
||||
@@ -126,7 +126,7 @@ class PrincipalPreference(BaseModel):
|
||||
db_table = "user_preference"
|
||||
|
||||
|
||||
class Favorites(models.Model):
|
||||
class Favorites(BaseModel):
|
||||
principal = models.ForeignKey(
|
||||
IAmPrincipal, on_delete=models.CASCADE, related_name="favorited_by"
|
||||
)
|
||||
@@ -137,3 +137,27 @@ class Favorites(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.principal}'s favorite: {self.event.title}"
|
||||
|
||||
|
||||
class EventReview(BaseModel):
|
||||
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="reviews")
|
||||
principal = models.ForeignKey(
|
||||
IAmPrincipal, on_delete=models.CASCADE, related_name="event_reviews"
|
||||
)
|
||||
review_text = models.TextField()
|
||||
rating = models.PositiveSmallIntegerField(
|
||||
choices=[
|
||||
(1, 1),
|
||||
(2, 2),
|
||||
(3, 3),
|
||||
(4, 4),
|
||||
(5, 5),
|
||||
]
|
||||
)
|
||||
|
||||
class Meta:
|
||||
# sort reviews, to show latest first by default:
|
||||
ordering = ["-created_on"]
|
||||
|
||||
def __str__(self):
|
||||
return f"Review by {self.principal} on {self.event}"
|
||||
|
||||
@@ -19,7 +19,34 @@ class WalletSerializer(serializers.ModelSerializer):
|
||||
class TransactionSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = models.Transaction
|
||||
fields = "__all__"
|
||||
fields = [
|
||||
'id',
|
||||
'principal',
|
||||
'principal_subscription',
|
||||
'transaction_type',
|
||||
'payment_method',
|
||||
'transaction_status',
|
||||
'amount',
|
||||
'comment',
|
||||
'order_id',
|
||||
'product_id',
|
||||
'reference_id',
|
||||
]
|
||||
extra_kwargs = {
|
||||
'principal': {'read_only': True},
|
||||
'order_id': {'required': False},
|
||||
'product_id': {'required': False},
|
||||
'reference_id': {'required': False},
|
||||
}
|
||||
|
||||
def to_representation(self, instance):
|
||||
# Customize the representation of the serializer, if necessary
|
||||
representation = super().to_representation(instance)
|
||||
# For example, to display a human-readable version of the transaction type:
|
||||
representation['transaction_type_display'] = instance.get_transaction_type_display()
|
||||
representation['payment_method_display'] = instance.get_payment_method_display()
|
||||
representation['transaction_status_display'] = instance.get_transaction_status_display()
|
||||
return representation
|
||||
|
||||
|
||||
class MerchantDepositSerializer(serializers.Serializer):
|
||||
|
||||
@@ -4,14 +4,13 @@ from . import views
|
||||
app_name = "manage_wallet_api"
|
||||
|
||||
urlpatterns = [
|
||||
|
||||
path('become-a-merchant/<str:principal_type>/', views.BecomeAMerchantView.as_view(), name='become_a_merchant'),
|
||||
path('webhook-test/', views.StripeWebhookTest.as_view(), name='webhook_test'),
|
||||
path('postman-test/', views.TestWebhookAPI.as_view(), name='postman_test'),
|
||||
path('postman-test-withdraw/', views.TestWebhookAPIWithdraw.as_view(), name='postman_test_withdraw'),
|
||||
path('get-wallet/', views.GetWallet.as_view(), name='get_wallet'),
|
||||
path('is-merchant/', views.IsMerchant.as_view(), name='is_merchant'),
|
||||
path('transactions/', views.TransactionView.as_view(), name='transactions'),
|
||||
path('merchant-deposit/', views.MerchantDeposit.as_view(), name='merchant_deposit'),
|
||||
|
||||
path("webhook-test/", views.StripeWebhookTest.as_view(), name="webhook_test"),
|
||||
path("postman-test/", views.TestWebhookAPI.as_view(), name="postman_test"),
|
||||
path(
|
||||
"postman-test-withdraw/",
|
||||
views.TestWebhookAPIWithdraw.as_view(),
|
||||
name="postman_test_withdraw",
|
||||
),
|
||||
path("get-wallet/", views.GetWallet.as_view(), name="get_wallet"),
|
||||
path("get-transaction/", views.TransactionView.as_view(), name="transactions"),
|
||||
]
|
||||
|
||||
@@ -24,38 +24,6 @@ from rest_framework_simplejwt.authentication import JWTAuthentication
|
||||
import stripe
|
||||
|
||||
|
||||
class BecomeAMerchantView(APIView):
|
||||
authentication_classes = [JWTAuthentication]
|
||||
permission_classes = [IsAuthenticated]
|
||||
stripe.api_key = settings.STRIPE_SECRET_KEY
|
||||
|
||||
def post(self, request, type):
|
||||
print(request.data)
|
||||
amount = 1000
|
||||
principal_type = request.data["principal_type"]
|
||||
try:
|
||||
payment_intent = stripe.PaymentIntent.create(
|
||||
amount=100000,
|
||||
currency="INR",
|
||||
description="Merchant Registration",
|
||||
metadata={
|
||||
"principal_id": request.user.id,
|
||||
"principal_type": principal_type,
|
||||
},
|
||||
)
|
||||
# payment_intent_id = payment_intent.id # Get the payment_intent ID
|
||||
|
||||
return Response(
|
||||
{
|
||||
"client_secret": payment_intent.client_secret,
|
||||
"message": "Payment intent created successfully",
|
||||
}
|
||||
)
|
||||
except stripe.error.StripeError as e:
|
||||
# Handle Stripe-related errors
|
||||
return Response({"error": str(e)}, status=400)
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name="dispatch")
|
||||
class StripeWebhookTest(APIView):
|
||||
authentication_classes = []
|
||||
@@ -165,39 +133,6 @@ class GetWallet(APIView):
|
||||
return ApiResponse.error(**error_response)
|
||||
|
||||
|
||||
class IsMerchant(APIView):
|
||||
authentication_classes = [JWTAuthentication]
|
||||
permission_classes = [IsAuthenticated]
|
||||
model = models.Wallet
|
||||
|
||||
def get(self, request):
|
||||
try:
|
||||
# Get the Wallet associated with the request.user
|
||||
wallet_obj = models.Wallet.objects.get(principal_id=request.user.id)
|
||||
merchant_deposit = wallet_obj.merchant_deposit
|
||||
if merchant_deposit >= 1.00:
|
||||
success_response = {
|
||||
"status": status.HTTP_200_OK,
|
||||
"message": constants.SUCCESS,
|
||||
"data": {"is_merchant": True},
|
||||
}
|
||||
return ApiResponse.success(**success_response)
|
||||
data_response = {
|
||||
"status": status.HTTP_200_OK,
|
||||
"message": constants.SUCCESS,
|
||||
"data": {"is_merchant": False},
|
||||
}
|
||||
return ApiResponse.success(**data_response)
|
||||
except Exception as e:
|
||||
# Handle any exceptions and return an error response
|
||||
error_response = {
|
||||
"status": status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
"message": constants.INTERNAL_SERVER_ERROR,
|
||||
"errors": str(e),
|
||||
}
|
||||
return ApiResponse.error(**error_response)
|
||||
|
||||
|
||||
class TransactionView(APIView):
|
||||
authentication_classes = [JWTAuthentication]
|
||||
permission_classes = [IsAuthenticated]
|
||||
@@ -335,45 +270,3 @@ class TestWebhookAPIWithdraw(APIView):
|
||||
"message": "Webhook received, but payment failed",
|
||||
}
|
||||
return ApiResponse.success(**intent_response)
|
||||
|
||||
|
||||
class MerchantDeposit(APIView):
|
||||
authentication_classes = [JWTAuthentication]
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
# print(request.data)
|
||||
serializer = serializers.MerchantDepositSerializer(data=request.data)
|
||||
# print("serializer: ", serializer)
|
||||
if serializer.is_valid():
|
||||
print(serializer.validated_data)
|
||||
wallet_manager = services.WalletManager(
|
||||
principal=request.user,
|
||||
principal_type=IAmPrincipalType.objects.filter(
|
||||
name=serializer.validated_data["principal_type"]
|
||||
).first(),
|
||||
)
|
||||
deposit_amount = serializer.validated_data["amount"]
|
||||
deposit_field = serializer.validated_data["field"]
|
||||
|
||||
deposit_transaction = wallet_manager.deposit(deposit_amount, deposit_field)
|
||||
|
||||
print("Passed Through principal_wallet Object")
|
||||
current_balance = models.Wallet.objects.get(principal=request.user)
|
||||
success_response = {
|
||||
"status": status.HTTP_200_OK,
|
||||
"message": "Deposit Received",
|
||||
"data": {
|
||||
**serializer.data,
|
||||
"merchant_deposit": current_balance.merchant_deposit,
|
||||
},
|
||||
}
|
||||
return ApiResponse.success(**success_response)
|
||||
|
||||
else:
|
||||
intent_response = {
|
||||
"status": status.HTTP_400_BAD_REQUEST,
|
||||
"message": "Deposit Failed",
|
||||
"errors": serializer.errors,
|
||||
}
|
||||
return ApiResponse.error(**intent_response)
|
||||
|
||||
@@ -78,9 +78,6 @@
|
||||
<th class="sorting" tabindex="5" aria-controls="style-3"
|
||||
|
||||
style="width: 79.7969px;">Rating</th>
|
||||
<th class="sorting" tabindex="5" aria-controls="style-3"
|
||||
|
||||
style="width: 79.7969px;">Reaction</th>
|
||||
<th class="dt-no-sorting sorting" tabindex="6"
|
||||
aria-controls="style-3"
|
||||
style="width: 51.625px;">Action</th>
|
||||
@@ -100,7 +97,6 @@
|
||||
</span>
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td>{{data_obj.feedback_reaction}}</td>
|
||||
<td class="text-center">
|
||||
<ul class="table-controls">
|
||||
<li><a href="{% url 'manage_communications:feedback_delete' data_obj.id %}" class="bs-tooltip"
|
||||
|
||||
Reference in New Issue
Block a user