From c86732a79d4fd5442cc3637171e1eee94035117e Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sun, 2 Feb 2025 18:27:22 +0330 Subject: [PATCH 01/25] add city and province fields --- ...essmodel_city_useraddressmodel_province.py | 25 +++++++++++++++++++ backend/account/models.py | 3 ++- backend/account/serializers.py | 2 +- 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 backend/account/migrations/0003_useraddressmodel_city_useraddressmodel_province.py diff --git a/backend/account/migrations/0003_useraddressmodel_city_useraddressmodel_province.py b/backend/account/migrations/0003_useraddressmodel_city_useraddressmodel_province.py new file mode 100644 index 0000000..40d8a25 --- /dev/null +++ b/backend/account/migrations/0003_useraddressmodel_city_useraddressmodel_province.py @@ -0,0 +1,25 @@ +# Generated by Django 5.1.2 on 2025-02-02 14:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0002_alter_user_options'), + ] + + operations = [ + migrations.AddField( + model_name='useraddressmodel', + name='city', + field=models.CharField(default='', max_length=30), + preserve_default=False, + ), + migrations.AddField( + model_name='useraddressmodel', + name='province', + field=models.CharField(default='', max_length=30), + preserve_default=False, + ), + ] diff --git a/backend/account/models.py b/backend/account/models.py index 9bc345d..e2db451 100644 --- a/backend/account/models.py +++ b/backend/account/models.py @@ -110,6 +110,7 @@ class UserAddressModel(models.Model): address = models.TextField() postal_code = models.CharField(max_length=10) phone = models.CharField(max_length=11) - + city = models.CharField(max_length=30) + province = models.CharField(max_length=30) def __str__(self): return f"{self.user.phone}, {self.name}" \ No newline at end of file diff --git a/backend/account/serializers.py b/backend/account/serializers.py index 7a82d81..2bb76a4 100644 --- a/backend/account/serializers.py +++ b/backend/account/serializers.py @@ -17,7 +17,7 @@ class ProfileSerializer(serializers.ModelSerializer): class UserAddressSerializer(serializers.ModelSerializer): class Meta: model = UserAddressModel - fields = ['id', 'name', 'address', 'postal_code', 'phone'] + fields = ['id', 'name', 'address', 'postal_code', 'phone', 'city', 'province'] def validate(self, data): user = self.context['request'].user From 520848b6eac7a3695a568c6f80822d65c4ce9cfd Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sun, 2 Feb 2025 19:23:20 +0330 Subject: [PATCH 02/25] update address model for me filed and serializer --- .../migrations/0004_useraddressmodel_for_me.py | 18 ++++++++++++++++++ backend/account/models.py | 1 + backend/account/serializers.py | 4 ++-- 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 backend/account/migrations/0004_useraddressmodel_for_me.py diff --git a/backend/account/migrations/0004_useraddressmodel_for_me.py b/backend/account/migrations/0004_useraddressmodel_for_me.py new file mode 100644 index 0000000..c35f6d8 --- /dev/null +++ b/backend/account/migrations/0004_useraddressmodel_for_me.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.2 on 2025-02-02 15:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0003_useraddressmodel_city_useraddressmodel_province'), + ] + + operations = [ + migrations.AddField( + model_name='useraddressmodel', + name='for_me', + field=models.BooleanField(default=False), + ), + ] diff --git a/backend/account/models.py b/backend/account/models.py index e2db451..994231d 100644 --- a/backend/account/models.py +++ b/backend/account/models.py @@ -112,5 +112,6 @@ class UserAddressModel(models.Model): phone = models.CharField(max_length=11) city = models.CharField(max_length=30) province = models.CharField(max_length=30) + for_me = models.BooleanField(default=False) def __str__(self): return f"{self.user.phone}, {self.name}" \ No newline at end of file diff --git a/backend/account/serializers.py b/backend/account/serializers.py index 2bb76a4..dae6e9e 100644 --- a/backend/account/serializers.py +++ b/backend/account/serializers.py @@ -17,8 +17,8 @@ class ProfileSerializer(serializers.ModelSerializer): class UserAddressSerializer(serializers.ModelSerializer): class Meta: model = UserAddressModel - fields = ['id', 'name', 'address', 'postal_code', 'phone', 'city', 'province'] - + fields = ['id', 'name', 'address', 'postal_code', 'phone', 'city', 'province', 'for_me'] + read_only_fields = ('id',) def validate(self, data): user = self.context['request'].user if not user.is_authenticated: From 714a90447618653690493e4f0088ba5a1287f6ca Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sun, 2 Feb 2025 20:02:59 +0330 Subject: [PATCH 03/25] new dashboard update --- backend/core/settings.py | 3 +- backend/core/views.py | 190 +++++++++++++++++++++++++ backend/templates/admin/base_site.html | 13 ++ backend/templates/admin/index.html | 93 ++++++++++++ backend/templates/formula/service.html | 25 ++++ 5 files changed, 323 insertions(+), 1 deletion(-) create mode 100644 backend/core/views.py create mode 100644 backend/templates/admin/base_site.html create mode 100644 backend/templates/admin/index.html create mode 100644 backend/templates/formula/service.html diff --git a/backend/core/settings.py b/backend/core/settings.py index 97c840d..77f47bc 100644 --- a/backend/core/settings.py +++ b/backend/core/settings.py @@ -131,7 +131,7 @@ ROOT_URLCONF = 'core.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], + 'DIRS': [BASE_DIR / "templates"], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ @@ -226,6 +226,7 @@ UNFOLD = { "SITE_HEADER": os.getenv("SITE_HEADER"), "SITE_URL": DOMAIN, "SITE_SYMBOL": "shield_person", + "DASHBOARD_CALLBACK": "core.views.dashboard_callback", "SITE_FAVICONS": [ { "rel": "icon", diff --git a/backend/core/views.py b/backend/core/views.py new file mode 100644 index 0000000..ca70765 --- /dev/null +++ b/backend/core/views.py @@ -0,0 +1,190 @@ +import json +import random +from functools import lru_cache + +from django.contrib.humanize.templatetags.humanize import intcomma +from django.utils.safestring import mark_safe +from django.utils.translation import gettext_lazy as _ +from django.views.generic import RedirectView, TemplateView +from unfold.views import UnfoldModelAdminViewMixin + +class HomeView(RedirectView): + pattern_name = "admin:index" + + + + +def dashboard_callback(request, context): + + context.update(random_data()) + return context + + +@lru_cache +def random_data(): + WEEKDAYS = [ + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat", + "Sun", + ] + + positive = [[1, random.randrange(8, 28)] for i in range(1, 28)] + negative = [[-1, -random.randrange(8, 28)] for i in range(1, 28)] + average = [r[1] - random.randint(3, 5) for r in positive] + performance_positive = [[1, random.randrange(8, 28)] for i in range(1, 28)] + performance_negative = [[-1, -random.randrange(8, 28)] for i in range(1, 28)] + + response = { + "navigation": [ + {"title": _("Dashboard"), "link": "/", "active": True}, + {"title": _("Products"), "link": "/admin/product/productmodel/"}, + {"title": _("Orders"), "link": "/admin/order/ordermodel/"}, + ], + "kpi": [ + { + "title": "Product A Performance", + "metric": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "footer": mark_safe( + f'+{intcomma(f"{random.uniform(1, 9):.02f}")}% progress from last week' + ), + "chart": json.dumps( + { + "labels": [WEEKDAYS[day % 7] for day in range(1, 28)], + "datasets": [{"data": average, "borderColor": "#9333ea"}], + } + ), + }, + { + "title": "Product B Performance", + "metric": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "footer": mark_safe( + f'+{intcomma(f"{random.uniform(1, 9):.02f}")}% progress from last week' + ), + }, + { + "title": "Product C Performance", + "metric": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "footer": mark_safe( + f'+{intcomma(f"{random.uniform(1, 9):.02f}")}% progress from last week' + ), + }, + ], + "progress": [ + { + "title": "🦆 Social marketing e-book", + "description": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "value": random.randint(10, 90), + }, + { + "title": "🦍 Freelancing tasks", + "description": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "value": random.randint(10, 90), + }, + { + "title": "🐋 Development coaching", + "description": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "value": random.randint(10, 90), + }, + { + "title": "🦑 Product consulting", + "description": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "value": random.randint(10, 90), + }, + { + "title": "🐨 Other income", + "description": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "value": random.randint(10, 90), + }, + { + "title": "🐶 Course sales", + "description": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "value": random.randint(10, 90), + }, + { + "title": "🐻‍❄️ Ads revenue", + "description": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "value": random.randint(10, 90), + }, + { + "title": "🦩 Customer Retention Rate", + "description": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "value": random.randint(10, 90), + }, + { + "title": "🦊 Marketing ROI", + "description": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "value": random.randint(10, 90), + }, + { + "title": "🦁 Affiliate partnerships", + "description": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "value": random.randint(10, 90), + }, + ], + "chart": json.dumps( + { + "labels": [WEEKDAYS[day % 7] for day in range(1, 28)], + "datasets": [ + { + "label": "Example 1", + "type": "line", + "data": average, + "borderColor": "var(--color-primary-500)", + }, + { + "label": "Example 2", + "data": positive, + "backgroundColor": "var(--color-primary-700)", + }, + { + "label": "Example 3", + "data": negative, + "backgroundColor": "var(--color-primary-300)", + }, + ], + } + ), + "performance": [ + { + "title": _("Last week revenue"), + "metric": "$1,234.56", + "footer": mark_safe( + '+3.14% progress from last week' + ), + "chart": json.dumps( + { + "labels": [WEEKDAYS[day % 7] for day in range(1, 28)], + "datasets": [ + { + "data": performance_positive, + "borderColor": "var(--color-primary-700)", + } + ], + } + ), + }, + { + "title": _("Last week expenses"), + "metric": "$1,234.56", + "footer": mark_safe( + '+3.14% progress from last week' + ), + "chart": json.dumps( + { + "labels": [WEEKDAYS[day % 7] for day in range(1, 28)], + "datasets": [ + { + "data": performance_negative, + "borderColor": "var(--color-primary-300)", + }, + ], + } + ), + }, + ], + } + return response diff --git a/backend/templates/admin/base_site.html b/backend/templates/admin/base_site.html new file mode 100644 index 0000000..f349bd7 --- /dev/null +++ b/backend/templates/admin/base_site.html @@ -0,0 +1,13 @@ +{% extends "admin/base.html" %} + +{% block title %}{% if subtitle %}{{ subtitle }} | {% endif %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %} + +{% block branding %} +

