Merge remote-tracking branch 'origin/main'

This commit is contained in:
marzban-dev
2025-03-19 17:39:22 +03:30
87 changed files with 3583 additions and 395 deletions
@@ -76,12 +76,12 @@ const handleDeleteAddress = (id: number) => {
</div>
<span class="flex items-center justify-between w-full gap-3">
<div
class="flex items-center gap-3 typo-sub-h-lg font-semibold text-slate-900"
class="flex items-center gap-3 lg:text-[1.125rem] font-semibold text-slate-900"
>
{{ !!address ? address.name : "آدرس" }}
<span
v-if="isSelected"
class="bg-black rounded-xl px-3 py-2 text-slate-200 text-xs"
class="bg-black rounded-lg px-3 py-2 text-slate-200 text-[10px] lg:text-xs"
>
انتخاب شده
</span>
@@ -97,7 +97,7 @@ const handleDeleteAddress = (id: number) => {
</span>
<div
class="flex flex-col items-center justify-between w-full gap-8 lg:flex-row"
class="flex flex-col items-center justify-between w-full gap-5 lg:gap-8 lg:flex-row"
>
<div class="w-full lg:w-9/12 overflow-hidden">
<div
+68 -25
View File
@@ -1,6 +1,7 @@
<script setup lang="ts">
// imports
import useDeleteDiscountCode from "~/composables/api/orders/useDeleteDiscountCode";
import useGetCartOrders from "~/composables/api/orders/useGetCartOrders";
import useSubmitDiscountCode from "~/composables/api/orders/useSubmitDiscountCode";
import { useToast } from "~/composables/global/useToast";
@@ -10,25 +11,31 @@ import { QUERY_KEYS } from "~/constants";
const route = useRoute();
const { $queryClient: queryClient } = useNuxtApp();
const discountCode = ref("");
const { addToast } = useToast();
// queries
const { data: cart, isLoading: cartIsLoading } = useGetCartOrders();
const { addToast } = useToast();
const discountCode = ref(cart.value?.discount_code?.code || "");
const {
mutateAsync: submitDiscountCode,
isPending: submitDiscountCodeIsPending,
} = useSubmitDiscountCode();
const {
mutateAsync: deleteDiscountCode,
isPending: deleteDiscountCodeIsPending,
} = useDeleteDiscountCode();
// computed
const nextPage: ComputedRef<{ name: string; label: string } | undefined> =
computed(() => route.meta.nextPage);
const hasSubmittedDiscountCode = computed(() => !!cart.value?.discount_code);
// methods
const handleSubmitDiscountCode = () => {
@@ -50,33 +57,49 @@ const handleSubmitDiscountCode = () => {
}
);
};
const handleDeleteDiscountCode = () => {
deleteDiscountCode(undefined, {
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.cart] });
discountCode.value = "";
},
onError: () => {
addToast({
message: "خطایی در حذف کد تخفیف رخ داد",
options: {
status: "error",
},
});
discountCode.value = "";
},
});
};
</script>
<template>
<div
class="flex flex-col bg-slate-50 sticky top-44 w-full lg:w-3/12 transition-all border border-slate-200 rounded-xl"
class="flex flex-col bg-slate-50 w-full lg:w-3/12 transition-all border border-slate-200 rounded-xl"
>
<div
class="w-full flex items-center justify-between p-5 border-b border-slate-200"
class="w-full flex items-center justify-between py-5 px-4 border-b border-slate-200"
>
<span class="typo-sub-h-xl text-black">فاکتور خرید</span>
<Icon name="ci:cart" class="**:stroke-black" size="24" />
</div>
<div v-if="cartIsLoading" class="flex flex-col p-5 gap-4">
<div v-if="cartIsLoading" class="flex flex-col p-4 gap-4 !rounded-lg">
<Skeleton
v-for="i in 5"
:key="i"
class="w-full !h-7"
:class="{
'!h-12': [4, 5].includes(i),
'!rounded-full': i == 5,
'!rounded-lg': i != 5,
}"
/>
</div>
<div v-else class="flex flex-col p-5 gap-4">
<div v-else class="flex flex-col p-4 gap-4">
<div
class="flex items-center justify-between w-full text-slate-800"
>
@@ -96,7 +119,7 @@ const handleSubmitDiscountCode = () => {
</div>
<div
v-if="!!cart?.discount_code"
v-if="hasSubmittedDiscountCode"
class="flex items-center justify-between w-full text-status-error-primary text-red-700"
>
<span class="max-w-1/2 text-sm"> تخفیف: </span>
@@ -116,23 +139,43 @@ const handleSubmitDiscountCode = () => {
</span>
</div>
<Input
v-model="discountCode"
placeholder="کد تخفیف"
class="!py-2 !pe-2 ps-2.5"
>
<template #endItem>
<button
@click="handleSubmitDiscountCode"
class="text-xs px-3 rounded-[7px] py-1.5 text-white bg-black hover:invert border border-white transition-all"
>
ثبت
</button>
</template>
</Input>
<div class="w-full flex justify-between">
<Input
v-model="discountCode"
placeholder="کد تخفیف"
class="!py-3 !pe-2 ps-2.5 w-full !rounded-none !border-e-[0px] !rounded-s-100"
:disabled="hasSubmittedDiscountCode"
/>
<button
@click="
hasSubmittedDiscountCode
? handleDeleteDiscountCode()
: handleSubmitDiscountCode()
"
class="text-xs px-5 rounded-e-100 py-1.5 text-white bg-black hover:invert border-[1.5px] border-black hover:border-white transition-all disabled:cursor-not-allowed"
:disabled="
!discountCode.length ||
submitDiscountCodeIsPending ||
deleteDiscountCodeIsPending
"
>
<Icon
v-if="
submitDiscountCodeIsPending ||
deleteDiscountCodeIsPending
"
name="svg-spinners:180-ring-with-bg"
size="20"
class="**:fill-white"
/>
<span v-else>
{{ hasSubmittedDiscountCode ? "حذف" : "ثبت" }}
</span>
</button>
</div>
<NuxtLink :to="{ name: nextPage?.name }">
<Button start-icon="bi:arrow-right" class="w-full rounded-full">
<Button start-icon="bi:arrow-right" class="w-full rounded-100">
{{ nextPage?.label }}
</Button>
</NuxtLink>
@@ -1,16 +1,42 @@
<script setup lang="ts"></script>
<script setup lang="ts">
// imports
import { useImage } from "@vueuse/core";
// types
type Props = {
image: string;
title: string;
};
// props
const props = defineProps<Props>();
const { image } = toRefs(props);
const { isLoading: cartImageIsLoading } = useImage({
src: image.value,
});
</script>
<template>
<div class="flex items-center w-full gap-4">
<div
v-if="!cartImageIsLoading"
class="size-[3.5rem] shrink-0 rounded-100 border border-gray-300 overflow-hidden"
>
<img src="/img/product-1.jpg" alt="product" class="object-conver" />
<img :src="image" alt="product" 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"
>
فشارسنج بازویی امرن Omron M3
{{ title }}
</span>
</div>
</template>
@@ -0,0 +1,63 @@
<script setup lang="ts">
// imports
import useGetCartOrders from "~/composables/api/orders/useGetCartOrders";
// state
const showMore = ref(false);
// queries
const { data: cart, isLoading: cartIsLoading } = useGetCartOrders();
</script>
<template>
<div
class="flex flex-col items-center w-full gap-4 p-4 border lg:gap-6 border-gray-300 rounded-xl bg-gray-50"
>
<span
class="flex items-center justify-start w-full lg:text-[1.125rem] font-semibold text-gray-900"
>
خلاصه سفارش
</span>
<div
class="grid w-full grid-cols-1 gap-4 lg:gap-6 lg:grid-cols-3 md:grid-cols-2 lg:h-max transition-all"
:class="showMore ? 'h-[calc(100px)]' : 'h-max'"
>
<template v-if="cartIsLoading">
<Skeleton
v-for="i in 6"
class="w-full !h-14 !rounded-100"
></Skeleton>
</template>
<template v-else>
<div class="w-full overflow-hidden gap-4 flex flex-col">
<MinimalCartItem
v-for="(cartItem, index) in cart?.items"
:key="index"
:image="cartItem.product.image"
:title="cartItem.product.title"
/>
</div>
<div
v-if="cart?.items.length > 5"
class="h-7 flex-center col-span-full lg:hidden"
>
<button
@click="showMore = !showMore"
class="gap-2 flex-center"
>
<span class="text-sm text-black"> مشاهده بیشتر </span>
<Icon name="bi:chevron-down" class="**:stroke-black" />
</button>
</div>
</template>
</div>
</div>
</template>
<style scoped></style>
+46 -38
View File
@@ -92,7 +92,7 @@ watch(
() => debouncedCounter.value,
(nv) => {
addCartItem(
{ id: data.value.id, quantity: nv },
{ id: data.value.product.id, quantity: nv },
{
onSuccess: () => {
invalidateCart();
@@ -116,20 +116,10 @@ watch(
<li
class="flex flex-col items-center w-full gap-4 p-4 border lg:flex-row border-slate-200 rounded-xl bg-slate-50 overflow-hidden relative"
>
<img
src="/logo.png"
class="absolute -top-5 -left-5 rotate-[135deg] size-28"
/>
<div class="flex items-center justify-start w-full gap-2.5 lg:gap-4">
<Skeleton
v-if="cartImageIsLoading"
class="!size-[12rem] aspect-square shrink-0 !rounded-xl border border-slate-200"
/>
<div class="flex items-start justify-start w-full gap-2.5 lg:gap-4">
<div
v-else
class="size-[12rem] aspect-square shrink-0 rounded-xl border border-slate-200 overflow-hidden"
v-if="!cartImageIsLoading"
class="size-[4rem] lg:size-[12rem] aspect-square shrink-0 rounded-xl border border-slate-200 overflow-hidden"
>
<img
:src="data.product.image"
@@ -137,34 +127,42 @@ watch(
alt="product"
/>
</div>
<Skeleton
v-else
class="!size-[12rem] aspect-square shrink-0 !rounded-xl border border-slate-200"
/>
<div class="flex flex-col w-full gap-4">
<span class="font-semibold typo-sub-h-sm text-slate-600">
{{ data.product.category }}
</span>
<div class="flex items-center justify-start gap-3">
<span class="font-semibold typo-sub-h-xl text-black">
{{ data.product.title }}
<div class="flex flex-col w-full gap-3 lg:gap-4">
<div class="flex items-center justify-between gap-3">
<span
class="font-semibold typo-sub-h-xs lg:typo-sub-h-sm text-slate-600"
>
{{ data.product.category }}
</span>
<div
v-if="data.product.discount > 0"
class="text-white bg-blue-500 px-4 py-2 text-xs rounded-full flex items-center gap-1"
v-if="data.discount > 0"
class="text-white bg-blue-500 px-3 lg:px-4 py-1.5 lg:py-2 text-[10px] lg:text-xs rounded-full flex items-center gap-1"
>
<Icon name="bi:percent" class="size-4" />
{{ data.product.discount }}
% تخفیف
{{ data.discount }}
تخفیف
</div>
</div>
<span
class="font-semibold typo-sub-h-sm lg:typo-sub-h-xl text-black"
>
{{ data.product.title }}
</span>
<div class="flex items-center justify-start gap-1.5">
<div
v-if="!!data.product.color"
class="px-3 py-1 rounded-full border border-slate-200 text-sm flex-center gap-1.5"
class="px-3 py-1 rounded-full border border-slate-200 text-xs lg:text-sm flex-center gap-1.5"
>
<span> رنگ </span>
<span
class="!size-4 shadow-black/30 shadow-inner rounded-full"
class="size-3 lg:!size-4 shadow-black/30 shadow-inner rounded-full"
:style="{
backgroundColor: `${data.product.color}`,
}"
@@ -176,7 +174,7 @@ watch(
v-for="(variant, index) in data.product
.product_attributes"
:index="index"
class="px-3 py-1 rounded-full border border-slate-200 text-sm"
class="px-3 py-1 rounded-full border border-slate-200 text-xs lg:text-sm"
>
{{ variant.value }}
</span>
@@ -229,15 +227,15 @@ watch(
<div class="flex items-end gap-2">
<div class="flex flex-col">
<span
v-if="data.product.discount > 0"
v-if="data.discount > 0"
class="typo-p-sm relative flex-center w-fit line-through"
>
{{ data.product.price }}
{{ data.price }}
</span>
<span
class="typo-p-xl relative flex-center w-fit font-medium"
>
{{ data.product.final_price }}
{{ data.final_price }}
</span>
</div>
</div>
@@ -249,7 +247,7 @@ watch(
<div class="flex items-center">
<button
@click="handleIncreaseQuantity"
class="border size-10 flex-center rounded-100 border-slate-300"
class="border size-7 p-1 lg:size-10 flex-center rounded-50 border-slate-300"
:class="
deleteCartItemIsPending ? 'pointer-events-none' : ''
"
@@ -257,11 +255,13 @@ watch(
<Icon name="bi:plus" class="**:stroke-slate-800" />
</button>
<div class="size-10 text-[1.125rem] flex-center">1</div>
<div class="size-10 text-sm flex-center">
{{ counter }}
</div>
<button
@click="handleDecreaseQuantity"
class="border size-10 flex-center rounded-100 border-slate-300"
class="border size-7 lg:size-10 p-1 flex-center rounded-50 border-slate-300"
:class="
deleteCartItemIsPending ? 'pointer-events-none' : ''
"
@@ -279,9 +279,17 @@ watch(
</button>
</div>
<span class="text-[1.125rem] text-slate-900 font-semibold">
{{ data.product.price }}
</span>
<div class="flex flex-col">
<span
v-if="data.discount > 0"
class="typo-p-xs relative flex-center w-fit line-through"
>
{{ data.price }}
</span>
<span class="typo-p-md relative flex-center w-fit font-medium">
{{ data.final_price }}
</span>
</div>
</div>
</li>
</template>
@@ -31,9 +31,6 @@ const handleSubmit = () => {
status: "success",
},
});
setTimeout(() => {
router.push({ name: "index" });
}, 1000);
},
onError: () => {
addToast({
@@ -56,7 +53,11 @@ const handleSubmit = () => {
@close="isShow = false"
>
<template #trigger>
<Button class="rounded-full" end-icon="bi:trash" size="md">
<Button
class="rounded-full shrink-0 whitespace-pre"
end-icon="bi:trash"
size="md"
>
حذف همه
</Button>
</template>
+1 -1
View File
@@ -18,7 +18,7 @@ const { variant, size, loading } = toRefs(props);
// computed
const classes = computed(() => {
return [
"flex items-center justify-center transition-all cursor-pointer",
"flex items-center justify-center transition-all cursor-pointer max-lg:text-xs",
{
"btn-solid": variant.value === "solid",
"btn-secondary": variant.value === "secondary",
+2 -1
View File
@@ -15,7 +15,7 @@ const props = withDefaults(defineProps<Props>(), {
disabled: false,
placeholder: "وارد نشده",
});
const { variant, error, modelValue } = toRefs(props);
const { variant, error, modelValue, disabled } = toRefs(props);
// emits
@@ -38,6 +38,7 @@ const classes = computed(() => {
{
"input-solid": variant.value === "solid",
"input-outlined": variant.value === "outlined",
"pointer-events-none opacity-80 text-slate-500": disabled.value,
"input-effects": !error.value,
[variant.value === "solid"
? "input-solid-error"
+24 -13
View File
@@ -1,31 +1,42 @@
<script lang="ts" setup>
// types
type Props = {
title: string,
products: ProductListItem[]
}
title?: string;
products: ProductListItem[];
withHeader?: boolean;
};
// props
defineProps<Props>();
withDefaults(defineProps<Props>(), {
withHeader: true,
});
</script>
<template>
<section class="w-full flex flex-col gap-10 md:gap-[4rem] py-[5rem] container">
<div class="w-full flex justify-between items-center">
<span class="text-black typo-h-6 max-sm:text-xl md:typo-h-5 lg:typo-h-4">
<section
class="w-full flex flex-col gap-10 md:gap-[4rem] py-[5rem] container"
>
<div v-if="withHeader" class="w-full flex justify-between items-center">
<span
class="text-black typo-h-6 max-sm:text-xl md:typo-h-5 lg:typo-h-4"
>
{{ title }}
</span>
<NuxtLink to="/products">
<Button variant="outlined" class="rounded-full max-sm:typo-label-sm max-sm:py-2" end-icon="ci:arrow-left">
<Button
variant="outlined"
class="rounded-full max-sm:typo-label-sm max-sm:py-2"
end-icon="ci:arrow-left"
>
نمایش همه
</Button>
</NuxtLink>
</div>
<div class="grid grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-y-8 gap-5 sm:gap-8">
<ul
class="grid grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-y-8 gap-5 sm:gap-8"
>
<ProductCard
v-for="product in products"
:key="product.id"
@@ -37,6 +48,6 @@ defineProps<Props>();
:rate="product.rating"
:dark-layer="true"
/>
</div>
</ul>
</section>
</template>
</template>
@@ -13,48 +13,57 @@ const highlights = ref<Highlight[]>([
{
icon: "/img/footer-support.svg",
title: "خدمات مشتری",
description: "پشتیبانی استثنایی، راه‌حل‌های پایدار برای شما"
description: "پشتیبانی استثنایی، راه‌حل‌های پایدار برای شما",
},
{
icon: "/img/footer-send.svg",
title: "ارسال سریع و رایگان",
description: "ارسال رایگان برای سفارش‌های بالای ۱۵۰ دلار"
description: "ارسال رایگان برای سفارش‌های بالای ۱۵۰ دلار",
},
{
icon: "/img/footer-share.svg",
title: "معرفی به دوستان",
description: "ما را به دوستان خود معرفی کنید"
description: "ما را به دوستان خود معرفی کنید",
},
{
icon: "/img/footer-security.svg",
title: "پرداخت امن",
description: "پرداخت شما به‌صورت امن پردازش می‌شود"
}
description: "پرداخت شما به‌صورت امن پردازش می‌شود",
},
]);
</script>
<template>
<section class="w-full border-t-[0.5px] border-slate-200">
<div class="w-full py-[5rem] gap-12 xs:gap-8 sm:gap-12 xl:gap-0 container grid grid-cols-1 xs:grid-cols-2 lg:grid-cols-4">
<div
class="w-full 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">
<div class="flex flex-col-center gap-[.75rem] px-5">
<img :src="highlight.icon" class="size-[70px] md:size-[90px]" alt="" />
<img
:src="highlight.icon"
class="size-[70px] md:size-[90px]"
alt=""
/>
<div class="w-full flex-col-center gap-[.25rem]">
<span class="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-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>
</div>
<!-- <div-->
<!-- class="w-[1px] h-[5rem] bg-slate-200"-->
<!-- v-if="index + 1 != highlights.length"-->
<!-- />-->
<!-- <div-->
<!-- class="w-[1px] h-[5rem] bg-slate-200"-->
<!-- v-if="index + 1 != highlights.length"-->
<!-- />-->
</template>
</div>
</section>
+1 -1
View File
@@ -43,7 +43,7 @@ const closeSideDrawer = () => {
<div
@click.stop
:class="modelValue ? 'translate-x-0' : 'translate-x-[100%]'"
class="md:hidden cursor-default flex top-0 right-0 fixed z-999 transition-all flex-col bg-white w-[350px] h-full gap-8 pt-12"
class="md:hidden cursor-default flex top-0 right-0 fixed z-999 transition-all duration-500 rounded-e-xl flex-col bg-white w-[300px] h-full gap-8 pt-12"
>
<div class="flex items-center flex-col justify-end gap-[1.5rem]">
<Tooltip v-if="!!account && !!token" title="حساب کاربری">
@@ -30,11 +30,10 @@ await suspense();
const onSwiper = (swiper: SwiperClass) => {
swiper_instance.value = swiper;
};
</script>
<template>
<section class="w-full flex flex-col gap-10 md:gap-[4rem] py-[5rem] lg:container">
<section class="w-full flex flex-col gap-10 md:gap-[4rem]">
<div class="w-full flex justify-between items-center max-lg:container">
<span class="text-black typo-h-6 md:typo-h-5 lg:typo-h-4">
{{ title }}
@@ -92,12 +91,12 @@ const onSwiper = (swiper: SwiperClass) => {
:breakpoints="{
640: {
centeredSlides: true,
slidesPerView : 2.5
slidesPerView: 2.5,
},
1024 : {
1024: {
centeredSlides: false,
slidesPerView : 3
}
slidesPerView: 3,
},
}"
>
<SwiperSlide
@@ -1,5 +1,4 @@
<script lang="ts" setup>
// import
import Tag from "~/components/global/Tag.vue";
@@ -10,7 +9,7 @@ import { useImageColor } from "~/composables/global/useImageColor";
// types
type Props = {
id: number,
id: number;
title: string;
colors: string[];
price: string;
@@ -28,77 +27,89 @@ const { id } = toRefs(props);
// state
const { colorObject } = useImageColor(`#product-image-${id.value}`);
</script>
<template>
<NuxtLink :to="'/product/' + id">
<div class="@container">
<div
class="group relative size-full aspect-square rounded-xl @[280px]:rounded-2xl bg-white brightness-[98%] overflow-hidden p-6"
>
<img
:id="`product-image-${id}`"
:src="picture"
class="group-hover:scale-105 transition-transform duration-200 size-full object-contain absolute inset-0"
alt="product-background"
/>
<li class="w-full">
<NuxtLink :to="'/product/' + id">
<div class="@container">
<div
v-if="darkLayer"
class="bg-linear-to-t inset-0 from-black/50 to-transparent to-55% absolute z-10 size-full"
/>
<div
class="flex justify-between items-center absolute px-4 @[280px]:px-6 pt-4 @[280px]:pt-6 top-0 w-full inset-x-0"
>
<Rate v-if="rate" :rate="rate" />
<Tag v-if="tag">
{{ tag }}
</Tag>
</div>
<div
:class="
colorObject?.isLight && !darkLayer
? 'text-black'
: 'text-white'
"
class="absolute inset-x-0 bottom-0 pb-4 @[280px]:pb-6 px-4 @[280px]:px-6 flex flex-row-reverse justify-between items-end z-10"
class="group relative size-full aspect-square rounded-xl @[280px]:rounded-2xl bg-white brightness-[98%] overflow-hidden p-6"
>
<img
:id="`product-image-${id}`"
:src="picture"
class="group-hover:scale-105 transition-transform duration-200 size-full object-contain absolute inset-0"
alt="product-background"
/>
<div class="flex flex-col gap-2 items-start w-full">
<span class="@max-[280px]:hidden typo-sub-h-md @[280px]:typo-sub-h-lg truncate w-full">
{{ title }}
</span>
<div class="flex items-center justify-between w-full mt-1">
<div class="flex items-center gap-2 @[280px]:mt-1">
<ColorCircle
v-for="color in colors"
:key="color"
:style="{ backgroundColor: color }"
class="!size-5 @[280px]:!size-6"
/>
</div>
<span class="@max-[280px]:hidden typo-p-xs @[280px]:typo-p-md !font-semibold whitespace-nowrap">
{{ price }}
<div
v-if="darkLayer"
class="bg-linear-to-t inset-0 from-black/50 to-transparent to-55% absolute z-10 size-full"
/>
<div
class="flex justify-between items-center absolute px-4 @[280px]:px-6 pt-4 @[280px]:pt-6 top-0 w-full inset-x-0"
>
<Rate v-if="rate" :rate="rate" />
<Tag v-if="tag">
{{ tag }}
</Tag>
</div>
<div
:class="
colorObject?.isLight && !darkLayer
? 'text-black'
: 'text-white'
"
class="absolute inset-x-0 bottom-0 pb-4 @[280px]:pb-6 px-4 @[280px]:px-6 flex flex-row-reverse justify-between items-end z-10"
>
<div class="flex flex-col gap-2 items-start w-full">
<span
class="@max-[280px]:hidden typo-sub-h-md @[280px]:typo-sub-h-lg truncate w-full"
>
{{ title }}
</span>
<div
class="flex items-center justify-between w-full mt-1"
>
<div
class="flex items-center gap-2 @[280px]:mt-1"
>
<ColorCircle
v-for="color in colors"
:key="color"
:style="{ backgroundColor: color }"
class="!size-5 @[280px]:!size-6"
/>
</div>
<span
class="@max-[280px]:hidden typo-p-xs @[280px]:typo-p-md !font-semibold whitespace-nowrap"
>
{{ price }}
</span>
</div>
</div>
</div>
</div>
</div>
<div class="flex flex-col gap-1 px-2 items-start w-full text-black mt-4 @[280px]:hidden">
<span class="typo-sub-h-sm w-full truncate">
{{ title }}
</span>
<div class="@[280px]:hidden flex items-center justify-between w-full mt-1">
<span class="typo-p-xs !font-semibold whitespace-nowrap">
{{ price }}
<div
class="flex flex-col gap-1 px-2 items-start w-full text-black mt-4 @[280px]:hidden"
>
<span class="typo-sub-h-sm w-full truncate">
{{ title }}
</span>
<div
class="@[280px]:hidden flex items-center justify-between w-full mt-1"
>
<span
class="typo-p-xs !font-semibold whitespace-nowrap"
>
{{ price }}
</span>
</div>
</div>
</div>
</div>
</NuxtLink>
</NuxtLink>
</li>
</template>
@@ -1,49 +1,41 @@
<script setup lang="ts">
// imports
import useDeleteCartAll from "~/composables/api/orders/useDeleteCartAll";
import { useAuth } from "~/composables/api/auth/useAuth";
import useSignOut from "~/composables/api/auth/useSignOut";
import { useToast } from "~/composables/global/useToast";
import { QUERY_KEYS } from "~/constants";
// state
const { $queryClient: queryClient } = useNuxtApp();
const router = useRouter();
const { refreshToken, logout } = useAuth();
const { addToast } = useToast();
const isShow = ref(false);
// queries
const { mutateAsync: deleteCartAll, isPending: deleteCartAllIsPending } =
useDeleteCartAll();
const { mutateAsync: signout, isPending: signoutIsPending } = useSignOut();
// methods
const handleSubmit = () => {
deleteCartAll(undefined, {
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.cart] });
isShow.value = false;
addToast({
message: "سبد با موفقیت حذف شد",
options: {
status: "success",
},
});
setTimeout(() => {
router.push({ name: "index" });
}, 1000);
},
onError: () => {
addToast({
message: "خطایی در حذف سبد رخ داد",
options: {
status: "error",
},
});
},
});
signout(
{ refresh_token: refreshToken.value! },
{
onSuccess: () => {
addToast({
message: "با موفقیت از حساب خارج شدید",
});
logout(true);
},
onError: () => {
addToast({
message: "خطایی در خروج از حساب رخ داد",
});
isShow.value = false;
},
}
);
};
</script>
@@ -78,10 +70,10 @@ const handleSubmit = () => {
size="md"
>
<Icon
v-if="deleteCartAllIsPending"
v-if="signoutIsPending"
name="svg-spinners:3-dots-bounce"
/>
<span v-else>آره; دارم میرم</span>
<span v-else>آره دارم میرم</span>
</Button>
<DialogClose aria-label="Close">
<Button