From 45494d7ae54131bf016cf633cfb8b9a41baa2737 Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Tue, 23 Jun 2026 11:16:25 +0330 Subject: [PATCH] user Addresses --- .env | 4 +- src/pages/customers/types/Types.ts | 24 +- src/pages/orders/Create.tsx | 38 ++- .../components/CreateOrderAddressSelector.tsx | 81 ++++++ .../components/CreateOrderCustomerSection.tsx | 19 +- .../components/CreateOrderDeliverySection.tsx | 56 +++- .../orders/components/CreateOrderSidebar.tsx | 251 ++++++++---------- src/pages/orders/hooks/useCreateOrderForm.ts | 100 ++++++- src/pages/orders/utils/mapSavedAddress.ts | 16 ++ 9 files changed, 422 insertions(+), 167 deletions(-) create mode 100644 src/pages/orders/components/CreateOrderAddressSelector.tsx create mode 100644 src/pages/orders/utils/mapSavedAddress.ts diff --git a/.env b/.env index 7b5e9c6..d0609b0 100644 --- a/.env +++ b/.env @@ -1,7 +1,7 @@ VITE_TOKEN_NAME = 'dmnu_a_t' VITE_REFRESH_TOKEN_NAME = 'dmnu-a-rt' -VITE_BASE_URL = 'https://dmenu-api.danakcorp.com' -# VITE_BASE_URL = 'http://192.168.99.131:2000' +# VITE_BASE_URL = 'https://dmenu-api.danakcorp.com' +VITE_BASE_URL = 'http://192.168.99.131:2000' VITE_SOCKET_URL = 'https://dmenu-api.danakcorp.com' \ No newline at end of file diff --git a/src/pages/customers/types/Types.ts b/src/pages/customers/types/Types.ts index 3090a9d..03c95ae 100644 --- a/src/pages/customers/types/Types.ts +++ b/src/pages/customers/types/Types.ts @@ -14,6 +14,28 @@ export type User = { referrer?: string | null; isActive?: boolean; gender?: boolean; + avatarUrl?: string | null; +}; + +export type UserSavedAddress = { + 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 UserWithAddresses = User & { + addresses?: UserSavedAddress[]; }; export type UserRestaurant = { @@ -73,4 +95,4 @@ export type ImportUsersResult = { export type ImportUsersResponse = IResponse; -export type SearchUserByPhoneResponse = IResponse; +export type SearchUserByPhoneResponse = IResponse; diff --git a/src/pages/orders/Create.tsx b/src/pages/orders/Create.tsx index 6721dbf..04ca2e1 100644 --- a/src/pages/orders/Create.tsx +++ b/src/pages/orders/Create.tsx @@ -23,6 +23,14 @@ const CreateOrder: FC = () => { updateItemQuantity, removeItem, isSubmitting, + customerAddresses, + selectedAddressId, + handleUserFound, + handleUserNotFound, + clearCustomerData, + applySavedAddress, + selectManualAddress, + handleAddressFieldChange, } = useCreateOrderForm() return ( @@ -42,18 +50,21 @@ const CreateOrder: FC = () => {
-
- +
+ -
- -
+ { isCourierDelivery={isCourierDelivery} isCarDelivery={isCarDelivery} isDineIn={isDineIn} + customerAddresses={customerAddresses} + selectedAddressId={selectedAddressId} + onSelectSavedAddress={applySavedAddress} + onSelectManualAddress={selectManualAddress} + onAddressFieldChange={handleAddressFieldChange} />
diff --git a/src/pages/orders/components/CreateOrderAddressSelector.tsx b/src/pages/orders/components/CreateOrderAddressSelector.tsx new file mode 100644 index 0000000..647f083 --- /dev/null +++ b/src/pages/orders/components/CreateOrderAddressSelector.tsx @@ -0,0 +1,81 @@ +import { type FC } from 'react' +import { Location } from 'iconsax-react' +import type { UserSavedAddress } from '@/pages/customers/types/Types' + +interface CreateOrderAddressSelectorProps { + addresses: UserSavedAddress[] + selectedAddressId: string | null + onSelectAddress: (address: UserSavedAddress) => void + onSelectManual: () => void +} + +const CreateOrderAddressSelector: FC = ({ + addresses, + selectedAddressId, + onSelectAddress, + onSelectManual, +}) => { + if (addresses.length === 0) { + return null + } + + const isManualSelected = selectedAddressId === null + + return ( +
+
انتخاب آدرس ذخیره‌شده
+
+ {addresses.map((address) => { + const isSelected = selectedAddressId === address.id + const location = [address.province, address.city].filter(Boolean).join('، ') + + return ( + + ) + })} + + +
+
+ ) +} + +export default CreateOrderAddressSelector diff --git a/src/pages/orders/components/CreateOrderCustomerSection.tsx b/src/pages/orders/components/CreateOrderCustomerSection.tsx index 95a1782..92622da 100644 --- a/src/pages/orders/components/CreateOrderCustomerSection.tsx +++ b/src/pages/orders/components/CreateOrderCustomerSection.tsx @@ -5,6 +5,7 @@ import { toast } from 'react-toastify' import Button from '@/components/Button' import Input from '@/components/Input' import { useSearchUserByPhone } from '@/pages/customers/hooks/useUsersData' +import type { UserWithAddresses } from '@/pages/customers/types/Types' import type { CreateOrderFormValues } from '../hooks/useCreateOrderForm' import type { ErrorType } from '@/helpers/types' import { extractErrorMessage } from '@/config/func' @@ -12,11 +13,19 @@ import { isAxiosError } from 'axios' interface CreateOrderCustomerSectionProps { formik: FormikProps + onUserFound: (user: UserWithAddresses) => void + onUserNotFound: () => void + onClearCustomerData: () => void } type CustomerSearchState = 'idle' | 'found' | 'not_found' -const CreateOrderCustomerSection: FC = ({ formik }) => { +const CreateOrderCustomerSection: FC = ({ + formik, + onUserFound, + onUserNotFound, + onClearCustomerData, +}) => { const { mutate: searchUser, isPending: isSearching } = useSearchUserByPhone() const [searchState, setSearchState] = useState('idle') @@ -32,8 +41,7 @@ const CreateOrderCustomerSection: FC = ({ formi const user = response?.data if (!user) { setSearchState('not_found') - formik.setFieldValue('firstName', '') - formik.setFieldValue('lastName', '') + onUserNotFound() toast.info('مشتری یافت نشد. اطلاعات را وارد کنید.') return } @@ -41,14 +49,14 @@ const CreateOrderCustomerSection: FC = ({ formi formik.setFieldValue('userPhone', user.phone || phone) formik.setFieldValue('firstName', user.firstName || '') formik.setFieldValue('lastName', user.lastName || '') + onUserFound(user) setSearchState('found') toast.success('اطلاعات مشتری بارگذاری شد') }, onError: (error: ErrorType) => { if (isAxiosError(error) && error.response?.status === 404) { setSearchState('not_found') - formik.setFieldValue('firstName', '') - formik.setFieldValue('lastName', '') + onUserNotFound() toast.info('مشتری یافت نشد. اطلاعات را وارد کنید.') return } @@ -63,6 +71,7 @@ const CreateOrderCustomerSection: FC = ({ formi formik.handleChange(e) if (searchState !== 'idle') { setSearchState('idle') + onClearCustomerData() } } diff --git a/src/pages/orders/components/CreateOrderDeliverySection.tsx b/src/pages/orders/components/CreateOrderDeliverySection.tsx index 7e56b3f..e3f1fd8 100644 --- a/src/pages/orders/components/CreateOrderDeliverySection.tsx +++ b/src/pages/orders/components/CreateOrderDeliverySection.tsx @@ -6,6 +6,8 @@ import Textarea from '@/components/Textarea' import { formatPrice } from '@/helpers/func' import type { CreateOrderFormValues } from '../hooks/useCreateOrderForm' import type { RestaurantPaymentMethod, RestaurantShipmentMethod } from '../hooks/useCreateOrderForm' +import type { UserSavedAddress } from '@/pages/customers/types/Types' +import CreateOrderAddressSelector from './CreateOrderAddressSelector' interface CreateOrderDeliverySectionProps { formik: FormikProps @@ -14,6 +16,11 @@ interface CreateOrderDeliverySectionProps { isCourierDelivery: boolean isCarDelivery: boolean isDineIn: boolean + customerAddresses: UserSavedAddress[] + selectedAddressId: string | null + onSelectSavedAddress: (address: UserSavedAddress) => void + onSelectManualAddress: () => void + onAddressFieldChange: (e: React.ChangeEvent) => void } const CreateOrderDeliverySection: FC = ({ @@ -23,6 +30,11 @@ const CreateOrderDeliverySection: FC = ({ isCourierDelivery, isCarDelivery, isDineIn, + customerAddresses, + selectedAddressId, + onSelectSavedAddress, + onSelectManualAddress, + onAddressFieldChange, }) => { const { t } = useTranslation('global') @@ -32,8 +44,11 @@ const CreateOrderDeliverySection: FC = ({ }) } + const isAddressReadOnly = + selectedAddressId !== null && customerAddresses.length > 0 + return ( - <> +
تحویل و پرداخت
@@ -108,46 +123,59 @@ const CreateOrderDeliverySection: FC = ({
{isCourierDelivery && ( -
+
آدرس تحویل
+ +
= ({ type='number' name='userAddress.longitude' value={formik.values.userAddress.longitude || ''} - onChange={formik.handleChange} + onChange={onAddressFieldChange} + readOnly={isAddressReadOnly} isNotRequired />
@@ -165,14 +194,15 @@ const CreateOrderDeliverySection: FC = ({ label='آدرس کامل' name='userAddress.address' value={formik.values.userAddress.address} - onChange={formik.handleChange} + onChange={onAddressFieldChange} + readOnly={isAddressReadOnly} />
)} {isCarDelivery && ( -
+
اطلاعات خودرو
@@ -204,7 +234,7 @@ const CreateOrderDeliverySection: FC = ({
)} -
+
توضیحات سفارش