{{ site_header|default:_('Django administration') }}

+{% endblock %} + +{% block extrahead %} + {% if plausible_domain %} + + {% endif %} +{% endblock %} diff --git a/backend/templates/admin/index.html b/backend/templates/admin/index.html new file mode 100644 index 0000000..099c689 --- /dev/null +++ b/backend/templates/admin/index.html @@ -0,0 +1,93 @@ +{% extends 'admin/base.html' %} + +{% load i18n unfold %} + +{% block breadcrumbs %}{% endblock %} + +{% block title %} + {% trans 'Dashboard' %} | {{ site_title|default:_('Django site admin') }} +{% endblock %} + + + +{% block branding %} +

{{ site_header|default:_('Django administration') }}

+{% endblock %} + +{% block content %} + {% include "unfold/helpers/messages.html" %} + + {% component "unfold/components/container.html" %} +
+
+ {% component "unfold/components/navigation.html" with items=navigation %}{% endcomponent %} +
+ + {% include "formula/service.html" %} + +
+ {% for stats in kpi %} + {% component "unfold/components/card.html" with class="lg:w-1/3" label=_("Last 7 days") footer=stats.footer %} + {% component "unfold/components/text.html" %} + {{ stats.title }} + {% endcomponent %} + + {% component "unfold/components/title.html" %} + {{ stats.metric }} + {% endcomponent %} + {% endcomponent %} + {% endfor %} +
+ + + + {% component "unfold/components/card.html" with title=_("Product performance in last 28 days") %} + {% component "unfold/components/chart/bar.html" with data=chart height=320 %}{% endcomponent %} + {% endcomponent %} + +
+ {% component "unfold/components/card.html" with class="lg:w-1/2" title=_("The most trending products in last 2 weeks") %} + {% component "unfold/components/title.html" with class="mb-2" %} + $1,234,567.89 + {% endcomponent %} + + {% component "unfold/components/text.html" %} + {% blocktrans %} + Total revenue between 1 - 31 October. Increase +3.14% comparing to previous month 1 - 30 September. + {% endblocktrans %} + {% endcomponent %} + + {% component "unfold/components/separator.html" %}{% endcomponent %} + +
+ {% for metric in progress %} + {% component "unfold/components/progress.html" with title=metric.title description=metric.description value=metric.value %}{% endcomponent %} + {% endfor %} +
+ + + + {% endcomponent %} + + +
+ + + {% for stats in performance %} + {% component "unfold/components/card.html" %} + {% component "unfold/components/text.html" %} + {{ stats.title }} + {% endcomponent %} + + {% component "unfold/components/title.html" with class="mb-8" %} + {{ stats.metric }} + {% endcomponent %} + + {% component "unfold/components/chart/line.html" with data=stats.chart %}{% endcomponent %} + {% endcomponent %} + {% endfor %} +
+
+
+ {% endcomponent %} +{% endblock %} diff --git a/backend/templates/formula/service.html b/backend/templates/formula/service.html new file mode 100644 index 0000000..9020c6e --- /dev/null +++ b/backend/templates/formula/service.html @@ -0,0 +1,25 @@ +{% load unfold i18n %} + +
+
+

