merge
This commit is contained in:
@@ -1,73 +0,0 @@
|
||||
@layer base {
|
||||
@font-face {
|
||||
font-family: "Morabba";
|
||||
src: url("/fonts/Morabba/Morabba-UltraLight.woff2");
|
||||
font-weight: 200;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Morabba";
|
||||
src: url("/fonts/Morabba/Morabba-Light.woff2");
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Morabba";
|
||||
src: url("/fonts/Morabba/Morabba-Regular.woff2");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Morabba";
|
||||
src: url("/fonts/Morabba/Morabba-Medium.woff2");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Morabba";
|
||||
src: url("/fonts/Morabba/Morabba-SemiBold.woff2");
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Morabba";
|
||||
src: url("/fonts/Morabba/Morabba-Bold.woff2");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Morabba";
|
||||
src: url("/fonts/Morabba/Morabba-ExtraBold.woff2");
|
||||
font-weight: 800;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Morabba";
|
||||
src: url("/fonts/Morabba/Morabba-Black.woff2");
|
||||
font-weight: 900;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Morabba";
|
||||
src: url("/fonts/Morabba/Morabba-Heavy.woff2");
|
||||
font-weight: 1000;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
@layer base {
|
||||
@font-face {
|
||||
font-family: "YekanBakh";
|
||||
src: url("/fonts/YekanBakh/YekanBakh-Thin.woff");
|
||||
font-weight: 100;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "YekanBakh";
|
||||
src: url("/fonts/YekanBakh/YekanBakh-Light.woff");
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "YekanBakh";
|
||||
src: url("/fonts/YekanBakh/YekanBakh-Regular.woff");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "YekanBakh";
|
||||
src: url("/fonts/YekanBakh/YekanBakh-SemiBold.woff");
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "YekanBakh";
|
||||
src: url("/fonts/YekanBakh/YekanBakh-Bold.woff");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,6 @@
|
||||
@import "./button.comp.css";
|
||||
@import "./input.comp.css";
|
||||
@import "./fonts/iran-yekan-x.css";
|
||||
@import "./fonts/morabba.css";
|
||||
@import "./fonts/yekan-bakh.css";
|
||||
|
||||
:root {
|
||||
/* CONTAINER */
|
||||
@@ -117,8 +115,6 @@
|
||||
--font-inter: "Inter", sans-serif;
|
||||
--font-dmsans: "DM Sans", sans-serif;
|
||||
--font-iran-yekan-x: "IRANYekanXVF", "sans-serif";
|
||||
--font-yekan-bakh: "YekanBakh", "sans-serif";
|
||||
--font-morabba: "Morabba", "sans-serif";
|
||||
|
||||
/* BREAKPOINTS */
|
||||
--breakpoint-2xs: 400px;
|
||||
|
||||
@@ -22,15 +22,16 @@ const emit = defineEmits<Emits>();
|
||||
<template>
|
||||
<button
|
||||
@click="emit('select', null)"
|
||||
:class="
|
||||
isSelected
|
||||
? 'ring-2 ring-offset-2 ring-blue-500 border-blue-500'
|
||||
: 'border-slate-200'
|
||||
"
|
||||
:class="isSelected ? 'ring-2 ring-offset-2 ring-blue-500 border-blue-500' : 'border-slate-200'"
|
||||
class="w-full p-5 border rounded-xl flex flex-col gap-4 transition-all cursor-pointer relative overflow-hidden"
|
||||
>
|
||||
<div class="aspect-square flex-center">
|
||||
<NuxtImg :src="gateway.picture" class="object-cover" />
|
||||
<NuxtImg
|
||||
:src="gateway.picture"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
class="object-cover"
|
||||
/>
|
||||
</div>
|
||||
<span class="typo-label-sm text-right text-black">
|
||||
{{ gateway.title }}
|
||||
@@ -43,7 +44,11 @@ const emit = defineEmits<Emits>();
|
||||
v-if="isSelected"
|
||||
class="bg-blue-500 rounded-md p-0.5 text-center bottom-4 left-4 text-slate-200 text-[10px] lg:text-xs absolute"
|
||||
>
|
||||
<Icon name="bi:check" size="20" class="**:fill-white" />
|
||||
<Icon
|
||||
name="bi:check"
|
||||
size="20"
|
||||
class="**:fill-white"
|
||||
/>
|
||||
</span>
|
||||
</Transition>
|
||||
</button>
|
||||
|
||||
@@ -30,15 +30,11 @@ const visible = computed({
|
||||
class="fixed inset-0 w-full h-svh z-9999 flex-center"
|
||||
v-if="visible"
|
||||
>
|
||||
<div
|
||||
class="overflow-y-auto max-h-svh absolute left-[50%] py-10 w-fit max-w-[50rem] translate-x-[-50%]"
|
||||
>
|
||||
<div class="overflow-y-auto max-h-svh absolute left-[50%] py-10 w-fit max-w-[50rem] translate-x-[-50%]">
|
||||
<DialogContent
|
||||
class="min-w-[30vw] max-w-[50vw] data-[state=open]:animate-content-show text-black font-iran-yekan-x focus:outline-none z-[100]"
|
||||
>
|
||||
<div
|
||||
class="w-full h-[350px] shrink-0 rounded-2xl relative overflow-hidden flex-center"
|
||||
>
|
||||
<div class="w-full h-[350px] shrink-0 rounded-2xl relative overflow-hidden flex-center">
|
||||
<div
|
||||
class="bg-custom-conic size-[200%] absolute -top-1/2 -left-1/2 animate-spin [animation-duration:3s] z-[1]"
|
||||
></div>
|
||||
@@ -48,6 +44,8 @@ const visible = computed({
|
||||
<NuxtImg
|
||||
class="aspect-square w-[300px]"
|
||||
src="/img/heymlz/heymlz-payment-progress.gif"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
/>
|
||||
|
||||
<div class="-translate-y-28 flex-center gap-3">
|
||||
@@ -55,9 +53,7 @@ const visible = computed({
|
||||
name="svg-spinners:3-dots-bounce"
|
||||
size="20"
|
||||
/>
|
||||
<span class="text-lg">
|
||||
در حال انتقال به درگاه پرداخت
|
||||
</span>
|
||||
<span class="text-lg"> در حال انتقال به درگاه پرداخت </span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -72,15 +68,7 @@ const visible = computed({
|
||||
.bg-custom-conic {
|
||||
background-size: 100% 100%;
|
||||
background-position: 0px 0px, 0px 0px;
|
||||
background-image: radial-gradient(
|
||||
142% 91% at -6% 90%,
|
||||
#ff000000 1%,
|
||||
#ff000000 99%
|
||||
),
|
||||
conic-gradient(
|
||||
from 0deg at 50% 50%,
|
||||
var(--color-blue-500) 0%,
|
||||
#ff000000 34%
|
||||
);
|
||||
background-image: radial-gradient(142% 91% at -6% 90%, #ff000000 1%, #ff000000 99%),
|
||||
conic-gradient(from 0deg at 50% 50%, var(--color-blue-500) 0%, #ff000000 34%);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -27,15 +27,19 @@ const { isLoading: cartImageIsLoading } = useImage({
|
||||
v-if="!cartImageIsLoading"
|
||||
class="size-[3.5rem] shrink-0 rounded-100 border border-gray-300 overflow-hidden"
|
||||
>
|
||||
<NuxtImg :src="image" alt="product" class="object-conver" />
|
||||
<NuxtImg
|
||||
:src="image"
|
||||
alt="product"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
class="object-conver"
|
||||
/>
|
||||
</div>
|
||||
<Skeleton
|
||||
v-else
|
||||
class="!size-[3.5rem] aspect-square shrink-0 !rounded-100 border border-slate-200"
|
||||
/>
|
||||
<span
|
||||
class="text-xs font-semibold lg:text-sm text-gray-800 line-clamp-2"
|
||||
>
|
||||
<span class="text-xs font-semibold lg:text-sm text-gray-800 line-clamp-2">
|
||||
{{ title }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -123,6 +123,8 @@ watch(
|
||||
>
|
||||
<NuxtImg
|
||||
:src="data.product.image"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
class="object-cover size-full"
|
||||
alt="product"
|
||||
/>
|
||||
|
||||
@@ -38,6 +38,8 @@ const remaining = computed(() => items.value.length - max.value);
|
||||
>
|
||||
<NuxtImg
|
||||
:src="item"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
alt="avatar"
|
||||
class="rounded-full object-cover w-full h-full"
|
||||
/>
|
||||
|
||||
@@ -51,6 +51,8 @@ const createdAt = usePersianTimeAgo(new Date(date.value));
|
||||
|
||||
<NuxtImg
|
||||
:src="image"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
class="group-hover:scale-105 transition-transform duration-200 absolute size-full object-cover z-10"
|
||||
alt=""
|
||||
/>
|
||||
@@ -98,6 +100,8 @@ const createdAt = usePersianTimeAgo(new Date(date.value));
|
||||
|
||||
<NuxtImg
|
||||
v-if="variant === 'lg'"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
:src="image"
|
||||
class="group-hover:scale-105 transition-transform duration-200 absolute size-full object-cover z-10"
|
||||
alt=""
|
||||
|
||||
@@ -41,6 +41,8 @@ const brands = ref([
|
||||
HEYMLZ
|
||||
</div>
|
||||
<NuxtImg
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
src="/img/heymlz/heymlz-logo.png"
|
||||
class="h-[25px] sm:h-[45px] invert"
|
||||
/>
|
||||
@@ -62,6 +64,8 @@ const brands = ref([
|
||||
class="flex items-center px-6 sm:px-10 h-[90px] sm:h-[140px]"
|
||||
>
|
||||
<NuxtImg
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
:src="brand"
|
||||
class="h-[25px] sm:h-[45px] opacity-25"
|
||||
/>
|
||||
@@ -70,23 +74,3 @@ const brands = ref([
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- <NuxtImg
|
||||
src="/img/brands/brand-2.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-3.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-4.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-5.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-6.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/> -->
|
||||
|
||||
@@ -44,6 +44,8 @@ const { colorObject } = useImageColor(`#category-image-${id.value}`);
|
||||
:id="`category-image-${id}`"
|
||||
class="group-hover:scale-105 transition-transform duration-200 absolute object-contain size-full"
|
||||
:src="picture"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
alt=""
|
||||
/>
|
||||
</Transition>
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<SideModal title="فیلتر محصولات">
|
||||
<SideModal
|
||||
modalId="product-filters"
|
||||
title="فیلتر محصولات"
|
||||
>
|
||||
<template #button>
|
||||
<Button
|
||||
end-icon="ci:filter"
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
<NuxtImg
|
||||
src="/img/footer-bg.jpg"
|
||||
alt=""
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
class="absolute z-10 object-cover opacity-45"
|
||||
:style="{
|
||||
mask: 'linear-gradient(to bottom, black 0%, rgba(0,0,0,0) 100%',
|
||||
@@ -13,6 +15,8 @@
|
||||
<div class="flex items-center flex-col gap-8 pb-[10px] pt-[80px] lg:pt-[100px] lg:pb-[50px] justify-center">
|
||||
<img
|
||||
src="/img/heymlz/heymlz-small-idle.gif"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
class="size-[150px] lg:size-[220px] rounded-full drop-shadow-2xl"
|
||||
/>
|
||||
<span class="font-bold text-2xl lg:text-5xl text-gradient bg-gradient-to-l from-blue-500 to-blue-700">
|
||||
|
||||
@@ -122,8 +122,18 @@ const isHomePage = computed(() => route.path === "/");
|
||||
</div>
|
||||
|
||||
<div class="header-navbar-item flex items-center justify-end h-full">
|
||||
<NuxtImg
|
||||
<!-- <NuxtImg
|
||||
src="/img/heymlz/heymlz-logomotion.gif"
|
||||
preload
|
||||
loading="eager"
|
||||
fetch-priority="high"
|
||||
class="h-2/3"
|
||||
/> -->
|
||||
<NuxtImg
|
||||
src="/img/static-logo.png"
|
||||
preload
|
||||
loading="eager"
|
||||
fetch-priority="high"
|
||||
class="h-2/3"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -35,24 +35,26 @@ const highlights = ref<Highlight[]>([
|
||||
|
||||
<template>
|
||||
<section class="w-full border-t-[0.5px] border-slate-200">
|
||||
<div
|
||||
class="w-full py-[3rem] lg:py-[5rem] gap-8 sm:gap-12 xl:gap-0 container grid grid-cols-2 lg:grid-cols-4"
|
||||
<div class="w-full py-[3rem] lg:py-[5rem] gap-8 sm:gap-12 xl:gap-0 container grid grid-cols-2 lg:grid-cols-4">
|
||||
<template
|
||||
v-for="(highlight, index) in highlights"
|
||||
:key="index"
|
||||
>
|
||||
<template v-for="(highlight, index) in highlights" :key="index">
|
||||
<div class="flex flex-col-center gap-[.75rem] px-5">
|
||||
<div class="size-[70px] md:size-[100px] flex-center">
|
||||
<NuxtImg :src="highlight.icon" class="w-full" />
|
||||
<NuxtImg
|
||||
:src="highlight.icon"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
class="w-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="w-full flex-col-center gap-[.25rem]">
|
||||
<span
|
||||
class="typo-sub-h-sm lg:typo-sub-h-md text-black text-center"
|
||||
>
|
||||
<span class="typo-sub-h-sm lg:typo-sub-h-md text-black text-center">
|
||||
{{ highlight.title }}
|
||||
</span>
|
||||
<p
|
||||
class="text-slate-500 typo-p-xs lg:typo-p-sm mt-1 text-center"
|
||||
>
|
||||
<p class="text-slate-500 typo-p-xs lg:typo-p-sm mt-1 text-center">
|
||||
{{ highlight.description }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -3,13 +3,15 @@
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
modalId: string;
|
||||
};
|
||||
|
||||
defineProps<Props>();
|
||||
const props = defineProps<Props>();
|
||||
const { modalId } = toRefs(props);
|
||||
|
||||
// state
|
||||
|
||||
const isSideShow = ref(false);
|
||||
const isSideShow = useState(`side-modal-${modalId.value}`, () => false);
|
||||
|
||||
const isLocked = useScrollLock(window);
|
||||
|
||||
|
||||
@@ -90,6 +90,8 @@ const changeSlide = (id: number) => {
|
||||
<NuxtImg
|
||||
class="absolute object-cover size-full"
|
||||
:src="slide.image"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
:alt="String(slide.id)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -67,6 +67,8 @@ const parallaxStyle = computed(() => {
|
||||
<NuxtImg
|
||||
:id="`product-image-${id}`"
|
||||
:src="picture"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
class="group-hover:scale-105 transition-transform duration-200 size-full object-contain absolute inset-0"
|
||||
alt="product-background"
|
||||
/>
|
||||
|
||||
@@ -42,6 +42,8 @@ const onSlideChange = (swiper: SwiperClass) => {
|
||||
:style="{
|
||||
filter: 'drop-shadow(0px 0px 20px rgba(0, 0, 0, 0.4))',
|
||||
}"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
src="/img/heymlz/heymlz-category-seat.gif"
|
||||
/>
|
||||
<Swiper
|
||||
|
||||
@@ -209,11 +209,15 @@ onUnmounted(() => {
|
||||
webkit-playsinline
|
||||
class="slide-video absolute inset-0 size-full object-cover brightness-90"
|
||||
:src="slide.video"
|
||||
poster="/img/test-thumbnail.jpeg"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<NuxtImg
|
||||
v-else
|
||||
preload
|
||||
loading="eager"
|
||||
fetch-priority="high"
|
||||
class="absolute inset-0 size-full object-cover"
|
||||
:src="slide.image!"
|
||||
:alt="slide.title"
|
||||
@@ -259,13 +263,15 @@ onUnmounted(() => {
|
||||
:to="slide.link"
|
||||
class="relative max-sm:hidden"
|
||||
>
|
||||
<NuxtImg
|
||||
<!-- <NuxtImg
|
||||
class="aspect-square w-[110px] lg:w-[120px] translate-y-[-75px] lg:translate-y-[-82px] absolute left-1/2 -translate-x-1/2 z-10"
|
||||
:style="{
|
||||
filter: 'drop-shadow(0px 0px 20px rgba(0, 0, 0, 0.4))',
|
||||
}"
|
||||
src="/img/heymlz/heymlz-seat.gif"
|
||||
/>
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
/> -->
|
||||
|
||||
<Button
|
||||
variant="primary"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
// import
|
||||
|
||||
import { Swiper, SwiperSlide } from "swiper/vue";
|
||||
@@ -7,7 +6,7 @@ import type { SwiperClass } from "swiper/react";
|
||||
|
||||
// types
|
||||
|
||||
type Props = {}
|
||||
type Props = {};
|
||||
|
||||
// props
|
||||
|
||||
@@ -23,7 +22,6 @@ const swiper_instance = ref<SwiperClass | null>(null);
|
||||
const onSwiper = (swiper: SwiperClass) => {
|
||||
swiper_instance.value = swiper;
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -31,6 +29,8 @@ const onSwiper = (swiper: SwiperClass) => {
|
||||
<NuxtImg
|
||||
class="absolute size-full object-cover"
|
||||
src="/img/hero-bg.jpg"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
alt=""
|
||||
/>
|
||||
<div class="absolute bg-black/60 size-full z-10" />
|
||||
@@ -50,15 +50,18 @@ const onSwiper = (swiper: SwiperClass) => {
|
||||
>
|
||||
<div class="flex justify-center items-center">
|
||||
<div class="max-w-[900px] px-4 text-white flex flex-col items-center gap-4">
|
||||
<Icon name="ci:instagram" size="28" class="**:stroke-white" />
|
||||
<p class="text-base xs:text-lg sm:typo-h-6 lg:typo-h-5 !font-normal !leading-[150%] lg:leading-[175%] max-sm:px-4 sm:max-w-[600px] lg:max-w-[800px] text-center">
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با
|
||||
استفاده از طراحان گرافیک است. چاپگرها و متون بلکه روزنامه و مجله
|
||||
در ستون و سطرآنچنان که لازم.
|
||||
<Icon
|
||||
name="ci:instagram"
|
||||
size="28"
|
||||
class="**:stroke-white"
|
||||
/>
|
||||
<p
|
||||
class="text-base xs:text-lg sm:typo-h-6 lg:typo-h-5 !font-normal !leading-[150%] lg:leading-[175%] max-sm:px-4 sm:max-w-[600px] lg:max-w-[800px] text-center"
|
||||
>
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک
|
||||
است. چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم.
|
||||
</p>
|
||||
<span class="typo-p-sm md:typo-p-xl text-center">
|
||||
- منصور مرزبان
|
||||
</span>
|
||||
<span class="typo-p-sm md:typo-p-xl text-center"> - منصور مرزبان </span>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
@@ -69,10 +72,8 @@ const onSwiper = (swiper: SwiperClass) => {
|
||||
v-for="(i, index) in 6"
|
||||
:class="swiper_instance?.realIndex === index ? 'bg-white' : 'bg-transparent'"
|
||||
class="border border-white size-1.5 md:size-2 rounded-full transition-all duration-200"
|
||||
>
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -88,6 +88,8 @@ watch(
|
||||
:src="homeData!.difreance_section.image1"
|
||||
class="select-none absolute size-full object-cover transition-[filter] duration-250 brightness-[95%]"
|
||||
:alt="homeData!.difreance_section.title1"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
/>
|
||||
<video
|
||||
v-else
|
||||
@@ -110,6 +112,8 @@ watch(
|
||||
:src="homeData!.difreance_section.image2"
|
||||
class="overlay-image select-none absolute object-cover size-full transition-[filter] duration-250 brightness-[95%]"
|
||||
:alt="homeData!.difreance_section.title2"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
/>
|
||||
<video
|
||||
v-else
|
||||
@@ -126,9 +130,7 @@ watch(
|
||||
:style="{
|
||||
left: `${clipPathPercent}%`,
|
||||
}"
|
||||
:class="[
|
||||
activeSlideVideo !== 'none' ? 'opacity-10' : '',
|
||||
]"
|
||||
:class="[activeSlideVideo !== 'none' ? 'opacity-10' : '']"
|
||||
class="select-none w-[5px] sm:w-2 bg-black h-full absolute left-0 flex items-center justify-center transition-opacity duration-250"
|
||||
>
|
||||
<div
|
||||
@@ -143,9 +145,7 @@ watch(
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="absolute bottom-0 p-6 md:p-10 w-full flex justify-between items-end transition-opacity"
|
||||
>
|
||||
<div class="absolute bottom-0 p-6 md:p-10 w-full flex justify-between items-end transition-opacity">
|
||||
<div
|
||||
class="flex flex-col gap-2 text-black transition-opacity"
|
||||
:class="activeSlideVideo === 'right' ? 'opacity-0' : ''"
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
<script setup lang="ts">
|
||||
// import
|
||||
|
||||
import useHomeData from "~/composables/api/home/useHomeData";
|
||||
|
||||
// state
|
||||
|
||||
const { data: homeData } = useHomeData();
|
||||
|
||||
const clipPathPercent = ref(49);
|
||||
|
||||
const activeSlideVideo = ref<"left" | "right" | "none">("none");
|
||||
|
||||
const draggableEl = ref<HTMLElement | null>(null);
|
||||
const previewContainerEl = ref<HTMLElement | null>(null);
|
||||
|
||||
const heymlzElement = useTemplateRef<HTMLDivElement>("heymlzElement");
|
||||
const heymlzElementIsVisible = useElementVisibility(heymlzElement, {
|
||||
rootMargin: "0px 0px -40% 0px",
|
||||
});
|
||||
|
||||
const showHeymlzAnimation = ref(false);
|
||||
|
||||
const { x: dragAxisX } = useDraggable(draggableEl, {
|
||||
initialValue: { x: 0, y: 0 },
|
||||
axis: "x",
|
||||
});
|
||||
|
||||
// watch
|
||||
|
||||
watch(
|
||||
heymlzElementIsVisible,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
setTimeout(() => {
|
||||
showHeymlzAnimation.value = true;
|
||||
setTimeout(() => {
|
||||
showHeymlzAnimation.value = false;
|
||||
}, 3200);
|
||||
}, 400);
|
||||
}
|
||||
},
|
||||
{
|
||||
once: true,
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => clipPathPercent.value,
|
||||
(newValue) => {
|
||||
if (newValue > 80) {
|
||||
activeSlideVideo.value = "right";
|
||||
} else if (newValue < 20) {
|
||||
activeSlideVideo.value = "left";
|
||||
} else {
|
||||
activeSlideVideo.value = "none";
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => dragAxisX.value,
|
||||
(newValue) => {
|
||||
const clientRect = previewContainerEl.value?.getBoundingClientRect()!;
|
||||
const percent = clientRect.width / 100;
|
||||
const clipPercent = (newValue + draggableEl.value!.clientWidth / 2 - clientRect.x - 8) / percent;
|
||||
if (clipPercent >= 1 && clipPercent <= 99) {
|
||||
clipPathPercent.value = clipPercent;
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container mb-40 lg:mb-40 max-lg:mt-20 lg:-mt-32">
|
||||
<div>
|
||||
<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-h-6 md:typo-h-5 lg:typo-h-3 text-black"> تفاوت محصلات ما را ببینید </span>
|
||||
</div>
|
||||
<div
|
||||
ref="previewContainerEl"
|
||||
class="rounded-200 overflow-hidden h-[70svh] relative"
|
||||
>
|
||||
<Transition name="fade">
|
||||
<NuxtImg
|
||||
v-if="activeSlideVideo !== 'right'"
|
||||
:src="homeData!.difreance_section.image1"
|
||||
:class="showHeymlzAnimation ? 'brightness-25 blur-sm' : 'brightness-[95%] blur-[0px]'"
|
||||
class="select-none absolute size-full object-cover transition-[filter] duration-250"
|
||||
:alt="homeData!.difreance_section.title1"
|
||||
/>
|
||||
<video
|
||||
v-else
|
||||
autoplay
|
||||
muted
|
||||
playsinline
|
||||
webkit-playsinline
|
||||
src="/video/vid-3.mp4"
|
||||
class="select-none absolute size-full object-cover brightness-[95%]"
|
||||
/>
|
||||
</Transition>
|
||||
|
||||
<div
|
||||
class="absolute size-full right-0 w-full"
|
||||
ref="heymlzElement"
|
||||
>
|
||||
<Transition name="fade">
|
||||
<NuxtImg
|
||||
v-if="activeSlideVideo !== 'left'"
|
||||
:src="homeData!.difreance_section.image2"
|
||||
:class="showHeymlzAnimation ? 'brightness-25 blur-sm' : 'brightness-[95%] blur-[0px]'"
|
||||
class="overlay-image select-none absolute object-cover size-full transition-[filter] duration-250"
|
||||
:alt="homeData!.difreance_section.title2"
|
||||
/>
|
||||
<video
|
||||
v-else
|
||||
autoplay
|
||||
muted
|
||||
playsinline
|
||||
webkit-playsinline
|
||||
src="/video/vid-3.mp4"
|
||||
class="overlay-image select-none absolute object-cover size-full brightness-[95%]"
|
||||
/>
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
name="fade"
|
||||
:duration="250"
|
||||
>
|
||||
<NuxtImg
|
||||
v-if="showHeymlzAnimation"
|
||||
src="/img/heymlz/heymlz-pullingg.gif"
|
||||
class="size-[250px] sm:size-[400px] absolute translate-x-[-62px] sm:translate-x-[-107px] z-10 top-[50%] -translate-y-1/2"
|
||||
:style="{
|
||||
left: `${clipPathPercent}%`,
|
||||
filter: 'drop-shadow(0px 0px 20px rgba(0, 0, 0, 0.3))',
|
||||
}"
|
||||
/>
|
||||
</Transition>
|
||||
|
||||
<div
|
||||
:style="{
|
||||
left: `${clipPathPercent}%`,
|
||||
}"
|
||||
:class="[
|
||||
activeSlideVideo !== 'none' ? 'opacity-10' : '',
|
||||
showHeymlzAnimation ? 'bg-neutral-200' : 'bg-black',
|
||||
]"
|
||||
class="select-none w-[5px] sm:w-2 h-full absolute left-0 flex items-center justify-center transition-opacity duration-250"
|
||||
>
|
||||
<div
|
||||
ref="draggableEl"
|
||||
:class="showHeymlzAnimation ? 'bg-neutral-200' : 'bg-black'"
|
||||
class="touch-none cursor-grab hover:scale-115 transition-transform rounded-full absolute size-9 sm:size-11 flex items-center justify-center"
|
||||
>
|
||||
<Icon
|
||||
name="ci:arrows"
|
||||
class="transition-all size-5 sm:size-6"
|
||||
:class="showHeymlzAnimation ? '**:stroke-black' : '**:stroke-white'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
:class="showHeymlzAnimation ? 'opacity-0' : 'opacity-100'"
|
||||
class="absolute bottom-0 p-6 md:p-10 w-full flex justify-between items-end transition-opacity"
|
||||
>
|
||||
<div
|
||||
class="flex flex-col gap-2 text-black transition-opacity"
|
||||
:class="activeSlideVideo === 'right' ? 'opacity-0' : ''"
|
||||
>
|
||||
<span class="typo-p-sm md:typo-p-md">
|
||||
{{ homeData!.difreance_section.description1 }}
|
||||
</span>
|
||||
<NuxtLink
|
||||
:to="homeData!.difreance_section.link1"
|
||||
class="typo-h-6 md:typo-h-5 lg:typo-h-3"
|
||||
>
|
||||
{{ homeData!.difreance_section.title1 }}
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-col gap-2 text-black transition-opacity"
|
||||
:class="activeSlideVideo === 'left' ? 'opacity-0' : ''"
|
||||
>
|
||||
<span class="typo-p-sm md:typo-p-md text-end">
|
||||
{{ homeData!.difreance_section.description2 }}
|
||||
</span>
|
||||
<NuxtLink
|
||||
:to="homeData!.difreance_section.link2"
|
||||
class="typo-h-6 md:typo-h-5 lg:typo-h-3 text-end"
|
||||
>
|
||||
{{ homeData!.difreance_section.title2 }}
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.overlay-image {
|
||||
clip-path: polygon(v-bind('clipPathPercent + "%"') 0, 100% 0, 100% 100%, v-bind('clipPathPercent + "%"') 100%);
|
||||
}
|
||||
</style>
|
||||
@@ -106,6 +106,8 @@ const childImageVariants = {
|
||||
<NuxtImg
|
||||
:src="slide.background_image"
|
||||
class="absolute size-full object-cover"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
/>
|
||||
</motion.div>
|
||||
</template>
|
||||
@@ -133,6 +135,8 @@ const childImageVariants = {
|
||||
<NuxtImg
|
||||
class="w-[130px] sm:w-[180px] lg:w-[250px] xl:w-[300px] z-20 mt-40"
|
||||
:src="slide.image3"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
alt=""
|
||||
/>
|
||||
</motion.div>
|
||||
@@ -144,6 +148,8 @@ const childImageVariants = {
|
||||
<NuxtImg
|
||||
class="w-[130px] sm:w-[180px] lg:w-[250px] xl:w-[300px] z-20"
|
||||
:src="slide.image2"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
alt=""
|
||||
/>
|
||||
</motion.div>
|
||||
@@ -155,6 +161,8 @@ const childImageVariants = {
|
||||
<NuxtImg
|
||||
class="w-[130px] sm:w-[180px] lg:w-[250px] xl:w-[300px] z-20 mt-40"
|
||||
:src="slide.image1"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
alt=""
|
||||
/>
|
||||
</motion.div>
|
||||
@@ -211,17 +219,19 @@ const childImageVariants = {
|
||||
<span class="text-white typo-h-6 sm:typo-h-5 lg:typo-h-4 xl:typo-h-3">
|
||||
{{ slide.title }}
|
||||
</span>
|
||||
<p
|
||||
<!-- <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 }}
|
||||
</p>
|
||||
</p> -->
|
||||
<NuxtLink
|
||||
:to="`/resellers/category/${slide.id}`"
|
||||
class="relative"
|
||||
>
|
||||
<NuxtImg
|
||||
src="/img/heymlz/heymlz-falling.gif"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
class="absolute top-[101px] sm:top-[100px] lg:top-[117px] left-1/2 -translate-1/2 w-[200px] lg:w-[250px] drop-shadow-md"
|
||||
/>
|
||||
<Button
|
||||
|
||||
@@ -187,6 +187,8 @@ whenever(
|
||||
<NuxtImg
|
||||
class="size-[225px] sm:size-[250px] drop-shadow-2xl"
|
||||
src="/img/heymlz/heymlz-small-idle.gif"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
@@ -199,6 +201,8 @@ whenever(
|
||||
<NuxtImg
|
||||
class="size-[225px] sm:size-[250px] drop-shadow-2xl"
|
||||
src="/img/heymlz/heymlz-small-idle.gif"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
alt=""
|
||||
/>
|
||||
<div class="flex flex-col gap-4 items-center">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import useGetAccount from '~/composables/api/account/useGetAccount';
|
||||
import useGetAccount from "~/composables/api/account/useGetAccount";
|
||||
|
||||
// types
|
||||
|
||||
@@ -85,31 +85,34 @@ onMounted(() => {
|
||||
src="/img/heymlz/heymlz-full-body.jpg"
|
||||
class="size-full object-cover absolute"
|
||||
alt="profile"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
/>
|
||||
<NuxtImg
|
||||
v-else
|
||||
:src="account?.profile_photo ?? ''"
|
||||
class="size-full object-cover absolute"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
alt="profile"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="rounded-150 px-4 py-3 whitespace-pre-wrap overflow-hidden"
|
||||
:class="
|
||||
reverse
|
||||
? 'bg-slate-100 text-slate-600'
|
||||
: 'bg-black text-white'
|
||||
"
|
||||
:class="reverse ? 'bg-slate-100 text-slate-600' : 'bg-black text-white'"
|
||||
>
|
||||
<div
|
||||
v-if="!loadingContent"
|
||||
:id="`chat-message-content-${id}`"
|
||||
class="typo-p-sm font-normal whitespace-pre-wrap"
|
||||
v-html="content"
|
||||
>
|
||||
</div>
|
||||
></div>
|
||||
|
||||
<Icon v-else name="svg-spinners:3-dots-bounce" size="20" />
|
||||
<Icon
|
||||
v-else
|
||||
name="svg-spinners:3-dots-bounce"
|
||||
size="20"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -127,6 +127,8 @@ const limitedComments = computed(() => {
|
||||
>
|
||||
<NuxtImg
|
||||
src="/img/heymlz/heymlz-contact-us.gif"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
class="w-[200px] lg:w-[300px] translate-y-[-25px]"
|
||||
/>
|
||||
<span class="text-xl text-black font-semibold translate-y-[-25px]"> هیچ نظری ثبت نشده است </span>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script lang="ts" setup>
|
||||
|
||||
// import
|
||||
|
||||
import type { ProductVariantProvideType } from "~/pages/product/[id].vue";
|
||||
@@ -7,7 +6,6 @@ import type { ProductVariantProvideType } from "~/pages/product/[id].vue";
|
||||
// provide / inject
|
||||
|
||||
const { selectedVariant } = inject("productVariant") as ProductVariantProvideType;
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -20,25 +18,21 @@ const { selectedVariant } = inject("productVariant") as ProductVariantProvideTyp
|
||||
<Accordion />
|
||||
</div>
|
||||
<div class="w-full lg:w-[450px] xl:w-[600px]">
|
||||
<div
|
||||
class="w-full bg-slate-50 rounded-xl flex-col-center px-5 py-16 sm:p-[5rem] gap-[1.5rem]"
|
||||
>
|
||||
<div class="w-full bg-slate-50 rounded-xl flex-col-center px-5 py-16 sm:p-[5rem] gap-[1.5rem]">
|
||||
<span class="typo-h-6 mb-8">داخل جعبه چیه؟</span>
|
||||
<div
|
||||
class="w-full grid grid-cols-2 gap-y-[1.5rem] sm:gap-x-[3rem]"
|
||||
>
|
||||
<div class="w-full grid grid-cols-2 gap-y-[1.5rem] sm:gap-x-[3rem]">
|
||||
<div
|
||||
v-for="inPackItem in selectedVariant!.in_pack_items"
|
||||
class="w-full flex-col-center gap-[.75rem]"
|
||||
>
|
||||
<div
|
||||
class="size-[6.25rem] rounded-full border-slate-200 bg-white flex-center"
|
||||
>
|
||||
<div class="size-[6.25rem] rounded-full border-slate-200 bg-white flex-center">
|
||||
<div class="size-11 relative">
|
||||
<NuxtImg
|
||||
class="size-full absolute object-cover"
|
||||
:src="inPackItem.cover"
|
||||
:alt="inPackItem.item_title"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -253,6 +253,26 @@ watch(
|
||||
<UpdateQuantity v-else-if="selectedVariant!.in_stock > 0" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex items-center gap-4"
|
||||
v-if="selectedVariant!.cart_quantity === 0"
|
||||
>
|
||||
<Button
|
||||
variant="secondary"
|
||||
class="w-full rounded-full h-full max-sm:h-[48px]"
|
||||
end-icon="bi:percent"
|
||||
>
|
||||
۱۰۰ تومان تخفیف بگیر
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
class="w-full rounded-full h-full max-sm:h-[48px]"
|
||||
end-icon="bi:person-check"
|
||||
>
|
||||
پورسانت بگیر
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<InfoCard />
|
||||
|
||||
<Share />
|
||||
|
||||
@@ -12,6 +12,8 @@ const router = useRouter();
|
||||
|
||||
const params = inject("params") as GetProductsFilters;
|
||||
|
||||
const isSideShow = useState("side-modal-product-filters");
|
||||
|
||||
const currentCategory = computed({
|
||||
get: () => {
|
||||
return Array.isArray(route.params.slug) ? route.params.slug[1] ?? undefined : undefined;
|
||||
@@ -238,6 +240,26 @@ watch(
|
||||
/>
|
||||
</div>
|
||||
</Transition>
|
||||
<div class="flex items-center gap-4 w-full">
|
||||
<Button
|
||||
:disabled="productsIsPending"
|
||||
variant="primary"
|
||||
@click="() => (isSideShow = false)"
|
||||
class="w-full rounded-full py-4 !cursor-pointer disabled:pointer-events-none z-[3]"
|
||||
>
|
||||
<Transition
|
||||
name="fade"
|
||||
mode="out-in"
|
||||
>
|
||||
<span class="flex-center gap-3">
|
||||
ثبت فیلتر جدید
|
||||
<Icon
|
||||
name="ci:plus"
|
||||
size="20"
|
||||
/>
|
||||
</span>
|
||||
</Transition>
|
||||
</Button>
|
||||
<Button
|
||||
:disabled="productsIsPending"
|
||||
variant="solid"
|
||||
@@ -272,4 +294,5 @@ watch(
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -38,9 +38,7 @@ const profile = computed(() => {
|
||||
});
|
||||
|
||||
const username = computed(() => {
|
||||
return is_user.value
|
||||
? `${account.value?.first_name} ${account.value?.last_name}`
|
||||
: "ادمین پشتیبانی هی ملز";
|
||||
return is_user.value ? `${account.value?.first_name} ${account.value?.last_name}` : "ادمین پشتیبانی هی ملز";
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -54,7 +52,12 @@ const username = computed(() => {
|
||||
:class="is_user ? 'rounded-br-none' : 'rounded-bl-none'"
|
||||
>
|
||||
<div class="w-2/12 flex items-start justify-start">
|
||||
<NuxtImg :src="profile" class="size-16 rounded-full" />
|
||||
<NuxtImg
|
||||
:src="profile"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
class="size-16 rounded-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="w-10/12 flex flex-col items-start pt-2">
|
||||
@@ -65,9 +68,7 @@ const username = computed(() => {
|
||||
>
|
||||
{{ timeAgo }}
|
||||
</p>
|
||||
<p
|
||||
class="text-xs font-semibold text-dynamic-secondary line-clamp-1"
|
||||
>
|
||||
<p class="text-xs font-semibold text-dynamic-secondary line-clamp-1">
|
||||
{{ username }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -76,7 +76,9 @@ const useSlider = ({ duration = 0, count }: Props) => {
|
||||
restartSliderTimer();
|
||||
});
|
||||
|
||||
onUnmounted(() => {});
|
||||
onUnmounted(() => {
|
||||
restartSliderTimer();
|
||||
});
|
||||
|
||||
return {
|
||||
activeSlide,
|
||||
|
||||
@@ -56,7 +56,7 @@ const hasCartItem = computed(() => !!cart.value && cart.value.items.length! > 0)
|
||||
<div class="hidden w-3/12 shrink-0 lg:block"> </div>
|
||||
</div>
|
||||
<div
|
||||
class="w-full flex flex-col items-center relative justify-between gap-4 lg:gap-6 lg:flex-row lg:items-start"
|
||||
class="w-full flex flex-col items-center relative justify-between gap-8 lg:gap-6 lg:flex-row lg:items-start"
|
||||
>
|
||||
<div
|
||||
class="flex flex-col w-full gap-4 lg:gap-6 shrink-0"
|
||||
|
||||
+11
-11
@@ -61,17 +61,17 @@ export default defineNuxtConfig({
|
||||
},
|
||||
|
||||
modules: [
|
||||
[
|
||||
"@nuxtjs/google-fonts",
|
||||
{
|
||||
families: {
|
||||
"DM Sans": "100..900",
|
||||
Inter: "100..900",
|
||||
download: true,
|
||||
inject: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
// [
|
||||
// "@nuxtjs/google-fonts",
|
||||
// {
|
||||
// families: {
|
||||
// "DM Sans": "100..900",
|
||||
// Inter: "100..900",
|
||||
// download: true,
|
||||
// inject: false,
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
"@nuxt/icon",
|
||||
"reka-ui/nuxt",
|
||||
"@vueuse/nuxt",
|
||||
|
||||
Generated
-26
@@ -25,7 +25,6 @@
|
||||
"date-fns-jalali": "^4.1.0-0",
|
||||
"fast-average-color": "^9.4.0",
|
||||
"gsap": "^3.12.7",
|
||||
"highlight.js": "^11.11.1",
|
||||
"jalali-ts": "^8.0.0",
|
||||
"motion-v": "^1.1.1",
|
||||
"nuxt": "^3.15.4",
|
||||
@@ -36,7 +35,6 @@
|
||||
"vue": "latest",
|
||||
"vue-image-zoomer": "^2.4.4",
|
||||
"vue-router": "latest",
|
||||
"vue-scrollto": "^2.20.0",
|
||||
"vue-skeletor": "^1.0.6",
|
||||
"vue3-marquee": "^4.2.2",
|
||||
"vue3-persian-datetime-picker": "^1.2.2",
|
||||
@@ -6555,12 +6553,6 @@
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bezier-easing": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bezier-easing/-/bezier-easing-2.1.0.tgz",
|
||||
"integrity": "sha512-gbIqZ/eslnUFC1tjEvtz0sgx+xTK20wDnYMIA27VA04R7w6xxXQPZDbibjA9DTWZRA2CXtwHykkVzlCaAJAZig==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bindings": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
||||
@@ -9240,15 +9232,6 @@
|
||||
"integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/highlight.js": {
|
||||
"version": "11.11.1",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz",
|
||||
"integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hookable": {
|
||||
"version": "5.5.3",
|
||||
"resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
|
||||
@@ -16810,15 +16793,6 @@
|
||||
"vue": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-scrollto": {
|
||||
"version": "2.20.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-scrollto/-/vue-scrollto-2.20.0.tgz",
|
||||
"integrity": "sha512-7i+AGKJTThZnMEkhIPgrZjyAX+fXV7/rGdg+CV283uZZwCxwiMXaBLTmIc5RTA4uwGnT+E6eJle3mXQfM2OD3A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bezier-easing": "2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-skeletor": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/vue-skeletor/-/vue-skeletor-1.0.6.tgz",
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
"date-fns-jalali": "^4.1.0-0",
|
||||
"fast-average-color": "^9.4.0",
|
||||
"gsap": "^3.12.7",
|
||||
"highlight.js": "^11.11.1",
|
||||
"jalali-ts": "^8.0.0",
|
||||
"motion-v": "^1.1.1",
|
||||
"nuxt": "^3.15.4",
|
||||
@@ -44,7 +43,6 @@
|
||||
"vue": "latest",
|
||||
"vue-image-zoomer": "^2.4.4",
|
||||
"vue-router": "latest",
|
||||
"vue-scrollto": "^2.20.0",
|
||||
"vue-skeletor": "^1.0.6",
|
||||
"vue3-marquee": "^4.2.2",
|
||||
"vue3-persian-datetime-picker": "^1.2.2",
|
||||
|
||||
@@ -38,6 +38,9 @@ if (response.isError) {
|
||||
class="absolute object-cover size-full"
|
||||
:alt="article!.title"
|
||||
:src="article!.cover_image"
|
||||
preload
|
||||
loading="eager"
|
||||
fetch-priority="high"
|
||||
/>
|
||||
<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">
|
||||
@@ -62,6 +65,8 @@ if (response.isError) {
|
||||
class="size-full object-cover absolute"
|
||||
:src="article!.author.profile_photo"
|
||||
alt="article-author"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
/>
|
||||
</div>
|
||||
<span class="typo-label-sm">
|
||||
|
||||
@@ -61,6 +61,8 @@ const deliveryData = ref<DeliveryData>({
|
||||
<div class="flex items-center gap-3 py-3">
|
||||
<NuxtImg
|
||||
src="/img/location.gif"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
class="size-12 pb-1 -mr-3"
|
||||
/>
|
||||
<span class="typo-sub-h-xl -mr-3"> آدرس های شما </span>
|
||||
|
||||
@@ -6,7 +6,7 @@ import useGetCartOrders from "~/composables/api/orders/useGetCartOrders";
|
||||
// meta
|
||||
|
||||
useSeoMeta({
|
||||
title : "سبد خرید"
|
||||
title: "سبد خرید",
|
||||
});
|
||||
|
||||
definePageMeta({
|
||||
@@ -22,9 +22,7 @@ const { data: cart, isLoading: cartIsLoading } = useGetCartOrders();
|
||||
|
||||
// computed
|
||||
|
||||
const hasCartItem = computed(
|
||||
() => !!cart.value && cart.value.items.length! > 0
|
||||
);
|
||||
const hasCartItem = computed(() => !!cart.value && cart.value.items.length! > 0);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -38,11 +36,17 @@ const hasCartItem = computed(
|
||||
class="!w-36 !h-[43px] !rounded-lg"
|
||||
/>
|
||||
|
||||
<div v-else class="flex items-center w-full gap-2 lg:w-1/2">
|
||||
<NuxtImg src="/img/box.gif" class="size-12 pb-2" />
|
||||
<p class="font-semibold lg:text-lg text-black">
|
||||
{{ cart?.items.length }} مرسوله
|
||||
</p>
|
||||
<div
|
||||
v-else
|
||||
class="flex items-center w-full gap-2 lg:w-1/2"
|
||||
>
|
||||
<NuxtImg
|
||||
src="/img/box.gif"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
class="size-12 pb-2"
|
||||
/>
|
||||
<p class="font-semibold lg:text-lg text-black">{{ cart?.items.length }} مرسوله</p>
|
||||
</div>
|
||||
|
||||
<Skeleton
|
||||
@@ -53,7 +57,10 @@ const hasCartItem = computed(
|
||||
<DeleteCartAllModal v-else />
|
||||
</div>
|
||||
|
||||
<ul v-if="cartIsLoading" class="w-full flex flex-col gap-4 lg:gap-6">
|
||||
<ul
|
||||
v-if="cartIsLoading"
|
||||
class="w-full flex flex-col gap-4 lg:gap-6"
|
||||
>
|
||||
<Skeleton
|
||||
v-for="i in 4"
|
||||
:key="i"
|
||||
@@ -61,20 +68,38 @@ const hasCartItem = computed(
|
||||
/>
|
||||
</ul>
|
||||
|
||||
<div v-else class="w-full h-max">
|
||||
<div v-if="!cart?.items.length" class="flex flex-grow w-full">
|
||||
<Placeholder title="سبد خرید شما خالی است :(" icon="bi:cart">
|
||||
<div
|
||||
v-else
|
||||
class="w-full h-max"
|
||||
>
|
||||
<div
|
||||
v-if="!cart?.items.length"
|
||||
class="flex flex-grow w-full"
|
||||
>
|
||||
<Placeholder
|
||||
title="سبد خرید شما خالی است :("
|
||||
icon="bi:cart"
|
||||
>
|
||||
<template #actions>
|
||||
<NuxtLink :to="{ name: 'products' }">
|
||||
<Button class="rounded-full" size="md">
|
||||
<Button
|
||||
class="rounded-full"
|
||||
size="md"
|
||||
>
|
||||
<span> مشاهده محصولات </span>
|
||||
<Icon name="ci:left-rotation" size="24" />
|
||||
<Icon
|
||||
name="ci:left-rotation"
|
||||
size="24"
|
||||
/>
|
||||
</Button>
|
||||
</NuxtLink>
|
||||
</template>
|
||||
</Placeholder>
|
||||
</div>
|
||||
<ul v-else class="w-full flex flex-col gap-4 lg:gap-6">
|
||||
<ul
|
||||
v-else
|
||||
class="w-full flex flex-col gap-4 lg:gap-6"
|
||||
>
|
||||
<CartItem
|
||||
v-for="(item, index) in cart?.items"
|
||||
:key="index"
|
||||
|
||||
@@ -22,7 +22,7 @@ if (response.isError) {
|
||||
|
||||
<template>
|
||||
<div class="w-full">
|
||||
<LoadingOverlay />
|
||||
<!-- <LoadingOverlay /> -->
|
||||
<Hero class="mb-20 max-md:mt-[80px]" />
|
||||
<Preview />
|
||||
<ProductsShowcase class="lg:mb-12" />
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
|
||||
// import
|
||||
|
||||
import hljs from "highlight.js";
|
||||
import json from "highlight.js/lib/languages/json";
|
||||
import xml from "highlight.js/lib/languages/xml";
|
||||
import "highlight.js/styles/atom-one-dark.css";
|
||||
import LogDate from "~/components/server-logs/LogDate.vue";
|
||||
import { useQuery } from "@tanstack/vue-query";
|
||||
|
||||
// meta
|
||||
|
||||
definePageMeta({
|
||||
middleware: "check-is-debug",
|
||||
layout: "none"
|
||||
});
|
||||
|
||||
// state
|
||||
|
||||
const { $axios: axios } = useNuxtApp();
|
||||
|
||||
const { data: serverLogs, isFetching, suspense } = useQuery({
|
||||
queryKey: ["server-logs"],
|
||||
queryFn: async () => {
|
||||
const response = await axios.get<AxiosLogType[]>("http://localhost:3000/api/server-logger");
|
||||
return response.data.reverse();
|
||||
},
|
||||
refetchInterval: 5000,
|
||||
staleTime: 0
|
||||
});
|
||||
|
||||
await suspense();
|
||||
|
||||
// computed
|
||||
|
||||
const logIcon = (status: number) => {
|
||||
if (status >= 200 && status < 300) return "bi:check-circle-fill";
|
||||
return "bi:x-circle-fill";
|
||||
};
|
||||
|
||||
// lifecycle
|
||||
|
||||
onMounted(() => {
|
||||
hljs.registerLanguage("json", json);
|
||||
hljs.registerLanguage("xml", xml);
|
||||
|
||||
hljs.highlightAll();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-neutral-900 w-full min-h-svh py-32">
|
||||
<div class="fixed top-10 right-1/2 translate-x-1/2 flex-center" v-if="isFetching">
|
||||
<Icon
|
||||
name="svg-spinners:180-ring-with-bg"
|
||||
class="size-12 mb-1 **:fill-neutral-500"
|
||||
/>
|
||||
</div>
|
||||
<div class="w-full container flex flex-col gap-8">
|
||||
<div
|
||||
v-for="(log,index) in serverLogs"
|
||||
:key="index"
|
||||
class="border-2 p-5 rounded-xl log-item-animation"
|
||||
:class="{
|
||||
'bg-success-950/30 border-success-800' : log.status >= 200 && log.status < 300,
|
||||
'bg-danger-950/30 border-danger-800' : log.status >= 400 && log.status < 600,
|
||||
}"
|
||||
>
|
||||
<div class="flex items-center gap-4 mt-4">
|
||||
<Icon
|
||||
:name="logIcon(log.status)"
|
||||
class="size-8 mb-1"
|
||||
:class="{
|
||||
'**:fill-success-500' : log.status >= 200 && log.status < 300,
|
||||
'**:fill-danger-500' : log.status >= 400 && log.status < 600,
|
||||
}"
|
||||
/>
|
||||
<h3 class="text-white font-medium text-3xl">
|
||||
{{ log.url }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 py-8">
|
||||
<div
|
||||
class="px-3 pb-1 pt-1.5 rounded-lg uppercase font-bold text-white"
|
||||
:class="{
|
||||
'bg-success-500' : log.status >= 200 && log.status < 300,
|
||||
'bg-danger-500' : log.status >= 400 && log.status < 600,
|
||||
}"
|
||||
>
|
||||
{{ log.method }}
|
||||
</div>
|
||||
<div
|
||||
class="px-3 pb-1 pt-1.5 rounded-lg font-bold text-white"
|
||||
:class="{
|
||||
'bg-success-500' : log.status >= 200 && log.status < 300,
|
||||
'bg-danger-500' : log.status >= 400 && log.status < 600,
|
||||
}"
|
||||
>
|
||||
{{ log.status }}
|
||||
</div>
|
||||
<LogDate :date="log.date" />
|
||||
</div>
|
||||
<details class="text-white">
|
||||
<summary class="cursor-pointer select-none">Details :</summary>
|
||||
<div class="flex flex-col gap-2 mt-2 ml-4">
|
||||
<details
|
||||
v-if="log.response && typeof log.response === 'string' && (log.response.startsWith('<!DOCTYPE html>') || log.response.startsWith('<html>'))"
|
||||
class="text-white"
|
||||
>
|
||||
<summary class="cursor-pointer select-none">Preview :</summary>
|
||||
<iframe class="w-full h-[500px] bg-white" :srcdoc="log.response"></iframe>
|
||||
</details>
|
||||
<details v-if="log.response" class="text-white">
|
||||
<summary class="cursor-pointer select-none">Response :</summary>
|
||||
<pre style="tab-size: 2">
|
||||
<code class="whitespace-pre-wrap">
|
||||
{{ String(log.response) }}
|
||||
</code>
|
||||
</pre>
|
||||
</details>
|
||||
<details class="text-white">
|
||||
<summary class="cursor-pointer select-none">Req Headers :</summary>
|
||||
<pre style="tab-size: 2">
|
||||
<code class="whitespace-pre-line">
|
||||
{{ log.requestHeaders }}
|
||||
</code>
|
||||
</pre>
|
||||
</details>
|
||||
<details class="text-white">
|
||||
<summary class="cursor-pointer select-none">Res Headers :</summary>
|
||||
<pre style="tab-size: 2">
|
||||
<code class="whitespace-pre-line">
|
||||
{{ log.responseHeaders }}
|
||||
</code>
|
||||
</pre>
|
||||
</details>
|
||||
<details v-if="log.payload" class="text-white">
|
||||
<summary class="cursor-pointer select-none">Payload :</summary>
|
||||
<pre style="tab-size: 2">
|
||||
<code class="whitespace-pre-line">
|
||||
{{ log.payload }}
|
||||
</code>
|
||||
</pre>
|
||||
</details>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
|
||||
.log-item-animation {
|
||||
animation-name: log-fade-in;
|
||||
animation-duration: 0.5s;
|
||||
}
|
||||
|
||||
@keyframes log-fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
scale: 0.8;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
scale: 1;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -9,8 +9,8 @@ import usePersianDate from "~/composables/global/usePersianDate";
|
||||
useSeoMeta({
|
||||
title: "نتیجه تراکنش",
|
||||
description: "",
|
||||
keywords : ""
|
||||
})
|
||||
keywords: "",
|
||||
});
|
||||
|
||||
definePageMeta({
|
||||
layout: "none",
|
||||
@@ -91,6 +91,8 @@ const statusTitle = computed(() => {
|
||||
class="aspect-square w-[220px] md:w-[280px] lg:w-[320px] absolute -top-[70px] md:-top-[110px] lg:-top-[138px] left-1/2 -translate-x-1/2 z-10 [filter:_drop-shadow(0px_4px_20px_rgba(0, 0, 0, 0.15))]"
|
||||
src="/img/heymlz/heymlz-seat.gif"
|
||||
:class="statusVariants.hue_deg"
|
||||
loading="lazy"
|
||||
fetch-priority="low"
|
||||
/>
|
||||
|
||||
<div
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import VueScrollTo from "vue-scrollto";
|
||||
|
||||
export default defineNuxtPlugin(() => {
|
||||
return {
|
||||
provide: {
|
||||
vScrollTo: VueScrollTo,
|
||||
},
|
||||
};
|
||||
});
|
||||
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.
|
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
Reference in New Issue
Block a user