feat: generation of modules from the server, moved modules fields from the general tab
This commit is contained in:
		
							
								
								
									
										3
									
								
								generateModules.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										3
									
								
								generateModules.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
sudo npx tsc ./src/modules/modulesFileGen/modulesFileGen.ts
 | 
			
		||||
mv -f ./src/modules/modulesFileGen/modulesFileGen.js ./src/modules/modulesFileGen/modulesFileGen.cjs
 | 
			
		||||
sudo node ./src/modules/modulesFileGen/modulesFileGen.cjs
 | 
			
		||||
@@ -62,6 +62,7 @@
 | 
			
		||||
        "@types/eslint__js": "^8.42.3",
 | 
			
		||||
        "@types/file-saver": "^2.0.7",
 | 
			
		||||
        "@types/lodash": "^4.17.7",
 | 
			
		||||
        "@types/node": "^22.13.9",
 | 
			
		||||
        "@types/react": "^18.3.3",
 | 
			
		||||
        "@types/react-dom": "^18.3.0",
 | 
			
		||||
        "@typescript-eslint/eslint-plugin": "^7.16.1",
 | 
			
		||||
 
 | 
			
		||||
@@ -300,6 +300,9 @@ export type { ProductGenerateBarcodeResponse } from './models/ProductGenerateBar
 | 
			
		||||
export type { ProductGetBarcodeImageResponse } from './models/ProductGetBarcodeImageResponse';
 | 
			
		||||
export type { ProductGetResponse } from './models/ProductGetResponse';
 | 
			
		||||
export type { ProductImageSchema } from './models/ProductImageSchema';
 | 
			
		||||
export type { ProductsAndServicesGeneralInfoRequest } from './models/ProductsAndServicesGeneralInfoRequest';
 | 
			
		||||
export type { ProductsAndServicesGeneralInfoResponse } from './models/ProductsAndServicesGeneralInfoResponse';
 | 
			
		||||
export type { ProductsAndServicesGeneralInfoSchema } from './models/ProductsAndServicesGeneralInfoSchema';
 | 
			
		||||
export type { ProductSchema } from './models/ProductSchema';
 | 
			
		||||
export type { ProductUpdateRequest } from './models/ProductUpdateRequest';
 | 
			
		||||
export type { ProductUpdateResponse } from './models/ProductUpdateResponse';
 | 
			
		||||
@@ -357,6 +360,10 @@ export type { UpdateBoardOrderRequest } from './models/UpdateBoardOrderRequest';
 | 
			
		||||
export type { UpdateBoardOrderResponse } from './models/UpdateBoardOrderResponse';
 | 
			
		||||
export type { UpdateBoardRequest } from './models/UpdateBoardRequest';
 | 
			
		||||
export type { UpdateBoardResponse } from './models/UpdateBoardResponse';
 | 
			
		||||
export type { UpdateCardClientRequest } from './models/UpdateCardClientRequest';
 | 
			
		||||
export type { UpdateCardClientResponse } from './models/UpdateCardClientResponse';
 | 
			
		||||
export type { UpdateCardManagerRequest } from './models/UpdateCardManagerRequest';
 | 
			
		||||
export type { UpdateCardManagerResponse } from './models/UpdateCardManagerResponse';
 | 
			
		||||
export type { UpdateDepartmentRequest } from './models/UpdateDepartmentRequest';
 | 
			
		||||
export type { UpdateDepartmentResponse } from './models/UpdateDepartmentResponse';
 | 
			
		||||
