feat: deals table
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
import ObjectSelect, {ObjectSelectProps} from "../../../../components/ObjectSelect/ObjectSelect.tsx";
|
||||
import {FC} from "react";
|
||||
import {DealStatuses} from "../../../../shared/enums/DealStatus.ts";
|
||||
|
||||
type DealStatus = {
|
||||
name: string;
|
||||
id: number
|
||||
}
|
||||
type Props = Omit<ObjectSelectProps<DealStatus>, 'data'>;
|
||||
|
||||
const DealStatusSelect: FC<Props> = (props) => {
|
||||
const data: DealStatus[] = DealStatuses;
|
||||
return (
|
||||
<ObjectSelect
|
||||
data={data}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
export default DealStatusSelect;
|
||||
49
src/pages/DealsPage/components/DealsTable/DealsTable.tsx
Normal file
49
src/pages/DealsPage/components/DealsTable/DealsTable.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import {CRUDTableProps} from "../../../../types/CRUDTable.tsx";
|
||||
import {DealService, DealSummary} from "../../../../client";
|
||||
import {FC} from "react";
|
||||
import useDealsTableColumns from "./columns.tsx";
|
||||
import {BaseTable} from "../../../../components/BaseTable/BaseTable.tsx";
|
||||
import {ActionIcon, Flex, Tooltip} from "@mantine/core";
|
||||
import {IconEdit} from "@tabler/icons-react";
|
||||
import {MRT_TableOptions} from "mantine-react-table";
|
||||
import {useDealPageContext} from "../../../LeadsPage/contexts/DealPageContext.tsx";
|
||||
|
||||
type Props = CRUDTableProps<DealSummary>;
|
||||
|
||||
const DealsTable: FC<Props> = ({items}) => {
|
||||
const columns = useDealsTableColumns();
|
||||
const {setSelectedDeal} = useDealPageContext();
|
||||
const onEditClick = (dealSummary: DealSummary) => {
|
||||
DealService.getDealById({dealId: dealSummary.id})
|
||||
.then((deal) => {
|
||||
setSelectedDeal(deal);
|
||||
})
|
||||
}
|
||||
return (
|
||||
<BaseTable
|
||||
data={items}
|
||||
columns={columns}
|
||||
restProps={{
|
||||
enableSorting: true,
|
||||
enableColumnActions: false,
|
||||
enablePagination: true,
|
||||
enableBottomToolbar: true,
|
||||
paginationDisplayMode: "pages",
|
||||
enableRowActions: true,
|
||||
renderRowActions: ({row}) => (
|
||||
<Flex gap="md">
|
||||
<Tooltip label="Редактировать">
|
||||
<ActionIcon
|
||||
onClick={() => onEditClick(row.original)}
|
||||
variant={"default"}>
|
||||
<IconEdit/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
)
|
||||
} as MRT_TableOptions<DealSummary>}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default DealsTable;
|
||||
50
src/pages/DealsPage/components/DealsTable/columns.tsx
Normal file
50
src/pages/DealsPage/components/DealsTable/columns.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import {useMemo} from "react";
|
||||
import {MRT_ColumnDef} from "mantine-react-table";
|
||||
import {DealSummary} from "../../../../client";
|
||||
import {ActionIcon, Image} from "@mantine/core";
|
||||
|
||||
const useDealsTableColumns = () => {
|
||||
return useMemo<MRT_ColumnDef<DealSummary>[]>(() => [
|
||||
|
||||
{
|
||||
header: "Маркетплейс",
|
||||
size: 10,
|
||||
Cell: ({row}) => (
|
||||
<ActionIcon variant={"transparent"}>
|
||||
<Image src={row.original.baseMarketplace?.iconUrl || ""}/>
|
||||
</ActionIcon>
|
||||
)
|
||||
},
|
||||
{
|
||||
header: "Дата создания",
|
||||
accessorKey: "createdAt",
|
||||
Cell: ({row}) => (new Date(row.original.createdAt)).toLocaleString("ru-RU"),
|
||||
enableSorting: true,
|
||||
sortingFn: (rowA, rowB) => ((new Date(rowB.original.createdAt)).getTime() - (new Date(rowA.original.createdAt)).getTime())
|
||||
},
|
||||
{
|
||||
accessorKey: "name",
|
||||
header: "Название",
|
||||
enableSorting: false
|
||||
},
|
||||
{
|
||||
accessorKey: "clientName",
|
||||
header: "Клиент",
|
||||
enableSorting: false
|
||||
},
|
||||
{
|
||||
Cell: ({row}) => (new Date(row.original.deadline)).toLocaleString("ru-RU"),
|
||||
accessorKey: "deadline",
|
||||
header: "Дедлайн",
|
||||
sortingFn: (rowA, rowB) => ((new Date(rowB.original.deadline)).getTime() - (new Date(rowA.original.deadline)).getTime())
|
||||
|
||||
},
|
||||
{
|
||||
header: "Общая стоимость",
|
||||
Cell: ({row}) => (row.original.totalPrice).toLocaleString("ru-RU") + '₽',
|
||||
accessorKey:"totalPrice"
|
||||
}
|
||||
], [])
|
||||
}
|
||||
|
||||
export default useDealsTableColumns;
|
||||
1
src/pages/DealsPage/index.ts
Normal file
1
src/pages/DealsPage/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export {DealsPage} from './ui/DealsPage.tsx';
|
||||
27
src/pages/DealsPage/ui/DealsPage.module.css
Normal file
27
src/pages/DealsPage/ui/DealsPage.module.css
Normal file
@@ -0,0 +1,27 @@
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
gap: rem(10);
|
||||
}
|
||||
|
||||
|
||||
.body-container {
|
||||
}
|
||||
|
||||
.top-panel {
|
||||
padding: rem(5);
|
||||
gap: rem(10);
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
display: flex;
|
||||
gap: rem(10);
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.table-pagination {
|
||||
align-self: flex-end;
|
||||
|
||||
}
|
||||
89
src/pages/DealsPage/ui/DealsPage.tsx
Normal file
89
src/pages/DealsPage/ui/DealsPage.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
import {FC, useEffect, useState} from "react";
|
||||
import PageBlock from "../../../components/PageBlock/PageBlock.tsx";
|
||||
import styles from './DealsPage.module.css';
|
||||
import DealStatusSelect from "../components/DealStatusSelect/DealStatusSelect.tsx";
|
||||
import DealsTable from "../components/DealsTable/DealsTable.tsx";
|
||||
import {useDealSummariesFull} from "../../LeadsPage/hooks/useDealSummaries.tsx";
|
||||
import {DealStatusType} from "../../../shared/enums/DealStatus.ts";
|
||||
import {BaseMarketplaceSchema, ClientSchema} from "../../../client";
|
||||
import BaseMarketplaceSelect from "../../../components/Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
|
||||
import {useForm} from "@mantine/form";
|
||||
import ClientSelectNew from "../../../components/Selects/ClientSelectNew/ClientSelectNew.tsx";
|
||||
import {DealPageContextProvider} from "../../LeadsPage/contexts/DealPageContext.tsx";
|
||||
import DealEditDrawer from "../../LeadsPage/drawers/DealEditDrawer/DealEditDrawer.tsx";
|
||||
|
||||
type State = {
|
||||
marketplace: BaseMarketplaceSchema | null;
|
||||
dealStatus: DealStatusType | null;
|
||||
client: ClientSchema | null;
|
||||
}
|
||||
export const DealsPage: FC = () => {
|
||||
const {objects} = useDealSummariesFull();
|
||||
const form = useForm<State>({
|
||||
initialValues: {
|
||||
marketplace: null,
|
||||
dealStatus: null,
|
||||
client: null
|
||||
}
|
||||
});
|
||||
const [data, setData] = useState(objects);
|
||||
const applyFilters = () => {
|
||||
let result = objects;
|
||||
if (form.values.marketplace) {
|
||||
result = result.filter(obj => obj.baseMarketplace?.key === form.values.marketplace?.key);
|
||||
}
|
||||
if (form.values.dealStatus) {
|
||||
result = result.filter(obj => obj.status === form.values.dealStatus?.id);
|
||||
}
|
||||
if (form.values.client) {
|
||||
result = result.filter(obj => obj.clientName === form.values.client?.name);
|
||||
|
||||
}
|
||||
setData(result);
|
||||
}
|
||||
useEffect(() => {
|
||||
applyFilters();
|
||||
}, [form.values, objects])
|
||||
|
||||
return (
|
||||
<>
|
||||
<DealPageContextProvider>
|
||||
<div className={styles['container']}>
|
||||
<PageBlock>
|
||||
<div className={styles['top-panel']}>
|
||||
<DealStatusSelect
|
||||
onClear={() => form.setFieldValue("dealStatus", null)}
|
||||
clearable
|
||||
placeholder={"Выберите статус "}
|
||||
{...form.getInputProps("dealStatus")}
|
||||
/>
|
||||
<BaseMarketplaceSelect
|
||||
onClear={() => form.setFieldValue("marketplace", null)}
|
||||
clearable
|
||||
|
||||
placeholder={"Выберите маркетплейс"}
|
||||
{...form.getInputProps("marketplace")}
|
||||
/>
|
||||
<ClientSelectNew
|
||||
onClear={() => form.setFieldValue("client", null)}
|
||||
clearable
|
||||
searchable
|
||||
placeholder={"Выберите клиента"}
|
||||
{...form.getInputProps("client")}
|
||||
/>
|
||||
</div>
|
||||
</PageBlock>
|
||||
<PageBlock>
|
||||
<div className={styles['body-container']}>
|
||||
<div className={styles['table-container']}>
|
||||
<DealsTable items={data}/>
|
||||
</div>
|
||||
</div>
|
||||
</PageBlock>
|
||||
</div>
|
||||
<DealEditDrawer
|
||||
/>
|
||||
</DealPageContextProvider>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import {useQuery} from "@tanstack/react-query";
|
||||
import {DealService} from "../../../client";
|
||||
import ObjectList from "../../../hooks/objectList.tsx";
|
||||
|
||||
export const useDealSummaries = () => {
|
||||
const {data: summariesRaw = [], refetch} = useQuery({
|
||||
queryKey: ['getDealSummaries'],
|
||||
queryFn: DealService.getDealSummaries,
|
||||
queryFn: () => DealService.getDealSummaries({full: false}),
|
||||
select: data => data.summaries || [] // Трансформируем полученные данные
|
||||
});
|
||||
|
||||
@@ -12,4 +13,9 @@ export const useDealSummaries = () => {
|
||||
// isLoading и isError могут быть использованы для отображения индикаторов загрузки или ошибки
|
||||
|
||||
return {summariesRaw, refetch};
|
||||
}
|
||||
}
|
||||
export const useDealSummariesFull = () => ObjectList({
|
||||
queryFn: () => DealService.getDealSummaries({full: true}),
|
||||
queryKey: "getDealSummariesFull",
|
||||
getObjectsFn: response => response.summaries
|
||||
});
|
||||
Reference in New Issue
Block a user