merge
This commit is contained in:
+2
-2
@@ -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 = 'Heymlz Shop'
|
||||
SITE_HEADER = 'Heymlz Shop'
|
||||
SITE_TITLE = 'فروشگاه هی ملز'
|
||||
SITE_HEADER = 'فروشگاه هی ملز'
|
||||
# jwt token configs
|
||||
ACCESS_TOKEN_LIFETIME = 5000
|
||||
REFRESH_TOKEN_LIFETIME = 5000
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
]
|
||||
@@ -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),
|
||||
),
|
||||
]
|
||||
@@ -110,6 +110,8 @@ 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)
|
||||
for_me = models.BooleanField(default=False)
|
||||
def __str__(self):
|
||||
return f"{self.user.phone}, {self.name}"
|
||||
@@ -17,8 +17,8 @@ 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', 'for_me']
|
||||
read_only_fields = ('id',)
|
||||
def validate(self, data):
|
||||
user = self.context['request'].user
|
||||
if not user.is_authenticated:
|
||||
|
||||
+12
-10
@@ -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': [
|
||||
@@ -178,7 +178,7 @@ STATIC_ROOT = '/app/static'
|
||||
|
||||
STATICFILES_DIRS = [
|
||||
os.path.join(BASE_DIR, 'custom_static'),
|
||||
# BASE_DIR / "core" / "static"
|
||||
BASE_DIR / "core" / "static"
|
||||
]
|
||||
|
||||
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
|
||||
@@ -225,7 +225,9 @@ UNFOLD = {
|
||||
"SITE_TITLE": os.getenv("SITE_TITLE"),
|
||||
"SITE_HEADER": os.getenv("SITE_HEADER"),
|
||||
"SITE_URL": DOMAIN,
|
||||
"THEME": 'dark',
|
||||
"SITE_SYMBOL": "shield_person",
|
||||
"DASHBOARD_CALLBACK": "core.views.dashboard_callback",
|
||||
"SITE_FAVICONS": [
|
||||
{
|
||||
"rel": "icon",
|
||||
@@ -239,7 +241,7 @@ UNFOLD = {
|
||||
# },
|
||||
|
||||
|
||||
"BORDER_RADIUS": "15px",
|
||||
"BORDER_RADIUS": "8px",
|
||||
"SHOW_HISTORY": True,
|
||||
"SHOW_VIEW_ON_SITE": True,
|
||||
"ENVIRONMENT": "core.settings.environment_callback",
|
||||
@@ -289,8 +291,8 @@ UNFOLD = {
|
||||
},
|
||||
|
||||
"SIDEBAR": {
|
||||
"show_search": False,
|
||||
"show_all_applications": False,
|
||||
"show_search": True,
|
||||
"show_all_applications": True,
|
||||
"navigation": [
|
||||
{
|
||||
|
||||
@@ -314,7 +316,7 @@ UNFOLD = {
|
||||
|
||||
|
||||
{
|
||||
"title": _("Shop Products"),
|
||||
"title": _("محصولات فروشگاه"),
|
||||
"separator": True,
|
||||
"collapsible": False,
|
||||
"items": [
|
||||
@@ -341,7 +343,7 @@ UNFOLD = {
|
||||
|
||||
|
||||
{
|
||||
"title": _("Categories section"),
|
||||
"title": _("سکشن دسته بندی"),
|
||||
"separator": True,
|
||||
"collapsible": False,
|
||||
"items": [
|
||||
@@ -360,7 +362,7 @@ UNFOLD = {
|
||||
],
|
||||
},
|
||||
{
|
||||
"title": _("Visual Sections "),
|
||||
"title": _("سکشن های نمایشی"),
|
||||
"separator": True,
|
||||
"collapsible": True,
|
||||
"items": [
|
||||
@@ -386,7 +388,7 @@ UNFOLD = {
|
||||
},
|
||||
|
||||
{
|
||||
"title": _("Users and Customers"),
|
||||
"title": _("کاربران و مشتریان"),
|
||||
"separator": True,
|
||||
"collapsible": True,
|
||||
"items": [
|
||||
@@ -410,7 +412,7 @@ UNFOLD = {
|
||||
},
|
||||
|
||||
{
|
||||
"title": _("Ticket and Support"),
|
||||
"title": _("پشتیبانی و تیکت"),
|
||||
"separator": True,
|
||||
"collapsible": True,
|
||||
"items": [
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
@layer base {
|
||||
@font-face {
|
||||
font-family: "IRANYekanXVF";
|
||||
src: url("./fonts/IranYekanX/IRANYekanX-Thin.woff2");
|
||||
font-weight: 100;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "IRANYekanXVF";
|
||||
src: url("./fonts/IranYekanX/IRANYekanX-UltraLight.woff2");
|
||||
font-weight: 200;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "IRANYekanXVF";
|
||||
src: url("./fonts/IranYekanX/IRANYekanX-Light.woff2");
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "IRANYekanXVF";
|
||||
src: url("./fonts/IranYekanX/IRANYekanX-Regular.woff2");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "IRANYekanXVF";
|
||||
src: url("./fonts/IranYekanX/IRANYekanX-Medium.woff2");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "IRANYekanXVF";
|
||||
src: url("./fonts/IranYekanX/IRANYekanX-DemiBold.woff2");
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "IRANYekanXVF";
|
||||
src: url("./fonts/IranYekanX/IRANYekanX-Bold.woff2");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "IRANYekanXVF";
|
||||
src: url("./fonts/IranYekanX/IRANYekanX-ExtraBold.woff2");
|
||||
font-weight: 800;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "IRANYekanXVF";
|
||||
src: url("./fonts/IranYekanX/IRANYekanX-Black.woff2");
|
||||
font-weight: 900;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "IRANYekanXVF";
|
||||
src: url("./fonts/IranYekanX/IRANYekanX-ExtraBlack.woff2");
|
||||
font-weight: 950;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "IRANYekanXVF";
|
||||
src: url("./fonts/IranYekanX/IRANYekanX-Heavy.woff2");
|
||||
font-weight: 1000;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
*:not(span[class^="material"]){
|
||||
font-family: 'IRANYekanXVF' !important;
|
||||
}
|
||||
@@ -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'<strong class="text-green-700 font-semibold dark:text-green-400">+{intcomma(f"{random.uniform(1, 9):.02f}")}%</strong> 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'<strong class="text-green-700 font-semibold dark:text-green-400">+{intcomma(f"{random.uniform(1, 9):.02f}")}%</strong> progress from last week'
|
||||
),
|
||||
},
|
||||
{
|
||||
"title": "Product C Performance",
|
||||
"metric": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}",
|
||||
"footer": mark_safe(
|
||||
f'<strong class="text-green-700 font-semibold dark:text-green-400">+{intcomma(f"{random.uniform(1, 9):.02f}")}%</strong> 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(
|
||||
'<strong class="text-green-600 font-medium">+3.14%</strong> 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(
|
||||
'<strong class="text-green-600 font-medium">+3.14%</strong> 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
|
||||
@@ -0,0 +1,17 @@
|
||||
{% extends "admin/base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static 'override.css' %}" />
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'fonts.css' %}" />
|
||||
{% endblock %}
|
||||
{% block title %}{% if subtitle %}{{ subtitle }} | {% endif %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}
|
||||
|
||||
{% block branding %}
|
||||
<h1 id="site-name"><a href="{% url 'admin:index' %}">{{ site_header|default:_('Django administration') }}</a></h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block extrahead %}
|
||||
{% if plausible_domain %}
|
||||
<script defer data-domain="{{ plausible_domain }}" src="https://plausible.io/js/script.js"></script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,96 @@
|
||||
{% extends 'admin/base.html' %}
|
||||
{% load static %}
|
||||
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static 'override.css' %}" />
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'fonts.css' %}" />
|
||||
{% endblock %}
|
||||
{% load i18n unfold %}
|
||||
|
||||
{% block breadcrumbs %}{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
{% trans 'Dashboard' %} | {{ site_title|default:_('Django site admin') }}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
{% block branding %}
|
||||
<h1 id="site-name"><a href="{% url 'admin:index' %}">{{ site_header|default:_('Django administration') }}</a></h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% include "unfold/helpers/messages.html" %}
|
||||
|
||||
{% component "unfold/components/container.html" %}
|
||||
<div class="flex flex-col gap-8 mb-12">
|
||||
{% include "formula/service.html" %}
|
||||
<!-- <div class="flex gap-4">
|
||||
{% component "unfold/components/navigation.html" with items=navigation %}{% endcomponent %}
|
||||
</div> -->
|
||||
|
||||
|
||||
<div class="flex flex-col gap-8 lg:flex-row">
|
||||
{% 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 %}
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{% 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 %}
|
||||
|
||||
<div class="flex flex-col gap-8 lg:flex-row">
|
||||
{% 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 <strong class="font-semibold text-font-important-light dark:text-font-important-dark dark:text-white">1 - 31 October</strong>. Increase <span class="text-green-700 font-semibold dark:text-green-400">+3.14%</span> comparing to previous month <strong class="font-semibold text-font-important-light dark:text-font-important-dark dark:text-white">1 - 30 September</strong>.
|
||||
{% endblocktrans %}
|
||||
{% endcomponent %}
|
||||
|
||||
{% component "unfold/components/separator.html" %}{% endcomponent %}
|
||||
|
||||
<div class="flex flex-col gap-5">
|
||||
{% for metric in progress %}
|
||||
{% component "unfold/components/progress.html" with title=metric.title description=metric.description value=metric.value %}{% endcomponent %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{% endcomponent %}
|
||||
|
||||
|
||||
<div class="flex flex-col gap-8 lg:w-1/2">
|
||||
|
||||
|
||||
{% 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 %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endcomponent %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,18 @@
|
||||
{% load unfold i18n %}
|
||||
|
||||
<div dir='rtl' class="bg-base-50 border border-base-200 border-dashed flex flex-col gap-4 p-4 rounded dark:bg-white/[.02] dark:border-base-700 lg:flex-row lg:justify-between w-full shrink-0 lg:items-center" style="justify-content: space-between;">
|
||||
<div class="flex flex-col lg:flex-row lg:items-center">
|
||||
<h2 class="font-semibold text-font-important-light text-base dark:text-font-important-dark flex items-center">
|
||||
<span class="material-symbols-outlined md-18 mr-3 w-4.5 align-middle">notifications</span>
|
||||
<span class="align-middle">سفارش جدید داری</span>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="flex lg:flex-row lg:items-center">
|
||||
{% component "unfold/components/flex.html" with class="flex-col gap-4 lg:flex-row" %}
|
||||
{% component "unfold/components/button.html" with href="/admin/order/ordermodel/" %}
|
||||
نمایش سفارشات
|
||||
{% endcomponent %}
|
||||
{% endcomponent %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -22,3 +22,4 @@ node_modules
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
/test-results/.last-run.json
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
<script lang="ts" setup>
|
||||
|
||||
// import
|
||||
|
||||
import Masonry from "masonry-layout";
|
||||
|
||||
// type
|
||||
|
||||
type Props = {
|
||||
articles: Article[],
|
||||
}
|
||||
|
||||
// props
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const { articles } = toRefs(props);
|
||||
|
||||
// state
|
||||
|
||||
onMounted(() => {
|
||||
new Masonry(".masonry-articles-container", {
|
||||
itemSelector: ".grid-item",
|
||||
columnWidth: ".grid-sizer",
|
||||
percentPosition: true,
|
||||
gutter: ".gutter-sizer"
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="masonry-articles-container w-full">
|
||||
|
||||
<div class="grid-sizer"></div>
|
||||
<div class="gutter-sizer"></div>
|
||||
|
||||
<BlogPost
|
||||
v-for="article in articles"
|
||||
:key="article.id"
|
||||
class="grid-item"
|
||||
:image="article.cover_image"
|
||||
:description="article.summery"
|
||||
:title="article.title"
|
||||
:comments="2"
|
||||
:id="article.id"
|
||||
:date="article.created_at"
|
||||
tag="تگ ندارد"
|
||||
/>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.grid-sizer,
|
||||
.grid-item {
|
||||
margin-bottom: 24px;
|
||||
width: 48%;
|
||||
}
|
||||
|
||||
.gutter-sizer {
|
||||
width: 4%;
|
||||
}
|
||||
</style>
|
||||
@@ -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,9 +22,10 @@ const {} = toRefs(props);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NuxtLink :to="`/article/${id}`">
|
||||
<div
|
||||
:class="variant === 'lg' ? 'rounded-150 overflow-hidden' : ''"
|
||||
class="group max-h-[700px] h-[700px] relative"
|
||||
:class="variant === 'lg' ? 'h-[600px] rounded-150 overflow-hidden' : 'h-fit'"
|
||||
class="group w-full relative"
|
||||
>
|
||||
|
||||
<Tag
|
||||
@@ -82,24 +82,21 @@ const {} = toRefs(props);
|
||||
|
||||
<div class="flex gap-4 flex-col">
|
||||
<span
|
||||
:class="variant === 'lg' ? 'typo-h-4' : 'typo-h-6'"
|
||||
:class="variant === 'lg' ? 'typo-h-5' : 'typo-h-6'"
|
||||
class="text-white"
|
||||
>
|
||||
برسی آیفون ۱۶ پرومکس
|
||||
{{ title }}
|
||||
</span>
|
||||
<p
|
||||
:class="variant === 'lg' ? 'typo-h-4' : 'typo-h-6 text-slate-500'"
|
||||
class="typo-p-md text-white text-justify"
|
||||
>
|
||||
نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد.
|
||||
نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد.
|
||||
کتابهای زیادی در شصت و سه درصد گذشته.
|
||||
</p>
|
||||
v-html="description"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<span class="underline text-white typo-p-md">
|
||||
<NuxtLink :to="`/article/${id}`" class="underline text-white typo-p-md">
|
||||
بیشتر بخوانید...
|
||||
</span>
|
||||
</NuxtLink>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -115,4 +112,5 @@ const {} = toRefs(props);
|
||||
class="w-full h-full bg-linear-to-t from-black to-transparent absolute inset-0 z-15"
|
||||
/>
|
||||
</div>
|
||||
</NuxtLink>
|
||||
</template>
|
||||
@@ -44,9 +44,7 @@ const filters = computed(() => {
|
||||
|
||||
const { data: categories, suspense } = useGetCategories();
|
||||
|
||||
await useAsyncData(async () => {
|
||||
await suspense();
|
||||
});
|
||||
|
||||
const { isPending: productsIsPending } = useGetProducts(filters);
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import useHomeData from "~/composables/api/home/useHomeData";
|
||||
|
||||
const { data: homeData } = useHomeData();
|
||||
const swiper_instance = ref<SwiperClass | null>(null);
|
||||
const isMuted = ref(true);
|
||||
|
||||
// methods
|
||||
|
||||
@@ -37,18 +38,29 @@ const onChange = (swiper: SwiperClass) => {
|
||||
@slide-change="onChange"
|
||||
>
|
||||
<SwiperSlide
|
||||
v-for="slide in homeData!.sliders"
|
||||
v-for="(slide, index) in homeData!.sliders"
|
||||
:key="slide.id"
|
||||
>
|
||||
<div class="relative w-full rounded-200 h-[80svh] overflow-hidden">
|
||||
<template v-if="!!slide.video">
|
||||
<button
|
||||
@click="isMuted = !isMuted"
|
||||
class="transition-all hover:invert cursor-pointer flex-center hover:scale-110 size-[50px] border border-white hover:border-transparent rounded-full absolute z-20 top-10 right-20 bg-black"
|
||||
>
|
||||
<Icon
|
||||
:name="isMuted ? 'bi:volume-mute-fill' : 'bi:volume-up-fill'"
|
||||
class="text-white"
|
||||
size="24px"
|
||||
/>
|
||||
</button>
|
||||
<video
|
||||
v-if="!!slide.video"
|
||||
muted
|
||||
:muted="swiper_instance?.realIndex !== index ? true : isMuted"
|
||||
autoplay
|
||||
loop
|
||||
class="absolute inset-0 size-full object-cover"
|
||||
:src="slide.video"
|
||||
/>
|
||||
</template>
|
||||
<img
|
||||
v-else
|
||||
class="absolute inset-0 size-full object-cover"
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
// types
|
||||
// state
|
||||
|
||||
type Props = {}
|
||||
import useGetArticles from "~/composables/api/blog/useGetArticles";
|
||||
|
||||
// props
|
||||
const page = ref(1);
|
||||
const { data: articles, suspense } = useGetArticles(page);
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const {} = toRefs(props);
|
||||
// ssr
|
||||
|
||||
await suspense();
|
||||
|
||||
</script>
|
||||
|
||||
@@ -23,49 +25,8 @@ const {} = toRefs(props);
|
||||
</Button>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<div class="flex gap-12">
|
||||
<div class="flex-1 flex flex-col gap-12">
|
||||
<BlogPost
|
||||
image="/img/blog-1.jpeg"
|
||||
description="aaasd"
|
||||
title="asd"
|
||||
:comments="2"
|
||||
link="#"
|
||||
date="2020-06-10"
|
||||
tag="asdsa"
|
||||
/>
|
||||
<BlogPost
|
||||
image="/img/blog-2.jpeg"
|
||||
description="aaasd"
|
||||
title="asd"
|
||||
:comments="2"
|
||||
link="#"
|
||||
date="2020-06-10"
|
||||
tag="asdsa"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-[0.8] flex flex-col">
|
||||
<BlogPost
|
||||
image="/img/blog-3.jpeg"
|
||||
description="aaasd"
|
||||
title="asd"
|
||||
:comments="2"
|
||||
link="#"
|
||||
date="2020-06-10"
|
||||
tag="asdsa"
|
||||
variant="sm"
|
||||
/>
|
||||
<BlogPost
|
||||
image="/img/blog-4.jpeg"
|
||||
description="aaasd"
|
||||
title="asd"
|
||||
:comments="2"
|
||||
link="#"
|
||||
date="2020-06-10"
|
||||
tag="asdsa"
|
||||
variant="sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ClientOnly>
|
||||
<ArticlesList :articles="articles!.results" />
|
||||
</ClientOnly>
|
||||
</section>
|
||||
</template>
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -47,16 +56,18 @@ const slides = computed(() => {
|
||||
<Rating />
|
||||
</div>
|
||||
|
||||
<p
|
||||
<div
|
||||
class="py-8 typo-p-md text-slate-500 text-justify [&_a]:text-blue-400 [&_strong]:font-bold [&_u]:text-red-400"
|
||||
v-html="product!.description"
|
||||
v-html="sanitizedProductDescription"
|
||||
/>
|
||||
|
||||
<div class="w-full flex flex-col gap-6 mt-4">
|
||||
|
||||
<RemainQuantity
|
||||
:maxQuantity="product!.in_stock"
|
||||
:quantity="quantity"
|
||||
/>
|
||||
|
||||
<div class="w-full flex gap-3 flex-col">
|
||||
<div class="w-full flex gap-3">
|
||||
<Button class="w-full rounded-full" end-icon="ci:plus">
|
||||
@@ -71,6 +82,7 @@ const slides = computed(() => {
|
||||
همین الان بخر
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<InfoCard />
|
||||
<Share />
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// imports
|
||||
|
||||
import { useQuery } from "@tanstack/vue-query";
|
||||
import { API_ENDPOINTS, QUERY_KEYS } from "~/constants";
|
||||
|
||||
// types
|
||||
|
||||
export type GetArticleResponse = Article;
|
||||
|
||||
const useGetArticle = (id: number | string | undefined) => {
|
||||
|
||||
// state
|
||||
|
||||
const { $axios: axios } = useNuxtApp();
|
||||
|
||||
// methods
|
||||
|
||||
const handleGetArticle = async () => {
|
||||
const { data } = await axios.get<GetArticleResponse>(`${API_ENDPOINTS.blog.article}/${id}`);
|
||||
return data;
|
||||
};
|
||||
|
||||
return useQuery({
|
||||
queryKey: [QUERY_KEYS.article, id],
|
||||
queryFn: () => handleGetArticle()
|
||||
});
|
||||
};
|
||||
|
||||
export default useGetArticle;
|
||||
@@ -5,11 +5,11 @@ import { API_ENDPOINTS, QUERY_KEYS } from "~/constants";
|
||||
|
||||
// types
|
||||
|
||||
export type GetArticlesResponse = ApiPaginated<UserComment>;
|
||||
export type GetArticlesResponse = ApiPaginated<Article>;
|
||||
|
||||
const useGetArticles = (
|
||||
page: Ref<number>,
|
||||
search: Ref<string>
|
||||
search?: Ref<string>
|
||||
) => {
|
||||
|
||||
// state
|
||||
@@ -23,7 +23,7 @@ const useGetArticles = (
|
||||
params: {
|
||||
offset: (page.value * 10) - 10,
|
||||
limit: 10,
|
||||
search: search.value.length > 0 ? search.value : undefined,
|
||||
search: search ? (search.value.length > 0 ? search.value : undefined) : undefined,
|
||||
}
|
||||
});
|
||||
return data;
|
||||
|
||||
@@ -58,7 +58,8 @@ export default defineNuxtConfig({
|
||||
"@nuxt/icon",
|
||||
"reka-ui/nuxt",
|
||||
"@vueuse/nuxt",
|
||||
"@formkit/auto-animate/nuxt"
|
||||
"@formkit/auto-animate/nuxt",
|
||||
'@nuxt/test-utils/module'
|
||||
],
|
||||
|
||||
runtimeConfig: {
|
||||
|
||||
Generated
+2131
-143
File diff suppressed because it is too large
Load Diff
+11
-1
@@ -7,6 +7,7 @@
|
||||
"build": "nuxt build",
|
||||
"dev": "nuxt dev",
|
||||
"dev-o": "nuxt dev -- -o",
|
||||
"test": "vitest",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview",
|
||||
"postinstall": "nuxt prepare"
|
||||
@@ -25,6 +26,8 @@
|
||||
"axios": "^1.7.9",
|
||||
"fast-average-color": "^9.4.0",
|
||||
"gsap": "^3.12.5",
|
||||
"isomorphic-dompurify": "^2.21.0",
|
||||
"masonry-layout": "^4.2.2",
|
||||
"nuxt": "^3.14.1592",
|
||||
"reka-ui": "^1.0.0-alpha.6",
|
||||
"swiper": "^11.1.15",
|
||||
@@ -35,9 +38,16 @@
|
||||
"vue-skeletor": "^1.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/test-utils": "^3.15.4",
|
||||
"@tailwindcss/postcss": "^4.0.0-beta.5",
|
||||
"@types/masonry-layout": "^4.2.8",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"happy-dom": "^16.8.1",
|
||||
"msw": "^2.7.0",
|
||||
"playwright-core": "^1.50.1",
|
||||
"postcss": "^8.4.49",
|
||||
"tailwindcss": "^4.0.0-beta.5"
|
||||
"tailwindcss": "^4.0.0-beta.5",
|
||||
"vitest": "^3.0.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
<script lang="ts" setup>
|
||||
|
||||
// import
|
||||
|
||||
import { sanitize } from "isomorphic-dompurify";
|
||||
import useGetArticle from "~/composables/api/blog/useGetArticle";
|
||||
|
||||
// state
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const id = route.params.id as string | undefined;
|
||||
|
||||
const { data: article, suspense } = useGetArticle(id);
|
||||
|
||||
// ssr
|
||||
|
||||
const response = await suspense();
|
||||
|
||||
if (response.isError) {
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: `Error in categories page prefetch`
|
||||
});
|
||||
}
|
||||
|
||||
// computed
|
||||
|
||||
const sanitizedArticleContent = computed(() => {
|
||||
return sanitize(article.value!.content);
|
||||
});
|
||||
|
||||
const sanitizedArticleSummery = computed(() => {
|
||||
return sanitize(article.value!.summery);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="w-full h-[80svh] rounded-3xl relative overflow-hidden">
|
||||
<img class="absolute object-cover size-full" :alt="article!.title" :src="article!.cover_image" />
|
||||
<div class="absolute bg-linear-to-t from-black/75 to-transparent size-full" />
|
||||
<div class="absolute pl-10 right-10 bottom-10 flex flex-col gap-6">
|
||||
<h1 class="typo-h-4 text-white pl-8">
|
||||
{{ article!.title }}
|
||||
</h1>
|
||||
|
||||
<div
|
||||
class="typo-p-lg text-slate-200 mb-6 text-justify w-[70%]"
|
||||
v-html="sanitizedArticleSummery"
|
||||
/>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
<div
|
||||
class="w-fit pr-2 pl-5 h-[50px] rounded-full flex items-center justify-center gap-3 bg-white">
|
||||
<div
|
||||
class="relative flex items-center justify-center rounded-full overflow-hidden size-[35px]">
|
||||
<img
|
||||
class="size-full object-cover absolute"
|
||||
:src="article!.author.profile_photo"
|
||||
alt="article-author"
|
||||
/>
|
||||
</div>
|
||||
<span class="typo-label-sm">
|
||||
{{ article!.author.full_name }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="w-fit pr-4 pl-5 h-[50px] rounded-full flex items-center justify-center gap-2 border-[1.5px] border-white text-white">
|
||||
<span class="typo-label-sm mt-0.5">
|
||||
دسته بندی موبایل
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
<div
|
||||
class="w-fit pr-4 pl-5 h-[50px] rounded-full flex items-center justify-center gap-2 border-[1.5px] border-white text-white">
|
||||
<Icon name="ci:calendar" size="24px" class="**:stroke-white" />
|
||||
<span class="typo-label-sm mt-0.5">
|
||||
۲۴ مهر 1403
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="w-fit pr-4 pl-5 h-[50px] rounded-full flex items-center justify-center gap-2 border-[1.5px] border-white text-white">
|
||||
<Icon name="ci:eye-open" size="24px" class="**:stroke-white" />
|
||||
<span class="typo-label-sm mt-0.5">
|
||||
{{ article!.views }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-4 mt-8">
|
||||
|
||||
<div
|
||||
class="p-8 flex-1 text-zinc-800 flex flex-col gap-6 [&_p,ul]:text-zinc-500 [&_h1]:typo-h-4 [&_h2]:typo-h-5 [&_h3]:typo-h-6 [&_p]:typo-p-md [&_ul]:list-disc [&_ul]:typo-p-md [&_ul]:space-y-2"
|
||||
v-html="sanitizedArticleContent"
|
||||
/>
|
||||
|
||||
<aside class="mt-8 p-8 h-fit bg-slate-100 w-[400px] sticky top-4 rounded-3xl">
|
||||
asdsa
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
@@ -3,6 +3,7 @@
|
||||
// import
|
||||
|
||||
import useGetArticles from "~/composables/api/blog/useGetArticles";
|
||||
import ArticlesList from "~/components/articles/ArticlesList.vue";
|
||||
|
||||
// state
|
||||
|
||||
@@ -14,8 +15,6 @@ const { data: articles, suspense } = useGetArticles(page, debouncedSearch);
|
||||
|
||||
// ssr
|
||||
|
||||
await useAsyncData(async () => {
|
||||
|
||||
const response = await suspense();
|
||||
|
||||
if (response.isError) {
|
||||
@@ -24,7 +23,7 @@ await useAsyncData(async () => {
|
||||
statusMessage: `Error in categories page prefetch`
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -50,50 +49,13 @@ await useAsyncData(async () => {
|
||||
</template>
|
||||
</Input>
|
||||
</div>
|
||||
<div class="flex gap-12">
|
||||
<div class="flex-1 flex flex-col gap-12">
|
||||
<BlogPost
|
||||
image="/img/blog-1.jpeg"
|
||||
description="aaasd"
|
||||
title="asd"
|
||||
:comments="2"
|
||||
link="#"
|
||||
date="2020-06-10"
|
||||
tag="asdsa"
|
||||
/>
|
||||
<BlogPost
|
||||
image="/img/blog-2.jpeg"
|
||||
description="aaasd"
|
||||
title="asd"
|
||||
:comments="2"
|
||||
link="#"
|
||||
date="2020-06-10"
|
||||
tag="asdsa"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-[0.8] flex flex-col">
|
||||
<BlogPost
|
||||
image="/img/blog-3.jpeg"
|
||||
description="aaasd"
|
||||
title="asd"
|
||||
:comments="2"
|
||||
link="#"
|
||||
date="2020-06-10"
|
||||
tag="asdsa"
|
||||
variant="sm"
|
||||
/>
|
||||
<BlogPost
|
||||
image="/img/blog-4.jpeg"
|
||||
description="aaasd"
|
||||
title="asd"
|
||||
:comments="2"
|
||||
link="#"
|
||||
date="2020-06-10"
|
||||
tag="asdsa"
|
||||
variant="sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- This is for masonry js package -->
|
||||
|
||||
<ClientOnly>
|
||||
<ArticlesList :articles="articles!.results" />
|
||||
</ClientOnly>
|
||||
|
||||
<div class="w-full flex-center pt-24 pb-10">
|
||||
<Pagination :items="[]" :total="100" />
|
||||
</div>
|
||||
|
||||
@@ -31,8 +31,6 @@ const filteredCategories = computed(() => {
|
||||
|
||||
// ssr
|
||||
|
||||
await useAsyncData(async () => {
|
||||
|
||||
const response = await suspense();
|
||||
|
||||
if (response.isError) {
|
||||
@@ -41,7 +39,6 @@ await useAsyncData(async () => {
|
||||
statusMessage: `Error in categories page prefetch`
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@@ -10,16 +10,14 @@ const { suspense } = useHomeData();
|
||||
|
||||
// ssr
|
||||
|
||||
await useAsyncData(async () => {
|
||||
const response = await suspense();
|
||||
|
||||
if (response.isError) {
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: `Landing error : ${response.error.message}`,
|
||||
})
|
||||
}
|
||||
statusMessage: `Landing error : ${response.error.message}`
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ const { suspense : suspenseComments} = useGetComments(id, page);
|
||||
|
||||
// ssr
|
||||
|
||||
await useAsyncData(async () => {
|
||||
const productResponse = await suspenseProduct();
|
||||
const commentsResponse = await suspenseComments();
|
||||
|
||||
@@ -24,7 +23,6 @@ await useAsyncData(async () => {
|
||||
statusMessage: `error : product ${id} prefetch error`
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@@ -158,6 +158,7 @@ const resetForm = () => {
|
||||
class="flex items-center gap-2 w-full"
|
||||
>
|
||||
<Input
|
||||
data-testid="phone-input"
|
||||
class="w-full"
|
||||
v-model="loginInfo.phone"
|
||||
placeholder="9380123456"
|
||||
@@ -185,6 +186,7 @@ const resetForm = () => {
|
||||
/>
|
||||
|
||||
<Button
|
||||
data-testid="send-otp-code-button"
|
||||
v-if="!showOtp"
|
||||
class="rounded-full w-full mt-4"
|
||||
type="submit"
|
||||
|
||||
Vendored
+19
@@ -43,6 +43,25 @@ declare global {
|
||||
meta_rating: number | null;
|
||||
};
|
||||
|
||||
type Article = {
|
||||
"id": number,
|
||||
"title": string,
|
||||
"slug": string,
|
||||
"content": string,
|
||||
"summery": string,
|
||||
"created_at": string,
|
||||
"updated_at": string,
|
||||
"cover_image": string,
|
||||
"views": number,
|
||||
"meta_description": string,
|
||||
"meta_keywords": string,
|
||||
"author": {
|
||||
"full_name": string,
|
||||
"profile_photo": string
|
||||
},
|
||||
"category": number
|
||||
}
|
||||
|
||||
type UserComment = {
|
||||
id: number;
|
||||
content: string;
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { defineVitestConfig } from "@nuxt/test-utils/config";
|
||||
|
||||
export default defineVitestConfig({
|
||||
test: {
|
||||
environment: "nuxt",
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user