Create order
This commit is contained in:
@@ -84,9 +84,14 @@ const Input: FC<Props> = (props: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full'>
|
<div className='w-full'>
|
||||||
<label className='text-sm'>
|
{props.label && (
|
||||||
{props.label}
|
<div className='flex items-center gap-1'>
|
||||||
</label>
|
<label className='text-sm'>{props.label}</label>
|
||||||
|
{props.isNotRequired && (
|
||||||
|
<span className='text-xs text-description'>(اختیاری)</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className='w-full relative mt-1'>
|
<div className='w-full relative mt-1'>
|
||||||
<input {...props} onChange={(e) => {
|
<input {...props} onChange={(e) => {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { clx } from '../helpers/utils'
|
|||||||
type Props = {
|
type Props = {
|
||||||
label?: string,
|
label?: string,
|
||||||
error_text?: string
|
error_text?: string
|
||||||
|
isNotRequired?: boolean
|
||||||
} & InputHTMLAttributes<HTMLTextAreaElement>
|
} & InputHTMLAttributes<HTMLTextAreaElement>
|
||||||
|
|
||||||
const Textarea: FC<Props> = (props: Props) => {
|
const Textarea: FC<Props> = (props: Props) => {
|
||||||
@@ -12,8 +13,11 @@ const Textarea: FC<Props> = (props: Props) => {
|
|||||||
<div className='w-full relative'>
|
<div className='w-full relative'>
|
||||||
{
|
{
|
||||||
props.label &&
|
props.label &&
|
||||||
<div className='text-sm' >
|
<div className='flex items-center gap-1'>
|
||||||
{props.label}
|
<div className='text-sm'>{props.label}</div>
|
||||||
|
{props.isNotRequired && (
|
||||||
|
<span className='text-xs text-description'>(اختیاری)</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as api from "../service/UsersService";
|
import * as api from "../service/UsersService";
|
||||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import type { GetUsersParams } from "../types/Types";
|
import type { CreateUserAddressType, GetUsersParams } from "../types/Types";
|
||||||
|
|
||||||
export const useGetUsers = (params?: GetUsersParams) => {
|
export const useGetUsers = (params?: GetUsersParams) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
@@ -26,6 +26,16 @@ export const useCreateUser = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useCreateUserAddress = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: ({
|
||||||
|
userId,
|
||||||
|
...params
|
||||||
|
}: CreateUserAddressType & { userId: string }) =>
|
||||||
|
api.createUserAddress(userId, params),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const useImportUsersFromExcel = () => {
|
export const useImportUsersFromExcel = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import axios from "@/config/axios";
|
import axios from "@/config/axios";
|
||||||
import type {
|
import type {
|
||||||
|
CreateUserAddressResponse,
|
||||||
|
CreateUserAddressType,
|
||||||
CreateUserResponse,
|
CreateUserResponse,
|
||||||
CreateUserType,
|
CreateUserType,
|
||||||
GetCustomersResponse,
|
GetCustomersResponse,
|
||||||
@@ -28,6 +30,17 @@ export const createUser = async (params: CreateUserType) => {
|
|||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const createUserAddress = async (
|
||||||
|
userId: string,
|
||||||
|
params: CreateUserAddressType,
|
||||||
|
) => {
|
||||||
|
const { data } = await axios.post<CreateUserAddressResponse>(
|
||||||
|
`/admin/users/${userId}/addresses`,
|
||||||
|
params,
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
export const importUsersFromExcel = async (file: File) => {
|
export const importUsersFromExcel = async (file: File) => {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("file", file);
|
formData.append("file", file);
|
||||||
|
|||||||
@@ -103,4 +103,25 @@ export type CreateUserType = {
|
|||||||
lastName: string;
|
lastName: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CreateUserResponse = IResponse<User>;
|
export type CreateUserResult = {
|
||||||
|
id: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
user: User;
|
||||||
|
orderCount: number;
|
||||||
|
totalOrderAmount: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CreateUserResponse = IResponse<CreateUserResult>;
|
||||||
|
|
||||||
|
export type CreateUserAddressType = {
|
||||||
|
title: string;
|
||||||
|
address: string;
|
||||||
|
postalCode?: string;
|
||||||
|
latitude: number;
|
||||||
|
longitude: number;
|
||||||
|
phone: string;
|
||||||
|
isDefault: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CreateUserAddressResponse = IResponse<UserSavedAddress>;
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ const CreateOrder: FC = () => {
|
|||||||
applySavedAddress,
|
applySavedAddress,
|
||||||
selectManualAddress,
|
selectManualAddress,
|
||||||
handleAddressFieldChange,
|
handleAddressFieldChange,
|
||||||
|
handleLocationSelect,
|
||||||
} = useCreateOrderForm()
|
} = useCreateOrderForm()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -78,6 +79,7 @@ const CreateOrder: FC = () => {
|
|||||||
onSelectSavedAddress={applySavedAddress}
|
onSelectSavedAddress={applySavedAddress}
|
||||||
onSelectManualAddress={selectManualAddress}
|
onSelectManualAddress={selectManualAddress}
|
||||||
onAddressFieldChange={handleAddressFieldChange}
|
onAddressFieldChange={handleAddressFieldChange}
|
||||||
|
onLocationSelect={handleLocationSelect}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ const CreateOrderAddressSelector: FC<CreateOrderAddressSelectorProps> = ({
|
|||||||
<div className='grid grid-cols-1 sm:grid-cols-2 gap-3'>
|
<div className='grid grid-cols-1 sm:grid-cols-2 gap-3'>
|
||||||
{addresses.map((address) => {
|
{addresses.map((address) => {
|
||||||
const isSelected = selectedAddressId === address.id
|
const isSelected = selectedAddressId === address.id
|
||||||
const location = [address.province, address.city].filter(Boolean).join('، ')
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
@@ -48,9 +47,6 @@ const CreateOrderAddressSelector: FC<CreateOrderAddressSelectorProps> = ({
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{location && (
|
|
||||||
<div className='text-xs text-description mt-1'>{location}</div>
|
|
||||||
)}
|
|
||||||
<div className='text-xs mt-2 line-clamp-2'>{address.address}</div>
|
<div className='text-xs mt-2 line-clamp-2'>{address.address}</div>
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import { type FC } from 'react'
|
import { type FC, useState } from 'react'
|
||||||
import type { FormikProps } from 'formik'
|
import type { FormikProps } from 'formik'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Input from '@/components/Input'
|
import Input from '@/components/Input'
|
||||||
import Textarea from '@/components/Textarea'
|
import Textarea from '@/components/Textarea'
|
||||||
|
import DefaulModal from '@/components/DefaulModal'
|
||||||
|
import LocationInput from '@/pages/settings/components/LocationInput'
|
||||||
|
import LocationPicker from '@/pages/settings/components/LocationPicker'
|
||||||
import { formatPrice } from '@/helpers/func'
|
import { formatPrice } from '@/helpers/func'
|
||||||
import type { CreateOrderFormValues } from '../hooks/useCreateOrderForm'
|
import type { CreateOrderFormValues } from '../hooks/useCreateOrderForm'
|
||||||
import type { RestaurantPaymentMethod, RestaurantShipmentMethod } from '../hooks/useCreateOrderForm'
|
import type { RestaurantPaymentMethod, RestaurantShipmentMethod } from '../hooks/useCreateOrderForm'
|
||||||
@@ -21,6 +24,7 @@ interface CreateOrderDeliverySectionProps {
|
|||||||
onSelectSavedAddress: (address: UserSavedAddress) => void
|
onSelectSavedAddress: (address: UserSavedAddress) => void
|
||||||
onSelectManualAddress: () => void
|
onSelectManualAddress: () => void
|
||||||
onAddressFieldChange: (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void
|
onAddressFieldChange: (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void
|
||||||
|
onLocationSelect: (lat: number, lng: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const CreateOrderDeliverySection: FC<CreateOrderDeliverySectionProps> = ({
|
const CreateOrderDeliverySection: FC<CreateOrderDeliverySectionProps> = ({
|
||||||
@@ -35,8 +39,10 @@ const CreateOrderDeliverySection: FC<CreateOrderDeliverySectionProps> = ({
|
|||||||
onSelectSavedAddress,
|
onSelectSavedAddress,
|
||||||
onSelectManualAddress,
|
onSelectManualAddress,
|
||||||
onAddressFieldChange,
|
onAddressFieldChange,
|
||||||
|
onLocationSelect,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation('global')
|
const { t } = useTranslation('global')
|
||||||
|
const [isLocationModalOpen, setIsLocationModalOpen] = useState(false)
|
||||||
|
|
||||||
const getDeliveryLabel = (method: string) => {
|
const getDeliveryLabel = (method: string) => {
|
||||||
return t(`shipment.${method}`, {
|
return t(`shipment.${method}`, {
|
||||||
@@ -47,6 +53,14 @@ const CreateOrderDeliverySection: FC<CreateOrderDeliverySectionProps> = ({
|
|||||||
const isAddressReadOnly =
|
const isAddressReadOnly =
|
||||||
selectedAddressId !== null && customerAddresses.length > 0
|
selectedAddressId !== null && customerAddresses.length > 0
|
||||||
|
|
||||||
|
const handleMapLocationSelect = (lat: number, lng: number) => {
|
||||||
|
onLocationSelect(lat, lng)
|
||||||
|
setIsLocationModalOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const latitude = formik.values.userAddress.latitude
|
||||||
|
const longitude = formik.values.userAddress.longitude
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col gap-8'>
|
<div className='flex flex-col gap-8'>
|
||||||
<div className='w-full bg-white rounded-4xl p-8'>
|
<div className='w-full bg-white rounded-4xl p-8'>
|
||||||
@@ -109,7 +123,7 @@ const CreateOrderDeliverySection: FC<CreateOrderDeliverySectionProps> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{(isDineIn || isCarDelivery) && (
|
{isDineIn && (
|
||||||
<div className='mt-8'>
|
<div className='mt-8'>
|
||||||
<Input
|
<Input
|
||||||
label='شماره میز'
|
label='شماره میز'
|
||||||
@@ -135,57 +149,36 @@ const CreateOrderDeliverySection: FC<CreateOrderDeliverySectionProps> = ({
|
|||||||
|
|
||||||
<div className='grid grid-cols-1 sm:grid-cols-2 gap-4 mt-6'>
|
<div className='grid grid-cols-1 sm:grid-cols-2 gap-4 mt-6'>
|
||||||
<Input
|
<Input
|
||||||
label='نام گیرنده'
|
label='عنوان آدرس'
|
||||||
name='userAddress.fullName'
|
name='userAddress.title'
|
||||||
value={formik.values.userAddress.fullName}
|
placeholder='مثال: منزل'
|
||||||
|
value={formik.values.userAddress.title}
|
||||||
onChange={onAddressFieldChange}
|
onChange={onAddressFieldChange}
|
||||||
readOnly={isAddressReadOnly}
|
readOnly={isAddressReadOnly}
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
label='شماره تماس گیرنده'
|
label='شماره تماس'
|
||||||
name='userAddress.phone'
|
name='userAddress.phone'
|
||||||
value={formik.values.userAddress.phone}
|
value={formik.values.userAddress.phone}
|
||||||
onChange={onAddressFieldChange}
|
onChange={onAddressFieldChange}
|
||||||
readOnly={isAddressReadOnly}
|
readOnly={isAddressReadOnly}
|
||||||
/>
|
/>
|
||||||
<Input
|
|
||||||
label='استان'
|
|
||||||
name='userAddress.province'
|
|
||||||
value={formik.values.userAddress.province}
|
|
||||||
onChange={onAddressFieldChange}
|
|
||||||
readOnly={isAddressReadOnly}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
label='شهر'
|
|
||||||
name='userAddress.city'
|
|
||||||
value={formik.values.userAddress.city}
|
|
||||||
onChange={onAddressFieldChange}
|
|
||||||
readOnly={isAddressReadOnly}
|
|
||||||
/>
|
|
||||||
<Input
|
<Input
|
||||||
label='کد پستی'
|
label='کد پستی'
|
||||||
name='userAddress.postalCode'
|
name='userAddress.postalCode'
|
||||||
value={formik.values.userAddress.postalCode}
|
value={formik.values.userAddress.postalCode}
|
||||||
onChange={onAddressFieldChange}
|
onChange={onAddressFieldChange}
|
||||||
readOnly={isAddressReadOnly}
|
readOnly={isAddressReadOnly}
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
label='عرض جغرافیایی'
|
|
||||||
type='number'
|
|
||||||
name='userAddress.latitude'
|
|
||||||
value={formik.values.userAddress.latitude || ''}
|
|
||||||
onChange={onAddressFieldChange}
|
|
||||||
readOnly={isAddressReadOnly}
|
|
||||||
isNotRequired
|
isNotRequired
|
||||||
/>
|
/>
|
||||||
<Input
|
</div>
|
||||||
label='طول جغرافیایی'
|
|
||||||
type='number'
|
<div className={isAddressReadOnly ? 'pointer-events-none opacity-60' : ''}>
|
||||||
name='userAddress.longitude'
|
<LocationInput
|
||||||
value={formik.values.userAddress.longitude || ''}
|
label='موقعیت روی نقشه'
|
||||||
onChange={onAddressFieldChange}
|
latitude={latitude || undefined}
|
||||||
readOnly={isAddressReadOnly}
|
longitude={longitude || undefined}
|
||||||
isNotRequired
|
onClick={() => setIsLocationModalOpen(true)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -198,6 +191,28 @@ const CreateOrderDeliverySection: FC<CreateOrderDeliverySectionProps> = ({
|
|||||||
readOnly={isAddressReadOnly}
|
readOnly={isAddressReadOnly}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<DefaulModal
|
||||||
|
open={isLocationModalOpen}
|
||||||
|
close={() => setIsLocationModalOpen(false)}
|
||||||
|
isHeader={true}
|
||||||
|
title_header='انتخاب موقعیت روی نقشه'
|
||||||
|
width={800}
|
||||||
|
>
|
||||||
|
<div className='p-4'>
|
||||||
|
<LocationPicker
|
||||||
|
initialPosition={
|
||||||
|
latitude && longitude
|
||||||
|
? [latitude, longitude]
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onLocationSelect={handleMapLocationSelect}
|
||||||
|
/>
|
||||||
|
<div className='mt-4 text-xs text-gray-600 text-center'>
|
||||||
|
روی نقشه کلیک کنید تا موقعیت را انتخاب کنید
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DefaulModal>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -243,6 +258,7 @@ const CreateOrderDeliverySection: FC<CreateOrderDeliverySectionProps> = ({
|
|||||||
placeholder='توضیحات اضافی برای سفارش...'
|
placeholder='توضیحات اضافی برای سفارش...'
|
||||||
value={formik.values.description}
|
value={formik.values.description}
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
|
isNotRequired
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import * as Yup from 'yup'
|
|||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { useCreateOrder } from './useOrderData'
|
import { useCreateOrder } from './useOrderData'
|
||||||
import { useCreateUser } from '@/pages/customers/hooks/useUsersData'
|
import { useCreateUser, useCreateUserAddress } from '@/pages/customers/hooks/useUsersData'
|
||||||
import { useGetFoods } from '@/pages/food/hooks/useFoodData'
|
import { useGetFoods } from '@/pages/food/hooks/useFoodData'
|
||||||
import { useGetRestaurantShipmentMethods } from '@/pages/shipmentMethod/hooks/useShipmentMethodData'
|
import { useGetRestaurantShipmentMethods } from '@/pages/shipmentMethod/hooks/useShipmentMethodData'
|
||||||
import { useGetRestaurantPaymentMethods } from '@/pages/paymentMethods/hooks/usePaymentMethodData'
|
import { useGetRestaurantPaymentMethods } from '@/pages/paymentMethods/hooks/usePaymentMethodData'
|
||||||
@@ -21,6 +21,7 @@ import type { RestaurantShipmentMethod } from '@/pages/shipmentMethod/types/Type
|
|||||||
import type { RestaurantPaymentMethod } from '@/pages/paymentMethods/types/Types'
|
import type { RestaurantPaymentMethod } from '@/pages/paymentMethods/types/Types'
|
||||||
import type { UserSavedAddress, UserWithAddresses } from '@/pages/customers/types/Types'
|
import type { UserSavedAddress, UserWithAddresses } from '@/pages/customers/types/Types'
|
||||||
import { mapSavedAddressToOrderAddress } from '../utils/mapSavedAddress'
|
import { mapSavedAddressToOrderAddress } from '../utils/mapSavedAddress'
|
||||||
|
import { mapFormAddressToCreateUserAddress } from '../utils/mapFormAddressToCreateUserAddress'
|
||||||
import { Pages } from '@/config/Pages'
|
import { Pages } from '@/config/Pages'
|
||||||
import type { ErrorType } from '@/helpers/types'
|
import type { ErrorType } from '@/helpers/types'
|
||||||
import { extractErrorMessage } from '@/config/func'
|
import { extractErrorMessage } from '@/config/func'
|
||||||
@@ -40,10 +41,8 @@ export interface CreateOrderFormValues {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const emptyUserAddress = (): CreateOrderUserAddress => ({
|
const emptyUserAddress = (): CreateOrderUserAddress => ({
|
||||||
fullName: '',
|
title: '',
|
||||||
phone: '',
|
phone: '',
|
||||||
city: '',
|
|
||||||
province: '',
|
|
||||||
address: '',
|
address: '',
|
||||||
postalCode: '',
|
postalCode: '',
|
||||||
latitude: 0,
|
latitude: 0,
|
||||||
@@ -66,7 +65,10 @@ export const useCreateOrderForm = () => {
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { mutate: createOrder, isPending: isCreating } = useCreateOrder()
|
const { mutate: createOrder, isPending: isCreating } = useCreateOrder()
|
||||||
const { mutateAsync: createUser, isPending: isCreatingUser } = useCreateUser()
|
const { mutateAsync: createUser, isPending: isCreatingUser } = useCreateUser()
|
||||||
|
const { mutateAsync: createUserAddress, isPending: isCreatingAddress } =
|
||||||
|
useCreateUserAddress()
|
||||||
const [customerAddresses, setCustomerAddresses] = useState<UserSavedAddress[]>([])
|
const [customerAddresses, setCustomerAddresses] = useState<UserSavedAddress[]>([])
|
||||||
|
const [customerUserId, setCustomerUserId] = useState<string | null>(null)
|
||||||
const [selectedAddressId, setSelectedAddressId] = useState<string | null>(null)
|
const [selectedAddressId, setSelectedAddressId] = useState<string | null>(null)
|
||||||
const [isNewCustomer, setIsNewCustomer] = useState(false)
|
const [isNewCustomer, setIsNewCustomer] = useState(false)
|
||||||
|
|
||||||
@@ -126,16 +128,14 @@ export const useCreateOrderForm = () => {
|
|||||||
|
|
||||||
if (selectedDelivery?.method === DeliveryMethodEnum.DeliveryCourier) {
|
if (selectedDelivery?.method === DeliveryMethodEnum.DeliveryCourier) {
|
||||||
const address = values.userAddress
|
const address = values.userAddress
|
||||||
if (
|
if (!address.title || !address.phone || !address.address) {
|
||||||
!address.fullName ||
|
|
||||||
!address.phone ||
|
|
||||||
!address.city ||
|
|
||||||
!address.province ||
|
|
||||||
!address.address
|
|
||||||
) {
|
|
||||||
toast.error('لطفاً آدرس تحویل را کامل کنید')
|
toast.error('لطفاً آدرس تحویل را کامل کنید')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (!address.latitude || !address.longitude) {
|
||||||
|
toast.error('لطفاً موقعیت روی نقشه را انتخاب کنید')
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedDelivery?.method === DeliveryMethodEnum.DeliveryCar) {
|
if (selectedDelivery?.method === DeliveryMethodEnum.DeliveryCar) {
|
||||||
@@ -146,11 +146,17 @@ export const useCreateOrderForm = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const submitOrder = () => {
|
if (
|
||||||
|
selectedDelivery?.method === DeliveryMethodEnum.DineIn &&
|
||||||
|
!values.tableNumber.trim()
|
||||||
|
) {
|
||||||
|
toast.error('لطفاً شماره میز را وارد کنید')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitOrder = (userId: string, addressId?: string) => {
|
||||||
const payload: CreateOrderType = {
|
const payload: CreateOrderType = {
|
||||||
userPhone: values.userPhone,
|
userId,
|
||||||
firstName: values.firstName,
|
|
||||||
lastName: values.lastName,
|
|
||||||
items: values.items,
|
items: values.items,
|
||||||
deliveryMethodId: values.deliveryMethodId,
|
deliveryMethodId: values.deliveryMethodId,
|
||||||
paymentMethodId: values.paymentMethodId,
|
paymentMethodId: values.paymentMethodId,
|
||||||
@@ -160,16 +166,15 @@ export const useCreateOrderForm = () => {
|
|||||||
payload.description = values.description.trim()
|
payload.description = values.description.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (values.tableNumber.trim()) {
|
if (
|
||||||
|
selectedDelivery?.method === DeliveryMethodEnum.DineIn &&
|
||||||
|
values.tableNumber.trim()
|
||||||
|
) {
|
||||||
payload.tableNumber = values.tableNumber.trim()
|
payload.tableNumber = values.tableNumber.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedDelivery?.method === DeliveryMethodEnum.DeliveryCourier) {
|
if (selectedDelivery?.method === DeliveryMethodEnum.DeliveryCourier && addressId) {
|
||||||
payload.userAddress = {
|
payload.addressId = addressId
|
||||||
...values.userAddress,
|
|
||||||
latitude: Number(values.userAddress.latitude) || 0,
|
|
||||||
longitude: Number(values.userAddress.longitude) || 0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedDelivery?.method === DeliveryMethodEnum.DeliveryCar) {
|
if (selectedDelivery?.method === DeliveryMethodEnum.DeliveryCar) {
|
||||||
@@ -192,20 +197,66 @@ export const useCreateOrderForm = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const registerNewCustomer = async () => {
|
const registerNewCustomer = async (): Promise<string | null> => {
|
||||||
if (!isNewCustomer) {
|
if (!isNewCustomer) {
|
||||||
submitOrder()
|
return customerUserId
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const response = await createUser({
|
||||||
|
phone: values.userPhone.trim(),
|
||||||
|
firstName: values.firstName.trim(),
|
||||||
|
lastName: values.lastName.trim(),
|
||||||
|
})
|
||||||
|
const userId = response.data.user.id
|
||||||
|
setCustomerUserId(userId)
|
||||||
|
setIsNewCustomer(false)
|
||||||
|
return userId
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolveAddressId = async (userId: string): Promise<string | undefined> => {
|
||||||
|
if (selectedDelivery?.method !== DeliveryMethodEnum.DeliveryCourier) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedAddressId) {
|
||||||
|
return selectedAddressId
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await createUserAddress({
|
||||||
|
userId,
|
||||||
|
...mapFormAddressToCreateUserAddress(
|
||||||
|
values.userAddress,
|
||||||
|
customerAddresses.length === 0
|
||||||
|
),
|
||||||
|
})
|
||||||
|
return response.data.id
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitFlow = async () => {
|
||||||
try {
|
try {
|
||||||
await createUser({
|
const userId = await registerNewCustomer()
|
||||||
phone: values.userPhone.trim(),
|
if (!userId) {
|
||||||
firstName: values.firstName.trim(),
|
toast.error('کاربر انتخاب نشده است')
|
||||||
lastName: values.lastName.trim(),
|
return
|
||||||
})
|
}
|
||||||
setIsNewCustomer(false)
|
|
||||||
submitOrder()
|
let addressId: string | undefined
|
||||||
|
try {
|
||||||
|
addressId = await resolveAddressId(userId)
|
||||||
|
} catch (error) {
|
||||||
|
toast.error(extractErrorMessage(error as ErrorType, 'خطا در ثبت آدرس'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
selectedDelivery?.method === DeliveryMethodEnum.DeliveryCourier &&
|
||||||
|
!addressId
|
||||||
|
) {
|
||||||
|
toast.error('لطفاً آدرس تحویل را انتخاب کنید')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
submitOrder(userId, addressId)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(extractErrorMessage(error as ErrorType, 'خطا در ثبت مشتری'))
|
toast.error(extractErrorMessage(error as ErrorType, 'خطا در ثبت مشتری'))
|
||||||
}
|
}
|
||||||
@@ -216,42 +267,30 @@ export const useCreateOrderForm = () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await registerNewCustomer()
|
await submitFlow()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
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(
|
const applySavedAddress = useCallback(
|
||||||
(address: UserSavedAddress, fullName?: string) => {
|
(address: UserSavedAddress) => {
|
||||||
formik.setFieldValue(
|
formik.setFieldValue('userAddress', mapSavedAddressToOrderAddress(address))
|
||||||
'userAddress',
|
|
||||||
mapSavedAddressToOrderAddress(address, fullName ?? getCustomerFullName())
|
|
||||||
)
|
|
||||||
setSelectedAddressId(address.id)
|
setSelectedAddressId(address.id)
|
||||||
},
|
},
|
||||||
[formik, getCustomerFullName]
|
[formik]
|
||||||
)
|
)
|
||||||
|
|
||||||
const selectManualAddress = useCallback(() => {
|
const selectManualAddress = useCallback(() => {
|
||||||
setSelectedAddressId(null)
|
setSelectedAddressId(null)
|
||||||
formik.setFieldValue('userAddress', {
|
formik.setFieldValue('userAddress', {
|
||||||
...emptyUserAddress(),
|
...emptyUserAddress(),
|
||||||
fullName: getCustomerFullName(),
|
|
||||||
phone: formik.values.userPhone,
|
phone: formik.values.userPhone,
|
||||||
})
|
})
|
||||||
}, [formik, getCustomerFullName])
|
}, [formik])
|
||||||
|
|
||||||
const handleUserFound = useCallback(
|
const handleUserFound = useCallback(
|
||||||
(user: UserWithAddresses) => {
|
(user: UserWithAddresses) => {
|
||||||
setIsNewCustomer(false)
|
setIsNewCustomer(false)
|
||||||
|
setCustomerUserId(user.id)
|
||||||
const addresses = user.addresses ?? []
|
const addresses = user.addresses ?? []
|
||||||
setCustomerAddresses(addresses)
|
setCustomerAddresses(addresses)
|
||||||
|
|
||||||
@@ -259,7 +298,6 @@ export const useCreateOrderForm = () => {
|
|||||||
setSelectedAddressId(null)
|
setSelectedAddressId(null)
|
||||||
formik.setFieldValue('userAddress', {
|
formik.setFieldValue('userAddress', {
|
||||||
...emptyUserAddress(),
|
...emptyUserAddress(),
|
||||||
fullName: getCustomerFullName(user.firstName, user.lastName),
|
|
||||||
phone: user.phone || formik.values.userPhone,
|
phone: user.phone || formik.values.userPhone,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
@@ -267,22 +305,21 @@ export const useCreateOrderForm = () => {
|
|||||||
|
|
||||||
const defaultAddress =
|
const defaultAddress =
|
||||||
addresses.find((address) => address.isDefault) ?? addresses[0]
|
addresses.find((address) => address.isDefault) ?? addresses[0]
|
||||||
applySavedAddress(
|
applySavedAddress(defaultAddress)
|
||||||
defaultAddress,
|
|
||||||
[user.firstName, user.lastName].filter(Boolean).join(' ')
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
[applySavedAddress, formik, getCustomerFullName]
|
[applySavedAddress, formik]
|
||||||
)
|
)
|
||||||
|
|
||||||
const clearCustomerData = useCallback(() => {
|
const clearCustomerData = useCallback(() => {
|
||||||
setIsNewCustomer(false)
|
setIsNewCustomer(false)
|
||||||
|
setCustomerUserId(null)
|
||||||
setCustomerAddresses([])
|
setCustomerAddresses([])
|
||||||
setSelectedAddressId(null)
|
setSelectedAddressId(null)
|
||||||
formik.setFieldValue('userAddress', emptyUserAddress())
|
formik.setFieldValue('userAddress', emptyUserAddress())
|
||||||
}, [formik])
|
}, [formik])
|
||||||
|
|
||||||
const handleUserNotFound = useCallback(() => {
|
const handleUserNotFound = useCallback(() => {
|
||||||
|
setCustomerUserId(null)
|
||||||
setCustomerAddresses([])
|
setCustomerAddresses([])
|
||||||
setSelectedAddressId(null)
|
setSelectedAddressId(null)
|
||||||
formik.setFieldValue('userAddress', emptyUserAddress())
|
formik.setFieldValue('userAddress', emptyUserAddress())
|
||||||
@@ -397,6 +434,15 @@ export const useCreateOrderForm = () => {
|
|||||||
[formik]
|
[formik]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const handleLocationSelect = useCallback(
|
||||||
|
(lat: number, lng: number) => {
|
||||||
|
setSelectedAddressId(null)
|
||||||
|
formik.setFieldValue('userAddress.latitude', lat)
|
||||||
|
formik.setFieldValue('userAddress.longitude', lng)
|
||||||
|
},
|
||||||
|
[formik]
|
||||||
|
)
|
||||||
|
|
||||||
const removeItem = (foodId: string) => {
|
const removeItem = (foodId: string) => {
|
||||||
formik.setFieldValue(
|
formik.setFieldValue(
|
||||||
'items',
|
'items',
|
||||||
@@ -418,7 +464,7 @@ export const useCreateOrderForm = () => {
|
|||||||
addFoodToOrder,
|
addFoodToOrder,
|
||||||
updateItemQuantity,
|
updateItemQuantity,
|
||||||
removeItem,
|
removeItem,
|
||||||
isSubmitting: isCreating || isCreatingUser,
|
isSubmitting: isCreating || isCreatingUser || isCreatingAddress,
|
||||||
customerAddresses,
|
customerAddresses,
|
||||||
selectedAddressId,
|
selectedAddressId,
|
||||||
handleUserFound,
|
handleUserFound,
|
||||||
@@ -427,6 +473,7 @@ export const useCreateOrderForm = () => {
|
|||||||
applySavedAddress,
|
applySavedAddress,
|
||||||
selectManualAddress,
|
selectManualAddress,
|
||||||
handleAddressFieldChange,
|
handleAddressFieldChange,
|
||||||
|
handleLocationSelect,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -249,10 +249,8 @@ export interface CreateOrderItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateOrderUserAddress {
|
export interface CreateOrderUserAddress {
|
||||||
fullName: string;
|
title: string;
|
||||||
phone: string;
|
phone: string;
|
||||||
city: string;
|
|
||||||
province: string;
|
|
||||||
address: string;
|
address: string;
|
||||||
postalCode: string;
|
postalCode: string;
|
||||||
latitude: number;
|
latitude: number;
|
||||||
@@ -267,18 +265,14 @@ export interface CreateOrderCarAddress {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateOrderType {
|
export interface CreateOrderType {
|
||||||
userPhone: string;
|
userId: string;
|
||||||
firstName: string;
|
|
||||||
lastName: string;
|
|
||||||
items: CreateOrderItem[];
|
items: CreateOrderItem[];
|
||||||
deliveryMethodId: string;
|
deliveryMethodId: string;
|
||||||
paymentMethodId: string;
|
paymentMethodId: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
tableNumber?: string;
|
tableNumber?: string;
|
||||||
userAddress?: CreateOrderUserAddress;
|
addressId?: string;
|
||||||
carAddress?: CreateOrderCarAddress;
|
carAddress?: CreateOrderCarAddress;
|
||||||
paymentDesc?: string;
|
|
||||||
paymentAttachments?: string[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CreateOrderResponse = IResponse<Order>;
|
export type CreateOrderResponse = IResponse<Order>;
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import type { CreateUserAddressType } from '@/pages/customers/types/Types'
|
||||||
|
import type { CreateOrderUserAddress } from '../types/Types'
|
||||||
|
|
||||||
|
export const mapFormAddressToCreateUserAddress = (
|
||||||
|
address: CreateOrderUserAddress,
|
||||||
|
isDefault: boolean
|
||||||
|
): CreateUserAddressType => {
|
||||||
|
const payload: CreateUserAddressType = {
|
||||||
|
title: address.title || 'آدرس جدید',
|
||||||
|
address: address.address,
|
||||||
|
latitude: Number(address.latitude) || 0,
|
||||||
|
longitude: Number(address.longitude) || 0,
|
||||||
|
phone: address.phone,
|
||||||
|
isDefault,
|
||||||
|
}
|
||||||
|
|
||||||
|
const postalCode = address.postalCode?.trim()
|
||||||
|
if (postalCode) {
|
||||||
|
payload.postalCode = postalCode
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload
|
||||||
|
}
|
||||||
@@ -2,15 +2,12 @@ import type { UserSavedAddress } from '@/pages/customers/types/Types'
|
|||||||
import type { CreateOrderUserAddress } from '../types/Types'
|
import type { CreateOrderUserAddress } from '../types/Types'
|
||||||
|
|
||||||
export const mapSavedAddressToOrderAddress = (
|
export const mapSavedAddressToOrderAddress = (
|
||||||
saved: UserSavedAddress,
|
saved: UserSavedAddress
|
||||||
fullName: string
|
|
||||||
): CreateOrderUserAddress => ({
|
): CreateOrderUserAddress => ({
|
||||||
fullName: fullName || saved.title,
|
title: saved.title || 'آدرس',
|
||||||
phone: saved.phone,
|
phone: saved.phone,
|
||||||
city: saved.city,
|
|
||||||
province: saved.province,
|
|
||||||
address: saved.address,
|
address: saved.address,
|
||||||
postalCode: saved.postalCode,
|
postalCode: saved.postalCode || '',
|
||||||
latitude: saved.latitude,
|
latitude: saved.latitude,
|
||||||
longitude: saved.longitude,
|
longitude: saved.longitude,
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user