fix: cards fetch optimization

This commit is contained in:
2025-04-15 10:59:56 +04:00
parent 5487aab274
commit 9ce112fd63
22 changed files with 523 additions and 324 deletions

View File

@@ -252,6 +252,7 @@ export type { GetBoardsResponse } from './models/GetBoardsResponse';
export type { GetCardBillById } from './models/GetCardBillById';
export type { GetCardProductsBarcodesPdfRequest } from './models/GetCardProductsBarcodesPdfRequest';
export type { GetCardProductsBarcodesPdfResponse } from './models/GetCardProductsBarcodesPdfResponse';
export type { GetCardSummariesRequest } from './models/GetCardSummariesRequest';
export type { GetChatRequest } from './models/GetChatRequest';
export type { GetChatResponse } from './models/GetChatResponse';
export type { GetClientMarketplacesRequest } from './models/GetClientMarketplacesRequest';

View File

@@ -3,7 +3,9 @@
/* tslint:disable */
/* eslint-disable */
import type { CardSummary } from './CardSummary';
import type { PaginationInfoSchema } from './PaginationInfoSchema';
export type CardSummaryResponse = {
summaries: Array<CardSummary>;
paginationInfo: PaginationInfoSchema;
};

View File

@@ -0,0 +1,16 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type GetCardSummariesRequest = {
full: boolean;
cardId?: (number | string | null);
cardName?: (string | null);
marketplaceKey?: (string | null);
shippingWarehouseId?: (number | null);
clientId?: (number | null);
projectId?: (number | null);
boardId?: (number | null);
statusId?: (number | null);
};

View File

