feat: cards, attributes and modules
This commit is contained in:
1
src/pages/CardPage/index.ts
Normal file
1
src/pages/CardPage/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { CardPage } from "./ui/CardPage.tsx";
|
||||
38
src/pages/CardPage/ui/CardPage.tsx
Normal file
38
src/pages/CardPage/ui/CardPage.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import { useParams } from "@tanstack/react-router";
|
||||
import { CardPageContextProvider, useCardPageContext } from "../../CardsPage/contexts/CardPageContext.tsx";
|
||||
import ProductAndServiceTab from "../../CardsPage/tabs/ProductAndServiceTab/ProductAndServiceTab.tsx";
|
||||
import React, { FC, useEffect } from "react";
|
||||
import { CardService } from "../../../client";
|
||||
|
||||
export type Props = {
|
||||
cardId: number;
|
||||
};
|
||||
const CardPageContent: FC<Props> = ({ cardId }) => {
|
||||
const { setSelectedCard } = useCardPageContext();
|
||||
useEffect(() => {
|
||||
CardService.getCardById({ cardId }).then(card => {
|
||||
setSelectedCard(card);
|
||||
});
|
||||
}, []);
|
||||
return <ProductAndServiceTab />;
|
||||
};
|
||||
|
||||
const CardPageWrapper: FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
return (
|
||||
<CardPageContextProvider
|
||||
refetchCards={async () => {
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</CardPageContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export const CardPage = () => {
|
||||
const { dealId } = useParams({ strict: false });
|
||||
return (
|
||||
<CardPageWrapper>
|
||||
<CardPageContent cardId={parseInt(dealId || "-1")} />
|
||||
</CardPageWrapper>
|
||||
);
|
||||
};
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
rem,
|
||||
} from "@mantine/core";
|
||||
import { BaseFormInputProps } from "../../../../types/utils.ts";
|
||||
import { DealProductServiceSchema, ServiceSchema } from "../../../../client";
|
||||
import { CardProductServiceSchema, ServiceSchema } from "../../../../client";
|
||||
import { FC, useEffect, useState } from "react";
|
||||
import ServiceWithPriceInput from "../../../../components/ServiceWithPriceInput/ServiceWithPriceInput.tsx";
|
||||
import { isNumber } from "lodash";
|
||||
@@ -21,13 +21,13 @@ import { RootState } from "../../../../redux/store.ts";
|
||||
type RestProps = {
|
||||
quantity: number;
|
||||
};
|
||||
type Props = BaseFormInputProps<DealProductServiceSchema[]> & RestProps;
|
||||
type Props = BaseFormInputProps<CardProductServiceSchema[]> & RestProps;
|
||||
const DealProductServiceTable: FC<Props> = (props: Props) => {
|
||||
const { value, onChange, quantity, error } = props;
|
||||
const authState = useSelector((state: RootState) => state.auth);
|
||||
|
||||
const [innerValue, setInnerValue] = useState<
|
||||
Partial<DealProductServiceSchema>[]
|
||||
Partial<CardProductServiceSchema>[]
|
||||
>(value || []);
|
||||
const onServiceChange = (idx: number, value: ServiceSchema) => {
|
||||
setInnerValue(oldValue =>
|
||||
@@ -79,7 +79,7 @@ const DealProductServiceTable: FC<Props> = (props: Props) => {
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
onChange(innerValue as DealProductServiceSchema[]);
|
||||
onChange(innerValue as CardProductServiceSchema[]);
|
||||
}, [innerValue]);
|
||||
return (
|
||||
<Input.Wrapper error={error}>
|
||||
@@ -2,7 +2,7 @@ import { BaseTable } from "../../../../components/BaseTable/BaseTable.tsx";
|
||||
import useDealProductsTableColumns from "./columns.tsx";
|
||||
import { FC } from "react";
|
||||
import { CRUDTableProps } from "../../../../types/CRUDTable.tsx";
|
||||
import { DealProductSchema, ProductService } from "../../../../client";
|
||||
import { CardProductSchema, ProductService } from "../../../../client";
|
||||
import { ActionIcon, Button, Flex, rem, Tooltip } from "@mantine/core";
|
||||
import { MRT_TableOptions } from "mantine-react-table";
|
||||
import { modals } from "@mantine/modals";
|
||||
@@ -12,9 +12,9 @@ import { CreateProductRequest } from "../../../ProductsPage/types.ts";
|
||||
|
||||
type RestProps = {
|
||||
clientId: number;
|
||||
onMultipleDelete?: (items: DealProductSchema[]) => void;
|
||||
onMultipleDelete?: (items: CardProductSchema[]) => void;
|
||||
};
|
||||
type Props = CRUDTableProps<DealProductSchema> & RestProps;
|
||||
type Props = CRUDTableProps<CardProductSchema> & RestProps;
|
||||
const DealProductsTable: FC<Props> = (props: Props) => {
|
||||
const { items, clientId, onChange, onCreate, onDelete, onMultipleDelete } =
|
||||
props;
|
||||
@@ -34,16 +34,16 @@ const DealProductsTable: FC<Props> = (props: Props) => {
|
||||
const onCreateClick = () => {
|
||||
if (!onCreate) return;
|
||||
modals.openContextModal({
|
||||
modal: "addDealProduct",
|
||||
modal: "addCardProduct",
|
||||
title: "Добавление товара",
|
||||
innerProps: {
|
||||
onCreate: product => onCreate(product as DealProductSchema),
|
||||
onCreate: product => onCreate(product as CardProductSchema),
|
||||
clientId,
|
||||
},
|
||||
size: "lg",
|
||||
});
|
||||
};
|
||||
const onPrintBarcodeClick = (product: DealProductSchema) => {
|
||||
const onPrintBarcodeClick = (product: CardProductSchema) => {
|
||||
modals.openContextModal({
|
||||
modal: "printBarcode",
|
||||
title: "Печать штрихкода",
|
||||
@@ -72,10 +72,10 @@ const DealProductsTable: FC<Props> = (props: Props) => {
|
||||
},
|
||||
});
|
||||
};
|
||||
const onEditClick = (product: DealProductSchema) => {
|
||||
const onEditClick = (product: CardProductSchema) => {
|
||||
if (!onChange) return;
|
||||
modals.openContextModal({
|
||||
modal: "addDealProduct",
|
||||
modal: "addCardProduct",
|
||||
title: "Создание товара",
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
@@ -159,7 +159,7 @@ const DealProductsTable: FC<Props> = (props: Props) => {
|
||||
</Button>
|
||||
</Flex>
|
||||
),
|
||||
} as MRT_TableOptions<DealProductSchema>
|
||||
} as MRT_TableOptions<CardProductSchema>
|
||||
}
|
||||
/>
|
||||
);
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useMemo } from "react";
|
||||
import { MRT_ColumnDef } from "mantine-react-table";
|
||||
import { DealProductSchema } from "../../../../client";
|
||||
import { CardProductSchema } from "../../../../client";
|
||||
import { List } from "@mantine/core";
|
||||
|
||||
type Props = {
|
||||
onChange: (product: DealProductSchema, quantity: number) => void;
|
||||
data: DealProductSchema[];
|
||||
onChange: (product: CardProductSchema, quantity: number) => void;
|
||||
data: CardProductSchema[];
|
||||
};
|
||||
const useDealProductsTableColumns = (props: Props) => {
|
||||
const { onChange, data } = props;
|
||||
@@ -27,7 +27,7 @@ const useDealProductsTableColumns = (props: Props) => {
|
||||
),
|
||||
[data]
|
||||
);
|
||||
return useMemo<MRT_ColumnDef<DealProductSchema>[]>(
|
||||
return useMemo<MRT_ColumnDef<CardProductSchema>[]>(
|
||||
() => [
|
||||
{
|
||||
accessorKey: "product.article",
|
||||
@@ -1,7 +1,7 @@
|
||||
import { FC } from "react";
|
||||
import { useDealServicesTableColumns } from "./columns.tsx";
|
||||
import { BaseTable } from "../../../../components/BaseTable/BaseTable.tsx";
|
||||
import { DealServiceSchema } from "../../../../client";
|
||||
import { CardServiceSchema } from "../../../../client";
|
||||
import { CRUDTableProps } from "../../../../types/CRUDTable.tsx";
|
||||
import { MRT_TableOptions } from "mantine-react-table";
|
||||
import { ActionIcon, Button, Flex, rem, Tooltip } from "@mantine/core";
|
||||
@@ -9,9 +9,9 @@ import { openContextModal } from "@mantine/modals";
|
||||
import { IconEdit, IconTrash } from "@tabler/icons-react";
|
||||
|
||||
type RestProps = {
|
||||
onMultipleDelete?: (items: DealServiceSchema[]) => void;
|
||||
onMultipleDelete?: (items: CardServiceSchema[]) => void;
|
||||
};
|
||||
type Props = CRUDTableProps<DealServiceSchema> & RestProps;
|
||||
type Props = CRUDTableProps<CardServiceSchema> & RestProps;
|
||||
const DealServicesTable: FC<Props> = ({
|
||||
items,
|
||||
onChange,
|
||||
@@ -30,18 +30,18 @@ const DealServicesTable: FC<Props> = ({
|
||||
if (!onCreate) return;
|
||||
openContextModal({
|
||||
title: "Добавление услуги",
|
||||
modal: "addDealService",
|
||||
modal: "addCardService",
|
||||
innerProps: {
|
||||
onCreate: event => onCreate(event as DealServiceSchema),
|
||||
onCreate: event => onCreate(event as CardServiceSchema),
|
||||
serviceIds,
|
||||
},
|
||||
});
|
||||
};
|
||||
const onEditClick = (service: DealServiceSchema) => {
|
||||
const onEditClick = (service: CardServiceSchema) => {
|
||||
if (!onChange) return;
|
||||
openContextModal({
|
||||
title: "Добавление услуги",
|
||||
modal: "addDealService",
|
||||
modal: "addCardService",
|
||||
innerProps: {
|
||||
element: service,
|
||||
onChange,
|
||||
@@ -119,7 +119,7 @@ const DealServicesTable: FC<Props> = ({
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
),
|
||||
} as MRT_TableOptions<DealServiceSchema>
|
||||
} as MRT_TableOptions<CardServiceSchema>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
@@ -1,9 +1,9 @@
|
||||
import { MRT_ColumnDef } from "mantine-react-table";
|
||||
import { useMemo } from "react";
|
||||
import { DealServiceSchema } from "../../../../client";
|
||||
import { CardServiceSchema } from "../../../../client";
|
||||
|
||||
type Props = {
|
||||
data: DealServiceSchema[];
|
||||
data: CardServiceSchema[];
|
||||
};
|
||||
|
||||
export const useDealServicesTableColumns = (props: Props) => {
|
||||
@@ -13,7 +13,7 @@ export const useDealServicesTableColumns = (props: Props) => {
|
||||
[data]
|
||||
);
|
||||
|
||||
return useMemo<MRT_ColumnDef<DealServiceSchema>[]>(
|
||||
return useMemo<MRT_ColumnDef<CardServiceSchema>[]>(
|
||||
() => [
|
||||
{
|
||||
accessorKey: "service.category",
|
||||
@@ -1,13 +1,13 @@
|
||||
import { DealStatusHistorySchema } from "../../../../client";
|
||||
import { CardStatusHistorySchema } from "../../../../client";
|
||||
import { useDealStatusChangeTableColumns } from "./columns.tsx";
|
||||
import { BaseTable } from "../../../../components/BaseTable/BaseTable.tsx";
|
||||
import { FC } from "react";
|
||||
|
||||
type Props = {
|
||||
items: DealStatusHistorySchema[];
|
||||
items: CardStatusHistorySchema[];
|
||||
};
|
||||
|
||||
const DealStatusChangeTable: FC<Props> = (props: Props) => {
|
||||
const CardStatusChangeTable: FC<Props> = (props: Props) => {
|
||||
const { items } = props;
|
||||
|
||||
return (
|
||||
@@ -26,4 +26,4 @@ const DealStatusChangeTable: FC<Props> = (props: Props) => {
|
||||
/>
|
||||
);
|
||||
};
|
||||
export default DealStatusChangeTable;
|
||||
export default CardStatusChangeTable;
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useMemo } from "react";
|
||||
import { MRT_ColumnDef } from "mantine-react-table";
|
||||
import { DealStatusHistorySchema } from "../../../../client";
|
||||
import { CardStatusHistorySchema } from "../../../../client";
|
||||
import { Spoiler, Text } from "@mantine/core";
|
||||
|
||||
export const useDealStatusChangeTableColumns = () => {
|
||||
return useMemo<MRT_ColumnDef<DealStatusHistorySchema>[]>(
|
||||
return useMemo<MRT_ColumnDef<CardStatusHistorySchema>[]>(
|
||||
() => [
|
||||
{
|
||||
accessorKey: "changedAt",
|
||||
@@ -1,24 +1,24 @@
|
||||
import { CRUDTableProps } from "../../../../types/CRUDTable.tsx";
|
||||
import { DealService, DealSummary } from "../../../../client";
|
||||
import { CardService, CardSummary } from "../../../../client";
|
||||
import { FC } from "react";
|
||||
import useDealsTableColumns from "./columns.tsx";
|
||||
import useCardsTableColumns 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 "../../contexts/DealPageContext.tsx";
|
||||
import { useCardPageContext } from "../../contexts/CardPageContext.tsx";
|
||||
|
||||
type RestProps = {
|
||||
viewOnly?: boolean;
|
||||
};
|
||||
type Props = CRUDTableProps<DealSummary> & RestProps;
|
||||
type Props = CRUDTableProps<CardSummary> & RestProps;
|
||||
|
||||
const DealsTable: FC<Props> = ({ items, onSelectionChange, viewOnly = false }) => {
|
||||
const columns = useDealsTableColumns();
|
||||
const { setSelectedDeal } = useDealPageContext();
|
||||
const onEditClick = (dealSummary: DealSummary) => {
|
||||
DealService.getDealById({ dealId: dealSummary.id }).then(deal => {
|
||||
setSelectedDeal(deal);
|
||||
const CardsTable: FC<Props> = ({ items, onSelectionChange, viewOnly = false }) => {
|
||||
const columns = useCardsTableColumns();
|
||||
const { setSelectedCard } = useCardPageContext();
|
||||
const onEditClick = (cardSummary: CardSummary) => {
|
||||
CardService.getCardById({ cardId: cardSummary.id }).then(card => {
|
||||
setSelectedCard(card);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -48,10 +48,10 @@ const DealsTable: FC<Props> = ({ items, onSelectionChange, viewOnly = false }) =
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
),
|
||||
} as MRT_TableOptions<DealSummary>
|
||||
} as MRT_TableOptions<CardSummary>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default DealsTable;
|
||||
export default CardsTable;
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useMemo } from "react";
|
||||
import { MRT_ColumnDef } from "mantine-react-table";
|
||||
import { DealSummary } from "../../../../client";
|
||||
import { CardSummary } from "../../../../client";
|
||||
import { ActionIcon, Image } from "@mantine/core";
|
||||
|
||||
const useDealsTableColumns = () => {
|
||||
return useMemo<MRT_ColumnDef<DealSummary>[]>(
|
||||
const useCardsTableColumns = () => {
|
||||
return useMemo<MRT_ColumnDef<CardSummary>[]>(
|
||||
() => [
|
||||
{
|
||||
accessorKey: "id",
|
||||
@@ -53,4 +53,4 @@ const useDealsTableColumns = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default useDealsTableColumns;
|
||||
export default useCardsTableColumns;
|
||||
@@ -1,21 +1,21 @@
|
||||
import { ActionIcon, Flex, rem, Text } from "@mantine/core";
|
||||
import { IconEdit, IconMenu2, IconMenuDeep } from "@tabler/icons-react";
|
||||
import { motion } from "framer-motion";
|
||||
import styles from "../../ui/DealsPage.module.css";
|
||||
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 { DealsPageState } from "../../hooks/useDealsPageState.tsx";
|
||||
import { CardsPageState } from "../../hooks/useCardsPageState.tsx";
|
||||
import React from "react";
|
||||
import { ProjectSchema } from "../../../../client";
|
||||
import { modals } from "@mantine/modals";
|
||||
import ObjectSelect from "../../../../components/ObjectSelect/ObjectSelect.tsx";
|
||||
import DealsTableFiltersModal from "../../modals/DealsTableFiltersModal.tsx";
|
||||
import CardsTableFiltersModal from "../../modals/CardsTableFiltersModal.tsx";
|
||||
|
||||
type Props = {
|
||||
displayMode: DisplayMode;
|
||||
setDisplayMode: React.Dispatch<React.SetStateAction<DisplayMode>>;
|
||||
form: UseFormReturnType<DealsPageState>;
|
||||
form: UseFormReturnType<CardsPageState>;
|
||||
projects: ProjectSchema[];
|
||||
refetchProjects: () => void;
|
||||
}
|
||||
@@ -66,7 +66,7 @@ const LeadsPageHeader = ({
|
||||
display: displayMode === DisplayMode.TABLE ? "flex" : "none",
|
||||
}}
|
||||
>
|
||||
<DealsTableFiltersModal
|
||||
<CardsTableFiltersModal
|
||||
form={form}
|
||||
projects={projects}
|
||||
/>
|
||||
75
src/pages/CardsPage/contexts/CardPageContext.tsx
Normal file
75
src/pages/CardsPage/contexts/CardPageContext.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import React, { createContext, FC, useContext, useEffect, useState } from "react";
|
||||
import { CardSchema, CardService, ProjectSchema } from "../../../client";
|
||||
|
||||
type CardPageContextState = {
|
||||
selectedCard?: CardSchema;
|
||||
setSelectedCard: (card: CardSchema | undefined) => void;
|
||||
refetchCards: () => Promise<void>;
|
||||
refetchCard: () => void;
|
||||
selectedProject?: ProjectSchema | null;
|
||||
};
|
||||
|
||||
const CardPageContext = createContext<CardPageContextState | undefined>(
|
||||
undefined,
|
||||
);
|
||||
|
||||
type CardPageContextStateProps = {
|
||||
refetchCards: () => Promise<void>;
|
||||
defaultCardId?: number;
|
||||
selectedProject?: ProjectSchema | null;
|
||||
}
|
||||
|
||||
const useCardPageContextState = (props: CardPageContextStateProps) => {
|
||||
const { refetchCards, defaultCardId } = props;
|
||||
const [selectedCard, setSelectedCard] = useState<CardSchema | undefined>(
|
||||
undefined,
|
||||
);
|
||||
|
||||
const refetchCard = () => {
|
||||
const cardId = selectedCard?.id ?? defaultCardId;
|
||||
if (!cardId) return;
|
||||
|
||||
CardService.getCardById({ cardId }).then(card => {
|
||||
setSelectedCard(card);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
refetchCard();
|
||||
}, []);
|
||||
|
||||
return {
|
||||
selectedCard,
|
||||
setSelectedCard,
|
||||
selectedProject: props.selectedProject,
|
||||
refetchCards,
|
||||
refetchCard,
|
||||
};
|
||||
};
|
||||
|
||||
type CardPageContextProviderProps = {
|
||||
children: React.ReactNode;
|
||||
} & CardPageContextStateProps;
|
||||
|
||||
export const CardPageContextProvider: FC<CardPageContextProviderProps> = ({
|
||||
children,
|
||||
...props
|
||||
}) => {
|
||||
const state = useCardPageContextState(props);
|
||||
|
||||
return (
|
||||
<CardPageContext.Provider value={state}>
|
||||
{children}
|
||||
</CardPageContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useCardPageContext = () => {
|
||||
const context = useContext(CardPageContext);
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
"useCardPageContext must be used within a CardPageContextProvider",
|
||||
);
|
||||
}
|
||||
return context;
|
||||
};
|
||||
71
src/pages/CardsPage/contexts/PrefillCardContext.tsx
Normal file
71
src/pages/CardsPage/contexts/PrefillCardContext.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import { createContext, Dispatch, FC, SetStateAction, useContext, useState } from "react";
|
||||
import { CardSchema, CardService } from "../../../client";
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
|
||||
type PrefillCardContextState = {
|
||||
prefillOpened: boolean;
|
||||
prefillOnClose: () => void;
|
||||
prefillOnOpen: () => void;
|
||||
|
||||
selectedPrefillCard?: CardSchema;
|
||||
selectPrefillCard: (cardId: number) => void;
|
||||
prefillCard?: CardSchema;
|
||||
setPrefillCard: Dispatch<SetStateAction<CardSchema | undefined>>;
|
||||
};
|
||||
|
||||
const PrefillCardContext = createContext<PrefillCardContextState | undefined>(
|
||||
undefined
|
||||
);
|
||||
const usePrefillCardContextState = () => {
|
||||
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;
|
||||
|
||||
const selectPrefillCard = (cardId: number) => {
|
||||
CardService.getCardById({ cardId }).then(card => {
|
||||
setSelectedPrefillCard(card);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
prefillOpened,
|
||||
prefillOnClose,
|
||||
prefillOnOpen,
|
||||
|
||||
selectedPrefillCard,
|
||||
selectPrefillCard,
|
||||
prefillCard,
|
||||
setPrefillCard,
|
||||
};
|
||||
};
|
||||
|
||||
type PrefillCardContextProviderProps = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export const PrefillCardContextProvider: FC<PrefillCardContextProviderProps> = ({
|
||||
children,
|
||||
}) => {
|
||||
const state = usePrefillCardContextState();
|
||||
return (
|
||||
<PrefillCardContext.Provider value={state}>
|
||||
{children}
|
||||
</PrefillCardContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const usePrefillCardContext = () => {
|
||||
const context = useContext(PrefillCardContext);
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
"usePrefillCardContext must be used within a PrefillCardContextProvider"
|
||||
);
|
||||
}
|
||||
return context;
|
||||
};
|
||||
@@ -1,14 +1,14 @@
|
||||
import { createContext, FC, useContext, useState } from "react";
|
||||
import React, { createContext, FC, useContext, useState } from "react";
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
import { DealsWithExcelForm, ProductExcelData } from "../drawers/PrefillDealWithExcelDrawer/types.tsx";
|
||||
import { CardsWithExcelForm, ProductExcelData } from "../drawers/PrefillCardWithExcelDrawer/types.tsx";
|
||||
import { FileWithPath } from "@mantine/dropzone";
|
||||
import { notifications } from "../../../shared/lib/notifications.ts";
|
||||
import { DealService, type ProductFromExcelSchema, ProductSchema, StatusSchema } from "../../../client";
|
||||
import { CardService, type ProductFromExcelSchema, ProductSchema, StatusSchema } from "../../../client";
|
||||
import UseExcelDropzone from "../../../types/UseExcelDropzone.tsx";
|
||||
import { useForm, UseFormReturnType } from "@mantine/form";
|
||||
import { useDealPageContext } from "./DealPageContext.tsx";
|
||||
import { useCardPageContext } from "./CardPageContext.tsx";
|
||||
|
||||
type PrefillDealsWithExcelContextState = {
|
||||
type PrefillCardsWithExcelContextState = {
|
||||
prefillWithExcelOpened: boolean;
|
||||
prefillWithExcelOnClose: () => void;
|
||||
prefillWithExcelOnOpen: () => void;
|
||||
@@ -16,18 +16,18 @@ type PrefillDealsWithExcelContextState = {
|
||||
onProductSelectChange: (barcode: string, selectedProduct: ProductSchema) => void,
|
||||
onDrop: (files: FileWithPath[]) => void;
|
||||
excelDropzone: UseExcelDropzone;
|
||||
createDeals: (values: DealsWithExcelForm, status: StatusSchema) => void;
|
||||
form: UseFormReturnType<DealsWithExcelForm>;
|
||||
createCards: (values: CardsWithExcelForm, status: StatusSchema) => void;
|
||||
form: UseFormReturnType<CardsWithExcelForm>;
|
||||
errors: string[];
|
||||
};
|
||||
|
||||
const PrefillDealsWithExcelContext = createContext<PrefillDealsWithExcelContextState | undefined>(
|
||||
const PrefillCardsWithExcelContext = createContext<PrefillCardsWithExcelContextState | undefined>(
|
||||
undefined,
|
||||
);
|
||||
|
||||
const usePrefillDealsWithExcelContextState = () => {
|
||||
const usePrefillCardsWithExcelContextState = () => {
|
||||
const [prefillWithExcelOpened, { open, close }] = useDisclosure(false);
|
||||
const { refetchDeals } = useDealPageContext();
|
||||
const { refetchCards } = useCardPageContext();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [errors, setErrors] = useState<string[]>([]);
|
||||
const excelDropzone: UseExcelDropzone = {
|
||||
@@ -35,7 +35,7 @@ const usePrefillDealsWithExcelContextState = () => {
|
||||
setIsLoading,
|
||||
};
|
||||
|
||||
const form = useForm<DealsWithExcelForm>({
|
||||
const form = useForm<CardsWithExcelForm>({
|
||||
validate: {
|
||||
client: client => !client && "Выберите клиента",
|
||||
},
|
||||
@@ -51,7 +51,7 @@ const usePrefillDealsWithExcelContextState = () => {
|
||||
const file = files[0];
|
||||
|
||||
setIsLoading(true);
|
||||
DealService.parseDealsExcel({
|
||||
CardService.parseDealsExcel({
|
||||
formData: {
|
||||
upload_file: file,
|
||||
},
|
||||
@@ -91,7 +91,7 @@ const usePrefillDealsWithExcelContextState = () => {
|
||||
form.reset();
|
||||
};
|
||||
|
||||
const createDeals = (values: DealsWithExcelForm, status: StatusSchema) => {
|
||||
const createCards = (values: CardsWithExcelForm, status: StatusSchema) => {
|
||||
const products: ProductFromExcelSchema[] = barcodeProductsMap.entries().map(([, productData]) => {
|
||||
return {
|
||||
productId: productData.selectedProduct!.id,
|
||||
@@ -99,7 +99,7 @@ const usePrefillDealsWithExcelContextState = () => {
|
||||
};
|
||||
}).toArray();
|
||||
|
||||
DealService.createDealsExcel({
|
||||
CardService.createDealsExcel({
|
||||
requestBody: {
|
||||
products,
|
||||
clientId: values.client?.id ?? -1,
|
||||
@@ -109,7 +109,7 @@ const usePrefillDealsWithExcelContextState = () => {
|
||||
.then(({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
if (ok) prefillWithExcelOnClose();
|
||||
refetchDeals();
|
||||
refetchCards();
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
};
|
||||
@@ -122,30 +122,30 @@ const usePrefillDealsWithExcelContextState = () => {
|
||||
onProductSelectChange,
|
||||
onDrop,
|
||||
excelDropzone,
|
||||
createDeals,
|
||||
createCards,
|
||||
form,
|
||||
errors,
|
||||
};
|
||||
};
|
||||
|
||||
type PrefillDealsWithExcelContextProviderProps = {
|
||||
type PrefillCardsWithExcelContextProviderProps = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export const PrefillDealsWithExcelContextProvider: FC<PrefillDealsWithExcelContextProviderProps> = ({ children }) => {
|
||||
const state = usePrefillDealsWithExcelContextState();
|
||||
export const PrefillCardsWithExcelContextProvider: FC<PrefillCardsWithExcelContextProviderProps> = ({ children }) => {
|
||||
const state = usePrefillCardsWithExcelContextState();
|
||||
return (
|
||||
<PrefillDealsWithExcelContext.Provider value={state}>
|
||||
<PrefillCardsWithExcelContext.Provider value={state}>
|
||||
{children}
|
||||
</PrefillDealsWithExcelContext.Provider>
|
||||
</PrefillCardsWithExcelContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const usePrefillDealsWithExcelContext = () => {
|
||||
const context = useContext(PrefillDealsWithExcelContext);
|
||||
export const usePrefillCardsWithExcelContext = () => {
|
||||
const context = useContext(PrefillCardsWithExcelContext);
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
"usePrefillDealsWithExcelContext must be used within a PrefillDealsWithExcelContextProvider",
|
||||
"usePrefillCardsWithExcelContext must be used within a PrefillCardsWithExcelContextProvider",
|
||||
);
|
||||
}
|
||||
return context;
|
||||
@@ -1,43 +1,47 @@
|
||||
import { Box, Drawer, rem, Tabs } from "@mantine/core";
|
||||
import { FC, ReactNode, useEffect } from "react";
|
||||
import { useDealPageContext } from "../../contexts/DealPageContext.tsx";
|
||||
import { useCardPageContext } from "../../contexts/CardPageContext.tsx";
|
||||
import { IconBox, IconCalendarUser, IconCubeSend, IconSettings, IconUser, IconUsersGroup } from "@tabler/icons-react";
|
||||
import DealStatusChangeTable from "../../components/DealStatusChangeTable/DealStatusChangeTable.tsx";
|
||||
import DealEditDrawerGeneralTab from "./tabs/DealEditDrawerGeneralTab.tsx";
|
||||
import CardStatusChangeTable from "../../components/DealStatusChangeTable/CardStatusChangeTable.tsx";
|
||||
import GeneralTab from "../../tabs/GeneralTab/GeneralTab.tsx";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import ProductAndServiceTab from "../../tabs/ProductAndServiceTab/ProductAndServiceTab.tsx";
|
||||
import { motion } from "framer-motion";
|
||||
import ShippingTab from "../../tabs/ShippingTab/ShippingTab.tsx";
|
||||
import EmployeesTab from "../../tabs/EmployeesTab/EmployeesTab.tsx";
|
||||
import ClientTab from "./tabs/ClientTab.tsx";
|
||||
import ClientTab from "../../tabs/ClientTab/ClientTab.tsx";
|
||||
|
||||
const useDealStatusChangeState = () => {
|
||||
const { selectedDeal } = useDealPageContext();
|
||||
const useCardStatusChangeState = () => {
|
||||
const { selectedCard } = useCardPageContext();
|
||||
return {
|
||||
statusHistory: selectedDeal?.statusHistory || [],
|
||||
statusHistory: selectedCard?.statusHistory || [],
|
||||
};
|
||||
};
|
||||
|
||||
const DealEditDrawerStatusChangeTable = () => {
|
||||
const { statusHistory } = useDealStatusChangeState();
|
||||
const CardEditDrawerStatusChangeTable = () => {
|
||||
const { statusHistory } = useCardStatusChangeState();
|
||||
|
||||
return <DealStatusChangeTable items={statusHistory} />;
|
||||
return <CardStatusChangeTable items={statusHistory} />;
|
||||
};
|
||||
|
||||
const useDealEditDrawerState = () => {
|
||||
const { selectedDeal, setSelectedDeal } = useDealPageContext();
|
||||
const { selectedCard, setSelectedCard } = useCardPageContext();
|
||||
return {
|
||||
isVisible: selectedDeal !== undefined,
|
||||
onClose: () => setSelectedDeal(undefined),
|
||||
isVisible: selectedCard !== undefined,
|
||||
onClose: () => setSelectedCard(undefined),
|
||||
};
|
||||
};
|
||||
|
||||
const DealEditDrawer: FC = () => {
|
||||
const CardEditDrawer: FC = () => {
|
||||
const { isVisible, onClose } = useDealEditDrawerState();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { selectedCard } = useCardPageContext();
|
||||
const modules = new Set<string>(selectedCard?.board.project.modules.map(module => module.key));
|
||||
|
||||
useEffect(() => {
|
||||
if (isVisible) return;
|
||||
queryClient.invalidateQueries({ queryKey: ["getDealSummaries"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["getCardSummaries"] });
|
||||
}, [isVisible]);
|
||||
|
||||
const getTabPanel = (value: string, component: ReactNode) => {
|
||||
@@ -58,6 +62,18 @@ const DealEditDrawer: FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const getTab = (key: string, icon: ReactNode, label: string) => {
|
||||
if (!modules.has(key)) return;
|
||||
|
||||
return (
|
||||
<Tabs.Tab
|
||||
value={key}
|
||||
leftSection={icon}>
|
||||
{label}
|
||||
</Tabs.Tab>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
size={"calc(100vw - 150px)"}
|
||||
@@ -96,25 +112,14 @@ const DealEditDrawer: FC = () => {
|
||||
leftSection={<IconCalendarUser />}>
|
||||
История
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
value={"servicesAndProducts"}
|
||||
leftSection={<IconBox />}>
|
||||
Товары и услуги
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
value={"shipment"}
|
||||
leftSection={<IconCubeSend />}>
|
||||
Отгрузка
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
value={"employees"}
|
||||
leftSection={<IconUsersGroup />}>
|
||||
Исполнители
|
||||
</Tabs.Tab>
|
||||
{getTab("servicesAndProducts", <IconBox />, "Товары и услуги")}
|
||||
{getTab("shipment", <IconCubeSend />, "Отгрузка")}
|
||||
{getTab("employees", <IconUsersGroup />, "Исполнители")}
|
||||
</Tabs.List>
|
||||
{getTabPanel("general", <DealEditDrawerGeneralTab />)}
|
||||
|
||||
{getTabPanel("general", <GeneralTab />)}
|
||||
{getTabPanel("client", <ClientTab />)}
|
||||
{getTabPanel("history", <DealEditDrawerStatusChangeTable />)}
|
||||
{getTabPanel("history", <CardEditDrawerStatusChangeTable />)}
|
||||
{getTabPanel("servicesAndProducts", <ProductAndServiceTab />)}
|
||||
{getTabPanel("shipment", <ShippingTab />)}
|
||||
{getTabPanel("employees", <EmployeesTab />)}
|
||||
@@ -123,4 +128,4 @@ const DealEditDrawer: FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default DealEditDrawer;
|
||||
export default CardEditDrawer;
|
||||
@@ -2,14 +2,14 @@ import { FC, useEffect } from "react";
|
||||
import { Button, Drawer, Flex, rem, TextInput } from "@mantine/core";
|
||||
import DealsTable from "./components/tables/DealsTable/DealsTable.tsx";
|
||||
import Preview from "./components/Preview/Preview.tsx";
|
||||
import styles from "./DealPrefillDrawer.module.css";
|
||||
import styles from "./CardPrefillDrawer.module.css";
|
||||
import BaseMarketplaceSelect from "../../../../components/Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
|
||||
import usePrefillDeal from "./hooks/usePrefillDeal.tsx";
|
||||
import { notifications } from "../../../../shared/lib/notifications.ts";
|
||||
import { usePrefillDealContext } from "../../contexts/PrefillDealContext.tsx";
|
||||
import { usePrefillCardContext } from "../../contexts/PrefillCardContext.tsx";
|
||||
|
||||
const DealPrefillDrawer: FC = () => {
|
||||
const { prefillOpened, prefillOnClose, selectedPrefillDeal, setPrefillDeal, prefillDeal } = usePrefillDealContext();
|
||||
const CardPrefillDrawer: FC = () => {
|
||||
const { prefillOpened, prefillOnClose, selectedPrefillCard, setPrefillCard, prefillCard } = usePrefillCardContext();
|
||||
const { data, form } = usePrefillDeal();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -52,19 +52,19 @@ const DealPrefillDrawer: FC = () => {
|
||||
<DealsTable items={data} />
|
||||
<Flex direction={"row"} gap="sm">
|
||||
<Button mt={10} w={"100%"} onClick={() => {
|
||||
if (!selectedPrefillDeal) {
|
||||
notifications.error({ message: "Сделка не выбрана." });
|
||||
if (!selectedPrefillCard) {
|
||||
notifications.error({ message: "Карточка не выбрана." });
|
||||
return;
|
||||
}
|
||||
setPrefillDeal(selectedPrefillDeal);
|
||||
setPrefillCard(selectedPrefillCard);
|
||||
prefillOnClose();
|
||||
}}>
|
||||
Предзаполнить
|
||||
</Button>
|
||||
{
|
||||
prefillDeal &&
|
||||
prefillCard &&
|
||||
<Button mt={10} w={"100%"} variant={"outline"} onClick={() => {
|
||||
setPrefillDeal(undefined);
|
||||
setPrefillCard(undefined);
|
||||
notifications.success({ message: "Предзаполнение отменено." });
|
||||
prefillOnClose();
|
||||
}}>
|
||||
@@ -79,4 +79,4 @@ const DealPrefillDrawer: FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default DealPrefillDrawer;
|
||||
export default CardPrefillDrawer;
|
||||
@@ -3,14 +3,14 @@ import styles from "./Preview.module.css";
|
||||
import { ScrollArea, Skeleton, Title } from "@mantine/core";
|
||||
import DealServicesTable from "../tables/DealServicesTable/DealServicesTable.tsx";
|
||||
import ProductPreview from "../ProductPreview/ProductPreview.tsx";
|
||||
import { usePrefillDealContext } from "../../../../contexts/PrefillDealContext.tsx";
|
||||
import { usePrefillCardContext } from "../../../../contexts/PrefillCardContext.tsx";
|
||||
|
||||
const Preview: FC = () => {
|
||||
const { selectedPrefillDeal } = usePrefillDealContext();
|
||||
const { selectedPrefillCard } = usePrefillCardContext();
|
||||
|
||||
const getTotalPrice = () => {
|
||||
if (!selectedPrefillDeal) return 0;
|
||||
const productServicesPrice = selectedPrefillDeal.products.reduce(
|
||||
if (!selectedPrefillCard) return 0;
|
||||
const productServicesPrice = selectedPrefillCard.products.reduce(
|
||||
(acc, row) =>
|
||||
acc +
|
||||
row.services.reduce(
|
||||
@@ -19,11 +19,11 @@ const Preview: FC = () => {
|
||||
),
|
||||
0,
|
||||
);
|
||||
const dealServicesPrice = selectedPrefillDeal.services.reduce(
|
||||
const cardServicesPrice = selectedPrefillCard.services.reduce(
|
||||
(acc, row) => acc + row.price * row.quantity,
|
||||
0,
|
||||
);
|
||||
return dealServicesPrice + productServicesPrice;
|
||||
return cardServicesPrice + productServicesPrice;
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -31,16 +31,16 @@ const Preview: FC = () => {
|
||||
<div className={styles["deal-container-wrapper"]}>
|
||||
<ScrollArea offsetScrollbars={"y"} w={"100%"}>
|
||||
<div style={{ height: "93vh" }}>
|
||||
<Skeleton visible={!selectedPrefillDeal}>
|
||||
<Skeleton visible={!selectedPrefillCard}>
|
||||
<Title order={4} mb={18}>
|
||||
Общая стоимость всех услуг:{" "}
|
||||
{getTotalPrice().toLocaleString("ru")}₽
|
||||
</Title>
|
||||
|
||||
<DealServicesTable items={selectedPrefillDeal?.services} />
|
||||
<DealServicesTable items={selectedPrefillCard?.services} />
|
||||
|
||||
<div className={styles["products-list"]}>
|
||||
{selectedPrefillDeal?.products.map(product => (
|
||||
{selectedPrefillCard?.products.map(product => (
|
||||
<ProductPreview
|
||||
key={product.product.id}
|
||||
product={product}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { FC } from "react";
|
||||
import { DealProductSchema, ProductSchema } from "../../../../../../client";
|
||||
import { CardProductSchema, ProductSchema } from "../../../../../../client";
|
||||
import { Image, rem, Text, Title } from "@mantine/core";
|
||||
import { isNil } from "lodash";
|
||||
import { ProductFieldNames } from "../../../../tabs/ProductAndServiceTab/components/ProductView/ProductView.tsx";
|
||||
@@ -7,7 +7,7 @@ import ProductServicesTable from "../tables/ProductServicesTable/ProductServices
|
||||
import styles from "./ProductPreview.module.css";
|
||||
|
||||
type Props = {
|
||||
product: DealProductSchema;
|
||||
product: CardProductSchema;
|
||||
};
|
||||
|
||||
const ProductPreview: FC<Props> = ({ product }) => {
|
||||
@@ -1,12 +1,12 @@
|
||||
import { FC } from "react";
|
||||
import { Flex, rem, Title } from "@mantine/core";
|
||||
import { DealServiceSchema, DealSummary } from "../../../../../../../client";
|
||||
import { CardServiceSchema, CardSummary } from "../../../../../../../client";
|
||||
import useDealServicesTableColumns from "./columns.tsx";
|
||||
import { BaseTable } from "../../../../../../../components/BaseTable/BaseTable.tsx";
|
||||
import { MRT_TableOptions } from "mantine-react-table";
|
||||
|
||||
type Props = {
|
||||
items?: DealServiceSchema[];
|
||||
items?: CardServiceSchema[];
|
||||
};
|
||||
|
||||
const DealServicesTable: FC<Props> = ({ items }) => {
|
||||
@@ -33,7 +33,7 @@ const DealServicesTable: FC<Props> = ({ items }) => {
|
||||
enableColumnActions: false,
|
||||
enablePagination: false,
|
||||
enableBottomToolbar: false,
|
||||
} as MRT_TableOptions<DealSummary>
|
||||
} as MRT_TableOptions<CardSummary>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useMemo } from "react";
|
||||
import { MRT_ColumnDef } from "mantine-react-table";
|
||||
import { DealServiceSchema } from "../../../../../../../client";
|
||||
import { CardServiceSchema } from "../../../../../../../client";
|
||||
|
||||
const useDealServicesTableColumns = () => {
|
||||
return useMemo<MRT_ColumnDef<DealServiceSchema>[]>(
|
||||
return useMemo<MRT_ColumnDef<CardServiceSchema>[]>(
|
||||
() => [
|
||||
{
|
||||
header: "Название",
|
||||
@@ -1,21 +1,21 @@
|
||||
import { FC, useEffect } from "react";
|
||||
import useDealsTableColumns from "./columns.tsx";
|
||||
import { DealSummary } from "../../../../../../../client";
|
||||
import { CardSummary } from "../../../../../../../client";
|
||||
import { BaseTable } from "../../../../../../../components/BaseTable/BaseTable.tsx";
|
||||
import { usePrefillDealContext } from "../../../../../contexts/PrefillDealContext.tsx";
|
||||
import { usePrefillCardContext } from "../../../../../contexts/PrefillCardContext.tsx";
|
||||
|
||||
type Props = {
|
||||
items: DealSummary[];
|
||||
items: CardSummary[];
|
||||
};
|
||||
|
||||
const DealsTable: FC<Props> = ({ items }) => {
|
||||
const { selectPrefillDeal } = usePrefillDealContext();
|
||||
const { selectPrefillCard } = usePrefillCardContext();
|
||||
const columns = useDealsTableColumns();
|
||||
const defaultSorting = [{ id: "createdAt", desc: false }];
|
||||
|
||||
useEffect(() => {
|
||||
if (items.length < 1) return;
|
||||
selectPrefillDeal(items[0].id);
|
||||
selectPrefillCard(items[0].id);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useMemo } from "react";
|
||||
import { MRT_ColumnDef } from "mantine-react-table";
|
||||
import { ActionIcon, Image, Radio } from "@mantine/core";
|
||||
import { DealSummary } from "../../../../../../../client";
|
||||
import { usePrefillDealContext } from "../../../../../contexts/PrefillDealContext.tsx";
|
||||
import { CardSummary } from "../../../../../../../client";
|
||||
import { usePrefillCardContext } from "../../../../../contexts/PrefillCardContext.tsx";
|
||||
|
||||
const useDealsTableColumns = () => {
|
||||
return useMemo<MRT_ColumnDef<DealSummary>[]>(
|
||||
return useMemo<MRT_ColumnDef<CardSummary>[]>(
|
||||
() => [
|
||||
{
|
||||
accessorKey: "select",
|
||||
@@ -13,13 +13,13 @@ const useDealsTableColumns = () => {
|
||||
size: 5,
|
||||
enableSorting: false,
|
||||
Cell: ({ row }) => {
|
||||
const { selectPrefillDeal, selectedPrefillDeal } = usePrefillDealContext();
|
||||
const checked = row.original.id === selectedPrefillDeal?.id;
|
||||
const { selectPrefillCard, selectedPrefillCard } = usePrefillCardContext();
|
||||
const checked = row.original.id === selectedPrefillCard?.id;
|
||||
return (
|
||||
<Radio
|
||||
checked={checked}
|
||||
onChange={() => {
|
||||
selectPrefillDeal(row.original.id);
|
||||
selectPrefillCard(row.original.id);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
@@ -1,12 +1,12 @@
|
||||
import { FC } from "react";
|
||||
import { MRT_TableOptions } from "mantine-react-table";
|
||||
import { DealProductServiceSchema } from "../../../../../../../client";
|
||||
import { CardProductServiceSchema } from "../../../../../../../client";
|
||||
import { BaseTable } from "../../../../../../../components/BaseTable/BaseTable.tsx";
|
||||
import useProductServicesTableColumns from "./columns.tsx";
|
||||
|
||||
|
||||
type Props = {
|
||||
items: DealProductServiceSchema[];
|
||||
items: CardProductServiceSchema[];
|
||||
quantity: number;
|
||||
};
|
||||
|
||||
@@ -23,7 +23,7 @@ const ProductServicesTable: FC<Props> = ({ items, quantity }) => {
|
||||
enableSorting: false,
|
||||
enableRowActions: false,
|
||||
enableBottomToolbar: false,
|
||||
} as MRT_TableOptions<DealProductServiceSchema>
|
||||
} as MRT_TableOptions<CardProductServiceSchema>
|
||||
}
|
||||
/>
|
||||
);
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useMemo } from "react";
|
||||
import { MRT_ColumnDef } from "mantine-react-table";
|
||||
import { DealProductServiceSchema } from "../../../../../../../client";
|
||||
import { CardProductServiceSchema } from "../../../../../../../client";
|
||||
import { useSelector } from "react-redux";
|
||||
import { RootState } from "../../../../../../../redux/store.ts";
|
||||
|
||||
type Props = {
|
||||
data: DealProductServiceSchema[];
|
||||
data: CardProductServiceSchema[];
|
||||
quantity: number;
|
||||
};
|
||||
|
||||
@@ -17,7 +17,7 @@ const useProductServicesTableColumns = (props: Props) => {
|
||||
[data, quantity]
|
||||
);
|
||||
const hideGuestColumns = ["service.cost"];
|
||||
return useMemo<MRT_ColumnDef<DealProductServiceSchema>[]>(
|
||||
return useMemo<MRT_ColumnDef<CardProductServiceSchema>[]>(
|
||||
() => [
|
||||
{
|
||||
accessorKey: "service.name",
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useForm } from "@mantine/form";
|
||||
import { useEffect, useState } from "react";
|
||||
import { BaseMarketplaceSchema } from "../../../../../client";
|
||||
import { useDealSummariesFull } from "../../../hooks/useDealSummaries.tsx";
|
||||
import { useCardSummariesFull } from "../../../hooks/useCardSummaries.tsx";
|
||||
|
||||
type State = {
|
||||
idOrName: string | null;
|
||||
@@ -9,7 +9,7 @@ type State = {
|
||||
};
|
||||
|
||||
const usePrefillDeal = () => {
|
||||
const { objects } = useDealSummariesFull();
|
||||
const { objects } = useCardSummariesFull();
|
||||
const form = useForm<State>({
|
||||
initialValues: {
|
||||
idOrName: null,
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Drawer, rem } from "@mantine/core";
|
||||
import ExcelDropzone from "../../../../components/ExcelDropzone/ExcelDropzone.tsx";
|
||||
import styles from "../PrefillDealWithExcelDrawer/PrefillDealsWithExcelDrawer.module.css";
|
||||
import { usePrefillDealsWithExcelContext } from "../../contexts/PrefillDealsWithExcelContext.tsx";
|
||||
import styles from "./PrefillCardsWithExcelDrawer.module.css";
|
||||
import { usePrefillCardsWithExcelContext } from "../../contexts/PrefillDealsWithExcelContext.tsx";
|
||||
import ProductsPreview from "./components/ProductsPreview.tsx";
|
||||
import { BoardSchema } from "../../../../client";
|
||||
|
||||
@@ -9,22 +9,22 @@ type Props = {
|
||||
board: BoardSchema | null;
|
||||
}
|
||||
|
||||
const PrefillDealsWithExcelDrawer = ({ board }: Props) => {
|
||||
const PrefillCardsWithExcelDrawer = ({ board }: Props) => {
|
||||
const {
|
||||
prefillWithExcelOpened,
|
||||
prefillWithExcelOnClose,
|
||||
barcodeProductsMap,
|
||||
onDrop,
|
||||
excelDropzone,
|
||||
} = usePrefillDealsWithExcelContext();
|
||||
} = usePrefillCardsWithExcelContext();
|
||||
|
||||
const getBody = () => {
|
||||
if (!board || board.dealStatuses.length === 0) return;
|
||||
if (!board || board.statuses.length === 0) return;
|
||||
|
||||
if (barcodeProductsMap?.size === 0) {
|
||||
return <ExcelDropzone dropzone={excelDropzone} onDrop={onDrop} />;
|
||||
}
|
||||
return <ProductsPreview status={board.dealStatuses[0]}/>;
|
||||
return <ProductsPreview status={board.statuses[0]}/>;
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -51,4 +51,4 @@ const PrefillDealsWithExcelDrawer = ({ board }: Props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default PrefillDealsWithExcelDrawer;
|
||||
export default PrefillCardsWithExcelDrawer;
|
||||
@@ -1,9 +1,9 @@
|
||||
import { usePrefillDealsWithExcelContext } from "../../../contexts/PrefillDealsWithExcelContext.tsx";
|
||||
import { usePrefillCardsWithExcelContext } from "../../../contexts/PrefillDealsWithExcelContext.tsx";
|
||||
import { Text, Tooltip } from "@mantine/core";
|
||||
import { IconAlertCircle, IconCircleCheck } from "@tabler/icons-react";
|
||||
|
||||
const ParsingResultsTooltip = () => {
|
||||
const { errors } = usePrefillDealsWithExcelContext();
|
||||
const { errors } = usePrefillCardsWithExcelContext();
|
||||
const isError = errors.length !== 0;
|
||||
|
||||
const errorLines = errors.map((error, i) => <Text key={i}>{error}</Text>);
|
||||
@@ -1,6 +1,6 @@
|
||||
import styles from "../PrefillDealsWithExcelDrawer.module.css";
|
||||
import styles from "../PrefillCardsWithExcelDrawer.module.css";
|
||||
import ProductsTable from "./ProductsTable.tsx";
|
||||
import { usePrefillDealsWithExcelContext } from "../../../contexts/PrefillDealsWithExcelContext.tsx";
|
||||
import { usePrefillCardsWithExcelContext } from "../../../contexts/PrefillDealsWithExcelContext.tsx";
|
||||
import { Box, Button, Flex, Group, Stack, Title } from "@mantine/core";
|
||||
import { ProductExcelData } from "../types.tsx";
|
||||
import BreakdownByCityTable from "./BreakdownByCityTable.tsx";
|
||||
@@ -13,7 +13,7 @@ type Props = {
|
||||
}
|
||||
|
||||
const ProductsPreview = ({ status }: Props) => {
|
||||
const { barcodeProductsMap, createDeals, form } = usePrefillDealsWithExcelContext();
|
||||
const { barcodeProductsMap, createCards, form } = usePrefillCardsWithExcelContext();
|
||||
|
||||
const getTitle = (barcode: string, productsData: ProductExcelData) => {
|
||||
if (productsData.products.length === 1) {
|
||||
@@ -46,7 +46,7 @@ const ProductsPreview = ({ status }: Props) => {
|
||||
return (
|
||||
<Stack gap={"md"}>
|
||||
<Title order={3}>Предпросмотр</Title>
|
||||
<form onSubmit={form.onSubmit((values) => createDeals(values, status))}>
|
||||
<form onSubmit={form.onSubmit((values) => createCards(values, status))}>
|
||||
<ClientSelect
|
||||
{...form.getInputProps("client")}
|
||||
inputContainer={(children) => (
|
||||
@@ -2,7 +2,7 @@ import { useEffect, useMemo, useState } from "react";
|
||||
import { MRT_ColumnDef } from "mantine-react-table";
|
||||
import { ProductSchema } from "../../../../../client";
|
||||
import { Radio } from "@mantine/core";
|
||||
import { usePrefillDealsWithExcelContext } from "../../../contexts/PrefillDealsWithExcelContext.tsx";
|
||||
import { usePrefillCardsWithExcelContext } from "../../../contexts/PrefillDealsWithExcelContext.tsx";
|
||||
import { ProductExcelData } from "../types.tsx";
|
||||
|
||||
type Props = {
|
||||
@@ -10,7 +10,7 @@ type Props = {
|
||||
}
|
||||
|
||||
export const useProductsTableColumns = ({ barcode }: Props) => {
|
||||
const { onProductSelectChange, barcodeProductsMap } = usePrefillDealsWithExcelContext();
|
||||
const { onProductSelectChange, barcodeProductsMap } = usePrefillCardsWithExcelContext();
|
||||
const [productData, setProductData] = useState<ProductExcelData>();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -6,6 +6,6 @@ export type ProductExcelData = {
|
||||
selectedProduct?: ProductSchema;
|
||||
}
|
||||
|
||||
export type DealsWithExcelForm = {
|
||||
export type CardsWithExcelForm = {
|
||||
client?: ClientSchema;
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { DealService } from "../../../client";
|
||||
import { CardService } from "../../../client";
|
||||
import ObjectList from "../../../hooks/objectList.tsx";
|
||||
|
||||
export const useDealSummaries = () => {
|
||||
export const useCardSummaries = () => {
|
||||
const { data: summariesRaw = [], refetch } = useQuery({
|
||||
queryKey: ["getDealSummaries"],
|
||||
queryFn: () => DealService.getDealSummaries({ full: false }),
|
||||
queryKey: ["getCardSummaries"],
|
||||
queryFn: () => CardService.getCardSummaries({ full: false }),
|
||||
select: data => data.summaries || [], // Трансформируем полученные данные
|
||||
});
|
||||
|
||||
@@ -14,9 +14,9 @@ export const useDealSummaries = () => {
|
||||
|
||||
return { summariesRaw, refetch };
|
||||
};
|
||||
export const useDealSummariesFull = () =>
|
||||
export const useCardSummariesFull = () =>
|
||||
ObjectList({
|
||||
queryFn: () => DealService.getDealSummaries({ full: true }),
|
||||
queryKey: "getDealSummariesFull",
|
||||
queryFn: () => CardService.getCardSummaries({ full: true }),
|
||||
queryKey: "getCardSummariesFull",
|
||||
getObjectsFn: response => response.summaries,
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useDealSummariesFull } from "./useDealSummaries.tsx";
|
||||
import { useCardSummariesFull } from "./useCardSummaries.tsx";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { useEffect, useState } from "react";
|
||||
import { BaseMarketplaceSchema, BoardSchema, ClientSchema, ProjectSchema, StatusSchema } from "../../../client";
|
||||
@@ -8,7 +8,7 @@ type Props = {
|
||||
projects: ProjectSchema[];
|
||||
}
|
||||
|
||||
export type DealsPageState = {
|
||||
export type CardsPageState = {
|
||||
id: number | null;
|
||||
marketplace: BaseMarketplaceSchema | null;
|
||||
client: ClientSchema | null;
|
||||
@@ -16,13 +16,13 @@ export type DealsPageState = {
|
||||
|
||||
projectForTable: ProjectSchema | null;
|
||||
board: BoardSchema | null;
|
||||
dealStatus: StatusSchema | null;
|
||||
status: StatusSchema | null;
|
||||
};
|
||||
|
||||
const useDealsPageState = ({ projects }: Props) => {
|
||||
const { objects } = useDealSummariesFull();
|
||||
const useCardsPageState = ({ projects }: Props) => {
|
||||
const { objects } = useCardSummariesFull();
|
||||
|
||||
const form = useForm<DealsPageState>({
|
||||
const form = useForm<CardsPageState>({
|
||||
initialValues: {
|
||||
project: null,
|
||||
id: null,
|
||||
@@ -31,7 +31,7 @@ const useDealsPageState = ({ projects }: Props) => {
|
||||
|
||||
projectForTable: null,
|
||||
board: null,
|
||||
dealStatus: null,
|
||||
status: null,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -59,9 +59,9 @@ const useDealsPageState = ({ projects }: Props) => {
|
||||
obj => obj.board.id === form.values.board?.id,
|
||||
);
|
||||
|
||||
if (form.values.dealStatus) {
|
||||
if (form.values.status) {
|
||||
result = result.filter(
|
||||
obj => obj.status.id === form.values.dealStatus?.id,
|
||||
obj => obj.status.id === form.values.status?.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -87,4 +87,4 @@ const useDealsPageState = ({ projects }: Props) => {
|
||||
return { data, form };
|
||||
};
|
||||
|
||||
export default useDealsPageState;
|
||||
export default useCardsPageState;
|
||||
@@ -1,14 +1,14 @@
|
||||
import { BoardSchema, DealSummary } from "../../../client";
|
||||
import { BoardSchema, CardSummary } from "../../../client";
|
||||
import { DragStart, DropResult } from "@hello-pangea/dnd";
|
||||
import { useState } from "react";
|
||||
import DragState from "../enums/DragState.ts";
|
||||
import useDealsDnd from "../../../components/Dnd/Deals/DealsDndColumn/hooks/useDealsDnd.tsx";
|
||||
import useCardsDnd from "../../../components/Dnd/Cards/CardsDndColumn/hooks/useCardsDnd.tsx";
|
||||
import useStatusesDnd from "../../../components/Dnd/Statuses/Statuses/hooks/useStatusesDnd.tsx";
|
||||
|
||||
|
||||
type Props = {
|
||||
selectedBoard: BoardSchema | null;
|
||||
summariesRaw: DealSummary[];
|
||||
summariesRaw: CardSummary[];
|
||||
refetchSummaries: () => void;
|
||||
refetchBoards: () => void;
|
||||
}
|
||||
@@ -24,7 +24,7 @@ const useDnd = ({
|
||||
const {
|
||||
summaries,
|
||||
onDealDragEnd,
|
||||
} = useDealsDnd({
|
||||
} = useCardsDnd({
|
||||
summariesRaw,
|
||||
refetchSummaries,
|
||||
})
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { type ProjectSchemaWithCount, ProjectService } from "../../../client";
|
||||
import { type FullProjectSchema, ProjectService } from "../../../client";
|
||||
|
||||
|
||||
const useProjects = () => {
|
||||
const [projects, setProjects] = useState<ProjectSchemaWithCount[]>([]);
|
||||
const [projects, setProjects] = useState<FullProjectSchema[]>([]);
|
||||
|
||||
const refetchProjects = () => {
|
||||
ProjectService.getProjects()
|
||||
1
src/pages/CardsPage/index.ts
Normal file
1
src/pages/CardsPage/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { CardsPage } from "./ui/CardsPage.tsx";
|
||||
@@ -2,7 +2,7 @@ import { ContextModalProps } from "@mantine/modals";
|
||||
import BaseFormModal, {
|
||||
CreateEditFormProps,
|
||||
} from "../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
|
||||
import { DealProductSchema, DealProductServiceSchema } from "../../../client";
|
||||
import { CardProductSchema, CardProductServiceSchema } from "../../../client";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { NumberInput } from "@mantine/core";
|
||||
import ProductSelect from "../../../components/ProductSelect/ProductSelect.tsx";
|
||||
@@ -13,8 +13,8 @@ type RestProps = {
|
||||
productIds?: number[];
|
||||
};
|
||||
|
||||
type Props = CreateEditFormProps<DealProductSchema> & RestProps;
|
||||
const AddDealProductModal = ({
|
||||
type Props = CreateEditFormProps<CardProductSchema> & RestProps;
|
||||
const AddCardProductModal = ({
|
||||
context,
|
||||
id,
|
||||
innerProps,
|
||||
@@ -22,13 +22,13 @@ const AddDealProductModal = ({
|
||||
const isEditing = "element" in innerProps;
|
||||
const restProps = omit(innerProps, ["clientId"]);
|
||||
|
||||
const validateServices = (services?: DealProductServiceSchema[]) => {
|
||||
const validateServices = (services?: CardProductServiceSchema[]) => {
|
||||
if (!services || services.length == 0) return null;
|
||||
return services.find(service => service.service === undefined)
|
||||
? "Удалите пустые услуги"
|
||||
: null;
|
||||
};
|
||||
const form = useForm<Partial<DealProductSchema>>({
|
||||
const form = useForm<Partial<CardProductSchema>>({
|
||||
initialValues: isEditing
|
||||
? innerProps.element
|
||||
: {
|
||||
@@ -37,7 +37,7 @@ const AddDealProductModal = ({
|
||||
quantity: 1,
|
||||
},
|
||||
validate: {
|
||||
product: (product?: DealProductSchema["product"]) =>
|
||||
product: (product?: CardProductSchema["product"]) =>
|
||||
product !== undefined ? null : "Необходимо выбрать товар",
|
||||
quantity: (quantity?: number) =>
|
||||
quantity && quantity > 0
|
||||
@@ -52,7 +52,7 @@ const AddDealProductModal = ({
|
||||
};
|
||||
return (
|
||||
<BaseFormModal
|
||||
{...(restProps as CreateEditFormProps<DealProductSchema>)}
|
||||
{...(restProps as CreateEditFormProps<CardProductSchema>)}
|
||||
form={form}
|
||||
closeOnSubmit
|
||||
onClose={onClose}>
|
||||
@@ -78,7 +78,7 @@ const AddDealProductModal = ({
|
||||
{/* <DealProductServiceTable*/}
|
||||
{/* quantity={form.values.quantity || 1}*/}
|
||||
{/* {...form.getInputProps('services') as*/}
|
||||
{/* BaseFormInputProps<DealProductServiceSchema[]>}*/}
|
||||
{/* BaseFormInputProps<CardProductServiceSchema[]>}*/}
|
||||
{/* />*/}
|
||||
{/*</Fieldset>*/}
|
||||
</>
|
||||
@@ -87,4 +87,4 @@ const AddDealProductModal = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default AddDealProductModal;
|
||||
export default AddCardProductModal;
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ContextModalProps } from "@mantine/modals";
|
||||
import BaseFormModal, { CreateEditFormProps } from "../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
|
||||
import { DealServiceSchema } from "../../../client";
|
||||
import { CardServiceSchema } from "../../../client";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { ComboboxItem, ComboboxItemGroup, NumberInput, OptionsFilter } from "@mantine/core";
|
||||
import ServiceWithPriceInput from "../../../components/ServiceWithPriceInput/ServiceWithPriceInput.tsx";
|
||||
@@ -11,15 +11,15 @@ import { RootState } from "../../../redux/store.ts";
|
||||
type RestProps = {
|
||||
serviceIds?: number[];
|
||||
};
|
||||
type Props = CreateEditFormProps<Partial<DealServiceSchema>> & RestProps;
|
||||
const AddDealServiceModal = ({
|
||||
type Props = CreateEditFormProps<Partial<CardServiceSchema>> & RestProps;
|
||||
const AddCardServiceModal = ({
|
||||
context,
|
||||
id,
|
||||
innerProps,
|
||||
}: ContextModalProps<Props>) => {
|
||||
const authState = useSelector((state: RootState) => state.auth);
|
||||
const isEditing = "element" in innerProps;
|
||||
const form = useForm<Partial<DealServiceSchema>>({
|
||||
const form = useForm<Partial<CardServiceSchema>>({
|
||||
initialValues: isEditing
|
||||
? innerProps.element
|
||||
: {
|
||||
@@ -28,7 +28,7 @@ const AddDealServiceModal = ({
|
||||
employees: [],
|
||||
},
|
||||
validate: {
|
||||
service: (service?: DealServiceSchema["service"]) =>
|
||||
service: (service?: CardServiceSchema["service"]) =>
|
||||
service !== undefined ? null : "Необходимо выбрать услугу",
|
||||
quantity: (quantity?: number) =>
|
||||
quantity && quantity > 0
|
||||
@@ -103,4 +103,4 @@ const AddDealServiceModal = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default AddDealServiceModal;
|
||||
export default AddCardServiceModal;
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ProjectSchema } from "../../../client";
|
||||
import { Flex, Modal, NumberInput, rem } from "@mantine/core";
|
||||
import { UseFormReturnType } from "@mantine/form";
|
||||
import { DealsPageState } from "../hooks/useDealsPageState.tsx";
|
||||
import { CardsPageState } from "../hooks/useCardsPageState.tsx";
|
||||
import ObjectSelect from "../../../components/ObjectSelect/ObjectSelect.tsx";
|
||||
import DealStatusSelect from "../../../components/DealStatusSelect/DealStatusSelect.tsx";
|
||||
import BaseMarketplaceSelect from "../../../components/Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
|
||||
@@ -12,11 +12,11 @@ import { IconFilter } from "@tabler/icons-react";
|
||||
import BoardSelect from "../../../components/BoardSelect/BoardSelect.tsx";
|
||||
|
||||
type Props = {
|
||||
form: UseFormReturnType<DealsPageState>;
|
||||
form: UseFormReturnType<CardsPageState>;
|
||||
projects: ProjectSchema[];
|
||||
};
|
||||
|
||||
const DealsTableFiltersModal = ({ form, projects }: Props) => {
|
||||
const CardsTableFiltersModal = ({ form, projects }: Props) => {
|
||||
const [opened, { open, close }] = useDisclosure();
|
||||
|
||||
return (
|
||||
@@ -51,7 +51,7 @@ const DealsTableFiltersModal = ({ form, projects }: Props) => {
|
||||
/>
|
||||
<DealStatusSelect
|
||||
board={form.values.board}
|
||||
{...form.getInputProps("dealStatus")}
|
||||
{...form.getInputProps("status")}
|
||||
clearable
|
||||
/>
|
||||
<BaseMarketplaceSelect
|
||||
@@ -75,4 +75,4 @@ const DealsTableFiltersModal = ({ form, projects }: Props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default DealsTableFiltersModal;
|
||||
export default CardsTableFiltersModal;
|
||||
@@ -2,7 +2,7 @@ import BaseFormModal, {
|
||||
CreateEditFormProps,
|
||||
} from "../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
|
||||
import {
|
||||
DealProductServiceSchema,
|
||||
CardProductServiceSchema,
|
||||
ServiceSchema,
|
||||
} from "../../../client";
|
||||
import { ContextModalProps } from "@mantine/modals";
|
||||
@@ -18,7 +18,7 @@ type RestProps = {
|
||||
quantity: number;
|
||||
serviceIds: number[];
|
||||
};
|
||||
type Props = CreateEditFormProps<DealProductServiceSchema> & RestProps;
|
||||
type Props = CreateEditFormProps<CardProductServiceSchema> & RestProps;
|
||||
|
||||
const ProductServiceFormModal = ({
|
||||
context,
|
||||
@@ -28,7 +28,7 @@ const ProductServiceFormModal = ({
|
||||
const authState = useSelector((state: RootState) => state.auth);
|
||||
|
||||
const isEditing = "onChange" in innerProps;
|
||||
const initialValues: Partial<DealProductServiceSchema> = isEditing
|
||||
const initialValues: Partial<CardProductServiceSchema> = isEditing
|
||||
? innerProps.element
|
||||
: {
|
||||
service: undefined,
|
||||
@@ -36,7 +36,7 @@ const ProductServiceFormModal = ({
|
||||
employees: [],
|
||||
isFixedPrice: false,
|
||||
};
|
||||
const form = useForm<Partial<DealProductServiceSchema>>({
|
||||
const form = useForm<Partial<CardProductServiceSchema>>({
|
||||
initialValues,
|
||||
validate: {
|
||||
service: (service?: ServiceSchema) =>
|
||||
@@ -51,7 +51,7 @@ const ProductServiceFormModal = ({
|
||||
return (
|
||||
<BaseFormModal
|
||||
{...innerProps}
|
||||
form={form as UseFormReturnType<DealProductServiceSchema>}
|
||||
form={form as UseFormReturnType<CardProductServiceSchema>}
|
||||
onClose={onClose}
|
||||
closeOnSubmit>
|
||||
<BaseFormModal.Body>
|
||||
@@ -1,4 +1,3 @@
|
||||
import { type ProjectSchemaWithCount } from "../../../../client";
|
||||
import { ContextModalProps } from "@mantine/modals";
|
||||
import { ActionIcon, Flex, rem, Stack, TextInput, Tooltip } from "@mantine/core";
|
||||
import { BaseTable } from "../../../../components/BaseTable/BaseTable.tsx";
|
||||
@@ -7,6 +6,7 @@ import { IconCheck, IconEdit, IconPlus, IconTrash } from "@tabler/icons-react";
|
||||
import { MRT_TableOptions } from "mantine-react-table";
|
||||
import InlineButton from "../../../../components/InlineButton/InlineButton.tsx";
|
||||
import useProjectModal from "./hooks/useProjectModal.tsx";
|
||||
import { FullProjectSchema } from "../../../../client";
|
||||
|
||||
type Props = {
|
||||
onUpdate: () => void;
|
||||
@@ -75,7 +75,7 @@ const ProjectsModal = ({ innerProps }: ContextModalProps<Props>) => {
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
),
|
||||
} as MRT_TableOptions<ProjectSchemaWithCount>
|
||||
} as MRT_TableOptions<FullProjectSchema>
|
||||
}
|
||||
/>
|
||||
</Stack>
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DealProductSchema } from "../../../client";
|
||||
import { CardProductSchema } from "../../../client";
|
||||
import { ContextModalProps } from "@mantine/modals";
|
||||
import { Button, Flex, rem } from "@mantine/core";
|
||||
import { useState } from "react";
|
||||
@@ -6,36 +6,36 @@ import ObjectMultiSelect from "../../../components/ObjectMultiSelect/ObjectMulti
|
||||
import { notifications } from "../../../shared/lib/notifications.ts";
|
||||
|
||||
type Props = {
|
||||
dealProducts: DealProductSchema[];
|
||||
dealProduct: DealProductSchema;
|
||||
cardProducts: CardProductSchema[];
|
||||
cardProduct: CardProductSchema;
|
||||
onSelect: (
|
||||
sourceProduct: DealProductSchema,
|
||||
destinationProducts: DealProductSchema[]
|
||||
sourceProduct: CardProductSchema,
|
||||
destinationProducts: CardProductSchema[]
|
||||
) => void;
|
||||
};
|
||||
|
||||
const SelectDealProductsModal = ({
|
||||
const SelectCardProductsModal = ({
|
||||
context,
|
||||
id,
|
||||
innerProps,
|
||||
}: ContextModalProps<Props>) => {
|
||||
const [dealProducts, setDealProducts] = useState<DealProductSchema[]>([]);
|
||||
const [cardProducts, setCardProducts] = useState<CardProductSchema[]>([]);
|
||||
const onSelectClick = () => {
|
||||
if (!dealProducts) {
|
||||
if (!cardProducts) {
|
||||
notifications.error({
|
||||
message:
|
||||
"Выберите товары на которые необходимо продублировать услуги",
|
||||
});
|
||||
return;
|
||||
}
|
||||
innerProps.onSelect(innerProps.dealProduct, dealProducts);
|
||||
innerProps.onSelect(innerProps.cardProduct, cardProducts);
|
||||
context.closeContextModal(id);
|
||||
};
|
||||
const onDuplicateAllClick = () => {
|
||||
innerProps.onSelect(
|
||||
innerProps.dealProduct,
|
||||
innerProps.dealProducts.filter(
|
||||
item => item !== innerProps.dealProduct
|
||||
innerProps.cardProduct,
|
||||
innerProps.cardProducts.filter(
|
||||
item => item !== innerProps.cardProduct
|
||||
)
|
||||
);
|
||||
context.closeContextModal(id);
|
||||
@@ -45,18 +45,18 @@ const SelectDealProductsModal = ({
|
||||
direction={"column"}
|
||||
gap={rem(10)}>
|
||||
<Flex>
|
||||
<ObjectMultiSelect<DealProductSchema>
|
||||
<ObjectMultiSelect<CardProductSchema>
|
||||
w={"100%"}
|
||||
label={"Товары"}
|
||||
placeholder={
|
||||
"Выберите товары на которые нужно продублировать услуги"
|
||||
}
|
||||
onChange={setDealProducts}
|
||||
value={dealProducts}
|
||||
data={innerProps.dealProducts}
|
||||
onChange={setCardProducts}
|
||||
value={cardProducts}
|
||||
data={innerProps.cardProducts}
|
||||
getLabelFn={item => item.product.name}
|
||||
getValueFn={item => item.product.id.toString()}
|
||||
filterBy={item => item !== innerProps.dealProduct}
|
||||
filterBy={item => item !== innerProps.cardProduct}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex
|
||||
@@ -80,4 +80,4 @@ const SelectDealProductsModal = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectDealProductsModal;
|
||||
export default SelectCardProductsModal;
|
||||
@@ -1,18 +1,18 @@
|
||||
import { Button, Fieldset, Flex, rem, Textarea, TextInput } from "@mantine/core";
|
||||
import { useDealPageContext } from "../../../contexts/DealPageContext.tsx";
|
||||
import { useCardPageContext } from "../../contexts/CardPageContext.tsx";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { DealGeneralFormType } from "./DealEditDrawerGeneralTab.tsx";
|
||||
import { ClientService, DealSchema, DealService } from "../../../../../client";
|
||||
import { CardGeneralFormType } from "../GeneralTab/GeneralTab.tsx";
|
||||
import { ClientService, CardSchema, CardService } from "../../../../client";
|
||||
import { isEqual } from "lodash";
|
||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||
import { notifications } from "../../../../shared/lib/notifications.ts";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
const ClientTab = () => {
|
||||
const { selectedDeal: deal, setSelectedDeal } = useDealPageContext();
|
||||
const initialValues: DealGeneralFormType = deal as DealSchema;
|
||||
const { selectedCard: card, setSelectedCard } = useCardPageContext();
|
||||
const initialValues: CardGeneralFormType = card as CardSchema;
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const form = useForm<DealGeneralFormType>(
|
||||
const form = useForm<CardGeneralFormType>(
|
||||
{
|
||||
initialValues: initialValues,
|
||||
validate: {
|
||||
@@ -21,7 +21,7 @@ const ClientTab = () => {
|
||||
},
|
||||
);
|
||||
const hasChanges = !isEqual(form.values, initialValues);
|
||||
const updateClientInfo = async (values: DealGeneralFormType) => {
|
||||
const updateClientInfo = async (values: CardGeneralFormType) => {
|
||||
return ClientService.updateClient({
|
||||
requestBody: {
|
||||
data: values.client,
|
||||
@@ -29,11 +29,11 @@ const ClientTab = () => {
|
||||
}).then(({ ok, message }) => notifications.guess(ok, { message }));
|
||||
};
|
||||
const update = async () => {
|
||||
return DealService.getDealById({ dealId: form.values.id }).then(data => {
|
||||
setSelectedDeal(data);
|
||||
return CardService.getCardById({ cardId: form.values.id }).then(data => {
|
||||
setSelectedCard(data);
|
||||
form.setInitialValues(data);
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["getDealSummaries"],
|
||||
queryKey: ["getCardSummaries"],
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -53,7 +53,7 @@ const ClientTab = () => {
|
||||
disabled
|
||||
placeholder={"Название"}
|
||||
label={"Название"}
|
||||
value={deal?.client.name}
|
||||
value={card?.client.name}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder={"Введите телефон"}
|
||||
@@ -4,7 +4,7 @@ import { UserSchema } from "../../../../../client";
|
||||
import useAvailableEmployeesList from "../hooks/useAvailableEmployeesList.tsx";
|
||||
|
||||
type DealData = {
|
||||
dealId: number;
|
||||
cardId: number;
|
||||
}
|
||||
|
||||
type Props = DealData & Omit<
|
||||
@@ -12,8 +12,8 @@ type Props = DealData & Omit<
|
||||
"data" | "getValueFn" | "getLabelFn"
|
||||
>;
|
||||
|
||||
const UserForDepartmentSelect: FC<Props> = ({ dealId, ...selectProps }) => {
|
||||
const { objects: employees } = useAvailableEmployeesList({ dealId });
|
||||
const UserForDepartmentSelect: FC<Props> = ({ cardId, ...selectProps }) => {
|
||||
const { objects: employees } = useAvailableEmployeesList({ cardId });
|
||||
|
||||
return (
|
||||
<ObjectSelect
|
||||
@@ -1,21 +1,21 @@
|
||||
import { useDealPageContext } from "../../../contexts/DealPageContext.tsx";
|
||||
import { useCardPageContext } from "../../../contexts/CardPageContext.tsx";
|
||||
import { BaseTable } from "../../../../../components/BaseTable/BaseTable.tsx";
|
||||
import useEmployeeTableColumns from "../hooks/useEmployeesTableColumns.tsx";
|
||||
import { ActionIcon, Flex, Tooltip } from "@mantine/core";
|
||||
import { IconTrash } from "@tabler/icons-react";
|
||||
import { MRT_TableOptions } from "mantine-react-table";
|
||||
import { DealEmployeesSchema } from "../../../../../client";
|
||||
import { CardEmployeesSchema } from "../../../../../client";
|
||||
import useEmployeesTab from "../hooks/useEmployeesTab.tsx";
|
||||
|
||||
|
||||
const EmployeesTable = () => {
|
||||
const { selectedDeal: deal } = useDealPageContext();
|
||||
const { selectedCard: card } = useCardPageContext();
|
||||
const columns = useEmployeeTableColumns();
|
||||
const { onUnassignEmployeeClick } = useEmployeesTab();
|
||||
|
||||
return (
|
||||
<BaseTable
|
||||
data={deal?.employees}
|
||||
data={card?.employees}
|
||||
columns={columns}
|
||||
restProps={
|
||||
{
|
||||
@@ -33,7 +33,7 @@ const EmployeesTable = () => {
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
),
|
||||
} as MRT_TableOptions<DealEmployeesSchema>
|
||||
} as MRT_TableOptions<CardEmployeesSchema>
|
||||
}
|
||||
/>
|
||||
);
|
||||
@@ -1,14 +1,14 @@
|
||||
import { DealService } from "../../../../../client";
|
||||
import { CardService } from "../../../../../client";
|
||||
import ObjectList from "../../../../../hooks/objectList.tsx";
|
||||
|
||||
|
||||
type Props = {
|
||||
dealId: number;
|
||||
cardId: number;
|
||||
}
|
||||
|
||||
const useAvailableEmployeesList = ({ dealId }: Props) =>
|
||||
const useAvailableEmployeesList = ({ cardId }: Props) =>
|
||||
ObjectList({
|
||||
queryFn: () => DealService.getAvailableEmployeesToAssign({ dealId }),
|
||||
queryFn: () => CardService.getAvailableEmployeesToAssign({ cardId }),
|
||||
getObjectsFn: response => response.employees,
|
||||
queryKey: "getAvailableEmployeesToAssign",
|
||||
});
|
||||
@@ -1,22 +1,22 @@
|
||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { useDealPageContext } from "../../../contexts/DealPageContext.tsx";
|
||||
import { DealEmployeesSchema, DealService } from "../../../../../client";
|
||||
import { useCardPageContext } from "../../../contexts/CardPageContext.tsx";
|
||||
import { CardEmployeesSchema, CardService } from "../../../../../client";
|
||||
|
||||
const useEmployeesTab = () => {
|
||||
const { selectedDeal: deal, refetchDeal } = useDealPageContext();
|
||||
const { selectedCard: card, refetchCard } = useCardPageContext();
|
||||
|
||||
const manageEmployee = (dealId: number, userId: number, isAssign: boolean) => {
|
||||
DealService.manageEmployee({
|
||||
const manageEmployee = (cardId: number, userId: number, isAssign: boolean) => {
|
||||
CardService.manageEmployee({
|
||||
requestBody: {
|
||||
dealId,
|
||||
cardId,
|
||||
userId,
|
||||
isAssign,
|
||||
},
|
||||
})
|
||||
.then(({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
refetchDeal();
|
||||
refetchCard();
|
||||
})
|
||||
.catch((err) => console.log(err));
|
||||
};
|
||||
@@ -28,8 +28,8 @@ const useEmployeesTab = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!deal) return;
|
||||
manageEmployee(deal.id, userId, true);
|
||||
if (!card) return;
|
||||
manageEmployee(card.id, userId, true);
|
||||
};
|
||||
|
||||
const onAssignEmployeeByQrClick = () => {
|
||||
@@ -44,20 +44,20 @@ const useEmployeesTab = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const onUnassignEmployeeClick = (assignment: DealEmployeesSchema) => {
|
||||
if (!deal) return;
|
||||
manageEmployee(deal.id, assignment.user.id, false);
|
||||
const onUnassignEmployeeClick = (assignment: CardEmployeesSchema) => {
|
||||
if (!card) return;
|
||||
manageEmployee(card.id, assignment.user.id, false);
|
||||
};
|
||||
|
||||
const onAssignEmployeeManuallyClick = () => {
|
||||
if (!deal) return;
|
||||
if (!card) return;
|
||||
|
||||
modals.openContextModal({
|
||||
modal: "assignUserModal",
|
||||
title: `Назначение исполнителя`,
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
deal,
|
||||
card,
|
||||
manageEmployee,
|
||||
},
|
||||
});
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useMemo } from "react";
|
||||
import { MRT_ColumnDef } from "mantine-react-table";
|
||||
import { DealEmployeesSchema } from "../../../../../client";
|
||||
import { CardEmployeesSchema } from "../../../../../client";
|
||||
|
||||
|
||||
const useEmployeeTableColumns = () => {
|
||||
return useMemo<MRT_ColumnDef<DealEmployeesSchema>[]>(
|
||||
return useMemo<MRT_ColumnDef<CardEmployeesSchema>[]>(
|
||||
() => [
|
||||
{
|
||||
accessorKey: "createdAt",
|
||||
@@ -1,13 +1,13 @@
|
||||
import { useForm } from "@mantine/form";
|
||||
import { ContextModalProps } from "@mantine/modals";
|
||||
import { Button, Flex, rem } from "@mantine/core";
|
||||
import { DealSchema } from "../../../../../client";
|
||||
import { CardSchema } from "../../../../../client";
|
||||
import AssignUserModalForm from "../types/AssignUserModalForm.tsx";
|
||||
import AvailableEmployeesSelect from "../components/AvailableEmployeesSelect.tsx";
|
||||
|
||||
type Props = {
|
||||
deal: DealSchema;
|
||||
manageEmployee: (dealId: number, userId: number, isAssign: boolean) => void;
|
||||
card: CardSchema;
|
||||
manageEmployee: (cardId: number, userId: number, isAssign: boolean) => void;
|
||||
}
|
||||
|
||||
const AssignEmployeeModal = ({
|
||||
@@ -16,7 +16,7 @@ const AssignEmployeeModal = ({
|
||||
innerProps,
|
||||
}: ContextModalProps<Props>) => {
|
||||
const {
|
||||
deal,
|
||||
card,
|
||||
manageEmployee,
|
||||
} = innerProps;
|
||||
|
||||
@@ -28,7 +28,7 @@ const AssignEmployeeModal = ({
|
||||
|
||||
const onSubmit = () => {
|
||||
if (!form.values.employee) return;
|
||||
manageEmployee(deal.id, form.values.employee.id, true);
|
||||
manageEmployee(card.id, form.values.employee.id, true);
|
||||
context.closeContextModal(id);
|
||||
};
|
||||
|
||||
@@ -42,7 +42,7 @@ const AssignEmployeeModal = ({
|
||||
label={"Работник"}
|
||||
placeholder={"Выберите работника"}
|
||||
{...form.getInputProps("employee")}
|
||||
dealId={deal.id}
|
||||
cardId={card.id}
|
||||
/>
|
||||
<Button
|
||||
variant={"default"}
|
||||
309
src/pages/CardsPage/tabs/GeneralTab/GeneralTab.tsx
Normal file
309
src/pages/CardsPage/tabs/GeneralTab/GeneralTab.tsx
Normal file
@@ -0,0 +1,309 @@
|
||||
import { FC, useState } from "react";
|
||||
import { useCardPageContext } from "../../contexts/CardPageContext.tsx";
|
||||
import { Button, Checkbox, Divider, Fieldset, Flex, Group, rem, Textarea, TextInput } from "@mantine/core";
|
||||
import { useForm } from "@mantine/form";
|
||||
import {
|
||||
CardSchema,
|
||||
CardService,
|
||||
ClientService,
|
||||
ProjectSchema,
|
||||
ShippingWarehouseSchema,
|
||||
StatusSchema,
|
||||
} from "../../../../client";
|
||||
import { isEqual } from "lodash";
|
||||
import { notifications } from "../../../../shared/lib/notifications.ts";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import ShippingWarehouseAutocomplete
|
||||
from "../../../../components/Selects/ShippingWarehouseAutocomplete/ShippingWarehouseAutocomplete.tsx";
|
||||
import { ButtonCopyControlled } from "../../../../components/ButtonCopyControlled/ButtonCopyControlled.tsx";
|
||||
import { useClipboard } from "@mantine/hooks";
|
||||
import ManagerSelect from "../../../../components/ManagerSelect/ManagerSelect.tsx";
|
||||
import ProjectSelect from "../../../../components/ProjectSelect/ProjectSelect.tsx";
|
||||
import BoardSelect from "../../../../components/BoardSelect/BoardSelect.tsx";
|
||||
import DealStatusSelect from "../../../../components/DealStatusSelect/DealStatusSelect.tsx";
|
||||
import CardAttributeFields from "../../../../components/CardAttributeFields/CardAttributeFields.tsx";
|
||||
import getAttributesFromCard from "../../../../components/CardAttributeFields/utils/getAttributesFromCard.ts";
|
||||
import isModuleInProject, { Modules } from "../../utils/isModuleInProject.ts";
|
||||
import PaymentLinkButton from "./components/PaymentLinkButton.tsx";
|
||||
import PrintDealBarcodesButton from "./components/PrintDealBarcodesButton.tsx";
|
||||
|
||||
type Props = {
|
||||
card: CardSchema;
|
||||
};
|
||||
|
||||
type Attributes = {
|
||||
[key: string]: number | boolean | string;
|
||||
};
|
||||
|
||||
export type CardGeneralFormType = Omit<CardSchema, "statusHistory" | "services" | "products">;
|
||||
|
||||
const Content: FC<Props> = ({ card }) => {
|
||||
const { setSelectedCard } = useCardPageContext();
|
||||
const clipboard = useClipboard();
|
||||
const queryClient = useQueryClient();
|
||||
const [project, setProject] = useState<ProjectSchema | null>(card.board.project);
|
||||
|
||||
const isServicesAndProductsIncluded = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, card.board.project);
|
||||
|
||||
const getInitialValues = (card: CardSchema): CardGeneralFormType => {
|
||||
return {
|
||||
...card,
|
||||
...getAttributesFromCard(card),
|
||||
};
|
||||
};
|
||||
|
||||
let initialValues = getInitialValues(card);
|
||||
|
||||
const form = useForm<CardGeneralFormType>({
|
||||
initialValues,
|
||||
validate: {
|
||||
name: (value: string) =>
|
||||
value.length > 0
|
||||
? null
|
||||
: "Название не может быть пустым",
|
||||
status: (value: StatusSchema) =>
|
||||
!value && "Статус не выбран",
|
||||
},
|
||||
});
|
||||
|
||||
const updateCardInfo = async (values: CardGeneralFormType) => {
|
||||
console.log("Updated attributes:");
|
||||
console.log(values);
|
||||
const formCardAttrs = values as unknown as Attributes;
|
||||
|
||||
const attributes = project?.attributes.reduce((attrs, projectAttr) => {
|
||||
return {
|
||||
...attrs,
|
||||
[projectAttr.name]: formCardAttrs[projectAttr.name],
|
||||
};
|
||||
}, {});
|
||||
|
||||
return CardService.updateCardGeneralInfo({
|
||||
requestBody: {
|
||||
cardId: card.id,
|
||||
data: {
|
||||
...values,
|
||||
statusId: values.status.id,
|
||||
boardId: values.board.id,
|
||||
shippingWarehouse: values.shippingWarehouse?.toString(),
|
||||
attributes,
|
||||
},
|
||||
},
|
||||
}).then(({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
if (!ok) return;
|
||||
CardService.getCardById({ cardId: card.id }).then(data => {
|
||||
console.log(data);
|
||||
setSelectedCard(data);
|
||||
initialValues = getInitialValues(data);
|
||||
form.setValues(initialValues);
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["getCardSummaries"],
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
const updateClientInfo = async (values: CardGeneralFormType) => {
|
||||
return ClientService.updateClient({
|
||||
requestBody: {
|
||||
data: values.client,
|
||||
},
|
||||
}).then(({ ok, message }) => notifications.guess(ok, { message }));
|
||||
};
|
||||
const handleSubmit = async (values: CardGeneralFormType) => {
|
||||
// Updating client info if there changes
|
||||
if (!isEqual(values.client, card.client)) {
|
||||
await updateClientInfo(values);
|
||||
}
|
||||
|
||||
const shippingWarehouse = isShippingWarehouse(values.shippingWarehouse) ? values.shippingWarehouse.name : values.shippingWarehouse;
|
||||
await updateCardInfo(
|
||||
{
|
||||
...values,
|
||||
shippingWarehouse,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const isShippingWarehouse = (
|
||||
value: ShippingWarehouseSchema | string | null | undefined,
|
||||
): value is ShippingWarehouseSchema => {
|
||||
return !["string", "null", "undefined"].includes(typeof value);
|
||||
};
|
||||
|
||||
const onCopyGuestUrlClick = () => {
|
||||
CardService.createDealGuestUrl({
|
||||
requestBody: {
|
||||
cardId: card.id,
|
||||
},
|
||||
}).then(({ ok, message, url }) => {
|
||||
if (!ok) notifications.guess(ok, { message });
|
||||
clipboard.copy(`${window.location.origin}/${url}`);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={form.onSubmit(values => handleSubmit(values))}>
|
||||
<Flex
|
||||
direction={"column"}
|
||||
justify={"space-between"}
|
||||
h={"100%"}>
|
||||
<Fieldset legend={`Общие параметры [ID: ${card.id}]`}>
|
||||
<Flex
|
||||
direction={"column"}
|
||||
gap={rem(10)}
|
||||
>
|
||||
<TextInput
|
||||
placeholder={"Название сделки"}
|
||||
label={"Название сделки"}
|
||||
{...form.getInputProps("name")}
|
||||
/>
|
||||
<TextInput
|
||||
disabled
|
||||
placeholder={"Дата создания"}
|
||||
label={"Дата создания"}
|
||||
value={new Date(card.createdAt).toLocaleString(
|
||||
"ru-RU",
|
||||
)}
|
||||
/>
|
||||
<ProjectSelect
|
||||
value={project}
|
||||
onChange={setProject}
|
||||
label={"Проект"}
|
||||
disabled
|
||||
/>
|
||||
<BoardSelect
|
||||
project={project}
|
||||
{...form.getInputProps("board")}
|
||||
label={"Доска"}
|
||||
/>
|
||||
<DealStatusSelect
|
||||
board={form.values.board}
|
||||
{...form.getInputProps("status")}
|
||||
label={"Статус"}
|
||||
/>
|
||||
<Textarea
|
||||
h={rem(150)}
|
||||
styles={{
|
||||
wrapper: { height: "90%" },
|
||||
input: { height: "90%" },
|
||||
}}
|
||||
label={"Коментарий"}
|
||||
placeholder={"Введите коментарий"}
|
||||
{...form.getInputProps("comment")}
|
||||
/>
|
||||
<ShippingWarehouseAutocomplete
|
||||
placeholder={"Введите склад отгрузки"}
|
||||
label={"Склад отгрузки"}
|
||||
value={
|
||||
isShippingWarehouse(
|
||||
form.values.shippingWarehouse,
|
||||
)
|
||||
? form.values.shippingWarehouse
|
||||
: undefined
|
||||
}
|
||||
onChange={event => {
|
||||
if (isShippingWarehouse(event)) {
|
||||
form.getInputProps(
|
||||
"shippingWarehouse",
|
||||
).onChange(event.name);
|
||||
return;
|
||||
}
|
||||
form.getInputProps(
|
||||
"shippingWarehouse",
|
||||
).onChange(event);
|
||||
}}
|
||||
/>
|
||||
<ManagerSelect
|
||||
placeholder={"Укажите менеджера"}
|
||||
label={"Менеджер"}
|
||||
{...form.getInputProps("manager")}
|
||||
/>
|
||||
{project && (
|
||||
<CardAttributeFields
|
||||
project={project}
|
||||
form={form}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
</Fieldset>
|
||||
<Flex
|
||||
mt={"md"}
|
||||
gap={rem(10)}
|
||||
align={"center"}
|
||||
justify={"flex-end"}>
|
||||
<Flex
|
||||
align={"center"}
|
||||
gap={rem(10)}
|
||||
justify={"center"}>
|
||||
<Flex
|
||||
gap={rem(10)}
|
||||
align={"center"}
|
||||
justify={"space-between"}>
|
||||
{isServicesAndProductsIncluded && (
|
||||
<PrintDealBarcodesButton card={card}/>
|
||||
)}
|
||||
<Flex gap={rem(10)}>
|
||||
{isServicesAndProductsIncluded && (
|
||||
<PaymentLinkButton card={card} />
|
||||
)}
|
||||
<ButtonCopyControlled
|
||||
onCopyClick={onCopyGuestUrlClick}
|
||||
onCopiedLabel={
|
||||
"Ссылка скопирована в буфер обмена"
|
||||
}
|
||||
copied={clipboard.copied}
|
||||
>
|
||||
Ссылка на редактирование
|
||||
</ButtonCopyControlled>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex gap={rem(10)}>
|
||||
{isServicesAndProductsIncluded && (
|
||||
<Checkbox
|
||||
label={"Оплачен"}
|
||||
checked={card.billRequest?.paid || card.group?.billRequest?.paid || false}
|
||||
disabled
|
||||
/>
|
||||
)}
|
||||
<Checkbox
|
||||
label={"Завершена"}
|
||||
{...form.getInputProps("isCompleted", { type: "checkbox" })}
|
||||
/>
|
||||
<Checkbox
|
||||
label={"Удалена"}
|
||||
{...form.getInputProps("isDeleted", { type: "checkbox" })}
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Divider orientation={"vertical"} />
|
||||
<Group
|
||||
align={"center"}
|
||||
justify={"center"}>
|
||||
<Button
|
||||
color={"red"}
|
||||
type={"reset"}
|
||||
disabled={isEqual(initialValues, form.values)}
|
||||
onClick={() => form.reset()}>
|
||||
Отменить изменения
|
||||
</Button>
|
||||
<Button
|
||||
variant={"default"}
|
||||
type={"submit"}
|
||||
disabled={isEqual(initialValues, form.values)}>
|
||||
Сохранить изменения
|
||||
</Button>
|
||||
</Group>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
const GeneralTab: FC = () => {
|
||||
const { selectedCard } = useCardPageContext();
|
||||
if (!selectedCard) return <>No card selected</>;
|
||||
return <Content card={selectedCard} />;
|
||||
};
|
||||
|
||||
export default GeneralTab;
|
||||
@@ -0,0 +1,43 @@
|
||||
import { CardSchema } from "../../../../../client";
|
||||
import ButtonCopy from "../../../../../components/ButtonCopy/ButtonCopy.tsx";
|
||||
import { ButtonCopyControlled } from "../../../../../components/ButtonCopyControlled/ButtonCopyControlled.tsx";
|
||||
import { getCurrentDateTimeForFilename } from "../../../../../shared/lib/date.ts";
|
||||
import FileSaver from "file-saver";
|
||||
|
||||
type Props = {
|
||||
card: CardSchema;
|
||||
}
|
||||
|
||||
const PaymentLinkButton = ({ card }: Props) => {
|
||||
const billRequestPdfUrl = card?.billRequest?.pdfUrl || card?.group?.billRequest?.pdfUrl;
|
||||
|
||||
if (billRequestPdfUrl) {
|
||||
return (
|
||||
<ButtonCopy
|
||||
onCopiedLabel={"Ссылка скопирована в буфер обмена"}
|
||||
value={billRequestPdfUrl}
|
||||
>
|
||||
Ссылка на оплату
|
||||
</ButtonCopy>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ButtonCopyControlled
|
||||
onCopyClick={() => {
|
||||
const date =
|
||||
getCurrentDateTimeForFilename();
|
||||
FileSaver.saveAs(
|
||||
`${import.meta.env.VITE_API_URL}/card/billing-document/${card.id}`,
|
||||
`bill_${card.id}_${date}.pdf`,
|
||||
);
|
||||
}}
|
||||
copied={false}
|
||||
onCopiedLabel={"Ссылка скопирована в буфер обмена"}
|
||||
>
|
||||
Ссылка на оплату (PDF)
|
||||
</ButtonCopyControlled>
|
||||
);
|
||||
}
|
||||
|
||||
export default PaymentLinkButton;
|
||||
@@ -0,0 +1,63 @@
|
||||
import { ActionIcon, Tooltip } from "@mantine/core";
|
||||
import styles from "../../../ui/CardsPage.module.css";
|
||||
import { CardSchema, CardService } from "../../../../../client";
|
||||
import { base64ToBlob } from "../../../../../shared/lib/utils.ts";
|
||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||
import { IconBarcode, IconPrinter } from "@tabler/icons-react";
|
||||
|
||||
|
||||
type Props = {
|
||||
card: CardSchema;
|
||||
}
|
||||
|
||||
const PrintDealBarcodesButton = ({ card }: Props) => {
|
||||
return (
|
||||
<>
|
||||
<Tooltip
|
||||
className={styles["print-deals-button"]}
|
||||
label={"Распечатать штрихкоды сделки"}
|
||||
>
|
||||
<ActionIcon
|
||||
onClick={async () => {
|
||||
const response =
|
||||
await CardService.getCardProductsBarcodesPdf({
|
||||
requestBody: {
|
||||
cardId: card.id,
|
||||
},
|
||||
});
|
||||
const pdfBlob = base64ToBlob(
|
||||
response.base64String,
|
||||
response.mimeType,
|
||||
);
|
||||
const pdfUrl = URL.createObjectURL(pdfBlob);
|
||||
const pdfWindow = window.open(pdfUrl);
|
||||
if (!pdfWindow) {
|
||||
notifications.error({ message: "Ошибка" });
|
||||
return;
|
||||
}
|
||||
pdfWindow.onload = () => {
|
||||
pdfWindow.print();
|
||||
};
|
||||
}}
|
||||
variant={"default"}>
|
||||
<IconBarcode />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={"Распечатать сделку"}>
|
||||
<ActionIcon
|
||||
onClick={() => {
|
||||
const pdfWindow = window.open(
|
||||
`${import.meta.env.VITE_API_URL}/card/tech-spec/${card.id}`,
|
||||
);
|
||||
if (!pdfWindow) return;
|
||||
pdfWindow.print();
|
||||
}}
|
||||
variant={"default"}>
|
||||
<IconPrinter />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PrintDealBarcodesButton;
|
||||
@@ -10,13 +10,13 @@ import {
|
||||
Text,
|
||||
Title,
|
||||
} from "@mantine/core";
|
||||
import DealServicesTable from "./components/DealServicesTable/DealServicesTable.tsx";
|
||||
import useDealProductAndServiceTabState from "./hooks/useProductAndServiceTabState.tsx";
|
||||
import CardServicesTable from "./components/DealServicesTable/CardServicesTable.tsx";
|
||||
import useCardProductAndServiceTabState from "./hooks/useProductAndServiceTabState.tsx";
|
||||
import { modals } from "@mantine/modals";
|
||||
import {
|
||||
BillingService,
|
||||
DealProductSchema,
|
||||
DealService,
|
||||
CardProductSchema,
|
||||
CardService,
|
||||
GetServiceKitSchema,
|
||||
ProductSchema,
|
||||
ProductService,
|
||||
@@ -26,27 +26,27 @@ import { CreateProductRequest } from "../../../ProductsPage/types.ts";
|
||||
import classNames from "classnames";
|
||||
|
||||
const ProductAndServiceTab: FC = () => {
|
||||
const { dealState, dealServicesState, dealProductsState } =
|
||||
useDealProductAndServiceTabState();
|
||||
const isLocked = Boolean(dealState.deal?.billRequest || dealState.deal?.group?.billRequest);
|
||||
const { cardState, cardServicesState, cardProductsState } =
|
||||
useCardProductAndServiceTabState();
|
||||
const isLocked = Boolean(cardState.card?.billRequest || cardState.card?.group?.billRequest);
|
||||
const onAddProductClick = () => {
|
||||
if (!dealProductsState.onCreate || !dealState.deal) return;
|
||||
const productIds = dealState.deal.products.map(
|
||||
if (!cardProductsState.onCreate || !cardState.card) return;
|
||||
const productIds = cardState.card.products.map(
|
||||
product => product.product.id
|
||||
);
|
||||
modals.openContextModal({
|
||||
modal: "addDealProduct",
|
||||
modal: "addCardProduct",
|
||||
innerProps: {
|
||||
onCreate: dealProductsState.onCreate,
|
||||
clientId: dealState.deal.clientId,
|
||||
onCreate: cardProductsState.onCreate,
|
||||
clientId: cardState.card.clientId,
|
||||
productIds: productIds,
|
||||
},
|
||||
withCloseButton: false,
|
||||
});
|
||||
};
|
||||
const getTotalPrice = () => {
|
||||
if (!dealState.deal) return 0;
|
||||
const productServicesPrice = dealState.deal.products.reduce(
|
||||
if (!cardState.card) return 0;
|
||||
const productServicesPrice = cardState.card.products.reduce(
|
||||
(acc, row) =>
|
||||
acc +
|
||||
row.services.reduce(
|
||||
@@ -55,20 +55,20 @@ const ProductAndServiceTab: FC = () => {
|
||||
),
|
||||
0
|
||||
);
|
||||
const dealServicesPrice = dealState.deal.services.reduce(
|
||||
const cardServicesPrice = cardState.card.services.reduce(
|
||||
(acc, row) => acc + row.price * row.quantity,
|
||||
0
|
||||
);
|
||||
return dealServicesPrice + productServicesPrice;
|
||||
return cardServicesPrice + productServicesPrice;
|
||||
};
|
||||
const onCopyServices = (
|
||||
sourceProduct: DealProductSchema,
|
||||
destinationProducts: DealProductSchema[]
|
||||
sourceProduct: CardProductSchema,
|
||||
destinationProducts: CardProductSchema[]
|
||||
) => {
|
||||
if (!dealState.deal) return;
|
||||
DealService.copyProductServices({
|
||||
if (!cardState.card) return;
|
||||
CardService.copyProductServices({
|
||||
requestBody: {
|
||||
dealId: dealState.deal.id,
|
||||
cardId: cardState.card.id,
|
||||
destinationProductIds: destinationProducts.map(
|
||||
product => product.product.id
|
||||
),
|
||||
@@ -77,48 +77,48 @@ const ProductAndServiceTab: FC = () => {
|
||||
}).then(async ({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
if (!ok) return;
|
||||
await dealState.refetch();
|
||||
await cardState.refetch();
|
||||
});
|
||||
};
|
||||
const onCopyServicesClick = (product: DealProductSchema) => {
|
||||
const onCopyServicesClick = (product: CardProductSchema) => {
|
||||
modals.openContextModal({
|
||||
modal: "selectDealProductsModal",
|
||||
modal: "selectCardProductsModal",
|
||||
title: "Дублирование услуг",
|
||||
size: "lg",
|
||||
innerProps: {
|
||||
dealProducts: dealState.deal?.products || [],
|
||||
dealProduct: product,
|
||||
cardProducts: cardState.card?.products || [],
|
||||
cardProduct: product,
|
||||
onSelect: onCopyServices,
|
||||
},
|
||||
withCloseButton: false,
|
||||
});
|
||||
};
|
||||
|
||||
const onKitAdd = (item: DealProductSchema, kit: GetServiceKitSchema) => {
|
||||
if (!dealState.deal) return;
|
||||
DealService.addKitToDealProduct({
|
||||
const onKitAdd = (item: CardProductSchema, kit: GetServiceKitSchema) => {
|
||||
if (!cardState.card) return;
|
||||
CardService.addKitToCardProduct({
|
||||
requestBody: {
|
||||
dealId: dealState.deal.id,
|
||||
cardId: cardState.card.id,
|
||||
kitId: kit.id,
|
||||
productId: item.product.id,
|
||||
},
|
||||
}).then(async ({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
if (!ok) return;
|
||||
await dealState.refetch();
|
||||
await cardState.refetch();
|
||||
});
|
||||
};
|
||||
const onDealKitAdd = (kit: GetServiceKitSchema) => {
|
||||
if (!dealState.deal) return;
|
||||
DealService.addKitToDeal({
|
||||
if (!cardState.card) return;
|
||||
CardService.addKitToCard({
|
||||
requestBody: {
|
||||
dealId: dealState.deal.id,
|
||||
cardId: cardState.card.id,
|
||||
kitId: kit.id,
|
||||
},
|
||||
}).then(async ({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
if (!ok) return;
|
||||
await dealState.refetch();
|
||||
await cardState.refetch();
|
||||
});
|
||||
};
|
||||
|
||||
@@ -130,13 +130,13 @@ const ProductAndServiceTab: FC = () => {
|
||||
});
|
||||
};
|
||||
const onCreateProductClick = () => {
|
||||
if (!dealState.deal) return;
|
||||
if (!cardState.card) return;
|
||||
modals.openContextModal({
|
||||
modal: "createProduct",
|
||||
title: "Создание товара",
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
clientId: dealState.deal.clientId,
|
||||
clientId: cardState.card.clientId,
|
||||
onCreate: onCreateProduct,
|
||||
},
|
||||
});
|
||||
@@ -146,14 +146,14 @@ const ProductAndServiceTab: FC = () => {
|
||||
async ({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
if (!ok) return;
|
||||
await dealState.refetch();
|
||||
await cardState.refetch();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const onCreateBillClick = () => {
|
||||
if (!dealState.deal) return;
|
||||
const dealId = dealState.deal.id;
|
||||
if (!cardState.card) return;
|
||||
const cardId = cardState.card.id;
|
||||
modals.openConfirmModal({
|
||||
withCloseButton: false,
|
||||
size: "xl",
|
||||
@@ -168,7 +168,7 @@ const ProductAndServiceTab: FC = () => {
|
||||
onConfirm: () => {
|
||||
BillingService.createDealBill({
|
||||
requestBody: {
|
||||
dealId,
|
||||
cardId,
|
||||
},
|
||||
}).then(async ({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
@@ -177,7 +177,7 @@ const ProductAndServiceTab: FC = () => {
|
||||
message:
|
||||
"Ссылка на оплату доступна во вкладе общее",
|
||||
});
|
||||
await dealState.refetch();
|
||||
await cardState.refetch();
|
||||
});
|
||||
},
|
||||
labels: {
|
||||
@@ -187,8 +187,8 @@ const ProductAndServiceTab: FC = () => {
|
||||
});
|
||||
};
|
||||
const onCancelBillClick = () => {
|
||||
if (!dealState.deal) return;
|
||||
const dealId = dealState.deal.id;
|
||||
if (!cardState.card) return;
|
||||
const cardId = cardState.card.id;
|
||||
modals.openConfirmModal({
|
||||
withCloseButton: false,
|
||||
children: (
|
||||
@@ -199,11 +199,11 @@ const ProductAndServiceTab: FC = () => {
|
||||
onConfirm: () => {
|
||||
BillingService.cancelDealBill({
|
||||
requestBody: {
|
||||
dealId,
|
||||
cardId,
|
||||
},
|
||||
}).then(async ({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
await dealState.refetch();
|
||||
await cardState.refetch();
|
||||
});
|
||||
},
|
||||
labels: {
|
||||
@@ -216,19 +216,19 @@ const ProductAndServiceTab: FC = () => {
|
||||
<div
|
||||
className={classNames(
|
||||
styles["container"],
|
||||
dealState.deal?.billRequest && styles["container-disabled"]
|
||||
cardState.card?.billRequest && styles["container-disabled"]
|
||||
)}>
|
||||
<div className={styles["products-list"]}>
|
||||
<ScrollArea offsetScrollbars>
|
||||
{dealState.deal?.products.map(product => (
|
||||
{cardState.card?.products.map(product => (
|
||||
<ProductView
|
||||
onProductEdit={onProductEdit}
|
||||
onKitAdd={onKitAdd}
|
||||
onCopyServices={onCopyServicesClick}
|
||||
key={product.product.id}
|
||||
product={product}
|
||||
onChange={dealProductsState.onChange}
|
||||
onDelete={dealProductsState.onDelete}
|
||||
onChange={cardProductsState.onChange}
|
||||
onDelete={cardProductsState.onDelete}
|
||||
/>
|
||||
))}
|
||||
</ScrollArea>
|
||||
@@ -239,9 +239,9 @@ const ProductAndServiceTab: FC = () => {
|
||||
<Flex
|
||||
direction={"column"}
|
||||
className={styles["deal-container-wrapper"]}>
|
||||
<DealServicesTable
|
||||
<CardServicesTable
|
||||
onKitAdd={onDealKitAdd}
|
||||
{...dealServicesState}
|
||||
{...cardServicesState}
|
||||
/>
|
||||
|
||||
<Divider my={rem(15)} />
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CRUDTableProps } from "../../../../../../types/CRUDTable.tsx";
|
||||
import { DealServiceSchema, GetServiceKitSchema, UserSchema } from "../../../../../../client";
|
||||
import { CardServiceSchema, GetServiceKitSchema, UserSchema } from "../../../../../../client";
|
||||
import { FC, useState } from "react";
|
||||
import { ActionIcon, Button, Flex, Modal, NumberInput, rem, Text, Title, Tooltip } from "@mantine/core";
|
||||
import { IconTrash, IconUsersGroup } from "@tabler/icons-react";
|
||||
@@ -9,36 +9,36 @@ import SimpleUsersTable from "../../../../components/SimpleUsersTable/SimpleUser
|
||||
import { ServiceType } from "../../../../../../shared/enums/ServiceType.ts";
|
||||
import { useSelector } from "react-redux";
|
||||
import { RootState } from "../../../../../../redux/store.ts";
|
||||
import useDealProductAndServiceTabState from "../../hooks/useProductAndServiceTabState.tsx";
|
||||
import useCardProductAndServiceTabState from "../../hooks/useProductAndServiceTabState.tsx";
|
||||
import LockCheckbox from "../../../../../../components/LockCheckbox/LockCheckbox.tsx";
|
||||
import { useDebouncedCallback } from "@mantine/hooks";
|
||||
|
||||
type RestProps = {
|
||||
onKitAdd?: (kit: GetServiceKitSchema) => void;
|
||||
};
|
||||
type Props = CRUDTableProps<DealServiceSchema> & RestProps;
|
||||
const DealServicesTable: FC<Props> = ({
|
||||
type Props = CRUDTableProps<CardServiceSchema> & RestProps;
|
||||
const CardServicesTable: FC<Props> = ({
|
||||
items,
|
||||
onDelete,
|
||||
onCreate,
|
||||
onChange,
|
||||
onKitAdd,
|
||||
}) => {
|
||||
const debouncedOnChange = useDebouncedCallback(async (item: DealServiceSchema) => {
|
||||
const debouncedOnChange = useDebouncedCallback(async (item: CardServiceSchema) => {
|
||||
if (!onChange) return;
|
||||
onChange(item);
|
||||
}, 200);
|
||||
const authState = useSelector((state: RootState) => state.auth);
|
||||
|
||||
const { dealState } = useDealProductAndServiceTabState();
|
||||
const isLocked = Boolean(dealState.deal?.billRequest);
|
||||
const { cardState } = useCardProductAndServiceTabState();
|
||||
const isLocked = Boolean(cardState.card?.billRequest);
|
||||
|
||||
const [currentService, setCurrentService] = useState<
|
||||
DealServiceSchema | undefined
|
||||
CardServiceSchema | undefined
|
||||
>();
|
||||
const [employeesModalVisible, setEmployeesModalVisible] = useState(false);
|
||||
|
||||
const onDeleteClick = (item: DealServiceSchema) => {
|
||||
const onDeleteClick = (item: CardServiceSchema) => {
|
||||
if (!onDelete) return;
|
||||
onDelete(item);
|
||||
};
|
||||
@@ -46,7 +46,7 @@ const DealServicesTable: FC<Props> = ({
|
||||
if (!onCreate) return;
|
||||
const serviceIds = items.map(service => service.service.id);
|
||||
modals.openContextModal({
|
||||
modal: "addDealService",
|
||||
modal: "addCardService",
|
||||
innerProps: {
|
||||
onCreate: onCreate,
|
||||
serviceIds,
|
||||
@@ -54,14 +54,14 @@ const DealServicesTable: FC<Props> = ({
|
||||
withCloseButton: false,
|
||||
});
|
||||
};
|
||||
const onQuantityChange = (item: DealServiceSchema, quantity: number) => {
|
||||
const onQuantityChange = (item: CardServiceSchema, quantity: number) => {
|
||||
if (!onChange) return;
|
||||
debouncedOnChange({
|
||||
...item,
|
||||
quantity,
|
||||
});
|
||||
};
|
||||
const onPriceChange = (item: DealServiceSchema, price: number) => {
|
||||
const onPriceChange = (item: CardServiceSchema, price: number) => {
|
||||
if (!onChange) return;
|
||||
debouncedOnChange({
|
||||
...item,
|
||||
@@ -69,14 +69,14 @@ const DealServicesTable: FC<Props> = ({
|
||||
isFixedPrice: true,
|
||||
});
|
||||
};
|
||||
const onLockChange = (item: DealServiceSchema, isLocked: boolean) => {
|
||||
const onLockChange = (item: CardServiceSchema, isLocked: boolean) => {
|
||||
if (!onChange) return;
|
||||
debouncedOnChange({
|
||||
...item,
|
||||
isFixedPrice: isLocked,
|
||||
});
|
||||
};
|
||||
const onEmployeeClick = (item: DealServiceSchema) => {
|
||||
const onEmployeeClick = (item: CardServiceSchema) => {
|
||||
if (!onChange) return;
|
||||
setCurrentService(item);
|
||||
setEmployeesModalVisible(true);
|
||||
@@ -248,4 +248,4 @@ const DealServicesTable: FC<Props> = ({
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default DealServicesTable;
|
||||
export default CardServicesTable;
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CRUDTableProps } from "../../../../../../types/CRUDTable.tsx";
|
||||
import { DealProductServiceSchema, UserSchema } from "../../../../../../client";
|
||||
import { CardProductServiceSchema, UserSchema } from "../../../../../../client";
|
||||
import { FC, useState } from "react";
|
||||
import useProductServicesTableColumns from "./columns.tsx";
|
||||
import { BaseTable } from "../../../../../../components/BaseTable/BaseTable.tsx";
|
||||
@@ -10,7 +10,7 @@ import { modals } from "@mantine/modals";
|
||||
import SimpleUsersTable from "../../../../components/SimpleUsersTable/SimpleUsersTable.tsx";
|
||||
import { useSelector } from "react-redux";
|
||||
import { RootState } from "../../../../../../redux/store.ts";
|
||||
import useDealProductAndServiceTabState from "../../hooks/useProductAndServiceTabState.tsx";
|
||||
import useCardProductAndServiceTabState from "../../hooks/useProductAndServiceTabState.tsx";
|
||||
|
||||
type RestProps = {
|
||||
quantity: number;
|
||||
@@ -18,7 +18,7 @@ type RestProps = {
|
||||
onKitAdd?: () => void;
|
||||
};
|
||||
|
||||
type Props = CRUDTableProps<DealProductServiceSchema> & RestProps;
|
||||
type Props = CRUDTableProps<CardProductServiceSchema> & RestProps;
|
||||
const ProductServicesTable: FC<Props> = ({
|
||||
items,
|
||||
quantity,
|
||||
@@ -28,15 +28,15 @@ const ProductServicesTable: FC<Props> = ({
|
||||
onCopyServices,
|
||||
onKitAdd,
|
||||
}) => {
|
||||
const { dealState } = useDealProductAndServiceTabState();
|
||||
const isLocked = Boolean(dealState.deal?.billRequest);
|
||||
const { cardState } = useCardProductAndServiceTabState();
|
||||
const isLocked = Boolean(cardState.card?.billRequest);
|
||||
const authState = useSelector((state: RootState) => state.auth);
|
||||
|
||||
const columns = useProductServicesTableColumns({ data: items, quantity });
|
||||
const serviceIds = items.map(service => service.service.id);
|
||||
|
||||
const [currentService, setCurrentService] = useState<
|
||||
DealProductServiceSchema | undefined
|
||||
CardProductServiceSchema | undefined
|
||||
>();
|
||||
const [employeesModalVisible, setEmployeesModalVisible] = useState(false);
|
||||
|
||||
@@ -53,7 +53,7 @@ const ProductServicesTable: FC<Props> = ({
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeClick = (item: DealProductServiceSchema) => {
|
||||
const onChangeClick = (item: CardProductServiceSchema) => {
|
||||
if (!onChange) return;
|
||||
modals.openContextModal({
|
||||
modal: "productServiceForm",
|
||||
@@ -66,7 +66,7 @@ const ProductServicesTable: FC<Props> = ({
|
||||
withCloseButton: false,
|
||||
});
|
||||
};
|
||||
const onEmployeeClick = (item: DealProductServiceSchema) => {
|
||||
const onEmployeeClick = (item: CardProductServiceSchema) => {
|
||||
if (!onChange) return;
|
||||
setCurrentService(item);
|
||||
setEmployeesModalVisible(true);
|
||||
@@ -168,7 +168,7 @@ const ProductServicesTable: FC<Props> = ({
|
||||
)}
|
||||
</Flex>
|
||||
),
|
||||
} as MRT_TableOptions<DealProductServiceSchema>
|
||||
} as MRT_TableOptions<CardProductServiceSchema>
|
||||
}
|
||||
/>
|
||||
</Flex>
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useMemo } from "react";
|
||||
import { MRT_ColumnDef } from "mantine-react-table";
|
||||
import { DealProductServiceSchema } from "../../../../../../client";
|
||||
import { CardProductServiceSchema } from "../../../../../../client";
|
||||
import { useSelector } from "react-redux";
|
||||
import { RootState } from "../../../../../../redux/store.ts";
|
||||
|
||||
type Props = {
|
||||
data: DealProductServiceSchema[];
|
||||
data: CardProductServiceSchema[];
|
||||
quantity: number;
|
||||
};
|
||||
const useProductServicesTableColumns = (props: Props) => {
|
||||
@@ -17,7 +17,7 @@ const useProductServicesTableColumns = (props: Props) => {
|
||||
);
|
||||
|
||||
const hideGuestColumns = ["service.cost"];
|
||||
return useMemo<MRT_ColumnDef<DealProductServiceSchema>[]>(
|
||||
return useMemo<MRT_ColumnDef<CardProductServiceSchema>[]>(
|
||||
() => [
|
||||
{
|
||||
accessorKey: "service.name",
|
||||
@@ -1,7 +1,7 @@
|
||||
import { FC } from "react";
|
||||
import {
|
||||
DealProductSchema,
|
||||
DealProductServiceSchema,
|
||||
CardProductSchema,
|
||||
CardProductServiceSchema,
|
||||
GetServiceKitSchema,
|
||||
ProductSchema,
|
||||
} from "../../../../../../client";
|
||||
@@ -12,15 +12,15 @@ import { isNil, isNumber } from "lodash";
|
||||
import { IconBarcode, IconEdit, IconTrash } from "@tabler/icons-react";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { ServiceType } from "../../../../../../shared/enums/ServiceType.ts";
|
||||
import useDealProductAndServiceTabState from "../../hooks/useProductAndServiceTabState.tsx";
|
||||
import useCardProductAndServiceTabState from "../../hooks/useProductAndServiceTabState.tsx";
|
||||
import { useDebouncedCallback } from "@mantine/hooks";
|
||||
|
||||
type Props = {
|
||||
product: DealProductSchema;
|
||||
onChange?: (item: DealProductSchema) => void;
|
||||
onDelete?: (item: DealProductSchema) => void;
|
||||
onCopyServices?: (item: DealProductSchema) => void;
|
||||
onKitAdd?: (item: DealProductSchema, kit: GetServiceKitSchema) => void;
|
||||
product: CardProductSchema;
|
||||
onChange?: (item: CardProductSchema) => void;
|
||||
onDelete?: (item: CardProductSchema) => void;
|
||||
onCopyServices?: (item: CardProductSchema) => void;
|
||||
onKitAdd?: (item: CardProductSchema, kit: GetServiceKitSchema) => void;
|
||||
onProductEdit: (product: ProductSchema) => void;
|
||||
};
|
||||
type ProductFieldNames = {
|
||||
@@ -42,18 +42,18 @@ const ProductView: FC<Props> = ({
|
||||
onKitAdd,
|
||||
onProductEdit,
|
||||
}) => {
|
||||
const { dealState } = useDealProductAndServiceTabState();
|
||||
const debouncedOnChange = useDebouncedCallback(async (item: DealProductSchema) => {
|
||||
const { cardState } = useCardProductAndServiceTabState();
|
||||
const debouncedOnChange = useDebouncedCallback(async (item: CardProductSchema) => {
|
||||
if (!onChange) return;
|
||||
onChange(item);
|
||||
}, 200);
|
||||
const isLocked = Boolean(dealState.deal?.billRequest);
|
||||
const isLocked = Boolean(cardState.card?.billRequest);
|
||||
const onDeleteClick = () => {
|
||||
if (!onDelete) return;
|
||||
onDelete(product);
|
||||
};
|
||||
|
||||
const onServiceDelete = (item: DealProductServiceSchema) => {
|
||||
const onServiceDelete = (item: CardProductServiceSchema) => {
|
||||
if (!onChange) return;
|
||||
onChange({
|
||||
...product,
|
||||
@@ -62,7 +62,7 @@ const ProductView: FC<Props> = ({
|
||||
),
|
||||
});
|
||||
};
|
||||
const onServiceCreate = (item: DealProductServiceSchema) => {
|
||||
const onServiceCreate = (item: CardProductServiceSchema) => {
|
||||
if (!onChange) return;
|
||||
onChange({
|
||||
...product,
|
||||
@@ -70,7 +70,7 @@ const ProductView: FC<Props> = ({
|
||||
});
|
||||
};
|
||||
|
||||
const onServiceChange = (item: DealProductServiceSchema) => {
|
||||
const onServiceChange = (item: CardProductServiceSchema) => {
|
||||
if (!onChange) return;
|
||||
onChange({
|
||||
...product,
|
||||
@@ -1,51 +1,51 @@
|
||||
import { CRUDTableProps } from "../../../../../types/CRUDTable.tsx";
|
||||
import { DealProductSchema, DealService, DealServiceSchema } from "../../../../../client";
|
||||
import { useDealPageContext } from "../../../contexts/DealPageContext.tsx";
|
||||
import { CardService, CardServiceSchema, CardProductSchema } from "../../../../../client";
|
||||
import { useCardPageContext } from "../../../contexts/CardPageContext.tsx";
|
||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||
|
||||
const useDealState = () => {
|
||||
const useCardState = () => {
|
||||
|
||||
const { selectedDeal, setSelectedDeal } = useDealPageContext();
|
||||
const { selectedCard, setSelectedCard } = useCardPageContext();
|
||||
const recalculate = async () => {
|
||||
return DealService.recalculateDealPrice({
|
||||
return CardService.recalculateCardPrice({
|
||||
requestBody: {
|
||||
dealId: selectedDeal?.id || -1,
|
||||
cardId: selectedCard?.id || -1,
|
||||
},
|
||||
});
|
||||
};
|
||||
const refetchDeal = async () => {
|
||||
if (!selectedDeal) return;
|
||||
const refetchCard = async () => {
|
||||
if (!selectedCard) return;
|
||||
|
||||
return DealService.getDealById({ dealId: selectedDeal.id }).then(
|
||||
async deal => {
|
||||
setSelectedDeal(deal);
|
||||
return CardService.getCardById({ cardId: selectedCard.id }).then(
|
||||
async card => {
|
||||
setSelectedCard(card);
|
||||
},
|
||||
);
|
||||
};
|
||||
const refetch = async () => {
|
||||
if (!selectedDeal) return;
|
||||
await refetchDeal();
|
||||
if (!selectedCard) return;
|
||||
await refetchCard();
|
||||
const { ok, message } = await recalculate();
|
||||
if (!ok) notifications.guess(ok, { message });
|
||||
|
||||
await refetchDeal();
|
||||
await refetchCard();
|
||||
};
|
||||
return {
|
||||
deal: selectedDeal,
|
||||
card: selectedCard,
|
||||
refetch,
|
||||
};
|
||||
};
|
||||
|
||||
const useDealServicesState = (): CRUDTableProps<DealServiceSchema> => {
|
||||
const { deal, refetch } = useDealState();
|
||||
const useCardServicesState = (): CRUDTableProps<CardServiceSchema> => {
|
||||
const { card, refetch } = useCardState();
|
||||
const refetchAndRecalculate = async () => {
|
||||
await refetch();
|
||||
};
|
||||
const onCreate = (item: DealServiceSchema) => {
|
||||
if (!deal) return;
|
||||
DealService.addDealService({
|
||||
const onCreate = (item: CardServiceSchema) => {
|
||||
if (!card) return;
|
||||
CardService.addCardService({
|
||||
requestBody: {
|
||||
dealId: deal.id,
|
||||
cardId: card.id,
|
||||
serviceId: item.service.id,
|
||||
quantity: item.quantity,
|
||||
price: item.price,
|
||||
@@ -55,11 +55,11 @@ const useDealServicesState = (): CRUDTableProps<DealServiceSchema> => {
|
||||
if (ok) await refetchAndRecalculate();
|
||||
});
|
||||
};
|
||||
const onDelete = (item: DealServiceSchema) => {
|
||||
if (!deal) return;
|
||||
DealService.deleteDealService({
|
||||
const onDelete = (item: CardServiceSchema) => {
|
||||
if (!card) return;
|
||||
CardService.deleteCardService({
|
||||
requestBody: {
|
||||
dealId: deal.id,
|
||||
cardId: card.id,
|
||||
serviceId: item.service.id,
|
||||
},
|
||||
}).then(async ({ ok, message }) => {
|
||||
@@ -67,11 +67,11 @@ const useDealServicesState = (): CRUDTableProps<DealServiceSchema> => {
|
||||
if (ok) await refetchAndRecalculate();
|
||||
});
|
||||
};
|
||||
const onChange = (item: DealServiceSchema) => {
|
||||
if (!deal) return;
|
||||
DealService.updateDealService({
|
||||
const onChange = (item: CardServiceSchema) => {
|
||||
if (!card) return;
|
||||
CardService.updateCardService({
|
||||
requestBody: {
|
||||
dealId: deal.id,
|
||||
cardId: card.id,
|
||||
service: item,
|
||||
},
|
||||
}).then(async ({ ok, message }) => {
|
||||
@@ -80,23 +80,23 @@ const useDealServicesState = (): CRUDTableProps<DealServiceSchema> => {
|
||||
});
|
||||
};
|
||||
return {
|
||||
items: deal?.services || [],
|
||||
items: card?.services || [],
|
||||
onCreate,
|
||||
onDelete,
|
||||
onChange,
|
||||
};
|
||||
};
|
||||
|
||||
const useDealProductsState = (): CRUDTableProps<DealProductSchema> => {
|
||||
const { deal, refetch } = useDealState();
|
||||
const useDealProductsState = (): CRUDTableProps<CardProductSchema> => {
|
||||
const { card, refetch } = useCardState();
|
||||
const refetchAndRecalculate = async () => {
|
||||
await refetch();
|
||||
};
|
||||
const onCreate = (item: DealProductSchema) => {
|
||||
if (!deal) return;
|
||||
DealService.addDealProduct({
|
||||
const onCreate = (item: CardProductSchema) => {
|
||||
if (!card) return;
|
||||
CardService.addCardProduct({
|
||||
requestBody: {
|
||||
dealId: deal.id,
|
||||
cardId: card.id,
|
||||
product: item,
|
||||
},
|
||||
}).then(async ({ ok, message }) => {
|
||||
@@ -104,11 +104,11 @@ const useDealProductsState = (): CRUDTableProps<DealProductSchema> => {
|
||||
if (ok) await refetchAndRecalculate();
|
||||
});
|
||||
};
|
||||
const onDelete = (item: DealProductSchema) => {
|
||||
if (!deal) return;
|
||||
DealService.deleteDealProduct({
|
||||
const onDelete = (item: CardProductSchema) => {
|
||||
if (!card) return;
|
||||
CardService.deleteCardProduct({
|
||||
requestBody: {
|
||||
dealId: deal.id,
|
||||
cardId: card.id,
|
||||
productId: item.product.id,
|
||||
},
|
||||
}).then(async ({ ok, message }) => {
|
||||
@@ -116,11 +116,11 @@ const useDealProductsState = (): CRUDTableProps<DealProductSchema> => {
|
||||
if (ok) await refetchAndRecalculate();
|
||||
});
|
||||
};
|
||||
const onChange = (item: DealProductSchema) => {
|
||||
if (!deal) return;
|
||||
DealService.updateDealProduct({
|
||||
const onChange = (item: CardProductSchema) => {
|
||||
if (!card) return;
|
||||
CardService.updateCardProduct({
|
||||
requestBody: {
|
||||
dealId: deal.id,
|
||||
cardId: card.id,
|
||||
product: item,
|
||||
},
|
||||
}).then(async ({ ok, message }) => {
|
||||
@@ -129,20 +129,20 @@ const useDealProductsState = (): CRUDTableProps<DealProductSchema> => {
|
||||
});
|
||||
};
|
||||
return {
|
||||
items: deal?.products || [],
|
||||
items: card?.products || [],
|
||||
onCreate,
|
||||
onDelete,
|
||||
onChange,
|
||||
};
|
||||
};
|
||||
const useDealProductAndServiceTabState = () => {
|
||||
const dealState = useDealState();
|
||||
const dealProductsState = useDealProductsState();
|
||||
const dealServicesState = useDealServicesState();
|
||||
const useCardProductAndServiceTabState = () => {
|
||||
const cardState = useCardState();
|
||||
const cardProductsState = useDealProductsState();
|
||||
const cardServicesState = useCardServicesState();
|
||||
return {
|
||||
dealState,
|
||||
dealProductsState,
|
||||
dealServicesState,
|
||||
cardState,
|
||||
cardProductsState,
|
||||
cardServicesState,
|
||||
};
|
||||
};
|
||||
export default useDealProductAndServiceTabState;
|
||||
export default useCardProductAndServiceTabState;
|
||||
@@ -8,7 +8,7 @@ import InlineButton from "../../../../components/InlineButton/InlineButton.tsx";
|
||||
|
||||
const ShippingTab = () => {
|
||||
const {
|
||||
onCreateBoxInDealClick,
|
||||
onCreateBoxInCardClick,
|
||||
onCreatePalletClick,
|
||||
} = useShipping();
|
||||
|
||||
@@ -24,7 +24,7 @@ const ShippingTab = () => {
|
||||
<InlineButton onClick={() => onCreatePalletClick()}>
|
||||
Добавить паллет
|
||||
</InlineButton>
|
||||
<InlineButton onClick={() => onCreateBoxInDealClick()}>
|
||||
<InlineButton onClick={() => onCreateBoxInCardClick()}>
|
||||
Добавить короб
|
||||
</InlineButton>
|
||||
<InlineButton onClick={() => onGetDealQrPdfClick()}>
|
||||
@@ -5,8 +5,8 @@ import { ActionIcon, Flex, Tooltip } from "@mantine/core";
|
||||
import { IconEdit, IconTrash } from "@tabler/icons-react";
|
||||
import { MRT_TableOptions } from "mantine-react-table";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { useDealPageContext } from "../../../contexts/DealPageContext.tsx";
|
||||
import useUpdateDeal from "../hooks/useUpdateDeal.tsx";
|
||||
import { useCardPageContext } from "../../../contexts/CardPageContext.tsx";
|
||||
import useUpdateCard from "../hooks/useUpdateCard.tsx";
|
||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ type Props = {
|
||||
|
||||
const BoxesTable = ({ items }: Props) => {
|
||||
const columns = useShippingTableColumns<BoxSchema>({ isBox: true });
|
||||
const { update } = useUpdateDeal();
|
||||
const { selectedDeal: deal } = useDealPageContext();
|
||||
const { update } = useUpdateCard();
|
||||
const { selectedCard: card } = useCardPageContext();
|
||||
|
||||
const onDeleteClick = (box: BoxSchema) => {
|
||||
ShippingService.deleteBox({
|
||||
@@ -31,13 +31,13 @@ const BoxesTable = ({ items }: Props) => {
|
||||
};
|
||||
|
||||
const onEditClick = (box: BoxSchema) => {
|
||||
if (!deal) return;
|
||||
if (!card) return;
|
||||
modals.openContextModal({
|
||||
modal: "shippingProductModal",
|
||||
title: "Редактирование короба",
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
deal,
|
||||
card,
|
||||
updateOnSubmit: update,
|
||||
isBox: true,
|
||||
shippingData: {
|
||||
@@ -5,9 +5,9 @@ import { ActionIcon, Flex, Tooltip } from "@mantine/core";
|
||||
import { IconEdit, IconTrash } from "@tabler/icons-react";
|
||||
import { MRT_TableOptions } from "mantine-react-table";
|
||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||
import useUpdateDeal from "../hooks/useUpdateDeal.tsx";
|
||||
import useUpdateCard from "../hooks/useUpdateCard.tsx";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { useDealPageContext } from "../../../contexts/DealPageContext.tsx";
|
||||
import { useCardPageContext } from "../../../contexts/CardPageContext.tsx";
|
||||
|
||||
|
||||
type Props = {
|
||||
@@ -16,8 +16,8 @@ type Props = {
|
||||
|
||||
const ShippingProductsTable = ({ items }: Props) => {
|
||||
const columns = useShippingTableColumns<ShippingProductSchema>({ isBox: false });
|
||||
const { update } = useUpdateDeal();
|
||||
const { selectedDeal: deal } = useDealPageContext();
|
||||
const { update } = useUpdateCard();
|
||||
const { selectedCard: card } = useCardPageContext();
|
||||
|
||||
const onDeleteClick = (shippingProduct: ShippingProductSchema) => {
|
||||
ShippingService.deleteShippingProduct({
|
||||
@@ -31,13 +31,13 @@ const ShippingProductsTable = ({ items }: Props) => {
|
||||
};
|
||||
|
||||
const onEditClick = (shippingProduct: ShippingProductSchema) => {
|
||||
if (!deal) return;
|
||||
if (!card) return;
|
||||
modals.openContextModal({
|
||||
modal: "shippingProductModal",
|
||||
title: "Редактирование товара на паллете",
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
deal,
|
||||
card,
|
||||
updateOnSubmit: update,
|
||||
isBox: false,
|
||||
shippingData: {
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useDealPageContext } from "../../../contexts/DealPageContext.tsx";
|
||||
import { useCardPageContext } from "../../../contexts/CardPageContext.tsx";
|
||||
import { Accordion, ActionIcon, Button, Center, Group, rem, Stack, Title, Tooltip } from "@mantine/core";
|
||||
import { BoxSchema, PalletSchema, ShippingProductSchema } from "../../../../../client";
|
||||
import ShippingProductsTable from "./ShippingProductsTable.tsx";
|
||||
@@ -7,7 +7,7 @@ import { IconBox, IconPlus, IconSpace, IconTrash } from "@tabler/icons-react";
|
||||
import useShipping from "../hooks/useShipping.tsx";
|
||||
|
||||
const ShippingTree = () => {
|
||||
const { selectedDeal: deal } = useDealPageContext();
|
||||
const { selectedCard: card } = useCardPageContext();
|
||||
|
||||
const {
|
||||
onCreateBoxInPallet,
|
||||
@@ -21,7 +21,7 @@ const ShippingTree = () => {
|
||||
};
|
||||
|
||||
const getPallets = () => {
|
||||
const sortedPallets = sortById(deal?.pallets) as PalletSchema[];
|
||||
const sortedPallets = sortById(card?.pallets) as PalletSchema[];
|
||||
const pallets = sortedPallets?.map((pallet => {
|
||||
palletIds.push(pallet.id.toString());
|
||||
return (
|
||||
@@ -39,8 +39,8 @@ const ShippingTree = () => {
|
||||
);
|
||||
})) ?? [];
|
||||
|
||||
if (deal?.boxes && deal?.boxes.length > 0) {
|
||||
const boxes = deal?.boxes.sort((b1, b2) => (b1.id - b2.id));
|
||||
if (card?.boxes && card?.boxes.length > 0) {
|
||||
const boxes = card?.boxes.sort((b1, b2) => (b1.id - b2.id));
|
||||
const itemValue = "noPallets";
|
||||
const boxesWithoutPallet = (
|
||||
<Accordion.Item key={-1} value={itemValue}>
|
||||
@@ -1,20 +1,20 @@
|
||||
import { useDealPageContext } from "../../../contexts/DealPageContext.tsx";
|
||||
import { CreateBoxInDealSchema, CreateBoxInPalletSchema, ShippingService } from "../../../../../client";
|
||||
import { useCardPageContext } from "../../../contexts/CardPageContext.tsx";
|
||||
import { CreateBoxInCardSchema, CreateBoxInPalletSchema, ShippingService } from "../../../../../client";
|
||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { Text } from "@mantine/core";
|
||||
import useUpdateDeal from "./useUpdateDeal.tsx";
|
||||
import useUpdateCard from "./useUpdateCard.tsx";
|
||||
|
||||
const useShipping = () => {
|
||||
const { selectedDeal: deal } = useDealPageContext();
|
||||
const { update } = useUpdateDeal();
|
||||
const { selectedCard: card } = useCardPageContext();
|
||||
const { update } = useUpdateCard();
|
||||
const palletIds: string[] = [];
|
||||
|
||||
const onCreatePalletClick = () => {
|
||||
if (!deal) return;
|
||||
if (!card) return;
|
||||
|
||||
ShippingService.createPallet({
|
||||
dealId: deal.id,
|
||||
cardId: card.id,
|
||||
})
|
||||
.then(({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
@@ -35,7 +35,7 @@ const useShipping = () => {
|
||||
};
|
||||
|
||||
const onDeletePalletClick = (palletId: number) => {
|
||||
if (!deal) return;
|
||||
if (!card) return;
|
||||
modals.openConfirmModal({
|
||||
title: "Удаление паллета",
|
||||
children: <Text size="sm">Вы уверены что хотите удалить паллет?</Text>,
|
||||
@@ -45,7 +45,7 @@ const useShipping = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const onCreateBox = (data: CreateBoxInPalletSchema | CreateBoxInDealSchema) => {
|
||||
const onCreateBox = (data: CreateBoxInPalletSchema | CreateBoxInCardSchema) => {
|
||||
ShippingService.updateBox({
|
||||
requestBody: {
|
||||
data,
|
||||
@@ -58,8 +58,8 @@ const useShipping = () => {
|
||||
.catch(err => console.log(err));
|
||||
};
|
||||
|
||||
const onCreateBoxInDealClick = () => {
|
||||
onCreateBox({ dealId: deal?.id ?? -1 });
|
||||
const onCreateBoxInCardClick = () => {
|
||||
onCreateBox({ cardId: card?.id ?? -1 });
|
||||
};
|
||||
|
||||
const onCreateBoxInPallet = (palletId: number) => {
|
||||
@@ -67,13 +67,13 @@ const useShipping = () => {
|
||||
};
|
||||
|
||||
const onCreateShippingProduct = (palletId: number) => {
|
||||
if (!deal) return;
|
||||
if (!card) return;
|
||||
modals.openContextModal({
|
||||
modal: "shippingProductModal",
|
||||
title: "Добавление товара на паллет",
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
deal,
|
||||
card,
|
||||
updateOnSubmit: update,
|
||||
isBox: false,
|
||||
shippingData: {
|
||||
@@ -86,7 +86,7 @@ const useShipping = () => {
|
||||
};
|
||||
|
||||
return {
|
||||
onCreateBoxInDealClick,
|
||||
onCreateBoxInCardClick,
|
||||
onCreateBoxInPallet,
|
||||
onCreateShippingProduct,
|
||||
onCreatePalletClick,
|
||||
@@ -1,28 +1,28 @@
|
||||
import { useDealPageContext } from "../../../contexts/DealPageContext.tsx";
|
||||
import { useCardPageContext } from "../../../contexts/CardPageContext.tsx";
|
||||
|
||||
|
||||
const useShippingQrs = () => {
|
||||
const { selectedDeal: deal } = useDealPageContext();
|
||||
const { selectedCard: card } = useCardPageContext();
|
||||
|
||||
const basePdfUrl = `${import.meta.env.VITE_API_URL}/shipping/pdf`;
|
||||
|
||||
const getPdf = (url: string) => {
|
||||
if (!deal) return;
|
||||
if (!card) return;
|
||||
const pdfWindow = window.open(url);
|
||||
if (!pdfWindow) return;
|
||||
pdfWindow.print();
|
||||
};
|
||||
|
||||
const onGetDealQrPdfClick = () => {
|
||||
getPdf(`${basePdfUrl}/deal/${deal?.id}`);
|
||||
getPdf(`${basePdfUrl}/deal/${card?.id}`);
|
||||
};
|
||||
|
||||
const onGetPalletsPdfClick = () => {
|
||||
getPdf(`${basePdfUrl}/pallets/${deal?.id}`);
|
||||
getPdf(`${basePdfUrl}/pallets/${card?.id}`);
|
||||
};
|
||||
|
||||
const onGetBoxesPdfClick = () => {
|
||||
getPdf(`${basePdfUrl}/boxes/${deal?.id}`);
|
||||
getPdf(`${basePdfUrl}/boxes/${card?.id}`);
|
||||
};
|
||||
|
||||
return {
|
||||
18
src/pages/CardsPage/tabs/ShippingTab/hooks/useUpdateCard.tsx
Normal file
18
src/pages/CardsPage/tabs/ShippingTab/hooks/useUpdateCard.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { useCardPageContext } from "../../../contexts/CardPageContext.tsx";
|
||||
import { CardService } from "../../../../../client";
|
||||
|
||||
const useUpdateCard = () => {
|
||||
const { selectedCard, setSelectedCard } = useCardPageContext();
|
||||
|
||||
const update = () => {
|
||||
if (!selectedCard) return;
|
||||
CardService.getCardById({ cardId: selectedCard.id })
|
||||
.then(data => {
|
||||
setSelectedCard(data);
|
||||
});
|
||||
};
|
||||
|
||||
return { update };
|
||||
};
|
||||
|
||||
export default useUpdateCard;
|
||||
@@ -3,11 +3,11 @@ import { ContextModalProps } from "@mantine/modals";
|
||||
import { Button, Flex, NumberInput, rem, Text } from "@mantine/core";
|
||||
import getRestProducts from "../utils/getRestProducts.tsx";
|
||||
import {
|
||||
CreateBoxInDealSchema,
|
||||
CreateBoxInCardSchema,
|
||||
CreateBoxInPalletSchema,
|
||||
CreateShippingProductSchema,
|
||||
DealProductSchema,
|
||||
DealSchema,
|
||||
CardProductSchema,
|
||||
CardSchema,
|
||||
ProductSchema,
|
||||
ShippingService,
|
||||
UpdateBoxSchema,
|
||||
@@ -21,7 +21,7 @@ import ShippingProductSelect from "../components/ShippingProductSelect.tsx";
|
||||
|
||||
type Props = {
|
||||
updateOnSubmit: () => void;
|
||||
deal: DealSchema;
|
||||
card: CardSchema;
|
||||
isBox: boolean;
|
||||
shippingData: Partial<ShippingData>;
|
||||
}
|
||||
@@ -31,7 +31,7 @@ const ShippingProductModal = ({
|
||||
id,
|
||||
innerProps,
|
||||
}: ContextModalProps<Props>) => {
|
||||
const [restProducts, setRestProducts] = useState<Map<number, DealProductSchema>>(new Map());
|
||||
const [restProducts, setRestProducts] = useState<Map<number, CardProductSchema>>(new Map());
|
||||
const [restProductsSelectData, setRestProductSelectData] = useState<ProductSchema[]>([]);
|
||||
|
||||
const getRestProductQuantity = () => {
|
||||
@@ -45,8 +45,8 @@ const ShippingProductModal = ({
|
||||
};
|
||||
|
||||
const findProductById = (productId?: number | null) => {
|
||||
const dealProduct = innerProps.deal.products.find(p => p.product.id === productId);
|
||||
return dealProduct ? dealProduct.product : null;
|
||||
const cardProduct = innerProps.card.products.find(p => p.product.id === productId);
|
||||
return cardProduct ? cardProduct.product : null;
|
||||
};
|
||||
|
||||
const initialValues: ShippingModalForm = {
|
||||
@@ -64,19 +64,19 @@ const ShippingProductModal = ({
|
||||
|
||||
useEffect(() => {
|
||||
const data = getRestProducts({
|
||||
deal: innerProps.deal,
|
||||
card: innerProps.card,
|
||||
unaccountedValues: innerProps.shippingData as UpdateShippingProductSchema | UpdateBoxSchema,
|
||||
});
|
||||
setRestProducts(data.restProducts);
|
||||
setRestProductSelectData(data.restProductsSelectData);
|
||||
}, [innerProps.deal]);
|
||||
}, [innerProps.card]);
|
||||
|
||||
const updateBox = () => {
|
||||
const data = {
|
||||
...innerProps.shippingData,
|
||||
...form.values,
|
||||
productId: form.values.product?.id,
|
||||
} as CreateBoxInPalletSchema | CreateBoxInDealSchema | UpdateBoxSchema;
|
||||
} as CreateBoxInPalletSchema | CreateBoxInCardSchema | UpdateBoxSchema;
|
||||
|
||||
ShippingService.updateBox({
|
||||
requestBody: { data },
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DealProductSchema, DealSchema, ProductSchema } from "../../../../../client";
|
||||
import { CardProductSchema, CardSchema, ProductSchema } from "../../../../../client";
|
||||
|
||||
type UnaccountedValues = {
|
||||
boxId?: number | null;
|
||||
@@ -6,16 +6,16 @@ type UnaccountedValues = {
|
||||
}
|
||||
|
||||
type Props = {
|
||||
deal?: DealSchema;
|
||||
card?: CardSchema;
|
||||
unaccountedValues: UnaccountedValues;
|
||||
}
|
||||
|
||||
const getRestProducts = ({
|
||||
deal,
|
||||
card,
|
||||
unaccountedValues,
|
||||
}: Props) => {
|
||||
const totalProducts = new Map(
|
||||
deal?.products.map(product => product && [product.product.id, product]),
|
||||
card?.products.map(product => product && [product.product.id, product]),
|
||||
);
|
||||
|
||||
const distributedProducts = new Map();
|
||||
@@ -30,12 +30,12 @@ const getRestProducts = ({
|
||||
}
|
||||
};
|
||||
|
||||
deal?.boxes?.forEach((box) => {
|
||||
card?.boxes?.forEach((box) => {
|
||||
if (!box.product || box.id === unaccountedValues.boxId) return;
|
||||
accountProduct(box.product, box.quantity);
|
||||
});
|
||||
|
||||
deal?.pallets?.forEach(pallet => {
|
||||
card?.pallets?.forEach(pallet => {
|
||||
pallet.shippingProducts.forEach(shippingProduct => {
|
||||
if (shippingProduct.id === unaccountedValues.shippingProductId) return;
|
||||
accountProduct(shippingProduct.product, shippingProduct.quantity);
|
||||
@@ -46,7 +46,7 @@ const getRestProducts = ({
|
||||
});
|
||||
});
|
||||
|
||||
const restProducts = new Map<number, DealProductSchema>();
|
||||
const restProducts = new Map<number, CardProductSchema>();
|
||||
|
||||
totalProducts.entries().forEach(([key, product]) => {
|
||||
const distributedProduct = distributedProducts.get(key);
|
||||
@@ -1,28 +1,28 @@
|
||||
import { FC, useState } from "react";
|
||||
import { useDealSummaries } from "../hooks/useDealSummaries.tsx";
|
||||
import { useCardSummaries } from "../hooks/useCardSummaries.tsx";
|
||||
import PageBlock from "../../../components/PageBlock/PageBlock.tsx";
|
||||
import DealEditDrawer from "../drawers/DealEditDrawer/DealEditDrawer.tsx";
|
||||
import { DealPageContextProvider } from "../contexts/DealPageContext.tsx";
|
||||
import CardEditDrawer from "../drawers/CardEditDrawer/CardEditDrawer.tsx";
|
||||
import { CardPageContextProvider } from "../contexts/CardPageContext.tsx";
|
||||
import { rem } from "@mantine/core";
|
||||
import useDealsPageState from "../hooks/useDealsPageState.tsx";
|
||||
import DealsTable from "../components/DealsTable/DealsTable.tsx";
|
||||
import useCardsPageState from "../hooks/useCardsPageState.tsx";
|
||||
import CardsTable from "../components/DealsTable/CardsTable.tsx";
|
||||
import { motion } from "framer-motion";
|
||||
import DealPrefillDrawer from "../drawers/DealPrefillDrawer/DealPrefillDrawer.tsx";
|
||||
import { PrefillDealContextProvider } from "../contexts/PrefillDealContext.tsx";
|
||||
import CardPrefillDrawer from "../drawers/CardPrefillDrawer/CardPrefillDrawer.tsx";
|
||||
import { PrefillCardContextProvider } from "../contexts/PrefillCardContext.tsx";
|
||||
import { useParams } from "@tanstack/react-router";
|
||||
import { PrefillDealsWithExcelContextProvider } from "../contexts/PrefillDealsWithExcelContext.tsx";
|
||||
import { PrefillCardsWithExcelContextProvider } from "../contexts/PrefillDealsWithExcelContext.tsx";
|
||||
import DisplayMode from "../enums/DisplayMode.ts";
|
||||
import LeadsPageHeader from "../components/LeadsPageHeader/LeadsPageHeader.tsx";
|
||||
import useProjects from "../hooks/useProjects.tsx";
|
||||
import Boards from "../../../components/Dnd/Boards/Boards/Boards.tsx";
|
||||
import useBoards from "../hooks/useBoards.tsx";
|
||||
|
||||
export const DealsPage: FC = () => {
|
||||
export const CardsPage: FC = () => {
|
||||
const { projects, refetchProjects } = useProjects();
|
||||
const { data, form } = useDealsPageState({ projects });
|
||||
const { data, form } = useCardsPageState({ projects });
|
||||
const { boards, refetchBoards } = useBoards({ projectId: form.values.project?.id });
|
||||
const { dealId } = useParams({ strict: false });
|
||||
const { summariesRaw, refetch: refetchSummaries } = useDealSummaries();
|
||||
const { summariesRaw, refetch: refetchSummaries } = useCardSummaries();
|
||||
|
||||
const [displayMode, setDisplayMode] = useState<DisplayMode>(
|
||||
DisplayMode.BOARD,
|
||||
@@ -30,7 +30,7 @@ export const DealsPage: FC = () => {
|
||||
|
||||
const getTableBody = () => {
|
||||
return (
|
||||
<DealsTable items={data} />
|
||||
<CardsTable items={data} />
|
||||
);
|
||||
};
|
||||
|
||||
@@ -41,7 +41,6 @@ export const DealsPage: FC = () => {
|
||||
refetchSummaries={refetchSummaries}
|
||||
boards={boards}
|
||||
refetchBoards={refetchBoards}
|
||||
project={form.values.project}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -73,14 +72,15 @@ export const DealsPage: FC = () => {
|
||||
padding: 0,
|
||||
}}
|
||||
>
|
||||
<DealPageContextProvider
|
||||
defaultDealId={(dealId && parseInt(dealId)) || undefined}
|
||||
refetchDeals={async () => {
|
||||
<CardPageContextProvider
|
||||
defaultCardId={(dealId && parseInt(dealId)) || undefined}
|
||||
refetchCards={async () => {
|
||||
await refetchSummaries();
|
||||
}}
|
||||
selectedProject={form.values.project}
|
||||
>
|
||||
<PrefillDealContextProvider>
|
||||
<PrefillDealsWithExcelContextProvider>
|
||||
<PrefillCardContextProvider>
|
||||
<PrefillCardsWithExcelContextProvider>
|
||||
<LeadsPageHeader
|
||||
form={form}
|
||||
displayMode={displayMode}
|
||||
@@ -98,11 +98,11 @@ export const DealsPage: FC = () => {
|
||||
>
|
||||
{getBody()}
|
||||
</PageBlock>
|
||||
<DealEditDrawer />
|
||||
<DealPrefillDrawer />
|
||||
</PrefillDealsWithExcelContextProvider>
|
||||
</PrefillDealContextProvider>
|
||||
</DealPageContextProvider>
|
||||
<CardEditDrawer />
|
||||
<CardPrefillDrawer />
|
||||
</PrefillCardsWithExcelContextProvider>
|
||||
</PrefillCardContextProvider>
|
||||
</CardPageContextProvider>
|
||||
</PageBlock>
|
||||
);
|
||||
};
|
||||
12
src/pages/CardsPage/utils/isModuleInProject.ts
Normal file
12
src/pages/CardsPage/utils/isModuleInProject.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { ProjectSchema } from "../../../client";
|
||||
|
||||
export enum Modules {
|
||||
SERVICES_AND_PRODUCTS = "servicesAndProducts",
|
||||
}
|
||||
|
||||
const isModuleInProject = (module: Modules, project?: ProjectSchema | null) => {
|
||||
console.log(module.toString());
|
||||
return project?.modules.findIndex(m => m.key === module.toString()) !== -1;
|
||||
};
|
||||
|
||||
export default isModuleInProject;
|
||||
@@ -1 +0,0 @@
|
||||
export { DealPage } from "./ui/DealPage";
|
||||
@@ -1,30 +0,0 @@
|
||||
import { useParams } from "@tanstack/react-router";
|
||||
import { DealPageContextProvider, useDealPageContext } from "../../DealsPage/contexts/DealPageContext.tsx";
|
||||
import ProductAndServiceTab from "../../DealsPage/tabs/ProductAndServiceTab/ProductAndServiceTab.tsx";
|
||||
import { FC, useEffect } from "react";
|
||||
import { DealService } from "../../../client";
|
||||
|
||||
export type Props = {
|
||||
dealId: number;
|
||||
};
|
||||
const DealPageContent: FC<Props> = ({ dealId }) => {
|
||||
const { setSelectedDeal } = useDealPageContext();
|
||||
useEffect(() => {
|
||||
DealService.getDealById({ dealId }).then(deal => {
|
||||
setSelectedDeal(deal);
|
||||
});
|
||||
}, []);
|
||||
return <ProductAndServiceTab />;
|
||||
};
|
||||
const DealPageWrapper: FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
return <DealPageContextProvider refetchDeals={async () => {
|
||||
}}>{children}</DealPageContextProvider>;
|
||||
};
|
||||
export const DealPage = () => {
|
||||
const { dealId } = useParams({ strict: false });
|
||||
return (
|
||||
<DealPageWrapper>
|
||||
<DealPageContent dealId={parseInt(dealId || "-1")} />
|
||||
</DealPageWrapper>
|
||||
);
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user