update customer
This commit is contained in:
+143
-143
@@ -1,158 +1,158 @@
|
||||
import Table from '@/components/Table'
|
||||
import Filters, { type FieldType } from '@/components/Filters'
|
||||
import Button from '@/components/Button'
|
||||
import { type FC, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { DocumentDownload, DocumentUpload } from 'iconsax-react'
|
||||
import { useGetUsers } from './hooks/useUsersData'
|
||||
import { useCustomerFilters } from './hooks/useCustomerFilters'
|
||||
import ImportCustomersModal from './components/ImportCustomersModal'
|
||||
import { downloadCustomerImportSampleExcel } from './utils/downloadCustomerImportSampleExcel'
|
||||
import type { CustomerListRow, UserRestaurant } from '@/pages/customers/types/Types'
|
||||
import { formatOptionalDate, formatFaNumber } from '@/helpers/func'
|
||||
import Button from "@/components/Button";
|
||||
import Filters, { type FieldType } from "@/components/Filters";
|
||||
import Table from "@/components/Table";
|
||||
import { Pages } from "@/config/Pages";
|
||||
import { formatFaNumber, formatOptionalDate } from "@/helpers/func";
|
||||
import type { CustomerListRow, UserRestaurant } from "@/pages/customers/types/Types";
|
||||
import { DocumentDownload, DocumentUpload, Edit } from "iconsax-react";
|
||||
import { type FC, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import ImportCustomersModal from "./components/ImportCustomersModal";
|
||||
import { useCustomerFilters } from "./hooks/useCustomerFilters";
|
||||
import { useGetUsers } from "./hooks/useUsersData";
|
||||
import { downloadCustomerImportSampleExcel } from "./utils/downloadCustomerImportSampleExcel";
|
||||
|
||||
const mapUserRestaurantToRow = (userRestaurant: UserRestaurant): CustomerListRow => {
|
||||
const { user, orderCount, totalOrderAmount } = userRestaurant
|
||||
const fullName = [user.firstName, user.lastName].filter(Boolean).join(' ').trim()
|
||||
return {
|
||||
id: user.id,
|
||||
fullName: fullName || 'بدون نام',
|
||||
phone: user.phone || '-',
|
||||
birthDate: formatOptionalDate(user.birthDate),
|
||||
marriageDate: formatOptionalDate(user.marriageDate),
|
||||
orderCount: formatFaNumber(orderCount),
|
||||
totalOrderAmount: formatFaNumber(totalOrderAmount),
|
||||
status: user.isActive !== false ? 'فعال' : 'غیرفعال',
|
||||
}
|
||||
}
|
||||
const { user, orderCount, totalOrderAmount } = userRestaurant;
|
||||
const fullName = [user.firstName, user.lastName].filter(Boolean).join(" ").trim();
|
||||
return {
|
||||
id: user.id,
|
||||
fullName: fullName || "بدون نام",
|
||||
phone: user.phone || "-",
|
||||
birthDate: formatOptionalDate(user.birthDate),
|
||||
marriageDate: formatOptionalDate(user.marriageDate),
|
||||
orderCount: formatFaNumber(orderCount),
|
||||
totalOrderAmount: formatFaNumber(totalOrderAmount),
|
||||
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 { t } = useTranslation('global')
|
||||
const [isImportModalOpen, setIsImportModalOpen] = useState(false)
|
||||
const { t } = useTranslation("global");
|
||||
const navigate = useNavigate();
|
||||
const [isImportModalOpen, setIsImportModalOpen] = useState(false);
|
||||
|
||||
const {
|
||||
filters,
|
||||
currentPage,
|
||||
apiParams,
|
||||
handleFiltersChange,
|
||||
handlePageChange,
|
||||
} = useCustomerFilters()
|
||||
const { filters, currentPage, apiParams, handleFiltersChange, handlePageChange } = useCustomerFilters();
|
||||
|
||||
const { data: usersData, isLoading } = useGetUsers(apiParams)
|
||||
const { data: usersData, isLoading } = useGetUsers(apiParams);
|
||||
|
||||
const filterFields = useMemo<FieldType[]>(() => [
|
||||
{
|
||||
type: 'input',
|
||||
name: 'search',
|
||||
placeholder: 'نام یا شماره تماس',
|
||||
},
|
||||
], [])
|
||||
const filterFields = useMemo<FieldType[]>(
|
||||
() => [
|
||||
{
|
||||
type: "input",
|
||||
name: "search",
|
||||
placeholder: "نام یا شماره تماس",
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
|
||||
const userRestaurants = usersData?.data ?? []
|
||||
const tableData = useMemo<CustomerListRow[]>(
|
||||
() => userRestaurants.map(mapUserRestaurantToRow),
|
||||
[userRestaurants],
|
||||
)
|
||||
const userRestaurants = usersData?.data ?? [];
|
||||
const tableData = useMemo<CustomerListRow[]>(() => userRestaurants.map(mapUserRestaurantToRow), [userRestaurants]);
|
||||
|
||||
const totalPages = usersData?.meta?.totalPages || 1
|
||||
const totalPages = usersData?.meta?.totalPages || 1;
|
||||
|
||||
const columns = useMemo(() => [
|
||||
{
|
||||
key: 'fullName',
|
||||
title: 'نام و نام خانوادگی',
|
||||
},
|
||||
{
|
||||
key: 'phone',
|
||||
title: 'شماره تماس',
|
||||
},
|
||||
{
|
||||
key: 'birthDate',
|
||||
title: 'تاریخ تولد',
|
||||
},
|
||||
{
|
||||
key: 'marriageDate',
|
||||
title: 'تاریخ ازدواج',
|
||||
},
|
||||
{
|
||||
key: 'orderCount',
|
||||
title: 'تعداد سفارش',
|
||||
},
|
||||
{
|
||||
key: 'totalOrderAmount',
|
||||
title: 'مجموع سفارش (تومان)',
|
||||
},
|
||||
{
|
||||
key: 'status',
|
||||
title: 'وضعیت',
|
||||
render: (item: CustomerListRow) => (
|
||||
<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',
|
||||
title: 'جزئیات',
|
||||
},
|
||||
], [])
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
key: "fullName",
|
||||
title: "نام و نام خانوادگی",
|
||||
},
|
||||
{
|
||||
key: "phone",
|
||||
title: "شماره تماس",
|
||||
},
|
||||
{
|
||||
key: "birthDate",
|
||||
title: "تاریخ تولد",
|
||||
},
|
||||
{
|
||||
key: "marriageDate",
|
||||
title: "تاریخ ازدواج",
|
||||
},
|
||||
{
|
||||
key: "orderCount",
|
||||
title: "تعداد سفارش",
|
||||
},
|
||||
{
|
||||
key: "totalOrderAmount",
|
||||
title: "مجموع سفارش (تومان)",
|
||||
},
|
||||
{
|
||||
key: "status",
|
||||
title: "وضعیت",
|
||||
render: (item: CustomerListRow) => (
|
||||
<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",
|
||||
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 (
|
||||
<div className='mt-5'>
|
||||
<div className='flex flex-wrap justify-between items-center gap-3'>
|
||||
<h1 className='text-lg font-light'>{t('customer.customer_list')}</h1>
|
||||
<div className='flex flex-wrap gap-2'>
|
||||
<Button
|
||||
type='button'
|
||||
className='w-fit px-4 bg-gray-100 text-gray-700'
|
||||
onClick={downloadCustomerImportSampleExcel}
|
||||
>
|
||||
<div className='flex items-center gap-2'>
|
||||
<DocumentDownload size={18} color='#374151' />
|
||||
<span>{t('customer.download_sample')}</span>
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
type='button'
|
||||
className='w-fit px-4'
|
||||
onClick={() => setIsImportModalOpen(true)}
|
||||
>
|
||||
<div className='flex items-center gap-2'>
|
||||
<DocumentUpload size={18} color='#fff' />
|
||||
<span>{t('customer.import_excel')}</span>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
return (
|
||||
<div className="mt-5">
|
||||
<div className="flex flex-wrap justify-between items-center gap-3">
|
||||
<h1 className="text-lg font-light">{t("customer.customer_list")}</h1>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button type="button" className="w-fit px-4 bg-gray-100 text-gray-700" onClick={downloadCustomerImportSampleExcel}>
|
||||
<div className="flex items-center gap-2">
|
||||
<DocumentDownload size={18} color="#374151" />
|
||||
<span>{t("customer.download_sample")}</span>
|
||||
</div>
|
||||
|
||||
<div className='mt-8'>
|
||||
<Filters
|
||||
fields={filterFields}
|
||||
onChange={handleFiltersChange}
|
||||
initialValues={filters}
|
||||
searchField="search"
|
||||
/>
|
||||
</Button>
|
||||
<Button type="button" className="w-fit px-4" onClick={() => setIsImportModalOpen(true)}>
|
||||
<div className="flex items-center gap-2">
|
||||
<DocumentUpload size={18} color="#fff" />
|
||||
<span>{t("customer.import_excel")}</span>
|
||||
</div>
|
||||
|
||||
<Table
|
||||
columns={columns}
|
||||
data={tableData}
|
||||
isloading={isLoading}
|
||||
selectable
|
||||
pagination={{
|
||||
currentPage,
|
||||
totalPages,
|
||||
onPageChange: handlePageChange,
|
||||
}}
|
||||
/>
|
||||
|
||||
<ImportCustomersModal
|
||||
open={isImportModalOpen}
|
||||
onClose={() => setIsImportModalOpen(false)}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
export default CustomersList
|
||||
<div className="mt-8">
|
||||
<Filters fields={filterFields} onChange={handleFiltersChange} initialValues={filters} searchField="search" />
|
||||
</div>
|
||||
|
||||
<Table
|
||||
columns={columns}
|
||||
data={tableData}
|
||||
isloading={isLoading}
|
||||
selectable
|
||||
pagination={{
|
||||
currentPage,
|
||||
totalPages,
|
||||
onPageChange: handlePageChange,
|
||||
}}
|
||||
/>
|
||||
|
||||
<ImportCustomersModal open={isImportModalOpen} onClose={() => setIsImportModalOpen(false)} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomersList;
|
||||
|
||||
Reference in New Issue
Block a user