changed directory and connected to api
This commit is contained in:
@@ -1,142 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
// state
|
||||
|
||||
const counter = ref(1);
|
||||
|
||||
// methods
|
||||
|
||||
const handleDeleteFromCart = () => {};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex flex-col items-center w-full gap-4 p-4 border lg:flex-row border-slate-200 rounded-xl bg-slate-50"
|
||||
>
|
||||
<div class="flex items-center justify-start w-full gap-2.5 lg:gap-4">
|
||||
<div
|
||||
class="size-[10rem] aspect-square shrink-0 rounded-xl border border-slate-200 overflow-hidden"
|
||||
>
|
||||
<img
|
||||
src="/img/product-1.jpg"
|
||||
class="object-cover size-full"
|
||||
alt="product"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col w-full gap-5">
|
||||
<span class="font-semibold typo-sub-h-md text-slate-600">
|
||||
موبایل
|
||||
</span>
|
||||
|
||||
<span class="font-semibold typo-sub-h-xl text-black">
|
||||
فشارسنج بازویی امرن Omron M3
|
||||
</span>
|
||||
|
||||
<div class="items-center justify-between hidden w-full lg:flex">
|
||||
<div class="flex items-center">
|
||||
<button
|
||||
@click="counter++"
|
||||
class="border size-10 flex-center rounded-100 border-slate-300"
|
||||
>
|
||||
<Icon name="bi:plus" class="**:stroke-slate-800" />
|
||||
</button>
|
||||
|
||||
<div class="size-10 flex-center">{{ counter }}</div>
|
||||
|
||||
<button
|
||||
@click="
|
||||
counter > 1 ? counter-- : handleDeleteFromCart
|
||||
"
|
||||
class="border size-10 flex-center rounded-100 border-slate-300"
|
||||
>
|
||||
<Icon
|
||||
v-if="counter == 1"
|
||||
name="bi:trash"
|
||||
class="**:fill-red-700"
|
||||
/>
|
||||
<Icon
|
||||
v-else
|
||||
name="bi:dash"
|
||||
class="**:stroke-slate-800"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<span class="typo-p-lg text-black font-semibold">
|
||||
۲,۸۹۱,۰۰۰ تومان
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between w-full lg:hidden">
|
||||
<div class="flex items-center">
|
||||
<button
|
||||
class="border size-10 flex-center rounded-100 border-slate-400"
|
||||
>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
class="stroke-slate-800"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M3.33334 8H12.6667"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="M8 3.33325V12.6666"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div class="size-10 text-[1.125rem] flex-center">1</div>
|
||||
|
||||
<button
|
||||
class="border size-10 flex-center rounded-100 border-slate-400"
|
||||
>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
class="stroke-status-error-primary"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M2 4H14"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="M12.6667 4V13.3333C12.6667 14 12 14.6667 11.3333 14.6667H4.66668C4.00001 14.6667 3.33334 14 3.33334 13.3333V4"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="M5.33334 3.99992V2.66659C5.33334 1.99992 6.00001 1.33325 6.66668 1.33325H9.33334C10 1.33325 10.6667 1.99992 10.6667 2.66659V3.99992"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<span class="text-[1.125rem] text-slate-900 font-semibold">
|
||||
۲,۸۹۱,۰۰۰ تومان
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,289 @@
|
||||
<script setup lang="ts">
|
||||
// imports
|
||||
|
||||
import { useImage } from "@vueuse/core";
|
||||
import { useToast } from "~/composables/global/useToast";
|
||||
import useDeleteCartItem from "~/composables/api/orders/useDeleteCartAll";
|
||||
import { QUERY_KEYS } from "~/constants";
|
||||
import useAddCartItem from "~/composables/api/orders/useAddCartItem";
|
||||
|
||||
// types
|
||||
|
||||
type Props = {
|
||||
data: CartItem;
|
||||
};
|
||||
|
||||
// props
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const { data } = toRefs(props);
|
||||
|
||||
// state
|
||||
|
||||
const { $queryClient: queryClient } = useNuxtApp();
|
||||
|
||||
const { addToast } = useToast();
|
||||
|
||||
const counter = ref(data.value.quantity);
|
||||
const debouncedCounter = refDebounced(counter, 700);
|
||||
|
||||
const { isLoading: cartImageIsLoading } = useImage({
|
||||
src: data.value.product.image,
|
||||
});
|
||||
|
||||
// queries
|
||||
|
||||
const { mutateAsync: deleteCartItem, isPending: deleteCartItemIsPending } =
|
||||
useDeleteCartItem();
|
||||
|
||||
const { mutateAsync: addCartItem } = useAddCartItem();
|
||||
|
||||
// methods
|
||||
|
||||
const invalidateCart = () => {
|
||||
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.cart] });
|
||||
};
|
||||
|
||||
const handleDeleteFromCart = () => {
|
||||
deleteCartItem(
|
||||
{ id: data.value.id },
|
||||
{
|
||||
onSuccess: () => {
|
||||
invalidateCart();
|
||||
},
|
||||
onError: () => {
|
||||
addToast({
|
||||
message: "خطایی در حذف محصول از سبد رخ داد",
|
||||
options: {
|
||||
status: "error",
|
||||
},
|
||||
});
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const handleIncreaseQuantity = () => {
|
||||
if (counter.value == data.value.product.in_stock) {
|
||||
addToast({
|
||||
message: "به حداکثر موجودی انبار رسیده اید",
|
||||
options: {
|
||||
status: "error",
|
||||
description: `تعداد موجودی ${data.value.product.in_stock} عدد میباشد`,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
counter.value++;
|
||||
}
|
||||
};
|
||||
|
||||
const handleDecreaseQuantity = () => {
|
||||
if (counter.value == 1) {
|
||||
handleDeleteFromCart();
|
||||
} else if (counter.value > 1) {
|
||||
counter.value--;
|
||||
}
|
||||
};
|
||||
|
||||
// watch
|
||||
|
||||
watch(
|
||||
() => debouncedCounter.value,
|
||||
(nv) => {
|
||||
addCartItem(
|
||||
{ id: data.value.id, quantity: nv },
|
||||
{
|
||||
onSuccess: () => {
|
||||
invalidateCart();
|
||||
},
|
||||
onError: () => {
|
||||
invalidateCart();
|
||||
addToast({
|
||||
message: `خطایی در تغییر تعداد محصول ${data.value.product.title} رخ داد`,
|
||||
options: {
|
||||
status: "error",
|
||||
},
|
||||
});
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<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
|
||||
v-else
|
||||
class="size-[12rem] aspect-square shrink-0 rounded-xl border border-slate-200 overflow-hidden"
|
||||
>
|
||||
<img
|
||||
:src="data.product.image"
|
||||
class="object-cover size-full"
|
||||
alt="product"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<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 }}
|
||||
</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"
|
||||
>
|
||||
<Icon name="material-symbols:percent" class="size-4" />
|
||||
{{ data.product.discount }}
|
||||
% تخفیف
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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"
|
||||
>
|
||||
<span> رنگ </span>
|
||||
<span
|
||||
class="!size-4 shadow-black/30 shadow-inner rounded-full"
|
||||
:style="{
|
||||
backgroundColor: `${data.product.color}`,
|
||||
}"
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
v-if="data.product.product_attributes.length > 0"
|
||||
v-for="(variant, index) in data.product
|
||||
.product_attributes"
|
||||
:index="index"
|
||||
class="px-3 py-1 rounded-full border border-slate-200 text-sm"
|
||||
>
|
||||
{{ variant.value }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="items-center justify-between hidden w-full lg:flex -mt-3"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<button
|
||||
@click="handleIncreaseQuantity"
|
||||
class="border size-10 flex-center rounded-100 border-slate-300"
|
||||
:class="
|
||||
deleteCartItemIsPending
|
||||
? 'pointer-events-none'
|
||||
: ''
|
||||
"
|
||||
>
|
||||
<Icon name="bi:plus" class="**:stroke-slate-800" />
|
||||
</button>
|
||||
|
||||
<div class="size-10 flex-center">{{ counter }}</div>
|
||||
|
||||
<button
|
||||
@click="handleDecreaseQuantity"
|
||||
class="border size-10 flex-center rounded-100 border-slate-300"
|
||||
:class="
|
||||
deleteCartItemIsPending
|
||||
? 'pointer-events-none'
|
||||
: ''
|
||||
"
|
||||
>
|
||||
<Icon
|
||||
v-if="counter == 1"
|
||||
:name="
|
||||
deleteCartItemIsPending
|
||||
? 'svg-spinners:3-dots-bounce'
|
||||
: 'bi:trash'
|
||||
"
|
||||
class="**:fill-red-700"
|
||||
/>
|
||||
<Icon
|
||||
v-else
|
||||
name="bi:dash"
|
||||
class="**:stroke-slate-800"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex items-end gap-2">
|
||||
<div class="flex flex-col">
|
||||
<span
|
||||
v-if="data.product.discount > 0"
|
||||
class="typo-p-sm relative flex-center w-fit line-through"
|
||||
>
|
||||
{{ data.product.price }}
|
||||
</span>
|
||||
<span
|
||||
class="typo-p-xl relative flex-center w-fit font-medium"
|
||||
>
|
||||
{{ data.product.final_price }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between w-full lg:hidden">
|
||||
<div class="flex items-center">
|
||||
<button
|
||||
@click="handleIncreaseQuantity"
|
||||
class="border size-10 flex-center rounded-100 border-slate-300"
|
||||
:class="
|
||||
deleteCartItemIsPending ? 'pointer-events-none' : ''
|
||||
"
|
||||
>
|
||||
<Icon name="bi:plus" class="**:stroke-slate-800" />
|
||||
</button>
|
||||
|
||||
<div class="size-10 text-[1.125rem] flex-center">1</div>
|
||||
|
||||
<button
|
||||
@click="handleDecreaseQuantity"
|
||||
class="border size-10 flex-center rounded-100 border-slate-300"
|
||||
:class="
|
||||
deleteCartItemIsPending ? 'pointer-events-none' : ''
|
||||
"
|
||||
>
|
||||
<Icon
|
||||
v-if="counter == 1"
|
||||
:name="
|
||||
deleteCartItemIsPending
|
||||
? 'svg-spinners:3-dots-bounce'
|
||||
: 'bi:trash'
|
||||
"
|
||||
class="**:fill-red-700"
|
||||
/>
|
||||
<Icon v-else name="bi:dash" class="**:stroke-slate-800" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<span class="text-[1.125rem] text-slate-900 font-semibold">
|
||||
{{ data.product.price }}
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
Reference in New Issue
Block a user