merage and test docker compsoe
@@ -35,6 +35,6 @@ const closeModal = () => {
|
|||||||
/>
|
/>
|
||||||
</ToastProvider>
|
</ToastProvider>
|
||||||
|
|
||||||
<VueQueryDevtools dir="ltr" buttonPosition="bottom-right" />
|
<VueQueryDevtools dir="ltr" buttonPosition="top-right"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -53,7 +53,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@apply bg-blue-200 border-blue-500 text-blue-500;
|
@apply bg-transparent border-blue-500 text-blue-500;
|
||||||
|
|
||||||
svg[class~="iconify"] path {
|
svg[class~="iconify"] path {
|
||||||
@apply stroke-blue-500;
|
@apply stroke-blue-500;
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// types
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
gateway: PaymentGateway;
|
||||||
|
isSelected: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Emits = {
|
||||||
|
select: [value: null];
|
||||||
|
};
|
||||||
|
|
||||||
|
// props
|
||||||
|
|
||||||
|
defineProps<Props>();
|
||||||
|
|
||||||
|
// emits
|
||||||
|
|
||||||
|
const emit = defineEmits<Emits>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<button
|
||||||
|
@click="emit('select', null)"
|
||||||
|
:class="
|
||||||
|
isSelected
|
||||||
|
? 'ring-2 ring-offset-2 ring-black border-black'
|
||||||
|
: '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" />
|
||||||
|
</div>
|
||||||
|
<span class="typo-label-sm text-right text-black">
|
||||||
|
{{ gateway.title }}
|
||||||
|
</span>
|
||||||
|
<Transition
|
||||||
|
enter-active-class="animate__animated animate__fadeInLeft animate__faster"
|
||||||
|
leave-active-class="animate__animated animate__fadeOutLeft animate__faster"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
v-if="isSelected"
|
||||||
|
class="bg-black 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" />
|
||||||
|
</span>
|
||||||
|
</Transition>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
@@ -43,6 +43,7 @@ const addressData = ref({
|
|||||||
: address.value?.for_me == true
|
: address.value?.for_me == true
|
||||||
? "بله"
|
? "بله"
|
||||||
: "خیر",
|
: "خیر",
|
||||||
|
is_main: address.value?.is_main ?? false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// queries
|
// queries
|
||||||
@@ -67,6 +68,7 @@ const closeModal = () => {
|
|||||||
name: "",
|
name: "",
|
||||||
phone: "",
|
phone: "",
|
||||||
for_me: "بله",
|
for_me: "بله",
|
||||||
|
is_main: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
isShow.value = false;
|
isShow.value = false;
|
||||||
@@ -253,6 +255,16 @@ watch(
|
|||||||
class="flex items-center field-sizing-content resize-none bg-slate-50 border-slate-200 hover:border-black focus:border-black max-h-[10rem] text-black justify-between cursor-text transition-all border-[1.5px] gap-3 typo-label-md px-4 py-3.5 selection:bg-slate-100 rounded-100 outline-none flex-1 !text-sm placeholder-slate-400"
|
class="flex items-center field-sizing-content resize-none bg-slate-50 border-slate-200 hover:border-black focus:border-black max-h-[10rem] text-black justify-between cursor-text transition-all border-[1.5px] gap-3 typo-label-md px-4 py-3.5 selection:bg-slate-100 rounded-100 outline-none flex-1 !text-sm placeholder-slate-400"
|
||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-between w-full gap-2">
|
||||||
|
<label
|
||||||
|
for="is_main"
|
||||||
|
class="text-xs font-semibold lg:text-sm text-gray-900"
|
||||||
|
>
|
||||||
|
به عنوان آدرس پیش فرض ثبت شود؟
|
||||||
|
</label>
|
||||||
|
<Switch id="is_main" v-model="addressData.is_main" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="py-6 border-t border-slate-200 flex gap-3">
|
<div class="py-6 border-t border-slate-200 flex gap-3">
|
||||||
|
|||||||
@@ -21,17 +21,19 @@ const discountCode = ref(cart.value?.discount_code?.code || "");
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
mutateAsync: submitDiscountCode,
|
mutateAsync: submitDiscountCode,
|
||||||
isPending: submitDiscountCodeIsPending
|
isPending: submitDiscountCodeIsPending,
|
||||||
} = useSubmitDiscountCode();
|
} = useSubmitDiscountCode();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
mutateAsync: deleteDiscountCode,
|
mutateAsync: deleteDiscountCode,
|
||||||
isPending: deleteDiscountCodeIsPending
|
isPending: deleteDiscountCodeIsPending,
|
||||||
} = useDeleteDiscountCode();
|
} = useDeleteDiscountCode();
|
||||||
|
|
||||||
// computed
|
// computed
|
||||||
|
|
||||||
const nextPage = computed(() => route.meta.nextPage as { name: string; label: string } | undefined);
|
const nextPage = computed(
|
||||||
|
() => route.meta.nextPage as { name: string; label: string } | undefined
|
||||||
|
);
|
||||||
|
|
||||||
const hasSubmittedDiscountCode = computed(() => !!cart.value?.discount_code);
|
const hasSubmittedDiscountCode = computed(() => !!cart.value?.discount_code);
|
||||||
|
|
||||||
@@ -48,11 +50,11 @@ const handleSubmitDiscountCode = () => {
|
|||||||
addToast({
|
addToast({
|
||||||
message: "خطایی در ثبت کد تخفیف رخ داد",
|
message: "خطایی در ثبت کد تخفیف رخ داد",
|
||||||
options: {
|
options: {
|
||||||
status: "error"
|
status: "error",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
discountCode.value = "";
|
discountCode.value = "";
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -67,11 +69,11 @@ const handleDeleteDiscountCode = () => {
|
|||||||
addToast({
|
addToast({
|
||||||
message: "خطایی در حذف کد تخفیف رخ داد",
|
message: "خطایی در حذف کد تخفیف رخ داد",
|
||||||
options: {
|
options: {
|
||||||
status: "error"
|
status: "error",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
discountCode.value = "";
|
discountCode.value = "";
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -12,42 +12,55 @@ const {} = toRefs(props);
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="relative w-full flex flex-col justify-center min-h-[450px] md:h-[80svh]">
|
<div class="relative w-full flex flex-col justify-center min-h-[450px] h-svh">
|
||||||
|
<div class="flex-col-center gap-6 mb-32">
|
||||||
|
<span class="typo-h-6 max-sm:text-xl md:typo-h-5 lg:typo-h-4 text-black">
|
||||||
|
مجله در ستون و سطرآنچ
|
||||||
|
</span>
|
||||||
|
<p class="text-slate-500 text-center max-w-[750px] typo-p-lg xl:typo-p-xl">
|
||||||
|
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. چاپگرها و
|
||||||
|
متون بلکه روزنامه و مجله در ستون و سطرآنچنان که
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
<div class="-rotate-z-2 z-20">
|
<div class="-rotate-z-2 z-20">
|
||||||
<div
|
<div
|
||||||
class="bg-warning-500 flex pr-20 gap-12 sm:gap-20 py-2 w-max animate-marquee-reverse"
|
class="bg-blue-500 flex items-center pr-20 gap-12 sm:gap-20 w-max animate-marquee-reverse h-[140px]"
|
||||||
>
|
>
|
||||||
<span
|
<template v-for="i in 10">
|
||||||
v-for="i in 10"
|
<div class="text-[30px] lg:text-[40px] text-white whitespace-nowrap font-semibold opacity-85">
|
||||||
class="text-[40px] lg:text-[50px] text-white whitespace-nowrap font-semibold"
|
HEYMLZ
|
||||||
>
|
</div>
|
||||||
TEST {{ i }}
|
<NuxtImg src="/img/heymlz/heymlz-logo.png" class="h-[45px] invert opacity-85" />
|
||||||
</span>
|
</template>
|
||||||
<span
|
<template v-for="i in 10">
|
||||||
v-for="i in 10"
|
<div class="text-[30px] lg:text-[40px] text-white whitespace-nowrap font-semibold opacity-85">
|
||||||
class="text-[40px] lg:text-[50px] text-white whitespace-nowrap font-semibold"
|
HEYMLZ
|
||||||
>
|
</div>
|
||||||
TEST {{ i }}
|
<NuxtImg src="/img/heymlz/heymlz-logo.png" class="h-[45px] invert opacity-85" />
|
||||||
</span>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="rotate-z-2 z-10">
|
<div class="rotate-z-2 z-10">
|
||||||
<div
|
<div
|
||||||
class="bg-slate-50 flex pr-20 gap-12 sm:gap-20 py-2 w-max animate-marquee"
|
class="bg-slate-100/70 flex items-center pr-20 gap-12 sm:gap-20 w-max animate-marquee h-[140px]"
|
||||||
>
|
>
|
||||||
<span
|
<template v-for="i in 1">
|
||||||
v-for="i in 10"
|
<NuxtImg src="/img/brands/brand-1.png" class="h-[45px] grayscale opacity-40" />
|
||||||
class="text-[40px] lg:text-[50px] text-slate-300 whitespace-nowrap font-semibold"
|
<NuxtImg src="/img/brands/brand-2.png" class="h-[45px] grayscale opacity-40" />
|
||||||
>
|
<NuxtImg src="/img/brands/brand-3.png" class="h-[45px] grayscale opacity-40" />
|
||||||
TEST {{ i }}
|
<NuxtImg src="/img/brands/brand-4.png" class="h-[45px] grayscale opacity-40" />
|
||||||
</span>
|
<NuxtImg src="/img/brands/brand-5.png" class="h-[45px] grayscale opacity-40" />
|
||||||
<span
|
<NuxtImg src="/img/brands/brand-6.png" class="h-[45px] grayscale opacity-40" />
|
||||||
v-for="i in 10"
|
</template>
|
||||||
class="text-[40px] lg:text-[50px] text-slate-300 whitespace-nowrap font-semibold"
|
<template v-for="i in 1">
|
||||||
>
|
<NuxtImg src="/img/brands/brand-1.png" class="h-[45px] grayscale opacity-40" />
|
||||||
TEST {{ i }}
|
<NuxtImg src="/img/brands/brand-2.png" class="h-[45px] grayscale opacity-40" />
|
||||||
</span>
|
<NuxtImg src="/img/brands/brand-3.png" class="h-[45px] grayscale opacity-40" />
|
||||||
|
<NuxtImg src="/img/brands/brand-4.png" class="h-[45px] grayscale opacity-40" />
|
||||||
|
<NuxtImg src="/img/brands/brand-5.png" class="h-[45px] grayscale opacity-40" />
|
||||||
|
<NuxtImg src="/img/brands/brand-6.png" class="h-[45px] grayscale opacity-40" />
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ withDefaults(defineProps<Props>(), {
|
|||||||
</span>
|
</span>
|
||||||
<NuxtLink to="/products">
|
<NuxtLink to="/products">
|
||||||
<Button
|
<Button
|
||||||
variant="outlined"
|
variant="primary"
|
||||||
class="rounded-full max-sm:typo-label-sm max-sm:py-2"
|
class="rounded-full max-sm:typo-label-sm max-sm:py-2"
|
||||||
end-icon="ci:arrow-left"
|
end-icon="ci:arrow-left"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -25,28 +25,27 @@ const onSwiper = (swiper: SwiperClass) => {
|
|||||||
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-[150svh] relative overflow-hidden"
|
||||||
>
|
>
|
||||||
|
|
||||||
<!-- <div class="w-full flex justify-center items-center relative z-10">-->
|
<div class="w-full relative translate-y-[-200px] z-10">
|
||||||
<!-- <span class="text-white typo-h-6 md:typo-h-5 lg:typo-h-4">-->
|
<div class="flex-col-center gap-6">
|
||||||
<!-- دسته بندی ها-->
|
<span class="text-white typo-h-6 md:typo-h-5 lg:typo-h-4">
|
||||||
<!-- </span>-->
|
دسته بندی ها
|
||||||
<!-- </div>-->
|
</span>
|
||||||
|
<p class="text-slate-300 text-center max-w-[750px] typo-p-lg xl:typo-p-xl">
|
||||||
|
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. چاپگرها
|
||||||
|
و
|
||||||
|
متون بلکه روزنامه و مجله در ستون و سطرآنچنان که
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<NuxtImg
|
|
||||||
src="/img/categories-gradient.png"
|
|
||||||
class="animate-spin [animation-duration:16s] object-cover absolute size-full brightness-45 scale-115 aspect-square"
|
|
||||||
:style="{
|
|
||||||
maskImage: 'radial-gradient(black, transparent 50%)'
|
|
||||||
}"
|
|
||||||
alt=""
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="w-full my-20 relative">
|
<div class="w-full my-20 relative">
|
||||||
<video
|
<video
|
||||||
class="aspect-square w-[450px] translate-[-253px] absolute left-1/2 -translate-x-1/2 z-10"
|
class="aspect-square w-[450px] translate-[-293px] 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-seat-2.webm"
|
src="/video/heymlz/heymlz-handshake-part-1.webm"
|
||||||
autoplay
|
autoplay
|
||||||
playsinline
|
playsinline
|
||||||
webkit-playsinline
|
webkit-playsinline
|
||||||
@@ -109,7 +108,7 @@ 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="solid" class="invert rounded-full max-xs:typo-label-sm !px-4 xs:!px-8"
|
<Button variant="primary" class="rounded-full max-xs:typo-label-sm !px-4 xs:!px-8"
|
||||||
end-icon="ci:arrow-left">
|
end-icon="ci:arrow-left">
|
||||||
مشاهده همه دسته ها
|
مشاهده همه دسته ها
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ await suspense();
|
|||||||
مقالات اخیر سایت
|
مقالات اخیر سایت
|
||||||
</span>
|
</span>
|
||||||
<NuxtLink to="/articles">
|
<NuxtLink to="/articles">
|
||||||
<Button variant="outlined" class="rounded-full max-sm:typo-label-sm max-sm:py-2"
|
<Button variant="primary" class="rounded-full max-sm:typo-label-sm max-sm:py-2"
|
||||||
end-icon="ci:arrow-left">
|
end-icon="ci:arrow-left">
|
||||||
نمایش همه
|
نمایش همه
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -15,6 +15,13 @@ const activeSlideVideo = ref<"left" | "right" | "none">("none");
|
|||||||
const draggableEl = ref<HTMLElement | null>(null);
|
const draggableEl = ref<HTMLElement | null>(null);
|
||||||
const previewContainerEl = 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, {
|
const { x: dragAxisX } = useDraggable(draggableEl, {
|
||||||
initialValue: { x: 0, y: 0 },
|
initialValue: { x: 0, y: 0 },
|
||||||
axis: "x"
|
axis: "x"
|
||||||
@@ -22,6 +29,17 @@ const { x: dragAxisX } = useDraggable(draggableEl, {
|
|||||||
|
|
||||||
// watch
|
// watch
|
||||||
|
|
||||||
|
watch(heymlzElementIsVisible, (newValue) => {
|
||||||
|
if (newValue) {
|
||||||
|
showHeymlzAnimation.value = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
showHeymlzAnimation.value = false;
|
||||||
|
}, 3200);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
once: true
|
||||||
|
});
|
||||||
|
|
||||||
watch(() => clipPathPercent.value, (newValue) => {
|
watch(() => clipPathPercent.value, (newValue) => {
|
||||||
if (newValue > 80) {
|
if (newValue > 80) {
|
||||||
activeSlideVideo.value = "right";
|
activeSlideVideo.value = "right";
|
||||||
@@ -62,7 +80,8 @@ watch(
|
|||||||
<NuxtImg
|
<NuxtImg
|
||||||
v-if="activeSlideVideo !== 'right'"
|
v-if="activeSlideVideo !== 'right'"
|
||||||
:src="homeData!.difreance_section.image1"
|
:src="homeData!.difreance_section.image1"
|
||||||
class="select-none absolute size-full object-cover brightness-[95%]"
|
:class="showHeymlzAnimation ? 'brightness-35' : 'brightness-[95%]'"
|
||||||
|
class="select-none absolute size-full object-cover transition-[filter] duration-250"
|
||||||
:alt="homeData!.difreance_section.title1"
|
:alt="homeData!.difreance_section.title1"
|
||||||
/>
|
/>
|
||||||
<video
|
<video
|
||||||
@@ -76,13 +95,14 @@ watch(
|
|||||||
/>
|
/>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
<div class="absolute size-full right-0 w-full">
|
<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="overlay-image select-none absolute object-cover size-full brightness-[95%]"
|
:class="showHeymlzAnimation ? 'brightness-35' : 'brightness-[95%]'"
|
||||||
|
class="overlay-image select-none absolute object-cover size-full transition-[filter] duration-250"
|
||||||
:alt="homeData!.difreance_section.title2"
|
:alt="homeData!.difreance_section.title2"
|
||||||
/>
|
/>
|
||||||
<video
|
<video
|
||||||
@@ -97,12 +117,14 @@ watch(
|
|||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
<video
|
<video
|
||||||
|
v-if="showHeymlzAnimation"
|
||||||
src="/video/heymlz/heymlz-pulling.webm"
|
src="/video/heymlz/heymlz-pulling.webm"
|
||||||
autoplay
|
autoplay
|
||||||
muted
|
muted
|
||||||
playsinline
|
playsinline
|
||||||
|
loop
|
||||||
webkit-playsinline
|
webkit-playsinline
|
||||||
class="size-[300px] absolute translate-x-[-100px] z-10 top-[32%] -translate-y-1/2"
|
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))'
|
||||||
@@ -113,18 +135,23 @@ watch(
|
|||||||
:style="{
|
:style="{
|
||||||
left: `${clipPathPercent}%`,
|
left: `${clipPathPercent}%`,
|
||||||
}"
|
}"
|
||||||
:class="activeSlideVideo !== 'none' ? 'opacity-10' : ''"
|
:class="[
|
||||||
class="select-none w-2 h-full bg-black absolute left-0 flex items-center justify-center transition-opacity"
|
activeSlideVideo !== 'none' ? 'opacity-10' : '',
|
||||||
|
showHeymlzAnimation ? 'bg-neutral-300' : 'bg-black'
|
||||||
|
]"
|
||||||
|
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="cursor-grab hover:scale-115 transition-transform rounded-full absolute bg-black size-11 flex items-center justify-center"
|
: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"
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
name="ci:arrows"
|
name="ci:arrows"
|
||||||
size="24"
|
size="24"
|
||||||
class="**:stroke-white"
|
class="transition-all"
|
||||||
|
:class="showHeymlzAnimation ? '**:stroke-black' : '**:stroke-white'"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -92,7 +92,6 @@ onUnmounted(() => {
|
|||||||
:to="slide.link"
|
:to="slide.link"
|
||||||
class="showcase-slide origin-bottom absolute size-full bg-transparent flex items-center justify-center"
|
class="showcase-slide origin-bottom absolute size-full bg-transparent flex items-center justify-center"
|
||||||
>
|
>
|
||||||
|
|
||||||
<NuxtImg
|
<NuxtImg
|
||||||
class="w-[280px] xs:w-[350px] lg:w-[500px] xl:w-[650px] z-20 mb-30 sm:mb-20 lg:mb-30"
|
class="w-[280px] xs:w-[350px] lg:w-[500px] xl:w-[650px] z-20 mb-30 sm:mb-20 lg:mb-30"
|
||||||
:src="slide.image"
|
:src="slide.image"
|
||||||
@@ -101,7 +100,7 @@ onUnmounted(() => {
|
|||||||
}"
|
}"
|
||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
<div class="flex flex-col items-center justify-center gap-4 text-center absolute z-20 mt-20">
|
<div 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">
|
<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>
|
||||||
@@ -110,8 +109,9 @@ onUnmounted(() => {
|
|||||||
</p>
|
</p>
|
||||||
<NuxtLink :to="slide.link">
|
<NuxtLink :to="slide.link">
|
||||||
<Button
|
<Button
|
||||||
|
variant="primary"
|
||||||
end-icon="ci:arrow-left"
|
end-icon="ci:arrow-left"
|
||||||
class="mt-6 invert max-sm:hidden max-lg:typo-label-xs px-12 rounded-full hover:bg-transparent"
|
class="mt-8 max-sm:hidden max-lg:typo-label-xs px-10 rounded-full hover:bg-transparent"
|
||||||
>
|
>
|
||||||
مشاهده دسته بندی
|
مشاهده دسته بندی
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -21,18 +21,18 @@ const { circle } = toRefs(props);
|
|||||||
<div
|
<div
|
||||||
:style="{
|
:style="{
|
||||||
height: `${size}px`,
|
height: `${size}px`,
|
||||||
width: circle ? `${size}px` : '100%',
|
width: circle ? `${size}px` : '100%'
|
||||||
}"
|
}"
|
||||||
class="relative flex items-center w-full justify-center shrink-0"
|
class="relative flex items-center w-full justify-center shrink-0"
|
||||||
:class="{
|
:class="{
|
||||||
'rounded-full overflow-hidden': circle,
|
'rounded-full overflow-hidden': circle,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<img
|
<NuxtImg
|
||||||
:style="{
|
:style="{
|
||||||
maskImage: 'radial-gradient(black, transparent 70%)'
|
maskImage: 'radial-gradient(black, transparent 70%)'
|
||||||
}"
|
}"
|
||||||
src="/public/img/ai-loading-2.gif"
|
src="/img/heymlz/heymlz-idle.gif"
|
||||||
class="size-full object-cover absolute pt-2"
|
class="size-full object-cover absolute pt-2"
|
||||||
alt="ai-loading"
|
alt="ai-loading"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ const { isLoggedIn } = useAuth();
|
|||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const id = route.params.id as string | number;
|
const id = route.params.id as string | number;
|
||||||
|
|
||||||
|
const scrollToBottomTimer = ref<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
const chatContainerEl = ref<HTMLElement | null>(null);
|
const chatContainerEl = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
const lastMessageBeforeUpdate = ref(0);
|
const lastMessageBeforeUpdate = ref(0);
|
||||||
@@ -63,7 +65,10 @@ useInfiniteScroll(
|
|||||||
// methods
|
// methods
|
||||||
|
|
||||||
const scrollToBottom = () => {
|
const scrollToBottom = () => {
|
||||||
|
if (scrollToBottomTimer.value) clearTimeout(scrollToBottomTimer.value);
|
||||||
|
scrollToBottomTimer.value = setTimeout(() => {
|
||||||
chatContainerScrollY.value = chatContainerEl.value?.scrollHeight ?? 0;
|
chatContainerScrollY.value = chatContainerEl.value?.scrollHeight ?? 0;
|
||||||
|
}, 50);
|
||||||
};
|
};
|
||||||
|
|
||||||
// computed
|
// computed
|
||||||
@@ -137,8 +142,7 @@ whenever(
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
:style="{
|
:style="{
|
||||||
maskImage:
|
maskImage: 'linear-gradient(to top, transparent, black 5%, black, black)'
|
||||||
'linear-gradient(to top, transparent, black 5%, black, black)',
|
|
||||||
}"
|
}"
|
||||||
class="hide-scrollbar flex flex-col py-7 gap-6 h-full overflow-y-auto"
|
class="hide-scrollbar flex flex-col py-7 gap-6 h-full overflow-y-auto"
|
||||||
ref="chatContainerEl"
|
ref="chatContainerEl"
|
||||||
@@ -184,11 +188,20 @@ whenever(
|
|||||||
</Transition>
|
</Transition>
|
||||||
</template>
|
</template>
|
||||||
<div
|
<div
|
||||||
class="text-black p-4.5 size-full flex justify-center items-center"
|
class="text-black p-6 size-full flex justify-center items-center flex-col"
|
||||||
v-else
|
v-else
|
||||||
>
|
>
|
||||||
<img class="size-[50px]" src="/img/heymlz/heymlz-idle.gif" alt="" />
|
<NuxtImg class="size-[250px]" src="/img/heymlz/heymlz-loading-1.gif" alt="" />
|
||||||
Please sign in first
|
<div class="flex flex-col gap-4 items-center">
|
||||||
|
<span class="text-center typo-p-xl font-bold">سلام دوست عزیز!</span>
|
||||||
|
<p class="text-center typo-p-md">
|
||||||
|
من میتونم هر سوالی رو درمورد این محصول جواب بدم
|
||||||
|
اگه میخوای شروع کنیم روی دکمه زیر کلیک کن
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<NuxtLink to="/signin">
|
||||||
|
<Button class="mt-8 rounded-full px-10">ورود به فروشگاه</Button>
|
||||||
|
</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ import { useToast } from "~/composables/global/useToast";
|
|||||||
|
|
||||||
// state
|
// state
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const id = route.params.id as string | number;
|
||||||
|
|
||||||
const { $queryClient: queryClient } = useNuxtApp();
|
const { $queryClient: queryClient } = useNuxtApp();
|
||||||
|
|
||||||
const { addToast } = useToast();
|
const { addToast } = useToast();
|
||||||
@@ -27,7 +30,7 @@ const sendMessage = async () => {
|
|||||||
|
|
||||||
await createMessage({
|
await createMessage({
|
||||||
new_message: value,
|
new_message: value,
|
||||||
productId: 1,
|
productId: id,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
addToast({
|
addToast({
|
||||||
|
|||||||
@@ -54,13 +54,12 @@ onMounted(() => {
|
|||||||
`#chat-message-content-${id.value}`,
|
`#chat-message-content-${id.value}`,
|
||||||
{
|
{
|
||||||
text: "",
|
text: "",
|
||||||
duration: 2.5,
|
|
||||||
ease: "none",
|
ease: "none",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: { value: content.value, rtl: false },
|
text: { value: content.value, rtl: false },
|
||||||
duration: 2.5,
|
|
||||||
ease: "none",
|
ease: "none",
|
||||||
|
duration: 2.5,
|
||||||
onUpdate: () => emit("textUpdate"),
|
onUpdate: () => emit("textUpdate"),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -78,9 +77,9 @@ onMounted(() => {
|
|||||||
<div
|
<div
|
||||||
class="relative overflow-hidden flex items-center justify-center mt-px bg-slate-300 rounded-full size-[35px] shrink-0"
|
class="relative overflow-hidden flex items-center justify-center mt-px bg-slate-300 rounded-full size-[35px] shrink-0"
|
||||||
>
|
>
|
||||||
<img
|
<NuxtImg
|
||||||
v-if="!reverse"
|
v-if="reverse"
|
||||||
src="/img/footer-bg.jpg"
|
src="/img/heymlz/footer-share.svg"
|
||||||
class="size-full object-cover absolute"
|
class="size-full object-cover absolute"
|
||||||
alt="profile"
|
alt="profile"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -45,11 +45,11 @@ const {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const avatars = ref([
|
const avatars = ref([
|
||||||
"/avatars/1.jpg",
|
"/img/avatars/1.jpg",
|
||||||
"/avatars/2.jpg",
|
"/img/avatars/2.jpg",
|
||||||
"/avatars/3.jpg",
|
"/img/avatars/3.jpg",
|
||||||
"/avatars/4.jpg",
|
"/img/avatars/4.jpg",
|
||||||
"/avatars/5.jpg",
|
"/img/avatars/5.jpg",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// queries
|
// queries
|
||||||
@@ -134,11 +134,7 @@ onFileDialogChange((files: any) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-full flex-col-center gap-5">
|
<div class="w-full flex-col-center gap-5">
|
||||||
<Avatar
|
<Avatar :src="currentProfile" alt="" class="!size-32" />
|
||||||
:src="currentProfile"
|
|
||||||
alt=""
|
|
||||||
class="!size-32"
|
|
||||||
/>
|
|
||||||
<Button
|
<Button
|
||||||
class="rounded-full w-[8rem]"
|
class="rounded-full w-[8rem]"
|
||||||
@click="openFileDialog"
|
@click="openFileDialog"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<Icon name="bi:info-circle" size="20" />
|
<Icon name="bi:info-circle" size="20" />
|
||||||
<h3 class="typo-sub-h-lg font-semibold">تغییر وضعیت سفارش</h3>
|
<h3 class="typo-sub-h-md font-semibold">تغییر وضعیت سفارش</h3>
|
||||||
|
|
|
|
||||||
<span class="typo-p-xs text-cyan-500 font-semibold">
|
<span class="typo-p-xs text-cyan-500 font-semibold">
|
||||||
۲۳ تیر
|
۲۳ تیر
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ const profile = computed(() => {
|
|||||||
return is_user.value
|
return is_user.value
|
||||||
? account.value?.profile_photo
|
? account.value?.profile_photo
|
||||||
? account.value?.profile_photo
|
? account.value?.profile_photo
|
||||||
: "/avatars/1.jpg"
|
: "/img/avatars/1.jpg"
|
||||||
: "/avatars/3.jpg";
|
: "/img/avatars/3.jpg";
|
||||||
});
|
});
|
||||||
|
|
||||||
const username = computed(() => {
|
const username = computed(() => {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { API_ENDPOINTS } from "~/constants";
|
|||||||
|
|
||||||
// types
|
// types
|
||||||
|
|
||||||
export type CreateOrUpdateAddressResponse = Omit<Address, "is_main">;
|
export type CreateOrUpdateAddressResponse = Address;
|
||||||
|
|
||||||
const useCreateOrUpdateAddress = (update: ComputedRef<Boolean>) => {
|
const useCreateOrUpdateAddress = (update: ComputedRef<Boolean>) => {
|
||||||
// state
|
// state
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import { useAuth } from "~/composables/api/auth/useAuth";
|
|||||||
|
|
||||||
// types
|
// types
|
||||||
|
|
||||||
export type GetBranchResponse = ApiPaginated<Chat>;
|
export type GetChatResponse = ApiPaginated<Chat>;
|
||||||
|
|
||||||
const useGetBranch = (productId: string | number, enabled: Ref<boolean>) => {
|
const useGetChat = (productId: string | number, enabled: Ref<boolean>) => {
|
||||||
// state
|
// state
|
||||||
|
|
||||||
const { $axios: axios } = useNuxtApp();
|
const { $axios: axios } = useNuxtApp();
|
||||||
@@ -26,7 +26,7 @@ const useGetBranch = (productId: string | number, enabled: Ref<boolean>) => {
|
|||||||
limit: number;
|
limit: number;
|
||||||
offset: number;
|
offset: number;
|
||||||
}) => {
|
}) => {
|
||||||
const { data } = await axios.get<GetBranchResponse>(
|
const { data } = await axios.get<GetChatResponse>(
|
||||||
`${API_ENDPOINTS.chat.messages}/${productId}`,
|
`${API_ENDPOINTS.chat.messages}/${productId}`,
|
||||||
{
|
{
|
||||||
params: {
|
params: {
|
||||||
@@ -65,4 +65,4 @@ const useGetBranch = (productId: string | number, enabled: Ref<boolean>) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useGetBranch;
|
export default useGetChat;
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
// imports
|
||||||
|
|
||||||
|
import { useMutation } from "@tanstack/vue-query";
|
||||||
|
import { API_ENDPOINTS } from "~/constants";
|
||||||
|
|
||||||
|
// types
|
||||||
|
|
||||||
|
export type SetOrderAddressRequest = {
|
||||||
|
address_id: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useSetOrderAddress = () => {
|
||||||
|
// state
|
||||||
|
|
||||||
|
const { $axios: axios } = useNuxtApp();
|
||||||
|
|
||||||
|
// methods
|
||||||
|
|
||||||
|
const handleSetOrderAddress = async (params: SetOrderAddressRequest) => {
|
||||||
|
const { data } = await axios.post(
|
||||||
|
API_ENDPOINTS.orders.delivery.set_address,
|
||||||
|
{ ...params }
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (AddressData: SetOrderAddressRequest) =>
|
||||||
|
handleSetOrderAddress(AddressData),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useSetOrderAddress;
|
||||||
@@ -53,6 +53,12 @@ export const API_ENDPOINTS = {
|
|||||||
add_discount: "/order/cart/discount",
|
add_discount: "/order/cart/discount",
|
||||||
delete_discount: "/order/cart/discount",
|
delete_discount: "/order/cart/discount",
|
||||||
},
|
},
|
||||||
|
delivery: {
|
||||||
|
set_address: "/order/cart/set-address",
|
||||||
|
},
|
||||||
|
checkout: {
|
||||||
|
payment: "/order/cart/payment",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// meta
|
// meta
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: "cart",
|
layout: "cart",
|
||||||
middleware: "check-is-logged-in",
|
middleware: "check-is-logged-in",
|
||||||
@@ -9,45 +11,63 @@ definePageMeta({
|
|||||||
nextPage: { name: "checkout", label: "پرداخت" },
|
nextPage: { name: "checkout", label: "پرداخت" },
|
||||||
});
|
});
|
||||||
|
|
||||||
// types
|
|
||||||
|
|
||||||
type PaymentGateway = {
|
|
||||||
id: number;
|
|
||||||
picture: string;
|
|
||||||
title: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
// state
|
// state
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const paymentGateways = ref<PaymentGateway[]>([
|
const paymentGateways = ref<PaymentGateway[]>([
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
picture: "/zarinpal.png",
|
picture: "/img/gateways/zarinpal.png",
|
||||||
title: "زرین پال",
|
title: "زرین پال",
|
||||||
|
type: "ZARINPAL",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
picture: "/saman-bank.png",
|
picture: "/img/gateways/sep.png",
|
||||||
title: "بانک سامان",
|
title: "سپ",
|
||||||
|
type: "SEP",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
picture: "/mellat-bank.png",
|
picture: "/img/gateways/mellat-bank.png",
|
||||||
title: "بانک ملت",
|
title: "بانک ملت",
|
||||||
|
type: "MELLAT",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
picture: "/jibimo.png",
|
picture: "/img/gateways/idpay.png",
|
||||||
title: "جیبی مو",
|
title: "آی دی پی",
|
||||||
|
type: "IDPAY",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 5,
|
id: 5,
|
||||||
picture: "/idpay.png",
|
picture: "/img/gateways/zibal.png",
|
||||||
title: "آی دی پی",
|
title: "زیبال",
|
||||||
|
type: "ZIBAL",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
picture: "/img/gateways/bahamta.png",
|
||||||
|
title: "باهمتا",
|
||||||
|
type: "BAHAMTA",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 7,
|
||||||
|
picture: "/img/gateways/bmi.png",
|
||||||
|
title: "بانک ملی",
|
||||||
|
type: "BMI",
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const selectedGateway = ref<PaymentGateway>(paymentGateways.value[0]);
|
const selectedGateway = computed({
|
||||||
|
get: () => {
|
||||||
|
return !!route.query["gw"]
|
||||||
|
? paymentGateways.value.find((i) => i.type == route.query["gw"])
|
||||||
|
: paymentGateways.value[0];
|
||||||
|
},
|
||||||
|
set: (nv: PaymentGateway) => router.push({ query: { gw: nv.type } }),
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -64,24 +84,13 @@ const selectedGateway = ref<PaymentGateway>(paymentGateways.value[0]);
|
|||||||
<div
|
<div
|
||||||
class="w-full grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4"
|
class="w-full grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4"
|
||||||
>
|
>
|
||||||
<div
|
<Gateway
|
||||||
v-for="(gateway, index) in paymentGateways"
|
v-for="(gateway, index) in paymentGateways"
|
||||||
@click="selectedGateway = { ...gateway }"
|
:index="index"
|
||||||
:key="index"
|
:gateway="gateway"
|
||||||
:class="
|
:isSelected="selectedGateway?.id == gateway.id"
|
||||||
selectedGateway.id == gateway.id
|
@select="selectedGateway = { ...gateway }"
|
||||||
? 'ring-2 ring-offset-2 ring-black border-black'
|
/>
|
||||||
: 'border-slate-200'
|
|
||||||
"
|
|
||||||
class="w-full p-5 border rounded-xl flex flex-col gap-4 transition-all cursor-pointer"
|
|
||||||
>
|
|
||||||
<div class="aspect-square flex-center">
|
|
||||||
<NuxtImg :src="gateway.picture" class="object-cover" />
|
|
||||||
</div>
|
|
||||||
<span class="typo-label-sm text-black">
|
|
||||||
{{ gateway.title }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<OrderSummary />
|
<OrderSummary />
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
// imports
|
// imports
|
||||||
|
|
||||||
import useGetAllAddress from "~/composables/api/account/useGetAllAddress";
|
import useGetAllAddress from "~/composables/api/account/useGetAllAddress";
|
||||||
|
import useSetOrderAddress from "~/composables/api/orders/useSetOrderAddress";
|
||||||
|
import { useToast } from "~/composables/global/useToast";
|
||||||
|
import { QUERY_KEYS } from "~/constants";
|
||||||
|
|
||||||
// meta
|
// meta
|
||||||
|
|
||||||
@@ -24,10 +27,26 @@ type DeliveryData = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// queries
|
||||||
|
|
||||||
|
const { data: addresses, isLoading: addressesIsLoading } = useGetAllAddress();
|
||||||
|
|
||||||
|
const { mutateAsync: setOrderAddress, isPending: setOrderAddressIsPending } =
|
||||||
|
useSetOrderAddress();
|
||||||
|
|
||||||
|
// computed
|
||||||
|
|
||||||
|
const selectedAddress = computed(() => {
|
||||||
|
return addresses.value?.find((i) => i.is_main) ?? null;
|
||||||
|
});
|
||||||
|
|
||||||
// state
|
// state
|
||||||
|
|
||||||
|
const { $queryClient: queryClient } = useNuxtApp();
|
||||||
|
const { addToast } = useToast();
|
||||||
|
|
||||||
const deliveryData = ref<DeliveryData>({
|
const deliveryData = ref<DeliveryData>({
|
||||||
address: null,
|
address: selectedAddress.value,
|
||||||
deliveryMethod: {
|
deliveryMethod: {
|
||||||
peyk: false,
|
peyk: false,
|
||||||
pishtaz: true,
|
pishtaz: true,
|
||||||
@@ -35,26 +54,57 @@ const deliveryData = ref<DeliveryData>({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: addresses, isLoading: addressesIsLoading } = useGetAllAddress();
|
|
||||||
|
|
||||||
// methods
|
// methods
|
||||||
|
|
||||||
const handleSelectAddress = (address: Address) => {
|
const handleSelectAddress = (address: Address) => {
|
||||||
deliveryData.value.address = { ...address };
|
deliveryData.value.address = { ...address };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// watch
|
||||||
|
|
||||||
|
whenever(
|
||||||
|
() => deliveryData.value.address,
|
||||||
|
(nv) => {
|
||||||
|
setOrderAddress(
|
||||||
|
{ address_id: nv.id! },
|
||||||
|
{
|
||||||
|
onSettled: () => {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: [QUERY_KEYS.addresses],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
addToast({
|
||||||
|
message: "در انتخاب آدرس خطایی رخ داد",
|
||||||
|
options: {
|
||||||
|
description: "لطفا مجدد تلاش کنید",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col w-full gap-5">
|
<div class="flex flex-col w-full gap-5">
|
||||||
<AddressItem />
|
<AddressItem />
|
||||||
<div class="flex flex-col w-full gap-6">
|
<div class="flex flex-col w-full gap-6">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
<span class="typo-sub-h-xl"> آدرس های شما </span>
|
<span class="typo-sub-h-xl"> آدرس های شما </span>
|
||||||
<div v-if="addressesIsLoading" class="flex flex-col gap-6 w-full">
|
<Icon
|
||||||
<Skeleton
|
name="svg-spinners:180-ring-with-bg"
|
||||||
v-for="i in 3"
|
size="20"
|
||||||
class="w-full !h-[10rem] !rounded-xl"
|
v-if="setOrderAddressIsPending"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="addressesIsLoading" class="flex flex-col gap-6 w-full">
|
||||||
|
<Skeleton v-for="i in 3" class="w-full !h-[8rem] !rounded-xl" />
|
||||||
|
</div>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div
|
<div
|
||||||
v-if="!addresses?.length"
|
v-if="!addresses?.length"
|
||||||
|
|||||||
@@ -132,8 +132,8 @@ const contactWays = ref([
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-4/12 h-full bg-red-300">
|
<div class="w-4/12 h-full flex-center">
|
||||||
<NuxtImg src="/mlz.jpeg" class="-mt-16" />
|
<NuxtImg src="/logo.png" class="size-2/3 -mt-5" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ const disableLoadingOverlay = useState("disableLoadingOverlay", () => false);
|
|||||||
const response = await suspense();
|
const response = await suspense();
|
||||||
|
|
||||||
if (response.isError) {
|
if (response.isError) {
|
||||||
console.log(response);
|
|
||||||
throw createError({
|
throw createError({
|
||||||
statusCode: 500,
|
statusCode: 500,
|
||||||
statusMessage: `Landing error : ${response.error.message}`
|
statusMessage: `Landing error : ${response.error.message}`
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import type {
|
import type {
|
||||||
DehydratedState,
|
DehydratedState,
|
||||||
VueQueryPluginOptions
|
VueQueryPluginOptions,
|
||||||
} from "@tanstack/vue-query";
|
} from "@tanstack/vue-query";
|
||||||
import {
|
import {
|
||||||
VueQueryPlugin,
|
VueQueryPlugin,
|
||||||
QueryClient,
|
QueryClient,
|
||||||
hydrate,
|
hydrate,
|
||||||
dehydrate
|
dehydrate,
|
||||||
} from "@tanstack/vue-query";
|
} from "@tanstack/vue-query";
|
||||||
|
|
||||||
import { defineNuxtPlugin, useState } from "#imports";
|
import { defineNuxtPlugin, useState } from "#imports";
|
||||||
@@ -15,7 +15,12 @@ export default defineNuxtPlugin((nuxt) => {
|
|||||||
const vueQueryState = useState<DehydratedState | null>("vue-query");
|
const vueQueryState = useState<DehydratedState | null>("vue-query");
|
||||||
|
|
||||||
const queryClient = new QueryClient({
|
const queryClient = new QueryClient({
|
||||||
defaultOptions: { queries: { staleTime: 5000 } }
|
defaultOptions: {
|
||||||
|
queries: {
|
||||||
|
staleTime: 5000,
|
||||||
|
experimental_prefetchInRender: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const options: VueQueryPluginOptions = { queryClient };
|
const options: VueQueryPluginOptions = { queryClient };
|
||||||
|
|
||||||
@@ -33,7 +38,7 @@ export default defineNuxtPlugin((nuxt) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
provide: {
|
provide: {
|
||||||
queryClient
|
queryClient,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 730 KiB After Width: | Height: | Size: 730 KiB |
|
Before Width: | Height: | Size: 809 KiB After Width: | Height: | Size: 809 KiB |
|
Before Width: | Height: | Size: 984 KiB After Width: | Height: | Size: 984 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 1002 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 3.4 MiB After Width: | Height: | Size: 3.4 MiB |
|
After Width: | Height: | Size: 651 KiB |
|
Before Width: | Height: | Size: 498 KiB After Width: | Height: | Size: 498 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 291 KiB |
|
Before Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 24 KiB |
@@ -11,22 +11,22 @@ declare global {
|
|||||||
type LogType = {
|
type LogType = {
|
||||||
title: string;
|
title: string;
|
||||||
status?: "success" | "error" | "info" | "warning";
|
status?: "success" | "error" | "info" | "warning";
|
||||||
message?: string,
|
message?: string;
|
||||||
details?: any
|
details?: any;
|
||||||
}
|
};
|
||||||
|
|
||||||
type AxiosLogType = {
|
type AxiosLogType = {
|
||||||
url: string,
|
url: string;
|
||||||
method: string,
|
method: string;
|
||||||
status: number,
|
status: number;
|
||||||
code: string,
|
code: string;
|
||||||
requestHeaders: Record<any, any>,
|
requestHeaders: Record<any, any>;
|
||||||
responseHeaders?: Record<any, any>,
|
responseHeaders?: Record<any, any>;
|
||||||
response?: any,
|
response?: any;
|
||||||
payload?: Record<any, any>,
|
payload?: Record<any, any>;
|
||||||
params?: Record<any, any>,
|
params?: Record<any, any>;
|
||||||
date: string
|
date: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
type Chat = {
|
type Chat = {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -276,4 +276,18 @@ declare global {
|
|||||||
updated_at: string;
|
updated_at: string;
|
||||||
order: Order;
|
order: Order;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type PaymentGateway = {
|
||||||
|
id: number;
|
||||||
|
picture: string;
|
||||||
|
title: string;
|
||||||
|
type:
|
||||||
|
| "ZARINPAL"
|
||||||
|
| "SEP"
|
||||||
|
| "MELLAT"
|
||||||
|
| "IDPAY"
|
||||||
|
| "ZIBAL"
|
||||||
|
| "BAHAMTA"
|
||||||
|
| "BMI";
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||