d29ed8e35b
- Updated ProductVariant model to include 'profit' and 'special_discount_percent' fields. - Added corresponding fields in the admin interface for ProductVariant. - Created migration to add new fields to the database. feat: implement special discount code functionality in cart - Added composables for submitting and deleting special discount codes. - Updated CartSummary and CartItem components to handle special discount codes. - Enhanced API endpoints to support special discount operations. - Updated global types to include special discount code details in the cart.
331 lines
11 KiB
Vue
331 lines
11 KiB
Vue
<script setup lang="ts">
|
|
// imports
|
|
|
|
import useDeleteDiscountCode from "~/composables/api/orders/useDeleteDiscountCode";
|
|
import useDeleteSpecialDiscountCode from "~/composables/api/orders/useDeleteSpecialDiscountCode";
|
|
import useGetCartOrders from "~/composables/api/orders/useGetCartOrders";
|
|
import usePayOrder from "~/composables/api/orders/usePayOrder";
|
|
import useSubmitDiscountCode from "~/composables/api/orders/useSubmitDiscountCode";
|
|
import useSubmitSpecialDiscountCode from "~/composables/api/orders/useSubmitSpecialDiscountCode";
|
|
import { useToast } from "~/composables/global/useToast";
|
|
import { QUERY_KEYS } from "~/constants";
|
|
|
|
// state
|
|
|
|
const route = useRoute();
|
|
const { $queryClient: queryClient } = useNuxtApp();
|
|
const { addToast } = useToast();
|
|
|
|
// queries
|
|
|
|
const { data: cart, isLoading: cartIsLoading } = useGetCartOrders();
|
|
|
|
const discountCode = ref(cart.value?.discount_code?.code || "");
|
|
const specialDiscountCode = ref(cart.value?.special_discount_code?.code || "");
|
|
|
|
const { mutateAsync: submitDiscountCode, isPending: submitDiscountCodeIsPending } = useSubmitDiscountCode();
|
|
|
|
const { mutateAsync: deleteDiscountCode, isPending: deleteDiscountCodeIsPending } = useDeleteDiscountCode();
|
|
|
|
const { mutateAsync: submitSpecialDiscountCode, isPending: submitSpecialDiscountCodeIsPending } = useSubmitSpecialDiscountCode();
|
|
|
|
const { mutateAsync: deleteSpecialDiscountCode, isPending: deleteSpecialDiscountCodeIsPending } = useDeleteSpecialDiscountCode();
|
|
|
|
const { mutateAsync: pay, isPending: paymentIsPending } = usePayOrder();
|
|
|
|
// computed
|
|
|
|
const nextPage = computed(() => route.meta.nextPage as { name: string; label: string; query?: string } | undefined);
|
|
|
|
const hasSubmittedDiscountCode = computed(() => !!cart.value?.discount_code);
|
|
|
|
const hasSubmittedSpecialDiscountCode = computed(() => !!cart.value?.special_discount_code);
|
|
|
|
// methods
|
|
|
|
const handleSubmitDiscountCode = () => {
|
|
submitDiscountCode(
|
|
{ code: discountCode.value },
|
|
{
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.cart] });
|
|
},
|
|
onError: () => {
|
|
addToast({
|
|
message: "خطایی در ثبت کد تخفیف رخ داد",
|
|
options: {
|
|
status: "error",
|
|
},
|
|
});
|
|
discountCode.value = "";
|
|
},
|
|
}
|
|
);
|
|
};
|
|
|
|
const handleDeleteDiscountCode = () => {
|
|
deleteDiscountCode(undefined, {
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.cart] });
|
|
discountCode.value = "";
|
|
},
|
|
onError: () => {
|
|
addToast({
|
|
message: "خطایی در حذف کد تخفیف رخ داد",
|
|
options: {
|
|
status: "error",
|
|
},
|
|
});
|
|
discountCode.value = "";
|
|
},
|
|
});
|
|
};
|
|
|
|
const handleSubmitSpecialDiscountCode = () => {
|
|
submitSpecialDiscountCode(
|
|
{ code: specialDiscountCode.value },
|
|
{
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.cart] });
|
|
},
|
|
onError: () => {
|
|
addToast({
|
|
message: "خطایی در ثبت کد تخفیف ویژه رخ داد",
|
|
options: {
|
|
status: "error",
|
|
},
|
|
});
|
|
specialDiscountCode.value = "";
|
|
},
|
|
}
|
|
);
|
|
};
|
|
|
|
const handleDeleteSpecialDiscountCode = () => {
|
|
deleteSpecialDiscountCode(undefined, {
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.cart] });
|
|
specialDiscountCode.value = "";
|
|
},
|
|
onError: () => {
|
|
addToast({
|
|
message: "خطایی در حذف کد تخفیف ویژه رخ داد",
|
|
options: {
|
|
status: "error",
|
|
},
|
|
});
|
|
specialDiscountCode.value = "";
|
|
},
|
|
});
|
|
};
|
|
|
|
const handlePayment = () => {
|
|
pay(
|
|
{
|
|
gateway_type: route.query["gw"] as any,
|
|
},
|
|
{
|
|
onSuccess: (data) => {
|
|
setTimeout(() => {
|
|
window.location.href = data.url;
|
|
}, 2000);
|
|
},
|
|
onError: () => {
|
|
addToast({
|
|
message: "خطایی در پرداخت رخ داد",
|
|
options: {
|
|
description: "لطفا با پشتیبانی تماس بگیرید",
|
|
status: "error",
|
|
},
|
|
});
|
|
},
|
|
}
|
|
);
|
|
};
|
|
|
|
// watch
|
|
|
|
watch(
|
|
() => cart.value?.discount_code,
|
|
(newCode) => {
|
|
if (newCode) {
|
|
discountCode.value = newCode.code;
|
|
} else {
|
|
discountCode.value = "";
|
|
}
|
|
}
|
|
);
|
|
|
|
watch(
|
|
() => cart.value?.special_discount_code,
|
|
(newCode) => {
|
|
if (newCode) {
|
|
specialDiscountCode.value = newCode.code;
|
|
} else {
|
|
specialDiscountCode.value = "";
|
|
}
|
|
}
|
|
);
|
|
</script>
|
|
|
|
<template>
|
|
<div 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 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-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),
|
|
}"
|
|
/>
|
|
</div>
|
|
|
|
<div
|
|
v-else
|
|
class="flex flex-col p-4 gap-4"
|
|
>
|
|
<div class="flex items-center justify-between w-full text-slate-800">
|
|
<span class="max-w-1/2 text-sm"> جمع سبد خرید: </span>
|
|
|
|
<span class="max-w-1/2 text-sm">
|
|
{{ cart?.cart_total }}
|
|
</span>
|
|
</div>
|
|
<div
|
|
class="flex items-center justify-between w-full text-gray-500"
|
|
>
|
|
<span class="max-w-1/2 text-sm"> تخفیف کلی محصولات: </span>
|
|
|
|
<span class="max-w-1/2 text-sm">
|
|
{{ cart?.items_discount_amount }}
|
|
</span>
|
|
</div>
|
|
|
|
<div
|
|
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>
|
|
|
|
<span class="max-w-1/2 text-sm">
|
|
{{ cart?.discount_code.amount }}
|
|
</span>
|
|
</div>
|
|
|
|
<div
|
|
v-if="cart?.special_discount_total && cart.special_discount_total !== '0 تومان'"
|
|
class="flex items-center justify-between w-full text-green-700"
|
|
>
|
|
<span class="max-w-1/2 text-sm"> تخفیف ویژه: </span>
|
|
|
|
<span class="max-w-1/2 text-sm">
|
|
{{ cart?.special_discount_total }}
|
|
</span>
|
|
</div>
|
|
|
|
<div class="flex items-center justify-between w-full text-slate-800">
|
|
<span class="max-w-1/2 text-sm"> مالیات ارزش افزوده: </span>
|
|
|
|
<span class="max-w-1/2 text-sm"> {{ cart?.tax_amount }} </span>
|
|
</div>
|
|
|
|
|
|
<div class="flex items-center justify-between w-full text-slate-900">
|
|
<span class="max-w-1/2 text-sm"> جمع کل: </span>
|
|
|
|
<span class="max-w-1/2 text-sm">
|
|
{{ cart?.final_price }}
|
|
</span>
|
|
</div>
|
|
|
|
<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:bg-transparent hover:text-black border-[1.5px] border-black hover:border-black 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>
|
|
|
|
<div class="w-full flex justify-between">
|
|
<Input
|
|
v-model="specialDiscountCode"
|
|
placeholder="کد تخفیف ویژه"
|
|
class="!py-3 !pe-2 ps-2.5 w-full !rounded-none !border-e-[0px] !rounded-s-100"
|
|
:disabled="hasSubmittedSpecialDiscountCode"
|
|
/>
|
|
<button
|
|
@click="hasSubmittedSpecialDiscountCode ? handleDeleteSpecialDiscountCode() : handleSubmitSpecialDiscountCode()"
|
|
class="text-xs px-5 rounded-e-100 py-1.5 text-white bg-green-600 hover:bg-transparent hover:text-green-600 border-[1.5px] border-green-600 hover:border-green-600 transition-all disabled:cursor-not-allowed"
|
|
:disabled="!specialDiscountCode.length || submitSpecialDiscountCodeIsPending || deleteSpecialDiscountCodeIsPending"
|
|
>
|
|
<Icon
|
|
v-if="submitSpecialDiscountCodeIsPending || deleteSpecialDiscountCodeIsPending"
|
|
name="svg-spinners:180-ring-with-bg"
|
|
size="20"
|
|
class="**:fill-white"
|
|
/>
|
|
<span v-else>
|
|
{{ hasSubmittedSpecialDiscountCode ? "حذف" : "ثبت" }}
|
|
</span>
|
|
</button>
|
|
</div>
|
|
|
|
<Button
|
|
v-if="nextPage?.name == 'payment'"
|
|
start-icon="ci:arrow-right"
|
|
class="w-full rounded-100"
|
|
@click="handlePayment"
|
|
variant="primary"
|
|
>
|
|
{{ nextPage?.label }}
|
|
</Button>
|
|
|
|
<NuxtLink
|
|
v-else
|
|
:to="{ name: nextPage?.name, query: { gw: nextPage?.query } }"
|
|
>
|
|
<Button
|
|
start-icon="arrow-right"
|
|
class="w-full rounded-100"
|
|
variant="primary"
|
|
>
|
|
{{ nextPage?.label }}
|
|
</Button>
|
|
</NuxtLink>
|
|
|
|
<PaymentPendingModal v-model:isShow="paymentIsPending" />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped></style>
|