@@ -56,6 +56,7 @@ import type { CreateCardsFromExcelResponse } from '../models/CreateCardsFromExce
import type { GetAvailableEmployeesToAssignResponse } from '../models/GetAvailableEmployeesToAssignResponse';
import type { GetCardProductsBarcodesPdfRequest } from '../models/GetCardProductsBarcodesPdfRequest';
import type { GetCardProductsBarcodesPdfResponse } from '../models/GetCardProductsBarcodesPdfResponse';
import type { GetCardSummariesRequest } from '../models/GetCardSummariesRequest';
import type { ManageEmployeeRequest } from '../models/ManageEmployeeRequest';
import type { ManageEmployeeResponse } from '../models/ManageEmployeeResponse';
import type { ParseCardsExcelResponse } from '../models/ParseCardsExcelResponse';
@@ -155,16 +156,23 @@ export class CardService {
* @throws ApiError
*/
public static getCardSummaries({
full,
requestBody,
page,
itemsPerPage,
}: {
full: (boolean | null),
requestBody: GetCardSummariesRequest,
page?: (number | null),
itemsPerPage?: (number | null),
}): CancelablePromise<CardSummaryResponse> {
return __request(OpenAPI, {
method: 'GET',
method: 'POST',
url: '/card/summaries',
query: {
'full': full,
'page': page,
'items_per_page': itemsPerPage,
},
body: requestBody,
mediaType: 'application/json',
errors: {
422: `Validation Error`,
},

View File

@@ -56,7 +56,11 @@ const CardAttributesInSummaryItem = ({ cardSummary }: Props) => {
.map(cardAttr => {
const isHighlight = isHighlightNeeded(cardAttr);
return (
<Text c={isHighlight ? "red" : "gray.6"} size={"sm"}>
<Text
key={cardAttr.attribute.id}
c={isHighlight ? "red" : "gray.6"}
size={"sm"}
>
{cardAttr.attribute.label}: {getAttrValueValue(cardAttr)}
</Text>
);

View File

@@ -5,7 +5,7 @@ import styles from "../../ui/CardsPage.module.css";
import PageBlock from "../../../../components/PageBlock/PageBlock.tsx";
import DisplayMode from "../../enums/DisplayMode.ts";
import { UseFormReturnType } from "@mantine/form";
import { CardsPageState } from "../../hooks/useCardsPageState.tsx";
import { CardsPageState } from "../../hooks/useCardsTableForm.tsx";
import React from "react";
import ObjectSelect from "../../../../components/ObjectSelect/ObjectSelect.tsx";
import CardsTableFiltersModal from "../../modals/CardsTableFiltersModal.tsx";

View File

@@ -1,19 +1,27 @@
import { CRUDTableProps } from "../../../../types/CRUDTable.tsx";
import { CardService, CardSummary } from "../../../../client";
import { FC } from "react";
import { FC, useEffect, useRef } from "react";
import useCardsTableColumns from "./columns.tsx";
import { BaseTable } from "../../../../components/BaseTable/BaseTable.tsx";
import { ActionIcon, Flex, Tooltip } from "@mantine/core";
import { ActionIcon, Flex, Group, Pagination, Stack, Tooltip } from "@mantine/core";
import { IconEdit } from "@tabler/icons-react";
import { MRT_TableOptions } from "mantine-react-table";
import { useCardPageContext } from "../../contexts/CardPageContext.tsx";
import useCardSummaries from "../../hooks/useCardSummaries.tsx";
import { CardsPageState } from "../../hooks/useCardsTableForm.tsx";
import { UseFormReturnType } from "@mantine/form";
import { useDebouncedValue } from "@mantine/hooks";
type RestProps = {
viewOnly?: boolean;
form: UseFormReturnType<CardsPageState>;
};
type Props = CRUDTableProps<CardSummary> & RestProps;
type Props = Omit<CRUDTableProps<CardSummary>, "items"> & RestProps;
const CardsTable: FC<Props> = ({ form, onSelectionChange, viewOnly = false }) => {
const [debouncedForm] = useDebouncedValue(form, 300);
const prevDebouncedValues = useRef(debouncedForm?.values);
const CardsTable: FC<Props> = ({ items, onSelectionChange, viewOnly = false }) => {
const columns = useCardsTableColumns();
const { setSelectedCard } = useCardPageContext();
const onEditClick = (cardSummary: CardSummary) => {
@@ -22,35 +30,65 @@ const CardsTable: FC<Props> = ({ items, onSelectionChange, viewOnly = false }) =
});
};
const {
summaries,
fetchSummaries,
totalPages,
page,
setPage,
} = useCardSummaries({
full: true,
values: form?.values,
withPagination: true,
});
useEffect(() => {
if (prevDebouncedValues.current !== debouncedForm.values) {
prevDebouncedValues.current = debouncedForm.values;
setPage(1);
}
fetchSummaries();
}, [debouncedForm.values, page]);
return (
<BaseTable
data={items}
columns={columns}
onSelectionChange={onSelectionChange}
restProps={
{
enableSorting: true,
enableColumnActions: false,
enablePagination: !viewOnly,
enableBottomToolbar: !viewOnly,
paginationDisplayMode: "pages",
enableRowActions: true,
enableRowSelection: true,
renderRowActions: ({ row }) => (
<Flex gap="md">
<Tooltip label="Редактировать">
<ActionIcon
disabled={viewOnly}
onClick={() => onEditClick(row.original)}
variant={"default"}>
<IconEdit />
</ActionIcon>
</Tooltip>
</Flex>
),
} as MRT_TableOptions<CardSummary>
}
/>
<Stack>
<BaseTable
data={summaries}
columns={columns}
onSelectionChange={onSelectionChange}
restProps={
{
enableSorting: true,
enableColumnActions: false,
paginationDisplayMode: "pages",
enableRowActions: true,
enableRowSelection: true,
renderRowActions: ({ row }) => (
<Flex gap="md">
<Tooltip label="Редактировать">
<ActionIcon
disabled={viewOnly}
onClick={() => onEditClick(row.original)}
variant={"default"}>
<IconEdit />
</ActionIcon>
</Tooltip>
</Flex>
),
} as MRT_TableOptions<CardSummary>
}
/>
{!viewOnly && (
<Group justify={"flex-end"}>
<Pagination
withEdges
total={totalPages}
value={page}
onChange={setPage}
/>
</Group>
)}
</Stack>
);
};

View File

@@ -4,7 +4,7 @@ import { CardSchema, CardService } from "../../../client";
type CardPageContextState = {
selectedCard?: CardSchema;
setSelectedCard: (card: CardSchema | undefined) => void;
refetchCards: () => Promise<void>;
refetchCards: () => void;
refetchCard: () => void;
};
@@ -13,7 +13,7 @@ const CardPageContext = createContext<CardPageContextState | undefined>(
);
type CardPageContextStateProps = {
refetchCards: () => Promise<void>;
refetchCards: () => void;
defaultCardId?: number;
}

View File

@@ -17,12 +17,8 @@ const PrefillCardContext = createContext<PrefillCardContextState | undefined>(
undefined
);
const usePrefillCardContextState = () => {
const [selectedPrefillCard, setSelectedPrefillCard] = useState<CardSchema | undefined>(
undefined,
);
const [prefillCard, setPrefillCard] = useState<CardSchema | undefined>(
undefined,
);
const [selectedPrefillCard, setSelectedPrefillCard] = useState<CardSchema | undefined>(undefined);
const [prefillCard, setPrefillCard] = useState<CardSchema | undefined>(undefined);
const [prefillOpened, { open, close }] = useDisclosure(false);
const prefillOnClose = close;
const prefillOnOpen = open;

View File

@@ -1,20 +1,10 @@
import { FC, useEffect } from "react";
import { Button, Drawer, Flex, rem, TextInput } from "@mantine/core";
import CardsTable from "./components/tables/CardsTable/CardsTable.tsx";
import Preview from "./components/Preview/Preview.tsx";
import styles from "./CardPrefillDrawer.module.css";
import BaseMarketplaceSelect from "../../../../components/Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
import usePrefillCard from "./hooks/usePrefillCard.tsx";
import { notifications } from "../../../../shared/lib/notifications.ts";
import { FC } from "react";
import { Drawer, rem } from "@mantine/core";
import { usePrefillCardContext } from "../../contexts/PrefillCardContext.tsx";
import CardPrefillDrawerContent from "./components/CardPrefillDrawerContent/CardPrefillDrawerContent.tsx";
const CardPrefillDrawer: FC = () => {
const { prefillOpened, prefillOnClose, selectedPrefillCard, setPrefillCard, prefillCard } = usePrefillCardContext();
const { data, form } = usePrefillCard();
useEffect(() => {
if (prefillOpened) return;
}, [prefillOpened]);
const { prefillOpened, prefillOnClose } = usePrefillCardContext();
return (
<Drawer
@@ -33,48 +23,7 @@ const CardPrefillDrawer: FC = () => {
},
}}
>
<div className={styles["card-container"]}>
<div className={styles["card-container-wrapper"]}>
<div className={styles["top-panel"]}>
<TextInput
placeholder={"Введите название / id"}
{...form.getInputProps("idOrName")}
/>
<BaseMarketplaceSelect
onClear={() =>
form.setFieldValue("marketplace", null)
}
clearable
placeholder={"Выберите маркетплейс"}
{...form.getInputProps("marketplace")}
/>
</div>
<CardsTable items={data} />
<Flex direction={"row"} gap="sm">
<Button mt={10} w={"100%"} onClick={() => {
if (!selectedPrefillCard) {
notifications.error({ message: "Карточка не выбрана." });
return;
}
setPrefillCard(selectedPrefillCard);
prefillOnClose();
}}>
Предзаполнить
</Button>
{
prefillCard &&
<Button mt={10} w={"100%"} variant={"outline"} onClick={() => {
setPrefillCard(undefined);
notifications.success({ message: "Предзаполнение отменено." });
prefillOnClose();
}}>
Отменить предзаполнение
</Button>
}
</Flex>
</div>
</div>
<Preview />
<CardPrefillDrawerContent />
</Drawer>
);
};

View File

@@ -0,0 +1,78 @@
import styles from "../../CardPrefillDrawer.module.css";
import { Button, Flex, TextInput } from "@mantine/core";
import BaseMarketplaceSelect
from "../../../../../../components/Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
import CardsTable from "../tables/CardsTable/CardsTable.tsx";
import { notifications } from "../../../../../../shared/lib/notifications.ts";
import Preview from "../Preview/Preview.tsx";
import usePrefillCard from "../../hooks/usePrefillCard.tsx";
import { usePrefillCardContext } from "../../../../contexts/PrefillCardContext.tsx";
const CardPrefillDrawerContent = () => {
const { prefillOnClose, selectedPrefillCard, setPrefillCard, prefillCard } = usePrefillCardContext();
const {
summaries,
idOrName,
setIdOrName,
marketplace,
setMarketplace,
totalPages,
page,
setPage,
} = usePrefillCard();
return (
<>
<div className={styles["card-container"]}>
<div className={styles["card-container-wrapper"]}>
<div className={styles["top-panel"]}>
<TextInput
placeholder={"Введите название / id"}
value={idOrName}
onChange={e => setIdOrName(e.target.value)}
/>
<BaseMarketplaceSelect
onClear={() => setMarketplace(null)}
clearable
placeholder={"Выберите маркетплейс"}
value={marketplace || undefined}
onChange={setMarketplace}
/>
</div>
<CardsTable
items={summaries}
totalPages={totalPages}
page={page}
setPage={setPage}
/>
<Flex direction={"row"} gap="sm">
<Button mt={10} w={"100%"} onClick={() => {
if (!selectedPrefillCard) {
notifications.error({ message: "Карточка не выбрана." });
return;
}
setPrefillCard(selectedPrefillCard);
prefillOnClose();
}}>
Предзаполнить
</Button>
{
prefillCard &&
<Button mt={10} w={"100%"} variant={"outline"} onClick={() => {
setPrefillCard(undefined);
notifications.success({ message: "Предзаполнение отменено." });
prefillOnClose();
}}>
Отменить предзаполнение
</Button>
}
</Flex>
</div>
</div>
<Preview />
</>
);
};
export default CardPrefillDrawerContent;

View File

@@ -1,47 +1,62 @@
import { FC, useEffect } from "react";
import { FC, useEffect, useState } from "react";
import useCardsTableColumns from "./columns.tsx";
import { CardSummary } from "../../../../../../../client";
import { BaseTable } from "../../../../../../../components/BaseTable/BaseTable.tsx";
import { usePrefillCardContext } from "../../../../../contexts/PrefillCardContext.tsx";
import { Group, Pagination, Stack } from "@mantine/core";
type Props = {
items: CardSummary[];
totalPages: number;
page: number;
setPage: (page: number) => void;
};
const CardsTable: FC<Props> = ({ items }) => {
const CardsTable: FC<Props> = ({ items, totalPages, page, setPage }) => {
const { selectPrefillCard } = usePrefillCardContext();
const columns = useCardsTableColumns();
const defaultSorting = [{ id: "createdAt", desc: false }];
const [isInitial, setIsInitial] = useState<boolean>(true);
useEffect(() => {
if (items.length < 1) return;
selectPrefillCard(items[0].id);
}, []);
if (isInitial && items.length > 0) {
selectPrefillCard(items[0].id);
setIsInitial(false);
}
}, [items]);
return (
<BaseTable
data={items}
columns={columns}
<Stack gap={"xs"}>
<BaseTable
data={items}
columns={columns}
restProps={
{
enableSorting: true,
enableColumnActions: false,
enablePagination: true,
enableBottomToolbar: true,
paginationDisplayMode: "pages",
initialState: {
sorting: defaultSorting,
},
mantinePaginationProps: {
showRowsPerPage: false,
},
enableStickyHeader: true,
enableStickyFooter: true,
mantineTableContainerProps: { style: { maxHeight: "81vh", height: "81vh" } },
restProps={
{
enableSorting: true,
enableColumnActions: false,
paginationDisplayMode: "pages",
initialState: {
sorting: defaultSorting,
},
mantinePaginationProps: {
showRowsPerPage: false,
},
enableStickyHeader: true,
enableStickyFooter: true,
mantineTableContainerProps: { style: { maxHeight: "81vh", height: "81vh" } },
}
}
}
/>
/>
<Group justify={"flex-end"}>
<Pagination
withEdges
total={totalPages}
value={page}
onChange={setPage}
/>
</Group>
</Stack>
);
};

View File

@@ -1,56 +1,58 @@
import { useForm } from "@mantine/form";
import { useEffect, useState } from "react";
import { useEffect, useRef, useState } from "react";
import { BaseMarketplaceSchema } from "../../../../../client";
import { useCardSummariesFull } from "../../../hooks/useCardSummaries.tsx";
import isModuleInProject from "../../../../../modules/utils/isModuleInProject.ts";
import { ModuleNames } from "../../../../../modules/modules.tsx";
import useCardSummaries from "../../../hooks/useCardSummaries.tsx";
import { useDebouncedValue } from "@mantine/hooks";
type State = {
idOrName: string | null;
marketplace: BaseMarketplaceSchema | null;
};
const usePrefillCard = () => {
const { objects } = useCardSummariesFull();
const form = useForm<State>({
initialValues: {
idOrName: null,
marketplace: null,
},
});
const [data, setData] = useState(objects);
const [idOrName, setIdOrName] = useState<string>("");
const [debouncedIdOrName] = useDebouncedValue(idOrName, 500);
const prevIdOrName = useRef(idOrName);
const [marketplace, setMarketplace] = useState<BaseMarketplaceSchema | null>(null);
const prevMarketplace = useRef(marketplace);
const applyFilters = () => {
let result = objects;
const getValues = () => {
let id: number | null = parseInt(debouncedIdOrName || "");
if (isNaN(id)) id = null;
result = result.filter(obj => isModuleInProject(ModuleNames.SERVICES_AND_PRODUCTS, obj.board.project));
if (form.values.idOrName) {
if (isNaN(parseInt(form.values.idOrName))) {
const name: string = form.values.idOrName.toLowerCase();
result = result.filter(
obj => obj.name.toLowerCase().search(name) !== -1,
);
} else {
const id = parseInt(form.values.idOrName);
result = result.filter(
obj => obj.id === id,
);
}
}
if (form.values.marketplace) {
result = result.filter(
obj => obj.baseMarketplace?.key === form.values.marketplace?.key,
);
}
setData(result);
return {
cardId: id,
cardName: id ? null : debouncedIdOrName,
marketplace,
};
};
useEffect(() => {
applyFilters();
}, [form.values, objects]);
const {
summaries,
fetchSummaries,
totalPages,
page,
setPage,
} = useCardSummaries({
full: true,
values: getValues(),
withPagination: true,
});
return { data, form };
useEffect(() => {
if (prevIdOrName.current !== debouncedIdOrName || prevMarketplace.current !== marketplace) {
prevIdOrName.current = debouncedIdOrName;
prevMarketplace.current = marketplace;
setPage(1);
}
fetchSummaries();
}, [debouncedIdOrName, marketplace, page]);
return {
summaries,
idOrName,
setIdOrName,
marketplace,
setMarketplace,
totalPages,
page,
setPage,
};
};
export default usePrefillCard;

View File

@@ -1,22 +1,50 @@
import { useQuery } from "@tanstack/react-query";
import { CardService } from "../../../client";
import ObjectList from "../../../hooks/objectList.tsx";
import { useState } from "react";
import { CardService, CardSummary } from "../../../client";
import { CardsPageState } from "./useCardsTableForm.tsx";
export const useCardSummaries = () => {
const { data: summariesRaw = [], refetch } = useQuery({
queryKey: ["getCardSummaries"],
queryFn: () => CardService.getCardSummaries({ full: false }),
select: data => data.summaries || [], // Трансформируем полученные данные
});
// Теперь summaries будет содержать либо трансформированные данные, либо пустой массив по умолчанию
// isLoading и isError могут быть использованы для отображения индикаторов загрузки или ошибки
type Props = {
full: boolean;
values?: CardsPageState;
withPagination?: boolean;
itemsPerPage?: number;
}
return { summariesRaw, refetch };
const useCardSummaries = ({ full, values, withPagination = false, itemsPerPage = 10 }: Props) => {
const [summaries, setSummaries] = useState<CardSummary[]>([]);
const [page, setPage] = useState(1);
const [totalPages, setTotalPages] = useState(0);
const fetchSummaries = () => {
CardService.getCardSummaries({
page: withPagination ? page : null,
itemsPerPage: withPagination ? itemsPerPage : null,
requestBody: {
full,
cardId: values?.cardId,
cardName: values?.cardName,
marketplaceKey: values?.marketplace?.key,
shippingWarehouseId: values?.shippingWarehouse?.id,
clientId: values?.client?.id,
projectId: values?.project?.id,
boardId: values?.board?.id,
statusId: values?.status?.id,
},
})
.then(({ summaries, paginationInfo }) => {
setSummaries(summaries);
setTotalPages(paginationInfo.totalPages);
})
.catch(err => console.log(err));
};
return {
summaries,
fetchSummaries,
page,
setPage,
totalPages,
};
};
export const useCardSummariesFull = () =>
ObjectList({
queryFn: () => CardService.getCardSummaries({ full: true }),
queryKey: "getCardSummariesFull",
getObjectsFn: response => response.summaries,
});
export default useCardSummaries;

View File

@@ -1,77 +0,0 @@
import { useCardSummariesFull } from "./useCardSummaries.tsx";
import { useForm } from "@mantine/form";
import { useEffect, useState } from "react";
import { BaseMarketplaceSchema, BoardSchema, ClientSchema, ProjectSchema, StatusSchema } from "../../../client";
export type CardsPageState = {
id: number | null;
marketplace: BaseMarketplaceSchema | null;
client: ClientSchema | null;
projectForTable: ProjectSchema | null;
board: BoardSchema | null;
status: StatusSchema | null;
};
const useCardsPageState = () => {
const { objects } = useCardSummariesFull();
const form = useForm<CardsPageState>({
initialValues: {
id: null,
marketplace: null,
client: null,
projectForTable: null,
board: null,
status: null,
},
});
const [data, setData] = useState(objects);
const applyFilters = () => {
let result = objects;
if (form.values.id) {
result = result.filter(
obj => obj.id === form.values.id,
);
}
if (form.values.marketplace) {
result = result.filter(
obj => obj.baseMarketplace?.key === form.values.marketplace?.key,
);
}
if (form.values.projectForTable) {
result = result.filter(
obj => obj.board.projectId === form.values.projectForTable?.id,
);
if (form.values.board) {
result = result.filter(
obj => obj.board.id === form.values.board?.id,
);
if (form.values.status) {
result = result.filter(
obj => obj.status.id === form.values.status?.id,
);
}
}
}
if (form.values.client) {
result = result.filter(
obj => obj.clientName === form.values.client?.name,
);
}
setData(result);
};
useEffect(() => {
applyFilters();
}, [form.values, objects]);
return { data, form };
};
export default useCardsPageState;

View File

@@ -0,0 +1,39 @@
import { useForm } from "@mantine/form";
import {
BaseMarketplaceSchema,
BoardSchema,
ClientSchema,
ProjectSchema,
ShippingWarehouseSchema,
StatusSchema,
} from "../../../client";
export type CardsPageState = {
cardId?: number | null;
cardName?: string | null;
marketplace?: BaseMarketplaceSchema | null;
shippingWarehouse?: ShippingWarehouseSchema | null;
client?: ClientSchema | null;
project?: ProjectSchema | null;
board?: BoardSchema | null;
status?: StatusSchema | null;
};
const useCardsTableForm = () => {
const form = useForm<CardsPageState>({
initialValues: {
cardId: null,
marketplace: null,
client: null,
project: null,
board: null,
status: null,
},
});
return { form };
};
export default useCardsTableForm;

View File

@@ -1,7 +1,7 @@
import { ProjectSchema } from "../../../client";
import { Flex, Modal, NumberInput, rem } from "@mantine/core";
import { UseFormReturnType } from "@mantine/form";
import { CardsPageState } from "../hooks/useCardsPageState.tsx";
import { CardsPageState } from "../hooks/useCardsTableForm.tsx";
import ObjectSelect from "../../../components/ObjectSelect/ObjectSelect.tsx";
import CardStatusSelect from "../../../components/CardStatusSelect/CardStatusSelect.tsx";
import BaseMarketplaceSelect from "../../../components/Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
@@ -33,7 +33,7 @@ const CardsTableFiltersModal = ({ form, projects }: Props) => {
<NumberInput
min={1}
placeholder={"Введите номер"}
{...form.getInputProps("id")}
{...form.getInputProps("cardId")}
hideControls
/>
<ObjectSelect
@@ -41,16 +41,16 @@ const CardsTableFiltersModal = ({ form, projects }: Props) => {
data={projects}
clearable
searchable
{...form.getInputProps("projectForTable")}
onClear={() => form.setFieldValue("projectForTable", null)}
{...form.getInputProps("project")}
onClear={() => form.setFieldValue("project", null)}
/>
<BoardSelect
project={form.values.projectForTable}
project={form.values.project || null}
{...form.getInputProps("board")}
clearable
/>
<CardStatusSelect
board={form.values.board}
board={form.values.board || null}
{...form.getInputProps("status")}
clearable
/>

View File

@@ -1,10 +1,9 @@
import { FC, useMemo, useState } from "react";
import { useCardSummaries } from "../hooks/useCardSummaries.tsx";
import { FC, useEffect, useMemo, useState } from "react";
import PageBlock from "../../../components/PageBlock/PageBlock.tsx";
import CardEditDrawer from "../drawers/CardEditDrawer/CardEditDrawer.tsx";
import { CardPageContextProvider } from "../contexts/CardPageContext.tsx";
import { rem } from "@mantine/core";
import useCardsPageState from "../hooks/useCardsPageState.tsx";
import useCardsTableForm from "../hooks/useCardsTableForm.tsx";
import CardsTable from "../components/CardsTable/CardsTable.tsx";
import { motion } from "framer-motion";
import CardPrefillDrawer from "../drawers/CardPrefillDrawer/CardPrefillDrawer.tsx";
@@ -17,30 +16,35 @@ import { ProjectsEditorContextProvider } from "../contexts/ProjectsEditorContext
import ProjectEditDrawer from "../drawers/ProjectEditDrawer/ProjectEditDrawer.tsx";
import Boards from "../../../components/Dnd/Boards/Boards/Boards.tsx";
import { DndContextProvider } from "../contexts/DndContext.tsx";
import useCardSummaries from "../hooks/useCardSummaries.tsx";
export const CardsPage: FC = () => {
const { data, form } = useCardsPageState();
const { form } = useCardsTableForm();
const { dealId } = useParams({ strict: false });
const { summariesRaw, refetch: refetchSummaries } = useCardSummaries();
const { summaries, fetchSummaries } = useCardSummaries({ full: false });
const [displayMode, setDisplayMode] = useState<DisplayMode>(DisplayMode.BOARD);
useEffect(() => {
fetchSummaries();
}, []);
const tableBody = useMemo(() => {
return (
<CardsTable items={data} />
<CardsTable form={form} />
);
}, [data]);
}, [form]);
const boardsBody = useMemo(() => {
return (
<DndContextProvider
summariesRaw={summariesRaw}
refetchSummaries={refetchSummaries}
summariesRaw={summaries}
refetchSummaries={fetchSummaries}
>
<Boards />
</DndContextProvider>
);
}, [summariesRaw]);
}, [summaries]);
const getBody = () => {
return (
@@ -72,9 +76,7 @@ export const CardsPage: FC = () => {
<ProjectsEditorContextProvider>
<CardPageContextProvider
defaultCardId={(dealId && parseInt(dealId)) || undefined}
refetchCards={async () => {
await refetchSummaries();
}}
refetchCards={fetchSummaries}
>
<PrefillCardContextProvider>
<PrefillCardsWithExcelContextProvider>

View File

@@ -0,0 +1,47 @@
import { CRUDTableProps } from "../../../../types/CRUDTable.tsx";
import { CardSummary, ShippingWarehouseSchema } from "../../../../client";
import { FC, useEffect } from "react";
import { BaseTable } from "../../../../components/BaseTable/BaseTable.tsx";
import { MRT_TableOptions } from "mantine-react-table";
import useCardsTablePreviewColumns from "./columns.tsx";
import useCardSummaries from "../../../CardsPage/hooks/useCardSummaries.tsx";
type RestProps = {
viewOnly?: boolean;
shippingWarehouse: ShippingWarehouseSchema;
};
type Props = Omit<CRUDTableProps<CardSummary>, "items"> & RestProps;
const CardsTablePreview: FC<Props> = ({ shippingWarehouse, onSelectionChange }) => {
const columns = useCardsTablePreviewColumns();
const { summaries, fetchSummaries } = useCardSummaries({
full: true,
values: {
shippingWarehouse,
},
withPagination: true,
itemsPerPage: 5,
});
useEffect(() => {
fetchSummaries();
}, []);
return (
<BaseTable
data={summaries}
columns={columns}
onSelectionChange={onSelectionChange}
restProps={
{
enableSorting: true,
enableColumnActions: false,
paginationDisplayMode: "pages",
} as MRT_TableOptions<CardSummary>
}
/>
);
};
export default CardsTablePreview;

View File

@@ -0,0 +1,56 @@
import { useMemo } from "react";
import { MRT_ColumnDef } from "mantine-react-table";
import { CardSummary } from "../../../../client";
import { ActionIcon, Image } from "@mantine/core";
const useCardsTablePreviewColumns = () => {
return useMemo<MRT_ColumnDef<CardSummary>[]>(
() => [
{
accessorKey: "id",
header: "Номер",
size: 20,
},
{
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,
},
{
header: "Общая стоимость",
Cell: ({ row }) =>
row.original.totalPrice.toLocaleString("ru-RU") + "₽",
accessorKey: "totalPrice",
},
],
[],
);
};
export default useCardsTablePreviewColumns;

View File

@@ -3,9 +3,7 @@ import { CardSummary, ShippingWarehouseSchema } from "../../../client";
import { ContextModalProps } from "@mantine/modals";
import { useForm } from "@mantine/form";
import { Input, TextInput } from "@mantine/core";
import CardsTable from "../../CardsPage/components/CardsTable/CardsTable.tsx";
import { CardPageContextProvider } from "../../CardsPage/contexts/CardPageContext.tsx";
import CardEditDrawer from "../../CardsPage/drawers/CardEditDrawer/CardEditDrawer.tsx";
import CardsTablePreview from "../components/CardsTablePreview/CardsTablePreview.tsx";
type RestProps = {
summaries: CardSummary[];
@@ -27,34 +25,28 @@ const ShippingWarehouseForm = ({
},
});
return (
<CardPageContextProvider refetchCards={async () => {}}>
<BaseFormModal
{...innerProps}
closeOnSubmit
form={form}
onClose={() => context.closeContextModal(id)}>
<BaseFormModal.Body>
<>
<TextInput
label={"Склад отгрузки"}
placeholder={"Введите название склада отгрузки"}
{...form.getInputProps("name")}
/>
{isEditing && (
<Input.Wrapper
label={"Последние 5 сделок"}
error={form.getInputProps("phoneNumber").error}>
<CardsTable
viewOnly={true}
items={innerProps.summaries}
/>
</Input.Wrapper>
)}
</>
</BaseFormModal.Body>
</BaseFormModal>
<CardEditDrawer />
</CardPageContextProvider>
<BaseFormModal
{...innerProps}
closeOnSubmit
form={form}
onClose={() => context.closeContextModal(id)}>
<BaseFormModal.Body>
<>
<TextInput
label={"Склад отгрузки"}
placeholder={"Введите название склада отгрузки"}
{...form.getInputProps("name")}
/>
{isEditing && (
<Input.Wrapper
label={"Последние 5 сделок"}
error={form.getInputProps("phoneNumber").error}>
<CardsTablePreview shippingWarehouse={form.values} />
</Input.Wrapper>
)}
</>
</BaseFormModal.Body>
</BaseFormModal>
);
};

View File

@@ -1,20 +1,23 @@
import styles from "../../ServicesPage/ui/ServicesPage.module.css";
import PageBlock from "../../../components/PageBlock/PageBlock.tsx";
import ShippingWarehousesTable from "../components/ShippingWarehousesTable/ShippingWarehousesTable.tsx";
import useShippingWarehousesList from "../../../components/Selects/ShippingWarehouseAutocomplete/hooks/useShippingWarehousesList.tsx";
import useShippingWarehousesList
from "../../../components/Selects/ShippingWarehouseAutocomplete/hooks/useShippingWarehousesList.tsx";
import { Button } from "@mantine/core";
import { modals } from "@mantine/modals";
import { useCRUD } from "../../../hooks/useCRUD.tsx";
import {
ShippingWarehouseSchema,
ShippingWarehouseService,
} from "../../../client";
import { ShippingWarehouseSchema, ShippingWarehouseService } from "../../../client";
import { notifications } from "../../../shared/lib/notifications.ts";
import { useCardSummariesFull } from "../../CardsPage/hooks/useCardSummaries.tsx";
import useCardSummaries from "../../CardsPage/hooks/useCardSummaries.tsx";
export const ShippingWarehousesPage = () => {
const { shippingWarehouses, refetch } = useShippingWarehousesList();
const { objects: summaries } = useCardSummariesFull();
const { summaries } = useCardSummaries({
full: true,
withPagination: true,
values: {},
});
const crud = useCRUD<ShippingWarehouseSchema>({
onChange: element => {
ShippingWarehouseService.updateShippingWarehouse({