This commit is contained in:
Mamalizz
2025-04-11 21:42:09 +03:30
37 changed files with 246 additions and 204 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
from django.contrib import admin from django.contrib import admin
from .models import * from .models import *
from unfold.admin import ModelAdmin, TabularInline from unfold.admin import TabularInline
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from import_export.admin import ImportExportModelAdmin from import_export.admin import ImportExportModelAdmin
from unfold.contrib.import_export.forms import ExportForm, ImportForm, SelectableFieldsExportForm from unfold.contrib.import_export.forms import ExportForm, ImportForm, SelectableFieldsExportForm
+2 -1
View File
@@ -11,6 +11,7 @@ urlpatterns = [
path('cart/all', CartItemClear.as_view()), path('cart/all', CartItemClear.as_view()),
path('cart/item/<int:pk>', CartItemViews.as_view(), name='change-item-cart'), path('cart/item/<int:pk>', CartItemViews.as_view(), name='change-item-cart'),
path('cart/payment', PaymentView.as_view(), name='payment'), path('cart/payment', PaymentView.as_view(), name='payment'),
path('callback', callback_view, name='callback-gateway'), # path('callback', callback_view, name='callback-gateway'),
path('transaction/<int:tracking_code>', CallbackView.as_view(), name='callback-gateway'),
path('<int:pk>', OrderGetView.as_view(), name='order-get'), path('<int:pk>', OrderGetView.as_view(), name='order-get'),
] ]
+19 -2
View File
@@ -222,8 +222,7 @@ class PaymentView(APIView):
) )
bank.set_request(request) bank.set_request(request)
bank.set_amount(amount) bank.set_amount(amount)
bank.set_client_callback_url(reverse("callback-gateway")) bank.set_client_callback_url('https://heymlz.com/transaction')
print(reverse('callback-gateway'))
bank.set_mobile_number(user_mobile_number) bank.set_mobile_number(user_mobile_number)
bank_record = bank.ready() bank_record = bank.ready()
@@ -270,6 +269,24 @@ def callback_view(request):
class CallbackView(APIView):
def get(self, request, tracking_code):
if not tracking_code:
return Response({'detail': 'کد تریسکد درست نمیباشد.'})
try:
bank_record = bank_models.Bank.objects.get(tracking_code=tracking_code)
except bank_models.Bank.DoesNotExist:
return Response({'detail': 'کد تریسکد معتبر نمیباشد.'}, status=status.HTTP_400_BAD_REQUEST)
if bank_record.is_success:
return Response({"detail" : "پرداخت با موفقیت انجام شد."}, status=status.HTTP_200_OK)
return Response(
{"detail": "پرداخت با شکست مواجه شده است. اگر پول کم شده است ظرف مدت ۴۸ ساعت پول به حساب شما بازخواهد گشت."}, status=status.HTTP_404_NOT_FOUND
)
class SetAddressSerilizer(serializers.Serializer): class SetAddressSerilizer(serializers.Serializer):
address_id = serializers.IntegerField() address_id = serializers.IntegerField()
+30 -18
View File
@@ -13,53 +13,65 @@ const {} = toRefs(props);
<template> <template>
<div class="relative w-full flex flex-col justify-center min-h-[450px] h-svh"> <div class="relative w-full flex flex-col justify-center min-h-[450px] h-svh">
<div class="flex-col-center gap-6 mb-32"> <div class="flex-col-center gap-6 mb-24 sm:mb-32 container">
<span class="typo-h-6 max-sm:text-xl md:typo-h-5 lg:typo-h-4 text-black"> <span class="typo-h-6 max-sm:text-xl md:typo-h-5 lg:typo-h-4 text-black">
مجله در ستون و سطرآنچ مجله در ستون و سطرآنچ
</span> </span>
<p class="text-slate-500 text-center max-w-[750px] typo-p-lg xl:typo-p-xl"> <p class="text-slate-500 text-center max-w-[750px] typo-p-sm md:typo-p-lg xl:typo-p-xl">
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. چاپگرها و لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. چاپگرها و
متون بلکه روزنامه و مجله در ستون و سطرآنچنان که متون بلکه روزنامه و مجله در ستون و سطرآنچنان که
</p> </p>
</div> </div>
<div class="-rotate-z-2 z-20"> <div class="-rotate-z-2 z-20">
<div <div
class="bg-blue-500 flex items-center pr-20 gap-12 sm:gap-20 w-max animate-marquee-reverse h-[140px]" class="bg-blue-500 flex items-center pr-20 gap-12 sm:gap-20 w-max animate-marquee-reverse h-[90px] sm:h-[140px]"
> >
<template v-for="i in 10"> <template v-for="i in 10">
<div class="text-[30px] lg:text-[40px] text-white whitespace-nowrap font-semibold opacity-85"> <div class="text-[30px] lg:text-[40px] text-white whitespace-nowrap font-semibold opacity-85">
HEYMLZ HEYMLZ
</div> </div>
<NuxtImg src="/img/heymlz/heymlz-logo.png" class="h-[45px] invert opacity-85" /> <NuxtImg src="/img/heymlz/heymlz-logo.png" class="h-[25px] sm:h-[45px] invert opacity-85" />
</template> </template>
<template v-for="i in 10"> <template v-for="i in 10">
<div class="text-[30px] lg:text-[40px] text-white whitespace-nowrap font-semibold opacity-85"> <div class="text-[30px] lg:text-[40px] text-white whitespace-nowrap font-semibold opacity-85">
HEYMLZ HEYMLZ
</div> </div>
<NuxtImg src="/img/heymlz/heymlz-logo.png" class="h-[45px] invert opacity-85" /> <NuxtImg src="/img/heymlz/heymlz-logo.png" class="h-[25px] sm:h-[45px] invert opacity-85" />
</template> </template>
</div> </div>
</div> </div>
<div class="rotate-z-2 z-10"> <div class="rotate-z-2 z-10">
<div <div
class="bg-slate-100/70 flex items-center pr-20 gap-12 sm:gap-20 w-max animate-marquee h-[140px]" class="bg-slate-100/70 flex items-center pr-20 gap-12 sm:gap-20 w-max animate-marquee h-[90px] sm:h-[140px]"
> >
<template v-for="i in 1"> <template v-for="i in 1">
<NuxtImg src="/img/brands/brand-1.png" class="h-[45px] grayscale opacity-40" /> <NuxtImg src="/img/brands/brand-1.png" class="h-[25px] sm:h-[45px] grayscale opacity-40" />
<NuxtImg src="/img/brands/brand-2.png" class="h-[45px] grayscale opacity-40" /> <NuxtImg src="/img/brands/brand-2.png" class="h-[25px] sm:h-[45px] grayscale opacity-40" />
<NuxtImg src="/img/brands/brand-3.png" class="h-[45px] grayscale opacity-40" /> <NuxtImg src="/img/brands/brand-3.png" class="h-[25px] sm:h-[45px] grayscale opacity-40" />
<NuxtImg src="/img/brands/brand-4.png" class="h-[45px] grayscale opacity-40" /> <NuxtImg src="/img/brands/brand-4.png" class="h-[25px] sm:h-[45px] grayscale opacity-40" />
<NuxtImg src="/img/brands/brand-5.png" class="h-[45px] grayscale opacity-40" /> <NuxtImg src="/img/brands/brand-5.png" class="h-[25px] sm:h-[45px] grayscale opacity-40" />
<NuxtImg src="/img/brands/brand-6.png" class="h-[45px] grayscale opacity-40" /> <NuxtImg src="/img/brands/brand-6.png" class="h-[25px] sm:h-[45px] grayscale opacity-40" />
<NuxtImg src="/img/brands/brand-1.png" class="h-[25px] sm:h-[45px] grayscale opacity-40" />
<NuxtImg src="/img/brands/brand-2.png" class="h-[25px] sm:h-[45px] grayscale opacity-40" />
<NuxtImg src="/img/brands/brand-3.png" class="h-[25px] sm:h-[45px] grayscale opacity-40" />
<NuxtImg src="/img/brands/brand-4.png" class="h-[25px] sm:h-[45px] grayscale opacity-40" />
<NuxtImg src="/img/brands/brand-5.png" class="h-[25px] sm:h-[45px] grayscale opacity-40" />
<NuxtImg src="/img/brands/brand-6.png" class="h-[25px] sm:h-[45px] grayscale opacity-40" />
</template> </template>
<template v-for="i in 1"> <template v-for="i in 1">
<NuxtImg src="/img/brands/brand-1.png" class="h-[45px] grayscale opacity-40" /> <NuxtImg src="/img/brands/brand-1.png" class="h-[25px] sm:h-[45px] grayscale opacity-40" />
<NuxtImg src="/img/brands/brand-2.png" class="h-[45px] grayscale opacity-40" /> <NuxtImg src="/img/brands/brand-2.png" class="h-[25px] sm:h-[45px] grayscale opacity-40" />
<NuxtImg src="/img/brands/brand-3.png" class="h-[45px] grayscale opacity-40" /> <NuxtImg src="/img/brands/brand-3.png" class="h-[25px] sm:h-[45px] grayscale opacity-40" />
<NuxtImg src="/img/brands/brand-4.png" class="h-[45px] grayscale opacity-40" /> <NuxtImg src="/img/brands/brand-4.png" class="h-[25px] sm:h-[45px] grayscale opacity-40" />
<NuxtImg src="/img/brands/brand-5.png" class="h-[45px] grayscale opacity-40" /> <NuxtImg src="/img/brands/brand-5.png" class="h-[25px] sm:h-[45px] grayscale opacity-40" />
<NuxtImg src="/img/brands/brand-6.png" class="h-[45px] grayscale opacity-40" /> <NuxtImg src="/img/brands/brand-6.png" class="h-[25px] sm:h-[45px] grayscale opacity-40" />
<NuxtImg src="/img/brands/brand-1.png" class="h-[25px] sm:h-[45px] grayscale opacity-40" />
<NuxtImg src="/img/brands/brand-2.png" class="h-[25px] sm:h-[45px] grayscale opacity-40" />
<NuxtImg src="/img/brands/brand-3.png" class="h-[25px] sm:h-[45px] grayscale opacity-40" />
<NuxtImg src="/img/brands/brand-4.png" class="h-[25px] sm:h-[45px] grayscale opacity-40" />
<NuxtImg src="/img/brands/brand-5.png" class="h-[25px] sm:h-[45px] grayscale opacity-40" />
<NuxtImg src="/img/brands/brand-6.png" class="h-[25px] sm:h-[45px] grayscale opacity-40" />
</template> </template>
</div> </div>
</div> </div>
+4 -9
View File
@@ -10,16 +10,11 @@
class="flex flex-col gap-4 items-center justify-center relative z-20" class="flex flex-col gap-4 items-center justify-center relative z-20"
> >
<div <div
class="flex items-center flex-col pb-[10px] pt-[80px] lg:py-[150px] justify-center" class="flex items-center flex-col gap-8 pb-[10px] pt-[80px] lg:py-[150px] justify-center"
> >
<video <img
src="/video/loading.mp4" src="/img/heymlz/heymlz-small-idle.gif"
autoplay class="size-[150px] lg:size-[220px] rounded-full drop-shadow-2xl"
muted
loop
playsinline
webkit-playsinline
class="size-[150px] lg:size-[220px] rounded-full"
/> />
<span <span
class="font-bold text-2xl lg:text-5xl text-gradient bg-gradient-to-l from-blue-500 to-blue-700" class="font-bold text-2xl lg:text-5xl text-gradient bg-gradient-to-l from-blue-500 to-blue-700"
+38 -42
View File
@@ -1,52 +1,48 @@
<script lang="ts" setup> <script lang="ts" setup>
// state // state
const { $gsap: gsap } = useNuxtApp(); const { $gsap: gsap } = useNuxtApp();
const showLoadingOverlay = useState("showLoadingOverlay");
const disableLoadingOverlay = useState("disableLoadingOverlay"); const disableLoadingOverlay = useState("disableLoadingOverlay");
const shouldRenderLoadingOverlay = ref(true); const shouldRenderLoadingOverlay = ref(true);
const isWindowScrollLocked = useScrollLock(window); const isWindowScrollLocked = useScrollLock(window);
// watch
watch(() => showLoadingOverlay.value, (value) => {
if (!value) {
const timeline = gsap.timeline();
const imageElement = document.querySelector("#loading-overlay-image") as HTMLImageElement;
imageElement.src = "/img/heymlz-loading-2.gif";
timeline
.to("#loading-overlay", {
scale: 1
})
.to("#loading-overlay", {
opacity: 0,
delay: 2,
onComplete: () => {
shouldRenderLoadingOverlay.value = false;
isWindowScrollLocked.value = false;
disableLoadingOverlay.value = true;
}
});
}
}, {
once: true
});
// lifecycle // lifecycle
onMounted(() => { onMounted(async () => {
const timeline = gsap.timeline();
timeline.to("#loading-overlay", {
opacity: 1,
});
isWindowScrollLocked.value = true; isWindowScrollLocked.value = true;
const newImage = new Image(); const preload = (url: string) => {
newImage.src = "/img/heymlz-loading-2.gif"; return new Promise((resolve) => {
}); const image = new Image();
image.src = url;
image.onload = () => resolve(true);
});
};
await Promise.all([
preload("/img/heymlz/heymlz-fast-loading.gif"),
preload("/img/heymlz/heymlz-pulling.gif"),
preload("/img/heymlz/heymlz-falling.gif"),
preload("/img/heymlz/heymlz-seat.gif"),
]);
timeline.to("#loading-overlay", {
opacity: 0,
delay: 5.5,
onComplete: () => {
shouldRenderLoadingOverlay.value = false;
isWindowScrollLocked.value = false;
disableLoadingOverlay.value = true;
},
});
});
</script> </script>
<template> <template>
@@ -57,19 +53,18 @@ onMounted(() => {
> >
<NuxtImg <NuxtImg
id="loading-overlay-image" id="loading-overlay-image"
src="/img/heymlz/heymlz-loading-1.gif" src="/img/heymlz/heymlz-fast-loading.gif"
class="opacity-0 scale-70 absolute z-20" class="absolute z-20 w-[700px] brightness-75"
alt="" alt=""
/> />
<div <!-- <div
id="loading-overlay-gradient" id="loading-overlay-gradient"
class="opacity-0 scale-x-0 w-[1000px] h-[70px] bg-linear-to-r from-blue-500 via-violet-500 to-purple-500 blur-[150px] rounded-[100px]" class="opacity-0 scale-x-0 w-[1000px] h-[70px] bg-linear-to-r from-blue-500 via-violet-500 to-purple-500 blur-[150px] rounded-[100px]"
/> /> -->
</div> </div>
</template> </template>
<style> <style>
#loading-overlay-image { #loading-overlay-image {
animation-name: loading-overlay-image-animation; animation-name: loading-overlay-image-animation;
animation-duration: 1s; animation-duration: 1s;
@@ -79,7 +74,8 @@ onMounted(() => {
#loading-overlay-gradient { #loading-overlay-gradient {
animation: 1.5s normal 0.5s 1 forwards loading-overlay-gradient-animation, animation: 1.5s normal 0.5s 1 forwards loading-overlay-gradient-animation,
1s ease-in-out 2s infinite alternate-reverse loading-overlay-gradient-pules-animation; 1s ease-in-out 2s infinite alternate-reverse
loading-overlay-gradient-pules-animation;
} }
@keyframes loading-overlay-image-animation { @keyframes loading-overlay-image-animation {
@@ -117,4 +113,4 @@ onMounted(() => {
scale: 1 1 1; scale: 1 1 1;
} }
} }
</style> </style>
+1 -1
View File
@@ -131,7 +131,7 @@ watch(() => status.value, (value) => {
:key="id" :key="id"
:index="index" :index="index"
:autofocus="autofocus ? index === 0 ? true : 'off' : 'off'" :autofocus="autofocus ? index === 0 ? true : 'off' : 'off'"
class="disabled:text-slate-400 focus-within:border-black transition-all size-16 bg-slate-50 typo-label-lg rounded-lg text-center border-[1.5px] border-slate-200 outline-none" class="disabled:text-slate-400 focus-within:border-black transition-all size-12 sm:size-16 bg-slate-50 typo-label-lg rounded-md sm:rounded-lg text-center border-[1.5px] border-slate-200 outline-none"
/> />
</PinInputRoot> </PinInputRoot>
</div> </div>
@@ -11,22 +11,22 @@ type Highlight = {
const highlights = ref<Highlight[]>([ const highlights = ref<Highlight[]>([
{ {
icon: "/img/heymlz/footer-support.png", icon: "/img/heymlz/footer-support.gif",
title: "خدمات مشتری", title: "خدمات مشتری",
description: "پشتیبانی استثنایی، راه‌حل‌های پایدار برای شما", description: "پشتیبانی استثنایی، راه‌حل‌های پایدار برای شما",
}, },
{ {
icon: "/img/heymlz/footer-send.png", icon: "/img/heymlz/footer-send.gif",
title: "ارسال سریع و رایگان", title: "ارسال سریع و رایگان",
description: "ارسال رایگان برای سفارش‌های بالای ۱۵۰ دلار", description: "ارسال رایگان برای سفارش‌های بالای ۱۵۰ دلار",
}, },
{ {
icon: "/img/heymlz/footer-share.png", icon: "/img/heymlz/footer-share.gif",
title: "معرفی به دوستان", title: "معرفی به دوستان",
description: "ما را به دوستان خود معرفی کنید", description: "ما را به دوستان خود معرفی کنید",
}, },
{ {
icon: "/img/heymlz/footer-security.png", icon: "/img/heymlz/footer-security.gif",
title: "پرداخت امن", title: "پرداخت امن",
description: "پرداخت شما به‌صورت امن پردازش می‌شود", description: "پرداخت شما به‌صورت امن پردازش می‌شود",
}, },
@@ -43,7 +43,7 @@ const highlights = ref<Highlight[]>([
<div class="size-[70px] md:size-[100px] flex-center"> <div class="size-[70px] md:size-[100px] flex-center">
<NuxtImg <NuxtImg
:src="highlight.icon" :src="highlight.icon"
class="w-full scale-150" class="w-full"
/> />
</div> </div>
+22 -26
View File
@@ -1,5 +1,4 @@
<script setup lang="ts"> <script setup lang="ts">
// imports // imports
import { Swiper, SwiperSlide } from "swiper/vue"; import { Swiper, SwiperSlide } from "swiper/vue";
@@ -16,40 +15,35 @@ const swiper_instance = ref<SwiperClass | null>(null);
const onSwiper = (swiper: SwiperClass) => { const onSwiper = (swiper: SwiperClass) => {
swiper_instance.value = swiper; swiper_instance.value = swiper;
}; };
</script> </script>
<template> <template>
<section <section
ref="sectionTarget" ref="sectionTarget"
class="flex flex-col justify-center gap-4 bg-black h-[150svh] relative overflow-hidden" class="flex flex-col justify-center gap-4 bg-black h-[110svh] sm:h-[150svh] relative overflow-hidden"
> >
<div class="w-full relative translate-y-[-90px] sm:translate-y-[-200px] z-10 container">
<div class="w-full relative translate-y-[-200px] z-10">
<div class="flex-col-center gap-6"> <div class="flex-col-center gap-6">
<span class="text-white typo-h-6 md:typo-h-5 lg:typo-h-4"> <span class="text-white typo-h-6 md:typo-h-5 lg:typo-h-4">
دسته بندی ها دسته بندی ها
</span> </span>
<p class="text-slate-300 text-center max-w-[750px] typo-p-lg xl:typo-p-xl"> <p
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. چاپگرها class="text-slate-300 text-center max-w-[750px] typo-p-sm md:typo-p-lg xl:typo-p-xl"
و >
متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و
با استفاده از طراحان گرافیک است. چاپگرها و متون بلکه روزنامه
و مجله در ستون و سطرآنچنان که
</p> </p>
</div> </div>
</div> </div>
<div class="w-full my-20 relative"> <div class="w-full my-20 relative">
<video <NuxtImg
class="aspect-square w-[450px] translate-[-293px] absolute left-1/2 -translate-x-1/2 z-10" class="aspect-square w-[240px] md:w-[300px] lg:w-[350px] translate-[-164px] md:translate-[-206px] lg:translate-[-240px] absolute left-1/2 -translate-x-1/2 z-10"
:style="{ :style="{
filter: 'drop-shadow(0px 0px 20px rgba(0, 0, 0, 0.4))', filter: 'drop-shadow(0px 0px 20px rgba(0, 0, 0, 0.4))',
}" }"
src="/video/heymlz/heymlz-handshake-part-1.webm" src="/img/heymlz/heymlz-seat.gif"
autoplay
playsinline
webkit-playsinline
muted
/> />
<Swiper <Swiper
:loop="true" :loop="true"
@@ -60,12 +54,12 @@ const onSwiper = (swiper: SwiperClass) => {
:breakpoints="{ :breakpoints="{
640: { 640: {
centeredSlides: true, centeredSlides: true,
slidesPerView : 2.65 slidesPerView: 2.65,
}, },
1024 : { 1024: {
centeredSlides: true, centeredSlides: true,
slidesPerView : 3.65 slidesPerView: 3.65,
} },
}" }"
> >
<SwiperSlide <SwiperSlide
@@ -108,12 +102,14 @@ const onSwiper = (swiper: SwiperClass) => {
<div class="w-full flex justify-center items-center"> <div class="w-full flex justify-center items-center">
<NuxtLink to="/category"> <NuxtLink to="/category">
<Button variant="primary" class="rounded-full max-xs:typo-label-sm !px-4 xs:!px-8" <Button
end-icon="ci:arrow-left"> variant="primary"
class="rounded-full max-xs:typo-label-sm !px-4 xs:!px-8"
end-icon="ci:arrow-left"
>
مشاهده همه دسته ها مشاهده همه دسته ها
</Button> </Button>
</NuxtLink> </NuxtLink>
</div> </div>
</section> </section>
</template> </template>
-4
View File
@@ -11,7 +11,6 @@ import useHomeData from "~/composables/api/home/useHomeData";
const { data: homeData } = useHomeData(); const { data: homeData } = useHomeData();
const { $gsap: gsap, $ScrollTrigger: ScrollTrigger } = useNuxtApp(); const { $gsap: gsap, $ScrollTrigger: ScrollTrigger } = useNuxtApp();
const swiper_instance = ref<SwiperClass | null>(null); const swiper_instance = ref<SwiperClass | null>(null);
const showLoadingOverlay = useState("showLoadingOverlay");
const observerTarget = ref(null); const observerTarget = ref(null);
const shouldPauseVideos = ref(false); const shouldPauseVideos = ref(false);
@@ -32,9 +31,6 @@ let scrollTrigger: ScrollTrigger;
// methods // methods
const onSwiper = (swiper: SwiperClass) => { const onSwiper = (swiper: SwiperClass) => {
setTimeout(() => {
showLoadingOverlay.value = false;
}, 1000);
swiper_instance.value = swiper; swiper_instance.value = swiper;
}; };
+64 -45
View File
@@ -1,5 +1,4 @@
<script setup lang="ts"> <script setup lang="ts">
// import // import
import useHomeData from "~/composables/api/home/useHomeData"; import useHomeData from "~/composables/api/home/useHomeData";
@@ -17,45 +16,54 @@ const previewContainerEl = ref<HTMLElement | null>(null);
const heymlzElement = useTemplateRef<HTMLDivElement>("heymlzElement"); const heymlzElement = useTemplateRef<HTMLDivElement>("heymlzElement");
const heymlzElementIsVisible = useElementVisibility(heymlzElement, { const heymlzElementIsVisible = useElementVisibility(heymlzElement, {
rootMargin: "0px 0px -40% 0px" rootMargin: "0px 0px -40% 0px",
}); });
const showHeymlzAnimation = ref(false); const showHeymlzAnimation = ref(false);
const { x: dragAxisX } = useDraggable(draggableEl, { const { x: dragAxisX } = useDraggable(draggableEl, {
initialValue: { x: 0, y: 0 }, initialValue: { x: 0, y: 0 },
axis: "x" axis: "x",
}); });
// watch // watch
watch(heymlzElementIsVisible, (newValue) => { watch(
if (newValue) { heymlzElementIsVisible,
showHeymlzAnimation.value = true; (newValue) => {
setTimeout(() => { if (newValue) {
showHeymlzAnimation.value = false; showHeymlzAnimation.value = true;
}, 3200); setTimeout(() => {
showHeymlzAnimation.value = false;
}, 3200);
}
},
{
once: true,
} }
}, { );
once: true
});
watch(() => clipPathPercent.value, (newValue) => { watch(
if (newValue > 80) { () => clipPathPercent.value,
activeSlideVideo.value = "right"; (newValue) => {
} else if (newValue < 20) { if (newValue > 80) {
activeSlideVideo.value = "left"; activeSlideVideo.value = "right";
} else { } else if (newValue < 20) {
activeSlideVideo.value = "none"; activeSlideVideo.value = "left";
} else {
activeSlideVideo.value = "none";
}
} }
}); );
watch( watch(
() => dragAxisX.value, () => dragAxisX.value,
(newValue) => { (newValue) => {
const clientRect = previewContainerEl.value?.getBoundingClientRect()!; const clientRect = previewContainerEl.value?.getBoundingClientRect()!;
const percent = clientRect.width / 100; const percent = clientRect.width / 100;
const clipPercent = ((newValue + draggableEl.value!.clientWidth / 2) - clientRect.x - 8) / percent; const clipPercent =
(newValue + draggableEl.value!.clientWidth / 2 - clientRect.x - 8) /
percent;
if (clipPercent >= 5 && clipPercent <= 95) { if (clipPercent >= 5 && clipPercent <= 95) {
clipPathPercent.value = clipPercent; clipPathPercent.value = clipPercent;
} }
@@ -64,10 +72,12 @@ watch(
</script> </script>
<template> <template>
<div class="container"> <div class="container mb-40 lg:mb-80 mt-20">
<div> <div>
<div class="flex flex-col items-center gap-3 mb-10 lg:mb-16"> <div class="flex flex-col items-center gap-3 mb-10 lg:mb-16">
<span class="typo-p-sm md:typo-p-md text-slate-500">مقایسه محصولات</span> <span class="typo-p-sm md:typo-p-md text-slate-500"
>مقایسه محصولات</span
>
<span class="typo-h-6 md:typo-h-5 lg:typo-h-3 text-black"> <span class="typo-h-6 md:typo-h-5 lg:typo-h-3 text-black">
تفاوت محصلات ما را ببینید تفاوت محصلات ما را ببینید
</span> </span>
@@ -80,7 +90,11 @@ watch(
<NuxtImg <NuxtImg
v-if="activeSlideVideo !== 'right'" v-if="activeSlideVideo !== 'right'"
:src="homeData!.difreance_section.image1" :src="homeData!.difreance_section.image1"
:class="showHeymlzAnimation ? 'brightness-35' : 'brightness-[95%]'" :class="
showHeymlzAnimation
? 'brightness-35'
: 'brightness-[95%]'
"
class="select-none absolute size-full object-cover transition-[filter] duration-250" class="select-none absolute size-full object-cover transition-[filter] duration-250"
:alt="homeData!.difreance_section.title1" :alt="homeData!.difreance_section.title1"
/> />
@@ -95,13 +109,19 @@ watch(
/> />
</Transition> </Transition>
<div class="absolute size-full right-0 w-full" ref="heymlzElement"> <div
class="absolute size-full right-0 w-full"
ref="heymlzElement"
>
<Transition name="fade"> <Transition name="fade">
<NuxtImg <NuxtImg
v-if="activeSlideVideo !== 'left'" v-if="activeSlideVideo !== 'left'"
:src="homeData!.difreance_section.image2" :src="homeData!.difreance_section.image2"
:class="showHeymlzAnimation ? 'brightness-35' : 'brightness-[95%]'" :class="
showHeymlzAnimation
? 'brightness-35'
: 'brightness-[95%]'
"
class="overlay-image select-none absolute object-cover size-full transition-[filter] duration-250" class="overlay-image select-none absolute object-cover size-full transition-[filter] duration-250"
:alt="homeData!.difreance_section.title2" :alt="homeData!.difreance_section.title2"
/> />
@@ -116,18 +136,13 @@ watch(
/> />
</Transition> </Transition>
<video <NuxtImg
v-if="showHeymlzAnimation" v-if="showHeymlzAnimation"
src="/video/heymlz/heymlz-pulling.webm" src="/img/heymlz/heymlz-pulling.gif"
autoplay class="size-[250px] sm:size-[400px] absolute translate-x-[-62px] sm:translate-x-[-107px] z-10 top-[50%] -translate-y-1/2"
muted
playsinline
loop
webkit-playsinline
class="size-[400px] absolute translate-x-[-107px] z-10 top-[50%] -translate-y-1/2"
:style="{ :style="{
left: `${clipPathPercent}%`, left: `${clipPathPercent}%`,
filter: 'drop-shadow(0px 0px 20px rgba(0, 0, 0, 0.3))' filter: 'drop-shadow(0px 0px 20px rgba(0, 0, 0, 0.3))',
}" }"
/> />
@@ -137,28 +152,35 @@ watch(
}" }"
:class="[ :class="[
activeSlideVideo !== 'none' ? 'opacity-10' : '', activeSlideVideo !== 'none' ? 'opacity-10' : '',
showHeymlzAnimation ? 'bg-neutral-300' : 'bg-black' showHeymlzAnimation ? 'bg-neutral-200' : 'bg-black',
]" ]"
class="select-none w-2 h-full absolute left-0 flex items-center justify-center transition-opacity duration-250" class="select-none w-2 h-full absolute left-0 flex items-center justify-center transition-opacity duration-250"
> >
<div <div
ref="draggableEl" ref="draggableEl"
:class="showHeymlzAnimation ? 'bg-neutral-300' : 'bg-black'" :class="
showHeymlzAnimation
? 'bg-neutral-300'
: 'bg-black'
"
class="cursor-grab hover:scale-115 transition-transform rounded-full absolute size-11 flex items-center justify-center" class="cursor-grab hover:scale-115 transition-transform rounded-full absolute size-11 flex items-center justify-center"
> >
<Icon <Icon
name="ci:arrows" name="ci:arrows"
size="24" size="24"
class="transition-all" class="transition-all"
:class="showHeymlzAnimation ? '**:stroke-black' : '**:stroke-white'" :class="
showHeymlzAnimation
? '**:stroke-black'
: '**:stroke-white'
"
/> />
</div> </div>
</div> </div>
</div> </div>
<div <div
class="max-xs:hidden absolute bottom-0 p-6 md:p-10 w-full flex justify-between items-end" class="absolute bottom-0 p-6 md:p-10 w-full flex justify-between items-end"
> >
<div <div
class="flex flex-col gap-2 text-black transition-opacity" class="flex flex-col gap-2 text-black transition-opacity"
@@ -191,9 +213,6 @@ watch(
</div> </div>
</div> </div>
</div> </div>
<div class="h-80"></div>
</div> </div>
</template> </template>
+45 -33
View File
@@ -1,5 +1,4 @@
<script lang="ts" setup> <script lang="ts" setup>
// import // import
import useHomeData from "~/composables/api/home/useHomeData"; import useHomeData from "~/composables/api/home/useHomeData";
@@ -21,26 +20,32 @@ onMounted(() => {
setTimeout(() => { setTimeout(() => {
showcaseElements.forEach((element, index) => { showcaseElements.forEach((element, index) => {
gsapTimeline.fromTo(
gsapTimeline.fromTo(element, index === 0 ? { element,
opacity: 1, index === 0
scale: 1, ? {
// rotateX: -25, opacity: 1,
top: 0, scale: 1,
ease: "none" // rotateX: -25,
} : { top: 0,
opacity: 0, ease: "none",
scale: 1, }
// rotateX: -25, : {
top: 20, opacity: 0,
ease: "none" scale: 1,
}, { // rotateX: -25,
opacity: 1, top: 20,
scale: 1, ease: "none",
// rotateX: 0, },
top: 0, {
ease: "none" opacity: 1,
}, index === 0 ? "-=0%" : undefined); scale: 1,
// rotateX: 0,
top: 0,
ease: "none",
},
index === 0 ? "-=0%" : undefined
);
if (index < showcaseElements.length - 1) { if (index < showcaseElements.length - 1) {
gsapTimeline.to(element, { gsapTimeline.to(element, {
@@ -48,10 +53,9 @@ onMounted(() => {
scale: 1.03, scale: 1.03,
// rotateX: 25, // rotateX: 25,
top: -20, top: -20,
ease: "none" ease: "none",
}); });
} }
}); });
scrollTrigger = ScrollTrigger.create({ scrollTrigger = ScrollTrigger.create({
@@ -62,14 +66,13 @@ onMounted(() => {
start: "top top", start: "top top",
anticipatePin: 1, anticipatePin: 1,
// markers: true, // markers: true,
end: "bottom top" end: "bottom top",
}); });
setTimeout(() => { setTimeout(() => {
scrollTrigger.update(); scrollTrigger.update();
scrollTrigger.refresh(); scrollTrigger.refresh();
}, 1000); }, 1000);
}, 1000); }, 1000);
}); });
@@ -77,7 +80,6 @@ onUnmounted(() => {
gsapTimeline.progress(1).pause(); gsapTimeline.progress(1).pause();
gsapTimeline.kill(); gsapTimeline.kill();
}); });
</script> </script>
<template> <template>
@@ -85,7 +87,7 @@ onUnmounted(() => {
id="products-showcase-container" id="products-showcase-container"
class="perspective-midrange relative z-[99999]" class="perspective-midrange relative z-[99999]"
> >
<div class="w-full h-[125svh] bg-black"> <div class="w-full h-[102svh] bg-black">
<NuxtLink <NuxtLink
v-for="slide in homeData!.show_case_slider" v-for="slide in homeData!.show_case_slider"
:key="slide.id" :key="slide.id"
@@ -100,18 +102,28 @@ onUnmounted(() => {
}" }"
alt="" alt=""
/> />
<div class="flex flex-col items-center justify-center gap-6 text-center absolute z-20 mt-20"> <div
<span class="text-white typo-h-6 sm:typo-h-5 lg:typo-h-4 xl:typo-h-3"> class="flex flex-col items-center justify-center gap-6 text-center absolute z-20 mt-20"
>
<span
class="text-white typo-h-6 sm:typo-h-5 lg:typo-h-4 xl:typo-h-3"
>
{{ slide.title }} {{ slide.title }}
</span> </span>
<p class="text-white max-w-[320px] xs:max-w-[360px] sm:max-w-[480px] lg:max-w-[550px] xl:max-w-[750px] typo-p-sm lg:typo-p-md xl:typo-p-lg"> <p
class="text-white max-w-[320px] xs:max-w-[360px] sm:max-w-[480px] lg:max-w-[550px] xl:max-w-[750px] typo-p-sm lg:typo-p-md xl:typo-p-lg"
>
{{ slide.description }} {{ slide.description }}
</p> </p>
<NuxtLink :to="slide.link"> <NuxtLink :to="slide.link" class="relative">
<NuxtImg
src="/img/heymlz/heymlz-falling.gif"
class="absolute top-[106px] sm:top-[105px] lg:top-[117px] left-1/2 -translate-1/2 w-[250px] drop-shadow-md"
/>
<Button <Button
variant="primary" variant="primary"
end-icon="ci:arrow-left" end-icon="ci:arrow-left"
class="mt-8 max-sm:hidden max-lg:typo-label-xs px-10 rounded-full hover:bg-transparent" class="mt-8 max-sm:py-2 max-lg:typo-label-xs px-10 rounded-full hover:bg-transparent"
> >
مشاهده دسته بندی مشاهده دسته بندی
</Button> </Button>
@@ -120,4 +132,4 @@ onUnmounted(() => {
</NuxtLink> </NuxtLink>
</div> </div>
</section> </section>
</template> </template>
+9 -13
View File
@@ -159,24 +159,20 @@ const resetForm = () => {
}" }"
/> />
<div <div
class="flex items-center justify-center flex-col size-full -translate-y-1/6" class="flex items-center justify-center flex-col size-full translate-y-[-100px]"
> >
<video <img
class="aspect-square w-[450px] translate-y-[157px] animate-fade-in" class="aspect-square w-[250px] sm:w-[325px] translate-y-[90px] sm:translate-y-[120px] animate-fade-in"
src="/video/heymlz/heymlz-handshake-full.webm" src="/img/heymlz/heymlz-signin.gif"
:style="{ :style="{
filter: 'drop-shadow(0px 4px 20px rgba(0, 0, 0, 0.15))', filter: 'drop-shadow(0px 4px 20px rgba(0, 0, 0, 0.15))',
}" }"
autoplay
playsinline
webkit-playsinline
muted
/> />
<div <div
class="max-w-[600px] w-full p-6 h-[400px] flex flex-col items-center bg-white border shadow-black/10 justify-center border-slate-300 rounded-3xl" class="max-w-[600px] w-full p-6 h-[350px] sm:h-[400px] flex flex-col items-center bg-white border shadow-black/10 justify-center border-slate-300 rounded-3xl"
> >
<h1 class="typo-h-5 mt-8">شماره خود را وارد کنید</h1> <h1 class="typo-h-6 sm:typo-h-5 mt-8">شماره خود را وارد کنید</h1>
<form @submit.prevent class="max-w-[500px] w-full mt-12"> <form @submit.prevent class="max-w-[500px] w-full mt-12">
<Input <Input
@@ -219,7 +215,7 @@ const resetForm = () => {
<Button <Button
data-testid="send-otp-code-button" data-testid="send-otp-code-button"
v-if="!showOtp" v-if="!showOtp"
class="rounded-full w-full mt-4" class="rounded-full w-full mt-4 max-sm:h-[45px]"
type="submit" type="submit"
@click="handleLogin" @click="handleLogin"
:loading="sendOtpIsPending" :loading="sendOtpIsPending"
@@ -230,7 +226,7 @@ const resetForm = () => {
<div v-else class="flex items-center w-full gap-4 mt-4"> <div v-else class="flex items-center w-full gap-4 mt-4">
<Button <Button
class="rounded-full w-full mt-4" class="rounded-full w-full mt-4 max-sm:h-[45px]"
type="button" type="button"
variant="secondary" variant="secondary"
@click="resetForm" @click="resetForm"
@@ -239,7 +235,7 @@ const resetForm = () => {
تغییر شماره تغییر شماره
</Button> </Button>
<Button <Button
class="rounded-full w-full mt-4" class="rounded-full w-full mt-4 max-sm:h-[45px]"
type="submit" type="submit"
@click="resendOtp" @click="resendOtp"
:loading="signInIsPending || sendOtpIsPending" :loading="signInIsPending || sendOtpIsPending"
+6
View File
@@ -0,0 +1,6 @@
export default defineNuxtPlugin((nuxtApp) => {
// Also possible
nuxtApp.hook('vue:error', (error, instance, info) => {
// handle error, e.g. report to a service
})
})
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 945 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 745 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 995 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 553 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 740 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 890 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 944 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 943 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1012 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 830 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 869 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 944 KiB