update customer
This commit is contained in:
@@ -1,138 +1,141 @@
|
|||||||
import Table from '@/components/Table'
|
import Button from "@/components/Button";
|
||||||
import Filters, { type FieldType } from '@/components/Filters'
|
import Filters, { type FieldType } from "@/components/Filters";
|
||||||
import Button from '@/components/Button'
|
import Table from "@/components/Table";
|
||||||
import { type FC, useMemo, useState } from 'react'
|
import { Pages } from "@/config/Pages";
|
||||||
import { useTranslation } from 'react-i18next'
|
import { formatFaNumber, formatOptionalDate } from "@/helpers/func";
|
||||||
import { DocumentDownload, DocumentUpload } from 'iconsax-react'
|
import type { CustomerListRow, UserRestaurant } from "@/pages/customers/types/Types";
|
||||||
import { useGetUsers } from './hooks/useUsersData'
|
import { DocumentDownload, DocumentUpload, Edit } from "iconsax-react";
|
||||||
import { useCustomerFilters } from './hooks/useCustomerFilters'
|
import { type FC, useMemo, useState } from "react";
|
||||||
import ImportCustomersModal from './components/ImportCustomersModal'
|
import { useTranslation } from "react-i18next";
|
||||||
import { downloadCustomerImportSampleExcel } from './utils/downloadCustomerImportSampleExcel'
|
import { useNavigate } from "react-router-dom";
|
||||||
import type { CustomerListRow, UserRestaurant } from '@/pages/customers/types/Types'
|
import ImportCustomersModal from "./components/ImportCustomersModal";
|
||||||
import { formatOptionalDate, formatFaNumber } from '@/helpers/func'
|
import { useCustomerFilters } from "./hooks/useCustomerFilters";
|
||||||
|
import { useGetUsers } from "./hooks/useUsersData";
|
||||||
|
import { downloadCustomerImportSampleExcel } from "./utils/downloadCustomerImportSampleExcel";
|
||||||
|
|
||||||
const mapUserRestaurantToRow = (userRestaurant: UserRestaurant): CustomerListRow => {
|
const mapUserRestaurantToRow = (userRestaurant: UserRestaurant): CustomerListRow => {
|
||||||
const { user, orderCount, totalOrderAmount } = userRestaurant
|
const { user, orderCount, totalOrderAmount } = userRestaurant;
|
||||||
const fullName = [user.firstName, user.lastName].filter(Boolean).join(' ').trim()
|
const fullName = [user.firstName, user.lastName].filter(Boolean).join(" ").trim();
|
||||||
return {
|
return {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
fullName: fullName || 'بدون نام',
|
fullName: fullName || "بدون نام",
|
||||||
phone: user.phone || '-',
|
phone: user.phone || "-",
|
||||||
birthDate: formatOptionalDate(user.birthDate),
|
birthDate: formatOptionalDate(user.birthDate),
|
||||||
marriageDate: formatOptionalDate(user.marriageDate),
|
marriageDate: formatOptionalDate(user.marriageDate),
|
||||||
orderCount: formatFaNumber(orderCount),
|
orderCount: formatFaNumber(orderCount),
|
||||||
totalOrderAmount: formatFaNumber(totalOrderAmount),
|
totalOrderAmount: formatFaNumber(totalOrderAmount),
|
||||||
status: user.isActive !== false ? 'فعال' : 'غیرفعال',
|
status: user.isActive !== false ? "فعال" : "غیرفعال",
|
||||||
}
|
firstName: user.firstName,
|
||||||
}
|
lastName: user.lastName,
|
||||||
|
birthDateRaw: user.birthDate,
|
||||||
|
marriageDateRaw: user.marriageDate,
|
||||||
|
gender: user.gender,
|
||||||
|
avatarUrl: user.avatarUrl,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const CustomersList: FC = () => {
|
const CustomersList: FC = () => {
|
||||||
const { t } = useTranslation('global')
|
const { t } = useTranslation("global");
|
||||||
const [isImportModalOpen, setIsImportModalOpen] = useState(false)
|
const navigate = useNavigate();
|
||||||
|
const [isImportModalOpen, setIsImportModalOpen] = useState(false);
|
||||||
|
|
||||||
const {
|
const { filters, currentPage, apiParams, handleFiltersChange, handlePageChange } = useCustomerFilters();
|
||||||
filters,
|
|
||||||
currentPage,
|
|
||||||
apiParams,
|
|
||||||
handleFiltersChange,
|
|
||||||
handlePageChange,
|
|
||||||
} = useCustomerFilters()
|
|
||||||
|
|
||||||
const { data: usersData, isLoading } = useGetUsers(apiParams)
|
const { data: usersData, isLoading } = useGetUsers(apiParams);
|
||||||
|
|
||||||
const filterFields = useMemo<FieldType[]>(() => [
|
const filterFields = useMemo<FieldType[]>(
|
||||||
|
() => [
|
||||||
{
|
{
|
||||||
type: 'input',
|
type: "input",
|
||||||
name: 'search',
|
name: "search",
|
||||||
placeholder: 'نام یا شماره تماس',
|
placeholder: "نام یا شماره تماس",
|
||||||
},
|
},
|
||||||
], [])
|
],
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
const userRestaurants = usersData?.data ?? []
|
const userRestaurants = usersData?.data ?? [];
|
||||||
const tableData = useMemo<CustomerListRow[]>(
|
const tableData = useMemo<CustomerListRow[]>(() => userRestaurants.map(mapUserRestaurantToRow), [userRestaurants]);
|
||||||
() => userRestaurants.map(mapUserRestaurantToRow),
|
|
||||||
[userRestaurants],
|
|
||||||
)
|
|
||||||
|
|
||||||
const totalPages = usersData?.meta?.totalPages || 1
|
const totalPages = usersData?.meta?.totalPages || 1;
|
||||||
|
|
||||||
const columns = useMemo(() => [
|
const columns = useMemo(
|
||||||
|
() => [
|
||||||
{
|
{
|
||||||
key: 'fullName',
|
key: "fullName",
|
||||||
title: 'نام و نام خانوادگی',
|
title: "نام و نام خانوادگی",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'phone',
|
key: "phone",
|
||||||
title: 'شماره تماس',
|
title: "شماره تماس",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'birthDate',
|
key: "birthDate",
|
||||||
title: 'تاریخ تولد',
|
title: "تاریخ تولد",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'marriageDate',
|
key: "marriageDate",
|
||||||
title: 'تاریخ ازدواج',
|
title: "تاریخ ازدواج",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'orderCount',
|
key: "orderCount",
|
||||||
title: 'تعداد سفارش',
|
title: "تعداد سفارش",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'totalOrderAmount',
|
key: "totalOrderAmount",
|
||||||
title: 'مجموع سفارش (تومان)',
|
title: "مجموع سفارش (تومان)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'status',
|
key: "status",
|
||||||
title: 'وضعیت',
|
title: "وضعیت",
|
||||||
render: (item: CustomerListRow) => (
|
render: (item: CustomerListRow) => (
|
||||||
<span
|
<span className={`px-3 py-1 rounded-full text-xs ${item.status === "فعال" ? "bg-green-100 text-green-700" : "bg-red-100 text-red-600"}`}>{item.status}</span>
|
||||||
className={`px-3 py-1 rounded-full text-xs ${item.status === 'فعال' ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-600'}`}
|
|
||||||
>
|
|
||||||
{item.status}
|
|
||||||
</span>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'details',
|
key: "details",
|
||||||
title: 'جزئیات',
|
title: "عملیات",
|
||||||
|
render: (item: CustomerListRow) => (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="text-primary text-xs flex items-center gap-1"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
navigate(Pages.customers.update + item.id, {
|
||||||
|
state: { customer: item },
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Edit size={18} color="#0047FF" />
|
||||||
|
</button>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
], [])
|
],
|
||||||
|
[navigate],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mt-5'>
|
<div className="mt-5">
|
||||||
<div className='flex flex-wrap justify-between items-center gap-3'>
|
<div className="flex flex-wrap justify-between items-center gap-3">
|
||||||
<h1 className='text-lg font-light'>{t('customer.customer_list')}</h1>
|
<h1 className="text-lg font-light">{t("customer.customer_list")}</h1>
|
||||||
<div className='flex flex-wrap gap-2'>
|
<div className="flex flex-wrap gap-2">
|
||||||
<Button
|
<Button type="button" className="w-fit px-4 bg-gray-100 text-gray-700" onClick={downloadCustomerImportSampleExcel}>
|
||||||
type='button'
|
<div className="flex items-center gap-2">
|
||||||
className='w-fit px-4 bg-gray-100 text-gray-700'
|
<DocumentDownload size={18} color="#374151" />
|
||||||
onClick={downloadCustomerImportSampleExcel}
|
<span>{t("customer.download_sample")}</span>
|
||||||
>
|
|
||||||
<div className='flex items-center gap-2'>
|
|
||||||
<DocumentDownload size={18} color='#374151' />
|
|
||||||
<span>{t('customer.download_sample')}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button type="button" className="w-fit px-4" onClick={() => setIsImportModalOpen(true)}>
|
||||||
type='button'
|
<div className="flex items-center gap-2">
|
||||||
className='w-fit px-4'
|
<DocumentUpload size={18} color="#fff" />
|
||||||
onClick={() => setIsImportModalOpen(true)}
|
<span>{t("customer.import_excel")}</span>
|
||||||
>
|
|
||||||
<div className='flex items-center gap-2'>
|
|
||||||
<DocumentUpload size={18} color='#fff' />
|
|
||||||
<span>{t('customer.import_excel')}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-8'>
|
<div className="mt-8">
|
||||||
<Filters
|
<Filters fields={filterFields} onChange={handleFiltersChange} initialValues={filters} searchField="search" />
|
||||||
fields={filterFields}
|
|
||||||
onChange={handleFiltersChange}
|
|
||||||
initialValues={filters}
|
|
||||||
searchField="search"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Table
|
<Table
|
||||||
@@ -147,12 +150,9 @@ const CustomersList: FC = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ImportCustomersModal
|
<ImportCustomersModal open={isImportModalOpen} onClose={() => setIsImportModalOpen(false)} />
|
||||||
open={isImportModalOpen}
|
|
||||||
onClose={() => setIsImportModalOpen(false)}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default CustomersList
|
export default CustomersList;
|
||||||
|
|||||||
@@ -0,0 +1,215 @@
|
|||||||
|
import { type FC, useEffect, useState } from 'react'
|
||||||
|
import { useFormik } from 'formik'
|
||||||
|
import { TickCircle } from 'iconsax-react'
|
||||||
|
import { toast } from 'react-toastify'
|
||||||
|
import { useLocation, useNavigate, useParams } from 'react-router-dom'
|
||||||
|
import Button from '@/components/Button'
|
||||||
|
import Input from '@/components/Input'
|
||||||
|
import Select from '@/components/Select'
|
||||||
|
import DatePicker from '@/components/DatePicker'
|
||||||
|
import UploadBox from '@/components/UploadBox'
|
||||||
|
import { useSingleUpload } from '@/pages/uploader/hooks/useUploaderData'
|
||||||
|
import { useUpdateUser } from './hooks/useUsersData'
|
||||||
|
import type { CustomerListRow, UpdateUserType } from './types/Types'
|
||||||
|
import { Pages } from '@/config/Pages'
|
||||||
|
import type { ErrorType } from '@/helpers/types'
|
||||||
|
import { extractErrorMessage } from '@/config/func'
|
||||||
|
|
||||||
|
type UpdateCustomerFormValues = {
|
||||||
|
firstName: string
|
||||||
|
lastName: string
|
||||||
|
birthDate: string
|
||||||
|
marriageDate: string
|
||||||
|
gender: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CustomerUpdateLocationState = {
|
||||||
|
customer?: CustomerListRow
|
||||||
|
}
|
||||||
|
|
||||||
|
const UpdateCustomer: FC = () => {
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const location = useLocation()
|
||||||
|
const { id } = useParams<{ id: string }>()
|
||||||
|
const { mutate: updateUser, isPending: isUpdating } = useUpdateUser()
|
||||||
|
const { mutate: singleUpload, isPending: isUploading } = useSingleUpload()
|
||||||
|
const [avatarFile, setAvatarFile] = useState<File[]>([])
|
||||||
|
const [avatarPreviewUrl, setAvatarPreviewUrl] = useState('')
|
||||||
|
|
||||||
|
const customer = (location.state as CustomerUpdateLocationState | null)?.customer
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!customer?.avatarUrl) {
|
||||||
|
setAvatarPreviewUrl('')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setAvatarPreviewUrl(customer.avatarUrl)
|
||||||
|
}, [customer?.avatarUrl])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (avatarFile.length > 0) {
|
||||||
|
const url = URL.createObjectURL(avatarFile[0])
|
||||||
|
setAvatarPreviewUrl(url)
|
||||||
|
return () => URL.revokeObjectURL(url)
|
||||||
|
}
|
||||||
|
}, [avatarFile])
|
||||||
|
|
||||||
|
const formik = useFormik<UpdateCustomerFormValues>({
|
||||||
|
initialValues: {
|
||||||
|
firstName: customer?.firstName || '',
|
||||||
|
lastName: customer?.lastName || '',
|
||||||
|
birthDate: customer?.birthDateRaw || '',
|
||||||
|
marriageDate: customer?.marriageDateRaw || '',
|
||||||
|
gender:
|
||||||
|
customer?.gender === undefined || customer?.gender === null
|
||||||
|
? ''
|
||||||
|
: String(customer.gender),
|
||||||
|
},
|
||||||
|
enableReinitialize: true,
|
||||||
|
onSubmit: (values) => {
|
||||||
|
if (!id) {
|
||||||
|
toast.error('شناسه مشتری یافت نشد')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitUpdate = (avatarUrl?: string) => {
|
||||||
|
const payload: UpdateUserType = {}
|
||||||
|
|
||||||
|
if (values.firstName.trim()) payload.firstName = values.firstName.trim()
|
||||||
|
if (values.lastName.trim()) payload.lastName = values.lastName.trim()
|
||||||
|
if (values.birthDate) payload.birthDate = values.birthDate
|
||||||
|
if (values.marriageDate) payload.marriageDate = values.marriageDate
|
||||||
|
if (values.gender !== '') payload.gender = values.gender === 'true'
|
||||||
|
if (avatarUrl) payload.avatarUrl = avatarUrl
|
||||||
|
|
||||||
|
if (Object.keys(payload).length === 0) {
|
||||||
|
toast.info('حداقل یک فیلد را برای ویرایش وارد کنید')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
updateUser(
|
||||||
|
{ userId: id, params: payload },
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.success('اطلاعات مشتری با موفقیت ویرایش شد')
|
||||||
|
navigate(Pages.customers.list)
|
||||||
|
},
|
||||||
|
onError: (error: ErrorType) => {
|
||||||
|
toast.error(extractErrorMessage(error, 'خطا در ویرایش مشتری'))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (avatarFile.length > 0) {
|
||||||
|
singleUpload(avatarFile[0], {
|
||||||
|
onSuccess: (response) => {
|
||||||
|
const avatarUrl = response?.data?.url || ''
|
||||||
|
submitUpdate(avatarUrl)
|
||||||
|
},
|
||||||
|
onError: (error: ErrorType) => {
|
||||||
|
toast.error(extractErrorMessage(error, 'خطا در آپلود تصویر'))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
submitUpdate(customer?.avatarUrl || undefined)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='mt-5'>
|
||||||
|
<div className='flex justify-between items-center'>
|
||||||
|
<h1 className='text-lg font-light'>ویرایش مشتری</h1>
|
||||||
|
<Button
|
||||||
|
className='w-fit px-6'
|
||||||
|
isloading={isUpdating || isUploading}
|
||||||
|
onClick={() => formik.handleSubmit()}
|
||||||
|
>
|
||||||
|
<div className='flex gap-2 items-center'>
|
||||||
|
<TickCircle size={20} color='#fff' />
|
||||||
|
<span>ذخیره تغییرات</span>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='mt-9'>
|
||||||
|
<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'>
|
||||||
|
<Input
|
||||||
|
label='نام'
|
||||||
|
placeholder=''
|
||||||
|
name='firstName'
|
||||||
|
value={formik.values.firstName}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
isNotRequired
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
label='نام خانوادگی'
|
||||||
|
placeholder=''
|
||||||
|
name='lastName'
|
||||||
|
value={formik.values.lastName}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
isNotRequired
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='grid grid-cols-1 sm:grid-cols-2 gap-4 mt-5'>
|
||||||
|
<DatePicker
|
||||||
|
label='تاریخ تولد'
|
||||||
|
placeholder='انتخاب'
|
||||||
|
isNotRequired
|
||||||
|
defaulValue={formik.values.birthDate}
|
||||||
|
onChange={(date) => formik.setFieldValue('birthDate', date)}
|
||||||
|
/>
|
||||||
|
<DatePicker
|
||||||
|
label='تاریخ ازدواج'
|
||||||
|
placeholder='انتخاب'
|
||||||
|
isNotRequired
|
||||||
|
defaulValue={formik.values.marriageDate}
|
||||||
|
onChange={(date) => formik.setFieldValue('marriageDate', date)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='grid grid-cols-1 sm:grid-cols-2 gap-4 mt-5'>
|
||||||
|
<Select
|
||||||
|
label='جنسیت'
|
||||||
|
placeholder='انتخاب'
|
||||||
|
name='gender'
|
||||||
|
value={formik.values.gender}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
isNotRequired
|
||||||
|
items={[
|
||||||
|
{ value: 'true', label: 'مرد' },
|
||||||
|
{ value: 'false', label: 'زن' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='mt-5'>
|
||||||
|
<UploadBox
|
||||||
|
label='تصویر پروفایل'
|
||||||
|
isNotRequired
|
||||||
|
onChange={(files) => setAvatarFile(files)}
|
||||||
|
isReset={avatarFile.length === 0}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{avatarPreviewUrl && (
|
||||||
|
<div className='mt-3'>
|
||||||
|
<img
|
||||||
|
src={avatarPreviewUrl}
|
||||||
|
alt='avatar preview'
|
||||||
|
className='w-20 h-20 rounded-full object-cover'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UpdateCustomer
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
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 { CreateUserAddressType, GetUsersParams } from "../types/Types";
|
import type {
|
||||||
|
CreateUserAddressType,
|
||||||
|
GetUsersParams,
|
||||||
|
UpdateUserType,
|
||||||
|
} from "../types/Types";
|
||||||
|
|
||||||
export const useGetUsers = (params?: GetUsersParams) => {
|
export const useGetUsers = (params?: GetUsersParams) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
@@ -26,6 +30,18 @@ export const useCreateUser = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useUpdateUser = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: ({ userId, params }: { userId: string; params: UpdateUserType }) =>
|
||||||
|
api.updateUser(userId, params),
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["users"] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const useCreateUserAddress = () => {
|
export const useCreateUserAddress = () => {
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: ({
|
mutationFn: ({
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import type {
|
|||||||
GetUsersParams,
|
GetUsersParams,
|
||||||
ImportUsersResponse,
|
ImportUsersResponse,
|
||||||
SearchUserByPhoneResponse,
|
SearchUserByPhoneResponse,
|
||||||
|
UpdateUserResponse,
|
||||||
|
UpdateUserType,
|
||||||
} from "@/pages/customers/types/Types";
|
} from "@/pages/customers/types/Types";
|
||||||
|
|
||||||
export const getUsers = async (params?: GetUsersParams) => {
|
export const getUsers = async (params?: GetUsersParams) => {
|
||||||
@@ -30,6 +32,14 @@ export const createUser = async (params: CreateUserType) => {
|
|||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const updateUser = async (userId: string, params: UpdateUserType) => {
|
||||||
|
const { data } = await axios.patch<UpdateUserResponse>(
|
||||||
|
`/admin/users/${userId}`,
|
||||||
|
params,
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
export const createUserAddress = async (
|
export const createUserAddress = async (
|
||||||
userId: string,
|
userId: string,
|
||||||
params: CreateUserAddressType,
|
params: CreateUserAddressType,
|
||||||
|
|||||||
@@ -76,6 +76,12 @@ export type CustomerListRow = RowDataType & {
|
|||||||
orderCount: string;
|
orderCount: string;
|
||||||
totalOrderAmount: string;
|
totalOrderAmount: string;
|
||||||
status: string;
|
status: string;
|
||||||
|
firstName?: string;
|
||||||
|
lastName?: string;
|
||||||
|
birthDateRaw?: string;
|
||||||
|
marriageDateRaw?: string;
|
||||||
|
gender?: boolean;
|
||||||
|
avatarUrl?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ImportUserRowError = {
|
export type ImportUserRowError = {
|
||||||
@@ -129,3 +135,14 @@ export type CreateUserAddressType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type CreateUserAddressResponse = IResponse<UserSavedAddress>;
|
export type CreateUserAddressResponse = IResponse<UserSavedAddress>;
|
||||||
|
|
||||||
|
export type UpdateUserType = {
|
||||||
|
firstName?: string;
|
||||||
|
lastName?: string;
|
||||||
|
birthDate?: string;
|
||||||
|
marriageDate?: string;
|
||||||
|
gender?: boolean;
|
||||||
|
avatarUrl?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdateUserResponse = IResponse<User>;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import CategoryFood from '@/pages/food/Category'
|
|||||||
import CustomersList from '@/pages/customers/List'
|
import CustomersList from '@/pages/customers/List'
|
||||||
import CustomerDetails from '@/pages/customers/Details'
|
import CustomerDetails from '@/pages/customers/Details'
|
||||||
import CreateCustomer from '@/pages/customers/Create'
|
import CreateCustomer from '@/pages/customers/Create'
|
||||||
|
import UpdateCustomer from '@/pages/customers/Update'
|
||||||
import OrdersList from '@/pages/orders/List'
|
import OrdersList from '@/pages/orders/List'
|
||||||
import CreateOrder from '@/pages/orders/Create'
|
import CreateOrder from '@/pages/orders/Create'
|
||||||
import OrderDetails from '@/pages/orders/Details'
|
import OrderDetails from '@/pages/orders/Details'
|
||||||
@@ -73,6 +74,7 @@ const MainRouter: FC = () => {
|
|||||||
<Route path={Pages.customers.list} element={<CustomersList />} />
|
<Route path={Pages.customers.list} element={<CustomersList />} />
|
||||||
<Route path={Pages.customers.detail + ':id'} element={<CustomerDetails />} />
|
<Route path={Pages.customers.detail + ':id'} element={<CustomerDetails />} />
|
||||||
<Route path={Pages.customers.add} element={<CreateCustomer />} />
|
<Route path={Pages.customers.add} element={<CreateCustomer />} />
|
||||||
|
<Route path={Pages.customers.update + ':id'} element={<UpdateCustomer />} />
|
||||||
<Route path={Pages.orders.list} element={<OrdersList />} />
|
<Route path={Pages.orders.list} element={<OrdersList />} />
|
||||||
<Route path={Pages.orders.add} element={<CreateOrder />} />
|
<Route path={Pages.orders.add} element={<CreateOrder />} />
|
||||||
<Route path={Pages.orders.foodReport} element={<FoodOrderReport />} />
|
<Route path={Pages.orders.foodReport} element={<FoodOrderReport />} />
|
||||||
|
|||||||
Reference in New Issue
Block a user