export type { UpdateDepartmentSectionRequest } from './models/UpdateDepartmentSectionRequest';
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,6 @@ export type CardGeneralInfoSchema = {
 | 
			
		||||
    isDeleted: boolean;
 | 
			
		||||
    isCompleted: boolean;
 | 
			
		||||
    comment: string;
 | 
			
		||||
    shippingWarehouse?: (string | null);
 | 
			
		||||
    manager?: (UserSchema | null);
 | 
			
		||||
    boardId: number;
 | 
			
		||||
    statusId: number;
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ export type ModuleSchema = {
 | 
			
		||||
    id: number;
 | 
			
		||||
    key: string;
 | 
			
		||||
    label: string;
 | 
			
		||||
    iconName?: (string | null);
 | 
			
		||||
    isDeleted: boolean;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								src/client/models/ProductsAndServicesGeneralInfoRequest.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/client/models/ProductsAndServicesGeneralInfoRequest.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do not edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
import type { ProductsAndServicesGeneralInfoSchema } from './ProductsAndServicesGeneralInfoSchema';
 | 
			
		||||
export type ProductsAndServicesGeneralInfoRequest = {
 | 
			
		||||
    cardId: number;
 | 
			
		||||
    data: ProductsAndServicesGeneralInfoSchema;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do not edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
export type ProductsAndServicesGeneralInfoResponse = {
 | 
			
		||||
    ok: boolean;
 | 
			
		||||
    message: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,8 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do not edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
export type ProductsAndServicesGeneralInfoSchema = {
 | 
			
		||||
    shippingWarehouse?: (string | null);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								src/client/models/UpdateCardClientRequest.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/UpdateCardClientRequest.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do not edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
export type UpdateCardClientRequest = {
 | 
			
		||||
    cardId: number;
 | 
			
		||||
    clientId: number;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								src/client/models/UpdateCardClientResponse.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/UpdateCardClientResponse.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do not edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
export type UpdateCardClientResponse = {
 | 
			
		||||
    ok: boolean;
 | 
			
		||||
    message: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								src/client/models/UpdateCardManagerRequest.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/UpdateCardManagerRequest.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do not edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
export type UpdateCardManagerRequest = {
 | 
			
		||||
    cardId: number;
 | 
			
		||||
    managerId: (number | null);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								src/client/models/UpdateCardManagerResponse.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/UpdateCardManagerResponse.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
/* generated using openapi-typescript-codegen -- do not edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
export type UpdateCardManagerResponse = {
 | 
			
		||||
    ok: boolean;
 | 
			
		||||
    message: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -59,6 +59,12 @@ import type { GetCardProductsBarcodesPdfResponse } from '../models/GetCardProduc
 | 
			
		||||
import type { ManageEmployeeRequest } from '../models/ManageEmployeeRequest';
 | 
			
		||||
import type { ManageEmployeeResponse } from '../models/ManageEmployeeResponse';
 | 
			
		||||
import type { ParseCardsExcelResponse } from '../models/ParseCardsExcelResponse';
 | 
			
		||||
import type { ProductsAndServicesGeneralInfoRequest } from '../models/ProductsAndServicesGeneralInfoRequest';
 | 
			
		||||
import type { ProductsAndServicesGeneralInfoResponse } from '../models/ProductsAndServicesGeneralInfoResponse';
 | 
			
		||||
import type { UpdateCardClientRequest } from '../models/UpdateCardClientRequest';
 | 
			
		||||
import type { UpdateCardClientResponse } from '../models/UpdateCardClientResponse';
 | 
			
		||||
import type { UpdateCardManagerRequest } from '../models/UpdateCardManagerRequest';
 | 
			
		||||
import type { UpdateCardManagerResponse } from '../models/UpdateCardManagerResponse';
 | 
			
		||||
import type { CancelablePromise } from '../core/CancelablePromise';
 | 
			
		||||
import { OpenAPI } from '../core/OpenAPI';
 | 
			
		||||
import { request as __request } from '../core/request';
 | 
			
		||||
@@ -236,6 +242,66 @@ export class CardService {
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Update Products And Services General Info
 | 
			
		||||
     * @returns ProductsAndServicesGeneralInfoResponse Successful Response
 | 
			
		||||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public static updateProductsAndServicesGeneralInfo({
 | 
			
		||||
        requestBody,
 | 
			
		||||
    }: {
 | 
			
		||||
        requestBody: ProductsAndServicesGeneralInfoRequest,
 | 
			
		||||
    }): CancelablePromise<ProductsAndServicesGeneralInfoResponse> {
 | 
			
		||||
        return __request(OpenAPI, {
 | 
			
		||||
            method: 'POST',
 | 
			
		||||
            url: '/card/update-products-and-services-general-info',
 | 
			
		||||
            body: requestBody,
 | 
			
		||||
            mediaType: 'application/json',
 | 
			
		||||
            errors: {
 | 
			
		||||
                422: `Validation Error`,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Update Card Manager
 | 
			
		||||
     * @returns UpdateCardManagerResponse Successful Response
 | 
			
		||||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public static updateCardManager({
 | 
			
		||||
        requestBody,
 | 
			
		||||
    }: {
 | 
			
		||||
        requestBody: UpdateCardManagerRequest,
 | 
			
		||||
    }): CancelablePromise<UpdateCardManagerResponse> {
 | 
			
		||||
        return __request(OpenAPI, {
 | 
			
		||||
            method: 'POST',
 | 
			
		||||
            url: '/card/update-card-manager',
 | 
			
		||||
            body: requestBody,
 | 
			
		||||
            mediaType: 'application/json',
 | 
			
		||||
            errors: {
 | 
			
		||||
                422: `Validation Error`,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Update Card Client
 | 
			
		||||
     * @returns UpdateCardClientResponse Successful Response
 | 
			
		||||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public static updateCardClient({
 | 
			
		||||
        requestBody,
 | 
			
		||||
    }: {
 | 
			
		||||
        requestBody: UpdateCardClientRequest,
 | 
			
		||||
    }): CancelablePromise<UpdateCardClientResponse> {
 | 
			
		||||
        return __request(OpenAPI, {
 | 
			
		||||
            method: 'POST',
 | 
			
		||||
            url: '/card/update-card-client',
 | 
			
		||||
            body: requestBody,
 | 
			
		||||
            mediaType: 'application/json',
 | 
			
		||||
            errors: {
 | 
			
		||||
                422: `Validation Error`,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Add Kit To Card
 | 
			
		||||
     * @returns CardAddKitResponse Successful Response
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ import { UseFormReturnType } from "@mantine/form";
 | 
			
		||||
import { rem, Stack } from "@mantine/core";
 | 
			
		||||
import { ReactNode } from "react";
 | 
			
		||||
import CardAttributeField from "./components/CardAttributeField.tsx";
 | 
			
		||||
import { CardGeneralFormType } from "../../pages/CardsPage/tabs/GeneralTab/GeneralTab.tsx";
 | 
			
		||||
import { CardGeneralFormType } from "../../pages/CardsPage/drawers/CardEditDrawer/tabs/GeneralTab/GeneralTab.tsx";
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
    project: ProjectSchema;
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ import { AttributeSchema } from "../../../client";
 | 
			
		||||
import { Checkbox, Group, NumberInput, TextInput, Tooltip } from "@mantine/core";
 | 
			
		||||
import { UseFormReturnType } from "@mantine/form";
 | 
			
		||||
import { DatePickerInput, DateTimePicker } from "@mantine/dates";
 | 
			
		||||
import { CardGeneralFormType } from "../../../pages/CardsPage/tabs/GeneralTab/GeneralTab.tsx";
 | 
			
		||||
import { CardGeneralFormType } from "../../../pages/CardsPage/drawers/CardEditDrawer/tabs/GeneralTab/GeneralTab.tsx";
 | 
			
		||||
import { IconInfoCircle } from "@tabler/icons-react";
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ type SelectProps = Omit<ObjectSelectProps<StatusSchema | null>, "data" | "getLab
 | 
			
		||||
 | 
			
		||||
type Props = OtherProps & SelectProps;
 | 
			
		||||
 | 
			
		||||
const DealStatusSelect: FC<Props> = ({ board, ...props}) => {
 | 
			
		||||
const CardStatusSelect: FC<Props> = ({ board, ...props}) => {
 | 
			
		||||
    const [isInitial, setIsInitial] = useState<boolean>(true);
 | 
			
		||||
 | 
			
		||||
    const filteredData = board?.statuses.filter(
 | 
			
		||||
@@ -37,4 +37,4 @@ const DealStatusSelect: FC<Props> = ({ board, ...props}) => {
 | 
			
		||||
        />
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
export default DealStatusSelect;
 | 
			
		||||
export default CardStatusSelect;
 | 
			
		||||
@@ -5,6 +5,8 @@ import { Flex, rem, Text, TextInput, useMantineColorScheme } from "@mantine/core
 | 
			
		||||
import { IconGripHorizontal } from "@tabler/icons-react";
 | 
			
		||||
import { useDebouncedValue } from "@mantine/hooks";
 | 
			
		||||
import { notifications } from "../../../../shared/lib/notifications.ts";
 | 
			
		||||
import { useProjectsContext } from "../../../../contexts/ProjectsContext.tsx";
 | 
			
		||||
import isModuleInProject, { Modules } from "../../../../modules/utils/isModuleInProject.ts";
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
    cards: CardSummary[];
 | 
			
		||||
@@ -15,6 +17,9 @@ export const CardGroupView: FC<Props> = ({ cards, group }) => {
 | 
			
		||||
    const theme = useMantineColorScheme();
 | 
			
		||||
    const [name, setName] = useState<string>(group.name || "");
 | 
			
		||||
    const [debouncedName] = useDebouncedValue(name, 200);
 | 
			
		||||
    const { selectedProject } = useProjectsContext();
 | 
			
		||||
    const isServicesAndProductsIncluded = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, selectedProject);
 | 
			
		||||
 | 
			
		||||
    const totalPrice = useMemo(() => cards.reduce((acc, card) => acc + card.totalPrice, 0), [cards]);
 | 
			
		||||
    const totalProducts = useMemo(() => cards.reduce((acc, card) => acc + card.totalProducts, 0), [cards]);
 | 
			
		||||
    const updateName = () => {
 | 
			
		||||
@@ -70,19 +75,21 @@ export const CardGroupView: FC<Props> = ({ cards, group }) => {
 | 
			
		||||
                    />
 | 
			
		||||
                ))}
 | 
			
		||||
            </Flex>
 | 
			
		||||
            <Flex
 | 
			
		||||
                p={rem(10)}
 | 
			
		||||
                direction={"column"}
 | 
			
		||||
                bg={theme.colorScheme === "dark" ? "var(--mantine-color-dark-6)" : "var(--mantine-color-gray-2)"}
 | 
			
		||||
                style={{ borderRadius: "0.5rem" }}
 | 
			
		||||
            >
 | 
			
		||||
                <Text
 | 
			
		||||
                    c={"gray.6"}
 | 
			
		||||
                    size={"xs"}>Сумма: {totalPrice.toLocaleString("ru-RU")} руб.</Text>
 | 
			
		||||
                <Text
 | 
			
		||||
                    c={"gray.6"}
 | 
			
		||||
                    size={"xs"}>Всего товаров: {totalProducts.toLocaleString("ru-RU")} шт.</Text>
 | 
			
		||||
            </Flex>
 | 
			
		||||
            {isServicesAndProductsIncluded && (
 | 
			
		||||
                <Flex
 | 
			
		||||
                    p={rem(10)}
 | 
			
		||||
                    direction={"column"}
 | 
			
		||||
                    bg={theme.colorScheme === "dark" ? "var(--mantine-color-dark-6)" : "var(--mantine-color-gray-2)"}
 | 
			
		||||
                    style={{ borderRadius: "0.5rem" }}
 | 
			
		||||
                >
 | 
			
		||||
                    <Text
 | 
			
		||||
                        c={"gray.6"}
 | 
			
		||||
                        size={"xs"}>Сумма: {totalPrice.toLocaleString("ru-RU")} руб.</Text>
 | 
			
		||||
                    <Text
 | 
			
		||||
                        c={"gray.6"}
 | 
			
		||||
                        size={"xs"}>Всего товаров: {totalProducts.toLocaleString("ru-RU")} шт.</Text>
 | 
			
		||||
                </Flex>
 | 
			
		||||
            )}
 | 
			
		||||
        </Flex>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
@@ -9,7 +9,7 @@ import { faCheck } from "@fortawesome/free-solid-svg-icons";
 | 
			
		||||
import { IconCheck, IconLayoutGridRemove, IconTrash } from "@tabler/icons-react";
 | 
			
		||||
import { useContextMenu } from "mantine-contextmenu";
 | 
			
		||||
import useCardSummaryState from "./useCardSummaryState.tsx";
 | 
			
		||||
import isModuleInProject, { Modules } from "../../../../pages/CardsPage/utils/isModuleInProject.ts";
 | 
			
		||||
import isModuleInProject, { Modules } from "../../../../modules/utils/isModuleInProject.ts";
 | 
			
		||||
import { useProjectsContext } from "../../../../contexts/ProjectsContext.tsx";
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ import { groupBy, has, uniq } from "lodash";
 | 
			
		||||
import { CardGroupView } from "../CardGroupView/CardGroupView.tsx";
 | 
			
		||||
import CreateDealsFromFileButton from "../CreateCardsFromFileButton/CreateDealsFromFileButton.tsx";
 | 
			
		||||
import DragState from "../../../../pages/CardsPage/enums/DragState.ts";
 | 
			
		||||
import isModuleInProject, { Modules } from "../../../../pages/CardsPage/utils/isModuleInProject.ts";
 | 
			
		||||
import isModuleInProject, { Modules } from "../../../../modules/utils/isModuleInProject.ts";
 | 
			
		||||
import { useProjectsContext } from "../../../../contexts/ProjectsContext.tsx";
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ import { CardService, StatusSchema } from "../../../../client";
 | 
			
		||||
import { useQueryClient } from "@tanstack/react-query";
 | 
			
		||||
import { dateWithoutTimezone } from "../../../../shared/lib/date.ts";
 | 
			
		||||
import { usePrefillCardContext } from "../../../../pages/CardsPage/contexts/PrefillCardContext.tsx";
 | 
			
		||||
import isModuleInProject, { Modules } from "../../../../pages/CardsPage/utils/isModuleInProject.ts";
 | 
			
		||||
import isModuleInProject, { Modules } from "../../../../modules/utils/isModuleInProject.ts";
 | 
			
		||||
import { useProjectsContext } from "../../../../contexts/ProjectsContext.tsx";
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ import ShippingWarehouseAutocomplete
 | 
			
		||||
    from "../../../Selects/ShippingWarehouseAutocomplete/ShippingWarehouseAutocomplete.tsx";
 | 
			
		||||
import BaseMarketplaceSelect from "../../../Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
 | 
			
		||||
import { usePrefillCardContext } from "../../../../pages/CardsPage/contexts/PrefillCardContext.tsx";
 | 
			
		||||
import isModuleInProject, { Modules } from "../../../../pages/CardsPage/utils/isModuleInProject.ts";
 | 
			
		||||
import isModuleInProject, { Modules } from "../../../../modules/utils/isModuleInProject.ts";
 | 
			
		||||
import { useProjectsContext } from "../../../../contexts/ProjectsContext.tsx";
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ import DragState from "../../../../pages/CardsPage/enums/DragState.ts";
 | 
			
		||||
import { useContextMenu } from "mantine-contextmenu";
 | 
			
		||||
import { IconEdit, IconTrash } from "@tabler/icons-react";
 | 
			
		||||
import useStatus from "./hooks/useStatus.tsx";
 | 
			
		||||
import isModuleInProject, { Modules } from "../../../../pages/CardsPage/utils/isModuleInProject.ts";
 | 
			
		||||
import isModuleInProject, { Modules } from "../../../../modules/utils/isModuleInProject.ts";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ type Props = Omit<
 | 
			
		||||
    ObjectSelectProps<UserSchema | null>,
 | 
			
		||||
    "data" | "getValueFn" | "getLabelFn"
 | 
			
		||||
>;
 | 
			
		||||
const UserSelect: FC<Props> = props => {
 | 
			
		||||
const ManagerSelect: FC<Props> = props => {
 | 
			
		||||
    const { objects: managers } = useManagersList();
 | 
			
		||||
    return (
 | 
			
		||||
        <ObjectSelect
 | 
			
		||||
@@ -21,4 +21,4 @@ const UserSelect: FC<Props> = props => {
 | 
			
		||||
        />
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
export default UserSelect;
 | 
			
		||||
export default ManagerSelect;
 | 
			
		||||
 
 | 
			
		||||
@@ -9,8 +9,9 @@ type Props = {
 | 
			
		||||
    withLabel?: boolean;
 | 
			
		||||
    error?: string;
 | 
			
		||||
    inputContainer?: (children: ReactNode) => ReactNode;
 | 
			
		||||
    disabled?: boolean;
 | 
			
		||||
};
 | 
			
		||||
const ClientSelect: FC<Props> = ({ value, onChange, error, inputContainer, withLabel = false }) => {
 | 
			
		||||
const ClientSelect: FC<Props> = ({ value, onChange, error, inputContainer, withLabel = false, disabled = false }) => {
 | 
			
		||||
    const { clients } = useClientsList();
 | 
			
		||||
    const options = clients.map(client => ({
 | 
			
		||||
        label: client.name,
 | 
			
		||||
@@ -37,6 +38,7 @@ const ClientSelect: FC<Props> = ({ value, onChange, error, inputContainer, withL
 | 
			
		||||
            label={withLabel && "Клиент"}
 | 
			
		||||
            error={error}
 | 
			
		||||
            inputContainer={inputContainer}
 | 
			
		||||
            disabled={disabled}
 | 
			
		||||
        />
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@ function usePollingEffect(
 | 
			
		||||
): void {
 | 
			
		||||
    const { interval = 3000, isActive = true, onCleanUp = () => {} } = options;
 | 
			
		||||
 | 
			
		||||
    const timeoutIdRef = useRef<number | null>(null);
 | 
			
		||||
    const timeoutIdRef = useRef<NodeJS.Timeout | number | null>(null);
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        if (!isActive) {
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ import { modals } from "./modals/modals.ts";
 | 
			
		||||
import TasksProvider from "./providers/TasksProvider/TasksProvider.tsx";
 | 
			
		||||
import { ContextMenuProvider } from "mantine-contextmenu";
 | 
			
		||||
import { ProjectsContextProvider } from "./contexts/ProjectsContext.tsx";
 | 
			
		||||
import { ModulesContextProvider } from "./modules/context/ModulesContext.tsx";
 | 
			
		||||
 | 
			
		||||
// Configuring router
 | 
			
		||||
const router = createRouter({ routeTree });
 | 
			
		||||
@@ -56,8 +57,10 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
 | 
			
		||||
                        <DatesProvider settings={{ locale: "ru" }}>
 | 
			
		||||
                            <TasksProvider>
 | 
			
		||||
                                <ProjectsContextProvider>
 | 
			
		||||
                                    <RouterProvider router={router} />
 | 
			
		||||
                                    <Notifications />
 | 
			
		||||
                                    <ModulesContextProvider>
 | 
			
		||||
                                        <RouterProvider router={router} />
 | 
			
		||||
                                        <Notifications />
 | 
			
		||||
                                    </ModulesContextProvider>
 | 
			
		||||
                                </ProjectsContextProvider>
 | 
			
		||||
                            </TasksProvider>
 | 
			
		||||
                        </DatesProvider>
 | 
			
		||||
 
 | 
			
		||||
@@ -3,13 +3,13 @@ import CreateServiceCategoryModal from "../pages/ServicesPage/modals/CreateServi
 | 
			
		||||
import CreateServiceModal from "../pages/ServicesPage/modals/CreateServiceModal.tsx";
 | 
			
		||||
import createProductModal from "../pages/ProductsPage/modals/CreateProductModal/CreateProductModal.tsx";
 | 
			
		||||
import ProductFormModal from "../pages/ClientsPage/modals/ClientFormModal/ClientFormModal.tsx";
 | 
			
		||||
import AddCardServiceModal from "../pages/CardsPage/modals/AddCardServiceModal.tsx";
 | 
			
		||||
import AddCardProductModal from "../pages/CardsPage/modals/AddCardProductModal.tsx";
 | 
			
		||||
import AddCardServiceModal from "../modules/cardModules/cardEditorTabs/ProductAndServiceTab/modals/AddCardServiceModal.tsx";
 | 
			
		||||
import AddCardProductModal from "../modules/cardModules/cardEditorTabs/ProductAndServiceTab/modals/AddCardProductModal.tsx";
 | 
			
		||||
import PrintBarcodeModal from "./PrintBarcodeModal/PrintBarcodeModal.tsx";
 | 
			
		||||
import AddBarcodeModal from "./AddBarcodeModal/AddBarcodeModal.tsx";
 | 
			
		||||
import BarcodeTemplateFormModal
 | 
			
		||||
    from "../pages/BarcodePage/modals/BarcodeTemplateFormModal/BarcodeTemplateFormModal.tsx";
 | 
			
		||||
import ProductServiceFormModal from "../pages/CardsPage/modals/ProductServiceFormModal.tsx";
 | 
			
		||||
import ProductServiceFormModal from "../modules/cardModules/cardEditorTabs/ProductAndServiceTab/modals/ProductServiceFormModal.tsx";
 | 
			
		||||
import UserFormModal from "../pages/AdminPage/modals/UserFormModal/UserFormModal.tsx";
 | 
			
		||||
import EmployeeSelectModal from "./EmployeeSelectModal/EmployeeSelectModal.tsx";
 | 
			
		||||
import EmployeeTableModal from "./EmployeeTableModal/EmployeeTableModal.tsx";
 | 
			
		||||
@@ -24,11 +24,11 @@ import MarketplaceFormModal from "../pages/MarketplacesPage/modals/MarketplaceFo
 | 
			
		||||
import ScanningModal from "./ScanningModal/ScanningModal.tsx";
 | 
			
		||||
import TransactionFormModal from "../pages/AdminPage/tabs/Transactions/modals/TransactionFormModal.tsx";
 | 
			
		||||
import TransactionTagsModal from "../pages/AdminPage/tabs/Transactions/modals/TransactionTagsModal.tsx";
 | 
			
		||||
import ShippingProductModal from "../pages/CardsPage/tabs/ShippingTab/modals/ShippingProductModal.tsx";
 | 
			
		||||
import ShippingProductModal from "../modules/cardModules/cardEditorTabs/ShippingTab/modals/ShippingProductModal.tsx";
 | 
			
		||||
import DepartmentModal from "../pages/AdminPage/tabs/OrganizationalStructureTab/modals/DepartmentModal.tsx";
 | 
			
		||||
import AddUserToDepartmentModal
 | 
			
		||||
    from "../pages/AdminPage/tabs/OrganizationalStructureTab/modals/AddUserToDepartmentModal.tsx";
 | 
			
		||||
import AssignEmployeeModal from "../pages/CardsPage/tabs/EmployeesTab/modals/AssignEmployeeModal.tsx";
 | 
			
		||||
import AssignEmployeeModal from "../modules/cardModules/cardEditorTabs/EmployeesTab/modals/AssignEmployeeModal.tsx";
 | 
			
		||||
import ResidualProductModal from "../pages/ResiduesPage/modals/ResidualProductModal/ResidualProductModal.tsx";
 | 
			
		||||
import NewReceiptModal from "../pages/ReceiptPage/components/NewReceipt/modals/NewReceiptModal.tsx";
 | 
			
		||||
import ReceiptModal from "../pages/ReceiptPage/components/ReceiptEditing/modals/ReceiptModal.tsx";
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										147
									
								
								src/modules/cardModules/cardEditorTabs/ClientTab/ClientTab.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								src/modules/cardModules/cardEditorTabs/ClientTab/ClientTab.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,147 @@
 | 
			
		||||
import { Button, Fieldset, Group, rem, Stack, Textarea, TextInput } from "@mantine/core";
 | 
			
		||||
import { useCardPageContext } from "../../../../pages/CardsPage/contexts/CardPageContext.tsx";
 | 
			
		||||
import { useForm } from "@mantine/form";
 | 
			
		||||
import { CardService, ClientSchema, ClientService } from "../../../../client";
 | 
			
		||||
import { notifications } from "../../../../shared/lib/notifications.ts";
 | 
			
		||||
import ClientSelect from "../../../../components/Selects/ClientSelect/ClientSelect.tsx";
 | 
			
		||||
import { useEffect, useState } from "react";
 | 
			
		||||
import InlineButton from "../../../../components/InlineButton/InlineButton.tsx";
 | 
			
		||||
import { isEqual } from "lodash";
 | 
			
		||||
 | 
			
		||||
const ClientTab = () => {
 | 
			
		||||
    const { selectedCard: card, refetchCard } = useCardPageContext();
 | 
			
		||||
    const [initialValues, setInitialValues] = useState<Partial<ClientSchema>>(card?.client ?? {});
 | 
			
		||||
 | 
			
		||||
    const [client, setClient] = useState<ClientSchema | undefined>(card?.client ?? undefined);
 | 
			
		||||
 | 
			
		||||
    const form = useForm<Partial<ClientSchema>>(
 | 
			
		||||
        {
 | 
			
		||||
            initialValues,
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        const data = card?.client ?? {};
 | 
			
		||||
        setInitialValues(data);
 | 
			
		||||
        form.setValues(data);
 | 
			
		||||
    }, [card]);
 | 
			
		||||
 | 
			
		||||
    const isEditorDisabled = () => client?.id !== card?.client?.id;
 | 
			
		||||
 | 
			
		||||
    const handleSubmitClientInfo = (values: ClientSchema) => {
 | 
			
		||||
        ClientService.updateClient({
 | 
			
		||||
            requestBody: {
 | 
			
		||||
                data: values,
 | 
			
		||||
            },
 | 
			
		||||
        })
 | 
			
		||||
            .then(({ ok, message }) => {
 | 
			
		||||
                if (!ok) {
 | 
			
		||||
                    notifications.error({ message });
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                refetchCard();
 | 
			
		||||
            })
 | 
			
		||||
            .catch(err => console.log(err));
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const handleSelectClient = () => {
 | 
			
		||||
        if (!(card && client)) return;
 | 
			
		||||
 | 
			
		||||
        CardService.updateCardClient({
 | 
			
		||||
            requestBody: {
 | 
			
		||||
                cardId: card?.id,
 | 
			
		||||
                clientId: client?.id,
 | 
			
		||||
            },
 | 
			
		||||
        })
 | 
			
		||||
            .then(({ ok, message }) => {
 | 
			
		||||
                if (!ok) {
 | 
			
		||||
                    notifications.error({ message });
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                refetchCard();
 | 
			
		||||
            })
 | 
			
		||||
            .catch(err => console.log(err));
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const clientDataEditor = (
 | 
			
		||||
        <Fieldset legend={"Данные клиента"} flex={1}>
 | 
			
		||||
            <form
 | 
			
		||||
                onSubmit={
 | 
			
		||||
                    form.onSubmit(values => handleSubmitClientInfo(values as ClientSchema))
 | 
			
		||||
                }>
 | 
			
		||||
                <Stack gap={rem(10)}>
 | 
			
		||||
                    <TextInput
 | 
			
		||||
                        disabled
 | 
			
		||||
                        placeholder={"Название"}
 | 
			
		||||
                        label={"Название"}
 | 
			
		||||
                        value={card?.client?.name}
 | 
			
		||||
                    />
 | 
			
		||||
                    <TextInput
 | 
			
		||||
                        disabled={isEditorDisabled()}
 | 
			
		||||
                        placeholder={"Введите телефон"}
 | 
			
		||||
                        label={"Телефон клиента"}
 | 
			
		||||
                        {...form.getInputProps("details.phoneNumber")}
 | 
			
		||||
                    />
 | 
			
		||||
                    <TextInput
 | 
			
		||||
                        disabled={isEditorDisabled()}
 | 
			
		||||
                        placeholder={"Введите email"}
 | 
			
		||||
                        label={"Email"}
 | 
			
		||||
                        {...form.getInputProps("details.email")}
 | 
			
		||||
                    />
 | 
			
		||||
                    <TextInput
 | 
			
		||||
                        disabled={isEditorDisabled()}
 | 
			
		||||
                        placeholder={"Введите телеграм"}
 | 
			
		||||
                        label={"Телеграм"}
 | 
			
		||||
                        {...form.getInputProps("details.telegram")}
 | 
			
		||||
                    />
 | 
			
		||||
                    <TextInput
 | 
			
		||||
                        disabled={isEditorDisabled()}
 | 
			
		||||
                        placeholder={"Введите ИНН"}
 | 
			
		||||
                        label={"ИНН"}
 | 
			
		||||
                        {...form.getInputProps("details.inn")}
 | 
			
		||||
                    />
 | 
			
		||||
                    <Textarea
 | 
			
		||||
                        disabled={isEditorDisabled()}
 | 
			
		||||
                        placeholder={"Введите комментарий"}
 | 
			
		||||
                        label={"Комментарий"}
 | 
			
		||||
                        {...form.getInputProps("comment")}
 | 
			
		||||
                    />
 | 
			
		||||
                    <Group>
 | 
			
		||||
                        <Button
 | 
			
		||||
                            variant={"default"}
 | 
			
		||||
                            disabled={isEditorDisabled() || isEqual(initialValues, form.values)}
 | 
			
		||||
                            type="submit"
 | 
			
		||||
                        >
 | 
			
		||||
                            Сохранить
 | 
			
		||||
                        </Button>
 | 
			
		||||
                    </Group>
 | 
			
		||||
                </Stack>
 | 
			
		||||
            </form>
 | 
			
		||||
        </Fieldset>
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <Stack flex={1}>
 | 
			
		||||
            <Fieldset legend={"Выбор клиента"} flex={1}>
 | 
			
		||||
                <Stack gap={rem(10)}>
 | 
			
		||||
                    <ClientSelect
 | 
			
		||||
                        value={client}
 | 
			
		||||
                        onChange={setClient}
 | 
			
		||||
                        withLabel
 | 
			
		||||
                        disabled={!isEqual(initialValues, form.values)}
 | 
			
		||||
                    />
 | 
			
		||||
                    <Group>
 | 
			
		||||
                        <InlineButton
 | 
			
		||||
                            onClick={handleSelectClient}
 | 
			
		||||
                            disabled={!isEditorDisabled()}
 | 
			
		||||
                        >
 | 
			
		||||
                            Сохранить
 | 
			
		||||
                        </InlineButton>
 | 
			
		||||
                    </Group>
 | 
			
		||||
                </Stack>
 | 
			
		||||
            </Fieldset>
 | 
			
		||||
            {clientDataEditor}
 | 
			
		||||
        </Stack>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
export default ClientTab;
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { useCardPageContext } from "../../../contexts/CardPageContext.tsx";
 | 
			
		||||
import { useCardPageContext } from "../../../../../pages/CardsPage/contexts/CardPageContext.tsx";
 | 
			
		||||
import { BaseTable } from "../../../../../components/BaseTable/BaseTable.tsx";
 | 
			
		||||
import useEmployeeTableColumns from "../hooks/useEmployeesTableColumns.tsx";
 | 
			
		||||
import { ActionIcon, Flex, Tooltip } from "@mantine/core";
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
 | 
			
		||||
import { modals } from "@mantine/modals";
 | 
			
		||||
import { useCardPageContext } from "../../../contexts/CardPageContext.tsx";
 | 
			
		||||
import { useCardPageContext } from "../../../../../pages/CardsPage/contexts/CardPageContext.tsx";
 | 
			
		||||
import { CardEmployeesSchema, CardService } from "../../../../../client";
 | 
			
		||||
 | 
			
		||||
const useEmployeesTab = () => {
 | 
			
		||||
@@ -0,0 +1,70 @@
 | 
			
		||||
import { Button, Fieldset, Group, rem, Stack } from "@mantine/core";
 | 
			
		||||
import { useCardPageContext } from "../../../../pages/CardsPage/contexts/CardPageContext.tsx";
 | 
			
		||||
import { useForm } from "@mantine/form";
 | 
			
		||||
import { CardSchema, CardService, type UserSchema } from "../../../../client";
 | 
			
		||||
import { notifications } from "../../../../shared/lib/notifications.ts";
 | 
			
		||||
import ManagerSelect from "../../../../components/ManagerSelect/ManagerSelect.tsx";
 | 
			
		||||
import { useEffect, useState } from "react";
 | 
			
		||||
import { isEqual } from "lodash";
 | 
			
		||||
 | 
			
		||||
type ManagerForm = {
 | 
			
		||||
    manager?: (UserSchema | null);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ManagerTab = () => {
 | 
			
		||||
    const { selectedCard: card, refetchCard } = useCardPageContext();
 | 
			
		||||
    const [initialValues, setInitialValues] = useState<ManagerForm>(card as CardSchema);
 | 
			
		||||
 | 
			
		||||
    const form = useForm<ManagerForm>({
 | 
			
		||||
        initialValues,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        const data = card ?? {};
 | 
			
		||||
        setInitialValues(data);
 | 
			
		||||
        form.setValues(data);
 | 
			
		||||
    }, [card]);
 | 
			
		||||
 | 
			
		||||
    const onSubmit = async (values: ManagerForm) => {
 | 
			
		||||
        if (!card) return;
 | 
			
		||||
 | 
			
		||||
        return CardService.updateCardManager({
 | 
			
		||||
            requestBody: {
 | 
			
		||||
                cardId: card.id,
 | 
			
		||||
                managerId: values.manager?.id ?? null,
 | 
			
		||||
            },
 | 
			
		||||
        })
 | 
			
		||||
            .then(({ ok, message }) => {
 | 
			
		||||
                if (!ok) {
 | 
			
		||||
                    notifications.error({ message });
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                refetchCard();
 | 
			
		||||
            })
 | 
			
		||||
            .catch(err => console.log(err));
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <form onSubmit={form.onSubmit(values => onSubmit(values))}>
 | 
			
		||||
            <Fieldset flex={1}>
 | 
			
		||||
                <Stack gap={rem(10)}>
 | 
			
		||||
                    <ManagerSelect
 | 
			
		||||
                        placeholder={"Укажите менеджера"}
 | 
			
		||||
                        label={"Менеджер"}
 | 
			
		||||
                        {...form.getInputProps("manager")}
 | 
			
		||||
                    />
 | 
			
		||||
                    <Group>
 | 
			
		||||
                        <Button
 | 
			
		||||
                            type={"submit"}
 | 
			
		||||
                            variant={"default"}
 | 
			
		||||
                            disabled={isEqual(initialValues, form.values)}
 | 
			
		||||
                        >
 | 
			
		||||
                            Сохранить
 | 
			
		||||
                        </Button>
 | 
			
		||||
                    </Group>
 | 
			
		||||
                </Stack>
 | 
			
		||||
            </Fieldset>
 | 
			
		||||
        </form>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
export default ManagerTab;
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.products-list {
 | 
			
		||||
    width: 60%;
 | 
			
		||||
    width: 52%;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    gap: rem(10);
 | 
			
		||||
@@ -1,16 +1,8 @@
 | 
			
		||||
import { FC } from "react";
 | 
			
		||||
import styles from "./ProductAndServiceTab.module.css";
 | 
			
		||||
import ProductView from "./components/ProductView/ProductView.tsx";
 | 
			
		||||
import {
 | 
			
		||||
    Button,
 | 
			
		||||
    Divider,
 | 
			
		||||
    Flex,
 | 
			
		||||
    rem,
 | 
			
		||||
    ScrollArea,
 | 
			
		||||
    Text,
 | 
			
		||||
    Title,
 | 
			
		||||
} from "@mantine/core";
 | 
			
		||||
import CardServicesTable from "./components/DealServicesTable/CardServicesTable.tsx";
 | 
			
		||||
import { Button, Checkbox, Divider, Flex, Group, rem, ScrollArea, Stack, Text, Title } from "@mantine/core";
 | 
			
		||||
import CardServicesTable from "./components/CardServicesTable/CardServicesTable.tsx";
 | 
			
		||||
import useCardProductAndServiceTabState from "./hooks/useProductAndServiceTabState.tsx";
 | 
			
		||||
import { modals } from "@mantine/modals";
 | 
			
		||||
import {
 | 
			
		||||
@@ -22,17 +14,19 @@ import {
 | 
			
		||||
    ProductService,
 | 
			
		||||
} from "../../../../client";
 | 
			
		||||
import { notifications } from "../../../../shared/lib/notifications.ts";
 | 
			
		||||
import { CreateProductRequest } from "../../../ProductsPage/types.ts";
 | 
			
		||||
import { CreateProductRequest } from "../../../../pages/ProductsPage/types.ts";
 | 
			
		||||
import classNames from "classnames";
 | 
			
		||||
import GeneralDataForm from "./components/GeneralDataForm/GeneralDataForm.tsx";
 | 
			
		||||
import PrintDealBarcodesButton from "./components/PrintDealBarcodesButton/PrintDealBarcodesButton.tsx";
 | 
			
		||||
import PaymentLinkButton from "./components/PaymentLinkButton/PaymentLinkButton.tsx";
 | 
			
		||||
 | 
			
		||||
const ProductAndServiceTab: FC = () => {
 | 
			
		||||
    const { cardState, cardServicesState, cardProductsState } =
 | 
			
		||||
        useCardProductAndServiceTabState();
 | 
			
		||||
    const { cardState, cardServicesState, cardProductsState } = useCardProductAndServiceTabState();
 | 
			
		||||
    const isLocked = Boolean(cardState.card?.billRequest || cardState.card?.group?.billRequest);
 | 
			
		||||
    const onAddProductClick = () => {
 | 
			
		||||
        if (!cardProductsState.onCreate || !cardState.card || !cardState.card.clientId) return;
 | 
			
		||||
        const productIds = cardState.card.products.map(
 | 
			
		||||
            product => product.product.id
 | 
			
		||||
            product => product.product.id,
 | 
			
		||||
        );
 | 
			
		||||
        modals.openContextModal({
 | 
			
		||||
            modal: "addCardProduct",
 | 
			
		||||
@@ -51,26 +45,26 @@ const ProductAndServiceTab: FC = () => {
 | 
			
		||||
                acc +
 | 
			
		||||
                row.services.reduce(
 | 
			
		||||
                    (acc2, row2) => acc2 + row2.price * row.quantity,
 | 
			
		||||
                    0
 | 
			
		||||
                    0,
 | 
			
		||||
                ),
 | 
			
		||||
            0
 | 
			
		||||
            0,
 | 
			
		||||
        );
 | 
			
		||||
        const cardServicesPrice = cardState.card.services.reduce(
 | 
			
		||||
            (acc, row) => acc + row.price * row.quantity,
 | 
			
		||||
            0
 | 
			
		||||
            0,
 | 
			
		||||
        );
 | 
			
		||||
        return cardServicesPrice + productServicesPrice;
 | 
			
		||||
    };
 | 
			
		||||
    const onCopyServices = (
 | 
			
		||||
        sourceProduct: CardProductSchema,
 | 
			
		||||
        destinationProducts: CardProductSchema[]
 | 
			
		||||
        destinationProducts: CardProductSchema[],
 | 
			
		||||
    ) => {
 | 
			
		||||
        if (!cardState.card) return;
 | 
			
		||||
        CardService.copyProductServices({
 | 
			
		||||
            requestBody: {
 | 
			
		||||
                cardId: cardState.card.id,
 | 
			
		||||
                destinationProductIds: destinationProducts.map(
 | 
			
		||||
                    product => product.product.id
 | 
			
		||||
                    product => product.product.id,
 | 
			
		||||
                ),
 | 
			
		||||
                sourceProductId: sourceProduct.product.id,
 | 
			
		||||
            },
 | 
			
		||||
@@ -147,7 +141,7 @@ const ProductAndServiceTab: FC = () => {
 | 
			
		||||
                notifications.guess(ok, { message });
 | 
			
		||||
                if (!ok) return;
 | 
			
		||||
                await cardState.refetch();
 | 
			
		||||
            }
 | 
			
		||||
            },
 | 
			
		||||
        );
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@@ -212,11 +206,12 @@ const ProductAndServiceTab: FC = () => {
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <div
 | 
			
		||||
            className={classNames(
 | 
			
		||||
                styles["container"],
 | 
			
		||||
                cardState.card?.billRequest && styles["container-disabled"]
 | 
			
		||||
                cardState.card?.billRequest && styles["container-disabled"],
 | 
			
		||||
            )}>
 | 
			
		||||
            <div className={styles["products-list"]}>
 | 
			
		||||
                <ScrollArea offsetScrollbars>
 | 
			
		||||
@@ -235,6 +230,27 @@ const ProductAndServiceTab: FC = () => {
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div className={styles["card-container"]}>
 | 
			
		||||
                {cardState.card && (
 | 
			
		||||
                    <Group
 | 
			
		||||
                        className={styles["card-container-wrapper"]}
 | 
			
		||||
                        justify={"space-between"}
 | 
			
		||||
                        wrap={"nowrap"}
 | 
			
		||||
                        mr={"xs"}
 | 
			
		||||
                    >
 | 
			
		||||
                        <Group wrap={"nowrap"}>
 | 
			
		||||
                            <PrintDealBarcodesButton card={cardState.card} />
 | 
			
		||||
                            <Checkbox
 | 
			
		||||
                                label={"Оплачен"}
 | 
			
		||||
                                checked={cardState.card.billRequest?.paid || cardState.card.group?.billRequest?.paid || false}
 | 
			
		||||
                                disabled
 | 
			
		||||
                            />
 | 
			
		||||
                        </Group>
 | 
			
		||||
                        <PaymentLinkButton card={cardState.card} />
 | 
			
		||||
                    </Group>
 | 
			
		||||
                )}
 | 
			
		||||
                <Stack className={styles["card-container-wrapper"]} mr={"xs"}>
 | 
			
		||||
                    <GeneralDataForm />
 | 
			
		||||
                </Stack>
 | 
			
		||||
                <ScrollArea offsetScrollbars>
 | 
			
		||||
                    <Flex
 | 
			
		||||
                        direction={"column"}
 | 
			
		||||
@@ -5,7 +5,7 @@ import { ActionIcon, Button, Flex, Modal, NumberInput, rem, Text, Title, Tooltip
 | 
			
		||||
import { IconTrash, IconUsersGroup } from "@tabler/icons-react";
 | 
			
		||||
import { modals } from "@mantine/modals";
 | 
			
		||||
import { isNumber } from "lodash";
 | 
			
		||||
import SimpleUsersTable from "../../../../components/SimpleUsersTable/SimpleUsersTable.tsx";
 | 
			
		||||
import SimpleUsersTable from "../../../../../../pages/CardsPage/components/SimpleUsersTable/SimpleUsersTable.tsx";
 | 
			
		||||
import { ServiceType } from "../../../../../../shared/enums/ServiceType.ts";
 | 
			
		||||
import { useSelector } from "react-redux";
 | 
			
		||||
import { RootState } from "../../../../../../redux/store.ts";
 | 
			
		||||
@@ -0,0 +1,107 @@
 | 
			
		||||
import ShippingWarehouseAutocomplete
 | 
			
		||||
    from "../../../../../../components/Selects/ShippingWarehouseAutocomplete/ShippingWarehouseAutocomplete.tsx";
 | 
			
		||||
import { CardService, ShippingWarehouseSchema } from "../../../../../../client";
 | 
			
		||||
import { useForm } from "@mantine/form";
 | 
			
		||||
import { useCardPageContext } from "../../../../../../pages/CardsPage/contexts/CardPageContext.tsx";
 | 
			
		||||
import { Button, Checkbox, Stack } from "@mantine/core";
 | 
			
		||||
import { notifications } from "../../../../../../shared/lib/notifications.ts";
 | 
			
		||||
import { useEffect, useState } from "react";
 | 
			
		||||
import { isEqual } from "lodash";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type GeneralDataFormType = {
 | 
			
		||||
    shippingWarehouse?: ShippingWarehouseSchema | null | string;
 | 
			
		||||
    isServicesProfitAccounted: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const GeneralDataForm = () => {
 | 
			
		||||
    const { selectedCard: card, refetchCard } = useCardPageContext();
 | 
			
		||||
    if (!card) return;
 | 
			
		||||
 | 
			
		||||
    const [initialValues, setInitialValues] = useState<GeneralDataFormType>(card);
 | 
			
		||||
 | 
			
		||||
    const form = useForm<GeneralDataFormType>({
 | 
			
		||||
        initialValues,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        const data = card ?? {};
 | 
			
		||||
        setInitialValues(data);
 | 
			
		||||
        form.setValues(data);
 | 
			
		||||
    }, [card]);
 | 
			
		||||
 | 
			
		||||
    const isShippingWarehouse = (
 | 
			
		||||
        value: ShippingWarehouseSchema | string | null | undefined,
 | 
			
		||||
    ): value is ShippingWarehouseSchema => {
 | 
			
		||||
        return !!value && !["string"].includes(typeof value);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const onSubmit = (values: GeneralDataFormType) => {
 | 
			
		||||
        if (!card) return;
 | 
			
		||||
 | 
			
		||||
        const shippingWarehouse = isShippingWarehouse(values.shippingWarehouse)
 | 
			
		||||
            ? values.shippingWarehouse.name
 | 
			
		||||
            : values.shippingWarehouse;
 | 
			
		||||
 | 
			
		||||
        CardService.updateProductsAndServicesGeneralInfo({
 | 
			
		||||
            requestBody: {
 | 
			
		||||
                cardId: card.id,
 | 
			
		||||
                data: {
 | 
			
		||||
                    ...values,
 | 
			
		||||
                    shippingWarehouse,
 | 
			
		||||
                },
 | 
			
		||||
            },
 | 
			
		||||
        })
 | 
			
		||||
            .then(({ ok, message }) => {
 | 
			
		||||
                if (!ok) {
 | 
			
		||||
                    notifications.error({ message });
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                refetchCard();
 | 
			
		||||
            })
 | 
			
		||||
            .catch(err => console.log(err));
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <form onSubmit={form.onSubmit(values => onSubmit(values))}>
 | 
			
		||||
            <Stack>
 | 
			
		||||
                <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);
 | 
			
		||||
                    }}
 | 
			
		||||
                />
 | 
			
		||||
                <Checkbox
 | 
			
		||||
                    label={"Учет выручки в статистике"}
 | 
			
		||||
                    {...form.getInputProps("isServicesProfitAccounted", { type: "checkbox" })}
 | 
			
		||||
                />
 | 
			
		||||
                <Button
 | 
			
		||||
                    type={"submit"}
 | 
			
		||||
                    variant={"default"}
 | 
			
		||||
                    disabled={isEqual(initialValues, form.values)}
 | 
			
		||||
                >
 | 
			
		||||
                    Сохранить
 | 
			
		||||
                </Button>
 | 
			
		||||
            </Stack>
 | 
			
		||||
        </form>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default GeneralDataForm;
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
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 { 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 = {
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
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 { ActionIcon, Group, Tooltip } from "@mantine/core";
 | 
			
		||||
import styles from "../../../../../../pages/CardsPage/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";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -12,7 +12,7 @@ type Props = {
 | 
			
		||||
 | 
			
		||||
const PrintDealBarcodesButton = ({ card }: Props) => {
 | 
			
		||||
    return (
 | 
			
		||||
        <>
 | 
			
		||||
        <Group wrap={"nowrap"}>
 | 
			
		||||
            <Tooltip
 | 
			
		||||
                className={styles["print-deals-button"]}
 | 
			
		||||
                label={"Распечатать штрихкоды сделки"}
 | 
			
		||||
@@ -56,7 +56,7 @@ const PrintDealBarcodesButton = ({ card }: Props) => {
 | 
			
		||||
                    <IconPrinter />
 | 
			
		||||
                </ActionIcon>
 | 
			
		||||
            </Tooltip>
 | 
			
		||||
        </>
 | 
			
		||||
        </Group>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -7,7 +7,7 @@ import { MRT_TableOptions } from "mantine-react-table";
 | 
			
		||||
import { ActionIcon, Button, Flex, Modal, rem, Tooltip } from "@mantine/core";
 | 
			
		||||
import { IconEdit, IconTrash, IconUsersGroup } from "@tabler/icons-react";
 | 
			
		||||
import { modals } from "@mantine/modals";
 | 
			
		||||
import SimpleUsersTable from "../../../../components/SimpleUsersTable/SimpleUsersTable.tsx";
 | 
			
		||||
import SimpleUsersTable from "../../../../../../pages/CardsPage/components/SimpleUsersTable/SimpleUsersTable.tsx";
 | 
			
		||||
import { useSelector } from "react-redux";
 | 
			
		||||
import { RootState } from "../../../../../../redux/store.ts";
 | 
			
		||||
import useCardProductAndServiceTabState from "../../hooks/useProductAndServiceTabState.tsx";
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import { CRUDTableProps } from "../../../../../types/CRUDTable.tsx";
 | 
			
		||||
import { CardService, CardServiceSchema, CardProductSchema } from "../../../../../client";
 | 
			
		||||
import { useCardPageContext } from "../../../contexts/CardPageContext.tsx";
 | 
			
		||||
import { useCardPageContext } from "../../../../../pages/CardsPage/contexts/CardPageContext.tsx";
 | 
			
		||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
 | 
			
		||||
 | 
			
		||||
const useCardState = () => {
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
import { ContextModalProps } from "@mantine/modals";
 | 
			
		||||
import BaseFormModal, {
 | 
			
		||||
    CreateEditFormProps,
 | 
			
		||||
} from "../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
 | 
			
		||||
import { CardProductSchema, CardProductServiceSchema } from "../../../client";
 | 
			
		||||
} from "../../../../../pages/ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
 | 
			
		||||
import { CardProductSchema, CardProductServiceSchema } from "../../../../../client";
 | 
			
		||||
import { useForm } from "@mantine/form";
 | 
			
		||||
import { NumberInput } from "@mantine/core";
 | 
			
		||||
import ProductSelect from "../../../components/ProductSelect/ProductSelect.tsx";
 | 
			
		||||
import ProductSelect from "../../../../../components/ProductSelect/ProductSelect.tsx";
 | 
			
		||||
import { omit } from "lodash";
 | 
			
		||||
 | 
			
		||||
type RestProps = {
 | 
			
		||||
@@ -1,12 +1,12 @@
 | 
			
		||||
import { ContextModalProps } from "@mantine/modals";
 | 
			
		||||
import BaseFormModal, { CreateEditFormProps } from "../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
 | 
			
		||||
import { CardServiceSchema } from "../../../client";
 | 
			
		||||
import BaseFormModal, { CreateEditFormProps } from "../../../../../pages/ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
 | 
			
		||||
import { CardServiceSchema } from "../../../../../client";
 | 
			
		||||
import { useForm } from "@mantine/form";
 | 
			
		||||
import { ComboboxItem, ComboboxItemGroup, NumberInput, OptionsFilter } from "@mantine/core";
 | 
			
		||||
import ServiceWithPriceInput from "../../../components/ServiceWithPriceInput/ServiceWithPriceInput.tsx";
 | 
			
		||||
import { ServiceType } from "../../../shared/enums/ServiceType.ts";
 | 
			
		||||
import ServiceWithPriceInput from "../../../../../components/ServiceWithPriceInput/ServiceWithPriceInput.tsx";
 | 
			
		||||
import { ServiceType } from "../../../../../shared/enums/ServiceType.ts";
 | 
			
		||||
import { useSelector } from "react-redux";
 | 
			
		||||
import { RootState } from "../../../redux/store.ts";
 | 
			
		||||
import { RootState } from "../../../../../redux/store.ts";
 | 
			
		||||
 | 
			
		||||
type RestProps = {
 | 
			
		||||
    serviceIds?: number[];
 | 
			
		||||
@@ -1,18 +1,18 @@
 | 
			
		||||
import BaseFormModal, {
 | 
			
		||||
    CreateEditFormProps,
 | 
			
		||||
} from "../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
 | 
			
		||||
} from "../../../../../pages/ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
 | 
			
		||||
import {
 | 
			
		||||
    CardProductServiceSchema,
 | 
			
		||||
    ServiceSchema,
 | 
			
		||||
} from "../../../client";
 | 
			
		||||
} from "../../../../../client";
 | 
			
		||||
import { ContextModalProps } from "@mantine/modals";
 | 
			
		||||
import { useForm, UseFormReturnType } from "@mantine/form";
 | 
			
		||||
import { isNil, isNumber } from "lodash";
 | 
			
		||||
import ServiceWithPriceInput from "../../../components/ServiceWithPriceInput/ServiceWithPriceInput.tsx";
 | 
			
		||||
import ServiceWithPriceInput from "../../../../../components/ServiceWithPriceInput/ServiceWithPriceInput.tsx";
 | 
			
		||||
import { Checkbox, Flex, rem } from "@mantine/core";
 | 
			
		||||
import { ServiceType } from "../../../shared/enums/ServiceType.ts";
 | 
			
		||||
import { ServiceType } from "../../../../../shared/enums/ServiceType.ts";
 | 
			
		||||
import { useSelector } from "react-redux";
 | 
			
		||||
import { RootState } from "../../../redux/store.ts";
 | 
			
		||||
import { RootState } from "../../../../../redux/store.ts";
 | 
			
		||||
 | 
			
		||||
type RestProps = {
 | 
			
		||||
    quantity: number;
 | 
			
		||||
@@ -8,7 +8,7 @@ import classes from "../ShippingTab.module.css";
 | 
			
		||||
import "mantine-datatable/styles.css";
 | 
			
		||||
import { useState } from "react";
 | 
			
		||||
import ShippingProductsTable from "./ShippingProductsTable.tsx";
 | 
			
		||||
import { Group, rem, Text } from "@mantine/core";
 | 
			
		||||
import { Flex, rem, Text } from "@mantine/core";
 | 
			
		||||
import { ShippingProductParentData } from "../types/ShippingProductData.tsx";
 | 
			
		||||
import { modals } from "@mantine/modals";
 | 
			
		||||
import InlineShippingButton from "./InlineShippingButton.tsx";
 | 
			
		||||
@@ -63,15 +63,15 @@ const BoxesTable = ({ items, onCreateShippingProduct }: Props) => {
 | 
			
		||||
 | 
			
		||||
    const getBoxActions = (box: BoxSchema) => {
 | 
			
		||||
        return (
 | 
			
		||||
            <Group wrap={"nowrap"} gap={rem(10)}>
 | 
			
		||||
            <Flex wrap={"nowrap"} direction={"row-reverse"} gap={rem(10)}>
 | 
			
		||||
                <InlineShippingButton onClick={() => onDeleteBoxClick(box)}>
 | 
			
		||||
                    <IconTrash />
 | 
			
		||||
                </InlineShippingButton>
 | 
			
		||||
                <InlineShippingButton onClick={() => onCreateShippingProduct({ boxId: box.id })}>
 | 
			
		||||
                    <IconPlus />
 | 
			
		||||
                    Товар
 | 
			
		||||
                </InlineShippingButton>
 | 
			
		||||
                <InlineShippingButton onClick={() => onDeleteBoxClick(box)}>
 | 
			
		||||
                    <IconTrash />
 | 
			
		||||
                </InlineShippingButton>
 | 
			
		||||
            </Group>
 | 
			
		||||
            </Flex>
 | 
			
		||||
        );
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { useCardPageContext } from "../../../contexts/CardPageContext.tsx";
 | 
			
		||||
import { useCardPageContext } from "../../../../../pages/CardsPage/contexts/CardPageContext.tsx";
 | 
			
		||||
import { Flex, rem, Stack } from "@mantine/core";
 | 
			
		||||
import { BoxSchema, PalletSchema, ShippingProductSchema } from "../../../../../client";
 | 
			
		||||
import ShippingProductsTable from "./ShippingProductsTable.tsx";
 | 
			
		||||
@@ -5,7 +5,7 @@ import { IconEdit, IconTrash } from "@tabler/icons-react";
 | 
			
		||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
 | 
			
		||||
import { modals } from "@mantine/modals";
 | 
			
		||||
import useUpdateCard from "./useUpdateCard.tsx";
 | 
			
		||||
import { useCardPageContext } from "../../../contexts/CardPageContext.tsx";
 | 
			
		||||
import { useCardPageContext } from "../../../../../pages/CardsPage/contexts/CardPageContext.tsx";
 | 
			
		||||
 | 
			
		||||
const useShippingTableColumns = () => {
 | 
			
		||||
    const { update } = useUpdateCard();
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { useCardPageContext } from "../../../contexts/CardPageContext.tsx";
 | 
			
		||||
import { useCardPageContext } from "../../../../../pages/CardsPage/contexts/CardPageContext.tsx";
 | 
			
		||||
import {
 | 
			
		||||
    CreateBoxInCardSchema,
 | 
			
		||||
    CreateBoxInPalletSchema, PalletSchema,
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { useCardPageContext } from "../../../contexts/CardPageContext.tsx";
 | 
			
		||||
import { useCardPageContext } from "../../../../../pages/CardsPage/contexts/CardPageContext.tsx";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const useShippingQrs = () => {
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { useCardPageContext } from "../../../contexts/CardPageContext.tsx";
 | 
			
		||||
import { useCardPageContext } from "../../../../../pages/CardsPage/contexts/CardPageContext.tsx";
 | 
			
		||||
import { CardService } from "../../../../../client";
 | 
			
		||||
 | 
			
		||||
const useUpdateCard = () => {
 | 
			
		||||
							
								
								
									
										20
									
								
								src/modules/connectModules.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/modules/connectModules.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
import { ModuleNames } from "./modules.tsx";
 | 
			
		||||
import ClientTab from "./cardModules/cardEditorTabs/ClientTab/ClientTab.tsx";
 | 
			
		||||
import ModulesType from "./types.tsx";
 | 
			
		||||
import ProductAndServiceTab from "./cardModules/cardEditorTabs/ProductAndServiceTab/ProductAndServiceTab.tsx";
 | 
			
		||||
import EmployeesTab from "./cardModules/cardEditorTabs/EmployeesTab/EmployeesTab.tsx";
 | 
			
		||||
import ShippingTab from "./cardModules/cardEditorTabs/ShippingTab/ShippingTab.tsx";
 | 
			
		||||
import ManagerTab from "./cardModules/cardEditorTabs/ManagersTab/ManagersTab.tsx";
 | 
			
		||||
 | 
			
		||||
const connectModules = (modules: ModulesType) => {
 | 
			
		||||
 | 
			
		||||
    modules[ModuleNames.CLIENTS].tab = <ClientTab />;
 | 
			
		||||
    modules[ModuleNames.SERVICES_AND_PRODUCTS].tab = <ProductAndServiceTab />;
 | 
			
		||||
    modules[ModuleNames.EMPLOYEES].tab = <EmployeesTab />;
 | 
			
		||||
    modules[ModuleNames.SHIPMENT].tab = <ShippingTab />;
 | 
			
		||||
    modules[ModuleNames.MANAGERS].tab = <ManagerTab />;
 | 
			
		||||
 | 
			
		||||
    return modules;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default connectModules;
 | 
			
		||||
							
								
								
									
										54
									
								
								src/modules/context/ModulesContext.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/modules/context/ModulesContext.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
import React, { createContext, FC, useContext, useEffect, useState } from "react";
 | 
			
		||||
import { useProjectsContext } from "../../contexts/ProjectsContext.tsx";
 | 
			
		||||
import { MODULES } from "../modules.tsx";
 | 
			
		||||
import { Module } from "../types.tsx";
 | 
			
		||||
 | 
			
		||||
type ModulesContextState = {
 | 
			
		||||
    modules: Module[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ModulesContext = createContext<ModulesContextState | undefined>(
 | 
			
		||||
    undefined,
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const useModulesContextState = () => {
 | 
			
		||||
    const { selectedProject } = useProjectsContext();
 | 
			
		||||
 | 
			
		||||
    const [modules, setModules] = useState<Module[]>([]);
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        const modules = selectedProject?.modules ?? [];
 | 
			
		||||
        const projectModules = modules.map(module => {
 | 
			
		||||
            return MODULES[module.key];
 | 
			
		||||
        }) ?? [];
 | 
			
		||||
        setModules(projectModules);
 | 
			
		||||
    }, [selectedProject?.id]);
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        modules,
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
type CardPageContextProviderProps = {
 | 
			
		||||
    children: React.ReactNode;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const ModulesContextProvider: FC<CardPageContextProviderProps> = ({ children }) => {
 | 
			
		||||
    const state = useModulesContextState();
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <ModulesContext.Provider value={state}>
 | 
			
		||||
            {children}
 | 
			
		||||
        </ModulesContext.Provider>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const useModulesContext = () => {
 | 
			
		||||
    const context = useContext(ModulesContext);
 | 
			
		||||
    if (!context) {
 | 
			
		||||
        throw new Error(
 | 
			
		||||
            "useModulesContext must be used within a ModulesContextProvider",
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
    return context;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										21
									
								
								src/modules/modules.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/modules/modules.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
import { IconUser, IconBox, IconCubeSend, IconUsersGroup, IconUserCog } from "@tabler/icons-react"
 | 
			
		||||
import ModulesType from "./types.tsx";
 | 
			
		||||
import connectModules from "./connectModules.tsx";
 | 
			
		||||
 | 
			
		||||
export enum ModuleNames {
 | 
			
		||||
	CLIENTS = "clients",
 | 
			
		||||
	SERVICES_AND_PRODUCTS = "servicesAndProducts",
 | 
			
		||||
	SHIPMENT = "shipment",
 | 
			
		||||
	EMPLOYEES = "employees",
 | 
			
		||||
	MANAGERS = "managers"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const modules: ModulesType = {
 | 
			
		||||
	[ModuleNames.CLIENTS]: { info: { label: "Клиенты", key: "clients", icon: <IconUser /> } },
 | 
			
		||||
	[ModuleNames.SERVICES_AND_PRODUCTS]: { info: { label: "Товары и услуги", key: "servicesAndProducts", icon: <IconBox /> } },
 | 
			
		||||
	[ModuleNames.SHIPMENT]: { info: { label: "Отгрузка", key: "shipment", icon: <IconCubeSend /> } },
 | 
			
		||||
	[ModuleNames.EMPLOYEES]: { info: { label: "Сотрудники", key: "employees", icon: <IconUsersGroup /> } },
 | 
			
		||||
	[ModuleNames.MANAGERS]: { info: { label: "Менеджер", key: "managers", icon: <IconUserCog /> } }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const MODULES = connectModules(modules);
 | 
			
		||||
							
								
								
									
										61
									
								
								src/modules/modulesFileGen/modulesFileGen.cjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/modules/modulesFileGen/modulesFileGen.cjs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
"use strict";
 | 
			
		||||
Object.defineProperty(exports, "__esModule", { value: true });
 | 
			
		||||
var axios_1 = require("axios");
 | 
			
		||||
var fs = require("fs");
 | 
			
		||||
// endregion
 | 
			
		||||
var OUTPUT_FILE = "./src/modules/modules.tsx";
 | 
			
		||||
function camelToConstCase(camelStr) {
 | 
			
		||||
    return camelStr
 | 
			
		||||
        .replace(/([a-z])([A-Z])/g, "$1_$2")
 | 
			
		||||
        .toUpperCase();
 | 
			
		||||
}
 | 
			
		||||
var writeToFile = function (data) {
 | 
			
		||||
    try {
 | 
			
		||||
        fs.writeFileSync(OUTPUT_FILE, data.trim());
 | 
			
		||||
        console.log("File successfully generated.");
 | 
			
		||||
    }
 | 
			
		||||
    catch (error) {
 | 
			
		||||
        console.error(error);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
var getImports = function (modules) {
 | 
			
		||||
    var prefix = "import { ";
 | 
			
		||||
    var postfix = " } from \"@tabler/icons-react\"\n" +
 | 
			
		||||
        "import ModulesType from \"./types.tsx\";\n" +
 | 
			
		||||
        "import connectModules from \"./connectModules.tsx\";";
 | 
			
		||||
    var filteredModules = modules.filter(function (module) { return module.iconName; });
 | 
			
		||||
    var icons = filteredModules.map(function (module) { return module.iconName; }).join(", ");
 | 
			
		||||
    return prefix + icons + postfix;
 | 
			
		||||
};
 | 
			
		||||
var getModuleNames = function (modules) {
 | 
			
		||||
    return modules.map(function (module) {
 | 
			
		||||
        return "".concat(camelToConstCase(module.key), " = \"").concat(module.key, "\"");
 | 
			
		||||
    }).join(",\n\t");
 | 
			
		||||
};
 | 
			
		||||
var getModules = function (modules) {
 | 
			
		||||
    return modules.map(function (module) {
 | 
			
		||||
        var iconStr = "null";
 | 
			
		||||
        if (module.iconName) {
 | 
			
		||||
            iconStr = "<" + module.iconName + " />";
 | 
			
		||||
        }
 | 
			
		||||
        return "[ModuleNames.".concat(camelToConstCase(module.key), "]: { info: { label: \"").concat(module.label, "\", key: \"").concat(module.key, "\", icon: ").concat(iconStr, " } }");
 | 
			
		||||
    }).join(",\n\t");
 | 
			
		||||
};
 | 
			
		||||
var generateRows = function (modules) {
 | 
			
		||||
    var imports = getImports(modules);
 | 
			
		||||
    var moduleNames = "\n\nexport enum ModuleNames {\n\t".concat(getModuleNames(modules), "\n}\n");
 | 
			
		||||
    var modulesStr = "\nconst modules: ModulesType = {\n\t".concat(getModules(modules), "\n};\n");
 | 
			
		||||
    var connectModules = "\nexport const MODULES = connectModules(modules);";
 | 
			
		||||
    var result = imports + moduleNames + modulesStr + connectModules;
 | 
			
		||||
    writeToFile(result);
 | 
			
		||||
};
 | 
			
		||||
var modulesFileGen = function () {
 | 
			
		||||
    console.log("Start file generation...");
 | 
			
		||||
    axios_1.default
 | 
			
		||||
        .get("http://127.0.0.1:8000/project/modules")
 | 
			
		||||
        .then(function (response) {
 | 
			
		||||
        generateRows(response.data.modules);
 | 
			
		||||
    })
 | 
			
		||||
        .catch(function (err) { return console.log(err); });
 | 
			
		||||
};
 | 
			
		||||
modulesFileGen();
 | 
			
		||||
							
								
								
									
										86
									
								
								src/modules/modulesFileGen/modulesFileGen.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								src/modules/modulesFileGen/modulesFileGen.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
			
		||||
import axios, { AxiosResponse } from "axios";
 | 
			
		||||
 | 
			
		||||
import * as fs from 'fs';
 | 
			
		||||
 | 
			
		||||
// region Types
 | 
			
		||||
type Module = {
 | 
			
		||||
    id: number;
 | 
			
		||||
    key: string;
 | 
			
		||||
    label: string;
 | 
			
		||||
    iconName?: string;
 | 
			
		||||
    isDeleted: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ModulesResponse = {
 | 
			
		||||
    modules: Module[];
 | 
			
		||||
}
 | 
			
		||||
// endregion
 | 
			
		||||
 | 
			
		||||
const OUTPUT_FILE = "./src/modules/modules.tsx";
 | 
			
		||||
 | 
			
		||||
function camelToConstCase(camelStr: string): string {
 | 
			
		||||
    return camelStr
 | 
			
		||||
        .replace(/([a-z])([A-Z])/g, "$1_$2")
 | 
			
		||||
        .toUpperCase();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const writeToFile = (data: string) => {
 | 
			
		||||
    try {
 | 
			
		||||
        fs.writeFileSync(OUTPUT_FILE, data.trim());
 | 
			
		||||
        console.log("File successfully generated.");
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
        console.error(error);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const getImports = (modules: Module[]): string => {
 | 
			
		||||
    const prefix = "import { ";
 | 
			
		||||
    const postfix = " } from \"@tabler/icons-react\"\n" +
 | 
			
		||||
        "import ModulesType from \"./types.tsx\";\n" +
 | 
			
		||||
        "import connectModules from \"./connectModules.tsx\";";
 | 
			
		||||
 | 
			
		||||
    const filteredModules = modules.filter(module => module.iconName);
 | 
			
		||||
    const icons = filteredModules.map((module: Module) => module.iconName).join(", ");
 | 
			
		||||
 | 
			
		||||
    return prefix + icons + postfix;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const getModuleNames = (modules: Module[]) => {
 | 
			
		||||
    return modules.map(module => {
 | 
			
		||||
        return `${camelToConstCase(module.key)} = "${module.key}"`;
 | 
			
		||||
    }).join(",\n\t");
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const getModules = (modules: Module[]) => {
 | 
			
		||||
    return modules.map(module => {
 | 
			
		||||
        let iconStr = "null";
 | 
			
		||||
        if (module.iconName) {
 | 
			
		||||
            iconStr = "<" + module.iconName + " />";
 | 
			
		||||
        }
 | 
			
		||||
        return `[ModuleNames.${camelToConstCase(module.key)}]: { info: { label: "${module.label}", key: "${module.key}", icon: ${iconStr} } }`;
 | 
			
		||||
    }).join(",\n\t");
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const generateRows = (modules: Module[]) => {
 | 
			
		||||
    const imports = getImports(modules);
 | 
			
		||||
 | 
			
		||||
    const moduleNames = `\n\nexport enum ModuleNames {\n\t${getModuleNames(modules)}\n}\n`;
 | 
			
		||||
    const modulesStr = `\nconst modules: ModulesType = {\n\t${getModules(modules)}\n};\n`;
 | 
			
		||||
    const connectModules = "\nexport const MODULES = connectModules(modules);";
 | 
			
		||||
    const result: string = imports + moduleNames + modulesStr + connectModules;
 | 
			
		||||
 | 
			
		||||
    writeToFile(result);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const modulesFileGen = () => {
 | 
			
		||||
    console.log("Start file generation...");
 | 
			
		||||
 | 
			
		||||
    axios
 | 
			
		||||
        .get("http://127.0.0.1:8000/project/modules")
 | 
			
		||||
        .then((response: AxiosResponse<ModulesResponse>) => {
 | 
			
		||||
            generateRows(response.data.modules);
 | 
			
		||||
        })
 | 
			
		||||
        .catch(err => console.log(err));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
modulesFileGen();
 | 
			
		||||
							
								
								
									
										16
									
								
								src/modules/types.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/modules/types.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
import { ReactNode } from "react";
 | 
			
		||||
 | 
			
		||||
export type Module = {
 | 
			
		||||
    info: {
 | 
			
		||||
        label: string;
 | 
			
		||||
        key: string;
 | 
			
		||||
        icon: ReactNode;
 | 
			
		||||
    },
 | 
			
		||||
    tab?: ReactNode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Modules = {
 | 
			
		||||
    [key: string]: Module;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default Modules;
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { ProjectSchema } from "../../../client";
 | 
			
		||||
import { ProjectSchema } from "../../client";
 | 
			
		||||
 | 
			
		||||
export enum Modules {
 | 
			
		||||
    SERVICES_AND_PRODUCTS = "servicesAndProducts",
 | 
			
		||||
@@ -6,7 +6,6 @@ export enum Modules {
 | 
			
		||||
    EMPLOYEES = "employees",
 | 
			
		||||
    CLIENTS = "clients",
 | 
			
		||||
    MANAGERS = "managers",
 | 
			
		||||
    MEGA_MODULE = "hui",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const isModuleInProject = (module: Modules, project?: ProjectSchema | null) => {
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import { useParams } from "@tanstack/react-router";
 | 
			
		||||
import { CardPageContextProvider, useCardPageContext } from "../../CardsPage/contexts/CardPageContext.tsx";
 | 
			
		||||
import ProductAndServiceTab from "../../CardsPage/tabs/ProductAndServiceTab/ProductAndServiceTab.tsx";
 | 
			
		||||
import ProductAndServiceTab from "../../../modules/cardModules/cardEditorTabs/ProductAndServiceTab/ProductAndServiceTab.tsx";
 | 
			
		||||
import React, { FC, useEffect } from "react";
 | 
			
		||||
import { CardService } from "../../../client";
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,12 @@
 | 
			
		||||
import { Box, Drawer, rem, Tabs } from "@mantine/core";
 | 
			
		||||
import { FC, ReactNode, useEffect } from "react";
 | 
			
		||||
import { useCardPageContext } from "../../contexts/CardPageContext.tsx";
 | 
			
		||||
import { IconBox, IconCalendarUser, IconCubeSend, IconSettings, IconUser, IconUsersGroup } from "@tabler/icons-react";
 | 
			
		||||
import CardStatusChangeTable from "../../components/CardStatusChangeTable/CardStatusChangeTable.tsx";
 | 
			
		||||
import GeneralTab from "../../tabs/GeneralTab/GeneralTab.tsx";
 | 
			
		||||
import { IconCalendarUser, IconSettings } from "@tabler/icons-react";
 | 
			
		||||
import CardStatusChangeTable from "./tabs/CardStatusChangeTable/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/ClientTab.tsx";
 | 
			
		||||
import { useModulesContext } from "../../../../modules/context/ModulesContext.tsx";
 | 
			
		||||
 | 
			
		||||
const useCardStatusChangeState = () => {
 | 
			
		||||
    const { selectedCard } = useCardPageContext();
 | 
			
		||||
@@ -36,8 +33,7 @@ const CardEditDrawer: FC = () => {
 | 
			
		||||
    const { isVisible, onClose } = useCardEditDrawerState();
 | 
			
		||||
    const queryClient = useQueryClient();
 | 
			
		||||
 | 
			
		||||
    const { selectedCard } = useCardPageContext();
 | 
			
		||||
    const modules = new Set<string>(selectedCard?.board.project.modules.map(module => module.key));
 | 
			
		||||
    const { modules } = useModulesContext();
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        if (isVisible) return;
 | 
			
		||||
@@ -62,23 +58,27 @@ const CardEditDrawer: FC = () => {
 | 
			
		||||
        );
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const getTab = (
 | 
			
		||||
        key: string,
 | 
			
		||||
        icon: ReactNode,
 | 
			
		||||
        label: string,
 | 
			
		||||
        enablingModules: string[] | null = null, // Show if at least one of modules is in project
 | 
			
		||||
    ) => {
 | 
			
		||||
        if (!enablingModules) {
 | 
			
		||||
            enablingModules = [key];
 | 
			
		||||
        }
 | 
			
		||||
        if (!enablingModules.some(key => modules.has(key))) return;
 | 
			
		||||
    const getTabs = () => {
 | 
			
		||||
        const moduleTabs = modules.map(module => (
 | 
			
		||||
            <Tabs.Tab
 | 
			
		||||
                value={module.info.key}
 | 
			
		||||
                leftSection={module.info.icon}
 | 
			
		||||
            >
 | 
			
		||||
                {module.info.label}
 | 
			
		||||
            </Tabs.Tab>
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        return (
 | 
			
		||||
            <Tabs.Tab
 | 
			
		||||
                value={key}
 | 
			
		||||
                leftSection={icon}>
 | 
			
		||||
                {label}
 | 
			
		||||
            </Tabs.Tab>
 | 
			
		||||
            <>{moduleTabs}</>
 | 
			
		||||
        );
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const getTabPanels = () => {
 | 
			
		||||
        const moduleTabPanels = modules.map(
 | 
			
		||||
            module => getTabPanel(module.info.key, module.tab),
 | 
			
		||||
        );
 | 
			
		||||
        return (
 | 
			
		||||
            <>{moduleTabPanels}</>
 | 
			
		||||
        );
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@@ -115,18 +115,12 @@ const CardEditDrawer: FC = () => {
 | 
			
		||||
                        leftSection={<IconCalendarUser />}>
 | 
			
		||||
                        История
 | 
			
		||||
                    </Tabs.Tab>
 | 
			
		||||
                    {getTab("clients", <IconUser />, "Клиент", ["servicesAndProducts", "clients"])}
 | 
			
		||||
                    {getTab("servicesAndProducts", <IconBox />, "Товары и услуги")}
 | 
			
		||||
                    {getTab("shipment", <IconCubeSend />, "Отгрузка")}
 | 
			
		||||
                    {getTab("employees", <IconUsersGroup />, "Исполнители")}
 | 
			
		||||
                    {getTabs()}
 | 
			
		||||
                </Tabs.List>
 | 
			
		||||
 | 
			
		||||
                {getTabPanel("general", <GeneralTab />)}
 | 
			
		||||
                {getTabPanel("clients", <ClientTab />)}
 | 
			
		||||
                {getTabPanel("history", <CardEditDrawerStatusChangeTable />)}
 | 
			
		||||
                {getTabPanel("servicesAndProducts", <ProductAndServiceTab />)}
 | 
			
		||||
                {getTabPanel("shipment", <ShippingTab />)}
 | 
			
		||||
                {getTabPanel("employees", <EmployeesTab />)}
 | 
			
		||||
                {getTabPanels()}
 | 
			
		||||
            </Tabs>
 | 
			
		||||
        </Drawer>
 | 
			
		||||
    );
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import { CardStatusHistorySchema } from "../../../../client";
 | 
			
		||||
import { CardStatusHistorySchema } from "../../../../../../client";
 | 
			
		||||
import { useDealStatusChangeTableColumns } from "./columns.tsx";
 | 
			
		||||
import { BaseTable } from "../../../../components/BaseTable/BaseTable.tsx";
 | 
			
		||||
import { BaseTable } from "../../../../../../components/BaseTable/BaseTable.tsx";
 | 
			
		||||
import { FC } from "react";
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import { useMemo } from "react";
 | 
			
		||||
import { MRT_ColumnDef } from "mantine-react-table";
 | 
			
		||||
import { CardStatusHistorySchema } from "../../../../client";
 | 
			
		||||
import { CardStatusHistorySchema } from "../../../../../../client";
 | 
			
		||||
import { Spoiler, Text } from "@mantine/core";
 | 
			
		||||
 | 
			
		||||
export const useDealStatusChangeTableColumns = () => {
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import { FC, useState } from "react";
 | 
			
		||||
import { useCardPageContext } from "../../contexts/CardPageContext.tsx";
 | 
			
		||||
import { useCardPageContext } from "../../../../contexts/CardPageContext.tsx";
 | 
			
		||||
import {
 | 
			
		||||
    Button,
 | 
			
		||||
    Checkbox,
 | 
			
		||||
@@ -14,31 +14,17 @@ import {
 | 
			
		||||
    TextInput,
 | 
			
		||||
} from "@mantine/core";
 | 
			
		||||
import { useForm } from "@mantine/form";
 | 
			
		||||
import {
 | 
			
		||||
    CardSchema,
 | 
			
		||||
    CardService,
 | 
			
		||||
    ClientService,
 | 
			
		||||
    ProjectSchema,
 | 
			
		||||
    ShippingWarehouseSchema,
 | 
			
		||||
    StatusSchema,
 | 
			
		||||
} from "../../../../client";
 | 
			
		||||
import { CardSchema, CardService, ClientService, ProjectSchema, StatusSchema } 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";
 | 
			
		||||
import ShippingWarehouseAutocomplete
 | 
			
		||||
    from "../../../../components/Selects/ShippingWarehouseAutocomplete/ShippingWarehouseAutocomplete.tsx";
 | 
			
		||||
import { ButtonCopyControlled } from "../../../../components/ButtonCopyControlled/ButtonCopyControlled.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";
 | 
			
		||||
import ClientSelect from "../../../../components/Selects/ClientSelect/ClientSelect.tsx";
 | 
			
		||||
import ProjectSelect from "../../../../../../components/ProjectSelect/ProjectSelect.tsx";
 | 
			
		||||
import BoardSelect from "../../../../../../components/BoardSelect/BoardSelect.tsx";
 | 
			
		||||
import CardStatusSelect from "../../../../../../components/DealStatusSelect/CardStatusSelect.tsx";
 | 
			
		||||
import CardAttributeFields from "../../../../../../components/CardAttributeFields/CardAttributeFields.tsx";
 | 
			
		||||
import getAttributesFromCard from "../../../../../../components/CardAttributeFields/utils/getAttributesFromCard.ts";
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
    card: CardSchema;
 | 
			
		||||
@@ -56,10 +42,6 @@ const Content: FC<Props> = ({ card }) => {
 | 
			
		||||
    const queryClient = useQueryClient();
 | 
			
		||||
    const [project, setProject] = useState<ProjectSchema | null>(card.board.project);
 | 
			
		||||
 | 
			
		||||
    const isServicesAndProductsIncluded = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, card.board.project);
 | 
			
		||||
    const isManagerIncluded = isModuleInProject(Modules.MANAGERS, card.board.project);
 | 
			
		||||
    const isClientIncluded = isModuleInProject(Modules.CLIENTS, card.board.project);
 | 
			
		||||
 | 
			
		||||
    const getInitialValues = (card: CardSchema): CardGeneralFormType => {
 | 
			
		||||
        return {
 | 
			
		||||
            ...card,
 | 
			
		||||
@@ -99,7 +81,6 @@ const Content: FC<Props> = ({ card }) => {
 | 
			
		||||
                    statusId: values.status.id,
 | 
			
		||||
                    boardId: values.board.id,
 | 
			
		||||
                    clientId: values.client?.id ?? null,
 | 
			
		||||
                    shippingWarehouse: values.shippingWarehouse?.toString(),
 | 
			
		||||
                    attributes,
 | 
			
		||||
                },
 | 
			
		||||
            },
 | 
			
		||||
@@ -107,7 +88,6 @@ const Content: FC<Props> = ({ card }) => {
 | 
			
		||||
            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);
 | 
			
		||||
@@ -131,21 +111,7 @@ const Content: FC<Props> = ({ card }) => {
 | 
			
		||||
            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 !!value && !["string"].includes(typeof value);
 | 
			
		||||
        await updateCardInfo(values);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const onCopyGuestUrlClick = () => {
 | 
			
		||||
@@ -196,7 +162,7 @@ const Content: FC<Props> = ({ card }) => {
 | 
			
		||||
                                    {...form.getInputProps("board")}
 | 
			
		||||
                                    label={"Доска"}
 | 
			
		||||
                                />
 | 
			
		||||
                                <DealStatusSelect
 | 
			
		||||
                                <CardStatusSelect
 | 
			
		||||
                                    board={form.values.board}
 | 
			
		||||
                                    {...form.getInputProps("status")}
 | 
			
		||||
                                    label={"Статус"}
 | 
			
		||||
@@ -211,49 +177,6 @@ const Content: FC<Props> = ({ card }) => {
 | 
			
		||||
                                    placeholder={"Введите коментарий"}
 | 
			
		||||
                                    {...form.getInputProps("comment")}
 | 
			
		||||
                                />
 | 
			
		||||
                                {isClientIncluded && (
 | 
			
		||||
                                    <ClientSelect
 | 
			
		||||
                                        {...form.getInputProps("client")}
 | 
			
		||||
                                        withLabel
 | 
			
		||||
                                    />
 | 
			
		||||
                                )}
 | 
			
		||||
                                {isServicesAndProductsIncluded && (
 | 
			
		||||
                                    <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);
 | 
			
		||||
                                        }}
 | 
			
		||||
                                    />
 | 
			
		||||
                                )}
 | 
			
		||||
                                {isManagerIncluded && (
 | 
			
		||||
                                    <ManagerSelect
 | 
			
		||||
                                        placeholder={"Укажите менеджера"}
 | 
			
		||||
                                        label={"Менеджер"}
 | 
			
		||||
                                        {...form.getInputProps("manager")}
 | 
			
		||||
                                    />
 | 
			
		||||
                                )}
 | 
			
		||||
                                {isServicesAndProductsIncluded && (
 | 
			
		||||
                                    <Checkbox
 | 
			
		||||
                                        label={"Учет выручки с услуг в статистике"}
 | 
			
		||||
                                        {...form.getInputProps("isServicesProfitAccounted", { type: "checkbox" })}
 | 
			
		||||
                                    />
 | 
			
		||||
                                )}
 | 
			
		||||
                                {project && (
 | 
			
		||||
                                    <CardAttributeFields
 | 
			
		||||
                                        project={project}
 | 
			
		||||
@@ -273,36 +196,16 @@ const Content: FC<Props> = ({ card }) => {
 | 
			
		||||
                        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>
 | 
			
		||||
                        <ButtonCopyControlled
 | 
			
		||||
                            onCopyClick={onCopyGuestUrlClick}
 | 
			
		||||
                            onCopiedLabel={
 | 
			
		||||
                                "Ссылка скопирована в буфер обмена"
 | 
			
		||||
                            }
 | 
			
		||||
                            copied={clipboard.copied}
 | 
			
		||||
                        >
 | 
			
		||||
                            Ссылка на редактирование
 | 
			
		||||
                        </ButtonCopyControlled>
 | 
			
		||||
                        <Flex gap={rem(10)}>
 | 
			
		||||
                            {isServicesAndProductsIncluded && (
 | 
			
		||||
                                <Checkbox
 | 
			
		||||
                                    label={"Оплачен"}
 | 
			
		||||
                                    checked={card.billRequest?.paid || card.group?.billRequest?.paid || false}
 | 
			
		||||
                                    disabled
 | 
			
		||||
                                />
 | 
			
		||||
                            )}
 | 
			
		||||
                            <Checkbox
 | 
			
		||||
                                label={"Завершена"}
 | 
			
		||||
                                {...form.getInputProps("isCompleted", { type: "checkbox" })}
 | 
			
		||||
@@ -2,7 +2,7 @@ import { FC } from "react";
 | 
			
		||||
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";
 | 
			
		||||
import { ProductFieldNames } from "../../../../../../modules/cardModules/cardEditorTabs/ProductAndServiceTab/components/ProductView/ProductView.tsx";
 | 
			
		||||
import ProductServicesTable from "../tables/ProductServicesTable/ProductServicesTable.tsx";
 | 
			
		||||
import styles from "./ProductPreview.module.css";
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ import { useForm } from "@mantine/form";
 | 
			
		||||
import { useEffect, useState } from "react";
 | 
			
		||||
import { BaseMarketplaceSchema } from "../../../../../client";
 | 
			
		||||
import { useCardSummariesFull } from "../../../hooks/useCardSummaries.tsx";
 | 
			
		||||
import isModuleInProject, { Modules } from "../../../utils/isModuleInProject.ts";
 | 
			
		||||
import isModuleInProject, { Modules } from "../../../../../modules/utils/isModuleInProject.ts";
 | 
			
		||||
 | 
			
		||||
type State = {
 | 
			
		||||
    idOrName: string | null;
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ import { Flex, Modal, NumberInput, rem } from "@mantine/core";
 | 
			
		||||
import { UseFormReturnType } from "@mantine/form";
 | 
			
		||||
import { CardsPageState } from "../hooks/useCardsPageState.tsx";
 | 
			
		||||
import ObjectSelect from "../../../components/ObjectSelect/ObjectSelect.tsx";
 | 
			
		||||
import DealStatusSelect from "../../../components/DealStatusSelect/DealStatusSelect.tsx";
 | 
			
		||||
import CardStatusSelect from "../../../components/DealStatusSelect/CardStatusSelect.tsx";
 | 
			
		||||
import BaseMarketplaceSelect from "../../../components/Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
 | 
			
		||||
import ClientSelectNew from "../../../components/Selects/ClientSelectNew/ClientSelectNew.tsx";
 | 
			
		||||
import { useDisclosure } from "@mantine/hooks";
 | 
			
		||||
@@ -25,7 +25,7 @@ const CardsTableFiltersModal = ({ form, projects }: Props) => {
 | 
			
		||||
                <IconFilter />
 | 
			
		||||
                Фильтры
 | 
			
		||||
            </InlineButton>
 | 
			
		||||
            <Modal title={"Фильтры для сделок"} opened={opened} onClose={close}>
 | 
			
		||||
            <Modal title={"Фильтры"} opened={opened} onClose={close}>
 | 
			
		||||
                <Flex
 | 
			
		||||
                    direction={"column"}
 | 
			
		||||
                    gap={rem(10)}
 | 
			
		||||
@@ -49,7 +49,7 @@ const CardsTableFiltersModal = ({ form, projects }: Props) => {
 | 
			
		||||
                        {...form.getInputProps("board")}
 | 
			
		||||
                        clearable
 | 
			
		||||
                    />
 | 
			
		||||
                    <DealStatusSelect
 | 
			
		||||
                    <CardStatusSelect
 | 
			
		||||
                        board={form.values.board}
 | 
			
		||||
                        {...form.getInputProps("status")}
 | 
			
		||||
                        clearable
 | 
			
		||||
 
 | 
			
		||||
@@ -1,103 +0,0 @@
 | 
			
		||||
import { Button, Fieldset, Flex, rem, Stack, Textarea, TextInput } from "@mantine/core";
 | 
			
		||||
import { useCardPageContext } from "../../contexts/CardPageContext.tsx";
 | 
			
		||||
import { useForm } from "@mantine/form";
 | 
			
		||||
import { CardGeneralFormType } from "../GeneralTab/GeneralTab.tsx";
 | 
			
		||||
import { CardSchema, CardService, ClientService } from "../../../../client";
 | 
			
		||||
import { isEqual } from "lodash";
 | 
			
		||||
import { notifications } from "../../../../shared/lib/notifications.ts";
 | 
			
		||||
import { useQueryClient } from "@tanstack/react-query";
 | 
			
		||||
 | 
			
		||||
const ClientTab = () => {
 | 
			
		||||
    const { selectedCard: card, setSelectedCard } = useCardPageContext();
 | 
			
		||||
    const initialValues: CardGeneralFormType = card as CardSchema;
 | 
			
		||||
    const queryClient = useQueryClient();
 | 
			
		||||
 | 
			
		||||
    if (!card?.client) return;
 | 
			
		||||
 | 
			
		||||
    const form = useForm<CardGeneralFormType>(
 | 
			
		||||
        {
 | 
			
		||||
            initialValues: initialValues,
 | 
			
		||||
            validate: {
 | 
			
		||||
                name: (value: string) => value.length > 0 ? null : "Название сделки не может быть пустым",
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
    );
 | 
			
		||||
    const hasChanges = !isEqual(form.values, initialValues);
 | 
			
		||||
    const updateClientInfo = async (values: CardGeneralFormType) => {
 | 
			
		||||
        if (!values.client) return;
 | 
			
		||||
 | 
			
		||||
        return ClientService.updateClient({
 | 
			
		||||
            requestBody: {
 | 
			
		||||
                data: values.client,
 | 
			
		||||
            },
 | 
			
		||||
        }).then(({ ok, message }) => notifications.guess(ok, { message }));
 | 
			
		||||
    };
 | 
			
		||||
    const update = async () => {
 | 
			
		||||
        return CardService.getCardById({ cardId: form.values.id }).then(data => {
 | 
			
		||||
            setSelectedCard(data);
 | 
			
		||||
            form.setInitialValues(data);
 | 
			
		||||
            queryClient.invalidateQueries({
 | 
			
		||||
                queryKey: ["getCardSummaries"],
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
    const handleSave = () => {
 | 
			
		||||
        updateClientInfo(form.values).then(async () => {
 | 
			
		||||
            await update();
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
    const handleCancel = () => {
 | 
			
		||||
        form.setInitialValues(initialValues);
 | 
			
		||||
    };
 | 
			
		||||
    return (
 | 
			
		||||
        <Flex direction={"column"} flex={1} gap={rem(10)}>
 | 
			
		||||
            <Flex flex={1}>
 | 
			
		||||
                <Fieldset legend={"Клиент"} flex={1}>
 | 
			
		||||
                    <Stack gap={rem(10)}>
 | 
			
		||||
                        <TextInput
 | 
			
		||||
                            disabled
 | 
			
		||||
                            placeholder={"Название"}
 | 
			
		||||
                            label={"Название"}
 | 
			
		||||
                            value={card?.client.name}
 | 
			
		||||
                        />
 | 
			
		||||
                        <TextInput
 | 
			
		||||
                            placeholder={"Введите телефон"}
 | 
			
		||||
                            label={"Телефон клиента"}
 | 
			
		||||
                            {...form.getInputProps("client.details.phoneNumber")}
 | 
			
		||||
                        />
 | 
			
		||||
                        <TextInput
 | 
			
		||||
                            placeholder={"Введите email"}
 | 
			
		||||
                            label={"Email"}
 | 
			
		||||
                            {...form.getInputProps("client.details.email")}
 | 
			
		||||
                        />
 | 
			
		||||
                        <TextInput
 | 
			
		||||
                            placeholder={"Введите телеграм"}
 | 
			
		||||
                            label={"Телеграм"}
 | 
			
		||||
                            {...form.getInputProps("client.details.telegram")}
 | 
			
		||||
                        />
 | 
			
		||||
                        <TextInput
 | 
			
		||||
                            placeholder={"Введите ИНН"}
 | 
			
		||||
                            label={"ИНН"}
 | 
			
		||||
                            {...form.getInputProps("client.details.inn")}
 | 
			
		||||
                        />
 | 
			
		||||
                        <Textarea
 | 
			
		||||
                            placeholder={"Введите комментарий"}
 | 
			
		||||
                            label={"Комментарий"}
 | 
			
		||||
                            {...form.getInputProps("client.comment")}
 | 
			
		||||
                        />
 | 
			
		||||
                    </Stack>
 | 
			
		||||
                </Fieldset>
 | 
			
		||||
            </Flex>
 | 
			
		||||
            <Flex
 | 
			
		||||
                gap={rem(10)}
 | 
			
		||||
                justify={"flex-end"}
 | 
			
		||||
                display={!hasChanges ? "none" : "flex"}
 | 
			
		||||
            >
 | 
			
		||||
                <Button onClick={handleCancel} variant={"default"}>Отмена</Button>
 | 
			
		||||
                <Button onClick={handleSave} variant={"default"}>Сохранить</Button>
 | 
			
		||||
            </Flex>
 | 
			
		||||
        </Flex>
 | 
			
		||||
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
export default ClientTab;
 | 
			
		||||
@@ -13,7 +13,7 @@ import {
 | 
			
		||||
import { ObjectSelectProps } from "../../../../../../components/ObjectSelect/ObjectSelect.tsx";
 | 
			
		||||
import BaseMarketplaceSelect
 | 
			
		||||
    from "../../../../../../components/Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
 | 
			
		||||
import DealStatusSelect from "../../../../../../components/DealStatusSelect/DealStatusSelect.tsx";
 | 
			
		||||
import CardStatusSelect from "../../../../../../components/DealStatusSelect/CardStatusSelect.tsx";
 | 
			
		||||
import { ProfitTableSegmentedControl } from "../ProfitTableSegmentedControl/ProfitTableSegmentedControl.tsx";
 | 
			
		||||
import ManagerSelect from "../../../../../../components/ManagerSelect/ManagerSelect.tsx";
 | 
			
		||||
import TransactionTagSelect from "../../../../components/ExpenseTagSelect/TransactionTagSelect.tsx";
 | 
			
		||||
@@ -107,7 +107,7 @@ export const Filters = (props: FiltersProps) => {
 | 
			
		||||
                />
 | 
			
		||||
            }
 | 
			
		||||
            {props.statusSelectProps &&
 | 
			
		||||
                <DealStatusSelect
 | 
			
		||||
                <CardStatusSelect
 | 
			
		||||
                    board={props.boardSelectProps?.value ?? null}
 | 
			
		||||
                    {...props.statusSelectProps}
 | 
			
		||||
                    clearable
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,12 @@
 | 
			
		||||
/* prettier-ignore-start */
 | 
			
		||||
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
 | 
			
		||||
// @ts-nocheck
 | 
			
		||||
 | 
			
		||||
// noinspection JSUnusedGlobalSymbols
 | 
			
		||||
 | 
			
		||||
// This file is auto-generated by TanStack Router
 | 
			
		||||
// This file was automatically generated by TanStack Router.
 | 
			
		||||
// You should NOT make any changes in this file as it will be overwritten.
 | 
			
		||||
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
 | 
			
		||||
 | 
			
		||||
import { createFileRoute } from '@tanstack/react-router'
 | 
			
		||||
 | 
			
		||||
@@ -36,16 +36,19 @@ const IndexLazyImport = createFileRoute('/')()
 | 
			
		||||
// Create/Update Routes
 | 
			
		||||
 | 
			
		||||
const TestLazyRoute = TestLazyImport.update({
 | 
			
		||||
  id: '/test',
 | 
			
		||||
  path: '/test',
 | 
			
		||||
  getParentRoute: () => rootRoute,
 | 
			
		||||
} as any).lazy(() => import('./routes/test.lazy').then((d) => d.Route))
 | 
			
		||||
 | 
			
		||||
const StatisticsLazyRoute = StatisticsLazyImport.update({
 | 
			
		||||
  id: '/statistics',
 | 
			
		||||
  path: '/statistics',
 | 
			
		||||
  getParentRoute: () => rootRoute,
 | 
			
		||||
} as any).lazy(() => import('./routes/statistics.lazy').then((d) => d.Route))
 | 
			
		||||
 | 
			
		||||
const ShippingwarehousesLazyRoute = ShippingwarehousesLazyImport.update({
 | 
			
		||||
  id: '/shipping_warehouses',
 | 
			
		||||
  path: '/shipping_warehouses',
 | 
			
		||||
  getParentRoute: () => rootRoute,
 | 
			
		||||
} as any).lazy(() =>
 | 
			
		||||
@@ -53,66 +56,79 @@ const ShippingwarehousesLazyRoute = ShippingwarehousesLazyImport.update({
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const ServicesLazyRoute = ServicesLazyImport.update({
 | 
			
		||||
  id: '/services',
 | 
			
		||||
  path: '/services',
 | 
			
		||||
  getParentRoute: () => rootRoute,
 | 
			
		||||
} as any).lazy(() => import('./routes/services.lazy').then((d) => d.Route))
 | 
			
		||||
 | 
			
		||||
const ResiduesLazyRoute = ResiduesLazyImport.update({
 | 
			
		||||
  id: '/residues',
 | 
			
		||||
  path: '/residues',
 | 
			
		||||
  getParentRoute: () => rootRoute,
 | 
			
		||||
} as any).lazy(() => import('./routes/residues.lazy').then((d) => d.Route))
 | 
			
		||||
 | 
			
		||||
const ReceiptLazyRoute = ReceiptLazyImport.update({
 | 
			
		||||
  id: '/receipt',
 | 
			
		||||
  path: '/receipt',
 | 
			
		||||
  getParentRoute: () => rootRoute,
 | 
			
		||||
} as any).lazy(() => import('./routes/receipt.lazy').then((d) => d.Route))
 | 
			
		||||
 | 
			
		||||
const ProductsLazyRoute = ProductsLazyImport.update({
 | 
			
		||||
  id: '/products',
 | 
			
		||||
  path: '/products',
 | 
			
		||||
  getParentRoute: () => rootRoute,
 | 
			
		||||
} as any).lazy(() => import('./routes/products.lazy').then((d) => d.Route))
 | 
			
		||||
 | 
			
		||||
const MarketplacesLazyRoute = MarketplacesLazyImport.update({
 | 
			
		||||
  id: '/marketplaces',
 | 
			
		||||
  path: '/marketplaces',
 | 
			
		||||
  getParentRoute: () => rootRoute,
 | 
			
		||||
} as any).lazy(() => import('./routes/marketplaces.lazy').then((d) => d.Route))
 | 
			
		||||
 | 
			
		||||
const LoginLazyRoute = LoginLazyImport.update({
 | 
			
		||||
  id: '/login',
 | 
			
		||||
  path: '/login',
 | 
			
		||||
  getParentRoute: () => rootRoute,
 | 
			
		||||
} as any).lazy(() => import('./routes/login.lazy').then((d) => d.Route))
 | 
			
		||||
 | 
			
		||||
const LeadsLazyRoute = LeadsLazyImport.update({
 | 
			
		||||
  id: '/leads',
 | 
			
		||||
  path: '/leads',
 | 
			
		||||
  getParentRoute: () => rootRoute,
 | 
			
		||||
} as any).lazy(() => import('./routes/leads.lazy').then((d) => d.Route))
 | 
			
		||||
 | 
			
		||||
const ClientsLazyRoute = ClientsLazyImport.update({
 | 
			
		||||
  id: '/clients',
 | 
			
		||||
  path: '/clients',
 | 
			
		||||
  getParentRoute: () => rootRoute,
 | 
			
		||||
} as any).lazy(() => import('./routes/clients.lazy').then((d) => d.Route))
 | 
			
		||||
 | 
			
		||||
const BarcodeLazyRoute = BarcodeLazyImport.update({
 | 
			
		||||
  id: '/barcode',
 | 
			
		||||
  path: '/barcode',
 | 
			
		||||
  getParentRoute: () => rootRoute,
 | 
			
		||||
} as any).lazy(() => import('./routes/barcode.lazy').then((d) => d.Route))
 | 
			
		||||
 | 
			
		||||
const AdminLazyRoute = AdminLazyImport.update({
 | 
			
		||||
  id: '/admin',
 | 
			
		||||
  path: '/admin',
 | 
			
		||||
  getParentRoute: () => rootRoute,
 | 
			
		||||
} as any).lazy(() => import('./routes/admin.lazy').then((d) => d.Route))
 | 
			
		||||
 | 
			
		||||
const IndexLazyRoute = IndexLazyImport.update({
 | 
			
		||||
  id: '/',
 | 
			
		||||
  path: '/',
 | 
			
		||||
  getParentRoute: () => rootRoute,
 | 
			
		||||
} as any).lazy(() => import('./routes/index.lazy').then((d) => d.Route))
 | 
			
		||||
 | 
			
		||||
const LeadsDealIdRoute = LeadsDealIdImport.update({
 | 
			
		||||
  id: '/$dealId',
 | 
			
		||||
  path: '/$dealId',
 | 
			
		||||
  getParentRoute: () => LeadsLazyRoute,
 | 
			
		||||
} as any)
 | 
			
		||||
 | 
			
		||||
const DealsDealIdRoute = DealsDealIdImport.update({
 | 
			
		||||
  id: '/deals/$dealId',
 | 
			
		||||
  path: '/deals/$dealId',
 | 
			
		||||
  getParentRoute: () => rootRoute,
 | 
			
		||||
} as any)
 | 
			
		||||
@@ -406,8 +422,6 @@ export const routeTree = rootRoute
 | 
			
		||||
  ._addFileChildren(rootRouteChildren)
 | 
			
		||||
  ._addFileTypes<FileRouteTypes>()
 | 
			
		||||
 | 
			
		||||
/* prettier-ignore-end */
 | 
			
		||||
 | 
			
		||||
/* ROUTE_MANIFEST_START
 | 
			
		||||
{
 | 
			
		||||
  "routes": {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import { ProductSchema } from "../client";
 | 
			
		||||
import { isNil } from "lodash";
 | 
			
		||||
import { ProductFieldNames } from "../pages/CardsPage/tabs/ProductAndServiceTab/components/ProductView/ProductView.tsx";
 | 
			
		||||
import { ProductFieldNames } from "../modules/cardModules/cardEditorTabs/ProductAndServiceTab/components/ProductView/ProductView.tsx";
 | 
			
		||||
import UseObjectState from "./UseObjectState.ts";
 | 
			
		||||
import { CRUDTableProps } from "./CRUDTable.tsx";
 | 
			
		||||
import { MRT_RowData } from "mantine-react-table";
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user