From 7f9ed031e9eb2b654e09a718966994d4e8913927 Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sat, 1 Feb 2025 18:39:58 +0330 Subject: [PATCH 01/37] chage password admin conf --- backend/account/admin.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/account/admin.py b/backend/account/admin.py index 3da5f16..f9589ec 100644 --- a/backend/account/admin.py +++ b/backend/account/admin.py @@ -7,6 +7,8 @@ from unfold.contrib.import_export.forms import ExportForm, ImportForm, Selectabl from unfold.contrib.forms.widgets import ArrayWidget, WysiwygWidget from django.contrib.postgres.fields import ArrayField from django.contrib.auth.models import Group +from unfold.forms import AdminPasswordChangeForm + @admin.register(User) class UserAdmin(BaseUserAdmin, ModelAdmin, ImportExportModelAdmin): @@ -20,7 +22,7 @@ class UserAdmin(BaseUserAdmin, ModelAdmin, ImportExportModelAdmin): exclude = ('otp_hash', 'otp_expiry', 'is_active', 'is_staff', 'password', 'last_login') import_form_class = ImportForm export_form_class = ExportForm - + change_password_form = AdminPasswordChangeForm fieldsets = ( ('Personal info', {'fields': ('first_name', 'last_name', 'profile_photo')}), ('contact', {'fields': ('phone', 'email')}), From 91e5401827bd11550d2e1422110bc3fd49a31153 Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sat, 1 Feb 2025 18:41:04 +0330 Subject: [PATCH 02/37] blog verbose name --- backend/blog/models.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/blog/models.py b/backend/blog/models.py index 103521a..1249202 100644 --- a/backend/blog/models.py +++ b/backend/blog/models.py @@ -26,6 +26,9 @@ class BlogModel(models.Model): def __str__(self): return self.title + class Meta: + verbose_name = 'بلاگ' + verbose_name_plural = 'بلاگ ها' # class Comment(models.Model): From 5e80598643c86a879355d839a4a4c40fff6acc38 Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sat, 1 Feb 2025 18:41:13 +0330 Subject: [PATCH 03/37] chat verbose name --- backend/chat/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/chat/models.py b/backend/chat/models.py index f61143e..ebd806d 100644 --- a/backend/chat/models.py +++ b/backend/chat/models.py @@ -15,6 +15,8 @@ class ProductChatModel(models.Model): class Meta: unique_together = ['user', 'product'] + verbose_name = 'جت محصلول و کاربر' + verbose_name_plural = 'چت های محصلول کاربر' def __str__(self): return f'{self.user} - {self.product}' From 74ba0d41445705db59d86d3afe46cf8a0c0b7bfb Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sat, 1 Feb 2025 18:41:27 +0330 Subject: [PATCH 04/37] HomeImageModel verbose name --- backend/home/models.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/home/models.py b/backend/home/models.py index 421f5f7..47c231e 100644 --- a/backend/home/models.py +++ b/backend/home/models.py @@ -30,3 +30,6 @@ class HomeImageModel(models.Model): unique_filed = models.CharField(max_length=20, choices=unique, unique=True, default='unique') def __str__(self): return f'{self.title1} - {self.title2}' + class Meta: + verbose_name = 'مدل عکس تفاوت خانه' + verbose_name_plural = 'مدل عکس تفاوت خانه' \ No newline at end of file From 1ae2a73e624bec53d2ced978744e8529d06d185d Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sat, 1 Feb 2025 18:41:42 +0330 Subject: [PATCH 05/37] dollor model verbose name --- backend/product/models.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/product/models.py b/backend/product/models.py index 0c9af20..1863ca4 100644 --- a/backend/product/models.py +++ b/backend/product/models.py @@ -80,6 +80,9 @@ class DollorModel(models.Model): return self.defualt_price return price_in_usd + class Meta: + verbose_name = 'مدل دلار' + verbose_name_plural = 'مدل دلار' class ProductModel(models.Model): From 6decde6c23a7dd5f862d1a685c307cbc991377e9 Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sat, 1 Feb 2025 18:42:01 +0330 Subject: [PATCH 06/37] tickt and message verbose name --- backend/ticket/models.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/backend/ticket/models.py b/backend/ticket/models.py index 15b03da..8fa5516 100644 --- a/backend/ticket/models.py +++ b/backend/ticket/models.py @@ -19,6 +19,12 @@ class Ticket(models.Model): def __str__(self): return self.subject + + class Meta: + verbose_name = 'تیکت' + verbose_name_plural = 'تیکت ها' + + class Message(models.Model): ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, related_name="messages") sender = models.ForeignKey(User, on_delete=models.CASCADE) @@ -26,4 +32,8 @@ class Message(models.Model): created_at = models.DateTimeField(auto_now_add=True) def __str__(self): - return f"Message by {self.sender.username} on {self.ticket.subject}" \ No newline at end of file + return f"Message by {self.sender.username} on {self.ticket.subject}" + + class Meta: + verbose_name = 'پیام تیکت' + verbose_name_plural = 'پیام های تیکت' \ No newline at end of file From 14655faf209b629ff083ba5f3d0db19dea6dfd40 Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sat, 1 Feb 2025 18:42:26 +0330 Subject: [PATCH 07/37] dynamic serializer comment --- backend/product/serializers.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/backend/product/serializers.py b/backend/product/serializers.py index 02beac6..513a7e4 100644 --- a/backend/product/serializers.py +++ b/backend/product/serializers.py @@ -28,6 +28,20 @@ class ProductSerializer(ProductChatSerializer): class Meta: model = ProductModel fields = "__all__" + # many_fields = ['name', 'price'] + # one_fields = ['description',] + # def __init__(self, *args, **kwargs): + + # many = kwargs.pop('many', True) + # super().__init__(*args, **kwargs) + + # allowed_fields = self.Meta.many_fields if many else self.Meta.one_fields + + # allowed = set(allowed_fields) + # existing = set(self.fields.keys()) + + # for field_name in existing - allowed: + # self.fields.pop(field_name) class CommentSerializer(serializers.ModelSerializer): class Meta: From 8b5ee6dbf8007a86b477208f932c16c0e1e3c780 Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sat, 1 Feb 2025 19:34:03 +0330 Subject: [PATCH 08/37] add color field and in stuck model --- .../migrations/0002_alter_user_options.py | 17 ++++++++++++ ...del_options_alter_blogmodel_cover_image.py | 22 +++++++++++++++ .../0002_alter_productchatmodel_options.py | 17 ++++++++++++ .../0002_alter_homeimagemodel_options.py | 17 ++++++++++++ backend/product/admin.py | 13 +++++++-- ...ons_alter_productmodel_options_and_more.py | 26 ++++++++++++++++++ .../product/migrations/0006_instuckcolors.py | 27 +++++++++++++++++++ ...7_remove_productmodel_in_stock_and_more.py | 22 +++++++++++++++ ...ctmodel_color_alter_instuckcolors_color.py | 22 +++++++++++++++ backend/product/models.py | 13 +++++++-- ...er_message_options_alter_ticket_options.py | 21 +++++++++++++++ 11 files changed, 213 insertions(+), 4 deletions(-) create mode 100644 backend/account/migrations/0002_alter_user_options.py create mode 100644 backend/blog/migrations/0002_alter_blogmodel_options_alter_blogmodel_cover_image.py create mode 100644 backend/chat/migrations/0002_alter_productchatmodel_options.py create mode 100644 backend/home/migrations/0002_alter_homeimagemodel_options.py create mode 100644 backend/product/migrations/0005_alter_dollormodel_options_alter_productmodel_options_and_more.py create mode 100644 backend/product/migrations/0006_instuckcolors.py create mode 100644 backend/product/migrations/0007_remove_productmodel_in_stock_and_more.py create mode 100644 backend/product/migrations/0008_remove_productmodel_color_alter_instuckcolors_color.py create mode 100644 backend/ticket/migrations/0002_alter_message_options_alter_ticket_options.py diff --git a/backend/account/migrations/0002_alter_user_options.py b/backend/account/migrations/0002_alter_user_options.py new file mode 100644 index 0000000..6b626d2 --- /dev/null +++ b/backend/account/migrations/0002_alter_user_options.py @@ -0,0 +1,17 @@ +# Generated by Django 5.1.2 on 2025-02-01 15:15 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='user', + options={'verbose_name': 'کاربر', 'verbose_name_plural': 'کاربران'}, + ), + ] diff --git a/backend/blog/migrations/0002_alter_blogmodel_options_alter_blogmodel_cover_image.py b/backend/blog/migrations/0002_alter_blogmodel_options_alter_blogmodel_cover_image.py new file mode 100644 index 0000000..f48f2e6 --- /dev/null +++ b/backend/blog/migrations/0002_alter_blogmodel_options_alter_blogmodel_cover_image.py @@ -0,0 +1,22 @@ +# Generated by Django 5.1.2 on 2025-02-01 15:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('blog', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='blogmodel', + options={'verbose_name': 'بلاگ', 'verbose_name_plural': 'بلاگ ها'}, + ), + migrations.AlterField( + model_name='blogmodel', + name='cover_image', + field=models.ImageField(blank=True, upload_to='blog_covers/'), + ), + ] diff --git a/backend/chat/migrations/0002_alter_productchatmodel_options.py b/backend/chat/migrations/0002_alter_productchatmodel_options.py new file mode 100644 index 0000000..0365d84 --- /dev/null +++ b/backend/chat/migrations/0002_alter_productchatmodel_options.py @@ -0,0 +1,17 @@ +# Generated by Django 5.1.2 on 2025-02-01 15:15 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('chat', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='productchatmodel', + options={'verbose_name': 'جت محصلول و کاربر', 'verbose_name_plural': 'چت های محصلول کاربر'}, + ), + ] diff --git a/backend/home/migrations/0002_alter_homeimagemodel_options.py b/backend/home/migrations/0002_alter_homeimagemodel_options.py new file mode 100644 index 0000000..dc156f4 --- /dev/null +++ b/backend/home/migrations/0002_alter_homeimagemodel_options.py @@ -0,0 +1,17 @@ +# Generated by Django 5.1.2 on 2025-02-01 15:15 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='homeimagemodel', + options={'verbose_name': 'مدل عکس تفاوت خانه', 'verbose_name_plural': 'مدل عکس تفاوت خانه'}, + ), + ] diff --git a/backend/product/admin.py b/backend/product/admin.py index 5514d08..075ed6b 100644 --- a/backend/product/admin.py +++ b/backend/product/admin.py @@ -1,18 +1,27 @@ from django.contrib import admin from .models import * -from unfold.admin import ModelAdmin +from unfold.admin import ModelAdmin, TabularInline from import_export.admin import ImportExportModelAdmin from unfold.contrib.import_export.forms import ExportForm, ImportForm, SelectableFieldsExportForm from unfold.contrib.forms.widgets import ArrayWidget, WysiwygWidget from django.contrib.postgres.fields import ArrayField +from unfold.widgets import ( + UnfoldAdminColorInputWidget, +) +class InStuckColorsInLine(TabularInline): + model = InStuckColors + extra = 1 + formfield_overrides = { + models.CharField: {"widget": UnfoldAdminColorInputWidget()}, + } @admin.register(ProductModel) class ProductModelAdmin(ModelAdmin, ImportExportModelAdmin): import_form_class = ImportForm export_form_class = ExportForm - + inlines = [InStuckColorsInLine] readonly_fields = ('slug', ) compressed_fields = True diff --git a/backend/product/migrations/0005_alter_dollormodel_options_alter_productmodel_options_and_more.py b/backend/product/migrations/0005_alter_dollormodel_options_alter_productmodel_options_and_more.py new file mode 100644 index 0000000..f1824a4 --- /dev/null +++ b/backend/product/migrations/0005_alter_dollormodel_options_alter_productmodel_options_and_more.py @@ -0,0 +1,26 @@ +# Generated by Django 5.1.2 on 2025-02-01 15:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0004_alter_subcategorymodel_parent'), + ] + + operations = [ + migrations.AlterModelOptions( + name='dollormodel', + options={'verbose_name': 'مدل دلار', 'verbose_name_plural': 'مدل دلار'}, + ), + migrations.AlterModelOptions( + name='productmodel', + options={'verbose_name': 'محصول', 'verbose_name_plural': 'محصولات'}, + ), + migrations.AddField( + model_name='productmodel', + name='color', + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='color'), + ), + ] diff --git a/backend/product/migrations/0006_instuckcolors.py b/backend/product/migrations/0006_instuckcolors.py new file mode 100644 index 0000000..ea641b8 --- /dev/null +++ b/backend/product/migrations/0006_instuckcolors.py @@ -0,0 +1,27 @@ +# Generated by Django 5.1.2 on 2025-02-01 15:32 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0005_alter_dollormodel_options_alter_productmodel_options_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='InStuckColors', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('color', models.CharField(blank=True, max_length=255, null=True, verbose_name='color')), + ('in_stuck', models.PositiveIntegerField(default=0)), + ('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='colors', to='product.productmodel')), + ], + options={ + 'verbose_name': 'تعداد موجود رنگ', + 'verbose_name_plural': 'تعداد موجود رنگ ها', + }, + ), + ] diff --git a/backend/product/migrations/0007_remove_productmodel_in_stock_and_more.py b/backend/product/migrations/0007_remove_productmodel_in_stock_and_more.py new file mode 100644 index 0000000..ea500ea --- /dev/null +++ b/backend/product/migrations/0007_remove_productmodel_in_stock_and_more.py @@ -0,0 +1,22 @@ +# Generated by Django 5.1.2 on 2025-02-01 15:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0006_instuckcolors'), + ] + + operations = [ + migrations.RemoveField( + model_name='productmodel', + name='in_stock', + ), + migrations.AlterField( + model_name='instuckcolors', + name='in_stuck', + field=models.PositiveIntegerField(default=0, verbose_name='تعداد موجود'), + ), + ] diff --git a/backend/product/migrations/0008_remove_productmodel_color_alter_instuckcolors_color.py b/backend/product/migrations/0008_remove_productmodel_color_alter_instuckcolors_color.py new file mode 100644 index 0000000..5aa5612 --- /dev/null +++ b/backend/product/migrations/0008_remove_productmodel_color_alter_instuckcolors_color.py @@ -0,0 +1,22 @@ +# Generated by Django 5.1.2 on 2025-02-01 15:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0007_remove_productmodel_in_stock_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='productmodel', + name='color', + ), + migrations.AlterField( + model_name='instuckcolors', + name='color', + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='رنگ'), + ), + ] diff --git a/backend/product/models.py b/backend/product/models.py index 1863ca4..7b61c2d 100644 --- a/backend/product/models.py +++ b/backend/product/models.py @@ -3,7 +3,7 @@ from django.utils.text import slugify from account.models import User from django.urls import reverse import requests - +from django.utils.translation import gettext_lazy as _ class MainCategoryModel(models.Model): name = models.CharField(max_length=50, verbose_name='نام') @@ -103,7 +103,6 @@ class ProductModel(models.Model): show = models.BooleanField(default=False, verbose_name='نمایش در خانه') view = models.IntegerField(default=0, verbose_name='بازدید') sell = models.IntegerField(default=0, verbose_name='فروش') - in_stock = models.IntegerField(default=0, verbose_name="تعداد موجود") discount = models.SmallIntegerField(default=0, verbose_name='تخفیف') slug = models.SlugField(max_length=50, unique=True, blank=True, null=True, allow_unicode=True, verbose_name='نام یکتا', help_text="این فیلد را خالی بگذارید") @@ -149,6 +148,16 @@ class ProductModel(models.Model): verbose_name_plural = 'محصولات' +class InStuckColors(models.Model): + color = models.CharField(_("رنگ"), null=True, blank=True, max_length=255) + in_stuck = models.PositiveIntegerField(default=0, verbose_name="تعداد موجود") + product = models.ForeignKey(ProductModel, on_delete=models.CASCADE, related_name='colors') + class Meta: + verbose_name = 'تعداد موجود رنگ' + verbose_name_plural = 'تعداد موجود رنگ ها' + def __str__(self): + return f'{product} - {color}' + class CommentModel(models.Model): product = models.ForeignKey(ProductModel, on_delete=models.CASCADE, related_name='comments', verbose_name='محصول') content = models.TextField(verbose_name='محتوای نظر') diff --git a/backend/ticket/migrations/0002_alter_message_options_alter_ticket_options.py b/backend/ticket/migrations/0002_alter_message_options_alter_ticket_options.py new file mode 100644 index 0000000..685edfb --- /dev/null +++ b/backend/ticket/migrations/0002_alter_message_options_alter_ticket_options.py @@ -0,0 +1,21 @@ +# Generated by Django 5.1.2 on 2025-02-01 15:15 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('ticket', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='message', + options={'verbose_name': 'پیام تیکت', 'verbose_name_plural': 'پیام های تیکت'}, + ), + migrations.AlterModelOptions( + name='ticket', + options={'verbose_name': 'تیکت', 'verbose_name_plural': 'تیکت ها'}, + ), + ] From bc1cdc1abdc3e71d15c9d6f97a938f08434a0c91 Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sat, 1 Feb 2025 21:00:55 +0330 Subject: [PATCH 09/37] user search field --- backend/account/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/account/admin.py b/backend/account/admin.py index f9589ec..0b015fb 100644 --- a/backend/account/admin.py +++ b/backend/account/admin.py @@ -15,7 +15,7 @@ class UserAdmin(BaseUserAdmin, ModelAdmin, ImportExportModelAdmin): filter_horizontal = [] ordering = [] list_filter = [] - + search_fields = ['phone', 'first_name', 'last_name', ] list_display = ['phone', 'email', 'is_superuser'] readonly_fields = [] From ae5d16789457def3d94298a859a04c14755004fa Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sat, 1 Feb 2025 21:50:31 +0330 Subject: [PATCH 10/37] realted product --- backend/product/admin.py | 3 ++- .../0009_productmodel_related_products.py | 18 ++++++++++++++++++ backend/product/models.py | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 backend/product/migrations/0009_productmodel_related_products.py diff --git a/backend/product/admin.py b/backend/product/admin.py index 075ed6b..188ebad 100644 --- a/backend/product/admin.py +++ b/backend/product/admin.py @@ -23,7 +23,8 @@ class ProductModelAdmin(ModelAdmin, ImportExportModelAdmin): export_form_class = ExportForm inlines = [InStuckColorsInLine] readonly_fields = ('slug', ) - + search_fields = ['name'] + autocomplete_fields = ['related_products'] compressed_fields = True warn_unsaved_form = True diff --git a/backend/product/migrations/0009_productmodel_related_products.py b/backend/product/migrations/0009_productmodel_related_products.py new file mode 100644 index 0000000..eaf63a4 --- /dev/null +++ b/backend/product/migrations/0009_productmodel_related_products.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.2 on 2025-02-01 16:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0008_remove_productmodel_color_alter_instuckcolors_color'), + ] + + operations = [ + migrations.AddField( + model_name='productmodel', + name='related_products', + field=models.ManyToManyField(to='product.productmodel'), + ), + ] diff --git a/backend/product/models.py b/backend/product/models.py index 7b61c2d..5d62e7c 100644 --- a/backend/product/models.py +++ b/backend/product/models.py @@ -111,7 +111,7 @@ class ProductModel(models.Model): meta_rating = models.FloatField(default=5, help_text='امتیاز محصول') created_at = models.DateTimeField(auto_now_add=True, verbose_name='زمان ثبت محصول') category = models.ForeignKey(SubCategoryModel, blank=True, null=True, on_delete=models.SET_NULL, related_name='products', verbose_name='دسته بندی محصول') - + related_products = models.ManyToManyField('self',) def format_discount_price(self): discount_price = int(self.price * (100 - self.discount) / 100) formatted_num = "{:,.0f}".format(discount_price) From 40cc21f241bc3d7c80d95221d993d538ef4a2cc0 Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sat, 1 Feb 2025 21:51:56 +0330 Subject: [PATCH 11/37] admin pannel update --- backend/core/settings.py | 6 ++++++ backend/product/admin.py | 21 +++++++++++++++++++-- backend/utils/admin.py | 5 +++++ 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 backend/utils/admin.py diff --git a/backend/core/settings.py b/backend/core/settings.py index f9e4201..5b02546 100644 --- a/backend/core/settings.py +++ b/backend/core/settings.py @@ -287,6 +287,12 @@ UNFOLD = { "icon": "home", "link": reverse_lazy("admin:home_slidermodel_changelist"), }, + { + "title": _("سفارشات"), + "icon": "shopping_cart", + "link": reverse_lazy("admin:order_ordermodel_changelist"), + "badge": "utils.admin.admin_pending_count", + }, ], }, diff --git a/backend/product/admin.py b/backend/product/admin.py index 188ebad..123a237 100644 --- a/backend/product/admin.py +++ b/backend/product/admin.py @@ -9,7 +9,7 @@ from django.contrib.postgres.fields import ArrayField from unfold.widgets import ( UnfoldAdminColorInputWidget, ) - +from unfold.decorators import action, display class InStuckColorsInLine(TabularInline): model = InStuckColors extra = 1 @@ -27,7 +27,7 @@ class ProductModelAdmin(ModelAdmin, ImportExportModelAdmin): autocomplete_fields = ['related_products'] compressed_fields = True warn_unsaved_form = True - + list_display = ['display_image', 'price',] formfield_overrides = { models.TextField: { "widget": WysiwygWidget, @@ -36,6 +36,23 @@ class ProductModelAdmin(ModelAdmin, ImportExportModelAdmin): "widget": ArrayWidget, } } + @display(description='محصول', header=True) + def display_image(self, instance): + if instance.image1: + return [ + instance.name, + None, + None, + { + "path": instance.image1.url, + "height": 24, + "width": 24, + "borderless": True, + # "squared": True, + }, + ] + return () + @admin.register(MainCategoryModel) class MainCategoryModelAdmin(ModelAdmin, ImportExportModelAdmin): diff --git a/backend/utils/admin.py b/backend/utils/admin.py new file mode 100644 index 0000000..01b2dc4 --- /dev/null +++ b/backend/utils/admin.py @@ -0,0 +1,5 @@ +from order.models import OrderModel + +def admin_pending_count(request): + pending_count = OrderModel.objects.filter(status='ADMIN_PENDING').count() + return str(pending_count) \ No newline at end of file From b0b0ca54625b24e5033adb2d6772cdbdca491007 Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sat, 1 Feb 2025 21:56:06 +0330 Subject: [PATCH 12/37] test piture size product update --- backend/product/admin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/product/admin.py b/backend/product/admin.py index 123a237..ff1eac8 100644 --- a/backend/product/admin.py +++ b/backend/product/admin.py @@ -45,10 +45,10 @@ class ProductModelAdmin(ModelAdmin, ImportExportModelAdmin): None, { "path": instance.image1.url, - "height": 24, - "width": 24, + "height": 26, + "width": 26, "borderless": True, - # "squared": True, + "squared": True, }, ] return () From 94ac22ed07b09ac11a0f94570841c6bf1c66ec62 Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sat, 1 Feb 2025 21:57:50 +0330 Subject: [PATCH 13/37] update size --- backend/product/admin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/product/admin.py b/backend/product/admin.py index ff1eac8..a1d4afb 100644 --- a/backend/product/admin.py +++ b/backend/product/admin.py @@ -45,10 +45,10 @@ class ProductModelAdmin(ModelAdmin, ImportExportModelAdmin): None, { "path": instance.image1.url, - "height": 26, - "width": 26, + "height": 30, + "width": 30, "borderless": True, - "squared": True, + # "squared": True, }, ] return () From 02d3a42ee0e7d7074182741ce935ccf262b658c0 Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sun, 2 Feb 2025 03:13:19 +0330 Subject: [PATCH 14/37] show dollor price in admin --- backend/utils/admin.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/backend/utils/admin.py b/backend/utils/admin.py index 01b2dc4..f4dc1be 100644 --- a/backend/utils/admin.py +++ b/backend/utils/admin.py @@ -1,5 +1,9 @@ from order.models import OrderModel - +from product.models import DollorModel def admin_pending_count(request): pending_count = OrderModel.objects.filter(status='ADMIN_PENDING').count() - return str(pending_count) \ No newline at end of file + return str(pending_count) + +def dollor_price(request): + dollor_object, _ = DollorModel.objects.get_or_create(unique_filed='unique') + return str(dollor_object.price)[:2] From aa2b15a22302dcb658e58f193f5414275c851a2a Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sun, 2 Feb 2025 03:13:30 +0330 Subject: [PATCH 15/37] update site title and header --- backend/.env.local | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/.env.local b/backend/.env.local index dadcbeb..e8affc4 100644 --- a/backend/.env.local +++ b/backend/.env.local @@ -20,8 +20,8 @@ TELEGRAM_BOT_TOKEN = '' DOMAIN = 'heymlz.com' # domain for api (the domain that django will use) API_DOMAIN = 'api.heymlz.com' -SITE_TITLE = '' -SITE_HEADER = '' +SITE_TITLE = 'Heymlz Shop' +SITE_HEADER = 'Heymlz Shop' # jwt token configs ACCESS_TOKEN_LIFETIME = 5000 REFRESH_TOKEN_LIFETIME = 5000 From 94de307d56924032ae7b2cdcc4591589c851c2ae Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sun, 2 Feb 2025 03:13:46 +0330 Subject: [PATCH 16/37] address mdoel admin --- backend/account/admin.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/backend/account/admin.py b/backend/account/admin.py index 0b015fb..aad9be0 100644 --- a/backend/account/admin.py +++ b/backend/account/admin.py @@ -51,4 +51,23 @@ from django.contrib import admin from rest_framework_simplejwt.token_blacklist.models import BlacklistedToken, OutstandingToken # Unregister the BlacklistedToken and OutstandingToken models admin.site.unregister(BlacklistedToken) -admin.site.unregister(OutstandingToken) \ No newline at end of file +admin.site.unregister(OutstandingToken) + + +@admin.register(UserAddressModel) +class AddressAdmin(ModelAdmin, ImportExportModelAdmin): + import_form_class = ImportForm + export_form_class = ExportForm + + + compressed_fields = True + warn_unsaved_form = True + + formfield_overrides = { + models.TextField: { + "widget": WysiwygWidget, + }, + ArrayField: { + "widget": ArrayWidget, + } + } \ No newline at end of file From abddb1bdc1f291d93b2a5764f8bcff1ec531ee83 Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sun, 2 Feb 2025 03:16:12 +0330 Subject: [PATCH 17/37] product dynanmic serializer --- backend/chat/models.py | 4 +-- backend/home/views.py | 4 +-- backend/product/serializers.py | 63 ++++++++++++++++++++++++---------- backend/product/views.py | 10 +++--- backend/utils/codes_to_test.py | 14 ++++++++ 5 files changed, 67 insertions(+), 28 deletions(-) create mode 100644 backend/utils/codes_to_test.py diff --git a/backend/chat/models.py b/backend/chat/models.py index ebd806d..f3882f4 100644 --- a/backend/chat/models.py +++ b/backend/chat/models.py @@ -4,7 +4,7 @@ from product.models import ProductModel, DollorModel from django.conf import settings import openai from time import sleep -from product.serializers import ProductChatSerializer +from product.serializers import DynamicProductSerializer ASSISTANT_ID = 'asst_1wOnCKncEHkOfp0FjOIz4Xkp' @@ -26,7 +26,7 @@ class ProductChatModel(models.Model): client = openai.OpenAI(api_key=settings.OPENAI_API_KEY) dollor_object, _ = DollorModel.objects.get_or_create(unique_filed='unique') dollor_price = dollor_object.price - product_json = ProductChatSerializer(instance=self.product, context={'dollor_price': dollor_price}).data + product_json = DynamicProductSerializer(instance=self.product, context={'dollor_price': dollor_price, 'view_type': 'chat'}).data try: thread = client.beta.threads.create( diff --git a/backend/home/views.py b/backend/home/views.py index 8d78300..226b824 100644 --- a/backend/home/views.py +++ b/backend/home/views.py @@ -1,7 +1,7 @@ from django.shortcuts import render from rest_framework.views import APIView, Response from product.models import ProductModel, SubCategoryModel, DollorModel -from product.serializers import SubCategorySerializer, ProductSerializer +from product.serializers import SubCategorySerializer, DynamicProductSerializer from .serializers import SliderSerializer, HomeImageSerializer from .models import SliderModel, HomeImageModel from rest_framework import status @@ -21,7 +21,7 @@ class HomeView(APIView): sub_category_ser = SubCategorySerializer(instance=sub_categories, many=True, context={'request': request}) products_to_show = ProductModel.objects.filter(show=True) - product_ser = ProductSerializer(instance=products_to_show, many=True, context={'request': request, 'dollor_price': dollor_price}) + product_ser = DynamicProductSerializer(instance=products_to_show, many=True, context={'request': request, 'dollor_price': dollor_price, 'view_type': 'list'}) home_image = HomeImageModel.objects.all().first() home_image_ser = HomeImageSerializer(instance=home_image, context={'request': request}) diff --git a/backend/product/serializers.py b/backend/product/serializers.py index 513a7e4..a4d4f16 100644 --- a/backend/product/serializers.py +++ b/backend/product/serializers.py @@ -3,12 +3,39 @@ from rest_framework import serializers from django.utils import timezone from datetime import timedelta -class ProductChatSerializer(serializers.ModelSerializer): + +class InStuckColorsSerializer(serializers.ModelSerializer): + class Meta: + model = InStuckColors + fields = ['color', 'in_stuck'] + + +class DynamicProductSerializer(serializers.ModelSerializer): + colors = InStuckColorsSerializer(many=True, read_only=True) price = serializers.SerializerMethodField() is_new = serializers.SerializerMethodField() + related_products = serializers.SerializerMethodField() + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + view_type = self.context.get('view_type', 'all') + if view_type != 'all': + allowed_fields = self.Meta.view_type[view_type] + allowed = set(allowed_fields) + existing = set(self.fields.keys()) + + for field_name in existing - allowed: + self.fields.pop(field_name) + + class Meta: model = ProductModel - fields = ['name', 'description', 'price', 'in_stock', 'discount', 'is_new'] + fields = "__all__" + view_type = { + 'list': ['name', 'price', 'image1', 'video', 'rating', 'discount', 'slug', 'category', 'colors'], + 'instance': ['name', 'description', 'price', 'image1', 'image2', 'image3', 'video', 'rating', 'discount', 'slug', 'meta_description', 'meta_keywords', 'meta_rating', 'category', 'colors', 'related_products'], + 'chat': ['name', 'description', 'price', 'in_stock', 'discount', 'colors'] + } + def get_price(self, obj): dollor_price = self.context.get('dollor_price') dollar_to_dirham = 0.27 @@ -21,27 +48,25 @@ class ProductChatSerializer(serializers.ModelSerializer): elif obj.currency == 'derham': toman_price = obj.price * dollor_price * dollar_to_dirham return "{:,.0f} تومان".format(toman_price) + def get_is_new(self, obj): return timezone.now() < obj.created_at + timedelta(days=7) -class ProductSerializer(ProductChatSerializer): - class Meta: - model = ProductModel - fields = "__all__" - # many_fields = ['name', 'price'] - # one_fields = ['description',] - # def __init__(self, *args, **kwargs): + def get_related_products(self, obj): + if obj.related_products.all().count() >= 5: + related_products = obj.related_products.all() + else: + related_products = obj.category.products + serializer = DynamicProductSerializer( + related_products, + many=True, + context={ + 'view_type': 'list', + 'dollor_price': self.context.get('dollor_price') + } + ) + return serializer.data - # many = kwargs.pop('many', True) - # super().__init__(*args, **kwargs) - - # allowed_fields = self.Meta.many_fields if many else self.Meta.one_fields - - # allowed = set(allowed_fields) - # existing = set(self.fields.keys()) - - # for field_name in existing - allowed: - # self.fields.pop(field_name) class CommentSerializer(serializers.ModelSerializer): class Meta: diff --git a/backend/product/views.py b/backend/product/views.py index e0e8f2b..e06c716 100644 --- a/backend/product/views.py +++ b/backend/product/views.py @@ -49,19 +49,19 @@ class AllCategories(APIView): return Response(categories_ser.data, status=status.HTTP_200_OK) class ProductView(APIView): - serializer_class = ProductSerializer + serializer_class = DynamicProductSerializer permission_classes = [AllowAny] authentication_classes = [] def get(self, request, pk): product = get_object_or_404(ProductModel, id=pk) dollor_object, _ = DollorModel.objects.get_or_create(unique_filed='unique') dollor_price = dollor_object.price - product_ser = self.serializer_class(instance=product, many=False, context={'dollor_price': dollor_price, 'request': request}) + product_ser = self.serializer_class(instance=product, many=False, context={'dollor_price': dollor_price, 'request': request, 'view_type': 'instance'}) return Response(product_ser.data, status=status.HTTP_200_OK) class AllProductsView(APIView): - serializer_class = ProductSerializer + serializer_class = DynamicProductSerializer pagination_class = StructurePagination authentication_classes = [] @extend_schema( @@ -137,7 +137,7 @@ class AllProductsView(APIView): "Provide a list of category IDs to filter products by those categories and their subcategories." ), responses={ - 200: ProductSerializer(many=True), + 200: DynamicProductSerializer(many=True, context={'view_type': 'list'}), 404: OpenApiTypes.OBJECT, }, ) @@ -191,7 +191,7 @@ class AllProductsView(APIView): paginated_products = paginator.paginate_queryset(products, request) dollor_object, _ = DollorModel.objects.get_or_create(unique_filed='unique') dollor_price = dollor_object.price - serializer = self.serializer_class(paginated_products, many=True, context={'dollor_price': dollor_price, 'request': request}) + serializer = self.serializer_class(paginated_products, many=True, context={'dollor_price': dollor_price, 'request': request, 'view_type': 'list'}) return paginator.get_paginated_response(serializer.data) except MainCategoryModel.DoesNotExist: diff --git a/backend/utils/codes_to_test.py b/backend/utils/codes_to_test.py new file mode 100644 index 0000000..a083f65 --- /dev/null +++ b/backend/utils/codes_to_test.py @@ -0,0 +1,14 @@ +# from unfold.widgets import UnfoldAdminCheckboxSelectMultiple +# from django import forms +# class DriverAdminForm(forms.ModelForm): +# flags = forms.MultipleChoiceField( +# label=("Flags"), +# choices=[ +# ("POPULAR", ("Popular")), +# ("FASTEST", ("Fastest")), +# ("TALENTED", ("Talented")), +# ], +# required=False, +# widget=UnfoldAdminCheckboxSelectMultiple, +# ) +# form = DriverAdminForm \ No newline at end of file From 7474bd70348d9e847c88235b91fb507f40e6ac56 Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sun, 2 Feb 2025 03:16:31 +0330 Subject: [PATCH 18/37] InStuckColorsInLine extra update --- backend/product/admin.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/backend/product/admin.py b/backend/product/admin.py index a1d4afb..9dd429f 100644 --- a/backend/product/admin.py +++ b/backend/product/admin.py @@ -12,7 +12,7 @@ from unfold.widgets import ( from unfold.decorators import action, display class InStuckColorsInLine(TabularInline): model = InStuckColors - extra = 1 + extra = 0 formfield_overrides = { models.CharField: {"widget": UnfoldAdminColorInputWidget()}, } @@ -51,7 +51,16 @@ class ProductModelAdmin(ModelAdmin, ImportExportModelAdmin): # "squared": True, }, ] - return () + return ('خالی',) + # @display( + # description=("نمایش در صفحه ی اصلی"), + # label={ + # True: "danger", + # False: "success", + # }, + # ) + # def display_show(self, instance): + # return instance.show @admin.register(MainCategoryModel) From e8051630484ddbb05385bd399acc9cd30f818497 Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sun, 2 Feb 2025 03:19:07 +0330 Subject: [PATCH 19/37] ticket model update --- backend/ticket/admin.py | 1 + .../migrations/0003_alter_ticket_status.py | 18 ++++++++++++++++++ backend/ticket/models.py | 4 ++-- 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 backend/ticket/migrations/0003_alter_ticket_status.py diff --git a/backend/ticket/admin.py b/backend/ticket/admin.py index 77e9db3..e1bcea0 100644 --- a/backend/ticket/admin.py +++ b/backend/ticket/admin.py @@ -26,6 +26,7 @@ class TicketAdmin(ModelAdmin, ImportExportModelAdmin): } } inlines = [MessageInline] + radio_fields = {'status': admin.VERTICAL} @admin.register(Message) class MessageAdmin(ModelAdmin, ImportExportModelAdmin): diff --git a/backend/ticket/migrations/0003_alter_ticket_status.py b/backend/ticket/migrations/0003_alter_ticket_status.py new file mode 100644 index 0000000..e643d6a --- /dev/null +++ b/backend/ticket/migrations/0003_alter_ticket_status.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.2 on 2025-02-01 23:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ticket', '0002_alter_message_options_alter_ticket_options'), + ] + + operations = [ + migrations.AlterField( + model_name='ticket', + name='status', + field=models.CharField(choices=[('open', 'باز'), ('in_progress', 'در حال پردازش'), ('resolved', 'حل شده'), ('closed', 'بسته')], default='open', max_length=20), + ), + ] diff --git a/backend/ticket/models.py b/backend/ticket/models.py index 8fa5516..fd63dc0 100644 --- a/backend/ticket/models.py +++ b/backend/ticket/models.py @@ -3,10 +3,10 @@ from account.models import User class Ticket(models.Model): STATUS_CHOICES = [ - ('open', 'یاز'), + ('open', 'باز'), ('in_progress', 'در حال پردازش'), ('resolved', 'حل شده'), - ('closed', 'باز'), + ('closed', 'بسته'), ] subject = models.CharField(max_length=255) From 83b265ad383014e991d8257d0a804e7f93ff41f8 Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sun, 2 Feb 2025 03:19:19 +0330 Subject: [PATCH 20/37] product model update --- ...010_alter_productmodel_related_products.py | 18 ++++++++++++++ ...11_alter_productmodel_category_and_more.py | 24 +++++++++++++++++++ backend/product/models.py | 6 ++--- 3 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 backend/product/migrations/0010_alter_productmodel_related_products.py create mode 100644 backend/product/migrations/0011_alter_productmodel_category_and_more.py diff --git a/backend/product/migrations/0010_alter_productmodel_related_products.py b/backend/product/migrations/0010_alter_productmodel_related_products.py new file mode 100644 index 0000000..a5abc36 --- /dev/null +++ b/backend/product/migrations/0010_alter_productmodel_related_products.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.2 on 2025-02-01 18:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0009_productmodel_related_products'), + ] + + operations = [ + migrations.AlterField( + model_name='productmodel', + name='related_products', + field=models.ManyToManyField(blank=True, null=True, to='product.productmodel'), + ), + ] diff --git a/backend/product/migrations/0011_alter_productmodel_category_and_more.py b/backend/product/migrations/0011_alter_productmodel_category_and_more.py new file mode 100644 index 0000000..af64444 --- /dev/null +++ b/backend/product/migrations/0011_alter_productmodel_category_and_more.py @@ -0,0 +1,24 @@ +# Generated by Django 5.1.2 on 2025-02-01 23:47 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0010_alter_productmodel_related_products'), + ] + + operations = [ + migrations.AlterField( + model_name='productmodel', + name='category', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='products', to='product.subcategorymodel', verbose_name='دسته بندی محصول'), + ), + migrations.AlterField( + model_name='productmodel', + name='related_products', + field=models.ManyToManyField(blank=True, to='product.productmodel'), + ), + ] diff --git a/backend/product/models.py b/backend/product/models.py index 5d62e7c..7381f6e 100644 --- a/backend/product/models.py +++ b/backend/product/models.py @@ -110,8 +110,8 @@ class ProductModel(models.Model): meta_keywords = models.CharField(max_length=300, blank=True, null=True, help_text='این فیلد را حتما پر کنید') meta_rating = models.FloatField(default=5, help_text='امتیاز محصول') created_at = models.DateTimeField(auto_now_add=True, verbose_name='زمان ثبت محصول') - category = models.ForeignKey(SubCategoryModel, blank=True, null=True, on_delete=models.SET_NULL, related_name='products', verbose_name='دسته بندی محصول') - related_products = models.ManyToManyField('self',) + category = models.ForeignKey(SubCategoryModel, null=True, on_delete=models.SET_NULL, related_name='products', verbose_name='دسته بندی محصول') + related_products = models.ManyToManyField('self', blank=True,) def format_discount_price(self): discount_price = int(self.price * (100 - self.discount) / 100) formatted_num = "{:,.0f}".format(discount_price) @@ -156,7 +156,7 @@ class InStuckColors(models.Model): verbose_name = 'تعداد موجود رنگ' verbose_name_plural = 'تعداد موجود رنگ ها' def __str__(self): - return f'{product} - {color}' + return f'{self.product} - {self.color}' class CommentModel(models.Model): product = models.ForeignKey(ProductModel, on_delete=models.CASCADE, related_name='comments', verbose_name='محصول') From 079e3ed7b7fcda7fc7d354445a6ffa43b76abb8c Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sun, 2 Feb 2025 03:19:33 +0330 Subject: [PATCH 21/37] admin pannel update --- backend/core/settings.py | 149 +++++++++++++++++++++++++++------------ 1 file changed, 102 insertions(+), 47 deletions(-) diff --git a/backend/core/settings.py b/backend/core/settings.py index 5b02546..97c840d 100644 --- a/backend/core/settings.py +++ b/backend/core/settings.py @@ -178,6 +178,7 @@ STATIC_ROOT = '/app/static' STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'custom_static'), + # BASE_DIR / "core" / "static" ] STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage" @@ -233,31 +234,50 @@ UNFOLD = { "href": lambda request: static("favicon.svg"), }, ], + # "LOGIN": { + # "image": lambda request: static("robot.png"), + # }, + + + "BORDER_RADIUS": "15px", "SHOW_HISTORY": True, "SHOW_VIEW_ON_SITE": True, "ENVIRONMENT": "core.settings.environment_callback", - "COLORS": { +"COLORS": { + "base": { + "50": "249 250 251", + "100": "243 244 246", + "200": "229 231 235", + "300": "209 213 219", + "400": "156 163 175", + "500": "107 114 128", + "600": "75 85 99", + "700": "55 65 81", + "800": "31 41 55", + "900": "17 24 39", + "950": "3 7 18" + }, + "primary": { + "50": "255 241 242", + "100": "255 228 230", + "200": "254 205 211", + "300": "253 164 175", + "400": "251 113 133", + "500": "244 63 94", + "600": "225 29 72", + "700": "190 18 60", + "800": "159 18 57", + "900": "136 19 55", + "950": "76 5 25" + }, "font": { - "subtle-light": "107 114 128", - "subtle-dark": "156 163 175", - "default-light": "75 85 99", - "default-dark": "209 213 219", - "important-light": "17 24 39", - "important-dark": "243 244 246", - }, - "primary": { - "50": "245 250 255", - "100": "230 243 254", - "200": "180 218 253", - "300": "131 193 252", - "400": "81 168 251", - "500": "31 144 249", - "600": "6 118 224", - "700": "4 92 174", - "800": "3 66 124", - "900": "2 39 75", - "950": "1 13 25" + "subtle-light": "var(--color-base-500)", # text-base-500 + "subtle-dark": "var(--color-base-400)", # text-base-400 + "default-light": "var(--color-base-600)", # text-base-600 + "default-dark": "var(--color-base-300)", # text-base-300 + "important-light": "var(--color-base-900)", # text-base-900 + "important-dark": "var(--color-base-100)", # text-base-100 }, }, "EXTENSIONS": { @@ -269,7 +289,7 @@ UNFOLD = { }, "SIDEBAR": { - "show_search": True, + "show_search": False, "show_all_applications": False, "navigation": [ { @@ -282,11 +302,6 @@ UNFOLD = { "icon": "dashboard", "link": reverse_lazy("admin:index"), }, - { - "title": _("اسلایدر"), - "icon": "home", - "link": reverse_lazy("admin:home_slidermodel_changelist"), - }, { "title": _("سفارشات"), "icon": "shopping_cart", @@ -299,9 +314,9 @@ UNFOLD = { { - "title": _("پنل فروش محصولات وبسایت"), + "title": _("Shop Products"), "separator": True, - "collapsible": True, + "collapsible": False, "items": [ { "title": _("محصولات"), @@ -309,7 +324,27 @@ UNFOLD = { "link": reverse_lazy("admin:product_productmodel_changelist"), }, - # esm category model ro lower case bezar inja amir + { + "title": _("نظرات"), + "icon": "chat", + "link": reverse_lazy("admin:product_commentmodel_changelist"), + }, + { + "title": _("قیمت دلار"), + "icon": "payments", + "link": reverse_lazy("admin:product_dollormodel_changelist"), + "badge": "utils.admin.dollor_price", + }, + + ], + }, + + + { + "title": _("Categories section"), + "separator": True, + "collapsible": False, + "items": [ { "title": _("دسته بندی"), @@ -320,23 +355,38 @@ UNFOLD = { "title": _("زیر دسته بندی"), "icon": "category", "link": reverse_lazy("admin:product_subcategorymodel_changelist"), + } + + ], + }, + { + "title": _("Visual Sections "), + "separator": True, + "collapsible": True, + "items": [ + + { + "title": _("اسلاید ها"), + "icon": "slide_library", + "link": reverse_lazy("admin:home_slidermodel_changelist"), }, { - "title": _("نظرات"), - "icon": "chat", - "link": reverse_lazy("admin:product_commentmodel_changelist"), - }, + "title": _("عکس مقایسه"), + "icon": "compare", + "link": reverse_lazy("admin:home_homeimagemodel_changelist"), + } + , { - "title": _("قیمت دلار"), - "icon": "payments", - "link": reverse_lazy("admin:product_dollormodel_changelist"), - }, + "title": _("مقالات و بلاگ ها"), + "icon": "newsmode", + "link": reverse_lazy("admin:blog_blogmodel_changelist"), + } ], }, { - "title": _("بخش کاربران و مشتریان"), + "title": _("Users and Customers"), "separator": True, "collapsible": True, "items": [ @@ -345,30 +395,35 @@ UNFOLD = { "title": _("کاربران"), "icon": "person", "link": reverse_lazy("admin:account_user_changelist"), + },{ + "title": _("چت محصول"), + "icon": "chat", + "link": reverse_lazy("admin:chat_productchatmodel_changelist"), + }, + { + "title": _("ادرس ها"), + "icon": "contact_mail", + "link": reverse_lazy("admin:account_useraddressmodel_changelist"), }, ], }, { - "title": _("بخش هوش مصنوعی"), + "title": _("Ticket and Support"), "separator": True, "collapsible": True, "items": [ { - "title": _("چت محصول"), - "icon": "chat", - "link": reverse_lazy("admin:chat_productchatmodel_changelist"), + "title": _("تیکت"), + "icon": "confirmation_number", + "link": reverse_lazy("admin:ticket_ticket_changelist"), }, ], }, - - - - ], }, @@ -376,7 +431,7 @@ UNFOLD = { AUTH_USER_MODEL = 'account.User' def environment_callback(request): - return ["Development", "warning"] + return ["Development", "danger"] def badge_callback(request): From 6954e3b9dacfeff55308d4ca410cce4a9ad307a6 Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sun, 2 Feb 2025 03:20:14 +0330 Subject: [PATCH 22/37] new version of django unfold --- backend/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index 5b64054..37e397b 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -19,7 +19,7 @@ django-dbbackup==4.2.1 django-filter==24.3 django-import-export==4.1.1 django-iranian-cities==1.0.2 -django-unfold==0.39.0 +django-unfold==0.46.0 djangorestframework==3.15.2 djangorestframework-simplejwt==5.3.1 djoser==2.3.1 From 52ad5614be63799db32c14826023669a76288d2c Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sun, 2 Feb 2025 17:04:58 +0330 Subject: [PATCH 23/37] admin product update --- backend/product/admin.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/backend/product/admin.py b/backend/product/admin.py index 9dd429f..f7c1832 100644 --- a/backend/product/admin.py +++ b/backend/product/admin.py @@ -13,6 +13,7 @@ from unfold.decorators import action, display class InStuckColorsInLine(TabularInline): model = InStuckColors extra = 0 + tab = True formfield_overrides = { models.CharField: {"widget": UnfoldAdminColorInputWidget()}, } @@ -25,9 +26,15 @@ class ProductModelAdmin(ModelAdmin, ImportExportModelAdmin): readonly_fields = ('slug', ) search_fields = ['name'] autocomplete_fields = ['related_products'] - compressed_fields = True + # compressed_fields = True warn_unsaved_form = True list_display = ['display_image', 'price',] + fieldsets = ( + ('Main Fileds', {'fields': ('name', 'description', 'price', 'currency', 'discount', 'category', 'related_products', 'show',), "classes": ["tab"],}), + ('SEO Fileds', {'fields': ('meta_description', 'meta_keywords', 'meta_rating', 'slug'), "classes": ["tab"],}), + ('Users Fileds', {'fields': ('rating', 'view', 'sell', ), "classes": ["tab"],}) + + ) formfield_overrides = { models.TextField: { "widget": WysiwygWidget, From a7584677820948d478b434f8b125baa36d8b3e16 Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sun, 2 Feb 2025 17:05:09 +0330 Subject: [PATCH 24/37] user admin update --- backend/account/admin.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/backend/account/admin.py b/backend/account/admin.py index aad9be0..6ea35fa 100644 --- a/backend/account/admin.py +++ b/backend/account/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin from .models import * -from unfold.admin import ModelAdmin +from unfold.admin import ModelAdmin, TabularInline from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from import_export.admin import ImportExportModelAdmin from unfold.contrib.import_export.forms import ExportForm, ImportForm, SelectableFieldsExportForm @@ -8,12 +8,21 @@ from unfold.contrib.forms.widgets import ArrayWidget, WysiwygWidget from django.contrib.postgres.fields import ArrayField from django.contrib.auth.models import Group from unfold.forms import AdminPasswordChangeForm +from unfold.forms import AdminPasswordChangeForm, UserChangeForm, UserCreationForm +class UserAddressInLine(TabularInline): + model = UserAddressModel + extra = 0 + tab = True + @admin.register(User) class UserAdmin(BaseUserAdmin, ModelAdmin, ImportExportModelAdmin): - + form = UserChangeForm + add_form = UserCreationForm + change_password_form = AdminPasswordChangeForm filter_horizontal = [] ordering = [] + inlines = [UserAddressInLine] list_filter = [] search_fields = ['phone', 'first_name', 'last_name', ] list_display = ['phone', 'email', 'is_superuser'] @@ -22,10 +31,9 @@ class UserAdmin(BaseUserAdmin, ModelAdmin, ImportExportModelAdmin): exclude = ('otp_hash', 'otp_expiry', 'is_active', 'is_staff', 'password', 'last_login') import_form_class = ImportForm export_form_class = ExportForm - change_password_form = AdminPasswordChangeForm fieldsets = ( - ('Personal info', {'fields': ('first_name', 'last_name', 'profile_photo')}), - ('contact', {'fields': ('phone', 'email')}), + ('Personal info', {'fields': ('first_name', 'last_name', 'profile_photo', 'password'),}), + ('contact', {'fields': ('phone', 'email'),}), ) add_fieldsets = ( From 85bfb347b23ee686893a086f43336a01b6c47e44 Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sun, 2 Feb 2025 17:32:01 +0330 Subject: [PATCH 25/37] blog serializer udpate --- backend/blog/serializers.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/backend/blog/serializers.py b/backend/blog/serializers.py index f392204..5027ecd 100644 --- a/backend/blog/serializers.py +++ b/backend/blog/serializers.py @@ -1,13 +1,27 @@ from rest_framework import serializers from .models import BlogModel +from account.models import User + +class AuthorSerializer(serializers.ModelSerializer): + full_name = serializers.SerializerMethodField() + class Meta: + model = User + fields = ['full_name', 'profile_photo'] + def get_full_name(self, obj): + if obj.first_name and obj.last_name: + return obj.first_name + ' ' + obj.last_name + else: + return 'ادمین وبسایت' class BlogSerilizer(serializers.ModelSerializer): + author = AuthorSerializer() class Meta: model = BlogModel - fields = ['title','author', 'slug', 'category', 'created_at', 'updated_at', 'cover_image', 'views'] + exclude = ('is_published',) class AllBlogSerilizer(serializers.ModelSerializer): + author = AuthorSerializer() class Meta: model = BlogModel - exclude = ('is_published',) \ No newline at end of file + exclude = ('is_published', 'content', 'summery', ) \ No newline at end of file From 97f6c12da432c139ab958c76648eb94a0967c106 Mon Sep 17 00:00:00 2001 From: Mamalizz Date: Sun, 2 Feb 2025 18:00:07 +0330 Subject: [PATCH 26/37] new changes --- frontend/components/cart/AddressItem.vue | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/components/cart/AddressItem.vue b/frontend/components/cart/AddressItem.vue index bcd660a..d2d27d6 100644 --- a/frontend/components/cart/AddressItem.vue +++ b/frontend/components/cart/AddressItem.vue @@ -23,12 +23,12 @@ const emit = defineEmits(["select"]); :class=" isSelected ? 'border-cyan-500 ring-2 ring-offset-2 ring-cyan-500' - : 'border-gray-300' + : 'border-slate-200' " - class="flex flex-col items-center transition-all cursor-pointer w-full gap-4 p-4 border rounded-xl bg-gray-50" + class="flex flex-col items-center transition-all cursor-pointer w-full gap-4 p-4 border rounded-xl bg-slate-50" > آدرس @@ -37,7 +37,7 @@ const emit = defineEmits(["select"]); class="flex flex-col items-center justify-between w-full gap-4 lg:flex-row" > {{ !!address From 9cace29fda2b23b8ecdcaf09dcabd26bac4ae2af Mon Sep 17 00:00:00 2001 From: Mamalizz Date: Sun, 2 Feb 2025 18:00:10 +0330 Subject: [PATCH 27/37] new changes --- frontend/components/cart/AddressModal.vue | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/frontend/components/cart/AddressModal.vue b/frontend/components/cart/AddressModal.vue index 52c3312..e28e8c7 100644 --- a/frontend/components/cart/AddressModal.vue +++ b/frontend/components/cart/AddressModal.vue @@ -62,25 +62,15 @@ const addNew = () => { " > - + Date: Sun, 2 Feb 2025 18:00:13 +0330 Subject: [PATCH 28/37] new changes --- frontend/components/cart/CartItem.vue | 30 +++++++++++++++------------ 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/frontend/components/cart/CartItem.vue b/frontend/components/cart/CartItem.vue index 2ae5567..88fa21f 100644 --- a/frontend/components/cart/CartItem.vue +++ b/frontend/components/cart/CartItem.vue @@ -10,11 +10,11 @@ const handleDeleteFromCart = () => {};