diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml
index 255860e..6c287c1 100644
--- a/.github/workflows/deploy.yaml
+++ b/.github/workflows/deploy.yaml
@@ -31,5 +31,5 @@ jobs:
script: |
cd /root/hshop/
docker compose down
- docker compose build --no-cache
+ docker compose build
docker compose up -d
\ No newline at end of file
diff --git a/backend/core/settings/base.py b/backend/core/settings/base.py
index 5c7c324..9572ade 100644
--- a/backend/core/settings/base.py
+++ b/backend/core/settings/base.py
@@ -240,15 +240,20 @@ AWS_S3_OBJECT_PARAMETERS = {
# ==============================================================================
AZ_IRANIAN_BANK_GATEWAYS = {
- 'GATEWAYS': {
- 'ZARINPAL': {
- 'MERCHANT_CODE': 'Merchant-Code',
- 'SANDBOX': True,
- }
+ "GATEWAYS": {
+ "ZARINPAL": {
+ "MERCHANT_CODE": "",
+ "SANDBOX": 0,
+ },
},
- 'IS_SAMPLE_FORM_ENABLE': True,
- 'DEFAULT_BANK': 'ZARINPAL',
- 'CURRENCY': 'IRR',
- 'TRACKING_CODE_QUERY_PARAM': 'tc',
- 'BANK_PRIORITIES': ['ZARINPAL'],
+ "IS_SAMPLE_FORM_ENABLE": True,
+ "DEFAULT": "ZARINPAL",
+ "CURRENCY": "IRT",
+ "TRACKING_CODE_QUERY_PARAM": "tc",
+ "TRACKING_CODE_LENGTH": 16,
+ "SETTING_VALUE_READER_CLASS": "azbankgateways.readers.DefaultReader",
+ "BANK_PRIORITIES": [
+ "ZARINPAL",
+ ],
+ "IS_SAFE_GET_GATEWAY_PAYMENT": False # better to be True
}
\ No newline at end of file
diff --git a/backend/core/urls.py b/backend/core/urls.py
index 9db83f5..3e46822 100644
--- a/backend/core/urls.py
+++ b/backend/core/urls.py
@@ -8,6 +8,9 @@ from product import views
from account.views import CustomTokenObtainPairView
from home.views import HomeView
from .views import FakeAdminLoginView
+from azbankgateways.urls import az_bank_gateways_urls
+
+admin.autodiscover()
urlpatterns = [
@@ -29,6 +32,7 @@ urlpatterns = [
path('blogs/', include('blog.urls')),
path('order/', include('order.urls')),
path('home/', include('home.urls')),
+ path("bankgateways/", az_bank_gateways_urls()),
path('', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
]
diff --git a/backend/order/migrations/0015_payment.py b/backend/order/migrations/0015_payment.py
new file mode 100644
index 0000000..fd65876
--- /dev/null
+++ b/backend/order/migrations/0015_payment.py
@@ -0,0 +1,24 @@
+# Generated by Django 5.1.2 on 2025-03-13 16:26
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('order', '0014_alter_orderitemmodel_price'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Payment',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('amount', models.PositiveIntegerField()),
+ ('status', models.CharField(default='Pending', max_length=50)),
+ ('tracking_code', models.CharField(blank=True, max_length=100)),
+ ('bank_type', models.CharField(max_length=100)),
+ ('created_at', models.DateTimeField(auto_now_add=True)),
+ ],
+ ),
+ ]
diff --git a/backend/order/migrations/0016_rename_payment_paymentmodel.py b/backend/order/migrations/0016_rename_payment_paymentmodel.py
new file mode 100644
index 0000000..37cbc3b
--- /dev/null
+++ b/backend/order/migrations/0016_rename_payment_paymentmodel.py
@@ -0,0 +1,17 @@
+# Generated by Django 5.1.2 on 2025-03-13 16:28
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('order', '0015_payment'),
+ ]
+
+ operations = [
+ migrations.RenameModel(
+ old_name='Payment',
+ new_name='PaymentModel',
+ ),
+ ]
diff --git a/backend/order/models.py b/backend/order/models.py
index a37a45f..69be701 100644
--- a/backend/order/models.py
+++ b/backend/order/models.py
@@ -120,3 +120,13 @@ class OrderItemModel(models.Model):
def __str__(self):
return f'({self.product}) - ({self.order.user})'
+
+#TODO complate this shit
+class PaymentModel(models.Model):
+ amount = models.PositiveIntegerField()
+ status = models.CharField(max_length=50, default='Pending')
+ tracking_code = models.CharField(max_length=100, blank=True)
+ bank_type = models.CharField(max_length=100)
+ created_at = models.DateTimeField(auto_now_add=True)
+ def __str__(self):
+ return 'payment'
\ No newline at end of file
diff --git a/backend/order/serializers.py b/backend/order/serializers.py
index e0b3b2c..e4e5724 100644
--- a/backend/order/serializers.py
+++ b/backend/order/serializers.py
@@ -14,7 +14,7 @@ class ProductVariantSerialzier(serializers.ModelSerializer):
category = serializers.SerializerMethodField()
class Meta:
model = ProductVariant
- fields = ['title', 'product_attributes', 'in_stock', 'price', 'discount', 'color', 'image', 'discount_amount', 'category', 'final_price']
+ fields = ['id', 'title', 'product_attributes', 'in_stock', 'price', 'discount', 'color', 'image', 'discount_amount', 'category', 'final_price']
def get_discount_amount(self, obj):
discount_amount = int(obj.price * (obj.discount / 100))
@@ -95,11 +95,10 @@ class CartSerializer(serializers.ModelSerializer):
class OrderSerializer(serializers.ModelSerializer):
count = serializers.SerializerMethodField()
images = serializers.SerializerMethodField()
- address = UserAddressSerializer()
- items = OrderItemSerailzier(many=True)
+ order_id = serializers.SerializerMethodField()
class Meta:
model = OrderModel
- fields = ['address', 'created_at', 'items', 'status', 'discount_code', "images", "count", "id"]
+ fields = ['created_at', 'status', "images", "count", "id", 'final_price', 'order_id']
def get_count(self, obj):
return obj.items.all().count()
@@ -110,4 +109,6 @@ class OrderSerializer(serializers.ModelSerializer):
if (image := item.product.images.all().first()) else None
for item in obj.items.all()[:3]
]
- return filter(lambda x: x is not None, image_list)
\ No newline at end of file
+ return filter(lambda x: x is not None, image_list)
+ def get_order_id(self, obj):
+ return obj.id + 1000
\ No newline at end of file
diff --git a/backend/order/urls.py b/backend/order/urls.py
index d29e3b5..c439005 100644
--- a/backend/order/urls.py
+++ b/backend/order/urls.py
@@ -2,6 +2,7 @@ from django.conf.urls.static import static
from django.contrib import admin
from django.urls import path, include
from .views import CartItemViews, CartView, OrderlistView, CartItemClear, ApplyDiscountView
+from .views import PaymentView, callback_view
urlpatterns = [
path('all', OrderlistView.as_view(), name='order-list'),
@@ -9,6 +10,6 @@ urlpatterns = [
path('cart/discount', ApplyDiscountView.as_view()),
path('cart/all', CartItemClear.as_view()),
path('cart/item/', CartItemViews.as_view(), name='change-item-cart'),
- # path('payment', CartView.as_view()),
- # path('', CartView.as_view()),
+ path('payment', PaymentView.as_view(), name='payment'),
+ path('callback', callback_view, name='callback-gateway'),
]
diff --git a/backend/order/views.py b/backend/order/views.py
index ebfec28..468138b 100644
--- a/backend/order/views.py
+++ b/backend/order/views.py
@@ -7,13 +7,17 @@ from rest_framework.permissions import IsAuthenticated
from .serializers import *
# from cart.models import
from rest_framework import status
-from .models import OrderItemModel, OrderModel, DiscountCode
+from .models import OrderItemModel, OrderModel, DiscountCode, PaymentModel
from .permissons import CanDeleteCartItemPermissions
+from azbankgateways import bankfactories, models as bank_models
+from azbankgateways.exceptions import AZBankGatewaysException
+from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiTypes
+from utils.pagination import StructurePagination
try:
pass
except DiscountNotAvailableError:
pass
-
+from django.urls import reverse
"""
add post
@@ -105,69 +109,110 @@ class CartView(APIView):
class OrderlistView(APIView):
permission_classes = [IsAuthenticated]
serializer_class = OrderSerializer
+ pagination_class = StructurePagination
+ @extend_schema(
+ parameters=[
+ OpenApiParameter(
+ name="limit",
+ description="لیمیتش",
+ required=False,
+ type=OpenApiTypes.INT,
+ ),
+ OpenApiParameter(
+ name="offset",
+ description="افستش",
+ required=False,
+ type=OpenApiTypes.INT,
+ ),
+ OpenApiParameter(
+ name="status",
+ description=(
+ "['CART', 'ADMIN_PENDING', 'PENDING', 'POSTED', 'RECEIVED', 'CANCELED', 'BACK']"
+ ),
+ required=False,
+ type=OpenApiTypes.STR,
+ ),
+ OpenApiParameter(
+ name="sort",
+ description=(
+ "Sort results by one of the following fields:\n"
+ "['created_at', '-created_at', 'final_price', '-final_price']"
+ "\nPrefix with `-` for descending order."
+ ),
+ required=False,
+ type=OpenApiTypes.STR,
+ ),
+ ]
+ )
def get(self, request):
user = request.user
orders = OrderModel.objects.filter(user=user).exclude(status="CART")
- orders_ser = self.serializer_class(instance=orders, many=True, context={'request': request})
- return Response(orders_ser.data, status=status.HTTP_200_OK)
+ status_filter = request.query_params.get("status", None)
+ sort = request.query_params.get('sort', None)
+ if status_filter in ['CART', 'ADMIN_PENDING', 'PENDING', 'POSTED', 'RECEIVED', 'CANCELED', 'BACK']:
+ orders.filter(status=status_filter)
+ if sort:
+ if sort not in ['created_at', '-created_at', 'final_price', '-final_price']:
+ return Response({'detail': 'پارامتر sort اشتباه است'}, status=status.HTTP_400_BAD_REQUEST)
+ orders = orders.order_by(sort)
+ paginator = self.pagination_class()
+ paginated_orders = paginator.paginate_queryset(orders, request)
+ orders_ser = self.serializer_class(instance=paginated_orders, many=True, context={'request': request})
+ return paginator.get_paginated_response(orders_ser.data)
-# from rest_framework.views import APIView
-# from rest_framework.response import Response
-# from rest_framework import status
-# from azbankgateways import bankfactories, models as bank_models
-# class PaymentView(APIView):
-# def post(self, request):
-# amount = request.data.get('amount')
-# user = request.user
+class PaymentView(APIView):
+ def post(self, request):
+ amount = 10000000
+ user_mobile_number = request.user.phone
+ factory = bankfactories.BankFactory()
+ try:
+ bank = (
+ factory.create(bank_models.BankType.ZARINPAL)
+ )
+ bank.set_request(request)
+ bank.set_amount(amount)
-# payment = Payment.objects.create(amount=amount, bank_type='ZARINPAL')
+ bank.set_client_callback_url(request.build_absolute_uri(reverse("callback-gateway")))
+ bank.set_mobile_number(user_mobile_number)
-
-# factory = bankfactories.ZarinpalBankFactory()
-# try:
-# bank = factory.create(
-# amount=amount,
-# user=user,
-# callback_url='http://.com/callback/',
-# reference_model=payment,
-# )
-# bank.ready()
-# return Response({'gateway_url': bank.redirect_url}, status=status.HTTP_200_OK)
-# except Exception as e:
-# return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)
+ bank_record = bank.ready()
+ return Response(bank.redirect_gateway().url)
+ except AZBankGatewaysException as e:
+ print(e)
+ return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)
+ return Response({'gateway_url': bank.redirect_url}, status=status.HTTP_200_OK)
+
+
-# from django.views.decorators.csrf import csrf_exempt
-# from rest_framework.decorators import api_view
-# from rest_framework.response import Response
-# from azbankgateways import bankfactories, models as bank_models
+from django.views.decorators.csrf import csrf_exempt
+from rest_framework.decorators import api_view
+from rest_framework.response import Response
+from azbankgateways import bankfactories, models as bank_models
-# @csrf_exempt
-# @api_view(['POST'])
-# def callback_view(request):
-# tracking_code = request.POST.get('tracking_code')
-# payment_id = request.POST.get('payment_id')
+@csrf_exempt
+@api_view(['POST'])
+def callback_view(request):
+ tracking_code = request.GET.get(settings.TRACKING_CODE_QUERY_PARAM, None)
+ if not tracking_code:
+ logging.debug("این لینک معتبر نیست.")
+ raise Http404
-# payment = Payment.objects.get(id=payment_id)
-# bank_type = payment.bank_type
+ try:
+ bank_record = bank_models.Bank.objects.get(tracking_code=tracking_code)
+ except bank_models.Bank.DoesNotExist:
+ logging.debug("این لینک معتبر نیست.")
+ raise Http404
-
-# factory = bankfactories.BankFactory.get_bank(bank_type)
-# try:
-# result = factory.verify_transaction(tracking_code)
-# if result.is_success:
-# payment.status = 'Paid'
-# payment.tracking_code = tracking_code
-# payment.save()
-# return Response({'status': 'Payment successful'})
-# else:
-# payment.status = 'Failed'
-# payment.save()
-# return Response({'status': 'Payment failed'})
-# except Exception as e:
-# return Response({'error': str(e)})
\ No newline at end of file
+ if bank_record.is_success:
+ return HttpResponse("پرداخت با موفقیت انجام شد.")
+
+
+ return HttpResponse(
+ "پرداخت با شکست مواجه شده است. اگر پول کم شده است ظرف مدت ۴۸ ساعت پول به حساب شما بازخواهد گشت."
+ )
\ No newline at end of file
diff --git a/backend/product/serializers.py b/backend/product/serializers.py
index 68ac5a6..cd65826 100644
--- a/backend/product/serializers.py
+++ b/backend/product/serializers.py
@@ -2,7 +2,7 @@ from .models import *
from rest_framework import serializers
from django.utils import timezone
from datetime import timedelta
-
+from django.contrib.auth.models import AnonymousUser
@@ -49,6 +49,7 @@ class ProductVariantSerialzier(serializers.ModelSerializer):
in_pack_items = InPackItemsSerialzier(many=True)
images = ProductImageSerailizer(many=True)
details = ProductDetailSerializer(many=True, read_only=True)
+ cart_quantity = serializers.SerializerMethodField()
class Meta:
model = ProductVariant
exclude = ('min_price', 'sell', 'currency', 'product', 'input_price')
@@ -60,6 +61,11 @@ class ProductVariantSerialzier(serializers.ModelSerializer):
if view_type == 'list':
self.fields.pop('in_pack_items', None)
+ def get_cart_quantity(self, obj):
+ request = self.context.get('request')
+ if not request or not request.user.is_authenticated:
+ return 0
+ return 1
@@ -135,7 +141,7 @@ class DynamicProductSerializer(serializers.ModelSerializer):
many=True,
context={
'view_type': 'list',
- 'dollor_price': self.context.get('dollor_price')
+ 'request': self.context.get('request')
}
)
return serializer.data
diff --git a/backend/product/views.py b/backend/product/views.py
index ea29436..79b1c60 100644
--- a/backend/product/views.py
+++ b/backend/product/views.py
@@ -51,7 +51,7 @@ class AllCategories(APIView):
class ProductView(APIView):
serializer_class = DynamicProductSerializer
permission_classes = [AllowAny]
- authentication_classes = []
+ # authentication_classes = []
def get(self, request, pk):
product = get_object_or_404(ProductModel, id=pk)
product_ser = self.serializer_class(instance=product, many=False, context={'request': request, 'view_type': 'instance'})
diff --git a/frontend/assets/css/tailwind.css b/frontend/assets/css/tailwind.css
index 9420e4f..465c6b0 100644
--- a/frontend/assets/css/tailwind.css
+++ b/frontend/assets/css/tailwind.css
@@ -9,10 +9,12 @@
@import "./fonts/morabba.css";
@import "./fonts/yekan-bakh.css";
-@theme {
+:root {
/* CONTAINER */
-
--app-container-padding: 1rem;
+}
+
+@theme {
/* COLORS */
--color-slate-50: hsl(210, 40%, 98%);
@@ -120,7 +122,6 @@
--font-morabba: "Morabba", "sans-serif";
/* BREAKPOINTS */
- --breakpoint-3xl: 1700px;
--breakpoint-2xs: 400px;
--breakpoint-xs: 480px;
--breakpoint-sm: 640px;
@@ -128,6 +129,7 @@
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
--breakpoint-2xl: 1536px;
+ --breakpoint-3xl: 1700px;
/* ANIMATIONS */
--animate-marquee: marquee 20s linear infinite;
diff --git a/frontend/components/articles/ArticlesList.vue b/frontend/components/articles/ArticlesList.vue
index 0e832fa..bbb5c47 100644
--- a/frontend/components/articles/ArticlesList.vue
+++ b/frontend/components/articles/ArticlesList.vue
@@ -29,6 +29,7 @@ const isMobile = useMediaQuery('(max-width: 1024px)');
:id="article.id"
:date="article.created_at"
:variant="isMobile ? 'sm' : 'lg'"
+ :category="article.category"
tag="تگ ندارد"
/>
diff --git a/frontend/components/global/BlogPost.vue b/frontend/components/global/BlogPost.vue
index b6df274..5e87211 100644
--- a/frontend/components/global/BlogPost.vue
+++ b/frontend/components/global/BlogPost.vue
@@ -2,6 +2,8 @@
// types
+import { usePersianTimeAgo } from "~/composables/global/usePersianTimeAgo";
+
type Props = {
id: number;
tag: string;
@@ -9,15 +11,22 @@ type Props = {
title: string;
description: string;
variant?: "sm" | "lg";
+ category: SubCategory;
image: string,
}
// props
-withDefaults(defineProps(), {
+const props = withDefaults(defineProps(), {
variant: "lg"
});
+const { date } = toRefs(props);
+
+// state
+
+const createdAt = usePersianTimeAgo(new Date(date.value));
+
@@ -32,7 +41,7 @@ withDefaults(defineProps(), {
v-if="variant === 'lg'"
class="bg-success-500 absolute left-6 top-6 z-20"
>
- اسپیکر
+ {{ category.name }}
(), {
- اسپیکر
+ {{ category.name }}
(), {
class="**:stroke-white size-3 md:size-3.5"
/>
- ۳۱ مهر ۱۴۰۳
+ {{ createdAt }}
@@ -89,7 +98,8 @@ withDefaults(defineProps(), {
v-if="variant === 'sm'"
class="text-white typo-p-xs max-sm:!leading-[175%] sm:typo-p-sm md:typo-p-md line-clamp-3"
>
- تا با نرم افزارها شناخت بیشتری را برای طراحان رایانه ای علی الخصوص طراحان خلاقی، و فرهنگ پیشرو در زبان فارسی ایجاد کرد، در این صورت می توان امید داشت که تمام و دشواری موجود در ارائه راهکارها
+ تا با نرم افزارها شناخت بیشتری را برای طراحان رایانه ای علی الخصوص طراحان خلاقی، و فرهنگ پیشرو
+ در زبان فارسی ایجاد کرد، در این صورت می توان امید داشت که تمام و دشواری موجود در ارائه راهکارها
diff --git a/frontend/components/global/Footer.vue b/frontend/components/global/Footer.vue
index b191475..2a5c925 100644
--- a/frontend/components/global/Footer.vue
+++ b/frontend/components/global/Footer.vue
@@ -11,6 +11,8 @@
autoplay
muted
loop
+ playsinline
+ webkit-playsinline
class="size-[150px] lg:size-[220px] rounded-full"
/>
@@ -19,13 +21,13 @@
-
+
-
+
با ما در ارتباط باشید...
-
+
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. چاپگرها
@@ -60,11 +62,11 @@
-
-
+
+
لینک های مفید
-
+
از طراحان گرافیک است
تولید نامفهوم
ستون و سطرآنچنان که لازم
@@ -73,11 +75,11 @@
-
-
+
+
لینک های مفید
-
+
از طراحان گرافیک است
تولید نامفهوم
ستون و سطرآنچنان که لازم
@@ -86,11 +88,11 @@
-
-
+
+
لینک های مفید
-
+
از طراحان گرافیک است
تولید نامفهوم
ستون و سطرآنچنان که لازم
diff --git a/frontend/components/global/product-detail/QuantityCounter.vue b/frontend/components/global/product-detail/QuantityCounter.vue
index 388d0d1..8cbe6f2 100644
--- a/frontend/components/global/product-detail/QuantityCounter.vue
+++ b/frontend/components/global/product-detail/QuantityCounter.vue
@@ -58,7 +58,7 @@ const onInput = (e: any) => {
diff --git a/frontend/components/global/product-detail/Slider.vue b/frontend/components/global/product-detail/Slider.vue
index 41d0e00..50eb7d2 100644
--- a/frontend/components/global/product-detail/Slider.vue
+++ b/frontend/components/global/product-detail/Slider.vue
@@ -1,6 +1,9 @@
{
/>
-
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/global/product/Comment.vue b/frontend/components/global/product/Comment.vue
index 46fd660..23f86e9 100644
--- a/frontend/components/global/product/Comment.vue
+++ b/frontend/components/global/product/Comment.vue
@@ -21,13 +21,13 @@ const formattedDate = useDateFormat(date.value, "YYYY/MM/DD");
-
-
+
+
-
+
خیلی محصول خوبی بودددد
-
+
{{ username }}
در
{{ formattedDate }}
diff --git a/frontend/components/global/product/StickyCard.vue b/frontend/components/global/product/StickyCard.vue
index eee0c11..2d49b77 100644
--- a/frontend/components/global/product/StickyCard.vue
+++ b/frontend/components/global/product/StickyCard.vue
@@ -32,7 +32,8 @@ const { picture, price, title, color } = toRefs(props);
- افزودن به سبد
+ افزودن
+ به سبد
\ No newline at end of file
diff --git a/frontend/components/home/Hero.vue b/frontend/components/home/Hero.vue
index 48937f0..684a48a 100644
--- a/frontend/components/home/Hero.vue
+++ b/frontend/components/home/Hero.vue
@@ -187,6 +187,8 @@ onUnmounted(() => {
:id="`slide-video-${index}`"
:muted="swiper_instance?.realIndex !== index ? true : isMuted"
loop
+ playsinline
+ webkit-playsinline
class="slide-video absolute inset-0 size-full object-cover brightness-90"
:src="slide.video"
/>
@@ -227,11 +229,12 @@ onUnmounted(() => {
-
- {{ slide.description }}
-
+
+ {{ slide.description }}
+
+ class="max-sm:hidden max-lg:typo-label-xs invert rounded-full hover:bg-transparent"
+ >
مشاهده
diff --git a/frontend/components/home/Preview.vue b/frontend/components/home/Preview.vue
index a82bf06..e2fdae1 100644
--- a/frontend/components/home/Preview.vue
+++ b/frontend/components/home/Preview.vue
@@ -69,6 +69,8 @@ watch(
v-else
autoplay
muted
+ playsinline
+ webkit-playsinline
src="/video/vid-3.mp4"
class="select-none absolute size-full object-cover brightness-[95%]"
/>
@@ -86,6 +88,8 @@ watch(
v-else
autoplay
muted
+ playsinline
+ webkit-playsinline
src="/video/vid-3.mp4"
class="overlay-image select-none absolute object-cover size-full brightness-[95%]"
/>
diff --git a/frontend/components/product/ProductComments.vue b/frontend/components/product/ProductComments.vue
index 1c0c592..ebb75f5 100644
--- a/frontend/components/product/ProductComments.vue
+++ b/frontend/components/product/ProductComments.vue
@@ -24,7 +24,7 @@ const { mutateAsync: createComment, isPending: isCreateCommentPending } =
const submitComment = async () => {
if (userComment.value.length > 3) {
await createComment({
- content: userComment.value,
+ content: userComment.value
});
userComment.value = "";
@@ -36,11 +36,11 @@ const submitComment = async () => {
-
+
-
نظرات کاربران
+
نظرات کاربران
diff --git a/frontend/components/product/ProductDetails.vue b/frontend/components/product/ProductDetails.vue
index bf267d5..e0ca566 100644
--- a/frontend/components/product/ProductDetails.vue
+++ b/frontend/components/product/ProductDetails.vue
@@ -9,21 +9,21 @@ const { selectedVariant } = inject("productVariant") as ProductVariantProvideTyp
-
+
- جزيات محصول
+ جزيات محصول
-
-
+
+
-
+
-
داخل جعبه چیه؟
+
داخل جعبه چیه؟
+
// import
import useGetProduct from "~/composables/api/product/useGetProduct";
@@ -20,9 +21,7 @@ const selectedColor = ref(product.value!.colors[0]);
// provide / inject
-const { selectedVariant, changeSelectedVariant } = inject(
- "productVariant"
-) as ProductVariantProvideType;
+const { selectedVariant, changeSelectedVariant } = inject("productVariant") as ProductVariantProvideType;
// computed
@@ -32,79 +31,105 @@ const sanitizedProductDescription = computed(() => {
// watch
-watch(
- () => selectedVariantId.value,
- (newId) => {
- const newVariant = product.value!.variants.find(
- (variant) => variant.id === newId
- )!;
- changeSelectedVariant(newVariant);
- }
-);
+watch(() => selectedVariantId.value, (newId) => {
+ const newVariant = product.value!.variants.find(variant => variant.id === newId)!;
+ changeSelectedVariant(newVariant);
+});
-watch(
- () => selectedColor.value,
- (newValue) => {
- const filteredVariants = product.value!.variants.filter(
- (v) => v.color === newValue
- );
- selectedVariantId.value = filteredVariants[0].id;
- selectedVariant.value = filteredVariants[0];
- },
- {
- immediate: true,
- }
-);
+watch(() => selectedColor.value, (newValue) => {
+ const filteredVariants = product.value!.variants.filter(v => v.color === newValue);
+ selectedVariantId.value = filteredVariants[0].id;
+ selectedVariant.value = filteredVariants[0];
+}, {
+ immediate: true
+});
+
+watch(() => selectedVariant.value, (newValue) => {
+ selectedQuantity.value = 1;
+ selectedSlide.value = newValue.images[0].id;
+});
-watch(
- () => selectedVariant.value,
- (newValue) => {
- selectedQuantity.value = 1;
- selectedSlide.value = newValue.images[0].id;
- }
-);
-
-
-
-
سامسونگ
-
{{ product!.name }}
+
+
+
{{ product!.category.name }}
+
{{ product!.name }}
{{ selectedVariant.price }}
- {{
- selectedVariant.discount > 0
- ? selectedVariant.price
- : selectedVariant.price
- }}
+ {{ selectedVariant.discount > 0 ? selectedVariant.price : selectedVariant.price }}
-
+
{{ selectedVariant.discount }}
درصد تخفیف
+
+
+
+
+ {{ product!.category.name }}
+
+
{{ product!.name }}
+
+
+
+
+ {{ selectedVariant.price }}
+
+
+ {{ selectedVariant.discount > 0 ? selectedVariant.price : selectedVariant.price }}
+
+
+
+
+ {{ selectedVariant.discount }}
+ درصد
+ تخفیف
+
+
+
+
+
+
+
+
+
-
تنوع رنگی :
+
+ تنوع رنگی :
+
@@ -128,11 +155,7 @@ watch(
0
- ? (selectedVariantId = variant.id)
- : undefined
- "
+ @click="variant.in_stock > 0 ? selectedVariantId = variant.id : undefined"
v-for="variant in product!.variants.filter(p => p.color === selectedColor)"
:key="variant.id"
:variantDetail="variant"
@@ -141,28 +164,26 @@ watch(
+
-
-
-
- افزودن به سبد خرید
-
-
-
-
- همین الان بخر
+
+
+ افزودن به سبد خرید
+
+
+
diff --git a/frontend/components/product/ProductVideo.vue b/frontend/components/product/ProductVideo.vue
index 1876fb3..1570b04 100644
--- a/frontend/components/product/ProductVideo.vue
+++ b/frontend/components/product/ProductVideo.vue
@@ -21,17 +21,21 @@ const { selectedVariant } = inject("productVariant") as ProductVariantProvideTyp
-
+
+
+
diff --git a/frontend/plugins/axios.ts b/frontend/plugins/axios.ts
index 6218f29..2ac856b 100644
--- a/frontend/plugins/axios.ts
+++ b/frontend/plugins/axios.ts
@@ -5,12 +5,10 @@ import Logger from "~/tools/logger";
export default defineNuxtPlugin(() => {
const config = useRuntimeConfig();
- const { token, logout } = useAuth();
+ const { token } = useAuth();
const axios = axiosOriginal.create({
- timeout: 30000,
- timeoutErrorMessage: "فرآیند بیش از حد انتظار طول کشید",
- baseURL: config.public.API_BASE_URL,
+ baseURL: config.public.API_BASE_URL
});
axios.interceptors.request.use((config) => {
@@ -30,7 +28,7 @@ export default defineNuxtPlugin(() => {
(response) => {
return response;
},
- async function (error) {
+ async function(error) {
await Logger.axiosErrorLog(error);
// if (error.status === 401) {
@@ -43,7 +41,7 @@ export default defineNuxtPlugin(() => {
return {
provide: {
- axios,
- },
+ axios
+ }
};
});
diff --git a/frontend/public/sw.js b/frontend/public/sw.js
index d8942a8..3b750c8 100644
--- a/frontend/public/sw.js
+++ b/frontend/public/sw.js
@@ -5,7 +5,7 @@ precacheAndRoute(self.__WB_MANIFEST);
// Version
-const VERSION = "1.0.2";
+const VERSION = "1.0.4";
// Service Worker Installation
self.addEventListener("install", (event) => {
diff --git a/frontend/types/global.d.ts b/frontend/types/global.d.ts
index d1a0086..46e5ed8 100644
--- a/frontend/types/global.d.ts
+++ b/frontend/types/global.d.ts
@@ -84,7 +84,7 @@ declare global {
meta_description: string | null;
meta_keywords: string | null;
meta_rating: number;
- category: number;
+ category: SubCategory;
colors: string[];
};
@@ -195,6 +195,7 @@ declare global {
type CartItem = {
id: number;
product: {
+ id: number;
title: string;
product_attributes: {
id: number;