From c84e5150f833c6af4827ce14b71358b49d1822fc Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Tue, 22 Apr 2025 02:41:11 +0330 Subject: [PATCH] contact us model view and admin functionality --- backend/core/settings/unfold_conf.py | 6 +++++ backend/core/views.py | 4 +++- backend/templates/formula/service.html | 23 +++++++++++++++++- backend/ticket/admin.py | 9 +++++++ .../ticket/migrations/0020_contactusmodel.py | 24 +++++++++++++++++++ .../0021_alter_contactusmodel_type.py | 18 ++++++++++++++ ...2_alter_contactusmodel_options_and_more.py | 22 +++++++++++++++++ backend/ticket/models.py | 24 ++++++++++++++++++- backend/ticket/serializers.py | 10 ++++++-- backend/ticket/urls.py | 1 + backend/ticket/views.py | 17 ++++++++++--- backend/utils/admin.py | 5 +++- 12 files changed, 154 insertions(+), 9 deletions(-) create mode 100644 backend/ticket/migrations/0020_contactusmodel.py create mode 100644 backend/ticket/migrations/0021_alter_contactusmodel_type.py create mode 100644 backend/ticket/migrations/0022_alter_contactusmodel_options_and_more.py diff --git a/backend/core/settings/unfold_conf.py b/backend/core/settings/unfold_conf.py index 23bf662..44f5e63 100644 --- a/backend/core/settings/unfold_conf.py +++ b/backend/core/settings/unfold_conf.py @@ -239,6 +239,12 @@ UNFOLD = { "badge": "utils.admin.new_ticket_count", }, + { + "title": _("ارتباط با ما"), + "icon": "perm_phone_msg", + "link": reverse_lazy("admin:ticket_contactusmodel_changelist"), + "badge": "utils.admin.new_contact_us_count", + }, ], }, { diff --git a/backend/core/views.py b/backend/core/views.py index be623df..9894f4e 100644 --- a/backend/core/views.py +++ b/backend/core/views.py @@ -8,7 +8,7 @@ from django.utils.translation import gettext_lazy as _ from django.views.generic import RedirectView, TemplateView from unfold.views import UnfoldModelAdminViewMixin from order.models import OrderModel -from ticket.models import Ticket +from ticket.models import Ticket, ContactUsModel from account.models import SecurityBreachAttemptModel import json @@ -19,9 +19,11 @@ def dashboard_callback(request, context): pending_count = OrderModel.objects.filter(status='ADMIN_PENDING').count() open_tickets_count = Ticket.objects.filter(status__in=['open', 'in_progress']).count() + open_contact_us_count = ContactUsModel.objects.filter(is_reviewed=False).count() context.update(random_data()) context.update({'pending_count': pending_count}) context.update({'open_tickets_count': open_tickets_count}) + context.update({'open_contact_us_count': open_contact_us_count}) return context diff --git a/backend/templates/formula/service.html b/backend/templates/formula/service.html index 57856a4..ca6cc92 100644 --- a/backend/templates/formula/service.html +++ b/backend/templates/formula/service.html @@ -39,4 +39,25 @@ {% endcomponent %} -{% endif %} \ No newline at end of file +{% endif %} +{% if open_contact_us_count%} + + +
+
+

+ confirmation_number + ارتباط با ما جدید داری + {{ open_contact_us_count }} +

+
+ +
+ {% component "unfold/components/flex.html" with class="flex-col gap-4 lg:flex-row" %} + {% component "unfold/components/button.html" with href="/secret-admin/ticket/contactusmodel/" %} + نمایش ارتباط با ما ها + {% endcomponent %} + {% endcomponent %} +
+
+{% endif %} diff --git a/backend/ticket/admin.py b/backend/ticket/admin.py index 1426233..bc50d27 100644 --- a/backend/ticket/admin.py +++ b/backend/ticket/admin.py @@ -18,6 +18,15 @@ class MessageInline(TabularInline): model = Message extra = 1 +@admin.register(ContactUsModel) +class ContactUsAdmin(ModelAdmin): + list_filter = ['type', 'is_reviewed'] + list_display = ['full_name', 'phone', 'email', 'message', 'is_reviewed'] + compressed_fields = True + warn_unsaved_form = True + readonly_fields = ['full_name', 'email', 'phone', 'type', 'message', ] + + @admin.register(Ticket) class TicketAdmin(ModelAdmin, ImportExportModelAdmin): import_form_class = ImportForm diff --git a/backend/ticket/migrations/0020_contactusmodel.py b/backend/ticket/migrations/0020_contactusmodel.py new file mode 100644 index 0000000..d2bd2be --- /dev/null +++ b/backend/ticket/migrations/0020_contactusmodel.py @@ -0,0 +1,24 @@ +# Generated by Django 5.1.2 on 2025-04-21 22:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ticket', '0019_alter_attachment_options'), + ] + + operations = [ + migrations.CreateModel( + name='ContactUsModel', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('full_name', models.CharField(max_length=50, verbose_name='نام و نام خانوادگی')), + ('email', models.EmailField(max_length=254, verbose_name='ایمیل')), + ('phone', models.CharField(max_length=30)), + ('type', models.CharField(choices=[('ORDER', 'پیگیری سفارش'), ('SUGGESTION', 'پیشنهادات'), ('COMPLAINT', 'انتقادات')], max_length=20, verbose_name='نوع')), + ('message', models.TextField(verbose_name='پیام')), + ], + ), + ] diff --git a/backend/ticket/migrations/0021_alter_contactusmodel_type.py b/backend/ticket/migrations/0021_alter_contactusmodel_type.py new file mode 100644 index 0000000..59c5790 --- /dev/null +++ b/backend/ticket/migrations/0021_alter_contactusmodel_type.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.2 on 2025-04-21 22:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ticket', '0020_contactusmodel'), + ] + + operations = [ + migrations.AlterField( + model_name='contactusmodel', + name='type', + field=models.CharField(choices=[('ORDER_FOLLOW_UP', 'پیگیری سفارش'), ('SUGGESTION', 'پیشنهادات'), ('COMPLAINT', 'انتقادات')], max_length=20, verbose_name='نوع'), + ), + ] diff --git a/backend/ticket/migrations/0022_alter_contactusmodel_options_and_more.py b/backend/ticket/migrations/0022_alter_contactusmodel_options_and_more.py new file mode 100644 index 0000000..537844b --- /dev/null +++ b/backend/ticket/migrations/0022_alter_contactusmodel_options_and_more.py @@ -0,0 +1,22 @@ +# Generated by Django 5.1.2 on 2025-04-21 22:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ticket', '0021_alter_contactusmodel_type'), + ] + + operations = [ + migrations.AlterModelOptions( + name='contactusmodel', + options={'verbose_name': 'ارتباط با ما', 'verbose_name_plural': 'ارتباط با ما ها '}, + ), + migrations.AddField( + model_name='contactusmodel', + name='is_reviewed', + field=models.BooleanField(default=False), + ), + ] diff --git a/backend/ticket/models.py b/backend/ticket/models.py index 0941fe3..a6b89a4 100644 --- a/backend/ticket/models.py +++ b/backend/ticket/models.py @@ -73,4 +73,26 @@ class Message(models.Model): class Meta: verbose_name = 'پیام تیکت' - verbose_name_plural = 'پیام های تیکت' \ No newline at end of file + verbose_name_plural = 'پیام های تیکت' + + +class ContactUsModel(models.Model): + full_name = models.CharField(max_length=50, verbose_name='نام و نام خانوادگی') + email = models.EmailField(max_length=254, verbose_name='ایمیل') + phone = models.CharField(max_length=30, verbose_name='شماره تماس') + FEEDBACK_TYPES = [ + ('ORDER_FOLLOW_UP', 'پیگیری سفارش'), + ('SUGGESTION', 'پیشنهادات'), + ('COMPLAINT', 'انتقادات'), + ] + + type = models.CharField(max_length=20, choices=FEEDBACK_TYPES, verbose_name='نوع') + message = models.TextField(verbose_name='پیام') + is_reviewed = models.BooleanField(default=False, verbose_name='بررسی شده') + + def __str__(self): + return f'{self.full_name} - {self.message[:15]}...' + + class Meta: + verbose_name = 'ارتباط با ما' + verbose_name_plural = 'ارتباط با ما ها ' \ No newline at end of file diff --git a/backend/ticket/serializers.py b/backend/ticket/serializers.py index d99a8d2..f4af17d 100644 --- a/backend/ticket/serializers.py +++ b/backend/ticket/serializers.py @@ -1,5 +1,5 @@ from rest_framework import serializers -from .models import Ticket, Message, Attachment +from .models import Ticket, Message, Attachment, ContactUsModel from django.utils.timezone import localtime from order.serializers import OrderListSerializer from order.serializers import OrderModel @@ -88,4 +88,10 @@ class TicketListSerializer(serializers.ModelSerializer): return obj.get_status_display() def get_ticket_category(self, obj): - return obj.get_ticket_category_display() \ No newline at end of file + return obj.get_ticket_category_display() + + +class ContactUsSerializer(serializers.ModelSerializer): + class Meta: + model = ContactUsModel + fields = ['full_name', 'email', 'phone', 'type', 'message'] \ No newline at end of file diff --git a/backend/ticket/urls.py b/backend/ticket/urls.py index c3a47f5..50eed91 100644 --- a/backend/ticket/urls.py +++ b/backend/ticket/urls.py @@ -6,6 +6,7 @@ urlpatterns = [ path('', views.TicketListView.as_view(), name='ticket-list'), path('', views.TicketDetailView.as_view(), name='ticket-detail'), path('message/create', views.MessageCreateView.as_view(), name='message-create'), + path('contact-us/create', views.CreateContactUsView.as_view(), name='contact-us-create'), path('attachment/create', views.AttachmentUploadView.as_view(), name='attachment-upload'), path('attachment/delete/', views.AttachmentDeleteView.as_view(), name='attachment-upload'), ] \ No newline at end of file diff --git a/backend/ticket/views.py b/backend/ticket/views.py index ac6fdae..e669864 100644 --- a/backend/ticket/views.py +++ b/backend/ticket/views.py @@ -2,7 +2,7 @@ from rest_framework import generics, permissions from rest_framework.response import Response from rest_framework.views import APIView from .models import Ticket, Message, Attachment -from .serializers import TicketListSerializer, MessageSerializer, TicketSerializer, AttachmentSerializer +from .serializers import TicketListSerializer, MessageSerializer, TicketSerializer, AttachmentSerializer, ContactUsSerializer from utils.pagination import StructurePagination from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiTypes from rest_framework.permissions import IsAuthenticated @@ -56,7 +56,6 @@ class TicketCreateView(APIView): permission_classes = [permissions.IsAuthenticated] def post(self, request): new_ticket_ser = self.serializer_class(data=request.data, context={'request': request}) - message = request.data.get('message', None) if new_ticket_ser.is_valid(): new_ticket_ser.save(customer=request.user) return Response(new_ticket_ser.data, status=status.HTTP_201_CREATED) @@ -159,4 +158,16 @@ class UpdateTicketStatusView(APIView): return Response({"error": "Invalid status"}, status=400) ticket.status = new_status ticket.save() - return Response({"message": "Ticket status updated successfully"}) \ No newline at end of file + return Response({"message": "Ticket status updated successfully"}) + + +class CreateContactUsView(APIView): + serializer_class = ContactUsSerializer + permission_classes = [permissions.AllowAny] + def post(self, request): + contact_us_ser = self.serializer_class(data=request.data, context={'request': request}) + if contact_us_ser.is_valid(): + contact_us_ser.save() + return Response(contact_us_ser.data, status=status.HTTP_201_CREATED) + else: + return Response(contact_us_ser.errors, status=status.HTTP_400_BAD_REQUEST) \ No newline at end of file diff --git a/backend/utils/admin.py b/backend/utils/admin.py index 1107fab..82c4d05 100644 --- a/backend/utils/admin.py +++ b/backend/utils/admin.py @@ -1,6 +1,6 @@ from order.models import OrderModel from product.models import DollorModel, CommentModel -from ticket.models import Ticket +from ticket.models import Ticket, ContactUsModel from home.models import LearnVideoModel from account.models import SecurityBreachAttemptModel @@ -24,6 +24,9 @@ def new_learn_video_count(request): def new_attck_count(request): return SecurityBreachAttemptModel.objects.filter(viewd=False).count() +def new_contact_us_count(request): + return ContactUsModel.objects.filter(is_reviewed=False).count() + from django.contrib import admin, messages from unfold.admin import ModelAdmin from home.models import LearnVideoModel