+ {% trans "Are you looking for a custom dashboard?" %} +

+ +

+ {% trans "Did you decide to start using Unfold in your application but you need help with integration? Feel free to get in touch for consulting, priority support on specific tickets or development services." %} +

+
+ +
+ {% component "unfold/components/flex.html" with class="flex-col gap-4 lg:flex-row" %} + {% component "unfold/components/button.html" with href="https://unfoldadmin.com/consulting/?utm_medium=referral&utm_source=formula" %} + {% trans "Book a call with Lukas" %} + {% endcomponent %} + + {% component "unfold/components/button.html" with href="https://unfoldadmin.com/docs" variant="default" %} + {% trans "I'm good with docs" %} + {% endcomponent %} + {% endcomponent %} +
+
\ No newline at end of file From b09bc2fb09dcbb4a677b628922bab732ccd3a85e Mon Sep 17 00:00:00 2001 From: marzban-dev Date: Sun, 2 Feb 2025 21:18:38 +0330 Subject: [PATCH 04/25] Update typo leading sizes --- frontend/assets/css/typo.utils.css | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/assets/css/typo.utils.css b/frontend/assets/css/typo.utils.css index 43e281a..763b533 100644 --- a/frontend/assets/css/typo.utils.css +++ b/frontend/assets/css/typo.utils.css @@ -55,27 +55,27 @@ /* TYPE PARAGRAPH */ @utility typo-p-2xl { - @apply text-[24px] leading-[40px] font-light ; + @apply text-[24px] leading-[42px] font-light ; } @utility typo-p-xl { - @apply text-[20px] leading-[32px] font-light ; + @apply text-[20px] leading-[34px] font-light ; } @utility typo-p-lg { - @apply text-[18px] leading-[32px] font-light ; + @apply text-[18px] leading-[34px] font-light ; } @utility typo-p-md { - @apply text-[16px] leading-[28px] font-light ; + @apply text-[16px] leading-[30px] font-light ; } @utility typo-p-sm { - @apply text-[14px] leading-[24px] font-light ; + @apply text-[14px] leading-[26px] font-light ; } @utility typo-p-xs { - @apply text-[12px] leading-[16px] font-light ; + @apply text-[12px] leading-[18px] font-light ; } /* TYPO LABEL */ From 633631c285909329a49e850242c8af3656b1fab0 Mon Sep 17 00:00:00 2001 From: marzban-dev Date: Sun, 2 Feb 2025 21:18:49 +0330 Subject: [PATCH 05/25] Create articles masonry list --- frontend/components/articles/ArticlesList.vue | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 frontend/components/articles/ArticlesList.vue diff --git a/frontend/components/articles/ArticlesList.vue b/frontend/components/articles/ArticlesList.vue new file mode 100644 index 0000000..4413d94 --- /dev/null +++ b/frontend/components/articles/ArticlesList.vue @@ -0,0 +1,63 @@ + + + + + \ No newline at end of file From a1248828deca0ead749fc8a0629fc108b974330e Mon Sep 17 00:00:00 2001 From: marzban-dev Date: Sun, 2 Feb 2025 21:19:08 +0330 Subject: [PATCH 06/25] Updated --- frontend/components/global/products/FilterProducts.vue | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frontend/components/global/products/FilterProducts.vue b/frontend/components/global/products/FilterProducts.vue index daadb14..2dcd364 100644 --- a/frontend/components/global/products/FilterProducts.vue +++ b/frontend/components/global/products/FilterProducts.vue @@ -44,9 +44,7 @@ const filters = computed(() => { const { data: categories, suspense } = useGetCategories(); -await useAsyncData(async () => { - await suspense(); -}); +await suspense(); const { isPending: productsIsPending } = useGetProducts(filters); From 65ea8b38082e9cf71ef8f5416a753dc1a34aa782 Mon Sep 17 00:00:00 2001 From: marzban-dev Date: Sun, 2 Feb 2025 21:19:16 +0330 Subject: [PATCH 07/25] Updated --- frontend/components/global/BlogPost.vue | 160 ++++++++++++------------ 1 file changed, 79 insertions(+), 81 deletions(-) diff --git a/frontend/components/global/BlogPost.vue b/frontend/components/global/BlogPost.vue index 485bf58..76f425f 100644 --- a/frontend/components/global/BlogPost.vue +++ b/frontend/components/global/BlogPost.vue @@ -3,12 +3,11 @@ // types type Props = { + id: number; tag: string; date: string; - comments: number; title: string; description: string; - link: string; variant?: "sm" | "lg"; image: string, } @@ -23,96 +22,95 @@ const {} = toRefs(props); \ No newline at end of file From ebc7a14103e0d552c6664d6a9d5b00712f10a490 Mon Sep 17 00:00:00 2001 From: marzban-dev Date: Sun, 2 Feb 2025 21:19:33 +0330 Subject: [PATCH 08/25] Add mute/unmute button for slider videos --- frontend/components/home/Hero.vue | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/frontend/components/home/Hero.vue b/frontend/components/home/Hero.vue index 02843e5..adf07db 100644 --- a/frontend/components/home/Hero.vue +++ b/frontend/components/home/Hero.vue @@ -10,6 +10,7 @@ import useHomeData from "~/composables/api/home/useHomeData"; const { data: homeData } = useHomeData(); const swiper_instance = ref(null); +const isMuted = ref(true); // methods @@ -37,18 +38,29 @@ const onChange = (swiper: SwiperClass) => { @slide-change="onChange" >
-
-
-
- - -
-
- - -
-
+ + + \ No newline at end of file From c22f5db23f7167db3196759f31b947a7a65d15cf Mon Sep 17 00:00:00 2001 From: marzban-dev Date: Sun, 2 Feb 2025 21:20:13 +0330 Subject: [PATCH 10/25] Sanitize product description content before render --- frontend/components/product/ProductHero.vue | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/frontend/components/product/ProductHero.vue b/frontend/components/product/ProductHero.vue index baad819..e186b38 100644 --- a/frontend/components/product/ProductHero.vue +++ b/frontend/components/product/ProductHero.vue @@ -3,6 +3,7 @@ // import import useGetProduct from "~/composables/api/product/useGetProduct"; +import { sanitize } from "isomorphic-dompurify"; // state @@ -14,6 +15,9 @@ const { data: product } = useGetProduct(id); const quantity = ref(1); const selectedSlide = ref(0); + +// computed + const slides = computed(() => { return [ { @@ -30,6 +34,11 @@ const slides = computed(() => { } ]; }); + +const sanitizedProductDescription = computed(() => { + return sanitize(product.value!.description); +}); +