merge with front and admin login and min price
This commit is contained in:
@@ -1,4 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
// imports
|
||||
|
||||
import useDeleteAddress from "~/composables/api/account/useDeleteAddress";
|
||||
import { useToast } from "~/composables/global/useToast";
|
||||
import { QUERY_KEYS } from "~/constants";
|
||||
|
||||
// types
|
||||
|
||||
type Props = {
|
||||
@@ -8,29 +14,86 @@ type Props = {
|
||||
|
||||
// props
|
||||
|
||||
const props = defineProps<Props>();
|
||||
defineProps<Props>();
|
||||
|
||||
// emit
|
||||
|
||||
const emit = defineEmits(["select"]);
|
||||
|
||||
// computed
|
||||
// state
|
||||
|
||||
const { $queryClient: queryClient } = useNuxtApp();
|
||||
|
||||
const { addToast } = useToast();
|
||||
|
||||
// queries
|
||||
|
||||
const { mutateAsync: deleteAddress, isPending: deleteAddressIsPending } =
|
||||
useDeleteAddress();
|
||||
|
||||
// methods
|
||||
|
||||
const handleDeleteAddress = (id: number) => {
|
||||
deleteAddress(
|
||||
{ id },
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [QUERY_KEYS.addresses],
|
||||
});
|
||||
addToast({
|
||||
message: "آدرس با موفقیت حذف شد",
|
||||
options: {
|
||||
status: "success",
|
||||
},
|
||||
});
|
||||
},
|
||||
onError: () => {
|
||||
addToast({
|
||||
message: "مشکلی در حذف آدرس رخ داد",
|
||||
options: {
|
||||
status: "error",
|
||||
},
|
||||
});
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
@click="!!address ? emit('select', address) : null"
|
||||
@click.prevent="!!address ? emit('select', address) : null"
|
||||
:class="
|
||||
isSelected
|
||||
? 'border-cyan-500 ring-2 ring-offset-2 ring-cyan-500'
|
||||
? 'border-slate-200 ring-2 ring-offset-2 ring-black'
|
||||
: 'border-slate-200'
|
||||
"
|
||||
class="flex flex-col items-center transition-all cursor-pointer w-full gap-4 p-4 border rounded-xl bg-slate-50"
|
||||
class="flex flex-col items-center transition-all relative cursor-pointer w-full group 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 lg:text-[1.125rem] font-semibold text-slate-900"
|
||||
>
|
||||
{{ !!address ? address.name : "آدرس" }}
|
||||
<span
|
||||
class="flex items-center justify-start w-full lg:text-[1.125rem] font-semibold text-slate-900"
|
||||
v-if="isSelected"
|
||||
class="bg-black rounded-xl px-3 py-2 text-slate-200 text-xs"
|
||||
>
|
||||
آدرس
|
||||
انتخاب شده
|
||||
</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
|
||||
@@ -41,16 +104,13 @@ const emit = defineEmits(["select"]);
|
||||
>
|
||||
{{
|
||||
!!address
|
||||
? `ایران, ${address.province}, ${address.city}, ${address.full_address}, کدپستی ${address.postal_code}`
|
||||
? `ایران, ${address.province}, ${address.city}, ${address.address}, کدپستی ${address.postal_code}`
|
||||
: "افزودن آدرس جدید"
|
||||
}}
|
||||
</span>
|
||||
|
||||
<div class="flex items-center justify-end w-full lg:w-3/12">
|
||||
<AddressModal
|
||||
@add="(data) => $emit('add', data)"
|
||||
:address="address"
|
||||
/>
|
||||
<AddressModal :address="address" />
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
// imports
|
||||
|
||||
import useCreateOrUpdateAddress, {
|
||||
type CreateOrUpdateAddressRequest,
|
||||
} from "~/composables/api/account/useCreateOrUpdateAddress";
|
||||
import useGetAccount from "~/composables/api/account/useGetAccount";
|
||||
import { QUERY_KEYS } from "~/constants";
|
||||
import { useToast } from "~/composables/global/useToast";
|
||||
|
||||
// types
|
||||
|
||||
type Props = {
|
||||
@@ -11,45 +20,105 @@ const props = defineProps<Props>();
|
||||
|
||||
const { address } = toRefs(props);
|
||||
|
||||
// emit
|
||||
|
||||
const emit = defineEmits(["add"]);
|
||||
|
||||
// state
|
||||
|
||||
const isShow = ref(false);
|
||||
|
||||
const addressData = ref<
|
||||
Pick<Address, "province" | "city" | "postal_code" | "full_address">
|
||||
>({
|
||||
province: address.value?.province ?? "",
|
||||
city: address.value?.city ?? "",
|
||||
postal_code: address.value?.postal_code ?? "",
|
||||
full_address: address.value?.full_address ?? "",
|
||||
});
|
||||
|
||||
// computed
|
||||
|
||||
const isEditing = computed(() => !!address.value);
|
||||
|
||||
// state
|
||||
|
||||
const { $queryClient: queryClient } = useNuxtApp();
|
||||
|
||||
const { addToast } = useToast();
|
||||
|
||||
const isShow = ref(false);
|
||||
|
||||
const addressData = ref<CreateOrUpdateAddressRequest>({
|
||||
id: address.value?.id ?? undefined,
|
||||
province: address.value?.province ?? "",
|
||||
city: address.value?.city ?? "",
|
||||
postal_code: address.value?.postal_code ?? "",
|
||||
address: address.value?.address ?? "",
|
||||
name: address.value?.name ?? "",
|
||||
phone: address.value?.phone ?? "",
|
||||
for_me: !isEditing.value
|
||||
? address.value?.for_me ?? "بله"
|
||||
: address.value?.for_me == true
|
||||
? "بله"
|
||||
: "خیر",
|
||||
});
|
||||
|
||||
// queries
|
||||
|
||||
const { data: account } = useGetAccount();
|
||||
|
||||
const {
|
||||
mutateAsync: createOrUpdateAddress,
|
||||
isPending: createAddressIsPending,
|
||||
} = useCreateOrUpdateAddress(isEditing);
|
||||
|
||||
// methods
|
||||
|
||||
const closeModal = () => {
|
||||
if (!isEditing.value) {
|
||||
addressData.value = {
|
||||
id: undefined,
|
||||
province: "",
|
||||
city: "",
|
||||
address: "",
|
||||
postal_code: "",
|
||||
full_address: "",
|
||||
name: "",
|
||||
phone: "",
|
||||
for_me: "بله",
|
||||
};
|
||||
}
|
||||
isShow.value = false;
|
||||
};
|
||||
|
||||
const addNew = () => {
|
||||
emit("add", { ...addressData.value, id: Date.now() });
|
||||
createOrUpdateAddress(
|
||||
{ ...addressData.value },
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [QUERY_KEYS.addresses],
|
||||
});
|
||||
closeModal();
|
||||
addToast({
|
||||
message: isEditing.value
|
||||
? "آدرس با موفقیت ویرایش شد"
|
||||
: "آدرس با موفقیت اضافه شد",
|
||||
options: {
|
||||
status: "success",
|
||||
},
|
||||
});
|
||||
},
|
||||
onError: () => {
|
||||
addToast({
|
||||
message: isEditing.value
|
||||
? "آدرس با موفقیت ویرایش شد"
|
||||
: "مشکلی در افزودن آدرس رخ داد",
|
||||
options: {
|
||||
status: "error",
|
||||
},
|
||||
});
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
watch(
|
||||
() => addressData.value.for_me,
|
||||
(newValue) => {
|
||||
if (!isEditing.value) {
|
||||
addressData.value.phone =
|
||||
newValue == "بله" ? account.value?.phone : "";
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -74,7 +143,7 @@ const addNew = () => {
|
||||
</DialogTrigger>
|
||||
<DialogPortal>
|
||||
<DialogOverlay
|
||||
class="bg-black/20 data-[state=open]:animate-overlay-show fixed inset-0 z-30"
|
||||
class="bg-black/50 backdrop-blur-sm data-[state=open]:animate-overlay-show fixed inset-0 z-30"
|
||||
/>
|
||||
<DialogContent
|
||||
class="data-[state=open]:animate-content-show text-black font-iran-yekan-x fixed top-[50%] left-[50%] max-h-[85vh] w-[90vw] max-w-[50rem] translate-x-[-50%] translate-y-[-50%] rounded-3xl bg-white shadow-[hsl(206_22%_7%_/_35%)_0px_10px_38px_-10px,_hsl(206_22%_7%_/_20%)_0px_10px_20px_-15px] focus:outline-none z-[100]"
|
||||
@@ -86,10 +155,16 @@ const addNew = () => {
|
||||
class="inline-flex size-8 items-center justify-center transition-all rounded-full bg-gray-50 border border-slate-200 hover:border-black focus:outline-none"
|
||||
aria-label="Close"
|
||||
>
|
||||
<Icon name="bi:x-lg" class="**:fill-red-600" />
|
||||
<Icon name="bi:x-lg" class="**:fill-black" />
|
||||
</DialogClose>
|
||||
<DialogTitle class="typo-sub-h-xl font-semibold">
|
||||
<DialogTitle
|
||||
class="typo-sub-h-xl font-semibold flex items-center gap-3"
|
||||
>
|
||||
{{ !!address ? "ویرایش آدرس" : "افزودن آدرس" }}
|
||||
<Icon
|
||||
:name="!!address ? 'bi:pen' : 'bi:plus'"
|
||||
:size="!!address ? '20' : '32'"
|
||||
/>
|
||||
</DialogTitle>
|
||||
</div>
|
||||
|
||||
@@ -97,6 +172,59 @@ const addNew = () => {
|
||||
<div
|
||||
class="grid w-full grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3"
|
||||
>
|
||||
<div class="flex flex-col gap-2">
|
||||
<label
|
||||
for="name"
|
||||
class="text-xs font-semibold lg:text-sm text-gray-900"
|
||||
>نام پیش فرض
|
||||
<span class="text-sm text-red-500"
|
||||
>*</span
|
||||
></label
|
||||
>
|
||||
|
||||
<Input
|
||||
id="name"
|
||||
type="text"
|
||||
placeholder="اینجا وارد کنید ..."
|
||||
v-model="addressData.name"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<label
|
||||
for="province"
|
||||
class="text-xs font-semibold lg:text-sm text-gray-900"
|
||||
>آدرس شما؟
|
||||
<span class="text-sm text-red-500"
|
||||
>*</span
|
||||
></label
|
||||
>
|
||||
|
||||
<Select
|
||||
:options="['بله', 'خیر']"
|
||||
placeholder="انتخاب کنید"
|
||||
v-model="addressData.for_me"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<label
|
||||
for="phone"
|
||||
class="text-xs font-semibold lg:text-sm text-gray-900"
|
||||
>شماره تلفن
|
||||
<span class="text-sm text-red-500"
|
||||
>*</span
|
||||
></label
|
||||
>
|
||||
|
||||
<Input
|
||||
id="phone"
|
||||
type="text"
|
||||
placeholder="اینجا وارد کنید ..."
|
||||
v-model="addressData.phone"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<label
|
||||
for="province"
|
||||
@@ -107,18 +235,10 @@ const addNew = () => {
|
||||
></label
|
||||
>
|
||||
|
||||
<ComboBox
|
||||
<Input
|
||||
id="province"
|
||||
:options="[
|
||||
{
|
||||
name: 'استان ها',
|
||||
children: [
|
||||
{ name: 'مشهد' },
|
||||
{ name: 'مشهد' },
|
||||
{ name: 'مشهد' },
|
||||
],
|
||||
},
|
||||
]"
|
||||
type="text"
|
||||
placeholder="اینجا وارد کنید ..."
|
||||
v-model="addressData.province"
|
||||
/>
|
||||
</div>
|
||||
@@ -132,18 +252,10 @@ const addNew = () => {
|
||||
>*</span
|
||||
></label
|
||||
>
|
||||
<ComboBox
|
||||
<Input
|
||||
id="city"
|
||||
:options="[
|
||||
{
|
||||
name: 'استان ها',
|
||||
children: [
|
||||
{ name: 'مشهد' },
|
||||
{ name: 'مشهد' },
|
||||
{ name: 'مشهد' },
|
||||
],
|
||||
},
|
||||
]"
|
||||
type="text"
|
||||
placeholder="اینجا وارد کنید ..."
|
||||
v-model="addressData.city"
|
||||
/>
|
||||
</div>
|
||||
@@ -160,8 +272,7 @@ const addNew = () => {
|
||||
<Input
|
||||
id="post"
|
||||
type="text"
|
||||
placeholder="جست و جو"
|
||||
class="rounded-xl"
|
||||
placeholder="اینجا وارد کنید ..."
|
||||
v-model="addressData.postal_code"
|
||||
/>
|
||||
</div>
|
||||
@@ -177,16 +288,24 @@ const addNew = () => {
|
||||
<textarea
|
||||
id="address"
|
||||
placeholder="آدرس خود را بنویسید"
|
||||
v-model="addressData.full_address"
|
||||
v-model="addressData.address"
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-6 border-t border-slate-200 flex gap-3">
|
||||
<Button @click="addNew" class="rounded-full px-10"
|
||||
>ثبت</Button
|
||||
<Button
|
||||
:disabled="createAddressIsPending"
|
||||
@click="addNew"
|
||||
class="rounded-full px-10"
|
||||
>
|
||||
<Icon
|
||||
v-if="createAddressIsPending"
|
||||
name="svg-spinners:3-dots-bounce"
|
||||
/>
|
||||
<span v-else>ثبت</span>
|
||||
</Button>
|
||||
<DialogClose aria-label="Close">
|
||||
<Button variant="outlined" class="rounded-full px-10">
|
||||
انصراف
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<script setup lang="ts">
|
||||
// types
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
icon: string;
|
||||
};
|
||||
|
||||
// props
|
||||
|
||||
defineProps<Props>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="w-full flex-col flex-grow py-[12rem] gap-6 border-2 border-slate-200 border-dashed size-full rounded-100 flex-center"
|
||||
>
|
||||
<Icon :name="icon" size="50" class="**:fill-gray-500" />
|
||||
<span class="text-lg text-gray-500"> {{ title }} </span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -13,7 +13,9 @@ type Emit = {
|
||||
|
||||
// props
|
||||
|
||||
defineProps<Props>();
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const { modelValue } = toRefs(props);
|
||||
|
||||
// emit
|
||||
|
||||
@@ -21,16 +23,12 @@ const emit = defineEmits<Emit>();
|
||||
|
||||
// state
|
||||
|
||||
const selectedValue = ref();
|
||||
const selectedValue = computed({
|
||||
get: () => modelValue.value,
|
||||
set: (value: string) => emit("update:modelValue", value),
|
||||
});
|
||||
|
||||
// watch
|
||||
|
||||
watch(
|
||||
() => selectedValue.value,
|
||||
(newValue) => {
|
||||
emit("update:modelValue", newValue);
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -41,7 +39,8 @@ watch(
|
||||
>
|
||||
<SelectValue
|
||||
:placeholder="placeholder"
|
||||
class="text-slate-400 font-iran-yekan-x"
|
||||
:class="selectedValue ? 'text-black' : 'text-slate-400'"
|
||||
class="font-iran-yekan-x"
|
||||
/>
|
||||
<Icon name="bi:chevron-down" size="16" />
|
||||
</SelectTrigger>
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<script setup lang="ts">
|
||||
// types
|
||||
|
||||
type Props = {
|
||||
modelValue: boolean;
|
||||
};
|
||||
|
||||
type Emit = {
|
||||
"update:modelValue": [value: boolean];
|
||||
};
|
||||
|
||||
// props
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const { modelValue } = toRefs(props);
|
||||
|
||||
// emit
|
||||
|
||||
const emit = defineEmits<Emit>();
|
||||
|
||||
// computed
|
||||
|
||||
const value = computed({
|
||||
get: () => modelValue.value ?? false,
|
||||
set: (value) => emit("update:modelValue", value),
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SwitchRoot
|
||||
v-model="value"
|
||||
class="w-[3rem] h-[1.8rem] flex data-[state=unchecked]:bg-slate-200 data-[state=checked]:bg-stone-800 border border-slate-300 data-[state=checked]:border-stone-700 rounded-full relative transition-all focus-within:outline-none"
|
||||
>
|
||||
<SwitchThumb
|
||||
class="size-6 my-auto bg-white text-sm ms-1 flex items-center justify-center shadow-xl rounded-full transition-transform translate-x-0.5 will-change-transform data-[state=checked]:-translate-x-[68%]"
|
||||
/>
|
||||
</SwitchRoot>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -177,27 +177,14 @@ watch(
|
||||
|
||||
<div class="flex items-center justify-between w-full gap-5">
|
||||
<span class="text-black">فقط کالاهای تخفیف دار</span>
|
||||
<SwitchRoot
|
||||
v-model="has_discount"
|
||||
class="w-[3rem] h-[1.8rem] flex data-[state=unchecked]:bg-slate-200 data-[state=checked]:bg-stone-800 border border-slate-300 data-[state=checked]:border-stone-700 rounded-full relative transition-all focus-within:outline-none"
|
||||
>
|
||||
<SwitchThumb
|
||||
class="size-6 my-auto bg-white text-sm ms-1 flex items-center justify-center shadow-xl rounded-full transition-transform translate-x-0.5 will-change-transform data-[state=checked]:-translate-x-[68%]"
|
||||
/>
|
||||
</SwitchRoot>
|
||||
|
||||
<Switch v-model="has_discount" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between w-full gap-5">
|
||||
<span class="text-black">فقط کالاهای موجود</span>
|
||||
|
||||
<SwitchRoot
|
||||
v-model="in_stock"
|
||||
class="w-[3rem] h-[1.8rem] flex data-[state=unchecked]:bg-slate-200 data-[state=checked]:bg-stone-800 border border-slate-300 data-[state=checked]:border-stone-700 rounded-full relative transition-all focus-within:outline-none"
|
||||
>
|
||||
<SwitchThumb
|
||||
class="size-6 my-auto bg-white text-sm ms-1 flex items-center justify-center shadow-xl rounded-full transition-transform translate-x-0.5 will-change-transform data-[state=checked]:-translate-x-[68%]"
|
||||
/>
|
||||
</SwitchRoot>
|
||||
<Switch v-model="has_discount" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// imports
|
||||
|
||||
import { useMutation } from "@tanstack/vue-query";
|
||||
import { API_ENDPOINTS } from "~/constants";
|
||||
|
||||
// types
|
||||
|
||||
export type CreateOrUpdateAddressRequest = Address;
|
||||
|
||||
const useCreateOrUpdateAddress = (update: ComputedRef<Boolean>) => {
|
||||
// state
|
||||
|
||||
const { $axios: axios } = useNuxtApp();
|
||||
|
||||
// method
|
||||
|
||||
const handleCreateOrUpdateAddress = async (
|
||||
addressData: CreateOrUpdateAddressRequest
|
||||
) => {
|
||||
const { data } = await axios[update.value ? "put" : "post"](
|
||||
update.value
|
||||
? `${API_ENDPOINTS.account.address.update}/${addressData.id}`
|
||||
: API_ENDPOINTS.account.address.create,
|
||||
{
|
||||
...addressData,
|
||||
for_me: addressData.for_me == "بله" ? true : false,
|
||||
}
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (addressData: CreateOrUpdateAddressRequest) =>
|
||||
handleCreateOrUpdateAddress(addressData),
|
||||
});
|
||||
};
|
||||
|
||||
export default useCreateOrUpdateAddress;
|
||||
@@ -0,0 +1,32 @@
|
||||
// imports
|
||||
|
||||
import { useMutation } from "@tanstack/vue-query";
|
||||
import { API_ENDPOINTS } from "~/constants";
|
||||
|
||||
// types
|
||||
|
||||
export type DeleteAddressRequest = {
|
||||
id: number;
|
||||
};
|
||||
|
||||
const useDeleteAddress = () => {
|
||||
// state
|
||||
|
||||
const { $axios: axios } = useNuxtApp();
|
||||
|
||||
// method
|
||||
|
||||
const handleDeleteAddress = async (id: number) => {
|
||||
const { data } = await axios.delete(
|
||||
`${API_ENDPOINTS.account.address.delete}/${id}`
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (data: DeleteAddressRequest) =>
|
||||
handleDeleteAddress(data.id),
|
||||
});
|
||||
};
|
||||
|
||||
export default useDeleteAddress;
|
||||
@@ -0,0 +1,30 @@
|
||||
// imports
|
||||
|
||||
import { useQuery } from "@tanstack/vue-query";
|
||||
import { API_ENDPOINTS, QUERY_KEYS } from "~/constants";
|
||||
|
||||
// types
|
||||
|
||||
export type GetAllAddressResponse = Address[];
|
||||
|
||||
const useGetAllAddress = () => {
|
||||
// state
|
||||
|
||||
const { $axios: axios } = useNuxtApp();
|
||||
|
||||
// methods
|
||||
|
||||
const handleGetAllAddress = async () => {
|
||||
const { data } = await axios.get<GetAllAddressResponse>(
|
||||
API_ENDPOINTS.account.address.get_all
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
return useQuery({
|
||||
queryKey: [QUERY_KEYS.addresses],
|
||||
queryFn: () => handleGetAllAddress(),
|
||||
});
|
||||
};
|
||||
|
||||
export default useGetAllAddress;
|
||||
+17
-10
@@ -2,31 +2,37 @@ export const API_ENDPOINTS = {
|
||||
home: "/home",
|
||||
blog: {
|
||||
articles: "/blogs/all",
|
||||
article: "/blogs"
|
||||
article: "/blogs",
|
||||
},
|
||||
account: {
|
||||
profile: "/accounts/profile",
|
||||
send_otp: "/accounts/send_otp"
|
||||
send_otp: "/accounts/send_otp",
|
||||
address: {
|
||||
create: "/accounts/address/create",
|
||||
update: "/accounts/address/edit",
|
||||
get_all: "/accounts/address/list",
|
||||
delete: "/accounts/address/delete",
|
||||
},
|
||||
},
|
||||
product: {
|
||||
comments: "/products/comments",
|
||||
create_comment: "/products/comments",
|
||||
get: "/products"
|
||||
get: "/products",
|
||||
},
|
||||
auth: {
|
||||
refresh: "/token/refresh",
|
||||
verify: "/accounts/verify",
|
||||
signin: "/token",
|
||||
logout: "/accounts/logout"
|
||||
logout: "/accounts/logout",
|
||||
},
|
||||
chat: {
|
||||
messages: "/chat/product",
|
||||
new_message: "/chat/product"
|
||||
new_message: "/chat/product",
|
||||
},
|
||||
products: {
|
||||
get_all: "/products",
|
||||
categories: "/products/categories"
|
||||
}
|
||||
categories: "/products/categories",
|
||||
},
|
||||
};
|
||||
|
||||
export const QUERY_KEYS = {
|
||||
@@ -38,14 +44,15 @@ export const QUERY_KEYS = {
|
||||
product: "product",
|
||||
products: "products",
|
||||
account: "account",
|
||||
categories: "categories"
|
||||
categories: "categories",
|
||||
addresses: "addresses",
|
||||
};
|
||||
|
||||
export const MUTATION_KEYS = {
|
||||
create_chat: "create_chat"
|
||||
create_chat: "create_chat",
|
||||
};
|
||||
|
||||
export const PRODUCT_RANGE = {
|
||||
min: 0,
|
||||
max: 100_000_000
|
||||
max: 100_000_000,
|
||||
};
|
||||
|
||||
@@ -52,7 +52,9 @@ const nextPage = computed(() => route.meta.nextPage);
|
||||
<div
|
||||
class="w-full flex flex-col items-center relative justify-between gap-8 lg:gap-6 lg:flex-row lg:items-start"
|
||||
>
|
||||
<div class="flex flex-col w-full gap-4 lg:gap-6 lg:w-9/12">
|
||||
<div
|
||||
class="flex flex-col w-full gap-4 lg:gap-6 lg:w-9/12 shrink-0"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
definePageMeta({
|
||||
layout: "cart",
|
||||
middleware: "check-is-logged-in",
|
||||
pageTitle: "ثبت سفارش",
|
||||
prevPage: { name: "cart-delivery", label: "انتخاب آدرس" },
|
||||
nextPage: { name: "checkout", label: "پرداخت" },
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
// imports
|
||||
|
||||
import useGetAllAddress from "~/composables/api/account/useGetAllAddress";
|
||||
|
||||
// meta
|
||||
|
||||
definePageMeta({
|
||||
layout: "cart",
|
||||
middleware: "check-is-logged-in",
|
||||
pageTitle: "انتخاب آدرس",
|
||||
prevPage: { name: "cart", label: "سبد خرید" },
|
||||
nextPage: { name: "cart-checkout", label: "تسویه حساب" },
|
||||
@@ -21,8 +26,6 @@ type DeliveryData = {
|
||||
|
||||
// state
|
||||
|
||||
const addresses = ref<Address[]>([]);
|
||||
|
||||
const deliveryData = ref<DeliveryData>({
|
||||
address: null,
|
||||
deliveryMethod: {
|
||||
@@ -32,11 +35,9 @@ const deliveryData = ref<DeliveryData>({
|
||||
},
|
||||
});
|
||||
|
||||
// methods
|
||||
const { data: addresses, isLoading: addressesIsLoading } = useGetAllAddress();
|
||||
|
||||
const handleAddNewAddress = (address: Address) => {
|
||||
addresses.value.push(address);
|
||||
};
|
||||
// methods
|
||||
|
||||
const handleSelectAddress = (address: Address) => {
|
||||
deliveryData.value.address = { ...address };
|
||||
@@ -45,10 +46,29 @@ const handleSelectAddress = (address: Address) => {
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col w-full gap-5">
|
||||
<AddressItem @add="handleAddNewAddress" />
|
||||
<div v-if="addresses.length > 0" class="flex flex-col w-full gap-6">
|
||||
<AddressItem />
|
||||
<div class="flex flex-col w-full gap-6">
|
||||
<span class="typo-sub-h-xl"> آدرس های شما </span>
|
||||
<div class="flex flex-col gap-6 w-full">
|
||||
<div v-if="addressesIsLoading" class="flex flex-col gap-6 w-full">
|
||||
<Skeleton
|
||||
v-for="i in 3"
|
||||
class="w-full !h-[10rem] !rounded-xl"
|
||||
/>
|
||||
</div>
|
||||
<template v-else>
|
||||
<div
|
||||
v-if="!addresses?.length"
|
||||
class="flex flex-grow w-full"
|
||||
v-auto-animate
|
||||
>
|
||||
<Placeholder
|
||||
title="آدرسی یافت نشد :("
|
||||
icon="bi:map"
|
||||
class="!py-[2rem]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-else class="flex flex-col gap-6 w-full">
|
||||
<AddressItem
|
||||
v-for="(address, index) in addresses"
|
||||
:key="index"
|
||||
@@ -57,10 +77,11 @@ const handleSelectAddress = (address: Address) => {
|
||||
:isSelected="address.id == deliveryData.address?.id"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex flex-col items-center w-full gap-4 p-4 border border-slate-200 rounded-xl bg-slate-50"
|
||||
class="flex flex-col items-center w-full gap-4 my-3 p-4 border border-slate-200 rounded-xl bg-slate-50"
|
||||
>
|
||||
<span
|
||||
class="flex items-center justify-start w-full lg:text-[1.125rem] font-semibold text-slate-900"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: "cart",
|
||||
middleware: "check-is-logged-in",
|
||||
pageTitle: "سبد خرید",
|
||||
prevPage: { name: "index", label: "بازگشت به خانه" },
|
||||
nextPage: { name: "cart-delivery", label: "انتخاب آدرس" },
|
||||
|
||||
Vendored
+28
-25
@@ -20,7 +20,7 @@ declare global {
|
||||
email: string;
|
||||
profile_photo: string;
|
||||
phone: string;
|
||||
}
|
||||
};
|
||||
|
||||
type Product = {
|
||||
id: number;
|
||||
@@ -28,10 +28,10 @@ declare global {
|
||||
name: string;
|
||||
description: string;
|
||||
currency: string;
|
||||
"video": string | null,
|
||||
"image1": string,
|
||||
"image2": string,
|
||||
"image3": string,
|
||||
video: string | null;
|
||||
image1: string;
|
||||
image2: string;
|
||||
image3: string;
|
||||
rating: number;
|
||||
view: number;
|
||||
sell: number;
|
||||
@@ -63,40 +63,43 @@ declare global {
|
||||
}
|
||||
|
||||
type UserComment = {
|
||||
"id": number,
|
||||
"content": string,
|
||||
"timestamp": string,
|
||||
"show": boolean,
|
||||
"product": number,
|
||||
"user": number
|
||||
}
|
||||
id: number;
|
||||
content: string;
|
||||
timestamp: string;
|
||||
show: boolean;
|
||||
product: number;
|
||||
user: number;
|
||||
};
|
||||
|
||||
type Category = {
|
||||
id: number;
|
||||
name: string;
|
||||
slug: string;
|
||||
icon: string;
|
||||
"product_count": string,
|
||||
"subcategorys": SubCategory[]
|
||||
product_count: string;
|
||||
subcategorys: SubCategory[];
|
||||
};
|
||||
|
||||
type SubCategory = {
|
||||
"id": number,
|
||||
"name": string,
|
||||
"slug": string,
|
||||
"icon": string,
|
||||
"image": string,
|
||||
"product_count": string,
|
||||
"parent": string,
|
||||
"show": boolean
|
||||
}
|
||||
id: number;
|
||||
name: string;
|
||||
slug: string;
|
||||
icon: string;
|
||||
image: string;
|
||||
product_count: string;
|
||||
parent: string;
|
||||
show: boolean;
|
||||
};
|
||||
|
||||
type Address = {
|
||||
id: number;
|
||||
id: number | undefined;
|
||||
province: string;
|
||||
city: string;
|
||||
postal_code: string;
|
||||
full_address: string;
|
||||
address: string;
|
||||
phone: string;
|
||||
name: string;
|
||||
for_me: "خیر" | "بله";
|
||||
};
|
||||
|
||||
type DeliveryMethod = {
|
||||
|
||||
Reference in New Issue
Block a user