From 9c16a6143cbf76ccecc81ccf8e2bd62ef17a09dd Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Thu, 27 Nov 2025 10:48:58 +0330 Subject: [PATCH] list of addresses --- .../profile/address/hooks/useAddressData.ts | 38 +++- .../[name]/(Profile)/profile/address/page.tsx | 180 +++++++++++++----- .../profile/address/service/AddressService.ts | 26 ++- .../(Profile)/profile/address/types/Types.ts | 20 ++ 4 files changed, 213 insertions(+), 51 deletions(-) diff --git a/src/app/[name]/(Profile)/profile/address/hooks/useAddressData.ts b/src/app/[name]/(Profile)/profile/address/hooks/useAddressData.ts index e5cf3dd..ad10303 100644 --- a/src/app/[name]/(Profile)/profile/address/hooks/useAddressData.ts +++ b/src/app/[name]/(Profile)/profile/address/hooks/useAddressData.ts @@ -1,5 +1,11 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import { createAddress, getAddresses } from "../service/AddressService"; +import { + createAddress, + getAddresses, + updateAddress, + deleteAddress, + setDefaultAddress, +} from "../service/AddressService"; export const useGetAddresses = () => { return useQuery({ @@ -17,3 +23,33 @@ export const useCreateAddress = () => { }, }); }; + +export const useUpdateAddress = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: updateAddress, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["addresses"] }); + }, + }); +}; + +export const useDeleteAddress = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: deleteAddress, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["addresses"] }); + }, + }); +}; + +export const useSetDefaultAddress = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: setDefaultAddress, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["addresses"] }); + }, + }); +}; diff --git a/src/app/[name]/(Profile)/profile/address/page.tsx b/src/app/[name]/(Profile)/profile/address/page.tsx index 6513a31..1e80034 100644 --- a/src/app/[name]/(Profile)/profile/address/page.tsx +++ b/src/app/[name]/(Profile)/profile/address/page.tsx @@ -2,25 +2,74 @@ import Button from '@/components/button/PrimaryButton'; import { ef } from '@/lib/helpers/utfNumbers'; -// import { useProfile } from '@/hooks/auth/useProfile'; -// import { useAuthStore } from '@/zustand/authStore'; -import { ArrowLeft, Edit2, TickCircle } from 'iconsax-react'; +import { ArrowLeft, Edit2, TickCircle, Trash } from 'iconsax-react'; import Link from 'next/link'; import { useRouter, useSearchParams } from 'next/navigation'; -import React from 'react' +import React, { useState } from 'react'; +import { useGetAddresses, useSetDefaultAddress, useDeleteAddress } from './hooks/useAddressData'; +import { Address } from './types/Types'; +import Prompt from '@/components/utils/Prompt'; +import { toast } from '@/components/Toast'; +import useToggle from '@/hooks/helpers/useToggle'; type Props = object function UserAddressesPage({ }: Props) { + const { data: addressesResponse, isLoading } = useGetAddresses(); const router = useRouter(); const searchParams = useSearchParams(); const redirect = searchParams.get('redirect'); + const { mutate: setDefault, isPending: isSettingDefault } = useSetDefaultAddress(); + const { mutate: deleteAddressMutation, isPending: isDeleting } = useDeleteAddress(); + const { state: deleteModalVisible, set: setDeleteModal } = useToggle(); + const [selectedAddressId, setSelectedAddressId] = useState(null); - const onSelected = () => { - if(redirect) { - router.replace(redirect); - } - } + const addresses = addressesResponse?.data || []; + + const handleSetDefault = (addressId: string) => { + setDefault(addressId, { + onSuccess: () => { + toast('آدرس به عنوان پیش‌فرض تنظیم شد', 'success'); + if (redirect) { + router.replace(redirect); + } + }, + onError: (error) => { + const errorMessage = error instanceof Error ? error.message : 'خطا در تنظیم آدرس پیش‌فرض'; + toast(errorMessage, 'error'); + }, + }); + }; + + const handleDeleteClick = (addressId: string) => { + setSelectedAddressId(addressId); + setDeleteModal(true); + }; + + const handleDeleteConfirm = () => { + if (!selectedAddressId) return; + + deleteAddressMutation(selectedAddressId, { + onSuccess: () => { + toast('آدرس با موفقیت حذف شد', 'success'); + setDeleteModal(false); + setSelectedAddressId(null); + }, + onError: (error) => { + const errorMessage = error instanceof Error ? error.message : 'خطا در حذف آدرس'; + toast(errorMessage, 'error'); + }, + }); + }; + + const handleDeleteCancel = () => { + setDeleteModal(false); + setSelectedAddressId(null); + }; + + const formatAddress = (address: Address) => { + return `${address.address}، ${address.city}، ${address.province}`; + }; return (
@@ -36,45 +85,71 @@ function UserAddressesPage({ }: Props) {
- {/*
- -

- شما هیچ آدرسی ثبت نکرده اید.برای ثبت آدرس دکمه افزودن را بزنید -

-
*/} -
-

منزل

-

- لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است -

-

- {ef('ارسال: 10,000 تومان')} -

-
- - - - + {isLoading ? ( +
+

+ در حال بارگذاری... +

-
- -
-

شرکت

-

- لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است -

-

- {ef('ارسال: 21,000 تومان')} -

-
+ ) : addresses.length === 0 ? ( +
+

+ شما هیچ آدرسی ثبت نکرده اید. برای ثبت آدرس دکمه افزودن را بزنید +

+
+ ) : ( + addresses.map((address) => ( +
+
+

{address.title}

+ {address.isDefault && ( + + پیش‌فرض + + )} +
+

+ {formatAddress(address)} +

+

+ کد پستی: {ef(address.postalCode || 'ثبت نشده')} +

+

+ تلفن: {ef(address.phone)} +

+
+ {!address.isDefault && ( + + )} +
+ + + + +
+
+
+ )) + )}
@@ -82,6 +157,17 @@ function UserAddressesPage({ }: Props) {
+ +
) } diff --git a/src/app/[name]/(Profile)/profile/address/service/AddressService.ts b/src/app/[name]/(Profile)/profile/address/service/AddressService.ts index 96c20ce..aa622d7 100644 --- a/src/app/[name]/(Profile)/profile/address/service/AddressService.ts +++ b/src/app/[name]/(Profile)/profile/address/service/AddressService.ts @@ -1,8 +1,12 @@ import { api } from "@/lib/api/axiosInstance"; -import { CreateAddressType } from "../types/Types"; +import { + CreateAddressType, + AddressesResponse, + UpdateAddressType, +} from "../types/Types"; -export const getAddresses = async () => { - const { data } = await api.get("/public/user/addresses"); +export const getAddresses = async (): Promise => { + const { data } = await api.get("/public/user/addresses"); return data; }; @@ -10,3 +14,19 @@ export const createAddress = async (params: CreateAddressType) => { const { data } = await api.post("/public/user/addresses", params); return data; }; + +export const updateAddress = async (params: UpdateAddressType) => { + const { id, ...rest } = params; + const { data } = await api.put(`/public/user/addresses/${id}`, rest); + return data; +}; + +export const deleteAddress = async (id: string) => { + const { data } = await api.delete(`/public/user/addresses/${id}`); + return data; +}; + +export const setDefaultAddress = async (id: string) => { + const { data } = await api.patch(`/public/user/addresses/${id}/set-default`); + return data; +}; diff --git a/src/app/[name]/(Profile)/profile/address/types/Types.ts b/src/app/[name]/(Profile)/profile/address/types/Types.ts index 761e294..f75e771 100644 --- a/src/app/[name]/(Profile)/profile/address/types/Types.ts +++ b/src/app/[name]/(Profile)/profile/address/types/Types.ts @@ -1,4 +1,24 @@ import type { Icon, DivIcon } from "leaflet"; +import type { BaseResponse } from "@/app/[name]/(Main)/types/Types"; + +export interface Address { + id: string; + createdAt: string; + updatedAt: string; + deletedAt: string | null; + user: string; + title: string; + address: string; + city: string; + province: string; + postalCode: string; + latitude: number; + longitude: number; + phone: string; + isDefault: boolean; +} + +export type AddressesResponse = BaseResponse; export type CreateAddressType = { title: string;