Files
hossein-por-shop/frontend/components/cart/delivery/AddressItem.vue
T
2025-04-18 19:49:27 +03:30

159 lines
5.1 KiB
Vue

<script setup lang="ts">
// imports
import useDeleteAddress from "~/composables/api/account/useDeleteAddress";
import useSetOrderAddress from "~/composables/api/orders/useSetOrderAddress";
import { useToast } from "~/composables/global/useToast";
import { QUERY_KEYS } from "~/constants";
// types
type Props = {
address?: Address;
isSelected?: boolean;
selectable?: boolean;
};
// props
const props = withDefaults(defineProps<Props>(), {
selectable: true,
});
const { address } = toRefs(props);
// state
const { $queryClient: queryClient } = useNuxtApp();
const { addToast } = useToast();
// queries
const { mutateAsync: deleteAddress, isPending: deleteAddressIsPending } = useDeleteAddress();
const { mutateAsync: setOrderAddress, isPending: setOrderAddressIsPending } = useSetOrderAddress();
// methods
const handleSelectAddress = () => {
setOrderAddress(
{ address_id: address.value?.id! },
{
onSettled: () => {
queryClient.invalidateQueries({
queryKey: [QUERY_KEYS.cart],
});
queryClient.invalidateQueries({
queryKey: [QUERY_KEYS.addresses],
});
},
onError: () => {
addToast({
message: "در انتخاب آدرس خطایی رخ داد",
options: {
description: "لطفا مجدد تلاش کنید",
},
});
},
}
);
};
const handleDeleteAddress = (id: number) => {
deleteAddress(
{ id },
{
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: [QUERY_KEYS.cart],
});
queryClient.invalidateQueries({
queryKey: [QUERY_KEYS.addresses],
});
addToast({
message: "آدرس با موفقیت حذف شد",
options: {
status: "success",
},
});
},
onError: () => {
addToast({
message: "مشکلی در حذف آدرس رخ داد",
options: {
status: "error",
},
});
},
}
);
};
</script>
<template>
<button
@click.prevent="!!address && selectable ? handleSelectAddress() : null"
:class="isSelected ? 'border-transparent ring-2 ring-offset-2 ring-blue-500' : 'border-slate-200'"
class="flex flex-col items-center transition-all relative cursor-pointer w-full group gap-2 lg:gap-4 p-4 border rounded-xl bg-slate-50 overflow-hidden"
>
<div
v-if="deleteAddressIsPending"
class="absolute inset-0"
>
<Skeleton class="!size-full !rounded-xl" />
</div>
<span class="flex items-center justify-between w-full gap-3">
<div class="flex items-center gap-3 max-lg:text-sm font-semibold text-slate-900">
{{ !!address ? address.name : "آدرس" }}
<span
v-if="isSelected || setOrderAddressIsPending"
class="bg-blue-500 rounded-lg px-3 py-2 text-slate-200 text-[10px] lg:text-xs"
>
<span v-if="setOrderAddressIsPending">
<Icon
name="svg-spinners:3-dots-bounce"
class="**:fill-white"
/>
</span>
<span v-else-if="isSelected && !setOrderAddressIsPending"> انتخاب شده </span>
</span>
</div>
<button
v-if="!!address"
@click.stop="handleDeleteAddress(address.id!)"
class="size-8 bg-slate-200/50 rounded-sm flex-center me-2 opacity-0 group-hover:opacity-100 transition-opacity"
>
<Icon
name="bi:trash"
class="**:fill-red-500"
/>
</button>
</span>
<div class="flex flex-col items-center justify-between w-full gap-3 lg:gap-8 lg:flex-row">
<div class="w-full lg:w-9/12 overflow-hidden">
<div
class="w-full overflow-hidden overflow-ellipsis gap-5 text-start whitespace-pre text-xs lg:text-sm text-slate-700"
>
{{
!!address
? `ایران, ${address.province}, ${address.city}, ${address.address}, کدپستی ${address.postal_code}`
: "افزودن آدرس جدید"
}}
</div>
</div>
<div class="flex items-center justify-end w-full lg:w-3/12">
<ClientOnly>
<AddressModal :address="address" />
</ClientOnly>
</div>
</div>
</button>
</template>
<style scoped></style>