user Addresses

This commit is contained in:
hamid zarghami
2026-06-23 11:16:25 +03:30
parent 223b1b5e8f
commit 45494d7ae5
9 changed files with 422 additions and 167 deletions
+2 -2
View File
@@ -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'
+23 -1
View File
@@ -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<ImportUsersResult>;
export type SearchUserByPhoneResponse = IResponse<User>;
export type SearchUserByPhoneResponse = IResponse<UserWithAddresses>;
+27 -11
View File
@@ -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 = () => {
</div>
<div className='flex flex-col xl:flex-row gap-7 mt-9'>
<div className='flex-1 min-w-0'>
<CreateOrderCustomerSection formik={formik} />
<div className='flex-1 min-w-0 flex flex-col gap-8'>
<CreateOrderCustomerSection
formik={formik}
onUserFound={handleUserFound}
onUserNotFound={handleUserNotFound}
onClearCustomerData={clearCustomerData}
/>
<div className='mt-8'>
<CreateOrderItemsSection
formik={formik}
foods={foods}
onAddFood={addFoodToOrder}
onUpdateQuantity={updateItemQuantity}
onRemoveItem={removeItem}
/>
</div>
<CreateOrderItemsSection
formik={formik}
foods={foods}
onAddFood={addFoodToOrder}
onUpdateQuantity={updateItemQuantity}
onRemoveItem={removeItem}
/>
<CreateOrderDeliverySection
formik={formik}
@@ -62,6 +73,11 @@ const CreateOrder: FC = () => {
isCourierDelivery={isCourierDelivery}
isCarDelivery={isCarDelivery}
isDineIn={isDineIn}
customerAddresses={customerAddresses}
selectedAddressId={selectedAddressId}
onSelectSavedAddress={applySavedAddress}
onSelectManualAddress={selectManualAddress}
onAddressFieldChange={handleAddressFieldChange}
/>
</div>
@@ -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<CreateOrderAddressSelectorProps> = ({
addresses,
selectedAddressId,
onSelectAddress,
onSelectManual,
}) => {
if (addresses.length === 0) {
return null
}
const isManualSelected = selectedAddressId === null
return (
<div className='mb-6'>
<div className='text-sm mb-3'>انتخاب آدرس ذخیرهشده</div>
<div className='grid grid-cols-1 sm:grid-cols-2 gap-3'>
{addresses.map((address) => {
const isSelected = selectedAddressId === address.id
const location = [address.province, address.city].filter(Boolean).join('، ')
return (
<button
key={address.id}
type='button'
onClick={() => onSelectAddress(address)}
className={`text-right p-4 rounded-xl border transition-colors ${
isSelected
? 'border-primary bg-primary/5'
: 'border-border hover:border-primary/40'
}`}
>
<div className='flex items-center justify-between gap-2'>
<div className='text-sm font-medium'>{address.title || 'آدرس'}</div>
{address.isDefault && (
<span className='text-[10px] px-2 py-0.5 rounded-full bg-primary/10 text-primary'>
پیشفرض
</span>
)}
</div>
{location && (
<div className='text-xs text-description mt-1'>{location}</div>
)}
<div className='text-xs mt-2 line-clamp-2'>{address.address}</div>
</button>
)
})}
<button
type='button'
onClick={onSelectManual}
className={`text-right p-4 rounded-xl border transition-colors ${
isManualSelected
? 'border-primary bg-primary/5'
: 'border-border hover:border-primary/40'
}`}
>
<div className='flex items-center gap-2 text-sm font-medium'>
<Location size={16} color='#000' />
<span>ورود آدرس جدید</span>
</div>
<div className='text-xs text-description mt-1'>
آدرس را بهصورت دستی وارد کنید
</div>
</button>
</div>
</div>
)
}
export default CreateOrderAddressSelector
@@ -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<CreateOrderFormValues>
onUserFound: (user: UserWithAddresses) => void
onUserNotFound: () => void
onClearCustomerData: () => void
}
type CustomerSearchState = 'idle' | 'found' | 'not_found'
const CreateOrderCustomerSection: FC<CreateOrderCustomerSectionProps> = ({ formik }) => {
const CreateOrderCustomerSection: FC<CreateOrderCustomerSectionProps> = ({
formik,
onUserFound,
onUserNotFound,
onClearCustomerData,
}) => {
const { mutate: searchUser, isPending: isSearching } = useSearchUserByPhone()
const [searchState, setSearchState] = useState<CustomerSearchState>('idle')
@@ -32,8 +41,7 @@ const CreateOrderCustomerSection: FC<CreateOrderCustomerSectionProps> = ({ 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<CreateOrderCustomerSectionProps> = ({ 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<CreateOrderCustomerSectionProps> = ({ formi
formik.handleChange(e)
if (searchState !== 'idle') {
setSearchState('idle')
onClearCustomerData()
}
}
@@ -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<CreateOrderFormValues>
@@ -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<HTMLInputElement | HTMLTextAreaElement>) => void
}
const CreateOrderDeliverySection: FC<CreateOrderDeliverySectionProps> = ({
@@ -23,6 +30,11 @@ const CreateOrderDeliverySection: FC<CreateOrderDeliverySectionProps> = ({
isCourierDelivery,
isCarDelivery,
isDineIn,
customerAddresses,
selectedAddressId,
onSelectSavedAddress,
onSelectManualAddress,
onAddressFieldChange,
}) => {
const { t } = useTranslation('global')
@@ -32,8 +44,11 @@ const CreateOrderDeliverySection: FC<CreateOrderDeliverySectionProps> = ({
})
}
const isAddressReadOnly =
selectedAddressId !== null && customerAddresses.length > 0
return (
<>
<div className='flex flex-col gap-8'>
<div className='w-full bg-white rounded-4xl p-8'>
<div className='text-lg font-light'>تحویل و پرداخت</div>
@@ -108,46 +123,59 @@ const CreateOrderDeliverySection: FC<CreateOrderDeliverySectionProps> = ({
</div>
{isCourierDelivery && (
<div className='w-full bg-white rounded-4xl p-8 mt-8'>
<div className='w-full bg-white rounded-4xl p-8'>
<div className='text-lg font-light'>آدرس تحویل</div>
<CreateOrderAddressSelector
addresses={customerAddresses}
selectedAddressId={selectedAddressId}
onSelectAddress={onSelectSavedAddress}
onSelectManual={onSelectManualAddress}
/>
<div className='grid grid-cols-1 sm:grid-cols-2 gap-4 mt-6'>
<Input
label='نام گیرنده'
name='userAddress.fullName'
value={formik.values.userAddress.fullName}
onChange={formik.handleChange}
onChange={onAddressFieldChange}
readOnly={isAddressReadOnly}
/>
<Input
label='شماره تماس گیرنده'
name='userAddress.phone'
value={formik.values.userAddress.phone}
onChange={formik.handleChange}
onChange={onAddressFieldChange}
readOnly={isAddressReadOnly}
/>
<Input
label='استان'
name='userAddress.province'
value={formik.values.userAddress.province}
onChange={formik.handleChange}
onChange={onAddressFieldChange}
readOnly={isAddressReadOnly}
/>
<Input
label='شهر'
name='userAddress.city'
value={formik.values.userAddress.city}
onChange={formik.handleChange}
onChange={onAddressFieldChange}
readOnly={isAddressReadOnly}
/>
<Input
label='کد پستی'
name='userAddress.postalCode'
value={formik.values.userAddress.postalCode}
onChange={formik.handleChange}
onChange={onAddressFieldChange}
readOnly={isAddressReadOnly}
/>
<Input
label='عرض جغرافیایی'
type='number'
name='userAddress.latitude'
value={formik.values.userAddress.latitude || ''}
onChange={formik.handleChange}
onChange={onAddressFieldChange}
readOnly={isAddressReadOnly}
isNotRequired
/>
<Input
@@ -155,7 +183,8 @@ const CreateOrderDeliverySection: FC<CreateOrderDeliverySectionProps> = ({
type='number'
name='userAddress.longitude'
value={formik.values.userAddress.longitude || ''}
onChange={formik.handleChange}
onChange={onAddressFieldChange}
readOnly={isAddressReadOnly}
isNotRequired
/>
</div>
@@ -165,14 +194,15 @@ const CreateOrderDeliverySection: FC<CreateOrderDeliverySectionProps> = ({
label='آدرس کامل'
name='userAddress.address'
value={formik.values.userAddress.address}
onChange={formik.handleChange}
onChange={onAddressFieldChange}
readOnly={isAddressReadOnly}
/>
</div>
</div>
)}
{isCarDelivery && (
<div className='w-full bg-white rounded-4xl p-8 mt-8'>
<div className='w-full bg-white rounded-4xl p-8'>
<div className='text-lg font-light'>اطلاعات خودرو</div>
<div className='grid grid-cols-1 sm:grid-cols-2 gap-4 mt-6'>
@@ -204,7 +234,7 @@ const CreateOrderDeliverySection: FC<CreateOrderDeliverySectionProps> = ({
</div>
)}
<div className='w-full bg-white rounded-4xl p-8 mt-8'>
<div className='w-full bg-white rounded-4xl p-8'>
<div className='text-lg font-light'>توضیحات سفارش</div>
<div className='mt-6'>
<Textarea
@@ -216,7 +246,7 @@ const CreateOrderDeliverySection: FC<CreateOrderDeliverySectionProps> = ({
/>
</div>
</div>
</>
</div>
)
}
+117 -134
View File
@@ -1,148 +1,131 @@
import { type FC } from 'react'
import { ShoppingCart, User } from 'iconsax-react'
import type { FormikProps } from 'formik'
import { formatFaNumber, formatPrice } from '@/helpers/func'
import { PaymentMethodEnum } from '@/pages/paymentMethods/enum/Enum'
import { DeliveryMethodEnum } from '@/pages/shipmentMethod/enum/Enum'
import type { CreateOrderFormValues } from '../hooks/useCreateOrderForm'
import type { RestaurantPaymentMethod, RestaurantShipmentMethod } from '../hooks/useCreateOrderForm'
import { formatFaNumber, formatPrice } from "@/helpers/func";
import { PaymentMethodEnum } from "@/pages/paymentMethods/enum/Enum";
import { DeliveryMethodEnum } from "@/pages/shipmentMethod/enum/Enum";
import type { FormikProps } from "formik";
import { ShoppingCart, User } from "iconsax-react";
import { type FC } from "react";
import type { CreateOrderFormValues, RestaurantPaymentMethod, RestaurantShipmentMethod } from "../hooks/useCreateOrderForm";
interface OrderSummaryData {
lineItems: Array<{
foodId: string
title: string
image: string
quantity: number
unitPrice: number
totalPrice: number
} | null>
subTotal: number
deliveryFee: number
total: number
totalItems: number
lineItems: Array<{
foodId: string;
title: string;
image: string;
quantity: number;
unitPrice: number;
totalPrice: number;
} | null>;
subTotal: number;
deliveryFee: number;
total: number;
totalItems: number;
}
interface CreateOrderSidebarProps {
formik: FormikProps<CreateOrderFormValues>
orderSummary: OrderSummaryData
selectedDeliveryMethod?: RestaurantShipmentMethod
selectedPaymentMethod?: RestaurantPaymentMethod
formik: FormikProps<CreateOrderFormValues>;
orderSummary: OrderSummaryData;
selectedDeliveryMethod?: RestaurantShipmentMethod;
selectedPaymentMethod?: RestaurantPaymentMethod;
}
const getDeliveryMethodText = (method?: string): string => {
const methodMap: Record<string, string> = {
[DeliveryMethodEnum.DineIn]: 'سرو در رستوران',
[DeliveryMethodEnum.CustomerPickup]: 'دریافت از محل',
[DeliveryMethodEnum.DeliveryCar]: 'تحویل به خودرو',
[DeliveryMethodEnum.DeliveryCourier]: 'بیرون‌بر',
}
return method ? methodMap[method] || method : '-'
}
const methodMap: Record<string, string> = {
[DeliveryMethodEnum.DineIn]: "سرو در رستوران",
[DeliveryMethodEnum.CustomerPickup]: "دریافت از محل",
[DeliveryMethodEnum.DeliveryCar]: "تحویل به خودرو",
[DeliveryMethodEnum.DeliveryCourier]: "بیرون‌بر",
};
return method ? methodMap[method] || method : "-";
};
const getPaymentMethodText = (method?: string): string => {
const methodMap: Record<string, string> = {
[PaymentMethodEnum.Cash]: 'پرداخت نقدی',
[PaymentMethodEnum.Online]: 'پرداخت آنلاین',
[PaymentMethodEnum.CreditCard]: 'کارت به کارت',
[PaymentMethodEnum.Wallet]: 'کیف پول',
}
return method ? methodMap[method] || method : '-'
}
const methodMap: Record<string, string> = {
[PaymentMethodEnum.Cash]: "پرداخت نقدی",
[PaymentMethodEnum.Online]: "پرداخت آنلاین",
[PaymentMethodEnum.CreditCard]: "کارت به کارت",
[PaymentMethodEnum.Wallet]: "کیف پول",
};
return method ? methodMap[method] || method : "-";
};
const CreateOrderSidebar: FC<CreateOrderSidebarProps> = ({
formik,
orderSummary,
selectedDeliveryMethod,
selectedPaymentMethod,
}) => {
const customerName = [formik.values.firstName, formik.values.lastName]
.filter(Boolean)
.join(' ')
const CreateOrderSidebar: FC<CreateOrderSidebarProps> = ({ formik, orderSummary, selectedDeliveryMethod, selectedPaymentMethod }) => {
const customerName = [formik.values.firstName, formik.values.lastName].filter(Boolean).join(" ");
return (
<div className='space-y-6'>
<div className='bg-white rounded-4xl p-8'>
<div className='flex items-center gap-2 text-lg font-light'>
<User size={20} color='#000' />
خلاصه سفارش
</div>
<div className='mt-6 space-y-3 text-[13px] font-light'>
<div className='flex justify-between items-center border-b border-border border-dashed pb-3'>
<span className='text-description'>مشتری</span>
<span>{customerName || '-'}</span>
</div>
<div className='flex justify-between items-center border-b border-border border-dashed pb-3'>
<span className='text-description'>شماره تماس</span>
<span>{formik.values.userPhone || '-'}</span>
</div>
<div className='flex justify-between items-center border-b border-border border-dashed pb-3'>
<span className='text-description'>روش تحویل</span>
<span>{getDeliveryMethodText(selectedDeliveryMethod?.method)}</span>
</div>
<div className='flex justify-between items-center border-b border-border border-dashed pb-3'>
<span className='text-description'>روش پرداخت</span>
<span>{getPaymentMethodText(selectedPaymentMethod?.method)}</span>
</div>
</div>
</div>
<div className='bg-white rounded-4xl p-8'>
<div className='flex items-center gap-2 text-lg font-light'>
<ShoppingCart size={20} color='#000' />
آیتمها
{orderSummary.totalItems > 0 && (
<span className='text-xs text-description'>
({formatFaNumber(orderSummary.totalItems)})
</span>
)}
</div>
{orderSummary.lineItems.length === 0 ? (
<div className='mt-6 text-center text-xs text-description py-6'>
سبد خالی است
</div>
) : (
<div className='mt-6 space-y-3'>
{orderSummary.lineItems.map((item) => {
if (!item) return null
return (
<div
key={item.foodId}
className='flex justify-between items-start gap-3 text-[13px] border-b border-border border-dashed pb-3'
>
<div className='flex-1'>
<div>{item.title}</div>
<div className='text-description mt-1'>
{formatFaNumber(item.quantity)} × {formatPrice(item.unitPrice)}
</div>
</div>
<div>{formatPrice(item.totalPrice)}</div>
</div>
)
})}
</div>
)}
<div className='mt-6 space-y-3 text-[13px] font-light'>
<div className='flex justify-between items-center'>
<span className='text-description'>جمع آیتمها</span>
<span>{formatPrice(orderSummary.subTotal)}</span>
</div>
{orderSummary.deliveryFee > 0 && (
<div className='flex justify-between items-center'>
<span className='text-description'>هزینه ارسال</span>
<span>{formatPrice(orderSummary.deliveryFee)}</span>
</div>
)}
<div className='flex justify-between items-center font-bold text-base pt-3 border-t-2 border-gray-200'>
<span>جمع کل (تقریبی)</span>
<span>{formatPrice(orderSummary.total)}</span>
</div>
</div>
</div>
return (
<div className="space-y-6">
<div className="bg-white rounded-4xl p-8">
<div className="flex items-center gap-2 text-lg font-light">
<User size={20} color="#000" />
خلاصه سفارش
</div>
)
}
export default CreateOrderSidebar
<div className="mt-6 space-y-3 text-[13px] font-light">
<div className="flex justify-between items-center border-b border-border border-dashed pb-3">
<span className="text-description">مشتری</span>
<span>{customerName || "-"}</span>
</div>
<div className="flex justify-between items-center border-b border-border border-dashed pb-3">
<span className="text-description">شماره تماس</span>
<span>{formik.values.userPhone || "-"}</span>
</div>
<div className="flex justify-between items-center border-b border-border border-dashed pb-3">
<span className="text-description">روش تحویل</span>
<span>{getDeliveryMethodText(selectedDeliveryMethod?.method)}</span>
</div>
<div className="flex justify-between items-center border-b border-border border-dashed pb-3">
<span className="text-description">روش پرداخت</span>
<span>{getPaymentMethodText(selectedPaymentMethod?.method)}</span>
</div>
</div>
</div>
<div className="bg-white rounded-4xl p-8">
<div className="flex items-center gap-2 text-lg font-light">
<ShoppingCart size={20} color="#000" />
آیتمها
{orderSummary.totalItems > 0 && <span className="text-xs text-description">({formatFaNumber(orderSummary.totalItems)})</span>}
</div>
{orderSummary.lineItems.length === 0 ? (
<div className="mt-6 text-center text-xs text-description py-6">سبد خالی است</div>
) : (
<div className="mt-6 space-y-3">
{orderSummary.lineItems.map((item) => {
if (!item) return null;
return (
<div key={item.foodId} className="flex justify-between items-start gap-3 text-[13px] border-b border-border border-dashed pb-3">
<div className="flex-1">
<div>{item.title}</div>
<div className="text-description mt-1">
{formatFaNumber(item.quantity)} × {formatPrice(item.unitPrice)}
</div>
</div>
<div>{formatPrice(item.totalPrice)}</div>
</div>
);
})}
</div>
)}
<div className="mt-6 space-y-3 text-[13px] font-light">
<div className="flex justify-between items-center">
<span className="text-description">جمع آیتمها</span>
<span>{formatPrice(orderSummary.subTotal)}</span>
</div>
{orderSummary.deliveryFee > 0 && (
<div className="flex justify-between items-center">
<span className="text-description">هزینه ارسال</span>
<span>{formatPrice(orderSummary.deliveryFee)}</span>
</div>
)}
<div className="flex justify-between items-center font-bold text-sm pt-3 border-t-2 border-gray-200">
<span>جمع کل</span>
<span>{formatPrice(orderSummary.total)}</span>
</div>
</div>
</div>
</div>
);
};
export default CreateOrderSidebar;
+99 -1
View File
@@ -1,4 +1,4 @@
import { useEffect, useMemo } from 'react'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { useFormik } from 'formik'
import * as Yup from 'yup'
import { toast } from 'react-toastify'
@@ -18,6 +18,8 @@ import type {
import type { Food } from '@/pages/food/types/Types'
import type { RestaurantShipmentMethod } from '@/pages/shipmentMethod/types/Types'
import type { RestaurantPaymentMethod } from '@/pages/paymentMethods/types/Types'
import type { UserSavedAddress, UserWithAddresses } from '@/pages/customers/types/Types'
import { mapSavedAddressToOrderAddress } from '../utils/mapSavedAddress'
import { Pages } from '@/config/Pages'
import type { ErrorType } from '@/helpers/types'
import { extractErrorMessage } from '@/config/func'
@@ -62,6 +64,8 @@ export const getFoodUnitPrice = (food: Food) => {
export const useCreateOrderForm = () => {
const navigate = useNavigate()
const { mutate: createOrder, isPending: isCreating } = useCreateOrder()
const [customerAddresses, setCustomerAddresses] = useState<UserSavedAddress[]>([])
const [selectedAddressId, setSelectedAddressId] = useState<string | null>(null)
const { data: foodsData } = useGetFoods({ limit: 500 })
const { data: deliveryMethodsData } = useGetRestaurantShipmentMethods({ limit: 100 })
@@ -194,6 +198,72 @@ export const useCreateOrderForm = () => {
},
})
const getCustomerFullName = useCallback(
(firstName?: string, lastName?: string) =>
[firstName ?? formik.values.firstName, lastName ?? formik.values.lastName]
.filter(Boolean)
.join(' '),
// eslint-disable-next-line react-hooks/exhaustive-deps
[formik.values.firstName, formik.values.lastName]
)
const applySavedAddress = useCallback(
(address: UserSavedAddress, fullName?: string) => {
formik.setFieldValue(
'userAddress',
mapSavedAddressToOrderAddress(address, fullName ?? getCustomerFullName())
)
setSelectedAddressId(address.id)
},
[formik, getCustomerFullName]
)
const selectManualAddress = useCallback(() => {
setSelectedAddressId(null)
formik.setFieldValue('userAddress', {
...emptyUserAddress(),
fullName: getCustomerFullName(),
phone: formik.values.userPhone,
})
}, [formik, getCustomerFullName])
const handleUserFound = useCallback(
(user: UserWithAddresses) => {
const addresses = user.addresses ?? []
setCustomerAddresses(addresses)
if (addresses.length === 0) {
setSelectedAddressId(null)
formik.setFieldValue('userAddress', {
...emptyUserAddress(),
fullName: getCustomerFullName(user.firstName, user.lastName),
phone: user.phone || formik.values.userPhone,
})
return
}
const defaultAddress =
addresses.find((address) => address.isDefault) ?? addresses[0]
applySavedAddress(
defaultAddress,
[user.firstName, user.lastName].filter(Boolean).join(' ')
)
},
[applySavedAddress, formik, getCustomerFullName]
)
const clearCustomerData = useCallback(() => {
setCustomerAddresses([])
setSelectedAddressId(null)
formik.setFieldValue('userAddress', emptyUserAddress())
}, [formik])
const handleUserNotFound = useCallback(() => {
clearCustomerData()
formik.setFieldValue('firstName', '')
formik.setFieldValue('lastName', '')
}, [clearCustomerData, formik])
const cashPaymentMethod = paymentMethods[0]
useEffect(() => {
@@ -215,6 +285,16 @@ export const useCreateOrderForm = () => {
selectedDeliveryMethod?.method === DeliveryMethodEnum.DeliveryCar
const isDineIn = selectedDeliveryMethod?.method === DeliveryMethodEnum.DineIn
useEffect(() => {
if (!isCourierDelivery || customerAddresses.length === 0 || selectedAddressId) {
return
}
const defaultAddress =
customerAddresses.find((address) => address.isDefault) ?? customerAddresses[0]
applySavedAddress(defaultAddress)
}, [isCourierDelivery, customerAddresses, selectedAddressId, applySavedAddress])
const foodsMap = useMemo(
() => new Map(foods.map((food) => [food.id, food])),
[foods]
@@ -280,6 +360,16 @@ export const useCreateOrderForm = () => {
)
}
const handleAddressFieldChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
if (e.target.name?.startsWith('userAddress.')) {
setSelectedAddressId(null)
}
formik.handleChange(e)
},
[formik]
)
const removeItem = (foodId: string) => {
formik.setFieldValue(
'items',
@@ -302,6 +392,14 @@ export const useCreateOrderForm = () => {
updateItemQuantity,
removeItem,
isSubmitting: isCreating,
customerAddresses,
selectedAddressId,
handleUserFound,
handleUserNotFound,
clearCustomerData,
applySavedAddress,
selectManualAddress,
handleAddressFieldChange,
}
}
+16
View File
@@ -0,0 +1,16 @@
import type { UserSavedAddress } from '@/pages/customers/types/Types'
import type { CreateOrderUserAddress } from '../types/Types'
export const mapSavedAddressToOrderAddress = (
saved: UserSavedAddress,
fullName: string
): CreateOrderUserAddress => ({
fullName: fullName || saved.title,
phone: saved.phone,
city: saved.city,
province: saved.province,
address: saved.address,
postalCode: saved.postalCode,
latitude: saved.latitude,
longitude: saved.longitude,
})