feat: prettier
This commit is contained in:
		
							
								
								
									
										3
									
								
								.prettierignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.prettierignore
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					public
 | 
				
			||||||
 | 
					node_modules
 | 
				
			||||||
 | 
					src/client
 | 
				
			||||||
@@ -1,11 +1,11 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "singleAttributePerLine": true,
 | 
					    "singleAttributePerLine": true,
 | 
				
			||||||
  "singleQuote": false,
 | 
					    "singleQuote": false,
 | 
				
			||||||
  "semi": true,
 | 
					    "semi": true,
 | 
				
			||||||
  "quoteProps": "consistent",
 | 
					    "quoteProps": "consistent",
 | 
				
			||||||
  "bracketSpacing": true,
 | 
					    "bracketSpacing": true,
 | 
				
			||||||
  "trailingComma": "es5",
 | 
					    "trailingComma": "es5",
 | 
				
			||||||
  "tabWidth": 4,
 | 
					    "tabWidth": 4,
 | 
				
			||||||
  "bracketSameLine": true,
 | 
					    "bracketSameLine": true,
 | 
				
			||||||
  "arrowParens": "avoid"
 | 
					    "arrowParens": "avoid"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										28
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								README.md
									
									
									
									
									
								
							@@ -4,27 +4,27 @@ This template provides a minimal setup to get React working in Vite with HMR and
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Currently, two official plugins are available:
 | 
					Currently, two official plugins are available:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
 | 
					-   [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
 | 
				
			||||||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
 | 
					-   [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Expanding the ESLint configuration
 | 
					## Expanding the ESLint configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
 | 
					If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Configure the top-level `parserOptions` property like this:
 | 
					-   Configure the top-level `parserOptions` property like this:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```js
 | 
					```js
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
  // other rules...
 | 
					    // other rules...
 | 
				
			||||||
  parserOptions: {
 | 
					    parserOptions: {
 | 
				
			||||||
    ecmaVersion: 'latest',
 | 
					        ecmaVersion: "latest",
 | 
				
			||||||
    sourceType: 'module',
 | 
					        sourceType: "module",
 | 
				
			||||||
    project: ['./tsconfig.json', './tsconfig.node.json'],
 | 
					        project: ["./tsconfig.json", "./tsconfig.node.json"],
 | 
				
			||||||
    tsconfigRootDir: __dirname,
 | 
					        tsconfigRootDir: __dirname,
 | 
				
			||||||
  },
 | 
					    },
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
 | 
					-   Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
 | 
				
			||||||
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
 | 
					-   Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
 | 
				
			||||||
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
 | 
					-   Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										27
									
								
								index.html
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								index.html
									
									
									
									
									
								
							@@ -1,13 +1,20 @@
 | 
				
			|||||||
<!doctype html>
 | 
					<!doctype html>
 | 
				
			||||||
<html lang="en">
 | 
					<html lang="en">
 | 
				
			||||||
  <head>
 | 
					    <head>
 | 
				
			||||||
    <meta charset="UTF-8" />
 | 
					        <meta charset="UTF-8" />
 | 
				
			||||||
    <link rel="icon" type="image/svg+xml" href="/icons/favicon.png" />
 | 
					        <link
 | 
				
			||||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
					            rel="icon"
 | 
				
			||||||
    <title>DENCO: Fulfillment</title>
 | 
					            type="image/svg+xml"
 | 
				
			||||||
  </head>
 | 
					            href="/icons/favicon.png" />
 | 
				
			||||||
  <body>
 | 
					        <meta
 | 
				
			||||||
    <div id="root"></div>
 | 
					            name="viewport"
 | 
				
			||||||
    <script type="module" src="/src/main.tsx"></script>
 | 
					            content="width=device-width, initial-scale=1.0" />
 | 
				
			||||||
  </body>
 | 
					        <title>DENCO: Fulfillment</title>
 | 
				
			||||||
 | 
					    </head>
 | 
				
			||||||
 | 
					    <body>
 | 
				
			||||||
 | 
					        <div id="root"></div>
 | 
				
			||||||
 | 
					        <script
 | 
				
			||||||
 | 
					            type="module"
 | 
				
			||||||
 | 
					            src="/src/main.tsx"></script>
 | 
				
			||||||
 | 
					    </body>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										7894
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										7894
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										156
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										156
									
								
								package.json
									
									
									
									
									
								
							@@ -1,80 +1,80 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "fulfillment-frontend",
 | 
					    "name": "fulfillment-frontend",
 | 
				
			||||||
  "private": true,
 | 
					    "private": true,
 | 
				
			||||||
  "version": "0.0.0",
 | 
					    "version": "0.0.0",
 | 
				
			||||||
  "type": "module",
 | 
					    "type": "module",
 | 
				
			||||||
  "scripts": {
 | 
					    "scripts": {
 | 
				
			||||||
    "dev": "vite",
 | 
					        "dev": "vite",
 | 
				
			||||||
    "build": "tsc && vite build",
 | 
					        "build": "tsc && vite build",
 | 
				
			||||||
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
 | 
					        "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
 | 
				
			||||||
    "preview": "vite preview",
 | 
					        "preview": "vite preview",
 | 
				
			||||||
    "generate-client": "openapi --input http://127.0.0.1:8000/openapi.json --output ./src/client --client axios --useOptions --useUnionTypes"
 | 
					        "generate-client": "openapi --input http://127.0.0.1:8000/openapi.json --output ./src/client --client axios --useOptions --useUnionTypes"
 | 
				
			||||||
  },
 | 
					    },
 | 
				
			||||||
  "dependencies": {
 | 
					    "dependencies": {
 | 
				
			||||||
    "@fortawesome/fontawesome-svg-core": "^6.6.0",
 | 
					        "@fortawesome/fontawesome-svg-core": "^6.6.0",
 | 
				
			||||||
    "@fortawesome/free-regular-svg-icons": "^6.6.0",
 | 
					        "@fortawesome/free-regular-svg-icons": "^6.6.0",
 | 
				
			||||||
    "@fortawesome/free-solid-svg-icons": "^6.6.0",
 | 
					        "@fortawesome/free-solid-svg-icons": "^6.6.0",
 | 
				
			||||||
    "@fortawesome/react-fontawesome": "^0.2.2",
 | 
					        "@fortawesome/react-fontawesome": "^0.2.2",
 | 
				
			||||||
    "@hello-pangea/dnd": "^16.6.0",
 | 
					        "@hello-pangea/dnd": "^16.6.0",
 | 
				
			||||||
    "@mantine/core": "^7.11.2",
 | 
					        "@mantine/core": "^7.11.2",
 | 
				
			||||||
    "@mantine/dates": "^7.11.2",
 | 
					        "@mantine/dates": "^7.11.2",
 | 
				
			||||||
    "@mantine/dropzone": "^7.11.2",
 | 
					        "@mantine/dropzone": "^7.11.2",
 | 
				
			||||||
    "@mantine/form": "^7.11.2",
 | 
					        "@mantine/form": "^7.11.2",
 | 
				
			||||||
    "@mantine/hooks": "^7.11.2",
 | 
					        "@mantine/hooks": "^7.11.2",
 | 
				
			||||||
    "@mantine/modals": "^7.11.2",
 | 
					        "@mantine/modals": "^7.11.2",
 | 
				
			||||||
    "@mantine/notifications": "^7.11.2",
 | 
					        "@mantine/notifications": "^7.11.2",
 | 
				
			||||||
    "@reduxjs/toolkit": "^2.2.6",
 | 
					        "@reduxjs/toolkit": "^2.2.6",
 | 
				
			||||||
    "@tabler/icons-react": "^3.11.0",
 | 
					        "@tabler/icons-react": "^3.11.0",
 | 
				
			||||||
    "@tanstack/react-query": "^5.51.9",
 | 
					        "@tanstack/react-query": "^5.51.9",
 | 
				
			||||||
    "@tanstack/react-router": "^1.45.6",
 | 
					        "@tanstack/react-router": "^1.45.6",
 | 
				
			||||||
    "@tanstack/router-devtools": "^1.45.6",
 | 
					        "@tanstack/router-devtools": "^1.45.6",
 | 
				
			||||||
    "@tanstack/router-vite-plugin": "^1.45.3",
 | 
					        "@tanstack/router-vite-plugin": "^1.45.3",
 | 
				
			||||||
    "axios": "^1.7.2",
 | 
					        "axios": "^1.7.2",
 | 
				
			||||||
    "classnames": "^2.5.1",
 | 
					        "classnames": "^2.5.1",
 | 
				
			||||||
    "clsx": "^2.1.1",
 | 
					        "clsx": "^2.1.1",
 | 
				
			||||||
    "cyrillic-to-translit-js": "^3.2.1",
 | 
					        "cyrillic-to-translit-js": "^3.2.1",
 | 
				
			||||||
    "dayjs": "^1.11.12",
 | 
					        "dayjs": "^1.11.12",
 | 
				
			||||||
    "dot-object": "^2.1.5",
 | 
					        "dot-object": "^2.1.5",
 | 
				
			||||||
    "file-saver": "^2.0.5",
 | 
					        "file-saver": "^2.0.5",
 | 
				
			||||||
    "framer-motion": "^11.3.8",
 | 
					        "framer-motion": "^11.3.8",
 | 
				
			||||||
    "globals": "^15.8.0",
 | 
					        "globals": "^15.8.0",
 | 
				
			||||||
    "jwt-decode": "^4.0.0",
 | 
					        "jwt-decode": "^4.0.0",
 | 
				
			||||||
    "lodash": "^4.17.21",
 | 
					        "lodash": "^4.17.21",
 | 
				
			||||||
    "mantine-form-zod-resolver": "^1.1.0",
 | 
					        "mantine-form-zod-resolver": "^1.1.0",
 | 
				
			||||||
    "mantine-react-table": "^2.0.0-beta.5",
 | 
					        "mantine-react-table": "^2.0.0-beta.5",
 | 
				
			||||||
    "phone": "^3.1.49",
 | 
					        "phone": "^3.1.49",
 | 
				
			||||||
    "react": "^18.3.1",
 | 
					        "react": "^18.3.1",
 | 
				
			||||||
    "react-barcode": "^1.5.3",
 | 
					        "react-barcode": "^1.5.3",
 | 
				
			||||||
    "react-dom": "^18.3.1",
 | 
					        "react-dom": "^18.3.1",
 | 
				
			||||||
    "react-imask": "^7.6.1",
 | 
					        "react-imask": "^7.6.1",
 | 
				
			||||||
    "react-redux": "^9.1.2",
 | 
					        "react-redux": "^9.1.2",
 | 
				
			||||||
    "react-to-print": "^2.15.1",
 | 
					        "react-to-print": "^2.15.1",
 | 
				
			||||||
    "reactflow": "^11.11.4",
 | 
					        "reactflow": "^11.11.4",
 | 
				
			||||||
    "zod": "^3.23.8"
 | 
					        "zod": "^3.23.8"
 | 
				
			||||||
  },
 | 
					    },
 | 
				
			||||||
  "devDependencies": {
 | 
					    "devDependencies": {
 | 
				
			||||||
    "@eslint/js": "^9.7.0",
 | 
					        "@eslint/js": "^9.7.0",
 | 
				
			||||||
    "@types/dot-object": "^2.1.6",
 | 
					        "@types/dot-object": "^2.1.6",
 | 
				
			||||||
    "@types/eslint__js": "^8.42.3",
 | 
					        "@types/eslint__js": "^8.42.3",
 | 
				
			||||||
    "@types/file-saver": "^2.0.7",
 | 
					        "@types/file-saver": "^2.0.7",
 | 
				
			||||||
    "@types/lodash": "^4.17.7",
 | 
					        "@types/lodash": "^4.17.7",
 | 
				
			||||||
    "@types/react": "^18.3.3",
 | 
					        "@types/react": "^18.3.3",
 | 
				
			||||||
    "@types/react-dom": "^18.3.0",
 | 
					        "@types/react-dom": "^18.3.0",
 | 
				
			||||||
    "@typescript-eslint/eslint-plugin": "^7.16.1",
 | 
					        "@typescript-eslint/eslint-plugin": "^7.16.1",
 | 
				
			||||||
    "@typescript-eslint/parser": "^7.16.1",
 | 
					        "@typescript-eslint/parser": "^7.16.1",
 | 
				
			||||||
    "@vitejs/plugin-react-swc": "^3.7.0",
 | 
					        "@vitejs/plugin-react-swc": "^3.7.0",
 | 
				
			||||||
    "eslint": "^9.7.0",
 | 
					        "eslint": "^9.7.0",
 | 
				
			||||||
    "eslint-plugin-react-hooks": "^4.6.2",
 | 
					        "eslint-plugin-react-hooks": "^4.6.2",
 | 
				
			||||||
    "eslint-plugin-react-refresh": "^0.4.8",
 | 
					        "eslint-plugin-react-refresh": "^0.4.8",
 | 
				
			||||||
    "openapi-typescript-codegen": "^0.29.0",
 | 
					        "openapi-typescript-codegen": "^0.29.0",
 | 
				
			||||||
    "postcss": "^8.4.39",
 | 
					        "postcss": "^8.4.39",
 | 
				
			||||||
    "postcss-preset-mantine": "^1.16.0",
 | 
					        "postcss-preset-mantine": "^1.16.0",
 | 
				
			||||||
    "postcss-simple-vars": "^7.0.1",
 | 
					        "postcss-simple-vars": "^7.0.1",
 | 
				
			||||||
    "sass": "^1.77.8",
 | 
					        "sass": "^1.77.8",
 | 
				
			||||||
    "typescript": "^5.5.3",
 | 
					        "typescript": "^5.5.3",
 | 
				
			||||||
    "typescript-eslint": "^7.16.1",
 | 
					        "typescript-eslint": "^7.16.1",
 | 
				
			||||||
    "vite": "^5.3.4",
 | 
					        "vite": "^5.3.4",
 | 
				
			||||||
    "yarn-upgrade-all": "^0.7.2"
 | 
					        "yarn-upgrade-all": "^0.7.2"
 | 
				
			||||||
  },
 | 
					    },
 | 
				
			||||||
  "packageManager": "yarn@4.1.0"
 | 
					    "packageManager": "yarn@4.1.0"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,13 +1,13 @@
 | 
				
			|||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
    plugins: {
 | 
					    plugins: {
 | 
				
			||||||
        'postcss-preset-mantine': {},
 | 
					        "postcss-preset-mantine": {},
 | 
				
			||||||
        'postcss-simple-vars': {
 | 
					        "postcss-simple-vars": {
 | 
				
			||||||
            variables: {
 | 
					            variables: {
 | 
				
			||||||
                'mantine-breakpoint-xs': '36em',
 | 
					                "mantine-breakpoint-xs": "36em",
 | 
				
			||||||
                'mantine-breakpoint-sm': '48em',
 | 
					                "mantine-breakpoint-sm": "48em",
 | 
				
			||||||
                'mantine-breakpoint-md': '62em',
 | 
					                "mantine-breakpoint-md": "62em",
 | 
				
			||||||
                'mantine-breakpoint-lg': '75em',
 | 
					                "mantine-breakpoint-lg": "75em",
 | 
				
			||||||
                'mantine-breakpoint-xl': '88em',
 | 
					                "mantine-breakpoint-xl": "88em",
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,14 +1,14 @@
 | 
				
			|||||||
import {useQuery} from "@tanstack/react-query";
 | 
					import { useQuery } from "@tanstack/react-query";
 | 
				
			||||||
import {BarcodeService} from "../../client";
 | 
					import { BarcodeService } from "../../client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const useGetAllBarcodeTemplates = () => {
 | 
					const useGetAllBarcodeTemplates = () => {
 | 
				
			||||||
    const {data, error, isLoading, refetch} = useQuery({
 | 
					    const { data, error, isLoading, refetch } = useQuery({
 | 
				
			||||||
        queryKey: ['getAllBarcodeTemplates'],
 | 
					        queryKey: ["getAllBarcodeTemplates"],
 | 
				
			||||||
        queryFn: () => BarcodeService.getAllBarcodeTemplates(),
 | 
					        queryFn: () => BarcodeService.getAllBarcodeTemplates(),
 | 
				
			||||||
        select: (data) => data.templates
 | 
					        select: data => data.templates,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    const barcodeTemplates = isLoading || error || !data ? [] : data;
 | 
					    const barcodeTemplates = isLoading || error || !data ? [] : data;
 | 
				
			||||||
    return {barcodeTemplates, refetch}
 | 
					    return { barcodeTemplates, refetch };
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default useGetAllBarcodeTemplates;
 | 
					export default useGetAllBarcodeTemplates;
 | 
				
			||||||
@@ -1,11 +1,11 @@
 | 
				
			|||||||
import {ProductService} from "../../client";
 | 
					import { ProductService } from "../../client";
 | 
				
			||||||
import {useQuery} from "@tanstack/react-query";
 | 
					import { useQuery } from "@tanstack/react-query";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const useGetProductById = (id: number) => {
 | 
					export const useGetProductById = (id: number) => {
 | 
				
			||||||
    const {data, error, isLoading, refetch} = useQuery({
 | 
					    const { data, error, isLoading, refetch } = useQuery({
 | 
				
			||||||
        queryKey: ['getProductById', id],
 | 
					        queryKey: ["getProductById", id],
 | 
				
			||||||
        queryFn: () => ProductService.getProductById({productId: id}),
 | 
					        queryFn: () => ProductService.getProductById({ productId: id }),
 | 
				
			||||||
        select: (data) => data
 | 
					        select: data => data,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    return {product: data, error, isLoading, refetch}
 | 
					    return { product: data, error, isLoading, refetch };
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
import {forwardRef, useContext, useRef} from "react";
 | 
					import { forwardRef, useContext, useRef } from "react";
 | 
				
			||||||
import {getRouterContext, Outlet} from "@tanstack/react-router";
 | 
					import { getRouterContext, Outlet } from "@tanstack/react-router";
 | 
				
			||||||
import {motion, useIsPresent} from "framer-motion";
 | 
					import { motion, useIsPresent } from "framer-motion";
 | 
				
			||||||
import {cloneDeep} from "lodash";
 | 
					import { cloneDeep } from "lodash";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const AnimatedOutlet = forwardRef<HTMLDivElement>((_, ref) => {
 | 
					const AnimatedOutlet = forwardRef<HTMLDivElement>((_, ref) => {
 | 
				
			||||||
    const RouterContext = getRouterContext();
 | 
					    const RouterContext = getRouterContext();
 | 
				
			||||||
@@ -17,26 +17,24 @@ const AnimatedOutlet = forwardRef<HTMLDivElement>((_, ref) => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <motion.div ref={ref}
 | 
					        <motion.div
 | 
				
			||||||
                    initial={{x: "-100%"}}
 | 
					            ref={ref}
 | 
				
			||||||
                    animate={{
 | 
					            initial={{ x: "-100%" }}
 | 
				
			||||||
                        x: 0,
 | 
					            animate={{
 | 
				
			||||||
                        transform: "",
 | 
					                x: 0,
 | 
				
			||||||
                        transitionEnd: {
 | 
					                transform: "",
 | 
				
			||||||
                            transform: "none"
 | 
					                transitionEnd: {
 | 
				
			||||||
                        }
 | 
					                    transform: "none",
 | 
				
			||||||
                    }}
 | 
					                },
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
                    transition={{
 | 
					            transition={{
 | 
				
			||||||
                        duration: 0.4,
 | 
					                duration: 0.4,
 | 
				
			||||||
                        ease: "circInOut",
 | 
					                ease: "circInOut",
 | 
				
			||||||
                    }}
 | 
					            }}>
 | 
				
			||||||
 | 
					 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
            <RouterContext.Provider value={renderedContext.current}>
 | 
					            <RouterContext.Provider value={renderedContext.current}>
 | 
				
			||||||
                <Outlet/>
 | 
					                <Outlet />
 | 
				
			||||||
            </RouterContext.Provider>
 | 
					            </RouterContext.Provider>
 | 
				
			||||||
        </motion.div>
 | 
					        </motion.div>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
export default AnimatedOutlet
 | 
					export default AnimatedOutlet;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,19 +4,18 @@ import {
 | 
				
			|||||||
    MRT_RowData,
 | 
					    MRT_RowData,
 | 
				
			||||||
    MRT_TableInstance,
 | 
					    MRT_TableInstance,
 | 
				
			||||||
    MRT_TableOptions,
 | 
					    MRT_TableOptions,
 | 
				
			||||||
    useMantineReactTable
 | 
					    useMantineReactTable,
 | 
				
			||||||
} from "mantine-react-table";
 | 
					} from "mantine-react-table";
 | 
				
			||||||
import {MRT_Localization_RU} from "mantine-react-table/locales/ru/index.cjs";
 | 
					import { MRT_Localization_RU } from "mantine-react-table/locales/ru/index.cjs";
 | 
				
			||||||
import {forwardRef, useEffect, useImperativeHandle} from 'react';
 | 
					import { forwardRef, useEffect, useImperativeHandle } from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props<T extends Record<string, unknown>, K extends keyof T> = {
 | 
					type Props<T extends Record<string, unknown>, K extends keyof T> = {
 | 
				
			||||||
    data: T[],
 | 
					    data: T[];
 | 
				
			||||||
    onSelectionChange?: (selectedRows: T[]) => void,
 | 
					    onSelectionChange?: (selectedRows: T[]) => void;
 | 
				
			||||||
    columns: MRT_ColumnDef<T, K>[],
 | 
					    columns: MRT_ColumnDef<T, K>[];
 | 
				
			||||||
    restProps?: MRT_TableOptions<T>,
 | 
					    restProps?: MRT_TableOptions<T>;
 | 
				
			||||||
    striped?: boolean
 | 
					    striped?: boolean;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Экспортируем тип рефа, чтобы он мог быть использован в других компонентах
 | 
					// Экспортируем тип рефа, чтобы он мог быть использован в других компонентах
 | 
				
			||||||
export type BaseTableRef<T extends MRT_RowData> = {
 | 
					export type BaseTableRef<T extends MRT_RowData> = {
 | 
				
			||||||
@@ -25,33 +24,42 @@ export type BaseTableRef<T extends MRT_RowData> = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
 | 
					// eslint-disable-next-line @typescript-eslint/ban-ts-comment
 | 
				
			||||||
// @ts-expect-error
 | 
					// @ts-expect-error
 | 
				
			||||||
export const BaseTable = forwardRef<BaseTableRef<never>, Props<never>>((props, ref) => {
 | 
					export const BaseTable = forwardRef<BaseTableRef<never>, Props<never>>(
 | 
				
			||||||
    const {data, columns, restProps, striped = true, onSelectionChange} = props;
 | 
					    (props, ref) => {
 | 
				
			||||||
 | 
					        const {
 | 
				
			||||||
 | 
					            data,
 | 
				
			||||||
 | 
					            columns,
 | 
				
			||||||
 | 
					            restProps,
 | 
				
			||||||
 | 
					            striped = true,
 | 
				
			||||||
 | 
					            onSelectionChange,
 | 
				
			||||||
 | 
					        } = props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const table = useMantineReactTable({
 | 
					        const table = useMantineReactTable({
 | 
				
			||||||
        localization: MRT_Localization_RU,
 | 
					            localization: MRT_Localization_RU,
 | 
				
			||||||
        enablePagination: false,
 | 
					            enablePagination: false,
 | 
				
			||||||
        data,
 | 
					            data,
 | 
				
			||||||
        columns,
 | 
					            columns,
 | 
				
			||||||
        mantineTableProps: {
 | 
					            mantineTableProps: {
 | 
				
			||||||
            striped: striped
 | 
					                striped: striped,
 | 
				
			||||||
        },
 | 
					            },
 | 
				
			||||||
        enableTopToolbar: false,
 | 
					            enableTopToolbar: false,
 | 
				
			||||||
        enableBottomToolbar: false,
 | 
					            enableBottomToolbar: false,
 | 
				
			||||||
        enableRowSelection: onSelectionChange !== undefined,
 | 
					            enableRowSelection: onSelectionChange !== undefined,
 | 
				
			||||||
        ...restProps,
 | 
					            ...restProps,
 | 
				
			||||||
    });
 | 
					        });
 | 
				
			||||||
    useEffect(() => {
 | 
					        useEffect(() => {
 | 
				
			||||||
        if (!onSelectionChange) return;
 | 
					            if (!onSelectionChange) return;
 | 
				
			||||||
        onSelectionChange(table.getSelectedRowModel().rows.map(row => row.original))
 | 
					            onSelectionChange(
 | 
				
			||||||
    }, [table.getState().rowSelection]);
 | 
					                table.getSelectedRowModel().rows.map(row => row.original)
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }, [table.getState().rowSelection]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Используем useImperativeHandle для определения, что будет доступно через ref
 | 
					        // Используем useImperativeHandle для определения, что будет доступно через ref
 | 
				
			||||||
    useImperativeHandle(ref, () => ({
 | 
					        useImperativeHandle(ref, () => ({
 | 
				
			||||||
        // @ts-ignore
 | 
					            // @ts-ignore
 | 
				
			||||||
        getTable: () => table
 | 
					            getTable: () => table,
 | 
				
			||||||
    }));
 | 
					        }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return <MantineReactTable table={table} />;
 | 
				
			||||||
    return <MantineReactTable table={table}/>;
 | 
					    }
 | 
				
			||||||
});
 | 
					);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +1,15 @@
 | 
				
			|||||||
import {Button, rem, Tooltip} from '@mantine/core';
 | 
					import { Button, rem, Tooltip } from "@mantine/core";
 | 
				
			||||||
import {IconCheck, IconCopy} from '@tabler/icons-react';
 | 
					import { IconCheck, IconCopy } from "@tabler/icons-react";
 | 
				
			||||||
import {FC} from "react";
 | 
					import { FC } from "react";
 | 
				
			||||||
import {useClipboard} from "@mantine/hooks";
 | 
					import { useClipboard } from "@mantine/hooks";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
    children: string;
 | 
					    children: string;
 | 
				
			||||||
    value: string
 | 
					    value: string;
 | 
				
			||||||
    onCopiedLabel: string;
 | 
					    onCopiedLabel: string;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ButtonCopy: FC<Props> = ({children, onCopiedLabel, value}) => {
 | 
					export const ButtonCopy: FC<Props> = ({ children, onCopiedLabel, value }) => {
 | 
				
			||||||
    const clipboard = useClipboard();
 | 
					    const clipboard = useClipboard();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
@@ -18,20 +18,19 @@ export const ButtonCopy: FC<Props> = ({children, onCopiedLabel, value}) => {
 | 
				
			|||||||
            offset={5}
 | 
					            offset={5}
 | 
				
			||||||
            position="bottom"
 | 
					            position="bottom"
 | 
				
			||||||
            radius="xl"
 | 
					            radius="xl"
 | 
				
			||||||
            transitionProps={{duration: 100, transition: 'slide-down'}}
 | 
					            transitionProps={{ duration: 100, transition: "slide-down" }}
 | 
				
			||||||
            opened={clipboard.copied}
 | 
					            opened={clipboard.copied}>
 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
            <Button
 | 
					            <Button
 | 
				
			||||||
                variant="light"
 | 
					                variant="light"
 | 
				
			||||||
                rightSection={
 | 
					                rightSection={
 | 
				
			||||||
                    clipboard.copied ? (
 | 
					                    clipboard.copied ? (
 | 
				
			||||||
                        <IconCheck
 | 
					                        <IconCheck
 | 
				
			||||||
                            style={{width: rem(20), height: rem(20)}}
 | 
					                            style={{ width: rem(20), height: rem(20) }}
 | 
				
			||||||
                            stroke={1.5}
 | 
					                            stroke={1.5}
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
                    ) : (
 | 
					                    ) : (
 | 
				
			||||||
                        <IconCopy
 | 
					                        <IconCopy
 | 
				
			||||||
                            style={{width: rem(20), height: rem(20)}}
 | 
					                            style={{ width: rem(20), height: rem(20) }}
 | 
				
			||||||
                            stroke={1.5}
 | 
					                            stroke={1.5}
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
@@ -42,13 +41,12 @@ export const ButtonCopy: FC<Props> = ({children, onCopiedLabel, value}) => {
 | 
				
			|||||||
                    root: {
 | 
					                    root: {
 | 
				
			||||||
                        paddingRight: rem(14),
 | 
					                        paddingRight: rem(14),
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    section: {marginLeft: rem(22)},
 | 
					                    section: { marginLeft: rem(22) },
 | 
				
			||||||
                }}
 | 
					                }}
 | 
				
			||||||
                onClick={() => clipboard.copy(value)}
 | 
					                onClick={() => clipboard.copy(value)}>
 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
                {children}
 | 
					                {children}
 | 
				
			||||||
            </Button>
 | 
					            </Button>
 | 
				
			||||||
        </Tooltip>
 | 
					        </Tooltip>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
export default ButtonCopy;
 | 
					export default ButtonCopy;
 | 
				
			||||||
@@ -1,35 +1,39 @@
 | 
				
			|||||||
import {Button, rem, Tooltip} from '@mantine/core';
 | 
					import { Button, rem, Tooltip } from "@mantine/core";
 | 
				
			||||||
import {IconCheck, IconCopy} from '@tabler/icons-react';
 | 
					import { IconCheck, IconCopy } from "@tabler/icons-react";
 | 
				
			||||||
import {FC} from "react";
 | 
					import { FC } from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
    children: string;
 | 
					    children: string;
 | 
				
			||||||
    onCopyClick: () => void;
 | 
					    onCopyClick: () => void;
 | 
				
			||||||
    onCopiedLabel: string;
 | 
					    onCopiedLabel: string;
 | 
				
			||||||
    copied: boolean;
 | 
					    copied: boolean;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ButtonCopyControlled: FC<Props> = ({children, onCopiedLabel, onCopyClick, copied}) => {
 | 
					export const ButtonCopyControlled: FC<Props> = ({
 | 
				
			||||||
 | 
					    children,
 | 
				
			||||||
 | 
					    onCopiedLabel,
 | 
				
			||||||
 | 
					    onCopyClick,
 | 
				
			||||||
 | 
					    copied,
 | 
				
			||||||
 | 
					}) => {
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Tooltip
 | 
					        <Tooltip
 | 
				
			||||||
            label={onCopiedLabel}
 | 
					            label={onCopiedLabel}
 | 
				
			||||||
            offset={5}
 | 
					            offset={5}
 | 
				
			||||||
            position="bottom"
 | 
					            position="bottom"
 | 
				
			||||||
            radius="xl"
 | 
					            radius="xl"
 | 
				
			||||||
            transitionProps={{duration: 100, transition: 'slide-down'}}
 | 
					            transitionProps={{ duration: 100, transition: "slide-down" }}
 | 
				
			||||||
            opened={copied}
 | 
					            opened={copied}>
 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
            <Button
 | 
					            <Button
 | 
				
			||||||
                variant="light"
 | 
					                variant="light"
 | 
				
			||||||
                rightSection={
 | 
					                rightSection={
 | 
				
			||||||
                    copied ? (
 | 
					                    copied ? (
 | 
				
			||||||
                        <IconCheck
 | 
					                        <IconCheck
 | 
				
			||||||
                            style={{width: rem(20), height: rem(20)}}
 | 
					                            style={{ width: rem(20), height: rem(20) }}
 | 
				
			||||||
                            stroke={1.5}
 | 
					                            stroke={1.5}
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
                    ) : (
 | 
					                    ) : (
 | 
				
			||||||
                        <IconCopy
 | 
					                        <IconCopy
 | 
				
			||||||
                            style={{width: rem(20), height: rem(20)}}
 | 
					                            style={{ width: rem(20), height: rem(20) }}
 | 
				
			||||||
                            stroke={1.5}
 | 
					                            stroke={1.5}
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
@@ -40,12 +44,11 @@ export const ButtonCopyControlled: FC<Props> = ({children, onCopiedLabel, onCopy
 | 
				
			|||||||
                    root: {
 | 
					                    root: {
 | 
				
			||||||
                        paddingRight: rem(14),
 | 
					                        paddingRight: rem(14),
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    section: {marginLeft: rem(22)},
 | 
					                    section: { marginLeft: rem(22) },
 | 
				
			||||||
                }}
 | 
					                }}
 | 
				
			||||||
                onClick={onCopyClick}
 | 
					                onClick={onCopyClick}>
 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
                {children}
 | 
					                {children}
 | 
				
			||||||
            </Button>
 | 
					            </Button>
 | 
				
			||||||
        </Tooltip>
 | 
					        </Tooltip>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,34 +1,34 @@
 | 
				
			|||||||
import {NumberInput, NumberInputProps} from "@mantine/core";
 | 
					import { NumberInput, NumberInputProps } from "@mantine/core";
 | 
				
			||||||
import {useEffect, useState} from "react";
 | 
					import { useEffect, useState } from "react";
 | 
				
			||||||
import {useDebouncedValue} from "@mantine/hooks";
 | 
					import { useDebouncedValue } from "@mantine/hooks";
 | 
				
			||||||
import {isNumber, omit} from "lodash";
 | 
					import { isNumber, omit } from "lodash";
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = NumberInputProps;
 | 
					type Props = NumberInputProps;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const DebouncedNumberInput = (props: Props) => {
 | 
					const DebouncedNumberInput = (props: Props) => {
 | 
				
			||||||
    const [value, setValue] = useState<number | string>(props.defaultValue || props.value || '');
 | 
					    const [value, setValue] = useState<number | string>(
 | 
				
			||||||
 | 
					        props.defaultValue || props.value || ""
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
    const [debounced] = useDebouncedValue(value, 200);
 | 
					    const [debounced] = useDebouncedValue(value, 200);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        if (!props.onChange) return;
 | 
					        if (!props.onChange) return;
 | 
				
			||||||
        props.onChange(debounced);
 | 
					        props.onChange(debounced);
 | 
				
			||||||
    }, [debounced])
 | 
					    }, [debounced]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        if (!isNumber(props.value)) return;
 | 
					        if (!isNumber(props.value)) return;
 | 
				
			||||||
        setValue(props.value);
 | 
					        setValue(props.value);
 | 
				
			||||||
    }, [props.value])
 | 
					    }, [props.value]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const restProps = omit(props, ["onChange", "value"])
 | 
					    const restProps = omit(props, ["onChange", "value"]);
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <NumberInput
 | 
					        <NumberInput
 | 
				
			||||||
            value={value}
 | 
					            value={value}
 | 
				
			||||||
            onChange={setValue}
 | 
					            onChange={setValue}
 | 
				
			||||||
            {...restProps}
 | 
					            {...restProps}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default DebouncedNumberInput;
 | 
					export default DebouncedNumberInput;
 | 
				
			||||||
@@ -3,7 +3,6 @@
 | 
				
			|||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    flex-direction: column;
 | 
					    flex-direction: column;
 | 
				
			||||||
    /*background-color: green;*/
 | 
					    /*background-color: green;*/
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.header {
 | 
					.header {
 | 
				
			||||||
@@ -34,7 +33,6 @@
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    @mixin dark {
 | 
					    @mixin dark {
 | 
				
			||||||
        background-color: var(--mantine-color-dark-5);
 | 
					        background-color: var(--mantine-color-dark-5);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    border-radius: var(--item-border-radius);
 | 
					    border-radius: var(--item-border-radius);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,13 +1,13 @@
 | 
				
			|||||||
import {FC} from "react";
 | 
					import { FC } from "react";
 | 
				
			||||||
import styles from './Board.module.css';
 | 
					import styles from "./Board.module.css";
 | 
				
			||||||
import {Divider, Text, Title} from '@mantine/core';
 | 
					import { Divider, Text, Title } from "@mantine/core";
 | 
				
			||||||
import {Draggable, Droppable} from "@hello-pangea/dnd";
 | 
					import { Draggable, Droppable } from "@hello-pangea/dnd";
 | 
				
			||||||
import CreateDealButton from "../CreateDealButton/CreateDealButton.tsx";
 | 
					import CreateDealButton from "../CreateDealButton/CreateDealButton.tsx";
 | 
				
			||||||
import {DealSummary} from "../../../client";
 | 
					import { DealSummary } from "../../../client";
 | 
				
			||||||
import DealSummaryCard from "../DealSummaryCard/DealSummaryCard.tsx";
 | 
					import DealSummaryCard from "../DealSummaryCard/DealSummaryCard.tsx";
 | 
				
			||||||
import classNames from "classnames";
 | 
					import classNames from "classnames";
 | 
				
			||||||
import {getPluralForm} from "../../../shared/lib/utils.ts";
 | 
					import { getPluralForm } from "../../../shared/lib/utils.ts";
 | 
				
			||||||
import {sum} from "lodash";
 | 
					import { sum } from "lodash";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
    droppableId: string;
 | 
					    droppableId: string;
 | 
				
			||||||
@@ -15,66 +15,74 @@ type Props = {
 | 
				
			|||||||
    withCreateButton?: boolean;
 | 
					    withCreateButton?: boolean;
 | 
				
			||||||
    summaries: DealSummary[];
 | 
					    summaries: DealSummary[];
 | 
				
			||||||
    color: string;
 | 
					    color: string;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const Board: FC<Props> = ({droppableId, title, summaries, color, withCreateButton = false}) => {
 | 
					export const Board: FC<Props> = ({
 | 
				
			||||||
 | 
					    droppableId,
 | 
				
			||||||
 | 
					    title,
 | 
				
			||||||
 | 
					    summaries,
 | 
				
			||||||
 | 
					    color,
 | 
				
			||||||
 | 
					    withCreateButton = false,
 | 
				
			||||||
 | 
					}) => {
 | 
				
			||||||
    const getDealsText = () => {
 | 
					    const getDealsText = () => {
 | 
				
			||||||
        const pluralForm = getPluralForm(summaries.length, 'сделка', 'сделки', 'сделок');
 | 
					        const pluralForm = getPluralForm(
 | 
				
			||||||
 | 
					            summaries.length,
 | 
				
			||||||
 | 
					            "сделка",
 | 
				
			||||||
 | 
					            "сделки",
 | 
				
			||||||
 | 
					            "сделок"
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
        return `${summaries.length} ${pluralForm}: ${sum(summaries.map(summary => summary.totalPrice)).toLocaleString("ru-RU")}₽`;
 | 
					        return `${summaries.length} ${pluralForm}: ${sum(summaries.map(summary => summary.totalPrice)).toLocaleString("ru-RU")}₽`;
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <div className={styles["container"]}>
 | 
					        <div className={styles["container"]}>
 | 
				
			||||||
            <div className={styles["header"]}>
 | 
					            <div className={styles["header"]}>
 | 
				
			||||||
                <Title size={"h4"}>{title}</Title>
 | 
					                <Title size={"h4"}>{title}</Title>
 | 
				
			||||||
                <Text>{getDealsText()}</Text>
 | 
					                <Text>{getDealsText()}</Text>
 | 
				
			||||||
                <Divider size={"xl"} my={10} color={color}/>
 | 
					                <Divider
 | 
				
			||||||
 | 
					                    size={"xl"}
 | 
				
			||||||
 | 
					                    my={10}
 | 
				
			||||||
 | 
					                    color={color}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <Droppable
 | 
					            <Droppable droppableId={droppableId}>
 | 
				
			||||||
                droppableId={droppableId}
 | 
					 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
                {(provided, snapshot) => (
 | 
					                {(provided, snapshot) => (
 | 
				
			||||||
                    <div ref={provided.innerRef}
 | 
					                    <div
 | 
				
			||||||
                         className={classNames(
 | 
					                        ref={provided.innerRef}
 | 
				
			||||||
                             styles["items-list"],
 | 
					                        className={classNames(
 | 
				
			||||||
                             (snapshot.isDraggingOver && !snapshot.draggingFromThisWith)
 | 
					                            styles["items-list"],
 | 
				
			||||||
                             && styles["items-list-drag-over"]
 | 
					                            snapshot.isDraggingOver &&
 | 
				
			||||||
                         )}
 | 
					                                !snapshot.draggingFromThisWith &&
 | 
				
			||||||
                         {...provided.droppableProps}
 | 
					                                styles["items-list-drag-over"]
 | 
				
			||||||
                    >
 | 
					                        )}
 | 
				
			||||||
                        {withCreateButton &&
 | 
					                        {...provided.droppableProps}>
 | 
				
			||||||
                            <CreateDealButton
 | 
					                        {withCreateButton && (
 | 
				
			||||||
 | 
					                            <CreateDealButton onClick={() => {}} />
 | 
				
			||||||
                                onClick={() => {
 | 
					                        )}
 | 
				
			||||||
                                }}
 | 
					                        {summaries.map(summary => (
 | 
				
			||||||
                            />}
 | 
					                            <Draggable
 | 
				
			||||||
                        {summaries.map(summary =>
 | 
					                                draggableId={summary.id.toString()}
 | 
				
			||||||
                            (
 | 
					                                index={summary.rank}
 | 
				
			||||||
                                <Draggable
 | 
					                                key={summary.id}>
 | 
				
			||||||
                                    draggableId={summary.id.toString()}
 | 
					                                {provided => (
 | 
				
			||||||
                                    index={summary.rank}
 | 
					                                    <div
 | 
				
			||||||
                                    key={summary.id}
 | 
					                                        {...provided.draggableProps}
 | 
				
			||||||
                                >
 | 
					                                        {...provided.dragHandleProps}
 | 
				
			||||||
                                    {(provided) => (
 | 
					                                        ref={provided.innerRef}>
 | 
				
			||||||
                                        <div            {...provided.draggableProps}
 | 
					                                        <DealSummaryCard
 | 
				
			||||||
                                                        {...provided.dragHandleProps}
 | 
					                                            dealSummary={summary}
 | 
				
			||||||
                                                        ref={provided.innerRef}
 | 
					                                        />
 | 
				
			||||||
 | 
					                                    </div>
 | 
				
			||||||
                                        >
 | 
					                                )}
 | 
				
			||||||
                                            <DealSummaryCard dealSummary={summary}/>
 | 
					                            </Draggable>
 | 
				
			||||||
                                        </div>
 | 
					                        ))}
 | 
				
			||||||
 | 
					 | 
				
			||||||
                                    )}
 | 
					 | 
				
			||||||
                                </Draggable>
 | 
					 | 
				
			||||||
                            ))}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        {provided.placeholder}
 | 
					                        {provided.placeholder}
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                )}
 | 
					                )}
 | 
				
			||||||
            </Droppable>
 | 
					            </Droppable>
 | 
				
			||||||
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Board;
 | 
					export default Board;
 | 
				
			||||||
@@ -9,11 +9,11 @@
 | 
				
			|||||||
    border: dashed var(--item-border-size) var(--mantine-color-default-border);
 | 
					    border: dashed var(--item-border-size) var(--mantine-color-default-border);
 | 
				
			||||||
    border-radius: var(--item-border-radius);
 | 
					    border-radius: var(--item-border-radius);
 | 
				
			||||||
    cursor: pointer;
 | 
					    cursor: pointer;
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.container:hover {
 | 
					.container:hover {
 | 
				
			||||||
    background-color: light-dark(var(--mantine-color-default-hover), var(--mantine-color-gray-filled-hover));
 | 
					    background-color: light-dark(
 | 
				
			||||||
 | 
					        var(--mantine-color-default-hover),
 | 
				
			||||||
 | 
					        var(--mantine-color-gray-filled-hover)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,60 +1,61 @@
 | 
				
			|||||||
import {FC, useState} from "react";
 | 
					import { FC, useState } from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import styles from './CreateDealButton.module.css';
 | 
					import styles from "./CreateDealButton.module.css";
 | 
				
			||||||
import {Text, Transition} from '@mantine/core';
 | 
					import { Text, Transition } from "@mantine/core";
 | 
				
			||||||
import CreateDealFrom from "../CreateDealForm/CreateDealFrom.tsx";
 | 
					import CreateDealFrom from "../CreateDealForm/CreateDealFrom.tsx";
 | 
				
			||||||
import {DealService} from "../../../client";
 | 
					import { DealService } from "../../../client";
 | 
				
			||||||
import {useQueryClient} from "@tanstack/react-query";
 | 
					import { useQueryClient } from "@tanstack/react-query";
 | 
				
			||||||
import {dateWithoutTimezone} from "../../../shared/lib/date.ts";
 | 
					import { dateWithoutTimezone } from "../../../shared/lib/date.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
    onClick: () => void;
 | 
					    onClick: () => void;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
const CreateDealButton: FC<Props> = () => {
 | 
					const CreateDealButton: FC<Props> = () => {
 | 
				
			||||||
    const [isCreating, setIsCreating] = useState(false);
 | 
					    const [isCreating, setIsCreating] = useState(false);
 | 
				
			||||||
    const [isTransitionEnded, setIsTransitionEnded] = useState(true);
 | 
					    const [isTransitionEnded, setIsTransitionEnded] = useState(true);
 | 
				
			||||||
    const queryClient = useQueryClient();
 | 
					    const queryClient = useQueryClient();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <div className={styles['container']}
 | 
					        <div
 | 
				
			||||||
             onClick={() => {
 | 
					            className={styles["container"]}
 | 
				
			||||||
                 if (isCreating) return;
 | 
					            onClick={() => {
 | 
				
			||||||
                 setIsCreating(prevState => !prevState)
 | 
					                if (isCreating) return;
 | 
				
			||||||
                 setIsTransitionEnded(false);
 | 
					                setIsCreating(prevState => !prevState);
 | 
				
			||||||
             }}
 | 
					                setIsTransitionEnded(false);
 | 
				
			||||||
        >
 | 
					            }}>
 | 
				
			||||||
            {(!isCreating && isTransitionEnded) &&
 | 
					            {!isCreating && isTransitionEnded && (
 | 
				
			||||||
                <Text>Быстрое добавление</Text>
 | 
					                <Text>Быстрое добавление</Text>
 | 
				
			||||||
            }
 | 
					            )}
 | 
				
			||||||
            <Transition
 | 
					            <Transition
 | 
				
			||||||
                mounted={isCreating}
 | 
					                mounted={isCreating}
 | 
				
			||||||
                transition={"scale-y"}
 | 
					                transition={"scale-y"}
 | 
				
			||||||
                onExited={() => setIsTransitionEnded(true)}
 | 
					                onExited={() => setIsTransitionEnded(true)}>
 | 
				
			||||||
 | 
					                {styles => (
 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
                {(styles) => (
 | 
					 | 
				
			||||||
                    <div style={styles}>
 | 
					                    <div style={styles}>
 | 
				
			||||||
                        <CreateDealFrom
 | 
					                        <CreateDealFrom
 | 
				
			||||||
                            onCancel={() => {
 | 
					                            onCancel={() => {
 | 
				
			||||||
                                setIsCreating(false)
 | 
					                                setIsCreating(false);
 | 
				
			||||||
                            }}
 | 
					                            }}
 | 
				
			||||||
                            onSubmit={(quickDeal) => {
 | 
					                            onSubmit={quickDeal => {
 | 
				
			||||||
                                DealService.quickCreateDealQuickCreatePost({
 | 
					                                DealService.quickCreateDealQuickCreatePost({
 | 
				
			||||||
                                    requestBody: {
 | 
					                                    requestBody: {
 | 
				
			||||||
                                        ...quickDeal,
 | 
					                                        ...quickDeal,
 | 
				
			||||||
                                        acceptanceDate: dateWithoutTimezone(quickDeal.acceptanceDate)
 | 
					                                        acceptanceDate: dateWithoutTimezone(
 | 
				
			||||||
                                    }
 | 
					                                            quickDeal.acceptanceDate
 | 
				
			||||||
 | 
					                                        ),
 | 
				
			||||||
 | 
					                                    },
 | 
				
			||||||
                                }).then(async () => {
 | 
					                                }).then(async () => {
 | 
				
			||||||
                                    await queryClient.invalidateQueries({queryKey: ['getDealSummaries']});
 | 
					                                    await queryClient.invalidateQueries({
 | 
				
			||||||
 | 
					                                        queryKey: ["getDealSummaries"],
 | 
				
			||||||
 | 
					                                    });
 | 
				
			||||||
                                    setIsCreating(false);
 | 
					                                    setIsCreating(false);
 | 
				
			||||||
                                })
 | 
					                                });
 | 
				
			||||||
                            }}
 | 
					                            }}
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                )}
 | 
					                )}
 | 
				
			||||||
            </Transition>
 | 
					            </Transition>
 | 
				
			||||||
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
export default CreateDealButton;
 | 
					export default CreateDealButton;
 | 
				
			||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
.inputs {
 | 
					.inputs {
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    flex-direction: column;
 | 
					    flex-direction: column;
 | 
				
			||||||
    width: 100%
 | 
					    width: 100%;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.inputs * {
 | 
					.inputs * {
 | 
				
			||||||
@@ -12,4 +12,3 @@
 | 
				
			|||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    gap: rem(10);
 | 
					    gap: rem(10);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,15 +5,14 @@ import { useForm } from "@mantine/form";
 | 
				
			|||||||
import styles from "./CreateDealForm.module.css";
 | 
					import styles from "./CreateDealForm.module.css";
 | 
				
			||||||
import ClientAutocomplete from "../../Selects/ClientAutocomplete/ClientAutocomplete.tsx";
 | 
					import ClientAutocomplete from "../../Selects/ClientAutocomplete/ClientAutocomplete.tsx";
 | 
				
			||||||
import { DateTimePicker } from "@mantine/dates";
 | 
					import { DateTimePicker } from "@mantine/dates";
 | 
				
			||||||
import ShippingWarehouseAutocomplete
 | 
					import ShippingWarehouseAutocomplete from "../../Selects/ShippingWarehouseAutocomplete/ShippingWarehouseAutocomplete.tsx";
 | 
				
			||||||
    from "../../Selects/ShippingWarehouseAutocomplete/ShippingWarehouseAutocomplete.tsx";
 | 
					 | 
				
			||||||
import BaseMarketplaceSelect from "../../Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
 | 
					import BaseMarketplaceSelect from "../../Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
 | 
				
			||||||
import ServicePriceCategorySelect from "../../Selects/ServicePriceCategorySelect/ServicePriceCategorySelect.tsx";
 | 
					import ServicePriceCategorySelect from "../../Selects/ServicePriceCategorySelect/ServicePriceCategorySelect.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
    onSubmit: (quickDeal: QuickDeal) => void
 | 
					    onSubmit: (quickDeal: QuickDeal) => void;
 | 
				
			||||||
    onCancel: () => void;
 | 
					    onCancel: () => void;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
const CreateDealFrom: FC<Props> = ({ onSubmit, onCancel }) => {
 | 
					const CreateDealFrom: FC<Props> = ({ onSubmit, onCancel }) => {
 | 
				
			||||||
    const form = useForm<QuickDeal>({
 | 
					    const form = useForm<QuickDeal>({
 | 
				
			||||||
        initialValues: {
 | 
					        initialValues: {
 | 
				
			||||||
@@ -33,15 +32,15 @@ const CreateDealFrom: FC<Props> = ({ onSubmit, onCancel }) => {
 | 
				
			|||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <form
 | 
					        <form
 | 
				
			||||||
            style={{ width: "100%" }}
 | 
					            style={{ width: "100%" }}
 | 
				
			||||||
            onSubmit={form.onSubmit((values) => onSubmit(values))}
 | 
					            onSubmit={form.onSubmit(values => onSubmit(values))}>
 | 
				
			||||||
        >
 | 
					            <div
 | 
				
			||||||
            <div style={{
 | 
					                style={{
 | 
				
			||||||
                display: "flex",
 | 
					                    display: "flex",
 | 
				
			||||||
                flexDirection: "column",
 | 
					                    flexDirection: "column",
 | 
				
			||||||
                width: "100%",
 | 
					                    width: "100%",
 | 
				
			||||||
                gap: rem(10),
 | 
					                    gap: rem(10),
 | 
				
			||||||
                padding: rem(10),
 | 
					                    padding: rem(10),
 | 
				
			||||||
            }}>
 | 
					                }}>
 | 
				
			||||||
                <div className={styles["inputs"]}>
 | 
					                <div className={styles["inputs"]}>
 | 
				
			||||||
                    <TextInput
 | 
					                    <TextInput
 | 
				
			||||||
                        placeholder={"Название сделки"}
 | 
					                        placeholder={"Название сделки"}
 | 
				
			||||||
@@ -63,7 +62,6 @@ const CreateDealFrom: FC<Props> = ({ onSubmit, onCancel }) => {
 | 
				
			|||||||
                        {...form.getInputProps("shippingWarehouse")}
 | 
					                        {...form.getInputProps("shippingWarehouse")}
 | 
				
			||||||
                        placeholder={"Склад отгрузки"}
 | 
					                        placeholder={"Склад отгрузки"}
 | 
				
			||||||
                    />
 | 
					                    />
 | 
				
			||||||
 | 
					 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                <div className={styles["inputs"]}>
 | 
					                <div className={styles["inputs"]}>
 | 
				
			||||||
                    <ServicePriceCategorySelect
 | 
					                    <ServicePriceCategorySelect
 | 
				
			||||||
@@ -89,19 +87,16 @@ const CreateDealFrom: FC<Props> = ({ onSubmit, onCancel }) => {
 | 
				
			|||||||
                    />
 | 
					                    />
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
                <div className={styles["buttons"]}>
 | 
					                <div className={styles["buttons"]}>
 | 
				
			||||||
                    <Button
 | 
					                    <Button type={"submit"}>Добавить</Button>
 | 
				
			||||||
                        type={"submit"}
 | 
					 | 
				
			||||||
                    >Добавить</Button>
 | 
					 | 
				
			||||||
                    <Button
 | 
					                    <Button
 | 
				
			||||||
                        variant={"outline"}
 | 
					                        variant={"outline"}
 | 
				
			||||||
                        onClick={() => onCancel()}
 | 
					                        onClick={() => onCancel()}>
 | 
				
			||||||
                    >Отменить</Button>
 | 
					                        Отменить
 | 
				
			||||||
 | 
					                    </Button>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        </form>
 | 
					        </form>
 | 
				
			||||||
 | 
					 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,6 @@
 | 
				
			|||||||
    @mixin dark {
 | 
					    @mixin dark {
 | 
				
			||||||
        background-color: var(--mantine-color-dark-5);
 | 
					        background-color: var(--mantine-color-dark-5);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.flex-row-left {
 | 
					.flex-row-left {
 | 
				
			||||||
@@ -27,13 +26,11 @@
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.flex-row {
 | 
					.flex-row {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    flex-direction: column;
 | 
					    flex-direction: column;
 | 
				
			||||||
    justify-content: space-between;
 | 
					    justify-content: space-between;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.flex-row-right {
 | 
					.flex-row-right {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    align-items: flex-end;
 | 
					    align-items: flex-end;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,109 +1,147 @@
 | 
				
			|||||||
import {FC} from "react";
 | 
					import { FC } from "react";
 | 
				
			||||||
import {DealService, DealSummary} from "../../../client";
 | 
					import { DealService, DealSummary } from "../../../client";
 | 
				
			||||||
import styles from './DealSummaryCard.module.css';
 | 
					import styles from "./DealSummaryCard.module.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {ActionIcon, Badge, CopyButton, Flex, Image, Popover, rem, Text} from '@mantine/core';
 | 
					import {
 | 
				
			||||||
 | 
					    ActionIcon,
 | 
				
			||||||
 | 
					    Badge,
 | 
				
			||||||
 | 
					    CopyButton,
 | 
				
			||||||
 | 
					    Flex,
 | 
				
			||||||
 | 
					    Image,
 | 
				
			||||||
 | 
					    Popover,
 | 
				
			||||||
 | 
					    rem,
 | 
				
			||||||
 | 
					    Text,
 | 
				
			||||||
 | 
					} from "@mantine/core";
 | 
				
			||||||
import classNames from "classnames";
 | 
					import classNames from "classnames";
 | 
				
			||||||
import {useDealPageContext} from "../../../pages/LeadsPage/contexts/DealPageContext.tsx";
 | 
					import { useDealPageContext } from "../../../pages/LeadsPage/contexts/DealPageContext.tsx";
 | 
				
			||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
 | 
					import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 | 
				
			||||||
import {faCheck} from "@fortawesome/free-solid-svg-icons";
 | 
					import { faCheck } from "@fortawesome/free-solid-svg-icons";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
    dealSummary: DealSummary
 | 
					    dealSummary: DealSummary;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const DealSummaryCard: FC<Props> = ({dealSummary}) => {
 | 
					const DealSummaryCard: FC<Props> = ({ dealSummary }) => {
 | 
				
			||||||
    const {setSelectedDeal} = useDealPageContext();
 | 
					    const { setSelectedDeal } = useDealPageContext();
 | 
				
			||||||
    const onDealSummaryClick = () => {
 | 
					    const onDealSummaryClick = () => {
 | 
				
			||||||
        DealService.getDealById({dealId: dealSummary.id})
 | 
					        DealService.getDealById({ dealId: dealSummary.id }).then(deal => {
 | 
				
			||||||
            .then((deal) => {
 | 
					            setSelectedDeal(deal);
 | 
				
			||||||
                setSelectedDeal(deal);
 | 
					        });
 | 
				
			||||||
            })
 | 
					    };
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    const getDeadlineTextColor = (deadline: string) => {
 | 
					    const getDeadlineTextColor = (deadline: string) => {
 | 
				
			||||||
        // generate three colors, yellow for 1 day, red for 0 days, green for more than 1 day
 | 
					        // generate three colors, yellow for 1 day, red for 0 days, green for more than 1 day
 | 
				
			||||||
        const deadlineDate = new Date(deadline);
 | 
					        const deadlineDate = new Date(deadline);
 | 
				
			||||||
        const currentDate = new Date();
 | 
					        const currentDate = new Date();
 | 
				
			||||||
        const diff = deadlineDate.getTime() - currentDate.getTime();
 | 
					        const diff = deadlineDate.getTime() - currentDate.getTime();
 | 
				
			||||||
        const diffDays = Math.ceil(diff / (1000 * 3600 * 24));
 | 
					        const diffDays = Math.ceil(diff / (1000 * 3600 * 24));
 | 
				
			||||||
        if (diffDays < 0)
 | 
					        if (diffDays < 0) return "red.8"; // for past deadlines
 | 
				
			||||||
            return 'red.8'; // for past deadlines
 | 
					 | 
				
			||||||
        if (diffDays === 1) {
 | 
					        if (diffDays === 1) {
 | 
				
			||||||
            return 'yellow.8';
 | 
					            return "yellow.8";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (diffDays === 0) {
 | 
					        if (diffDays === 0) {
 | 
				
			||||||
            return 'orange.8';
 | 
					            return "orange.8";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return 'green.8';
 | 
					        return "green.8";
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <div onClick={() => onDealSummaryClick()} className={styles['container']}>
 | 
					        <div
 | 
				
			||||||
            <div className={classNames(styles['flex-row'], styles["flex-row-left"])}>
 | 
					            onClick={() => onDealSummaryClick()}
 | 
				
			||||||
 | 
					            className={styles["container"]}>
 | 
				
			||||||
                <div className={styles['flex-item']}>
 | 
					            <div
 | 
				
			||||||
                    <Text size={"sm"} c={"gray.6"}>
 | 
					                className={classNames(
 | 
				
			||||||
 | 
					                    styles["flex-row"],
 | 
				
			||||||
 | 
					                    styles["flex-row-left"]
 | 
				
			||||||
 | 
					                )}>
 | 
				
			||||||
 | 
					                <div className={styles["flex-item"]}>
 | 
				
			||||||
 | 
					                    <Text
 | 
				
			||||||
 | 
					                        size={"sm"}
 | 
				
			||||||
 | 
					                        c={"gray.6"}>
 | 
				
			||||||
                        Клиент: {dealSummary.clientName}
 | 
					                        Клиент: {dealSummary.clientName}
 | 
				
			||||||
                    </Text>
 | 
					                    </Text>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                <div className={styles['flex-item']}>
 | 
					                <div className={styles["flex-item"]}>
 | 
				
			||||||
                    <Text size={"md"} c={"blue.5"}>{dealSummary.name}</Text>
 | 
					                    <Text
 | 
				
			||||||
                    {dealSummary.shipmentWarehouseName &&
 | 
					                        size={"md"}
 | 
				
			||||||
                        <Text size={"sm"} c={"gray.6"}>{dealSummary.shipmentWarehouseName}</Text>
 | 
					                        c={"blue.5"}>
 | 
				
			||||||
                    }
 | 
					                        {dealSummary.name}
 | 
				
			||||||
 | 
					                    </Text>
 | 
				
			||||||
 | 
					                    {dealSummary.shipmentWarehouseName && (
 | 
				
			||||||
 | 
					                        <Text
 | 
				
			||||||
 | 
					                            size={"sm"}
 | 
				
			||||||
 | 
					                            c={"gray.6"}>
 | 
				
			||||||
 | 
					                            {dealSummary.shipmentWarehouseName}
 | 
				
			||||||
 | 
					                        </Text>
 | 
				
			||||||
 | 
					                    )}
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                <div className={styles['flex-item']}>
 | 
					                <div className={styles["flex-item"]}>
 | 
				
			||||||
                    <Text size={"sm"} c={"gray.6"}>
 | 
					                    <Text
 | 
				
			||||||
                        {dealSummary.totalPrice.toLocaleString('ru-RU')} руб, {dealSummary.totalProducts.toLocaleString("ru-RU")} тов.
 | 
					                        size={"sm"}
 | 
				
			||||||
 | 
					                        c={"gray.6"}>
 | 
				
			||||||
 | 
					                        {dealSummary.totalPrice.toLocaleString("ru-RU")} руб,{" "}
 | 
				
			||||||
 | 
					                        {dealSummary.totalProducts.toLocaleString("ru-RU")} тов.
 | 
				
			||||||
                    </Text>
 | 
					                    </Text>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div
 | 
					            <div
 | 
				
			||||||
 | 
					                className={classNames(
 | 
				
			||||||
                className={classNames(styles['flex-row'], styles['flex-row-right'])}>
 | 
					                    styles["flex-row"],
 | 
				
			||||||
                <div className={styles['flex-item']}>
 | 
					                    styles["flex-row-right"]
 | 
				
			||||||
 | 
					                )}>
 | 
				
			||||||
 | 
					                <div className={styles["flex-item"]}>
 | 
				
			||||||
                    <ActionIcon variant={"transparent"}>
 | 
					                    <ActionIcon variant={"transparent"}>
 | 
				
			||||||
                        <Image src={dealSummary.baseMarketplace?.iconUrl || ""}/>
 | 
					                        <Image
 | 
				
			||||||
 | 
					                            src={dealSummary.baseMarketplace?.iconUrl || ""}
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
                    </ActionIcon>
 | 
					                    </ActionIcon>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                <div className={styles['flex-item']}>
 | 
					                <div className={styles["flex-item"]}>
 | 
				
			||||||
                    <Text size={"sm"} c={getDeadlineTextColor(dealSummary.deadline)}>
 | 
					                    <Text
 | 
				
			||||||
                        {new Date(dealSummary.deadline).toLocaleString('ru-RU').slice(0, -3)}
 | 
					                        size={"sm"}
 | 
				
			||||||
 | 
					                        c={getDeadlineTextColor(dealSummary.deadline)}>
 | 
				
			||||||
 | 
					                        {new Date(dealSummary.deadline)
 | 
				
			||||||
 | 
					                            .toLocaleString("ru-RU")
 | 
				
			||||||
 | 
					                            .slice(0, -3)}
 | 
				
			||||||
                    </Text>
 | 
					                    </Text>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                <CopyButton value={"https://google.com"}>
 | 
					                <CopyButton value={"https://google.com"}>
 | 
				
			||||||
                    {({copy, copied}) => (
 | 
					                    {({ copy, copied }) => (
 | 
				
			||||||
                        <Popover opened={copied} withArrow>
 | 
					                        <Popover
 | 
				
			||||||
 | 
					                            opened={copied}
 | 
				
			||||||
 | 
					                            withArrow>
 | 
				
			||||||
                            <Popover.Target>
 | 
					                            <Popover.Target>
 | 
				
			||||||
                                <div
 | 
					                                <div
 | 
				
			||||||
 | 
					                                    onClick={e => {
 | 
				
			||||||
                                    onClick={(e) => {
 | 
					 | 
				
			||||||
                                        e.stopPropagation();
 | 
					                                        e.stopPropagation();
 | 
				
			||||||
                                        copy();
 | 
					                                        copy();
 | 
				
			||||||
                                    }}
 | 
					                                    }}
 | 
				
			||||||
                                    className={styles['flex-item']}>
 | 
					                                    className={styles["flex-item"]}>
 | 
				
			||||||
                                    <Badge variant={"light"} radius={"sm"}>
 | 
					                                    <Badge
 | 
				
			||||||
 | 
					                                        variant={"light"}
 | 
				
			||||||
 | 
					                                        radius={"sm"}>
 | 
				
			||||||
                                        ID: {dealSummary.id}
 | 
					                                        ID: {dealSummary.id}
 | 
				
			||||||
                                    </Badge>
 | 
					                                    </Badge>
 | 
				
			||||||
                                </div>
 | 
					                                </div>
 | 
				
			||||||
                            </Popover.Target>
 | 
					                            </Popover.Target>
 | 
				
			||||||
                            <Popover.Dropdown>
 | 
					                            <Popover.Dropdown>
 | 
				
			||||||
                                <Flex justify={"center"} align={"center"} gap={rem(5)}>
 | 
					                                <Flex
 | 
				
			||||||
 | 
					                                    justify={"center"}
 | 
				
			||||||
 | 
					                                    align={"center"}
 | 
				
			||||||
 | 
					                                    gap={rem(5)}>
 | 
				
			||||||
                                    <FontAwesomeIcon
 | 
					                                    <FontAwesomeIcon
 | 
				
			||||||
                                        bounce
 | 
					                                        bounce
 | 
				
			||||||
                                        style={{animationIterationCount: 1}}
 | 
					                                        style={{ animationIterationCount: 1 }}
 | 
				
			||||||
                                        icon={faCheck}
 | 
					                                        icon={faCheck}
 | 
				
			||||||
                                    />
 | 
					                                    />
 | 
				
			||||||
                                    <Text size={"xs"}>ID сделки скопирован</Text>
 | 
					                                    <Text size={"xs"}>
 | 
				
			||||||
 | 
					                                        ID сделки скопирован
 | 
				
			||||||
 | 
					                                    </Text>
 | 
				
			||||||
                                </Flex>
 | 
					                                </Flex>
 | 
				
			||||||
 | 
					 | 
				
			||||||
                            </Popover.Dropdown>
 | 
					                            </Popover.Dropdown>
 | 
				
			||||||
                        </Popover>
 | 
					                        </Popover>
 | 
				
			||||||
                    )}
 | 
					                    )}
 | 
				
			||||||
                </CopyButton>
 | 
					                </CopyButton>
 | 
				
			||||||
 | 
					 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
export default DealSummaryCard;
 | 
					export default DealSummaryCard;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,9 +2,13 @@
 | 
				
			|||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    align-items: center;
 | 
					    align-items: center;
 | 
				
			||||||
    border-radius: var(--mantine-radius-md);
 | 
					    border-radius: var(--mantine-radius-md);
 | 
				
			||||||
    border: rem(1px) solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));
 | 
					    border: rem(1px) solid
 | 
				
			||||||
 | 
					        light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));
 | 
				
			||||||
    padding: var(--mantine-spacing-sm) var(--mantine-spacing-xl);
 | 
					    padding: var(--mantine-spacing-sm) var(--mantine-spacing-xl);
 | 
				
			||||||
    background-color: light-dark(var(--mantine-color-white), var(--mantine-color-dark-5));
 | 
					    background-color: light-dark(
 | 
				
			||||||
 | 
					        var(--mantine-color-white),
 | 
				
			||||||
 | 
					        var(--mantine-color-dark-5)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
    margin-bottom: var(--mantine-spacing-sm);
 | 
					    margin-bottom: var(--mantine-spacing-sm);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,26 +1,25 @@
 | 
				
			|||||||
import styles from './Header.module.css';
 | 
					import styles from "./Header.module.css";
 | 
				
			||||||
import {Button, TextInput} from "@mantine/core";
 | 
					import { Button, TextInput } from "@mantine/core";
 | 
				
			||||||
import {FC} from "react";
 | 
					import { FC } from "react";
 | 
				
			||||||
 | 
					 | 
				
			||||||
const Header: FC = ()=>{
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Header: FC = () => {
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <div className={styles['header']}>
 | 
					        <div className={styles["header"]}>
 | 
				
			||||||
 | 
					 | 
				
			||||||
            <TextInput
 | 
					            <TextInput
 | 
				
			||||||
                radius={0}
 | 
					                radius={0}
 | 
				
			||||||
                placeholder={"Поиск и фильтры"}
 | 
					                placeholder={"Поиск и фильтры"}
 | 
				
			||||||
                size={"xl"}
 | 
					                size={"xl"}
 | 
				
			||||||
                className={styles['header-input']}
 | 
					                className={styles["header-input"]}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
            <Button
 | 
					            <Button
 | 
				
			||||||
                radius={0}
 | 
					                radius={0}
 | 
				
			||||||
                color={"gray"}
 | 
					                color={"gray"}
 | 
				
			||||||
                variant={'default'}
 | 
					                variant={"default"}
 | 
				
			||||||
                className={styles['header-button']}
 | 
					                className={styles["header-button"]}>
 | 
				
			||||||
            >Поиск</Button>
 | 
					                Поиск
 | 
				
			||||||
 | 
					            </Button>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Header;
 | 
					export default Header;
 | 
				
			||||||
@@ -1,31 +1,45 @@
 | 
				
			|||||||
import {Dropzone, DropzoneProps, FileWithPath} from "@mantine/dropzone";
 | 
					import { Dropzone, DropzoneProps, FileWithPath } from "@mantine/dropzone";
 | 
				
			||||||
import {FC, useState} from "react";
 | 
					import { FC, useState } from "react";
 | 
				
			||||||
import {Button, Fieldset, Flex, Group, Image, Loader, rem, Text} from "@mantine/core";
 | 
					import {
 | 
				
			||||||
import {IconPhoto, IconUpload, IconX} from "@tabler/icons-react";
 | 
					    Button,
 | 
				
			||||||
import {omit} from "lodash";
 | 
					    Fieldset,
 | 
				
			||||||
import {BaseFormInputProps} from "../../types/utils.ts";
 | 
					    Flex,
 | 
				
			||||||
import {notifications} from "../../shared/lib/notifications.ts";
 | 
					    Group,
 | 
				
			||||||
import {ProductService} from "../../client";
 | 
					    Image,
 | 
				
			||||||
 | 
					    Loader,
 | 
				
			||||||
 | 
					    rem,
 | 
				
			||||||
 | 
					    Text,
 | 
				
			||||||
 | 
					} from "@mantine/core";
 | 
				
			||||||
 | 
					import { IconPhoto, IconUpload, IconX } from "@tabler/icons-react";
 | 
				
			||||||
 | 
					import { omit } from "lodash";
 | 
				
			||||||
 | 
					import { BaseFormInputProps } from "../../types/utils.ts";
 | 
				
			||||||
 | 
					import { notifications } from "../../shared/lib/notifications.ts";
 | 
				
			||||||
 | 
					import { ProductService } from "../../client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface RestProps {
 | 
					interface RestProps {
 | 
				
			||||||
    imageUrlInputProps?: BaseFormInputProps<string>;
 | 
					    imageUrlInputProps?: BaseFormInputProps<string>;
 | 
				
			||||||
    productId?: number;
 | 
					    productId?: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = Omit<DropzoneProps, "onDrop"> & RestProps;
 | 
				
			||||||
type Props = Omit<DropzoneProps, 'onDrop'> & RestProps;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ImageDropzone: FC<Props> = (props: Props) => {
 | 
					const ImageDropzone: FC<Props> = (props: Props) => {
 | 
				
			||||||
    const [showDropzone, setShowDropzone] = useState(
 | 
					    const [showDropzone, setShowDropzone] = useState(
 | 
				
			||||||
        !(typeof props.imageUrlInputProps?.value === 'string' &&
 | 
					        !(
 | 
				
			||||||
            props.imageUrlInputProps.value.trim() !== '')
 | 
					            typeof props.imageUrlInputProps?.value === "string" &&
 | 
				
			||||||
 | 
					            props.imageUrlInputProps.value.trim() !== ""
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    const [isLoading, setIsLoading] = useState(false);
 | 
					    const [isLoading, setIsLoading] = useState(false);
 | 
				
			||||||
    const restProps = omit(props, ['imageUrl', 'productId', 'imageUrlInputProps']);
 | 
					    const restProps = omit(props, [
 | 
				
			||||||
 | 
					        "imageUrl",
 | 
				
			||||||
 | 
					        "productId",
 | 
				
			||||||
 | 
					        "imageUrlInputProps",
 | 
				
			||||||
 | 
					    ]);
 | 
				
			||||||
    const onDrop = (files: FileWithPath[]) => {
 | 
					    const onDrop = (files: FileWithPath[]) => {
 | 
				
			||||||
        if (!props.productId || !props.imageUrlInputProps) return;
 | 
					        if (!props.productId || !props.imageUrlInputProps) return;
 | 
				
			||||||
        if (files.length > 1) {
 | 
					        if (files.length > 1) {
 | 
				
			||||||
            notifications.error({message: "Прикрепите одно изображение"});
 | 
					            notifications.error({ message: "Прикрепите одно изображение" });
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const file = files[0];
 | 
					        const file = files[0];
 | 
				
			||||||
@@ -34,33 +48,35 @@ const ImageDropzone: FC<Props> = (props: Props) => {
 | 
				
			|||||||
        ProductService.uploadProductImage({
 | 
					        ProductService.uploadProductImage({
 | 
				
			||||||
            productId: props.productId,
 | 
					            productId: props.productId,
 | 
				
			||||||
            formData: {
 | 
					            formData: {
 | 
				
			||||||
                upload_file: file
 | 
					                upload_file: file,
 | 
				
			||||||
            }
 | 
					            },
 | 
				
			||||||
        }).then(({ok, message, imageUrl}) => {
 | 
					        })
 | 
				
			||||||
            notifications.guess(ok, {message});
 | 
					            .then(({ ok, message, imageUrl }) => {
 | 
				
			||||||
            setIsLoading(false);
 | 
					                notifications.guess(ok, { message });
 | 
				
			||||||
 | 
					                setIsLoading(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!ok || !imageUrl) {
 | 
					                if (!ok || !imageUrl) {
 | 
				
			||||||
 | 
					                    setShowDropzone(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                props.imageUrlInputProps?.onChange(imageUrl);
 | 
				
			||||||
 | 
					                setShowDropzone(false);
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .catch(error => {
 | 
				
			||||||
 | 
					                notifications.error({ message: error.toString() });
 | 
				
			||||||
                setShowDropzone(true);
 | 
					                setShowDropzone(true);
 | 
				
			||||||
 | 
					                setIsLoading(false);
 | 
				
			||||||
                return;
 | 
					            });
 | 
				
			||||||
            }
 | 
					    };
 | 
				
			||||||
            props.imageUrlInputProps?.onChange(imageUrl);
 | 
					 | 
				
			||||||
            setShowDropzone(false);
 | 
					 | 
				
			||||||
        }).catch(error => {
 | 
					 | 
				
			||||||
            notifications.error({message: error.toString()});
 | 
					 | 
				
			||||||
            setShowDropzone(true);
 | 
					 | 
				
			||||||
            setIsLoading(false);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    const getBody = () => {
 | 
					    const getBody = () => {
 | 
				
			||||||
        return props.imageUrlInputProps?.value && !showDropzone ? (
 | 
					        return props.imageUrlInputProps?.value && !showDropzone ? (
 | 
				
			||||||
            <Image src={props.imageUrlInputProps.value}/>
 | 
					            <Image src={props.imageUrlInputProps.value} />
 | 
				
			||||||
        ) : (
 | 
					        ) : (
 | 
				
			||||||
 | 
					 | 
				
			||||||
            <Dropzone
 | 
					            <Dropzone
 | 
				
			||||||
                {...restProps}
 | 
					                {...restProps}
 | 
				
			||||||
                accept={["image/png",
 | 
					                accept={[
 | 
				
			||||||
 | 
					                    "image/png",
 | 
				
			||||||
                    "image/jpeg",
 | 
					                    "image/jpeg",
 | 
				
			||||||
                    "image/gif",
 | 
					                    "image/gif",
 | 
				
			||||||
                    "image/bmp",
 | 
					                    "image/bmp",
 | 
				
			||||||
@@ -68,58 +84,74 @@ const ImageDropzone: FC<Props> = (props: Props) => {
 | 
				
			|||||||
                    "image/x-icon",
 | 
					                    "image/x-icon",
 | 
				
			||||||
                    "image/webp",
 | 
					                    "image/webp",
 | 
				
			||||||
                    "image/svg+xml",
 | 
					                    "image/svg+xml",
 | 
				
			||||||
                    "image/heic"]}
 | 
					                    "image/heic",
 | 
				
			||||||
 | 
					                ]}
 | 
				
			||||||
                multiple={false}
 | 
					                multiple={false}
 | 
				
			||||||
                onDrop={onDrop}
 | 
					                onDrop={onDrop}>
 | 
				
			||||||
 | 
					                <Group
 | 
				
			||||||
            >
 | 
					                    justify="center"
 | 
				
			||||||
                <Group justify="center" gap="xl" style={{pointerEvents: 'none'}}>
 | 
					                    gap="xl"
 | 
				
			||||||
 | 
					                    style={{ pointerEvents: "none" }}>
 | 
				
			||||||
                    <Dropzone.Accept>
 | 
					                    <Dropzone.Accept>
 | 
				
			||||||
                        <IconUpload
 | 
					                        <IconUpload
 | 
				
			||||||
                            style={{width: rem(52), height: rem(52), color: 'var(--mantine-color-blue-6)'}}
 | 
					                            style={{
 | 
				
			||||||
 | 
					                                width: rem(52),
 | 
				
			||||||
 | 
					                                height: rem(52),
 | 
				
			||||||
 | 
					                                color: "var(--mantine-color-blue-6)",
 | 
				
			||||||
 | 
					                            }}
 | 
				
			||||||
                            stroke={1.5}
 | 
					                            stroke={1.5}
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
                    </Dropzone.Accept>
 | 
					                    </Dropzone.Accept>
 | 
				
			||||||
                    <Dropzone.Reject>
 | 
					                    <Dropzone.Reject>
 | 
				
			||||||
                        <IconX
 | 
					                        <IconX
 | 
				
			||||||
                            style={{width: rem(52), height: rem(52), color: 'var(--mantine-color-red-6)'}}
 | 
					                            style={{
 | 
				
			||||||
 | 
					                                width: rem(52),
 | 
				
			||||||
 | 
					                                height: rem(52),
 | 
				
			||||||
 | 
					                                color: "var(--mantine-color-red-6)",
 | 
				
			||||||
 | 
					                            }}
 | 
				
			||||||
                            stroke={1.5}
 | 
					                            stroke={1.5}
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
                    </Dropzone.Reject>
 | 
					                    </Dropzone.Reject>
 | 
				
			||||||
                    <Dropzone.Idle>
 | 
					                    <Dropzone.Idle>
 | 
				
			||||||
                        <IconPhoto
 | 
					                        <IconPhoto
 | 
				
			||||||
                            style={{width: rem(52), height: rem(52), color: 'var(--mantine-color-dimmed)'}}
 | 
					                            style={{
 | 
				
			||||||
 | 
					                                width: rem(52),
 | 
				
			||||||
 | 
					                                height: rem(52),
 | 
				
			||||||
 | 
					                                color: "var(--mantine-color-dimmed)",
 | 
				
			||||||
 | 
					                            }}
 | 
				
			||||||
                            stroke={1.5}
 | 
					                            stroke={1.5}
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
                    </Dropzone.Idle>
 | 
					                    </Dropzone.Idle>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <div style={{textAlign: "center"}}>
 | 
					                    <div style={{ textAlign: "center" }}>
 | 
				
			||||||
                        <Text size="xl" inline>
 | 
					                        <Text
 | 
				
			||||||
 | 
					                            size="xl"
 | 
				
			||||||
 | 
					                            inline>
 | 
				
			||||||
                            Перенесите изображение или нажмите чтоб выбрать файл
 | 
					                            Перенесите изображение или нажмите чтоб выбрать файл
 | 
				
			||||||
                        </Text>
 | 
					                        </Text>
 | 
				
			||||||
 | 
					 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </Group>
 | 
					                </Group>
 | 
				
			||||||
            </Dropzone>
 | 
					            </Dropzone>
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Flex
 | 
					        <Flex
 | 
				
			||||||
            gap={rem(10)}
 | 
					            gap={rem(10)}
 | 
				
			||||||
            direction={"column"}
 | 
					            direction={"column"}>
 | 
				
			||||||
 | 
					            <Fieldset legend={"Изображение"}>
 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
            <Fieldset
 | 
					 | 
				
			||||||
                legend={'Изображение'}>
 | 
					 | 
				
			||||||
                <Flex justify={"center"}>
 | 
					                <Flex justify={"center"}>
 | 
				
			||||||
                    {isLoading ? <Loader/> : getBody()}
 | 
					                    {isLoading ? <Loader /> : getBody()}
 | 
				
			||||||
                </Flex>
 | 
					                </Flex>
 | 
				
			||||||
            </Fieldset>
 | 
					            </Fieldset>
 | 
				
			||||||
            {!showDropzone &&
 | 
					            {!showDropzone && (
 | 
				
			||||||
                <Button onClick={() => setShowDropzone(true)} variant={"default"}>Заменить изображение {}</Button>
 | 
					                <Button
 | 
				
			||||||
            }
 | 
					                    onClick={() => setShowDropzone(true)}
 | 
				
			||||||
 | 
					                    variant={"default"}>
 | 
				
			||||||
 | 
					                    Заменить изображение {}
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					            )}
 | 
				
			||||||
        </Flex>
 | 
					        </Flex>
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default ImageDropzone;
 | 
					export default ImageDropzone;
 | 
				
			||||||
@@ -1,34 +1,47 @@
 | 
				
			|||||||
.control {
 | 
					.control {
 | 
				
			||||||
  font-weight: 500;
 | 
					    font-weight: 500;
 | 
				
			||||||
  display: block;
 | 
					    display: block;
 | 
				
			||||||
  width: 100%;
 | 
					    width: 100%;
 | 
				
			||||||
  padding: var(--mantine-spacing-xs) var(--mantine-spacing-md);
 | 
					    padding: var(--mantine-spacing-xs) var(--mantine-spacing-md);
 | 
				
			||||||
  color: var(--mantine-color-text);
 | 
					    color: var(--mantine-color-text);
 | 
				
			||||||
  font-size: var(--mantine-font-size-sm);
 | 
					    font-size: var(--mantine-font-size-sm);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @mixin hover {
 | 
					    @mixin hover {
 | 
				
			||||||
    background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-7));
 | 
					        background-color: light-dark(
 | 
				
			||||||
    color: light-dark(var(--mantine-color-black), var(--mantine-color-dark-0));
 | 
					            var(--mantine-color-gray-0),
 | 
				
			||||||
  }
 | 
					            var(--mantine-color-dark-7)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        color: light-dark(
 | 
				
			||||||
 | 
					            var(--mantine-color-black),
 | 
				
			||||||
 | 
					            var(--mantine-color-dark-0)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.link {
 | 
					.link {
 | 
				
			||||||
  font-weight: 500;
 | 
					    font-weight: 500;
 | 
				
			||||||
  display: block;
 | 
					    display: block;
 | 
				
			||||||
  text-decoration: none;
 | 
					    text-decoration: none;
 | 
				
			||||||
  padding: var(--mantine-spacing-xs) var(--mantine-spacing-md);
 | 
					    padding: var(--mantine-spacing-xs) var(--mantine-spacing-md);
 | 
				
			||||||
  padding-left: var(--mantine-spacing-md);
 | 
					    padding-left: var(--mantine-spacing-md);
 | 
				
			||||||
  margin-left: var(--mantine-spacing-xl);
 | 
					    margin-left: var(--mantine-spacing-xl);
 | 
				
			||||||
  font-size: var(--mantine-font-size-sm);
 | 
					    font-size: var(--mantine-font-size-sm);
 | 
				
			||||||
  color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0));
 | 
					    color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0));
 | 
				
			||||||
  border-left: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
 | 
					    border-left: 1px solid
 | 
				
			||||||
 | 
					        light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @mixin hover {
 | 
					    @mixin hover {
 | 
				
			||||||
    background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-7));
 | 
					        background-color: light-dark(
 | 
				
			||||||
    color: light-dark(var(--mantine-color-black), var(--mantine-color-dark-0));
 | 
					            var(--mantine-color-gray-0),
 | 
				
			||||||
  }
 | 
					            var(--mantine-color-dark-7)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        color: light-dark(
 | 
				
			||||||
 | 
					            var(--mantine-color-black),
 | 
				
			||||||
 | 
					            var(--mantine-color-dark-0)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.chevron {
 | 
					.chevron {
 | 
				
			||||||
  transition: transform 200ms ease;
 | 
					    transition: transform 200ms ease;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,8 +1,15 @@
 | 
				
			|||||||
import {useState} from 'react';
 | 
					import { useState } from "react";
 | 
				
			||||||
import {Box, Collapse, Group, rem, ThemeIcon, UnstyledButton} from '@mantine/core';
 | 
					import {
 | 
				
			||||||
import {IconCalendarStats, IconChevronRight} from '@tabler/icons-react';
 | 
					    Box,
 | 
				
			||||||
import classes from './LinksGroup.module.css';
 | 
					    Collapse,
 | 
				
			||||||
import {Link} from "@tanstack/react-router";
 | 
					    Group,
 | 
				
			||||||
 | 
					    rem,
 | 
				
			||||||
 | 
					    ThemeIcon,
 | 
				
			||||||
 | 
					    UnstyledButton,
 | 
				
			||||||
 | 
					} from "@mantine/core";
 | 
				
			||||||
 | 
					import { IconCalendarStats, IconChevronRight } from "@tabler/icons-react";
 | 
				
			||||||
 | 
					import classes from "./LinksGroup.module.css";
 | 
				
			||||||
 | 
					import { Link } from "@tanstack/react-router";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface LinksGroupProps {
 | 
					interface LinksGroupProps {
 | 
				
			||||||
    icon: React.FC<any>;
 | 
					    icon: React.FC<any>;
 | 
				
			||||||
@@ -11,28 +18,36 @@ interface LinksGroupProps {
 | 
				
			|||||||
    links?: { label: string; link: string }[];
 | 
					    links?: { label: string; link: string }[];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function LinksGroup({icon: Icon, label, initiallyOpened, links}: LinksGroupProps) {
 | 
					export function LinksGroup({
 | 
				
			||||||
 | 
					    icon: Icon,
 | 
				
			||||||
 | 
					    label,
 | 
				
			||||||
 | 
					    initiallyOpened,
 | 
				
			||||||
 | 
					    links,
 | 
				
			||||||
 | 
					}: LinksGroupProps) {
 | 
				
			||||||
    const hasLinks = Array.isArray(links);
 | 
					    const hasLinks = Array.isArray(links);
 | 
				
			||||||
    const [opened, setOpened] = useState(initiallyOpened || false);
 | 
					    const [opened, setOpened] = useState(initiallyOpened || false);
 | 
				
			||||||
    const items = (hasLinks ? links : []).map((link) => (
 | 
					    const items = (hasLinks ? links : []).map(link => (
 | 
				
			||||||
        <Link to={link.link}
 | 
					        <Link
 | 
				
			||||||
              className={classes.link}
 | 
					            to={link.link}
 | 
				
			||||||
              key={link.label}
 | 
					            className={classes.link}
 | 
				
			||||||
 | 
					            key={link.label}>
 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            {link.label}
 | 
					            {link.label}
 | 
				
			||||||
        </Link>
 | 
					        </Link>
 | 
				
			||||||
 | 
					 | 
				
			||||||
    ));
 | 
					    ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <>
 | 
					        <>
 | 
				
			||||||
            <UnstyledButton onClick={() => setOpened((o) => !o)} className={classes.control}>
 | 
					            <UnstyledButton
 | 
				
			||||||
                <Group justify="space-between" gap={0}>
 | 
					                onClick={() => setOpened(o => !o)}
 | 
				
			||||||
                    <Box style={{display: 'flex', alignItems: 'center'}}>
 | 
					                className={classes.control}>
 | 
				
			||||||
                        <ThemeIcon variant="light" size={30}>
 | 
					                <Group
 | 
				
			||||||
                            <Icon style={{width: rem(18), height: rem(18)}}/>
 | 
					                    justify="space-between"
 | 
				
			||||||
 | 
					                    gap={0}>
 | 
				
			||||||
 | 
					                    <Box style={{ display: "flex", alignItems: "center" }}>
 | 
				
			||||||
 | 
					                        <ThemeIcon
 | 
				
			||||||
 | 
					                            variant="light"
 | 
				
			||||||
 | 
					                            size={30}>
 | 
				
			||||||
 | 
					                            <Icon style={{ width: rem(18), height: rem(18) }} />
 | 
				
			||||||
                        </ThemeIcon>
 | 
					                        </ThemeIcon>
 | 
				
			||||||
                        <Box ml="md">{label}</Box>
 | 
					                        <Box ml="md">{label}</Box>
 | 
				
			||||||
                    </Box>
 | 
					                    </Box>
 | 
				
			||||||
@@ -43,7 +58,7 @@ export function LinksGroup({icon: Icon, label, initiallyOpened, links}: LinksGro
 | 
				
			|||||||
                            style={{
 | 
					                            style={{
 | 
				
			||||||
                                width: rem(16),
 | 
					                                width: rem(16),
 | 
				
			||||||
                                height: rem(16),
 | 
					                                height: rem(16),
 | 
				
			||||||
                                transform: opened ? 'rotate(-90deg)' : 'none',
 | 
					                                transform: opened ? "rotate(-90deg)" : "none",
 | 
				
			||||||
                            }}
 | 
					                            }}
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
                    )}
 | 
					                    )}
 | 
				
			||||||
@@ -55,18 +70,20 @@ export function LinksGroup({icon: Icon, label, initiallyOpened, links}: LinksGro
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const mockdata = {
 | 
					const mockdata = {
 | 
				
			||||||
    label: 'Releases',
 | 
					    label: "Releases",
 | 
				
			||||||
    icon: IconCalendarStats,
 | 
					    icon: IconCalendarStats,
 | 
				
			||||||
    links: [
 | 
					    links: [
 | 
				
			||||||
        {label: 'Upcoming releases', link: '/'},
 | 
					        { label: "Upcoming releases", link: "/" },
 | 
				
			||||||
        {label: 'Previous releases', link: '/'},
 | 
					        { label: "Previous releases", link: "/" },
 | 
				
			||||||
        {label: 'Releases schedule', link: '/'},
 | 
					        { label: "Releases schedule", link: "/" },
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function NavbarLinksGroup() {
 | 
					export function NavbarLinksGroup() {
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Box mih={220} p="md">
 | 
					        <Box
 | 
				
			||||||
 | 
					            mih={220}
 | 
				
			||||||
 | 
					            p="md">
 | 
				
			||||||
            <LinksGroup {...mockdata} />
 | 
					            <LinksGroup {...mockdata} />
 | 
				
			||||||
        </Box>
 | 
					        </Box>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,17 @@
 | 
				
			|||||||
export function Logo(props: React.ComponentPropsWithoutRef<'svg'>) {
 | 
					export function Logo(props: React.ComponentPropsWithoutRef<"svg">) {
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <svg  {...props} version="1.0" xmlns="http://www.w3.org/2000/svg"
 | 
					        <svg
 | 
				
			||||||
             viewBox="0 0 1024.000000 1024.000000"
 | 
					            {...props}
 | 
				
			||||||
              preserveAspectRatio="xMidYMid meet">
 | 
					            version="1.0"
 | 
				
			||||||
 | 
					            xmlns="http://www.w3.org/2000/svg"
 | 
				
			||||||
            <g transform="translate(0.000000,1024.000000) scale(0.100000,-0.100000)"
 | 
					            viewBox="0 0 1024.000000 1024.000000"
 | 
				
			||||||
               fill="#2a75ec" stroke="none">
 | 
					            preserveAspectRatio="xMidYMid meet">
 | 
				
			||||||
                <path d="M2600 7290 l0 -760 690 0 690 0 0 196 0 195 728 -4 c613 -3 743 -7
 | 
					            <g
 | 
				
			||||||
 | 
					                transform="translate(0.000000,1024.000000) scale(0.100000,-0.100000)"
 | 
				
			||||||
 | 
					                fill="#2a75ec"
 | 
				
			||||||
 | 
					                stroke="none">
 | 
				
			||||||
 | 
					                <path
 | 
				
			||||||
 | 
					                    d="M2600 7290 l0 -760 690 0 690 0 0 196 0 195 728 -4 c613 -3 743 -7
 | 
				
			||||||
    831 -20 57 -10 106 -15 108 -13 7 7 341 -80 358 -93 8 -7 79 -34 95 -36 28 -4
 | 
					    831 -20 57 -10 106 -15 108 -13 7 7 341 -80 358 -93 8 -7 79 -34 95 -36 28 -4
 | 
				
			||||||
    293 -151 298 -166 2 -5 8 -9 13 -9 9 0 75 -51 177 -138 44 -38 120 -123 218
 | 
					    293 -151 298 -166 2 -5 8 -9 13 -9 9 0 75 -51 177 -138 44 -38 120 -123 218
 | 
				
			||||||
    -245 73 -91 215 -390 230 -484 3 -21 8 -42 11 -46 20 -33 46 -190 58 -355 12
 | 
					    -245 73 -91 215 -390 230 -484 3 -21 8 -42 11 -46 20 -33 46 -190 58 -355 12
 | 
				
			||||||
@@ -27,11 +32,13 @@ export function Logo(props: React.ComponentPropsWithoutRef<'svg'>) {
 | 
				
			|||||||
    -40 57 -103 155 -5 9 -15 20 -21 26 -6 5 -40 48 -76 95 -127 165 -291 325
 | 
					    -40 57 -103 155 -5 9 -15 20 -21 26 -6 5 -40 48 -76 95 -127 165 -291 325
 | 
				
			||||||
    -473 459 -38 29 -83 62 -98 74 -16 12 -40 29 -55 37 -15 8 -67 39 -115 69 -99
 | 
					    -473 459 -38 29 -83 62 -98 74 -16 12 -40 29 -55 37 -15 8 -67 39 -115 69 -99
 | 
				
			||||||
    59 -339 177 -443 216 -245 93 -524 167 -774 205 -335 51 -306 50 -1877 50
 | 
					    59 -339 177 -443 216 -245 93 -524 167 -774 205 -335 51 -306 50 -1877 50
 | 
				
			||||||
    l-1473 0 0 -760z"/>
 | 
					    l-1473 0 0 -760z"
 | 
				
			||||||
                <path d="M1730 5175 l0 -1095 1580 0 1580 0 0 1095 0 1095 -1580 0 -1580 0 0
 | 
					                />
 | 
				
			||||||
        -1095z"/>
 | 
					                <path
 | 
				
			||||||
 | 
					                    d="M1730 5175 l0 -1095 1580 0 1580 0 0 1095 0 1095 -1580 0 -1580 0 0
 | 
				
			||||||
 | 
					        -1095z"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
            </g>
 | 
					            </g>
 | 
				
			||||||
        </svg>
 | 
					        </svg>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -6,11 +6,15 @@
 | 
				
			|||||||
    align-items: center;
 | 
					    align-items: center;
 | 
				
			||||||
    @mixin dark {
 | 
					    @mixin dark {
 | 
				
			||||||
        background-color: var(--mantine-color-body);
 | 
					        background-color: var(--mantine-color-body);
 | 
				
			||||||
        box-shadow: 0 2px 4px var(--mantine-color-dark-7), 0 4px 24px var(--mantine-color-dark-7);
 | 
					        box-shadow:
 | 
				
			||||||
 | 
					            0 2px 4px var(--mantine-color-dark-7),
 | 
				
			||||||
 | 
					            0 4px 24px var(--mantine-color-dark-7);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    @mixin light {
 | 
					    @mixin light {
 | 
				
			||||||
        background-color: #f9f9f9;
 | 
					        background-color: #f9f9f9;
 | 
				
			||||||
        box-shadow: 0 2px 4px rgba(0, 0, 0, .08), 0 4px 24px rgba(0, 0, 0, .08);
 | 
					        box-shadow:
 | 
				
			||||||
 | 
					            0 2px 4px rgba(0, 0, 0, 0.08),
 | 
				
			||||||
 | 
					            0 4px 24px rgba(0, 0, 0, 0.08);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    border-radius: rem(20);
 | 
					    border-radius: rem(20);
 | 
				
			||||||
    width: 100%;
 | 
					    width: 100%;
 | 
				
			||||||
@@ -32,7 +36,10 @@
 | 
				
			|||||||
    color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0));
 | 
					    color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    &:hover {
 | 
					    &:hover {
 | 
				
			||||||
        background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-5));
 | 
					        background-color: light-dark(
 | 
				
			||||||
 | 
					            var(--mantine-color-gray-0),
 | 
				
			||||||
 | 
					            var(--mantine-color-dark-5)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    &[data-active] {
 | 
					    &[data-active] {
 | 
				
			||||||
@@ -43,4 +50,3 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,20 +1,31 @@
 | 
				
			|||||||
import {Center, Flex, Image, rem, Stack, Tooltip, UnstyledButton, useMantineColorScheme} from '@mantine/core';
 | 
					import {
 | 
				
			||||||
 | 
					    Center,
 | 
				
			||||||
 | 
					    Flex,
 | 
				
			||||||
 | 
					    Image,
 | 
				
			||||||
 | 
					    rem,
 | 
				
			||||||
 | 
					    Stack,
 | 
				
			||||||
 | 
					    Tooltip,
 | 
				
			||||||
 | 
					    UnstyledButton,
 | 
				
			||||||
 | 
					    useMantineColorScheme,
 | 
				
			||||||
 | 
					} from "@mantine/core";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    IconBarcode,
 | 
					    IconBarcode,
 | 
				
			||||||
    IconBox, IconBuildingWarehouse,
 | 
					    IconBox,
 | 
				
			||||||
 | 
					    IconBuildingWarehouse,
 | 
				
			||||||
    IconCash,
 | 
					    IconCash,
 | 
				
			||||||
    IconDashboard,
 | 
					    IconDashboard,
 | 
				
			||||||
    IconFileBarcode,
 | 
					    IconFileBarcode,
 | 
				
			||||||
    IconHome2,
 | 
					    IconHome2,
 | 
				
			||||||
    IconLogout,
 | 
					    IconLogout,
 | 
				
			||||||
    IconMan,
 | 
					    IconMan,
 | 
				
			||||||
    IconMoon, IconShoppingCart,
 | 
					    IconMoon,
 | 
				
			||||||
 | 
					    IconShoppingCart,
 | 
				
			||||||
    IconSun,
 | 
					    IconSun,
 | 
				
			||||||
} from '@tabler/icons-react';
 | 
					} from "@tabler/icons-react";
 | 
				
			||||||
import classes from './Navbar.module.css';
 | 
					import classes from "./Navbar.module.css";
 | 
				
			||||||
import {useAppDispatch} from "../../redux/store.ts";
 | 
					import { useAppDispatch } from "../../redux/store.ts";
 | 
				
			||||||
import {logout} from "../../features/authSlice.ts";
 | 
					import { logout } from "../../features/authSlice.ts";
 | 
				
			||||||
import {useNavigate, useRouterState} from "@tanstack/react-router";
 | 
					import { useNavigate, useRouterState } from "@tanstack/react-router";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface NavbarLinkProps {
 | 
					interface NavbarLinkProps {
 | 
				
			||||||
    icon: typeof IconHome2;
 | 
					    icon: typeof IconHome2;
 | 
				
			||||||
@@ -28,24 +39,31 @@ interface NavbarLinkProps {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function NavbarLink(props: NavbarLinkProps) {
 | 
					function NavbarLink(props: NavbarLinkProps) {
 | 
				
			||||||
    const {icon: Icon, label, active, onClick} = props;
 | 
					    const { icon: Icon, label, active, onClick } = props;
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Tooltip display={!label ? "none" : "flex"} label={label} position="right" transitionProps={{duration: 0}}>
 | 
					        <Tooltip
 | 
				
			||||||
            <UnstyledButton onClick={() => onClick && onClick(props)}
 | 
					            display={!label ? "none" : "flex"}
 | 
				
			||||||
                            className={classes.link}
 | 
					            label={label}
 | 
				
			||||||
                            data-active={active || undefined}>
 | 
					            position="right"
 | 
				
			||||||
                <Icon style={{width: rem(20), height: rem(20)}} stroke={1.5}/>
 | 
					            transitionProps={{ duration: 0 }}>
 | 
				
			||||||
 | 
					            <UnstyledButton
 | 
				
			||||||
 | 
					                onClick={() => onClick && onClick(props)}
 | 
				
			||||||
 | 
					                className={classes.link}
 | 
				
			||||||
 | 
					                data-active={active || undefined}>
 | 
				
			||||||
 | 
					                <Icon
 | 
				
			||||||
 | 
					                    style={{ width: rem(20), height: rem(20) }}
 | 
				
			||||||
 | 
					                    stroke={1.5}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
            </UnstyledButton>
 | 
					            </UnstyledButton>
 | 
				
			||||||
        </Tooltip>
 | 
					        </Tooltip>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const mockdata = [
 | 
					const mockdata = [
 | 
				
			||||||
 | 
					 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        icon: IconCash,
 | 
					        icon: IconCash,
 | 
				
			||||||
        label: 'Сделки',
 | 
					        label: "Сделки",
 | 
				
			||||||
        href: '/leads'
 | 
					        href: "/leads",
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    // {
 | 
					    // {
 | 
				
			||||||
    //     icon: IconTable,
 | 
					    //     icon: IconTable,
 | 
				
			||||||
@@ -54,48 +72,50 @@ const mockdata = [
 | 
				
			|||||||
    // },
 | 
					    // },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        icon: IconMan,
 | 
					        icon: IconMan,
 | 
				
			||||||
        label: 'Клиенты',
 | 
					        label: "Клиенты",
 | 
				
			||||||
        href: '/clients'
 | 
					        href: "/clients",
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        icon: IconBox,
 | 
					        icon: IconBox,
 | 
				
			||||||
        label: 'Услуги',
 | 
					        label: "Услуги",
 | 
				
			||||||
        href: '/services'
 | 
					        href: "/services",
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        icon: IconBarcode,
 | 
					        icon: IconBarcode,
 | 
				
			||||||
        label: 'Товары',
 | 
					        label: "Товары",
 | 
				
			||||||
        href: '/products'
 | 
					        href: "/products",
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        icon: IconFileBarcode,
 | 
					        icon: IconFileBarcode,
 | 
				
			||||||
        label: 'Штрихкоды',
 | 
					        label: "Штрихкоды",
 | 
				
			||||||
        href: '/barcode'
 | 
					        href: "/barcode",
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        icon: IconBuildingWarehouse,
 | 
					        icon: IconBuildingWarehouse,
 | 
				
			||||||
        label: 'Склады отгрузки',
 | 
					        label: "Склады отгрузки",
 | 
				
			||||||
        href: '/shipping_warehouses'
 | 
					        href: "/shipping_warehouses",
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        icon:IconShoppingCart,
 | 
					        icon: IconShoppingCart,
 | 
				
			||||||
        label: 'Маркетплейсы',
 | 
					        label: "Маркетплейсы",
 | 
				
			||||||
        href: '/marketplaces'
 | 
					        href: "/marketplaces",
 | 
				
			||||||
    }
 | 
					    },
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function Navbar() {
 | 
					export function Navbar() {
 | 
				
			||||||
    const dispatch = useAppDispatch();
 | 
					    const dispatch = useAppDispatch();
 | 
				
			||||||
    const navigate = useNavigate();
 | 
					    const navigate = useNavigate();
 | 
				
			||||||
    const router = useRouterState();
 | 
					    const router = useRouterState();
 | 
				
			||||||
    const {colorScheme, toggleColorScheme} = useMantineColorScheme({keepTransitions: true});
 | 
					    const { colorScheme, toggleColorScheme } = useMantineColorScheme({
 | 
				
			||||||
 | 
					        keepTransitions: true,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
    const onLogoutClick = () => {
 | 
					    const onLogoutClick = () => {
 | 
				
			||||||
        dispatch(logout());
 | 
					        dispatch(logout());
 | 
				
			||||||
        navigate({to: '/login'});
 | 
					        navigate({ to: "/login" });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    const onNavlinkClick = (props: NavbarLinkProps) => {
 | 
					    const onNavlinkClick = (props: NavbarLinkProps) => {
 | 
				
			||||||
        navigate({to: props.href});
 | 
					        navigate({ to: props.href });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    const links = mockdata.map((link, index) => (
 | 
					    const links = mockdata.map((link, index) => (
 | 
				
			||||||
        <NavbarLink
 | 
					        <NavbarLink
 | 
				
			||||||
            {...link}
 | 
					            {...link}
 | 
				
			||||||
@@ -108,34 +128,61 @@ export function Navbar() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <nav className={classes.navbar}>
 | 
					        <nav className={classes.navbar}>
 | 
				
			||||||
            <Flex direction={"column"} gap={rem(30)}>
 | 
					            <Flex
 | 
				
			||||||
                <Center
 | 
					                direction={"column"}
 | 
				
			||||||
                    p={rem(5)}
 | 
					                gap={rem(30)}>
 | 
				
			||||||
                >
 | 
					                <Center p={rem(5)}>
 | 
				
			||||||
                    <Image
 | 
					                    <Image
 | 
				
			||||||
                        flex={1}
 | 
					                        flex={1}
 | 
				
			||||||
                        // style={{filter: "drop-shadow(0 0 30px #fff)"}}
 | 
					                        // style={{filter: "drop-shadow(0 0 30px #fff)"}}
 | 
				
			||||||
                        src={colorScheme == "dark" ? "/icons/logo-light.png" : "/icons/logo.png"}
 | 
					                        src={
 | 
				
			||||||
 | 
					                            colorScheme == "dark"
 | 
				
			||||||
 | 
					                                ? "/icons/logo-light.png"
 | 
				
			||||||
 | 
					                                : "/icons/logo.png"
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                    />
 | 
					                    />
 | 
				
			||||||
                </Center>
 | 
					                </Center>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <div className={classes.navbarMain}>
 | 
					                <div className={classes.navbarMain}>
 | 
				
			||||||
                    <Stack justify="center" gap={rem(10)}>
 | 
					                    <Stack
 | 
				
			||||||
 | 
					                        justify="center"
 | 
				
			||||||
 | 
					                        gap={rem(10)}>
 | 
				
			||||||
                        {links}
 | 
					                        {links}
 | 
				
			||||||
                    </Stack>
 | 
					                    </Stack>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </Flex>
 | 
					            </Flex>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <Stack w={"100%"} justify="center" gap={0}>
 | 
					            <Stack
 | 
				
			||||||
                <NavbarLink icon={IconDashboard}
 | 
					                w={"100%"}
 | 
				
			||||||
                            href={"/admin"}
 | 
					                justify="center"
 | 
				
			||||||
                            index={-1}
 | 
					                gap={0}>
 | 
				
			||||||
                            label={"Панель администратора"}
 | 
					                <NavbarLink
 | 
				
			||||||
                            onClick={() => onNavlinkClick({href: "/admin", index: -1, icon: IconDashboard})}
 | 
					                    icon={IconDashboard}
 | 
				
			||||||
 | 
					                    href={"/admin"}
 | 
				
			||||||
 | 
					                    index={-1}
 | 
				
			||||||
 | 
					                    label={"Панель администратора"}
 | 
				
			||||||
 | 
					                    onClick={() =>
 | 
				
			||||||
 | 
					                        onNavlinkClick({
 | 
				
			||||||
 | 
					                            href: "/admin",
 | 
				
			||||||
 | 
					                            index: -1,
 | 
				
			||||||
 | 
					                            icon: IconDashboard,
 | 
				
			||||||
 | 
					                        })
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					                <NavbarLink
 | 
				
			||||||
 | 
					                    label={"Сменить тему"}
 | 
				
			||||||
 | 
					                    onClick={toggleColorScheme}
 | 
				
			||||||
 | 
					                    icon={colorScheme == "dark" ? IconSun : IconMoon}
 | 
				
			||||||
 | 
					                    href={"#"}
 | 
				
			||||||
 | 
					                    index={-1}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					                <NavbarLink
 | 
				
			||||||
 | 
					                    index={-1}
 | 
				
			||||||
 | 
					                    href={"#"}
 | 
				
			||||||
 | 
					                    onClick={onLogoutClick}
 | 
				
			||||||
 | 
					                    icon={IconLogout}
 | 
				
			||||||
 | 
					                    label="Выйти"
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
                <NavbarLink label={"Сменить тему"} onClick={toggleColorScheme}
 | 
					 | 
				
			||||||
                            icon={colorScheme == "dark" ? IconSun : IconMoon} href={"#"} index={-1}/>
 | 
					 | 
				
			||||||
                <NavbarLink index={-1} href={"#"} onClick={onLogoutClick} icon={IconLogout} label="Выйти"/>
 | 
					 | 
				
			||||||
            </Stack>
 | 
					            </Stack>
 | 
				
			||||||
        </nav>
 | 
					        </nav>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,13 @@
 | 
				
			|||||||
.user {
 | 
					.user {
 | 
				
			||||||
  display: block;
 | 
					    display: block;
 | 
				
			||||||
  width: 100%;
 | 
					    width: 100%;
 | 
				
			||||||
  padding: var(--mantine-spacing-md);
 | 
					    padding: var(--mantine-spacing-md);
 | 
				
			||||||
  color: light-dark(var(--mantine-color-black), var(--mantine-color-dark-0));
 | 
					    color: light-dark(var(--mantine-color-black), var(--mantine-color-dark-0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @mixin hover {
 | 
					    @mixin hover {
 | 
				
			||||||
    background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-8));
 | 
					        background-color: light-dark(
 | 
				
			||||||
  }
 | 
					            var(--mantine-color-gray-0),
 | 
				
			||||||
 | 
					            var(--mantine-color-dark-8)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
import { UnstyledButton, Group, Avatar, Text, rem } from '@mantine/core';
 | 
					import { UnstyledButton, Group, Avatar, Text, rem } from "@mantine/core";
 | 
				
			||||||
import { IconChevronRight } from '@tabler/icons-react';
 | 
					import { IconChevronRight } from "@tabler/icons-react";
 | 
				
			||||||
import classes from './UserButton.module.css';
 | 
					import classes from "./UserButton.module.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function UserButton() {
 | 
					export function UserButton() {
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
@@ -12,16 +12,23 @@ export function UserButton() {
 | 
				
			|||||||
                />
 | 
					                />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <div style={{ flex: 1 }}>
 | 
					                <div style={{ flex: 1 }}>
 | 
				
			||||||
                    <Text size="sm" fw={500}>
 | 
					                    <Text
 | 
				
			||||||
 | 
					                        size="sm"
 | 
				
			||||||
 | 
					                        fw={500}>
 | 
				
			||||||
                        Harriette Spoonlicker
 | 
					                        Harriette Spoonlicker
 | 
				
			||||||
                    </Text>
 | 
					                    </Text>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <Text c="dimmed" size="xs">
 | 
					                    <Text
 | 
				
			||||||
 | 
					                        c="dimmed"
 | 
				
			||||||
 | 
					                        size="xs">
 | 
				
			||||||
                        hspoonlicker@outlook.com
 | 
					                        hspoonlicker@outlook.com
 | 
				
			||||||
                    </Text>
 | 
					                    </Text>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <IconChevronRight style={{ width: rem(14), height: rem(14) }} stroke={1.5} />
 | 
					                <IconChevronRight
 | 
				
			||||||
 | 
					                    style={{ width: rem(14), height: rem(14) }}
 | 
				
			||||||
 | 
					                    stroke={1.5}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
            </Group>
 | 
					            </Group>
 | 
				
			||||||
        </UnstyledButton>
 | 
					        </UnstyledButton>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,25 +1,23 @@
 | 
				
			|||||||
import {IconAdjustments, IconGauge} from "@tabler/icons-react";
 | 
					import { IconAdjustments, IconGauge } from "@tabler/icons-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const NavbarLinks = [
 | 
					export const NavbarLinks = [
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        label: 'Главная',
 | 
					        label: "Главная",
 | 
				
			||||||
        icon: IconGauge,
 | 
					        icon: IconGauge,
 | 
				
			||||||
        links: [
 | 
					        links: [
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                label: "123",
 | 
					                label: "123",
 | 
				
			||||||
                link: "/login"
 | 
					                link: "/login",
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
 | 
					 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        label: 'Настройки',
 | 
					        label: "Настройки",
 | 
				
			||||||
        icon: IconAdjustments,
 | 
					        icon: IconAdjustments,
 | 
				
			||||||
        links:[
 | 
					        links: [
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                label: "Профиль"
 | 
					                label: "Профиль",
 | 
				
			||||||
            }
 | 
					            },
 | 
				
			||||||
        ]
 | 
					        ],
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,40 +1,44 @@
 | 
				
			|||||||
import {Autocomplete, AutocompleteProps} from "@mantine/core";
 | 
					import { Autocomplete, AutocompleteProps } from "@mantine/core";
 | 
				
			||||||
import {useEffect, useMemo, useState} from "react";
 | 
					import { useEffect, useMemo, useState } from "react";
 | 
				
			||||||
import {ObjectWithNameAndId} from "../../types/utils.ts";
 | 
					import { ObjectWithNameAndId } from "../../types/utils.ts";
 | 
				
			||||||
import {omit} from "lodash";
 | 
					import { omit } from "lodash";
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type AutocompleteObjectType<T extends ObjectWithNameAndId> = T;
 | 
					export type AutocompleteObjectType<T extends ObjectWithNameAndId> = T;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ControlledValueProps<T extends ObjectWithNameAndId> = {
 | 
					type ControlledValueProps<T extends ObjectWithNameAndId> = {
 | 
				
			||||||
    value: AutocompleteObjectType<T>,
 | 
					    value: AutocompleteObjectType<T>;
 | 
				
			||||||
    onChange: (value: string) => void;
 | 
					    onChange: (value: string) => void;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type RestProps<T extends ObjectWithNameAndId> = {
 | 
					type RestProps<T extends ObjectWithNameAndId> = {
 | 
				
			||||||
    defaultValue?: AutocompleteObjectType<T>
 | 
					    defaultValue?: AutocompleteObjectType<T>;
 | 
				
			||||||
    onChange: (value: string) => void;
 | 
					    onChange: (value: string) => void;
 | 
				
			||||||
    data: AutocompleteObjectType<T>[];
 | 
					    data: AutocompleteObjectType<T>[];
 | 
				
			||||||
    filterBy?: (item: AutocompleteObjectType<T>) => boolean;
 | 
					    filterBy?: (item: AutocompleteObjectType<T>) => boolean;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type ObjectAutocompleteProps<T extends ObjectWithNameAndId> =
 | 
					export type ObjectAutocompleteProps<T extends ObjectWithNameAndId> =
 | 
				
			||||||
    (RestProps<T> & Partial<ControlledValueProps<T>>)
 | 
					    (RestProps<T> & Partial<ControlledValueProps<T>>) &
 | 
				
			||||||
    & Omit<AutocompleteProps, 'value' | 'onChange' | 'data'>;
 | 
					        Omit<AutocompleteProps, "value" | "onChange" | "data">;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ObjectAutocomplete = <T extends ObjectWithNameAndId, >(props: ObjectAutocompleteProps<T>) => {
 | 
					const ObjectAutocomplete = <T extends ObjectWithNameAndId>(
 | 
				
			||||||
 | 
					    props: ObjectAutocompleteProps<T>
 | 
				
			||||||
    const isControlled = 'value' in props;
 | 
					) => {
 | 
				
			||||||
    const [internalValue, setInternalValue] = useState<undefined | string>(props.defaultValue);
 | 
					    const isControlled = "value" in props;
 | 
				
			||||||
 | 
					    const [internalValue, setInternalValue] = useState<undefined | string>(
 | 
				
			||||||
 | 
					        props.defaultValue
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const value = isControlled ? props.value?.name : internalValue;
 | 
					    const value = isControlled ? props.value?.name : internalValue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const data = useMemo(() => {
 | 
					    const data = useMemo(() => {
 | 
				
			||||||
        const propsData = props.filterBy ? props.data.filter(props.filterBy) : props.data;
 | 
					        const propsData = props.filterBy
 | 
				
			||||||
 | 
					            ? props.data.filter(props.filterBy)
 | 
				
			||||||
 | 
					            : props.data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return propsData.map(item => ({
 | 
					        return propsData.map(item => ({
 | 
				
			||||||
            label: item.name,
 | 
					            label: item.name,
 | 
				
			||||||
            value: item.id.toString()
 | 
					            value: item.id.toString(),
 | 
				
			||||||
        }));
 | 
					        }));
 | 
				
			||||||
    }, [props.data]);
 | 
					    }, [props.data]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -45,13 +49,13 @@ const ObjectAutocomplete = <T extends ObjectWithNameAndId, >(props: ObjectAutoco
 | 
				
			|||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        setInternalValue(event);
 | 
					        setInternalValue(event);
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        if (isControlled || !internalValue) return;
 | 
					        if (isControlled || !internalValue) return;
 | 
				
			||||||
        props.onChange(internalValue);
 | 
					        props.onChange(internalValue);
 | 
				
			||||||
    }, [internalValue]);
 | 
					    }, [internalValue]);
 | 
				
			||||||
    const restProps = omit(props, ['filterBy', 'groupBy']);
 | 
					    const restProps = omit(props, ["filterBy", "groupBy"]);
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Autocomplete
 | 
					        <Autocomplete
 | 
				
			||||||
            {...restProps}
 | 
					            {...restProps}
 | 
				
			||||||
@@ -59,7 +63,7 @@ const ObjectAutocomplete = <T extends ObjectWithNameAndId, >(props: ObjectAutoco
 | 
				
			|||||||
            onChange={handleOnChange}
 | 
					            onChange={handleOnChange}
 | 
				
			||||||
            data={data}
 | 
					            data={data}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default ObjectAutocomplete;
 | 
					export default ObjectAutocomplete;
 | 
				
			||||||
@@ -1,83 +1,91 @@
 | 
				
			|||||||
import {MultiSelect, MultiSelectProps} from "@mantine/core";
 | 
					import { MultiSelect, MultiSelectProps } from "@mantine/core";
 | 
				
			||||||
import {useEffect, useMemo, useState} from "react";
 | 
					import { useEffect, useMemo, useState } from "react";
 | 
				
			||||||
import {groupBy} from "lodash";
 | 
					import { groupBy } from "lodash";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface ObjectWithIdAndName {
 | 
					interface ObjectWithIdAndName {
 | 
				
			||||||
    id: number,
 | 
					    id: number;
 | 
				
			||||||
    name: string
 | 
					    name: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type MultiselectObjectType<T> = T;
 | 
					export type MultiselectObjectType<T> = T;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ControlledValueProps<T> = {
 | 
					type ControlledValueProps<T> = {
 | 
				
			||||||
    value: MultiselectObjectType<T>[],
 | 
					    value: MultiselectObjectType<T>[];
 | 
				
			||||||
    onChange: (value: MultiselectObjectType<T>[]) => void;
 | 
					    onChange: (value: MultiselectObjectType<T>[]) => void;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type CustomLabelAndKeyProps<T> = {
 | 
					type CustomLabelAndKeyProps<T> = {
 | 
				
			||||||
    getLabelFn: (item: MultiselectObjectType<T>) => string;
 | 
					    getLabelFn: (item: MultiselectObjectType<T>) => string;
 | 
				
			||||||
    getValueFn: (item: MultiselectObjectType<T>) => string;
 | 
					    getValueFn: (item: MultiselectObjectType<T>) => string;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
type RestProps<T> = {
 | 
					type RestProps<T> = {
 | 
				
			||||||
    defaultValue?: MultiselectObjectType<T>[]
 | 
					    defaultValue?: MultiselectObjectType<T>[];
 | 
				
			||||||
    onChange: (value: MultiselectObjectType<T>[]) => void;
 | 
					    onChange: (value: MultiselectObjectType<T>[]) => void;
 | 
				
			||||||
    data: MultiselectObjectType<T>[];
 | 
					    data: MultiselectObjectType<T>[];
 | 
				
			||||||
    groupBy?: (item: MultiselectObjectType<T>) => string;
 | 
					    groupBy?: (item: MultiselectObjectType<T>) => string;
 | 
				
			||||||
    filterBy?: (item: MultiselectObjectType<T>) => boolean;
 | 
					    filterBy?: (item: MultiselectObjectType<T>) => boolean;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
const defaultGetLabelFn = <T extends { name: string }>(item: T): string => {
 | 
					const defaultGetLabelFn = <T extends { name: string }>(item: T): string => {
 | 
				
			||||||
    return item.name;
 | 
					    return item.name;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const defaultGetValueFn = <T extends { id: number }>(item: T): string => {
 | 
					const defaultGetValueFn = <T extends { id: number }>(item: T): string => {
 | 
				
			||||||
    return item.id.toString();
 | 
					    return item.id.toString();
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
export type ObjectMultiSelectProps<T> =
 | 
					export type ObjectMultiSelectProps<T> = (RestProps<T> &
 | 
				
			||||||
    (RestProps<T> & Partial<ControlledValueProps<T>>)
 | 
					    Partial<ControlledValueProps<T>>) &
 | 
				
			||||||
    & Omit<MultiSelectProps, 'value' | 'onChange' | 'data'>
 | 
					    Omit<MultiSelectProps, "value" | "onChange" | "data"> &
 | 
				
			||||||
    & (T extends ObjectWithIdAndName ? Partial<CustomLabelAndKeyProps<T>> : CustomLabelAndKeyProps<T>);
 | 
					    (T extends ObjectWithIdAndName
 | 
				
			||||||
 | 
					        ? Partial<CustomLabelAndKeyProps<T>>
 | 
				
			||||||
 | 
					        : CustomLabelAndKeyProps<T>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ObjectMultiSelect = <T, >(props: ObjectMultiSelectProps<T>) => {
 | 
					const ObjectMultiSelect = <T,>(props: ObjectMultiSelectProps<T>) => {
 | 
				
			||||||
 | 
					    const isControlled = "value" in props;
 | 
				
			||||||
 | 
					    const haveGetValueFn = "getValueFn" in props;
 | 
				
			||||||
 | 
					    const haveGetLabelFn = "getLabelFn" in props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const isControlled = 'value' in props;
 | 
					    const [internalValue, setInternalValue] = useState<
 | 
				
			||||||
    const haveGetValueFn = 'getValueFn' in props;
 | 
					        MultiselectObjectType<T>[] | undefined
 | 
				
			||||||
    const haveGetLabelFn = 'getLabelFn' in props;
 | 
					    >(props.defaultValue);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    const [internalValue, setInternalValue] = useState<MultiselectObjectType<T>[] | undefined>(props.defaultValue);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const value = (isControlled ? props.value : internalValue) || [];
 | 
					    const value = (isControlled ? props.value : internalValue) || [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const getValueFn = (haveGetValueFn && props.getValueFn) || defaultGetValueFn;
 | 
					    const getValueFn =
 | 
				
			||||||
    const getLabelFn = (haveGetLabelFn && props.getLabelFn) || defaultGetLabelFn;
 | 
					        (haveGetValueFn && props.getValueFn) || defaultGetValueFn;
 | 
				
			||||||
 | 
					    const getLabelFn =
 | 
				
			||||||
 | 
					        (haveGetLabelFn && props.getLabelFn) || defaultGetLabelFn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const data = useMemo(() => {
 | 
					    const data = useMemo(() => {
 | 
				
			||||||
        const propsData = props.filterBy ? props.data.filter(props.filterBy) : props.data;
 | 
					        const propsData = props.filterBy
 | 
				
			||||||
 | 
					            ? props.data.filter(props.filterBy)
 | 
				
			||||||
 | 
					            : props.data;
 | 
				
			||||||
        if (props.groupBy) {
 | 
					        if (props.groupBy) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
            const groupedData = groupBy(propsData, props.groupBy);
 | 
					            const groupedData = groupBy(propsData, props.groupBy);
 | 
				
			||||||
            return Object.entries(groupedData).map(([group, items]) => ({
 | 
					            return Object.entries(groupedData).map(([group, items]) => ({
 | 
				
			||||||
                group,
 | 
					                group,
 | 
				
			||||||
                items: items.map(item => ({
 | 
					                items: items.map(item => ({
 | 
				
			||||||
                    label: getLabelFn(item),
 | 
					                    label: getLabelFn(item),
 | 
				
			||||||
                    value: getValueFn(item)
 | 
					                    value: getValueFn(item),
 | 
				
			||||||
                }))
 | 
					                })),
 | 
				
			||||||
            }));
 | 
					            }));
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            return propsData.map(item => ({
 | 
					            return propsData.map(item => ({
 | 
				
			||||||
                label: getLabelFn(item),
 | 
					                label: getLabelFn(item),
 | 
				
			||||||
                value: getValueFn(item)
 | 
					                value: getValueFn(item),
 | 
				
			||||||
            }));
 | 
					            }));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }, [props.data, props.groupBy]);
 | 
					    }, [props.data, props.groupBy]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const handleOnChange = (event: string[]) => {
 | 
					    const handleOnChange = (event: string[]) => {
 | 
				
			||||||
        const objects = props.data.filter(item => event.includes(getValueFn(item)));
 | 
					        const objects = props.data.filter(item =>
 | 
				
			||||||
 | 
					            event.includes(getValueFn(item))
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
        if (isControlled) {
 | 
					        if (isControlled) {
 | 
				
			||||||
            props.onChange(objects);
 | 
					            props.onChange(objects);
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        setInternalValue(objects);
 | 
					        setInternalValue(objects);
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        if (isControlled || !internalValue) return;
 | 
					        if (isControlled || !internalValue) return;
 | 
				
			||||||
        props.onChange(internalValue);
 | 
					        props.onChange(internalValue);
 | 
				
			||||||
@@ -90,7 +98,7 @@ const ObjectMultiSelect = <T, >(props: ObjectMultiSelectProps<T>) => {
 | 
				
			|||||||
            onChange={handleOnChange}
 | 
					            onChange={handleOnChange}
 | 
				
			||||||
            data={data}
 | 
					            data={data}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default ObjectMultiSelect;
 | 
					export default ObjectMultiSelect;
 | 
				
			||||||
@@ -1,73 +1,77 @@
 | 
				
			|||||||
import {Select, SelectProps} from "@mantine/core";
 | 
					import { Select, SelectProps } from "@mantine/core";
 | 
				
			||||||
import {useEffect, useMemo, useState} from "react";
 | 
					import { useEffect, useMemo, useState } from "react";
 | 
				
			||||||
import {groupBy, omit} from "lodash";
 | 
					import { groupBy, omit } from "lodash";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface ObjectWithIdAndName {
 | 
					interface ObjectWithIdAndName {
 | 
				
			||||||
    id: number,
 | 
					    id: number;
 | 
				
			||||||
    name: string
 | 
					    name: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type SelectObjectType<T> = T;
 | 
					export type SelectObjectType<T> = T;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ControlledValueProps<T> = {
 | 
					type ControlledValueProps<T> = {
 | 
				
			||||||
    value: SelectObjectType<T>,
 | 
					    value: SelectObjectType<T>;
 | 
				
			||||||
    onChange: (value: SelectObjectType<T>) => void;
 | 
					    onChange: (value: SelectObjectType<T>) => void;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
type CustomLabelAndKeyProps<T> = {
 | 
					type CustomLabelAndKeyProps<T> = {
 | 
				
			||||||
    getLabelFn: (item: SelectObjectType<T>) => string;
 | 
					    getLabelFn: (item: SelectObjectType<T>) => string;
 | 
				
			||||||
    getValueFn: (item: SelectObjectType<T>) => string;
 | 
					    getValueFn: (item: SelectObjectType<T>) => string;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type RestProps<T> = {
 | 
					type RestProps<T> = {
 | 
				
			||||||
    defaultValue?: SelectObjectType<T>
 | 
					    defaultValue?: SelectObjectType<T>;
 | 
				
			||||||
    onChange: (value: SelectObjectType<T>) => void;
 | 
					    onChange: (value: SelectObjectType<T>) => void;
 | 
				
			||||||
    data: SelectObjectType<T>[];
 | 
					    data: SelectObjectType<T>[];
 | 
				
			||||||
    groupBy?: (item: SelectObjectType<T>) => string;
 | 
					    groupBy?: (item: SelectObjectType<T>) => string;
 | 
				
			||||||
    filterBy?: (item: SelectObjectType<T>) => boolean;
 | 
					    filterBy?: (item: SelectObjectType<T>) => boolean;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
const defaultGetLabelFn = <T extends { name: string }>(item: T): string => {
 | 
					const defaultGetLabelFn = <T extends { name: string }>(item: T): string => {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    return item.name;
 | 
					    return item.name;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const defaultGetValueFn = <T extends { id: number }>(item: T): string => {
 | 
					const defaultGetValueFn = <T extends { id: number }>(item: T): string => {
 | 
				
			||||||
    if (!item) return item;
 | 
					    if (!item) return item;
 | 
				
			||||||
    return item.id.toString();
 | 
					    return item.id.toString();
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
export type ObjectSelectProps<T> =
 | 
					export type ObjectSelectProps<T> = (RestProps<T> &
 | 
				
			||||||
    (RestProps<T> & Partial<ControlledValueProps<T>>)
 | 
					    Partial<ControlledValueProps<T>>) &
 | 
				
			||||||
    & Omit<SelectProps, 'value' | 'onChange' | 'data'>
 | 
					    Omit<SelectProps, "value" | "onChange" | "data"> &
 | 
				
			||||||
    & (T extends ObjectWithIdAndName ? Partial<CustomLabelAndKeyProps<T>> : CustomLabelAndKeyProps<T>)
 | 
					    (T extends ObjectWithIdAndName
 | 
				
			||||||
 | 
					        ? Partial<CustomLabelAndKeyProps<T>>
 | 
				
			||||||
 | 
					        : CustomLabelAndKeyProps<T>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ObjectSelect = <T, >(props: ObjectSelectProps<T>) => {
 | 
					const ObjectSelect = <T,>(props: ObjectSelectProps<T>) => {
 | 
				
			||||||
 | 
					    const isControlled = "value" in props;
 | 
				
			||||||
    const isControlled = 'value' in props;
 | 
					    const haveGetValueFn = "getValueFn" in props;
 | 
				
			||||||
    const haveGetValueFn = 'getValueFn' in props;
 | 
					    const haveGetLabelFn = "getLabelFn" in props;
 | 
				
			||||||
    const haveGetLabelFn = 'getLabelFn' in props;
 | 
					    const [internalValue, setInternalValue] = useState<
 | 
				
			||||||
    const [internalValue, setInternalValue] = useState<SelectObjectType<T> | undefined>(props.defaultValue);
 | 
					        SelectObjectType<T> | undefined
 | 
				
			||||||
 | 
					    >(props.defaultValue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const value = isControlled ? props.value : internalValue;
 | 
					    const value = isControlled ? props.value : internalValue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const getValueFn =
 | 
				
			||||||
    const getValueFn = (haveGetValueFn && props.getValueFn) || defaultGetValueFn;
 | 
					        (haveGetValueFn && props.getValueFn) || defaultGetValueFn;
 | 
				
			||||||
    const getLabelFn = (haveGetLabelFn && props.getLabelFn) || defaultGetLabelFn;
 | 
					    const getLabelFn =
 | 
				
			||||||
 | 
					        (haveGetLabelFn && props.getLabelFn) || defaultGetLabelFn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const data = useMemo(() => {
 | 
					    const data = useMemo(() => {
 | 
				
			||||||
        const propsData = props.filterBy ? props.data.filter(props.filterBy) : props.data;
 | 
					        const propsData = props.filterBy
 | 
				
			||||||
 | 
					            ? props.data.filter(props.filterBy)
 | 
				
			||||||
 | 
					            : props.data;
 | 
				
			||||||
        if (props.groupBy) {
 | 
					        if (props.groupBy) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
            const groupedData = groupBy(propsData, props.groupBy);
 | 
					            const groupedData = groupBy(propsData, props.groupBy);
 | 
				
			||||||
            return Object.entries(groupedData).map(([group, items]) => ({
 | 
					            return Object.entries(groupedData).map(([group, items]) => ({
 | 
				
			||||||
                group,
 | 
					                group,
 | 
				
			||||||
                items: items.map(item => ({
 | 
					                items: items.map(item => ({
 | 
				
			||||||
                    label: getLabelFn(item),
 | 
					                    label: getLabelFn(item),
 | 
				
			||||||
                    value: getValueFn(item)
 | 
					                    value: getValueFn(item),
 | 
				
			||||||
                }))
 | 
					                })),
 | 
				
			||||||
            }));
 | 
					            }));
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            return propsData.map(item => ({
 | 
					            return propsData.map(item => ({
 | 
				
			||||||
                label: getLabelFn(item),
 | 
					                label: getLabelFn(item),
 | 
				
			||||||
                value: getValueFn(item)
 | 
					                value: getValueFn(item),
 | 
				
			||||||
            }));
 | 
					            }));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }, [props.data, props.groupBy]);
 | 
					    }, [props.data, props.groupBy]);
 | 
				
			||||||
@@ -81,14 +85,19 @@ const ObjectSelect = <T, >(props: ObjectSelectProps<T>) => {
 | 
				
			|||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        setInternalValue(object);
 | 
					        setInternalValue(object);
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        if (isControlled || !internalValue) return;
 | 
					        if (isControlled || !internalValue) return;
 | 
				
			||||||
        props.onChange(internalValue);
 | 
					        props.onChange(internalValue);
 | 
				
			||||||
    }, [internalValue]);
 | 
					    }, [internalValue]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const restProps = omit(props, ['filterBy', 'groupBy', 'getValueFn', 'getLabelFn']);
 | 
					    const restProps = omit(props, [
 | 
				
			||||||
 | 
					        "filterBy",
 | 
				
			||||||
 | 
					        "groupBy",
 | 
				
			||||||
 | 
					        "getValueFn",
 | 
				
			||||||
 | 
					        "getLabelFn",
 | 
				
			||||||
 | 
					    ]);
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Select
 | 
					        <Select
 | 
				
			||||||
            {...restProps}
 | 
					            {...restProps}
 | 
				
			||||||
@@ -96,7 +105,7 @@ const ObjectSelect = <T, >(props: ObjectSelectProps<T>) => {
 | 
				
			|||||||
            onChange={handleOnChange}
 | 
					            onChange={handleOnChange}
 | 
				
			||||||
            data={data}
 | 
					            data={data}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default ObjectSelect;
 | 
					export default ObjectSelect;
 | 
				
			||||||
@@ -3,14 +3,17 @@
 | 
				
			|||||||
    background-color: #f9f9f9;
 | 
					    background-color: #f9f9f9;
 | 
				
			||||||
    @mixin dark {
 | 
					    @mixin dark {
 | 
				
			||||||
        background-color: var(--mantine-color-body);
 | 
					        background-color: var(--mantine-color-body);
 | 
				
			||||||
        box-shadow: 0 2px 4px var(--mantine-color-dark-7), 0 4px 24px var(--mantine-color-dark-7);
 | 
					        box-shadow:
 | 
				
			||||||
 | 
					            0 2px 4px var(--mantine-color-dark-7),
 | 
				
			||||||
 | 
					            0 4px 24px var(--mantine-color-dark-7);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    @mixin light {
 | 
					    @mixin light {
 | 
				
			||||||
        background-color: #f9f9f9;
 | 
					        background-color: #f9f9f9;
 | 
				
			||||||
        box-shadow: 0 2px 4px rgba(0, 0, 0, .08), 0 4px 24px rgba(0, 0, 0, .08);
 | 
					        box-shadow:
 | 
				
			||||||
 | 
					            0 2px 4px rgba(0, 0, 0, 0.08),
 | 
				
			||||||
 | 
					            0 4px 24px rgba(0, 0, 0, 0.08);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    padding: rem(15);
 | 
					    padding: rem(15);
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.container-fluid {
 | 
					.container-fluid {
 | 
				
			||||||
@@ -23,5 +26,4 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
.container-full-height-fixed {
 | 
					.container-full-height-fixed {
 | 
				
			||||||
    height: calc(100vh - (rem(20) * 2));
 | 
					    height: calc(100vh - (rem(20) * 2));
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,27 +1,32 @@
 | 
				
			|||||||
import {CSSProperties, FC, ReactNode} from "react";
 | 
					import { CSSProperties, FC, ReactNode } from "react";
 | 
				
			||||||
import styles from './PageBlock.module.css';
 | 
					import styles from "./PageBlock.module.css";
 | 
				
			||||||
import classNames from "classnames";
 | 
					import classNames from "classnames";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
    children: ReactNode
 | 
					    children: ReactNode;
 | 
				
			||||||
    fluid?: boolean;
 | 
					    fluid?: boolean;
 | 
				
			||||||
    style?: CSSProperties;
 | 
					    style?: CSSProperties;
 | 
				
			||||||
    fullHeight?: boolean;
 | 
					    fullHeight?: boolean;
 | 
				
			||||||
    fullHeightFixed?: boolean;
 | 
					    fullHeightFixed?: boolean;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
export const PageBlock: FC<Props> = ({children, style, fluid = true, fullHeight = false, fullHeightFixed = false}) => {
 | 
					export const PageBlock: FC<Props> = ({
 | 
				
			||||||
 | 
					    children,
 | 
				
			||||||
 | 
					    style,
 | 
				
			||||||
 | 
					    fluid = true,
 | 
				
			||||||
 | 
					    fullHeight = false,
 | 
				
			||||||
 | 
					    fullHeightFixed = false,
 | 
				
			||||||
 | 
					}) => {
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <div style={style}
 | 
					        <div
 | 
				
			||||||
             className={
 | 
					            style={style}
 | 
				
			||||||
                 classNames(
 | 
					            className={classNames(
 | 
				
			||||||
                     styles['container'],
 | 
					                styles["container"],
 | 
				
			||||||
                     fluid && styles['container-fluid'],
 | 
					                fluid && styles["container-fluid"],
 | 
				
			||||||
                     fullHeight && styles['container-full-height'],
 | 
					                fullHeight && styles["container-full-height"],
 | 
				
			||||||
                     fullHeightFixed && styles['container-full-height-fixed']
 | 
					                fullHeightFixed && styles["container-full-height-fixed"]
 | 
				
			||||||
                 )
 | 
					            )}>
 | 
				
			||||||
             }>
 | 
					 | 
				
			||||||
            {children}
 | 
					            {children}
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
export default PageBlock;
 | 
					export default PageBlock;
 | 
				
			||||||
@@ -1,17 +1,17 @@
 | 
				
			|||||||
import {ActionIcon, Flex, NumberInput, rem} from "@mantine/core";
 | 
					import { ActionIcon, Flex, NumberInput, rem } from "@mantine/core";
 | 
				
			||||||
import {IconMinus, IconPlus} from "@tabler/icons-react";
 | 
					import { IconMinus, IconPlus } from "@tabler/icons-react";
 | 
				
			||||||
import styles from './PlusMinusInput.module.css';
 | 
					import styles from "./PlusMinusInput.module.css";
 | 
				
			||||||
import {FC, useEffect, useState} from "react";
 | 
					import { FC, useEffect, useState } from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ControlledValueProps = {
 | 
					type ControlledValueProps = {
 | 
				
			||||||
    value: number;
 | 
					    value: number;
 | 
				
			||||||
    onChange: (value: number) => void;
 | 
					    onChange: (value: number) => void;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type RestProps = {
 | 
					type RestProps = {
 | 
				
			||||||
    defaultValue?: number;
 | 
					    defaultValue?: number;
 | 
				
			||||||
    onChange: (value: number) => void;
 | 
					    onChange: (value: number) => void;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = RestProps & Partial<ControlledValueProps>;
 | 
					type Props = RestProps & Partial<ControlledValueProps>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -22,7 +22,6 @@ const PlusMinusInput: FC<Props> = (props: Props) => {
 | 
				
			|||||||
    const value = isControlled ? props.value : internalValue;
 | 
					    const value = isControlled ? props.value : internalValue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const onMinusClick = () => {
 | 
					    const onMinusClick = () => {
 | 
				
			||||||
 | 
					 | 
				
			||||||
        const newValue = (value || 0) - 1;
 | 
					        const newValue = (value || 0) - 1;
 | 
				
			||||||
        if (newValue < 0) {
 | 
					        if (newValue < 0) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
@@ -32,7 +31,7 @@ const PlusMinusInput: FC<Props> = (props: Props) => {
 | 
				
			|||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            setInternalValue(newValue);
 | 
					            setInternalValue(newValue);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const onPlusClick = () => {
 | 
					    const onPlusClick = () => {
 | 
				
			||||||
        const newValue = (value || 0) + 1;
 | 
					        const newValue = (value || 0) + 1;
 | 
				
			||||||
@@ -41,7 +40,7 @@ const PlusMinusInput: FC<Props> = (props: Props) => {
 | 
				
			|||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            setInternalValue(newValue);
 | 
					            setInternalValue(newValue);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const handleInputChange = (event: number | string) => {
 | 
					    const handleInputChange = (event: number | string) => {
 | 
				
			||||||
        let newValue = typeof event === "string" ? 0 : event;
 | 
					        let newValue = typeof event === "string" ? 0 : event;
 | 
				
			||||||
@@ -53,7 +52,7 @@ const PlusMinusInput: FC<Props> = (props: Props) => {
 | 
				
			|||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            setInternalValue(newValue);
 | 
					            setInternalValue(newValue);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        if (!isControlled) {
 | 
					        if (!isControlled) {
 | 
				
			||||||
@@ -64,34 +63,33 @@ const PlusMinusInput: FC<Props> = (props: Props) => {
 | 
				
			|||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Flex
 | 
					        <Flex
 | 
				
			||||||
            align={"center"}
 | 
					            align={"center"}
 | 
				
			||||||
            gap={rem(10)}
 | 
					            gap={rem(10)}>
 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
            <ActionIcon
 | 
					            <ActionIcon
 | 
				
			||||||
                disabled={value === 0}
 | 
					                disabled={value === 0}
 | 
				
			||||||
                onClick={onMinusClick}
 | 
					                onClick={onMinusClick}
 | 
				
			||||||
                variant={"default"}>
 | 
					                variant={"default"}>
 | 
				
			||||||
                <IconMinus/>
 | 
					                <IconMinus />
 | 
				
			||||||
            </ActionIcon>
 | 
					            </ActionIcon>
 | 
				
			||||||
            <NumberInput
 | 
					            <NumberInput
 | 
				
			||||||
                min={0}
 | 
					                min={0}
 | 
				
			||||||
                styles={{
 | 
					                styles={{
 | 
				
			||||||
                    input: {
 | 
					                    input: {
 | 
				
			||||||
                        textAlign: "center"
 | 
					                        textAlign: "center",
 | 
				
			||||||
                    }
 | 
					                    },
 | 
				
			||||||
                }}
 | 
					                }}
 | 
				
			||||||
                allowNegative={false}
 | 
					                allowNegative={false}
 | 
				
			||||||
                hideControls
 | 
					                hideControls
 | 
				
			||||||
                value={value}
 | 
					                value={value}
 | 
				
			||||||
                className={styles['number-input']}
 | 
					                className={styles["number-input"]}
 | 
				
			||||||
                onChange={(event) => handleInputChange(event)}
 | 
					                onChange={event => handleInputChange(event)}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
            <ActionIcon
 | 
					            <ActionIcon
 | 
				
			||||||
                onClick={onPlusClick}
 | 
					                onClick={onPlusClick}
 | 
				
			||||||
                variant={"default"}>
 | 
					                variant={"default"}>
 | 
				
			||||||
                <IconPlus/>
 | 
					                <IconPlus />
 | 
				
			||||||
            </ActionIcon>
 | 
					            </ActionIcon>
 | 
				
			||||||
        </Flex>
 | 
					        </Flex>
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default PlusMinusInput;
 | 
					export default PlusMinusInput;
 | 
				
			||||||
@@ -1,81 +1,113 @@
 | 
				
			|||||||
import {ProductSchema} from "../../client";
 | 
					import { ProductSchema } from "../../client";
 | 
				
			||||||
import {FC, useState} from "react";
 | 
					import { FC, useState } from "react";
 | 
				
			||||||
import useProductsList from "../../pages/ProductsPage/hooks/useProductsList.tsx";
 | 
					import useProductsList from "../../pages/ProductsPage/hooks/useProductsList.tsx";
 | 
				
			||||||
import {omit} from "lodash";
 | 
					import { omit } from "lodash";
 | 
				
			||||||
import ObjectSelect, {ObjectSelectProps} from "../ObjectSelect/ObjectSelect.tsx";
 | 
					import ObjectSelect, {
 | 
				
			||||||
import {ComboboxItem, Image, Loader, OptionsFilter, rem, SelectProps, Text, Tooltip} from "@mantine/core";
 | 
					    ObjectSelectProps,
 | 
				
			||||||
import {getProductFields} from "../../types/utils.ts";
 | 
					} from "../ObjectSelect/ObjectSelect.tsx";
 | 
				
			||||||
import {useDebouncedValue} from "@mantine/hooks";
 | 
					import {
 | 
				
			||||||
 | 
					    ComboboxItem,
 | 
				
			||||||
 | 
					    Image,
 | 
				
			||||||
 | 
					    Loader,
 | 
				
			||||||
 | 
					    OptionsFilter,
 | 
				
			||||||
 | 
					    rem,
 | 
				
			||||||
 | 
					    SelectProps,
 | 
				
			||||||
 | 
					    Text,
 | 
				
			||||||
 | 
					    Tooltip,
 | 
				
			||||||
 | 
					} from "@mantine/core";
 | 
				
			||||||
 | 
					import { getProductFields } from "../../types/utils.ts";
 | 
				
			||||||
 | 
					import { useDebouncedValue } from "@mantine/hooks";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type RestProps = {
 | 
					type RestProps = {
 | 
				
			||||||
    clientId: number;
 | 
					    clientId: number;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
const MAX_PRODUCTS = 200;
 | 
					const MAX_PRODUCTS = 200;
 | 
				
			||||||
type Props = Omit<ObjectSelectProps<ProductSchema>, 'data'> & RestProps;
 | 
					type Props = Omit<ObjectSelectProps<ProductSchema>, "data"> & RestProps;
 | 
				
			||||||
const ProductSelect: FC<Props> = (props: Props) => {
 | 
					const ProductSelect: FC<Props> = (props: Props) => {
 | 
				
			||||||
    const [searchValue, setSearchValue] = useState("");
 | 
					    const [searchValue, setSearchValue] = useState("");
 | 
				
			||||||
    const [debounced] = useDebouncedValue(searchValue, 500);
 | 
					    const [debounced] = useDebouncedValue(searchValue, 500);
 | 
				
			||||||
    const {products, isLoading} = useProductsList({
 | 
					    const { products, isLoading } = useProductsList({
 | 
				
			||||||
        clientId: props.clientId,
 | 
					        clientId: props.clientId,
 | 
				
			||||||
        searchInput: debounced,
 | 
					        searchInput: debounced,
 | 
				
			||||||
        page: 0,
 | 
					        page: 0,
 | 
				
			||||||
        itemsPerPage: MAX_PRODUCTS
 | 
					        itemsPerPage: MAX_PRODUCTS,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    const restProps = omit(props, ['clientId']);
 | 
					    const restProps = omit(props, ["clientId"]);
 | 
				
			||||||
    const renderOption: SelectProps['renderOption'] = (item) => {
 | 
					    const renderOption: SelectProps["renderOption"] = item => {
 | 
				
			||||||
        const product = products.find(product => product.id == parseInt(item.option.value));
 | 
					        const product = products.find(
 | 
				
			||||||
 | 
					            product => product.id == parseInt(item.option.value)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
        if (!product) return item.option.label;
 | 
					        if (!product) return item.option.label;
 | 
				
			||||||
        const productFields = getProductFields(product);
 | 
					        const productFields = getProductFields(product);
 | 
				
			||||||
        const imageUrl = product.images && product.images[0] ? product.images[0].imageUrl : undefined;
 | 
					        const imageUrl =
 | 
				
			||||||
 | 
					            product.images && product.images[0]
 | 
				
			||||||
 | 
					                ? product.images[0].imageUrl
 | 
				
			||||||
 | 
					                : undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <Tooltip
 | 
					            <Tooltip
 | 
				
			||||||
                style={{whiteSpace: "pre-line"}}
 | 
					                style={{ whiteSpace: "pre-line" }}
 | 
				
			||||||
                multiline
 | 
					                multiline
 | 
				
			||||||
                disabled={productFields.length === 0}
 | 
					                disabled={productFields.length === 0}
 | 
				
			||||||
                label={
 | 
					                label={
 | 
				
			||||||
                    <>
 | 
					                    <>
 | 
				
			||||||
                        {productFields.map(([key, value]) => {
 | 
					                        {productFields
 | 
				
			||||||
                            return `${key.toString()}: ${value.toString()}`;
 | 
					                            .map(([key, value]) => {
 | 
				
			||||||
                        }).join('\n')}
 | 
					                                return `${key.toString()}: ${value.toString()}`;
 | 
				
			||||||
                        {imageUrl && <Image
 | 
					                            })
 | 
				
			||||||
                            src={imageUrl}
 | 
					                            .join("\n")}
 | 
				
			||||||
                            alt={product.name}
 | 
					                        {imageUrl && (
 | 
				
			||||||
                            maw={rem(250)}
 | 
					                            <Image
 | 
				
			||||||
                        />}
 | 
					                                src={imageUrl}
 | 
				
			||||||
 | 
					                                alt={product.name}
 | 
				
			||||||
 | 
					                                maw={rem(250)}
 | 
				
			||||||
 | 
					                            />
 | 
				
			||||||
 | 
					                        )}
 | 
				
			||||||
                    </>
 | 
					                    </>
 | 
				
			||||||
                }>
 | 
					                }>
 | 
				
			||||||
                <div>
 | 
					                <div>
 | 
				
			||||||
                    {product.name}<br/>
 | 
					                    {product.name}
 | 
				
			||||||
                    {product.barcodes && <Text size={"xs"}>{product.barcodes[0]}</Text>}
 | 
					                    <br />
 | 
				
			||||||
 | 
					                    {product.barcodes && (
 | 
				
			||||||
 | 
					                        <Text size={"xs"}>{product.barcodes[0]}</Text>
 | 
				
			||||||
 | 
					                    )}
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </Tooltip>)
 | 
					            </Tooltip>
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    const optionsFilter: OptionsFilter = ({options, search}) => {
 | 
					 | 
				
			||||||
        return options;
 | 
					 | 
				
			||||||
        const filtered = (options as ComboboxItem[]).filter((option) => {
 | 
					 | 
				
			||||||
                const product = products.find(product => product.id == parseInt(option.value));
 | 
					 | 
				
			||||||
                if (!product) return true;
 | 
					 | 
				
			||||||
                return product.name.toLowerCase().includes(search.toLowerCase()) ||
 | 
					 | 
				
			||||||
                    product.barcodes.some((value) => value.toLowerCase().includes(search.toLowerCase())) ||
 | 
					 | 
				
			||||||
                    product.article?.toLowerCase() === search.toLowerCase();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    const optionsFilter: OptionsFilter = ({ options, search }) => {
 | 
				
			||||||
 | 
					        return options;
 | 
				
			||||||
 | 
					        const filtered = (options as ComboboxItem[]).filter(option => {
 | 
				
			||||||
 | 
					            const product = products.find(
 | 
				
			||||||
 | 
					                product => product.id == parseInt(option.value)
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            if (!product) return true;
 | 
				
			||||||
 | 
					            return (
 | 
				
			||||||
 | 
					                product.name.toLowerCase().includes(search.toLowerCase()) ||
 | 
				
			||||||
 | 
					                product.barcodes.some(value =>
 | 
				
			||||||
 | 
					                    value.toLowerCase().includes(search.toLowerCase())
 | 
				
			||||||
 | 
					                ) ||
 | 
				
			||||||
 | 
					                product.article?.toLowerCase() === search.toLowerCase()
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
        filtered.sort((a, b) => a.label.localeCompare(b.label));
 | 
					        filtered.sort((a, b) => a.label.localeCompare(b.label));
 | 
				
			||||||
        return filtered.length > MAX_PRODUCTS ? filtered.slice(0, MAX_PRODUCTS) : filtered;
 | 
					        return filtered.length > MAX_PRODUCTS
 | 
				
			||||||
 | 
					            ? filtered.slice(0, MAX_PRODUCTS)
 | 
				
			||||||
 | 
					            : filtered;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    const setSearchValueImpl = (value: string) => {
 | 
					    const setSearchValueImpl = (value: string) => {
 | 
				
			||||||
        const names = products.map(product => product.name);
 | 
					        const names = products.map(product => product.name);
 | 
				
			||||||
        if (names.includes(value)) return;
 | 
					        if (names.includes(value)) return;
 | 
				
			||||||
        setSearchValue(value);
 | 
					        setSearchValue(value);
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <ObjectSelect
 | 
					        <ObjectSelect
 | 
				
			||||||
            // disabled={isLoading}
 | 
					            // disabled={isLoading}
 | 
				
			||||||
            rightSection={
 | 
					            rightSection={
 | 
				
			||||||
                (isLoading || searchValue !== debounced) ?
 | 
					                isLoading || searchValue !== debounced ? (
 | 
				
			||||||
                    <Loader size={"sm"}/> : null
 | 
					                    <Loader size={"sm"} />
 | 
				
			||||||
 | 
					                ) : null
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            onSearchChange={setSearchValueImpl}
 | 
					            onSearchChange={setSearchValueImpl}
 | 
				
			||||||
            renderOption={renderOption}
 | 
					            renderOption={renderOption}
 | 
				
			||||||
@@ -84,8 +116,8 @@ const ProductSelect: FC<Props> = (props: Props) => {
 | 
				
			|||||||
            data={products}
 | 
					            data={products}
 | 
				
			||||||
            filter={optionsFilter}
 | 
					            filter={optionsFilter}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
export default ProductSelect;
 | 
					export default ProductSelect;
 | 
				
			||||||
// type ControlledValueProps = {
 | 
					// type ControlledValueProps = {
 | 
				
			||||||
//     value: ProductSchema;
 | 
					//     value: ProductSchema;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,18 +1,20 @@
 | 
				
			|||||||
import ObjectSelect, {ObjectSelectProps} from "../../ObjectSelect/ObjectSelect.tsx";
 | 
					import ObjectSelect, {
 | 
				
			||||||
import {BarcodeTemplateSchema} from "../../../client";
 | 
					    ObjectSelectProps,
 | 
				
			||||||
 | 
					} from "../../ObjectSelect/ObjectSelect.tsx";
 | 
				
			||||||
 | 
					import { BarcodeTemplateSchema } from "../../../client";
 | 
				
			||||||
import useGetAllBarcodeTemplates from "../../../api/barcode/useGetAllBarcodeTemplates.tsx";
 | 
					import useGetAllBarcodeTemplates from "../../../api/barcode/useGetAllBarcodeTemplates.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = Omit<ObjectSelectProps<BarcodeTemplateSchema>, 'data'>;
 | 
					type Props = Omit<ObjectSelectProps<BarcodeTemplateSchema>, "data">;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const BarcodeTemplateSelect = (props: Props) => {
 | 
					const BarcodeTemplateSelect = (props: Props) => {
 | 
				
			||||||
    const {barcodeTemplates} = useGetAllBarcodeTemplates();
 | 
					    const { barcodeTemplates } = useGetAllBarcodeTemplates();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <ObjectSelect
 | 
					        <ObjectSelect
 | 
				
			||||||
            data={barcodeTemplates}
 | 
					            data={barcodeTemplates}
 | 
				
			||||||
            {...props}
 | 
					            {...props}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default BarcodeTemplateSelect;
 | 
					export default BarcodeTemplateSelect;
 | 
				
			||||||
@@ -1,45 +1,54 @@
 | 
				
			|||||||
import {BaseEnumListSchema, type CancelablePromise} from "../../../client";
 | 
					import { BaseEnumListSchema, type CancelablePromise } from "../../../client";
 | 
				
			||||||
import {FC, useEffect, useMemo, useState} from "react";
 | 
					import { FC, useEffect, useMemo, useState } from "react";
 | 
				
			||||||
import {useQuery} from "@tanstack/react-query";
 | 
					import { useQuery } from "@tanstack/react-query";
 | 
				
			||||||
import {Select, SelectProps} from "@mantine/core";
 | 
					import { Select, SelectProps } from "@mantine/core";
 | 
				
			||||||
import {omit} from "lodash";
 | 
					import { omit } from "lodash";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ControlledValueProps = {
 | 
					type ControlledValueProps = {
 | 
				
			||||||
    value: number,
 | 
					    value: number;
 | 
				
			||||||
    onChange: (value: number) => void;
 | 
					    onChange: (value: number) => void;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
type RestProps = {
 | 
					type RestProps = {
 | 
				
			||||||
    defaultValue?: number;
 | 
					    defaultValue?: number;
 | 
				
			||||||
    onChange: (value: number) => void;
 | 
					    onChange: (value: number) => void;
 | 
				
			||||||
    fetchFn: () => CancelablePromise<BaseEnumListSchema>;
 | 
					    fetchFn: () => CancelablePromise<BaseEnumListSchema>;
 | 
				
			||||||
    queryKey: string;
 | 
					    queryKey: string;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
export type BaseEnumSelectProps =
 | 
					export type BaseEnumSelectProps = (RestProps & Partial<ControlledValueProps>) &
 | 
				
			||||||
    (RestProps & Partial<ControlledValueProps>)
 | 
					    Omit<SelectProps, "value" | "onChange" | "data" | "defaultValue">;
 | 
				
			||||||
    & Omit<SelectProps, 'value' | 'onChange' | 'data' | 'defaultValue'>;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type EnumSelectProps = Omit<BaseEnumSelectProps, 'fetchFn' | 'queryKey'>;
 | 
					export type EnumSelectProps = Omit<BaseEnumSelectProps, "fetchFn" | "queryKey">;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const BaseEnumSelect: FC<BaseEnumSelectProps> = (props: BaseEnumSelectProps) => {
 | 
					const BaseEnumSelect: FC<BaseEnumSelectProps> = (
 | 
				
			||||||
    const {data: queryData = []} = useQuery({
 | 
					    props: BaseEnumSelectProps
 | 
				
			||||||
 | 
					) => {
 | 
				
			||||||
 | 
					    const { data: queryData = [] } = useQuery({
 | 
				
			||||||
        queryKey: [props.queryKey],
 | 
					        queryKey: [props.queryKey],
 | 
				
			||||||
        queryFn: props.fetchFn,
 | 
					        queryFn: props.fetchFn,
 | 
				
			||||||
        select: data => data.items || []
 | 
					        select: data => data.items || [],
 | 
				
			||||||
    })
 | 
					    });
 | 
				
			||||||
    const isControlled = 'value' in props;
 | 
					    const isControlled = "value" in props;
 | 
				
			||||||
    const [internalValue, setInternalValue] = useState<number | undefined>(props.defaultValue);
 | 
					    const [internalValue, setInternalValue] = useState<number | undefined>(
 | 
				
			||||||
 | 
					        props.defaultValue
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
    const value = isControlled ? props.value : internalValue;
 | 
					    const value = isControlled ? props.value : internalValue;
 | 
				
			||||||
    const selectData = useMemo(() => queryData.reduce((acc, item) => {
 | 
					    const selectData = useMemo(
 | 
				
			||||||
        acc.push({
 | 
					        () =>
 | 
				
			||||||
            label: item.name,
 | 
					            queryData.reduce(
 | 
				
			||||||
            value: item.id.toString()
 | 
					                (acc, item) => {
 | 
				
			||||||
        });
 | 
					                    acc.push({
 | 
				
			||||||
        return acc;
 | 
					                        label: item.name,
 | 
				
			||||||
    }, [] as { label: string, value: string }[]), [queryData]);
 | 
					                        value: item.id.toString(),
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                    return acc;
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                [] as { label: string; value: string }[]
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        [queryData]
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
    const handleOnChange = (event: string | null) => {
 | 
					    const handleOnChange = (event: string | null) => {
 | 
				
			||||||
        if (typeof event === 'undefined' || event === null) return;
 | 
					        if (typeof event === "undefined" || event === null) return;
 | 
				
			||||||
        const object = queryData.find(item => event == item.id.toString());
 | 
					        const object = queryData.find(item => event == item.id.toString());
 | 
				
			||||||
        if (!object) return;
 | 
					        if (!object) return;
 | 
				
			||||||
        if (isControlled) {
 | 
					        if (isControlled) {
 | 
				
			||||||
@@ -47,22 +56,23 @@ const BaseEnumSelect: FC<BaseEnumSelectProps> = (props: BaseEnumSelectProps) =>
 | 
				
			|||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        setInternalValue(parseInt(event));
 | 
					        setInternalValue(parseInt(event));
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    const restProps = omit(props, ['fetchFn', 'queryKey'])
 | 
					    const restProps = omit(props, ["fetchFn", "queryKey"]);
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        if (isControlled || typeof internalValue === 'undefined') return;
 | 
					        if (isControlled || typeof internalValue === "undefined") return;
 | 
				
			||||||
        props.onChange(internalValue);
 | 
					        props.onChange(internalValue);
 | 
				
			||||||
    }, [internalValue]);
 | 
					    }, [internalValue]);
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Select
 | 
					        <Select
 | 
				
			||||||
 | 
					 | 
				
			||||||
            {...restProps}
 | 
					            {...restProps}
 | 
				
			||||||
            defaultValue={props.defaultValue ? props.defaultValue.toString() : undefined}
 | 
					            defaultValue={
 | 
				
			||||||
 | 
					                props.defaultValue ? props.defaultValue.toString() : undefined
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            value={value?.toString()}
 | 
					            value={value?.toString()}
 | 
				
			||||||
            onChange={handleOnChange}
 | 
					            onChange={handleOnChange}
 | 
				
			||||||
            data={selectData}
 | 
					            data={selectData}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default BaseEnumSelect;
 | 
					export default BaseEnumSelect;
 | 
				
			||||||
@@ -1,30 +1,42 @@
 | 
				
			|||||||
import ObjectSelect, {ObjectSelectProps} from "../../ObjectSelect/ObjectSelect.tsx";
 | 
					import ObjectSelect, {
 | 
				
			||||||
import {BaseMarketplaceSchema} from "../../../client";
 | 
					    ObjectSelectProps,
 | 
				
			||||||
import {FC} from "react";
 | 
					} from "../../ObjectSelect/ObjectSelect.tsx";
 | 
				
			||||||
 | 
					import { BaseMarketplaceSchema } from "../../../client";
 | 
				
			||||||
 | 
					import { FC } from "react";
 | 
				
			||||||
import useBaseMarketplacesList from "../../../hooks/useBaseMarketplacesList.tsx";
 | 
					import useBaseMarketplacesList from "../../../hooks/useBaseMarketplacesList.tsx";
 | 
				
			||||||
import {ActionIcon, Image} from "@mantine/core";
 | 
					import { ActionIcon, Image } from "@mantine/core";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = Omit<ObjectSelectProps<BaseMarketplaceSchema>, 'data' | 'getValueFn' | 'getLabelFn'>
 | 
					type Props = Omit<
 | 
				
			||||||
 | 
					    ObjectSelectProps<BaseMarketplaceSchema>,
 | 
				
			||||||
 | 
					    "data" | "getValueFn" | "getLabelFn"
 | 
				
			||||||
 | 
					>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const BaseMarketplaceSelect: FC<Props> = (props) => {
 | 
					const BaseMarketplaceSelect: FC<Props> = props => {
 | 
				
			||||||
    const {objects: baseMarketplaces} = useBaseMarketplacesList();
 | 
					    const { objects: baseMarketplaces } = useBaseMarketplacesList();
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <ObjectSelect
 | 
					        <ObjectSelect
 | 
				
			||||||
            renderOption={(baseMarketplace) =>
 | 
					            renderOption={baseMarketplace => (
 | 
				
			||||||
                <>
 | 
					                <>
 | 
				
			||||||
                    <ActionIcon radius={"md"} variant={"transparent"}>
 | 
					                    <ActionIcon
 | 
				
			||||||
 | 
					                        radius={"md"}
 | 
				
			||||||
 | 
					                        variant={"transparent"}>
 | 
				
			||||||
                        <Image
 | 
					                        <Image
 | 
				
			||||||
                            src={baseMarketplaces.find(el => baseMarketplace.option.value === el.key)?.iconUrl || ""}/>
 | 
					                            src={
 | 
				
			||||||
 | 
					                                baseMarketplaces.find(
 | 
				
			||||||
 | 
					                                    el =>
 | 
				
			||||||
 | 
					                                        baseMarketplace.option.value === el.key
 | 
				
			||||||
 | 
					                                )?.iconUrl || ""
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
                    </ActionIcon>
 | 
					                    </ActionIcon>
 | 
				
			||||||
                    {baseMarketplace.option.label}
 | 
					                    {baseMarketplace.option.label}
 | 
				
			||||||
                </>
 | 
					                </>
 | 
				
			||||||
 | 
					            )}
 | 
				
			||||||
            }
 | 
					            getValueFn={baseMarketplace => baseMarketplace.key}
 | 
				
			||||||
            getValueFn={(baseMarketplace) => baseMarketplace.key}
 | 
					            getLabelFn={baseMarketplace => baseMarketplace.name}
 | 
				
			||||||
            getLabelFn={(baseMarketplace) => baseMarketplace.name}
 | 
					 | 
				
			||||||
            data={baseMarketplaces}
 | 
					            data={baseMarketplaces}
 | 
				
			||||||
            {...props}
 | 
					            {...props}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
export default BaseMarketplaceSelect
 | 
					export default BaseMarketplaceSelect;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,54 +1,63 @@
 | 
				
			|||||||
import {useDebouncedValue} from "@mantine/hooks";
 | 
					import { useDebouncedValue } from "@mantine/hooks";
 | 
				
			||||||
import {Autocomplete, AutocompleteProps, TextInputProps} from "@mantine/core";
 | 
					import { Autocomplete, AutocompleteProps, TextInputProps } from "@mantine/core";
 | 
				
			||||||
import {FC, useEffect, useState} from "react";
 | 
					import { FC, useEffect, useState } from "react";
 | 
				
			||||||
import {Client} from "../../../types/Client.ts";
 | 
					import { Client } from "../../../types/Client.ts";
 | 
				
			||||||
import {ClientService} from "../../../client";
 | 
					import { ClientService } from "../../../client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
    onSelect?: (client: Client) => void;
 | 
					    onSelect?: (client: Client) => void;
 | 
				
			||||||
    withAddress?: boolean;
 | 
					    withAddress?: boolean;
 | 
				
			||||||
    nameRestProps?: AutocompleteProps;
 | 
					    nameRestProps?: AutocompleteProps;
 | 
				
			||||||
    addressRestProps?: TextInputProps;
 | 
					    addressRestProps?: TextInputProps;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
const ClientAutocomplete: FC<Props> = ({onSelect, addressRestProps, nameRestProps, withAddress = false}) => {
 | 
					const ClientAutocomplete: FC<Props> = ({
 | 
				
			||||||
    const [value, setValue] = useState('');
 | 
					    onSelect,
 | 
				
			||||||
 | 
					    addressRestProps,
 | 
				
			||||||
 | 
					    nameRestProps,
 | 
				
			||||||
 | 
					    withAddress = false,
 | 
				
			||||||
 | 
					}) => {
 | 
				
			||||||
 | 
					    const [value, setValue] = useState("");
 | 
				
			||||||
    const [debouncedValue] = useDebouncedValue(value, 200);
 | 
					    const [debouncedValue] = useDebouncedValue(value, 200);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // const [isLoading, setIsLoading] = useState(false);
 | 
					    // const [isLoading, setIsLoading] = useState(false);
 | 
				
			||||||
    const [clients, setClients] = useState<Client[]>([])
 | 
					    const [clients, setClients] = useState<Client[]>([]);
 | 
				
			||||||
    const [selectedClient, selectClient] = useState<Client>();
 | 
					    const [selectedClient, selectClient] = useState<Client>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const handleChange = (value: string) => {
 | 
					    const handleChange = (value: string) => {
 | 
				
			||||||
        setClients([]);
 | 
					        setClients([]);
 | 
				
			||||||
        setValue(value);
 | 
					        setValue(value);
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    const handleDebouncedChange = () => {
 | 
					    const handleDebouncedChange = () => {
 | 
				
			||||||
        if (!value.trim()) return;
 | 
					        if (!value.trim()) return;
 | 
				
			||||||
        // setIsLoading(true);
 | 
					        // setIsLoading(true);
 | 
				
			||||||
        ClientService.searchClients({name: value}).then(({clients}) => {
 | 
					        ClientService.searchClients({ name: value }).then(({ clients }) => {
 | 
				
			||||||
            setClients(clients);
 | 
					            setClients(clients);
 | 
				
			||||||
            // setIsLoading(false);
 | 
					            // setIsLoading(false);
 | 
				
			||||||
        })
 | 
					        });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        handleDebouncedChange();
 | 
					        handleDebouncedChange();
 | 
				
			||||||
    }, [debouncedValue]);
 | 
					    }, [debouncedValue]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        selectClient(clients.find(client =>
 | 
					        selectClient(
 | 
				
			||||||
                client.name.toLowerCase().trim() == value.toLowerCase().trim())
 | 
					            clients.find(
 | 
				
			||||||
            ||
 | 
					                client =>
 | 
				
			||||||
            {
 | 
					                    client.name.toLowerCase().trim() ==
 | 
				
			||||||
 | 
					                    value.toLowerCase().trim()
 | 
				
			||||||
 | 
					            ) || {
 | 
				
			||||||
                name: value,
 | 
					                name: value,
 | 
				
			||||||
                id: -1,
 | 
					                id: -1,
 | 
				
			||||||
                address: ""
 | 
					                address: "",
 | 
				
			||||||
            });
 | 
					            }
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
    }, [value]);
 | 
					    }, [value]);
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        if (!selectedClient) return;
 | 
					        if (!selectedClient) return;
 | 
				
			||||||
        if (onSelect) onSelect(selectedClient);
 | 
					        if (onSelect) onSelect(selectedClient);
 | 
				
			||||||
        if (nameRestProps?.onChange) nameRestProps.onChange(selectedClient.name);
 | 
					        if (nameRestProps?.onChange)
 | 
				
			||||||
 | 
					            nameRestProps.onChange(selectedClient.name);
 | 
				
			||||||
        if (addressRestProps?.onChange) {
 | 
					        if (addressRestProps?.onChange) {
 | 
				
			||||||
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
 | 
					            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
 | 
				
			||||||
            // @ts-expect-error
 | 
					            // @ts-expect-error
 | 
				
			||||||
@@ -59,25 +68,22 @@ const ClientAutocomplete: FC<Props> = ({onSelect, addressRestProps, nameRestProp
 | 
				
			|||||||
        <>
 | 
					        <>
 | 
				
			||||||
            <Autocomplete
 | 
					            <Autocomplete
 | 
				
			||||||
                {...nameRestProps}
 | 
					                {...nameRestProps}
 | 
				
			||||||
                placeholder={'Клиент'}
 | 
					                placeholder={"Клиент"}
 | 
				
			||||||
                onChange={handleChange}
 | 
					                onChange={handleChange}
 | 
				
			||||||
                value={value}
 | 
					                value={value}
 | 
				
			||||||
                data={clients.map(client => client.name)}
 | 
					                data={clients.map(client => client.name)}
 | 
				
			||||||
                styles={withAddress ? {
 | 
					                styles={
 | 
				
			||||||
                    input: {
 | 
					                    withAddress
 | 
				
			||||||
                        borderBottomLeftRadius: 0,
 | 
					                        ? {
 | 
				
			||||||
                        borderBottomRightRadius: 0
 | 
					                              input: {
 | 
				
			||||||
                    }
 | 
					                                  borderBottomLeftRadius: 0,
 | 
				
			||||||
                } : {}}
 | 
					                                  borderBottomRightRadius: 0,
 | 
				
			||||||
 | 
					                              },
 | 
				
			||||||
 | 
					                          }
 | 
				
			||||||
 | 
					                        : {}
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        </>
 | 
					        </>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
    )
 | 
					};
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
export default ClientAutocomplete;
 | 
					export default ClientAutocomplete;
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,31 +1,39 @@
 | 
				
			|||||||
import {FC} from "react";
 | 
					import { FC } from "react";
 | 
				
			||||||
import {Select} from "@mantine/core";
 | 
					import { Select } from "@mantine/core";
 | 
				
			||||||
import {ClientSchema} from "../../../client";
 | 
					import { ClientSchema } from "../../../client";
 | 
				
			||||||
import useClientsList from "../../../pages/ClientsPage/hooks/useClientsList.tsx";
 | 
					import useClientsList from "../../../pages/ClientsPage/hooks/useClientsList.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
    value?: ClientSchema;
 | 
					    value?: ClientSchema;
 | 
				
			||||||
    onChange: (client: ClientSchema) => void;
 | 
					    onChange: (client: ClientSchema) => void;
 | 
				
			||||||
    withLabel?: boolean;
 | 
					    withLabel?: boolean;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
const ClientSelect: FC<Props> = ({value, onChange, withLabel = false}) => {
 | 
					const ClientSelect: FC<Props> = ({ value, onChange, withLabel = false }) => {
 | 
				
			||||||
    const {clients} = useClientsList();
 | 
					    const { clients } = useClientsList();
 | 
				
			||||||
    const options = clients.map(client => ({label: client.name, value: client.id.toString()}))
 | 
					    const options = clients.map(client => ({
 | 
				
			||||||
 | 
					        label: client.name,
 | 
				
			||||||
 | 
					        value: client.id.toString(),
 | 
				
			||||||
 | 
					    }));
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Select
 | 
					        <Select
 | 
				
			||||||
            searchable
 | 
					            searchable
 | 
				
			||||||
            placeholder={"Выберите клиента"}
 | 
					            placeholder={"Выберите клиента"}
 | 
				
			||||||
            value={value && options.find(client => client.value == value.id.toString())?.value}
 | 
					            value={
 | 
				
			||||||
 | 
					                value &&
 | 
				
			||||||
 | 
					                options.find(client => client.value == value.id.toString())
 | 
				
			||||||
 | 
					                    ?.value
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            onChange={event => {
 | 
					            onChange={event => {
 | 
				
			||||||
                if (!event) return;
 | 
					                if (!event) return;
 | 
				
			||||||
                const client = clients.find(client => client.id == parseInt(event));
 | 
					                const client = clients.find(
 | 
				
			||||||
 | 
					                    client => client.id == parseInt(event)
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
                if (!client) return;
 | 
					                if (!client) return;
 | 
				
			||||||
                onChange(client);
 | 
					                onChange(client);
 | 
				
			||||||
            }}
 | 
					            }}
 | 
				
			||||||
            data={options}
 | 
					            data={options}
 | 
				
			||||||
            label={withLabel && "Клиент"}
 | 
					            label={withLabel && "Клиент"}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
export default ClientSelect;
 | 
					export default ClientSelect;
 | 
				
			||||||
@@ -1,18 +1,20 @@
 | 
				
			|||||||
import ObjectSelect, {ObjectSelectProps} from "../../ObjectSelect/ObjectSelect.tsx";
 | 
					import ObjectSelect, {
 | 
				
			||||||
import {ClientSchema} from "../../../client";
 | 
					    ObjectSelectProps,
 | 
				
			||||||
import {FC} from "react";
 | 
					} from "../../ObjectSelect/ObjectSelect.tsx";
 | 
				
			||||||
 | 
					import { ClientSchema } from "../../../client";
 | 
				
			||||||
 | 
					import { FC } from "react";
 | 
				
			||||||
import useClientsList from "../../../pages/ClientsPage/hooks/useClientsList.tsx";
 | 
					import useClientsList from "../../../pages/ClientsPage/hooks/useClientsList.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = Omit<ObjectSelectProps<ClientSchema>, 'data'>
 | 
					type Props = Omit<ObjectSelectProps<ClientSchema>, "data">;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ClientSelectNew: FC<Props> = (props) => {
 | 
					const ClientSelectNew: FC<Props> = props => {
 | 
				
			||||||
    const {clients} = useClientsList();
 | 
					    const { clients } = useClientsList();
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <ObjectSelect
 | 
					        <ObjectSelect
 | 
				
			||||||
            searchable
 | 
					            searchable
 | 
				
			||||||
            data={clients}
 | 
					            data={clients}
 | 
				
			||||||
            {...props}
 | 
					            {...props}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
export default ClientSelectNew;
 | 
					export default ClientSelectNew;
 | 
				
			||||||
@@ -1,19 +1,24 @@
 | 
				
			|||||||
import ObjectSelect, {ObjectSelectProps} from "../../ObjectSelect/ObjectSelect.tsx";
 | 
					import ObjectSelect, {
 | 
				
			||||||
import {PayRateSchema} from "../../../client";
 | 
					    ObjectSelectProps,
 | 
				
			||||||
import {FC} from "react";
 | 
					} from "../../ObjectSelect/ObjectSelect.tsx";
 | 
				
			||||||
 | 
					import { PayRateSchema } from "../../../client";
 | 
				
			||||||
 | 
					import { FC } from "react";
 | 
				
			||||||
import usePayRatesList from "../../../pages/AdminPage/hooks/usePayRatesList.tsx";
 | 
					import usePayRatesList from "../../../pages/AdminPage/hooks/usePayRatesList.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = Omit<ObjectSelectProps<PayRateSchema>, 'data' | 'getValueFn' | 'getLabelFn'>
 | 
					type Props = Omit<
 | 
				
			||||||
 | 
					    ObjectSelectProps<PayRateSchema>,
 | 
				
			||||||
 | 
					    "data" | "getValueFn" | "getLabelFn"
 | 
				
			||||||
 | 
					>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const PayRateSelect: FC<Props> = (props) => {
 | 
					const PayRateSelect: FC<Props> = props => {
 | 
				
			||||||
    const {objects: payRates} = usePayRatesList();
 | 
					    const { objects: payRates } = usePayRatesList();
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <ObjectSelect
 | 
					        <ObjectSelect
 | 
				
			||||||
            getValueFn={(baseMarketplace) => baseMarketplace.id.toLocaleString()}
 | 
					            getValueFn={baseMarketplace => baseMarketplace.id.toLocaleString()}
 | 
				
			||||||
            getLabelFn={(baseMarketplace) => baseMarketplace.name}
 | 
					            getLabelFn={baseMarketplace => baseMarketplace.name}
 | 
				
			||||||
            data={payRates}
 | 
					            data={payRates}
 | 
				
			||||||
            {...props}
 | 
					            {...props}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
export default PayRateSelect;
 | 
					export default PayRateSelect;
 | 
				
			||||||
@@ -1,19 +1,24 @@
 | 
				
			|||||||
import ObjectSelect, {ObjectSelectProps} from "../../ObjectSelect/ObjectSelect.tsx";
 | 
					import ObjectSelect, {
 | 
				
			||||||
import {PayrollSchemeSchema} from "../../../client";
 | 
					    ObjectSelectProps,
 | 
				
			||||||
import {FC} from "react";
 | 
					} from "../../ObjectSelect/ObjectSelect.tsx";
 | 
				
			||||||
 | 
					import { PayrollSchemeSchema } from "../../../client";
 | 
				
			||||||
 | 
					import { FC } from "react";
 | 
				
			||||||
import usePayrollSchemasList from "../../../hooks/usePayrollSchemasList.tsx";
 | 
					import usePayrollSchemasList from "../../../hooks/usePayrollSchemasList.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = Omit<ObjectSelectProps<PayrollSchemeSchema>, 'data' | 'getValueFn' | 'getLabelFn'>
 | 
					type Props = Omit<
 | 
				
			||||||
 | 
					    ObjectSelectProps<PayrollSchemeSchema>,
 | 
				
			||||||
 | 
					    "data" | "getValueFn" | "getLabelFn"
 | 
				
			||||||
 | 
					>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const PayrollSchemeSelect: FC<Props> = (props) => {
 | 
					const PayrollSchemeSelect: FC<Props> = props => {
 | 
				
			||||||
    const {objects: payrollSchemeSchemas} = usePayrollSchemasList();
 | 
					    const { objects: payrollSchemeSchemas } = usePayrollSchemasList();
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <ObjectSelect
 | 
					        <ObjectSelect
 | 
				
			||||||
            getValueFn={(baseMarketplace) => baseMarketplace.key}
 | 
					            getValueFn={baseMarketplace => baseMarketplace.key}
 | 
				
			||||||
            getLabelFn={(baseMarketplace) => baseMarketplace.name}
 | 
					            getLabelFn={baseMarketplace => baseMarketplace.name}
 | 
				
			||||||
            data={payrollSchemeSchemas}
 | 
					            data={payrollSchemeSchemas}
 | 
				
			||||||
            {...props}
 | 
					            {...props}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
export default PayrollSchemeSelect
 | 
					export default PayrollSchemeSelect;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,10 @@
 | 
				
			|||||||
import ObjectSelect, { ObjectSelectProps } from "../../ObjectSelect/ObjectSelect.tsx";
 | 
					import ObjectSelect, {
 | 
				
			||||||
 | 
					    ObjectSelectProps,
 | 
				
			||||||
 | 
					} from "../../ObjectSelect/ObjectSelect.tsx";
 | 
				
			||||||
import { ServicePriceCategorySchema } from "../../../client";
 | 
					import { ServicePriceCategorySchema } from "../../../client";
 | 
				
			||||||
import useServicePriceCategoriesList from "../../../pages/ServicesPage/hooks/useServicePriceCategoriesList.tsx";
 | 
					import useServicePriceCategoriesList from "../../../pages/ServicesPage/hooks/useServicePriceCategoriesList.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = Omit<ObjectSelectProps<ServicePriceCategorySchema>, "data">
 | 
					type Props = Omit<ObjectSelectProps<ServicePriceCategorySchema>, "data">;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ServicePriceCategorySelect = (props: Props) => {
 | 
					const ServicePriceCategorySelect = (props: Props) => {
 | 
				
			||||||
    const { objects } = useServicePriceCategoriesList();
 | 
					    const { objects } = useServicePriceCategoriesList();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,31 +1,36 @@
 | 
				
			|||||||
import {FC} from "react";
 | 
					import { FC } from "react";
 | 
				
			||||||
import ObjectSelect, {ObjectSelectProps} from "../../ObjectSelect/ObjectSelect.tsx";
 | 
					import ObjectSelect, {
 | 
				
			||||||
import {ServiceSchema} from "../../../client";
 | 
					    ObjectSelectProps,
 | 
				
			||||||
 | 
					} from "../../ObjectSelect/ObjectSelect.tsx";
 | 
				
			||||||
 | 
					import { ServiceSchema } from "../../../client";
 | 
				
			||||||
import useServicesList from "../../../pages/ServicesPage/hooks/useServicesList.tsx";
 | 
					import useServicesList from "../../../pages/ServicesPage/hooks/useServicesList.tsx";
 | 
				
			||||||
import {omit} from "lodash";
 | 
					import { omit } from "lodash";
 | 
				
			||||||
import {ServiceType} from "../../../shared/enums/ServiceType.ts";
 | 
					import { ServiceType } from "../../../shared/enums/ServiceType.ts";
 | 
				
			||||||
import {ComboboxItem, OptionsFilter} from "@mantine/core";
 | 
					import { ComboboxItem, OptionsFilter } from "@mantine/core";
 | 
				
			||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
 | 
					// eslint-disable-next-line @typescript-eslint/ban-ts-comment
 | 
				
			||||||
// @ts-expect-error
 | 
					// @ts-expect-error
 | 
				
			||||||
import {ComboboxParsedItemGroup} from "@mantine/core/lib/components/Combobox/Combobox.types";
 | 
					import { ComboboxParsedItemGroup } from "@mantine/core/lib/components/Combobox/Combobox.types";
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
type RestProps = {
 | 
					type RestProps = {
 | 
				
			||||||
    filterType?: ServiceType;
 | 
					    filterType?: ServiceType;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
type Props = Omit<ObjectSelectProps<ServiceSchema>, 'data'> & RestProps;
 | 
					type Props = Omit<ObjectSelectProps<ServiceSchema>, "data"> & RestProps;
 | 
				
			||||||
const ServiceSelectNew: FC<Props> = (props: Props) => {
 | 
					const ServiceSelectNew: FC<Props> = (props: Props) => {
 | 
				
			||||||
    const {services} = useServicesList();
 | 
					    const { services } = useServicesList();
 | 
				
			||||||
    const data = props.filterType ? services.filter(service => service.serviceType === props.filterType) : services;
 | 
					    const data = props.filterType
 | 
				
			||||||
 | 
					        ? services.filter(service => service.serviceType === props.filterType)
 | 
				
			||||||
 | 
					        : services;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const restProps = omit(props, ['filterType']);
 | 
					    const restProps = omit(props, ["filterType"]);
 | 
				
			||||||
    const optionsFilter: OptionsFilter = ({options, search}) => {
 | 
					    const optionsFilter: OptionsFilter = ({ options, search }) => {
 | 
				
			||||||
        return (options as ComboboxParsedItemGroup<ComboboxItem>[]).map((option) => {
 | 
					        return (options as ComboboxParsedItemGroup<ComboboxItem>[]).map(
 | 
				
			||||||
 | 
					            option => {
 | 
				
			||||||
                return {
 | 
					                return {
 | 
				
			||||||
                    ...option,
 | 
					                    ...option,
 | 
				
			||||||
                    items:
 | 
					                    items: option.items.filter((item: ComboboxItem) =>
 | 
				
			||||||
                        option.items.filter((item: ComboboxItem) => item.label.toLowerCase().includes(search.toLowerCase()))
 | 
					                        item.label.toLowerCase().includes(search.toLowerCase())
 | 
				
			||||||
                }
 | 
					                    ),
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
@@ -37,6 +42,6 @@ const ServiceSelectNew: FC<Props> = (props: Props) => {
 | 
				
			|||||||
            groupBy={item => item.category.name}
 | 
					            groupBy={item => item.category.name}
 | 
				
			||||||
            filter={optionsFilter}
 | 
					            filter={optionsFilter}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
export default ServiceSelectNew;
 | 
					export default ServiceSelectNew;
 | 
				
			||||||
@@ -1,17 +1,19 @@
 | 
				
			|||||||
import ObjectSelect, {ObjectSelectProps} from "../../ObjectSelect/ObjectSelect.tsx";
 | 
					import ObjectSelect, {
 | 
				
			||||||
import {GetServiceKitSchema} from "../../../client";
 | 
					    ObjectSelectProps,
 | 
				
			||||||
import {FC} from "react";
 | 
					} from "../../ObjectSelect/ObjectSelect.tsx";
 | 
				
			||||||
 | 
					import { GetServiceKitSchema } from "../../../client";
 | 
				
			||||||
 | 
					import { FC } from "react";
 | 
				
			||||||
import useServicesKitsList from "../../../pages/ServicesPage/hooks/useServicesKitsList.tsx";
 | 
					import useServicesKitsList from "../../../pages/ServicesPage/hooks/useServicesKitsList.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = Omit<ObjectSelectProps<GetServiceKitSchema>, 'data'>
 | 
					type Props = Omit<ObjectSelectProps<GetServiceKitSchema>, "data">;
 | 
				
			||||||
const ServicesKitSelect: FC<Props> = (props) => {
 | 
					const ServicesKitSelect: FC<Props> = props => {
 | 
				
			||||||
    const {objects} = useServicesKitsList();
 | 
					    const { objects } = useServicesKitsList();
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <ObjectSelect
 | 
					        <ObjectSelect
 | 
				
			||||||
            data={objects}
 | 
					            data={objects}
 | 
				
			||||||
            {...props}
 | 
					            {...props}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default ServicesKitSelect;
 | 
					export default ServicesKitSelect;
 | 
				
			||||||
@@ -1,16 +1,18 @@
 | 
				
			|||||||
import ObjectMultiSelect, {ObjectMultiSelectProps} from "../../ObjectMultiSelect/ObjectMultiSelect.tsx";
 | 
					import ObjectMultiSelect, {
 | 
				
			||||||
import {ServiceSchema} from "../../../client";
 | 
					    ObjectMultiSelectProps,
 | 
				
			||||||
import {FC} from "react";
 | 
					} from "../../ObjectMultiSelect/ObjectMultiSelect.tsx";
 | 
				
			||||||
 | 
					import { ServiceSchema } from "../../../client";
 | 
				
			||||||
 | 
					import { FC } from "react";
 | 
				
			||||||
import useServicesList from "../../../pages/ServicesPage/hooks/useServicesList.tsx";
 | 
					import useServicesList from "../../../pages/ServicesPage/hooks/useServicesList.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = Omit<ObjectMultiSelectProps<ServiceSchema>, 'data'>
 | 
					type Props = Omit<ObjectMultiSelectProps<ServiceSchema>, "data">;
 | 
				
			||||||
const ServicesMultiselect: FC<Props> = (props: Props) => {
 | 
					const ServicesMultiselect: FC<Props> = (props: Props) => {
 | 
				
			||||||
    const {services} = useServicesList();
 | 
					    const { services } = useServicesList();
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <ObjectMultiSelect
 | 
					        <ObjectMultiSelect
 | 
				
			||||||
            data={services}
 | 
					            data={services}
 | 
				
			||||||
            {...props}
 | 
					            {...props}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
export default ServicesMultiselect;
 | 
					export default ServicesMultiselect;
 | 
				
			||||||
@@ -1,17 +1,19 @@
 | 
				
			|||||||
import ObjectAutocomplete, {ObjectAutocompleteProps} from "../../ObjectAutocomplete/ObjectAutocomplete.tsx";
 | 
					import ObjectAutocomplete, {
 | 
				
			||||||
 | 
					    ObjectAutocompleteProps,
 | 
				
			||||||
 | 
					} from "../../ObjectAutocomplete/ObjectAutocomplete.tsx";
 | 
				
			||||||
import useShippingWarehousesList from "./hooks/useShippingWarehousesList.tsx";
 | 
					import useShippingWarehousesList from "./hooks/useShippingWarehousesList.tsx";
 | 
				
			||||||
import {FC} from "react";
 | 
					import { FC } from "react";
 | 
				
			||||||
import {ShippingWarehouseSchema} from "../../../client";
 | 
					import { ShippingWarehouseSchema } from "../../../client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = Omit<ObjectAutocompleteProps<ShippingWarehouseSchema>, 'data'>;
 | 
					type Props = Omit<ObjectAutocompleteProps<ShippingWarehouseSchema>, "data">;
 | 
				
			||||||
const ShippingWarehouseAutocomplete: FC<Props> = (props) => {
 | 
					const ShippingWarehouseAutocomplete: FC<Props> = props => {
 | 
				
			||||||
    const {shippingWarehouses} = useShippingWarehousesList();
 | 
					    const { shippingWarehouses } = useShippingWarehousesList();
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <ObjectAutocomplete
 | 
					        <ObjectAutocomplete
 | 
				
			||||||
            {...props}
 | 
					            {...props}
 | 
				
			||||||
            data={shippingWarehouses}
 | 
					            data={shippingWarehouses}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default ShippingWarehouseAutocomplete;
 | 
					export default ShippingWarehouseAutocomplete;
 | 
				
			||||||
@@ -1,13 +1,14 @@
 | 
				
			|||||||
import {useQuery} from "@tanstack/react-query";
 | 
					import { useQuery } from "@tanstack/react-query";
 | 
				
			||||||
import {ShippingWarehouseService} from "../../../../client";
 | 
					import { ShippingWarehouseService } from "../../../../client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const useShippingWarehousesList = () => {
 | 
					const useShippingWarehousesList = () => {
 | 
				
			||||||
    const {isPending, error, data, refetch} = useQuery({
 | 
					    const { isPending, error, data, refetch } = useQuery({
 | 
				
			||||||
        queryKey: ['getAllShippingWarehouses'],
 | 
					        queryKey: ["getAllShippingWarehouses"],
 | 
				
			||||||
        queryFn: ShippingWarehouseService.getAllShippingWarehouses
 | 
					        queryFn: ShippingWarehouseService.getAllShippingWarehouses,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    const shippingWarehouses = isPending || error || !data ? [] : data.shippingWarehouses;
 | 
					    const shippingWarehouses =
 | 
				
			||||||
 | 
					        isPending || error || !data ? [] : data.shippingWarehouses;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return {shippingWarehouses, refetch}
 | 
					    return { shippingWarehouses, refetch };
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
export default useShippingWarehousesList;
 | 
					export default useShippingWarehousesList;
 | 
				
			||||||
@@ -1,16 +1,23 @@
 | 
				
			|||||||
import ObjectSelect, {ObjectSelectProps} from "../../ObjectSelect/ObjectSelect.tsx";
 | 
					import ObjectSelect, {
 | 
				
			||||||
import {UserSchema} from "../../../client";
 | 
					    ObjectSelectProps,
 | 
				
			||||||
import {FC} from "react";
 | 
					} from "../../ObjectSelect/ObjectSelect.tsx";
 | 
				
			||||||
 | 
					import { UserSchema } from "../../../client";
 | 
				
			||||||
 | 
					import { FC } from "react";
 | 
				
			||||||
import useUsersList from "../../../pages/AdminPage/hooks/useUsersList.tsx";
 | 
					import useUsersList from "../../../pages/AdminPage/hooks/useUsersList.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = Omit<ObjectSelectProps<UserSchema>, 'data' | 'getValueFn' | 'getLabelFn'>
 | 
					type Props = Omit<
 | 
				
			||||||
const UserSelect: FC<Props> = (props) => {
 | 
					    ObjectSelectProps<UserSchema>,
 | 
				
			||||||
    const {objects: users} = useUsersList();
 | 
					    "data" | "getValueFn" | "getLabelFn"
 | 
				
			||||||
    return (<ObjectSelect
 | 
					>;
 | 
				
			||||||
        data={users}
 | 
					const UserSelect: FC<Props> = props => {
 | 
				
			||||||
        getLabelFn={(user) => `${user.firstName} ${user.secondName}`}
 | 
					    const { objects: users } = useUsersList();
 | 
				
			||||||
        getValueFn={(user) => user.id.toString()}
 | 
					    return (
 | 
				
			||||||
        {...props}
 | 
					        <ObjectSelect
 | 
				
			||||||
    />)
 | 
					            data={users}
 | 
				
			||||||
}
 | 
					            getLabelFn={user => `${user.firstName} ${user.secondName}`}
 | 
				
			||||||
 | 
					            getValueFn={user => user.id.toString()}
 | 
				
			||||||
 | 
					            {...props}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
export default UserSelect;
 | 
					export default UserSelect;
 | 
				
			||||||
@@ -1,48 +1,66 @@
 | 
				
			|||||||
import {ServiceSchema} from "../../client";
 | 
					import { ServiceSchema } from "../../client";
 | 
				
			||||||
import {FC, useEffect, useMemo, useState} from "react";
 | 
					import { FC, useEffect, useMemo, useState } from "react";
 | 
				
			||||||
import {Select, SelectProps} from "@mantine/core";
 | 
					import { Select, SelectProps } from "@mantine/core";
 | 
				
			||||||
import useServicesList from "../../pages/ServicesPage/hooks/useServicesList.tsx";
 | 
					import useServicesList from "../../pages/ServicesPage/hooks/useServicesList.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ControlledValueProps = {
 | 
					type ControlledValueProps = {
 | 
				
			||||||
    value: ServiceSchema;
 | 
					    value: ServiceSchema;
 | 
				
			||||||
    onChange: (value: ServiceSchema) => void;
 | 
					    onChange: (value: ServiceSchema) => void;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
type RestProps = {
 | 
					type RestProps = {
 | 
				
			||||||
    defaultValue?: ServiceSchema;
 | 
					    defaultValue?: ServiceSchema;
 | 
				
			||||||
    onChange: (value: ServiceSchema) => void;
 | 
					    onChange: (value: ServiceSchema) => void;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
type Props = (RestProps & Partial<ControlledValueProps>) & Omit<SelectProps, 'value' | 'onChange'>;
 | 
					type Props = (RestProps & Partial<ControlledValueProps>) &
 | 
				
			||||||
 | 
					    Omit<SelectProps, "value" | "onChange">;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ServiceSelect: FC<Props> = props => {
 | 
				
			||||||
const ServiceSelect: FC<Props> = (props) => {
 | 
					 | 
				
			||||||
    const isControlled = props.value !== undefined;
 | 
					    const isControlled = props.value !== undefined;
 | 
				
			||||||
    const [internalValue, setInternalValue] = useState<ServiceSchema | undefined>(props.defaultValue);
 | 
					    const [internalValue, setInternalValue] = useState<
 | 
				
			||||||
 | 
					        ServiceSchema | undefined
 | 
				
			||||||
 | 
					    >(props.defaultValue);
 | 
				
			||||||
    const value = isControlled ? props.value : internalValue;
 | 
					    const value = isControlled ? props.value : internalValue;
 | 
				
			||||||
    const {services} = useServicesList();
 | 
					    const { services } = useServicesList();
 | 
				
			||||||
    const categories = useMemo(() => services.reduce((acc, service) => {
 | 
					    const categories = useMemo(
 | 
				
			||||||
        if (!acc.includes(service.category.name)) {
 | 
					        () =>
 | 
				
			||||||
            acc.push(service.category.name);
 | 
					            services.reduce((acc, service) => {
 | 
				
			||||||
        }
 | 
					                if (!acc.includes(service.category.name)) {
 | 
				
			||||||
        return acc;
 | 
					                    acc.push(service.category.name);
 | 
				
			||||||
    }, [] as string[]), [services]);
 | 
					                }
 | 
				
			||||||
 | 
					                return acc;
 | 
				
			||||||
    const data = useMemo(() => categories.map(category => ({
 | 
					            }, [] as string[]),
 | 
				
			||||||
        group: category,
 | 
					        [services]
 | 
				
			||||||
        items: services.filter(service => service.category.name === category)
 | 
					    );
 | 
				
			||||||
            .map(service => ({
 | 
					 | 
				
			||||||
                value: service.id.toString(),
 | 
					 | 
				
			||||||
                label: service.name
 | 
					 | 
				
			||||||
            }))
 | 
					 | 
				
			||||||
    })), [services, categories]);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const data = useMemo(
 | 
				
			||||||
 | 
					        () =>
 | 
				
			||||||
 | 
					            categories.map(category => ({
 | 
				
			||||||
 | 
					                group: category,
 | 
				
			||||||
 | 
					                items: services
 | 
				
			||||||
 | 
					                    .filter(service => service.category.name === category)
 | 
				
			||||||
 | 
					                    .map(service => ({
 | 
				
			||||||
 | 
					                        value: service.id.toString(),
 | 
				
			||||||
 | 
					                        label: service.name,
 | 
				
			||||||
 | 
					                    })),
 | 
				
			||||||
 | 
					            })),
 | 
				
			||||||
 | 
					        [services, categories]
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const handleOnChange = (value: string) => {
 | 
					    const handleOnChange = (value: string) => {
 | 
				
			||||||
        if (isControlled) {
 | 
					        if (isControlled) {
 | 
				
			||||||
            props.onChange(services.find(service => service.id.toString() === value) as ServiceSchema);
 | 
					            props.onChange(
 | 
				
			||||||
 | 
					                services.find(
 | 
				
			||||||
 | 
					                    service => service.id.toString() === value
 | 
				
			||||||
 | 
					                ) as ServiceSchema
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        setInternalValue(services.find(service => service.id.toString() === value) as ServiceSchema);
 | 
					        setInternalValue(
 | 
				
			||||||
    }
 | 
					            services.find(
 | 
				
			||||||
 | 
					                service => service.id.toString() === value
 | 
				
			||||||
 | 
					            ) as ServiceSchema
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        if (!isControlled) {
 | 
					        if (!isControlled) {
 | 
				
			||||||
            props.onChange(internalValue as ServiceSchema);
 | 
					            props.onChange(internalValue as ServiceSchema);
 | 
				
			||||||
@@ -57,7 +75,7 @@ const ServiceSelect: FC<Props> = (props) => {
 | 
				
			|||||||
            onChange={event => event && handleOnChange(event)}
 | 
					            onChange={event => event && handleOnChange(event)}
 | 
				
			||||||
            data={data}
 | 
					            data={data}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default ServiceSelect;
 | 
					export default ServiceSelect;
 | 
				
			||||||
@@ -1,6 +1,12 @@
 | 
				
			|||||||
import { ObjectSelectProps } from "../ObjectSelect/ObjectSelect.tsx";
 | 
					import { ObjectSelectProps } from "../ObjectSelect/ObjectSelect.tsx";
 | 
				
			||||||
import { ServicePriceCategorySchema, ServiceSchema } from "../../client";
 | 
					import { ServicePriceCategorySchema, ServiceSchema } from "../../client";
 | 
				
			||||||
import { Flex, FlexProps, NumberInput, NumberInputProps, rem } from "@mantine/core";
 | 
					import {
 | 
				
			||||||
 | 
					    Flex,
 | 
				
			||||||
 | 
					    FlexProps,
 | 
				
			||||||
 | 
					    NumberInput,
 | 
				
			||||||
 | 
					    NumberInputProps,
 | 
				
			||||||
 | 
					    rem,
 | 
				
			||||||
 | 
					} from "@mantine/core";
 | 
				
			||||||
import { FC, useEffect, useRef, useState } from "react";
 | 
					import { FC, useEffect, useRef, useState } from "react";
 | 
				
			||||||
import ServiceSelectNew from "../Selects/ServiceSelectNew/ServiceSelectNew.tsx";
 | 
					import ServiceSelectNew from "../Selects/ServiceSelectNew/ServiceSelectNew.tsx";
 | 
				
			||||||
import { ServiceType } from "../../shared/enums/ServiceType.ts";
 | 
					import { ServiceType } from "../../shared/enums/ServiceType.ts";
 | 
				
			||||||
@@ -9,38 +15,47 @@ type ServiceProps = Omit<ObjectSelectProps<ServiceSchema>, "data">;
 | 
				
			|||||||
type PriceProps = NumberInputProps;
 | 
					type PriceProps = NumberInputProps;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
    serviceProps: ServiceProps,
 | 
					    serviceProps: ServiceProps;
 | 
				
			||||||
    priceProps: PriceProps,
 | 
					    priceProps: PriceProps;
 | 
				
			||||||
    quantity: number,
 | 
					    quantity: number;
 | 
				
			||||||
    containerProps: FlexProps,
 | 
					    containerProps: FlexProps;
 | 
				
			||||||
    filterType?: ServiceType,
 | 
					    filterType?: ServiceType;
 | 
				
			||||||
    lockOnEdit?: boolean
 | 
					    lockOnEdit?: boolean;
 | 
				
			||||||
    category?: ServicePriceCategorySchema
 | 
					    category?: ServicePriceCategorySchema;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
const ServiceWithPriceInput: FC<Props> = ({
 | 
					const ServiceWithPriceInput: FC<Props> = ({
 | 
				
			||||||
                                              serviceProps,
 | 
					    serviceProps,
 | 
				
			||||||
                                              priceProps,
 | 
					    priceProps,
 | 
				
			||||||
                                              quantity,
 | 
					    quantity,
 | 
				
			||||||
                                              containerProps,
 | 
					    containerProps,
 | 
				
			||||||
                                              filterType = ServiceType.PRODUCT_SERVICE,
 | 
					    filterType = ServiceType.PRODUCT_SERVICE,
 | 
				
			||||||
                                              lockOnEdit = true,
 | 
					    lockOnEdit = true,
 | 
				
			||||||
                                              category,
 | 
					    category,
 | 
				
			||||||
                                          }) => {
 | 
					}) => {
 | 
				
			||||||
    const [price, setPrice] = useState<number | undefined>(
 | 
					    const [price, setPrice] = useState<number | undefined>(
 | 
				
			||||||
        typeof priceProps.value === "number" ? priceProps.value : undefined);
 | 
					        typeof priceProps.value === "number" ? priceProps.value : undefined
 | 
				
			||||||
    const [service, setService] = useState<ServiceSchema | undefined>(serviceProps.value);
 | 
					    );
 | 
				
			||||||
 | 
					    const [service, setService] = useState<ServiceSchema | undefined>(
 | 
				
			||||||
 | 
					        serviceProps.value
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
    const isFirstRender = useRef(true);
 | 
					    const isFirstRender = useRef(true);
 | 
				
			||||||
    const setPriceBasedOnQuantity = (): boolean => {
 | 
					    const setPriceBasedOnQuantity = (): boolean => {
 | 
				
			||||||
        if (!service || !service.priceRanges.length) return false;
 | 
					        if (!service || !service.priceRanges.length) return false;
 | 
				
			||||||
        const range = service.priceRanges.find(priceRange =>
 | 
					        const range =
 | 
				
			||||||
            quantity >= priceRange.fromQuantity && quantity <= priceRange.toQuantity) || service.priceRanges[0];
 | 
					            service.priceRanges.find(
 | 
				
			||||||
 | 
					                priceRange =>
 | 
				
			||||||
 | 
					                    quantity >= priceRange.fromQuantity &&
 | 
				
			||||||
 | 
					                    quantity <= priceRange.toQuantity
 | 
				
			||||||
 | 
					            ) || service.priceRanges[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        setPrice(range.price);
 | 
					        setPrice(range.price);
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    const setPriceBasedOnCategory = () => {
 | 
					    const setPriceBasedOnCategory = () => {
 | 
				
			||||||
        if (!category || !service) return false;
 | 
					        if (!category || !service) return false;
 | 
				
			||||||
        const categoryPrice = service.categoryPrices.find(categoryPrice => categoryPrice.category.id === category.id);
 | 
					        const categoryPrice = service.categoryPrices.find(
 | 
				
			||||||
 | 
					            categoryPrice => categoryPrice.category.id === category.id
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
        if (!categoryPrice) return false;
 | 
					        if (!categoryPrice) return false;
 | 
				
			||||||
        setPrice(categoryPrice.price);
 | 
					        setPrice(categoryPrice.price);
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
@@ -84,14 +99,11 @@ const ServiceWithPriceInput: FC<Props> = ({
 | 
				
			|||||||
        isFirstRender.current = false;
 | 
					        isFirstRender.current = false;
 | 
				
			||||||
    }, []);
 | 
					    }, []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Flex
 | 
					        <Flex
 | 
				
			||||||
            align={"center"}
 | 
					            align={"center"}
 | 
				
			||||||
            gap={rem(10)}
 | 
					            gap={rem(10)}
 | 
				
			||||||
            {...containerProps}
 | 
					            {...containerProps}>
 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            {/*<ActionIcon variant={"default"}>*/}
 | 
					            {/*<ActionIcon variant={"default"}>*/}
 | 
				
			||||||
            {/*<IconReload onClick={() => onReload()}/>*/}
 | 
					            {/*<IconReload onClick={() => onReload()}/>*/}
 | 
				
			||||||
            {/*</ActionIcon>*/}
 | 
					            {/*</ActionIcon>*/}
 | 
				
			||||||
@@ -108,7 +120,6 @@ const ServiceWithPriceInput: FC<Props> = ({
 | 
				
			|||||||
                // value={price}
 | 
					                // value={price}
 | 
				
			||||||
                defaultValue={priceProps.value}
 | 
					                defaultValue={priceProps.value}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
 | 
					 | 
				
			||||||
        </Flex>
 | 
					        </Flex>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,90 +1,93 @@
 | 
				
			|||||||
import PropTypes from 'prop-types'
 | 
					import PropTypes from "prop-types";
 | 
				
			||||||
import React, { useRef, useEffect } from 'react'
 | 
					import React, { useRef, useEffect } from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface TelegramUser {
 | 
					export interface TelegramUser {
 | 
				
			||||||
    id: number
 | 
					    id: number;
 | 
				
			||||||
    first_name: string
 | 
					    first_name: string;
 | 
				
			||||||
    username: string
 | 
					    username: string;
 | 
				
			||||||
    photo_url: string
 | 
					    photo_url: string;
 | 
				
			||||||
    auth_date: number
 | 
					    auth_date: number;
 | 
				
			||||||
    hash: string
 | 
					    hash: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Props {
 | 
					interface Props {
 | 
				
			||||||
    botName: string
 | 
					    botName: string;
 | 
				
			||||||
    usePic?: boolean
 | 
					    usePic?: boolean;
 | 
				
			||||||
    className?: string
 | 
					    className?: string;
 | 
				
			||||||
    cornerRadius?: number
 | 
					    cornerRadius?: number;
 | 
				
			||||||
    requestAccess?: boolean
 | 
					    requestAccess?: boolean;
 | 
				
			||||||
    dataAuthUrl?: string
 | 
					    dataAuthUrl?: string;
 | 
				
			||||||
    dataOnauth?: (user: TelegramUser) => void
 | 
					    dataOnauth?: (user: TelegramUser) => void;
 | 
				
			||||||
    buttonSize?: 'large' | 'medium' | 'small'
 | 
					    buttonSize?: "large" | "medium" | "small";
 | 
				
			||||||
    wrapperProps?: React.HTMLProps<HTMLDivElement>
 | 
					    wrapperProps?: React.HTMLProps<HTMLDivElement>;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare global {
 | 
					declare global {
 | 
				
			||||||
    interface Window {
 | 
					    interface Window {
 | 
				
			||||||
        TelegramLoginWidget: {
 | 
					        TelegramLoginWidget: {
 | 
				
			||||||
            dataOnauth: (user: TelegramUser) => void
 | 
					            dataOnauth: (user: TelegramUser) => void;
 | 
				
			||||||
        }
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const TelegramLoginButton: React.FC<Props> = ({
 | 
					const TelegramLoginButton: React.FC<Props> = ({
 | 
				
			||||||
                                                  wrapperProps,
 | 
					    wrapperProps,
 | 
				
			||||||
                                                  dataAuthUrl,
 | 
					    dataAuthUrl,
 | 
				
			||||||
                                                  usePic = false,
 | 
					    usePic = false,
 | 
				
			||||||
                                                  botName,
 | 
					    botName,
 | 
				
			||||||
                                                  className,
 | 
					    className,
 | 
				
			||||||
                                                  buttonSize = 'large',
 | 
					    buttonSize = "large",
 | 
				
			||||||
                                                  dataOnauth,
 | 
					    dataOnauth,
 | 
				
			||||||
                                                  cornerRadius,
 | 
					    cornerRadius,
 | 
				
			||||||
                                                  requestAccess = true
 | 
					    requestAccess = true,
 | 
				
			||||||
                                              }) => {
 | 
					}) => {
 | 
				
			||||||
    const ref = useRef<HTMLDivElement>(null)
 | 
					    const ref = useRef<HTMLDivElement>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        if (ref.current === null) return
 | 
					        if (ref.current === null) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (
 | 
					        if (
 | 
				
			||||||
            typeof dataOnauth === 'undefined' &&
 | 
					            typeof dataOnauth === "undefined" &&
 | 
				
			||||||
            typeof dataAuthUrl === 'undefined'
 | 
					            typeof dataAuthUrl === "undefined"
 | 
				
			||||||
        ) {
 | 
					        ) {
 | 
				
			||||||
            throw new Error(
 | 
					            throw new Error(
 | 
				
			||||||
                'One of this props should be defined: dataAuthUrl (redirect URL), dataOnauth (callback fn) should be defined.'
 | 
					                "One of this props should be defined: dataAuthUrl (redirect URL), dataOnauth (callback fn) should be defined."
 | 
				
			||||||
            )
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (typeof dataOnauth === 'function') {
 | 
					        if (typeof dataOnauth === "function") {
 | 
				
			||||||
            window.TelegramLoginWidget = {
 | 
					            window.TelegramLoginWidget = {
 | 
				
			||||||
                dataOnauth: (user: TelegramUser) => dataOnauth(user)
 | 
					                dataOnauth: (user: TelegramUser) => dataOnauth(user),
 | 
				
			||||||
            }
 | 
					            };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const script = document.createElement('script')
 | 
					        const script = document.createElement("script");
 | 
				
			||||||
        script.src = 'https://telegram.org/js/telegram-widget.js?22'
 | 
					        script.src = "https://telegram.org/js/telegram-widget.js?22";
 | 
				
			||||||
        script.setAttribute('data-telegram-login', botName)
 | 
					        script.setAttribute("data-telegram-login", botName);
 | 
				
			||||||
        script.setAttribute('data-size', buttonSize)
 | 
					        script.setAttribute("data-size", buttonSize);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (cornerRadius !== undefined) {
 | 
					        if (cornerRadius !== undefined) {
 | 
				
			||||||
            script.setAttribute('data-radius', cornerRadius.toString())
 | 
					            script.setAttribute("data-radius", cornerRadius.toString());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (requestAccess) {
 | 
					        if (requestAccess) {
 | 
				
			||||||
            script.setAttribute('data-request-access', 'write')
 | 
					            script.setAttribute("data-request-access", "write");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        script.setAttribute('data-userpic', usePic.toString())
 | 
					        script.setAttribute("data-userpic", usePic.toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (typeof dataAuthUrl === 'string') {
 | 
					        if (typeof dataAuthUrl === "string") {
 | 
				
			||||||
            script.setAttribute('data-auth-url', dataAuthUrl)
 | 
					            script.setAttribute("data-auth-url", dataAuthUrl);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            script.setAttribute('data-onauth', 'TelegramLoginWidget.dataOnauth(user)')
 | 
					            script.setAttribute(
 | 
				
			||||||
 | 
					                "data-onauth",
 | 
				
			||||||
 | 
					                "TelegramLoginWidget.dataOnauth(user)"
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        script.async = true
 | 
					        script.async = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ref.current.appendChild(script)
 | 
					        ref.current.appendChild(script);
 | 
				
			||||||
    }, [
 | 
					    }, [
 | 
				
			||||||
        botName,
 | 
					        botName,
 | 
				
			||||||
        buttonSize,
 | 
					        buttonSize,
 | 
				
			||||||
@@ -93,11 +96,17 @@ const TelegramLoginButton: React.FC<Props> = ({
 | 
				
			|||||||
        requestAccess,
 | 
					        requestAccess,
 | 
				
			||||||
        usePic,
 | 
					        usePic,
 | 
				
			||||||
        ref,
 | 
					        ref,
 | 
				
			||||||
        dataAuthUrl
 | 
					        dataAuthUrl,
 | 
				
			||||||
    ])
 | 
					    ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return <div ref={ref} className={className} {...wrapperProps} />
 | 
					    return (
 | 
				
			||||||
}
 | 
					        <div
 | 
				
			||||||
 | 
					            ref={ref}
 | 
				
			||||||
 | 
					            className={className}
 | 
				
			||||||
 | 
					            {...wrapperProps}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TelegramLoginButton.propTypes = {
 | 
					TelegramLoginButton.propTypes = {
 | 
				
			||||||
    botName: PropTypes.string.isRequired,
 | 
					    botName: PropTypes.string.isRequired,
 | 
				
			||||||
@@ -108,7 +117,7 @@ TelegramLoginButton.propTypes = {
 | 
				
			|||||||
    wrapperProps: PropTypes.object,
 | 
					    wrapperProps: PropTypes.object,
 | 
				
			||||||
    dataOnauth: PropTypes.func,
 | 
					    dataOnauth: PropTypes.func,
 | 
				
			||||||
    dataAuthUrl: PropTypes.string,
 | 
					    dataAuthUrl: PropTypes.string,
 | 
				
			||||||
    buttonSize: PropTypes.oneOf(['large', 'medium', 'small'])
 | 
					    buttonSize: PropTypes.oneOf(["large", "medium", "small"]),
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default TelegramLoginButton
 | 
					export default TelegramLoginButton;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,59 +1,75 @@
 | 
				
			|||||||
import cx from 'clsx';
 | 
					import cx from "clsx";
 | 
				
			||||||
import {Text} from '@mantine/core';
 | 
					import { Text } from "@mantine/core";
 | 
				
			||||||
import {useListState} from '@mantine/hooks';
 | 
					import { useListState } from "@mantine/hooks";
 | 
				
			||||||
import {DragDropContext, Droppable, Draggable} from '@hello-pangea/dnd';
 | 
					import { DragDropContext, Droppable, Draggable } from "@hello-pangea/dnd";
 | 
				
			||||||
import classes from './DndList.module.css';
 | 
					import classes from "./DndList.module.css";
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const data = [
 | 
					const data = [
 | 
				
			||||||
    {position: 6, mass: 12.011, symbol: 'C', name: 'Carbon'},
 | 
					    { position: 6, mass: 12.011, symbol: "C", name: "Carbon" },
 | 
				
			||||||
    {position: 7, mass: 14.007, symbol: 'N', name: 'Nitrogen'},
 | 
					    { position: 7, mass: 14.007, symbol: "N", name: "Nitrogen" },
 | 
				
			||||||
    {position: 39, mass: 88.906, symbol: 'Y', name: 'Yttrium'},
 | 
					    { position: 39, mass: 88.906, symbol: "Y", name: "Yttrium" },
 | 
				
			||||||
    {position: 56, mass: 137.33, symbol: 'Ba', name: 'Barium'},
 | 
					    { position: 56, mass: 137.33, symbol: "Ba", name: "Barium" },
 | 
				
			||||||
    {position: 58, mass: 140.12, symbol: 'Ce', name: 'Cerium'},
 | 
					    { position: 58, mass: 140.12, symbol: "Ce", name: "Cerium" },
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function DndList() {
 | 
					export function DndList() {
 | 
				
			||||||
    const [state, handlers] = useListState(data);
 | 
					    const [state, handlers] = useListState(data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const items = (listIndex: number) => state.map((item, index) => (
 | 
					    const items = (listIndex: number) =>
 | 
				
			||||||
        <Draggable key={item.symbol + `${listIndex}`} index={index} draggableId={item.symbol + `${listIndex}`}>
 | 
					        state.map((item, index) => (
 | 
				
			||||||
            {(provided, snapshot) => (
 | 
					            <Draggable
 | 
				
			||||||
                <div
 | 
					                key={item.symbol + `${listIndex}`}
 | 
				
			||||||
                    className={cx(classes.item, {[classes.itemDragging]: snapshot.isDragging})}
 | 
					                index={index}
 | 
				
			||||||
                    {...provided.draggableProps}
 | 
					                draggableId={item.symbol + `${listIndex}`}>
 | 
				
			||||||
                    {...provided.dragHandleProps}
 | 
					                {(provided, snapshot) => (
 | 
				
			||||||
                    ref={provided.innerRef}
 | 
					                    <div
 | 
				
			||||||
                >
 | 
					                        className={cx(classes.item, {
 | 
				
			||||||
                    <Text className={classes.symbol}>{item.symbol}</Text>
 | 
					                            [classes.itemDragging]: snapshot.isDragging,
 | 
				
			||||||
                    <div>
 | 
					                        })}
 | 
				
			||||||
                        <Text>{item.name}</Text>
 | 
					                        {...provided.draggableProps}
 | 
				
			||||||
                        <Text c="dimmed" size="sm">
 | 
					                        {...provided.dragHandleProps}
 | 
				
			||||||
                            Position: {item.position} • Mass: {item.mass}
 | 
					                        ref={provided.innerRef}>
 | 
				
			||||||
                        </Text>
 | 
					                        <Text className={classes.symbol}>{item.symbol}</Text>
 | 
				
			||||||
 | 
					                        <div>
 | 
				
			||||||
 | 
					                            <Text>{item.name}</Text>
 | 
				
			||||||
 | 
					                            <Text
 | 
				
			||||||
 | 
					                                c="dimmed"
 | 
				
			||||||
 | 
					                                size="sm">
 | 
				
			||||||
 | 
					                                Position: {item.position} • Mass: {item.mass}
 | 
				
			||||||
 | 
					                            </Text>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </div>
 | 
					                )}
 | 
				
			||||||
            )}
 | 
					            </Draggable>
 | 
				
			||||||
        </Draggable>
 | 
					        ));
 | 
				
			||||||
    ));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <DragDropContext
 | 
					        <DragDropContext
 | 
				
			||||||
            onDragEnd={({destination, source}) =>
 | 
					            onDragEnd={({ destination, source }) =>
 | 
				
			||||||
                handlers.reorder({from: source.index, to: destination?.index || 0})
 | 
					                handlers.reorder({
 | 
				
			||||||
            }
 | 
					                    from: source.index,
 | 
				
			||||||
        >
 | 
					                    to: destination?.index || 0,
 | 
				
			||||||
            <Droppable droppableId="dnd-list" direction="vertical">
 | 
					                })
 | 
				
			||||||
                {(provided) => (
 | 
					            }>
 | 
				
			||||||
                    <div {...provided.droppableProps} ref={provided.innerRef}>
 | 
					            <Droppable
 | 
				
			||||||
 | 
					                droppableId="dnd-list"
 | 
				
			||||||
 | 
					                direction="vertical">
 | 
				
			||||||
 | 
					                {provided => (
 | 
				
			||||||
 | 
					                    <div
 | 
				
			||||||
 | 
					                        {...provided.droppableProps}
 | 
				
			||||||
 | 
					                        ref={provided.innerRef}>
 | 
				
			||||||
                        {items(1)}
 | 
					                        {items(1)}
 | 
				
			||||||
                        {provided.placeholder}
 | 
					                        {provided.placeholder}
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                )}
 | 
					                )}
 | 
				
			||||||
            </Droppable>
 | 
					            </Droppable>
 | 
				
			||||||
            <Droppable droppableId="dnd-list-2" direction="vertical">
 | 
					            <Droppable
 | 
				
			||||||
                {(provided) => (
 | 
					                droppableId="dnd-list-2"
 | 
				
			||||||
                    <div {...provided.droppableProps} ref={provided.innerRef}>
 | 
					                direction="vertical">
 | 
				
			||||||
 | 
					                {provided => (
 | 
				
			||||||
 | 
					                    <div
 | 
				
			||||||
 | 
					                        {...provided.droppableProps}
 | 
				
			||||||
 | 
					                        ref={provided.innerRef}>
 | 
				
			||||||
                        {items(2)}
 | 
					                        {items(2)}
 | 
				
			||||||
                        {provided.placeholder}
 | 
					                        {provided.placeholder}
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
import {createSlice, PayloadAction} from "@reduxjs/toolkit";
 | 
					import { createSlice, PayloadAction } from "@reduxjs/toolkit";
 | 
				
			||||||
import {jwtDecode, JwtPayload} from "jwt-decode";
 | 
					import { jwtDecode, JwtPayload } from "jwt-decode";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface AuthState {
 | 
					interface AuthState {
 | 
				
			||||||
    isAuthorized: boolean;
 | 
					    isAuthorized: boolean;
 | 
				
			||||||
@@ -15,34 +15,35 @@ const initialState = (): AuthState => {
 | 
				
			|||||||
    return {
 | 
					    return {
 | 
				
			||||||
        accessToken: "",
 | 
					        accessToken: "",
 | 
				
			||||||
        isAuthorized: false,
 | 
					        isAuthorized: false,
 | 
				
			||||||
        isGuest: false
 | 
					        isGuest: false,
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const authSlice = createSlice({
 | 
					const authSlice = createSlice({
 | 
				
			||||||
    name: 'auth',
 | 
					    name: "auth",
 | 
				
			||||||
    initialState,
 | 
					    initialState,
 | 
				
			||||||
    reducers: {
 | 
					    reducers: {
 | 
				
			||||||
        login: (state, action: PayloadAction<{ accessToken: string }>) => {
 | 
					        login: (state, action: PayloadAction<{ accessToken: string }>) => {
 | 
				
			||||||
            try {
 | 
					            try {
 | 
				
			||||||
                const {sub} = jwtDecode<JwtPayload>(action.payload.accessToken);
 | 
					                const { sub } = jwtDecode<JwtPayload>(
 | 
				
			||||||
 | 
					                    action.payload.accessToken
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
                state.accessToken = action.payload.accessToken;
 | 
					                state.accessToken = action.payload.accessToken;
 | 
				
			||||||
                state.isAuthorized = true;
 | 
					                state.isAuthorized = true;
 | 
				
			||||||
                if (sub === "guest")
 | 
					                if (sub === "guest") state.isGuest = true;
 | 
				
			||||||
                    state.isGuest = true;
 | 
					 | 
				
			||||||
            } catch (_) {
 | 
					            } catch (_) {
 | 
				
			||||||
                const url = window.location.href;
 | 
					                const url = window.location.href;
 | 
				
			||||||
                const urlObj = new URL(url);
 | 
					                const urlObj = new URL(url);
 | 
				
			||||||
                urlObj.search = '';
 | 
					                urlObj.search = "";
 | 
				
			||||||
                history.replaceState(null, '', urlObj);
 | 
					                history.replaceState(null, "", urlObj);
 | 
				
			||||||
                window.location.reload();
 | 
					                window.location.reload();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        logout: (state) => {
 | 
					        logout: state => {
 | 
				
			||||||
            state.isAuthorized = false;
 | 
					            state.isAuthorized = false;
 | 
				
			||||||
            state.accessToken = '';
 | 
					            state.accessToken = "";
 | 
				
			||||||
        }
 | 
					        },
 | 
				
			||||||
    }
 | 
					    },
 | 
				
			||||||
})
 | 
					});
 | 
				
			||||||
export const {login, logout} = authSlice.actions;
 | 
					export const { login, logout } = authSlice.actions;
 | 
				
			||||||
export default authSlice.reducer;
 | 
					export default authSlice.reducer;
 | 
				
			||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
import {createSlice, PayloadAction} from "@reduxjs/toolkit";
 | 
					import { createSlice, PayloadAction } from "@reduxjs/toolkit";
 | 
				
			||||||
import {notifications} from "../shared/lib/notifications.ts";
 | 
					import { notifications } from "../shared/lib/notifications.ts";
 | 
				
			||||||
import {IconCheck, IconX} from "@tabler/icons-react";
 | 
					import { IconCheck, IconX } from "@tabler/icons-react";
 | 
				
			||||||
import {rem} from "@mantine/core";
 | 
					import { rem } from "@mantine/core";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type TaskData = {
 | 
					export type TaskData = {
 | 
				
			||||||
    title: string;
 | 
					    title: string;
 | 
				
			||||||
@@ -43,13 +43,15 @@ const tasksSlice = createSlice({
 | 
				
			|||||||
                autoClose: false,
 | 
					                autoClose: false,
 | 
				
			||||||
                withCloseButton: false,
 | 
					                withCloseButton: false,
 | 
				
			||||||
                withBorder: true,
 | 
					                withBorder: true,
 | 
				
			||||||
                radius: "sm"
 | 
					                radius: "sm",
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            state.tasks.push(task);
 | 
					            state.tasks.push(task);
 | 
				
			||||||
            localStorage.setItem("tasks", JSON.stringify(state.tasks));
 | 
					            localStorage.setItem("tasks", JSON.stringify(state.tasks));
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        removeTask: (state, action: PayloadAction<string>) => {
 | 
					        removeTask: (state, action: PayloadAction<string>) => {
 | 
				
			||||||
            state.tasks = state.tasks.filter((task) => task.id !== action.payload);
 | 
					            state.tasks = state.tasks.filter(
 | 
				
			||||||
 | 
					                task => task.id !== action.payload
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
            localStorage.setItem("tasks", JSON.stringify(state.tasks));
 | 
					            localStorage.setItem("tasks", JSON.stringify(state.tasks));
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        failTask: (state, action: PayloadAction<Task>) => {
 | 
					        failTask: (state, action: PayloadAction<Task>) => {
 | 
				
			||||||
@@ -59,16 +61,20 @@ const tasksSlice = createSlice({
 | 
				
			|||||||
            if (!notificationId) return;
 | 
					            if (!notificationId) return;
 | 
				
			||||||
            notifications.update({
 | 
					            notifications.update({
 | 
				
			||||||
                id: notificationId,
 | 
					                id: notificationId,
 | 
				
			||||||
                color: 'red',
 | 
					                color: "red",
 | 
				
			||||||
                title: task.config.onErrorData.title,
 | 
					                title: task.config.onErrorData.title,
 | 
				
			||||||
                message: task.config.onErrorData.message,
 | 
					                message: task.config.onErrorData.message,
 | 
				
			||||||
                icon: <IconX style={{width: rem(18), height: rem(18)}}/>,
 | 
					                icon: <IconX style={{ width: rem(18), height: rem(18) }} />,
 | 
				
			||||||
                loading: false,
 | 
					                loading: false,
 | 
				
			||||||
                autoClose: 2000,
 | 
					                autoClose: 2000,
 | 
				
			||||||
            })
 | 
					            });
 | 
				
			||||||
            state.tasks = state.tasks.filter((task) => task.id !== action.payload.id);
 | 
					            state.tasks = state.tasks.filter(
 | 
				
			||||||
 | 
					                task => task.id !== action.payload.id
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
            state.notificationTaskMap = Object.fromEntries(
 | 
					            state.notificationTaskMap = Object.fromEntries(
 | 
				
			||||||
                Object.entries(state.notificationTaskMap).filter(([taskId]) => taskId !== task.id)
 | 
					                Object.entries(state.notificationTaskMap).filter(
 | 
				
			||||||
 | 
					                    ([taskId]) => taskId !== task.id
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
            localStorage.setItem("tasks", JSON.stringify(state.tasks));
 | 
					            localStorage.setItem("tasks", JSON.stringify(state.tasks));
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
@@ -79,23 +85,28 @@ const tasksSlice = createSlice({
 | 
				
			|||||||
            if (!notificationId) return;
 | 
					            if (!notificationId) return;
 | 
				
			||||||
            notifications.update({
 | 
					            notifications.update({
 | 
				
			||||||
                id: notificationId,
 | 
					                id: notificationId,
 | 
				
			||||||
                color: 'teal',
 | 
					                color: "teal",
 | 
				
			||||||
                title: task.config.onSuccessData.title,
 | 
					                title: task.config.onSuccessData.title,
 | 
				
			||||||
                message: task.config.onSuccessData.message,
 | 
					                message: task.config.onSuccessData.message,
 | 
				
			||||||
                icon: <IconCheck style={{width: rem(18), height: rem(18)}}/>,
 | 
					                icon: <IconCheck style={{ width: rem(18), height: rem(18) }} />,
 | 
				
			||||||
                loading: false,
 | 
					                loading: false,
 | 
				
			||||||
                autoClose: 2000,
 | 
					                autoClose: 2000,
 | 
				
			||||||
            })
 | 
					            });
 | 
				
			||||||
            state.tasks = state.tasks.filter((task) => task.id !== action.payload.id);
 | 
					            state.tasks = state.tasks.filter(
 | 
				
			||||||
 | 
					                task => task.id !== action.payload.id
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
            state.notificationTaskMap = Object.fromEntries(
 | 
					            state.notificationTaskMap = Object.fromEntries(
 | 
				
			||||||
                Object.entries(state.notificationTaskMap).filter(([taskId]) => taskId !== task.id)
 | 
					                Object.entries(state.notificationTaskMap).filter(
 | 
				
			||||||
 | 
					                    ([taskId]) => taskId !== task.id
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            localStorage.setItem("tasks", JSON.stringify(state.tasks));
 | 
					            localStorage.setItem("tasks", JSON.stringify(state.tasks));
 | 
				
			||||||
        }
 | 
					        },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const {addTask, removeTask, successTask, failTask} = tasksSlice.actions;
 | 
					export const { addTask, removeTask, successTask, failTask } =
 | 
				
			||||||
 | 
					    tasksSlice.actions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default tasksSlice.reducer;
 | 
					export default tasksSlice.reducer;
 | 
				
			||||||
@@ -1,23 +1,22 @@
 | 
				
			|||||||
import {createSlice, PayloadAction} from "@reduxjs/toolkit";
 | 
					import { createSlice, PayloadAction } from "@reduxjs/toolkit";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface UIState {
 | 
					interface UIState {
 | 
				
			||||||
    isLoading: boolean;
 | 
					    isLoading: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const initialState: UIState = {
 | 
					const initialState: UIState = {
 | 
				
			||||||
    isLoading: false
 | 
					    isLoading: false,
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const uiSlice = createSlice({
 | 
					const uiSlice = createSlice({
 | 
				
			||||||
        name: 'ui',
 | 
					    name: "ui",
 | 
				
			||||||
        initialState,
 | 
					    initialState,
 | 
				
			||||||
        reducers: {
 | 
					    reducers: {
 | 
				
			||||||
            setIsLoading: (state, action: PayloadAction<boolean>) => {
 | 
					        setIsLoading: (state, action: PayloadAction<boolean>) => {
 | 
				
			||||||
                state.isLoading = action.payload;
 | 
					            state.isLoading = action.payload;
 | 
				
			||||||
            }
 | 
					        },
 | 
				
			||||||
        }
 | 
					    },
 | 
				
			||||||
    }
 | 
					});
 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default uiSlice.reducer;
 | 
					export default uiSlice.reducer;
 | 
				
			||||||
export const {setIsLoading} = uiSlice.actions;
 | 
					export const { setIsLoading } = uiSlice.actions;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,51 +1,68 @@
 | 
				
			|||||||
import {QueryObserverResult, RefetchOptions, useQuery} from "@tanstack/react-query";
 | 
					import {
 | 
				
			||||||
import {CancelablePromise, PaginationInfoSchema} from "../client";
 | 
					    QueryObserverResult,
 | 
				
			||||||
import {Pagination} from "../types/Pagination.ts";
 | 
					    RefetchOptions,
 | 
				
			||||||
 | 
					    useQuery,
 | 
				
			||||||
 | 
					} from "@tanstack/react-query";
 | 
				
			||||||
 | 
					import { CancelablePromise, PaginationInfoSchema } from "../client";
 | 
				
			||||||
 | 
					import { Pagination } from "../types/Pagination.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props<T, K> = {
 | 
					type Props<T, K> = {
 | 
				
			||||||
    queryFn: () => CancelablePromise<T>,
 | 
					    queryFn: () => CancelablePromise<T>;
 | 
				
			||||||
    getObjectsFn: (response: T) => K[],
 | 
					    getObjectsFn: (response: T) => K[];
 | 
				
			||||||
    queryKey: string
 | 
					    queryKey: string;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
type Response<T, K> = {
 | 
					type Response<T, K> = {
 | 
				
			||||||
    objects: K[],
 | 
					    objects: K[];
 | 
				
			||||||
    refetch: (options?: RefetchOptions) => Promise<QueryObserverResult<T, Error>>
 | 
					    refetch: (
 | 
				
			||||||
}
 | 
					        options?: RefetchOptions
 | 
				
			||||||
const ObjectList = <T, K, >(props: Props<T, K>): Response<T, K> => {
 | 
					    ) => Promise<QueryObserverResult<T, Error>>;
 | 
				
			||||||
    const {isPending, error, data, refetch} = useQuery({
 | 
					};
 | 
				
			||||||
 | 
					const ObjectList = <T, K>(props: Props<T, K>): Response<T, K> => {
 | 
				
			||||||
 | 
					    const { isPending, error, data, refetch } = useQuery({
 | 
				
			||||||
        queryKey: [props.queryKey],
 | 
					        queryKey: [props.queryKey],
 | 
				
			||||||
        queryFn: props.queryFn
 | 
					        queryFn: props.queryFn,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    const objects = isPending || error || !data ? ([] as K[]) : props.getObjectsFn(data);
 | 
					    const objects =
 | 
				
			||||||
    return {objects, refetch}
 | 
					        isPending || error || !data ? ([] as K[]) : props.getObjectsFn(data);
 | 
				
			||||||
}
 | 
					    return { objects, refetch };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface ObjectWithPagination {
 | 
					interface ObjectWithPagination {
 | 
				
			||||||
    paginationInfo: PaginationInfoSchema
 | 
					    paginationInfo: PaginationInfoSchema;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type PropsWithPagination<T extends ObjectWithPagination, K> = {
 | 
					type PropsWithPagination<T extends ObjectWithPagination, K> = {
 | 
				
			||||||
    queryFn: () => CancelablePromise<T>,
 | 
					    queryFn: () => CancelablePromise<T>;
 | 
				
			||||||
    getObjectsFn: (response: T) => K[],
 | 
					    getObjectsFn: (response: T) => K[];
 | 
				
			||||||
    queryKey: string,
 | 
					    queryKey: string;
 | 
				
			||||||
    pagination: Pagination
 | 
					    pagination: Pagination;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
type ResponseWithPagination<T extends ObjectWithPagination, K> = {
 | 
					type ResponseWithPagination<T extends ObjectWithPagination, K> = {
 | 
				
			||||||
    objects: K[],
 | 
					    objects: K[];
 | 
				
			||||||
    pagination?: PaginationInfoSchema,
 | 
					    pagination?: PaginationInfoSchema;
 | 
				
			||||||
    refetch: (options?: RefetchOptions) => Promise<QueryObserverResult<T, Error>>
 | 
					    refetch: (
 | 
				
			||||||
}
 | 
					        options?: RefetchOptions
 | 
				
			||||||
export const ObjectListWithPagination = <T extends ObjectWithPagination, K, >(props: PropsWithPagination<T, K>): ResponseWithPagination<T, K> => {
 | 
					    ) => Promise<QueryObserverResult<T, Error>>;
 | 
				
			||||||
    const {isPending, error, data, refetch} = useQuery({
 | 
					};
 | 
				
			||||||
        queryKey: [props.queryKey, props, props.pagination.itemsPerPage, props.pagination.page],
 | 
					export const ObjectListWithPagination = <T extends ObjectWithPagination, K>(
 | 
				
			||||||
 | 
					    props: PropsWithPagination<T, K>
 | 
				
			||||||
 | 
					): ResponseWithPagination<T, K> => {
 | 
				
			||||||
 | 
					    const { isPending, error, data, refetch } = useQuery({
 | 
				
			||||||
 | 
					        queryKey: [
 | 
				
			||||||
 | 
					            props.queryKey,
 | 
				
			||||||
 | 
					            props,
 | 
				
			||||||
 | 
					            props.pagination.itemsPerPage,
 | 
				
			||||||
 | 
					            props.pagination.page,
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
        queryFn: props.queryFn,
 | 
					        queryFn: props.queryFn,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    const objects = isPending || error || !data ? ([] as K[]) : props.getObjectsFn(data);
 | 
					    const objects =
 | 
				
			||||||
 | 
					        isPending || error || !data ? ([] as K[]) : props.getObjectsFn(data);
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
        objects,
 | 
					        objects,
 | 
				
			||||||
        pagination: data?.paginationInfo,
 | 
					        pagination: data?.paginationInfo,
 | 
				
			||||||
        refetch
 | 
					        refetch,
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default ObjectList
 | 
					export default ObjectList;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,10 @@
 | 
				
			|||||||
import ObjectList from "./objectList.tsx";
 | 
					import ObjectList from "./objectList.tsx";
 | 
				
			||||||
import {MarketplaceService} from "../client";
 | 
					import { MarketplaceService } from "../client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const useBaseMarketplacesList = () => ObjectList({
 | 
					const useBaseMarketplacesList = () =>
 | 
				
			||||||
    queryFn: MarketplaceService.getAllBaseMarketplaces,
 | 
					    ObjectList({
 | 
				
			||||||
    getObjectsFn: (response) => response.baseMarketplaces,
 | 
					        queryFn: MarketplaceService.getAllBaseMarketplaces,
 | 
				
			||||||
    queryKey: "getAllBaseMarketplaces"
 | 
					        getObjectsFn: response => response.baseMarketplaces,
 | 
				
			||||||
})
 | 
					        queryKey: "getAllBaseMarketplaces",
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
export default useBaseMarketplacesList;
 | 
					export default useBaseMarketplacesList;
 | 
				
			||||||
@@ -7,14 +7,13 @@ type Props<T> = {
 | 
				
			|||||||
export function useCRUD<T>(props: Props<T>) {
 | 
					export function useCRUD<T>(props: Props<T>) {
 | 
				
			||||||
    const onCreate = (element: T) => {
 | 
					    const onCreate = (element: T) => {
 | 
				
			||||||
        props.onCreate(element);
 | 
					        props.onCreate(element);
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    const onChange = (element: T) => {
 | 
					    const onChange = (element: T) => {
 | 
				
			||||||
        props.onChange(element);
 | 
					        props.onChange(element);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    const onDelete = (element: T) => {
 | 
					    const onDelete = (element: T) => {
 | 
				
			||||||
        props.onDelete(element);
 | 
					        props.onDelete(element);
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return {onCreate, onChange, onDelete};
 | 
					    return { onCreate, onChange, onDelete };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,11 +1,11 @@
 | 
				
			|||||||
import {ObjectListWithPagination} from "./objectList.tsx";
 | 
					import { ObjectListWithPagination } from "./objectList.tsx";
 | 
				
			||||||
import {PayrollService} from "../client";
 | 
					import { PayrollService } from "../client";
 | 
				
			||||||
import {Pagination} from "../types/Pagination.ts";
 | 
					import { Pagination } from "../types/Pagination.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const usePaymentRecordsList = (pagination: Pagination) =>
 | 
				
			||||||
export const usePaymentRecordsList = (pagination: Pagination) => ObjectListWithPagination({
 | 
					    ObjectListWithPagination({
 | 
				
			||||||
    queryFn: () => PayrollService.getPaymentRecords(pagination),
 | 
					        queryFn: () => PayrollService.getPaymentRecords(pagination),
 | 
				
			||||||
    queryKey: "getPaymentRecords",
 | 
					        queryKey: "getPaymentRecords",
 | 
				
			||||||
    getObjectsFn: (response) => response.paymentRecords,
 | 
					        getObjectsFn: response => response.paymentRecords,
 | 
				
			||||||
    pagination
 | 
					        pagination,
 | 
				
			||||||
})
 | 
					    });
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,10 @@
 | 
				
			|||||||
import ObjectList from "./objectList.tsx";
 | 
					import ObjectList from "./objectList.tsx";
 | 
				
			||||||
import {PayrollService} from "../client";
 | 
					import { PayrollService } from "../client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const usePayrollSchemasList = () => ObjectList({
 | 
					const usePayrollSchemasList = () =>
 | 
				
			||||||
    queryFn: PayrollService.getAllPayrollSchemas,
 | 
					    ObjectList({
 | 
				
			||||||
    getObjectsFn: (response) => response.payrollSchemas,
 | 
					        queryFn: PayrollService.getAllPayrollSchemas,
 | 
				
			||||||
    queryKey: "getAllPayrollSchemas"
 | 
					        getObjectsFn: response => response.payrollSchemas,
 | 
				
			||||||
})
 | 
					        queryKey: "getAllPayrollSchemas",
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
export default usePayrollSchemasList;
 | 
					export default usePayrollSchemasList;
 | 
				
			||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
import {useEffect, useRef, DependencyList} from 'react';
 | 
					import { useEffect, useRef, DependencyList } from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type UsePollingEffectOptions = {
 | 
					type UsePollingEffectOptions = {
 | 
				
			||||||
    interval?: number;
 | 
					    interval?: number;
 | 
				
			||||||
@@ -11,17 +11,13 @@ function usePollingEffect(
 | 
				
			|||||||
    dependencies: DependencyList = [],
 | 
					    dependencies: DependencyList = [],
 | 
				
			||||||
    options: UsePollingEffectOptions = {}
 | 
					    options: UsePollingEffectOptions = {}
 | 
				
			||||||
): void {
 | 
					): void {
 | 
				
			||||||
    const {
 | 
					    const { interval = 3000, isActive = true, onCleanUp = () => {} } = options;
 | 
				
			||||||
        interval = 3000,
 | 
					 | 
				
			||||||
        isActive = true,
 | 
					 | 
				
			||||||
        onCleanUp = () => {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    } = options;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const timeoutIdRef = useRef<number | null>(null);
 | 
					    const timeoutIdRef = useRef<number | null>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        if (!isActive) { // If not active, don't do anything
 | 
					        if (!isActive) {
 | 
				
			||||||
 | 
					            // If not active, don't do anything
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -32,7 +28,10 @@ function usePollingEffect(
 | 
				
			|||||||
                await asyncCallback();
 | 
					                await asyncCallback();
 | 
				
			||||||
            } finally {
 | 
					            } finally {
 | 
				
			||||||
                if (!stopped) {
 | 
					                if (!stopped) {
 | 
				
			||||||
                    timeoutIdRef.current = setTimeout(pollingCallback, interval);
 | 
					                    timeoutIdRef.current = setTimeout(
 | 
				
			||||||
 | 
					                        pollingCallback,
 | 
				
			||||||
 | 
					                        interval
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								src/main.tsx
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/main.tsx
									
									
									
									
									
								
							@@ -40,22 +40,24 @@ const queryClient = new QueryClient();
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Configuring OpenAPI
 | 
					// Configuring OpenAPI
 | 
				
			||||||
OpenAPI.BASE = import.meta.env.VITE_API_URL;
 | 
					OpenAPI.BASE = import.meta.env.VITE_API_URL;
 | 
				
			||||||
OpenAPI.TOKEN = JSON.parse(localStorage.getItem("authState") || "{}")["accessToken"];
 | 
					OpenAPI.TOKEN = JSON.parse(localStorage.getItem("authState") || "{}")[
 | 
				
			||||||
 | 
					    "accessToken"
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
ReactDOM.createRoot(document.getElementById("root")!).render(
 | 
					ReactDOM.createRoot(document.getElementById("root")!).render(
 | 
				
			||||||
    <Provider store={store}>
 | 
					    <Provider store={store}>
 | 
				
			||||||
        <QueryClientProvider client={queryClient}>
 | 
					        <QueryClientProvider client={queryClient}>
 | 
				
			||||||
            <MantineProvider defaultColorScheme={"dark"}>
 | 
					            <MantineProvider defaultColorScheme={"dark"}>
 | 
				
			||||||
                <ModalsProvider labels={{ confirm: "Да", cancel: "Нет" }} modals={modals}>
 | 
					                <ModalsProvider
 | 
				
			||||||
 | 
					                    labels={{ confirm: "Да", cancel: "Нет" }}
 | 
				
			||||||
 | 
					                    modals={modals}>
 | 
				
			||||||
                    <DatesProvider settings={{ locale: "ru" }}>
 | 
					                    <DatesProvider settings={{ locale: "ru" }}>
 | 
				
			||||||
                        <TasksProvider>
 | 
					                        <TasksProvider>
 | 
				
			||||||
 | 
					 | 
				
			||||||
                            <RouterProvider router={router} />
 | 
					                            <RouterProvider router={router} />
 | 
				
			||||||
                            <Notifications />
 | 
					                            <Notifications />
 | 
				
			||||||
                        </TasksProvider>
 | 
					                        </TasksProvider>
 | 
				
			||||||
 | 
					 | 
				
			||||||
                    </DatesProvider>
 | 
					                    </DatesProvider>
 | 
				
			||||||
                </ModalsProvider>
 | 
					                </ModalsProvider>
 | 
				
			||||||
            </MantineProvider>
 | 
					            </MantineProvider>
 | 
				
			||||||
        </QueryClientProvider>
 | 
					        </QueryClientProvider>
 | 
				
			||||||
    </Provider>,
 | 
					    </Provider>
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,18 +1,18 @@
 | 
				
			|||||||
import {ContextModalProps} from "@mantine/modals";
 | 
					import { ContextModalProps } from "@mantine/modals";
 | 
				
			||||||
import {Button, Flex, rem, TextInput} from "@mantine/core";
 | 
					import { Button, Flex, rem, TextInput } from "@mantine/core";
 | 
				
			||||||
import {useEffect, useState} from "react";
 | 
					import { useEffect, useState } from "react";
 | 
				
			||||||
import {ProductService} from "../../client";
 | 
					import { ProductService } from "../../client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
    productId: number;
 | 
					    productId: number;
 | 
				
			||||||
    onSubmit: (barcode: string) => void;
 | 
					    onSubmit: (barcode: string) => void;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
const PrintBarcodeModal = ({
 | 
					const PrintBarcodeModal = ({
 | 
				
			||||||
                               id,
 | 
					    id,
 | 
				
			||||||
                               context,
 | 
					    context,
 | 
				
			||||||
                               innerProps
 | 
					    innerProps,
 | 
				
			||||||
                           }: ContextModalProps<Props>) => {
 | 
					}: ContextModalProps<Props>) => {
 | 
				
			||||||
    const {productId, onSubmit} = innerProps;
 | 
					    const { productId, onSubmit } = innerProps;
 | 
				
			||||||
    const [barcode, setBarcode] = useState<string | undefined>();
 | 
					    const [barcode, setBarcode] = useState<string | undefined>();
 | 
				
			||||||
    const [isBarcodeExist, setIsBarcodeExist] = useState<boolean>(true);
 | 
					    const [isBarcodeExist, setIsBarcodeExist] = useState<boolean>(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -20,44 +20,41 @@ const PrintBarcodeModal = ({
 | 
				
			|||||||
        if (!barcode) return "Штрихкод не может быть пустым";
 | 
					        if (!barcode) return "Штрихкод не может быть пустым";
 | 
				
			||||||
        if (isBarcodeExist) return "Штрихкод уже существует";
 | 
					        if (isBarcodeExist) return "Штрихкод уже существует";
 | 
				
			||||||
        return undefined;
 | 
					        return undefined;
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        if (!barcode) return;
 | 
					        if (!barcode) return;
 | 
				
			||||||
        ProductService.existsProductBarcode({
 | 
					        ProductService.existsProductBarcode({
 | 
				
			||||||
            productId: innerProps.productId,
 | 
					            productId: innerProps.productId,
 | 
				
			||||||
            barcode: barcode.trim()
 | 
					            barcode: barcode.trim(),
 | 
				
			||||||
        }).then((response) => {
 | 
					        }).then(response => {
 | 
				
			||||||
            setIsBarcodeExist(response.exists);
 | 
					            setIsBarcodeExist(response.exists);
 | 
				
			||||||
        })
 | 
					        });
 | 
				
			||||||
    }, [productId, barcode]);
 | 
					    }, [productId, barcode]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const onSubmitClick = () => {
 | 
					    const onSubmitClick = () => {
 | 
				
			||||||
        if (!barcode || isBarcodeExist) return;
 | 
					        if (!barcode || isBarcodeExist) return;
 | 
				
			||||||
        onSubmit(barcode.trim());
 | 
					        onSubmit(barcode.trim());
 | 
				
			||||||
        context.closeModal(id);
 | 
					        context.closeModal(id);
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Flex
 | 
					        <Flex
 | 
				
			||||||
            gap={rem(10)}
 | 
					            gap={rem(10)}
 | 
				
			||||||
            direction={"column"}
 | 
					            direction={"column"}>
 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
            <TextInput
 | 
					            <TextInput
 | 
				
			||||||
                required
 | 
					                required
 | 
				
			||||||
                error={getErrorMessage()}
 | 
					                error={getErrorMessage()}
 | 
				
			||||||
                label={"Штрихкод"}
 | 
					                label={"Штрихкод"}
 | 
				
			||||||
                placeholder={"Введите или отсканируйте штрихкод"}
 | 
					                placeholder={"Введите или отсканируйте штрихкод"}
 | 
				
			||||||
                value={barcode}
 | 
					                value={barcode}
 | 
				
			||||||
                onChange={(event) => setBarcode(event.currentTarget.value)}
 | 
					                onChange={event => setBarcode(event.currentTarget.value)}
 | 
				
			||||||
 | 
					 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
            <Button
 | 
					            <Button
 | 
				
			||||||
                onClick={onSubmitClick}
 | 
					                onClick={onSubmitClick}
 | 
				
			||||||
                disabled={!barcode || isBarcodeExist}
 | 
					                disabled={!barcode || isBarcodeExist}>
 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
                Добавить
 | 
					                Добавить
 | 
				
			||||||
            </Button>
 | 
					            </Button>
 | 
				
			||||||
        </Flex>
 | 
					        </Flex>
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default PrintBarcodeModal;
 | 
					export default PrintBarcodeModal;
 | 
				
			||||||
@@ -1,67 +1,68 @@
 | 
				
			|||||||
import {UserSchema} from "../../client";
 | 
					import { UserSchema } from "../../client";
 | 
				
			||||||
import {ObjectSelectProps} from "../../components/ObjectSelect/ObjectSelect.tsx";
 | 
					import { ObjectSelectProps } from "../../components/ObjectSelect/ObjectSelect.tsx";
 | 
				
			||||||
import {ContextModalProps} from "@mantine/modals";
 | 
					import { ContextModalProps } from "@mantine/modals";
 | 
				
			||||||
import {Button, Flex, rem} from "@mantine/core";
 | 
					import { Button, Flex, rem } from "@mantine/core";
 | 
				
			||||||
import {useState} from "react";
 | 
					import { useState } from "react";
 | 
				
			||||||
import UserSelect from "../../components/Selects/UserSelect/UserSelect.tsx";
 | 
					import UserSelect from "../../components/Selects/UserSelect/UserSelect.tsx";
 | 
				
			||||||
import {notifications} from "../../shared/lib/notifications.ts";
 | 
					import { notifications } from "../../shared/lib/notifications.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type SelectProps = Omit<ObjectSelectProps<UserSchema>, 'data' | 'getValueFn' | 'getLabelFn' | 'onChange'>;
 | 
					type SelectProps = Omit<
 | 
				
			||||||
 | 
					    ObjectSelectProps<UserSchema>,
 | 
				
			||||||
 | 
					    "data" | "getValueFn" | "getLabelFn" | "onChange"
 | 
				
			||||||
 | 
					>;
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
    onSelect: (user: UserSchema) => void
 | 
					    onSelect: (user: UserSchema) => void;
 | 
				
			||||||
    selectProps?: SelectProps;
 | 
					    selectProps?: SelectProps;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const EmployeeSelectModal = ({
 | 
					const EmployeeSelectModal = ({
 | 
				
			||||||
                                 context,
 | 
					    context,
 | 
				
			||||||
                                 id,
 | 
					    id,
 | 
				
			||||||
                                 innerProps,
 | 
					    innerProps,
 | 
				
			||||||
                             }: ContextModalProps<Props>) => {
 | 
					}: ContextModalProps<Props>) => {
 | 
				
			||||||
    const [innerValue, setInnerValue] = useState<UserSchema | undefined>();
 | 
					    const [innerValue, setInnerValue] = useState<UserSchema | undefined>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const closeSelf = () => {
 | 
					    const closeSelf = () => {
 | 
				
			||||||
        context.closeContextModal(id);
 | 
					        context.closeContextModal(id);
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const onSelectClick = () => {
 | 
					    const onSelectClick = () => {
 | 
				
			||||||
        if (!innerValue) {
 | 
					        if (!innerValue) {
 | 
				
			||||||
            notifications.error({message: "Необходимо выбрать сотрудника"})
 | 
					            notifications.error({ message: "Необходимо выбрать сотрудника" });
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        innerProps.onSelect(innerValue);
 | 
					        innerProps.onSelect(innerValue);
 | 
				
			||||||
        closeSelf();
 | 
					        closeSelf();
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    const onCloseClick = () => {
 | 
					    const onCloseClick = () => {
 | 
				
			||||||
        closeSelf();
 | 
					        closeSelf();
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Flex direction={"column"}
 | 
					        <Flex
 | 
				
			||||||
              gap={rem(10)}
 | 
					            direction={"column"}
 | 
				
			||||||
        >
 | 
					            gap={rem(10)}>
 | 
				
			||||||
            <Flex w={"100%"}>
 | 
					            <Flex w={"100%"}>
 | 
				
			||||||
                <UserSelect
 | 
					                <UserSelect
 | 
				
			||||||
                    onChange={setInnerValue}
 | 
					                    onChange={setInnerValue}
 | 
				
			||||||
                    w={"100%"}
 | 
					                    w={"100%"}
 | 
				
			||||||
                    placeholder={"Выберите сотрудника"}
 | 
					                    placeholder={"Выберите сотрудника"}
 | 
				
			||||||
                    {...innerProps.selectProps}
 | 
					                    {...innerProps.selectProps}
 | 
				
			||||||
 | 
					 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
            </Flex>
 | 
					            </Flex>
 | 
				
			||||||
            <Flex justify={"flex-end"} gap={rem(10)}>
 | 
					            <Flex
 | 
				
			||||||
 | 
					                justify={"flex-end"}
 | 
				
			||||||
 | 
					                gap={rem(10)}>
 | 
				
			||||||
                <Button
 | 
					                <Button
 | 
				
			||||||
                    variant={"default"}
 | 
					                    variant={"default"}
 | 
				
			||||||
                    onClick={() => onCloseClick()}
 | 
					                    onClick={() => onCloseClick()}>
 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                    Отменить
 | 
					                    Отменить
 | 
				
			||||||
                </Button>
 | 
					                </Button>
 | 
				
			||||||
                <Button
 | 
					                <Button onClick={() => onSelectClick()}>
 | 
				
			||||||
                    onClick={() => onSelectClick()}
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                    Выбрать сотрудника
 | 
					                    Выбрать сотрудника
 | 
				
			||||||
                </Button>
 | 
					                </Button>
 | 
				
			||||||
            </Flex>
 | 
					            </Flex>
 | 
				
			||||||
        </Flex>
 | 
					        </Flex>
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default EmployeeSelectModal
 | 
					export default EmployeeSelectModal;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,29 +1,27 @@
 | 
				
			|||||||
import SimpleUsersTable from "../../pages/LeadsPage/components/SimpleUsersTable/SimpleUsersTable.tsx";
 | 
					import SimpleUsersTable from "../../pages/LeadsPage/components/SimpleUsersTable/SimpleUsersTable.tsx";
 | 
				
			||||||
import {ContextModalProps} from "@mantine/modals";
 | 
					import { ContextModalProps } from "@mantine/modals";
 | 
				
			||||||
import {Flex, rem} from "@mantine/core";
 | 
					import { Flex, rem } from "@mantine/core";
 | 
				
			||||||
import {UserSchema} from "../../client";
 | 
					import { UserSchema } from "../../client";
 | 
				
			||||||
import {MutableRefObject, useEffect} from "react";
 | 
					import { MutableRefObject, useEffect } from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
    items: MutableRefObject<UserSchema[]>
 | 
					    items: MutableRefObject<UserSchema[]>;
 | 
				
			||||||
    onChange: (items: UserSchema[]) => void;
 | 
					    onChange: (items: UserSchema[]) => void;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const EmployeeTableModal = ({
 | 
					const EmployeeTableModal = ({ innerProps }: ContextModalProps<Props>) => {
 | 
				
			||||||
                                innerProps
 | 
					    useEffect(() => {}, [innerProps.items.current]);
 | 
				
			||||||
                            }: ContextModalProps<Props>) => {
 | 
					 | 
				
			||||||
    useEffect(() => {
 | 
					 | 
				
			||||||
    }, [innerProps.items.current])
 | 
					 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Flex direction={"column"} gap={rem(10)}>
 | 
					        <Flex
 | 
				
			||||||
 | 
					            direction={"column"}
 | 
				
			||||||
 | 
					            gap={rem(10)}>
 | 
				
			||||||
            <SimpleUsersTable
 | 
					            <SimpleUsersTable
 | 
				
			||||||
                items={innerProps.items.current}
 | 
					                items={innerProps.items.current}
 | 
				
			||||||
                onChange={(event) => {
 | 
					                onChange={event => {
 | 
				
			||||||
                    innerProps.onChange(event);
 | 
					                    innerProps.onChange(event);
 | 
				
			||||||
                }}
 | 
					                }}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
 | 
					 | 
				
			||||||
        </Flex>
 | 
					        </Flex>
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
export default EmployeeTableModal;
 | 
					export default EmployeeTableModal;
 | 
				
			||||||
@@ -1,81 +1,83 @@
 | 
				
			|||||||
import {ContextModalProps} from "@mantine/modals";
 | 
					import { ContextModalProps } from "@mantine/modals";
 | 
				
			||||||
import {Button, Flex, rem, Textarea} from "@mantine/core";
 | 
					import { Button, Flex, rem, Textarea } from "@mantine/core";
 | 
				
			||||||
import {DateTimePicker, DateValue} from "@mantine/dates";
 | 
					import { DateTimePicker, DateValue } from "@mantine/dates";
 | 
				
			||||||
import {useForm} from "@mantine/form";
 | 
					import { useForm } from "@mantine/form";
 | 
				
			||||||
import {DealSummaryReorderRequest} from "../../client";
 | 
					import { DealSummaryReorderRequest } from "../../client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {dateWithoutTimezone} from "../../shared/lib/date.ts";
 | 
					import { dateWithoutTimezone } from "../../shared/lib/date.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Deadline = {
 | 
					type Deadline = {
 | 
				
			||||||
    deadline: DateValue,
 | 
					    deadline: DateValue;
 | 
				
			||||||
    comment: string
 | 
					    comment: string;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
    request: Partial<DealSummaryReorderRequest>,
 | 
					    request: Partial<DealSummaryReorderRequest>;
 | 
				
			||||||
    onSubmit: (
 | 
					    onSubmit: (request: DealSummaryReorderRequest) => void;
 | 
				
			||||||
        request: DealSummaryReorderRequest,
 | 
					};
 | 
				
			||||||
    ) => void;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const EnterDeadlineModal = ({
 | 
					const EnterDeadlineModal = ({
 | 
				
			||||||
                                context,
 | 
					    context,
 | 
				
			||||||
                                id,
 | 
					    id,
 | 
				
			||||||
                                innerProps,
 | 
					    innerProps,
 | 
				
			||||||
                            }: ContextModalProps<Props>) => {
 | 
					}: ContextModalProps<Props>) => {
 | 
				
			||||||
    const form = useForm<Deadline>({
 | 
					    const form = useForm<Deadline>({
 | 
				
			||||||
        initialValues: {
 | 
					        initialValues: {
 | 
				
			||||||
            deadline: null,
 | 
					            deadline: null,
 | 
				
			||||||
            comment: '',
 | 
					            comment: "",
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        validate: {
 | 
					        validate: {
 | 
				
			||||||
            deadline: (datetime) => datetime !== null ? null : 'Необходимо ввести дедлайн',
 | 
					            deadline: datetime =>
 | 
				
			||||||
        }
 | 
					                datetime !== null ? null : "Необходимо ввести дедлайн",
 | 
				
			||||||
    })
 | 
					        },
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
    const onCancelClick = () => {
 | 
					    const onCancelClick = () => {
 | 
				
			||||||
        context.closeModal(id);
 | 
					        context.closeModal(id);
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    const onSubmit = (values: Deadline) => {
 | 
					    const onSubmit = (values: Deadline) => {
 | 
				
			||||||
        const {deadline, comment} = values;
 | 
					        const { deadline, comment } = values;
 | 
				
			||||||
        if (!deadline) return;
 | 
					        if (!deadline) return;
 | 
				
			||||||
        innerProps.onSubmit({
 | 
					        innerProps.onSubmit({
 | 
				
			||||||
            ...innerProps.request,
 | 
					            ...innerProps.request,
 | 
				
			||||||
            deadline: dateWithoutTimezone(deadline),
 | 
					            deadline: dateWithoutTimezone(deadline),
 | 
				
			||||||
            comment
 | 
					            comment,
 | 
				
			||||||
        } as unknown as DealSummaryReorderRequest);
 | 
					        } as unknown as DealSummaryReorderRequest);
 | 
				
			||||||
        context.closeModal(id);
 | 
					        context.closeModal(id);
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <form onSubmit={form.onSubmit((values) => onSubmit(values))}>
 | 
					        <form onSubmit={form.onSubmit(values => onSubmit(values))}>
 | 
				
			||||||
 | 
					            <Flex
 | 
				
			||||||
            <Flex direction={'column'} gap={10}>
 | 
					                direction={"column"}
 | 
				
			||||||
                <Flex direction={'column'} gap={rem(10)}>
 | 
					                gap={10}>
 | 
				
			||||||
 | 
					                <Flex
 | 
				
			||||||
 | 
					                    direction={"column"}
 | 
				
			||||||
 | 
					                    gap={rem(10)}>
 | 
				
			||||||
                    <DateTimePicker
 | 
					                    <DateTimePicker
 | 
				
			||||||
                        required
 | 
					                        required
 | 
				
			||||||
                        label={'Дата и время'}
 | 
					                        label={"Дата и время"}
 | 
				
			||||||
                        placeholder={'Введите дату и время'}
 | 
					                        placeholder={"Введите дату и время"}
 | 
				
			||||||
                        minDate={new Date()}
 | 
					                        minDate={new Date()}
 | 
				
			||||||
                        {...form.getInputProps('deadline')}
 | 
					                        {...form.getInputProps("deadline")}
 | 
				
			||||||
                    />
 | 
					                    />
 | 
				
			||||||
                    <Textarea
 | 
					                    <Textarea
 | 
				
			||||||
                        label={'Коментарий'}
 | 
					                        label={"Коментарий"}
 | 
				
			||||||
                        placeholder={'Введите коментарий'}
 | 
					                        placeholder={"Введите коментарий"}
 | 
				
			||||||
                        {...form.getInputProps('comment')}
 | 
					                        {...form.getInputProps("comment")}
 | 
				
			||||||
                    />
 | 
					                    />
 | 
				
			||||||
                </Flex>
 | 
					                </Flex>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <Flex justify={'flex-end'} gap={rem(10)}>
 | 
					                <Flex
 | 
				
			||||||
 | 
					                    justify={"flex-end"}
 | 
				
			||||||
 | 
					                    gap={rem(10)}>
 | 
				
			||||||
                    <Button
 | 
					                    <Button
 | 
				
			||||||
                        variant={'default'}
 | 
					                        variant={"default"}
 | 
				
			||||||
                        onClick={onCancelClick}
 | 
					                        onClick={onCancelClick}>
 | 
				
			||||||
                    >Отменить</Button>
 | 
					                        Отменить
 | 
				
			||||||
                    <Button
 | 
					                    </Button>
 | 
				
			||||||
                        type={'submit'}
 | 
					                    <Button type={"submit"}>Сохранить</Button>
 | 
				
			||||||
                    >Сохранить</Button>
 | 
					 | 
				
			||||||
                </Flex>
 | 
					                </Flex>
 | 
				
			||||||
            </Flex>
 | 
					            </Flex>
 | 
				
			||||||
        </form>
 | 
					        </form>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
export default EnterDeadlineModal;
 | 
					export default EnterDeadlineModal;
 | 
				
			||||||
@@ -1,31 +1,38 @@
 | 
				
			|||||||
import BaseFormModal, {CreateEditFormProps} from "../../pages/ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
 | 
					import BaseFormModal, {
 | 
				
			||||||
import {PositionSchema} from "../../client";
 | 
					    CreateEditFormProps,
 | 
				
			||||||
import {ContextModalProps} from "@mantine/modals";
 | 
					} from "../../pages/ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
 | 
				
			||||||
import {useForm} from "@mantine/form";
 | 
					import { PositionSchema } from "../../client";
 | 
				
			||||||
import {Flex, rem, TextInput} from "@mantine/core";
 | 
					import { ContextModalProps } from "@mantine/modals";
 | 
				
			||||||
import {useEffect} from "react";
 | 
					import { useForm } from "@mantine/form";
 | 
				
			||||||
import CyrillicToTranslit from 'cyrillic-to-translit-js';
 | 
					import { Flex, rem, TextInput } from "@mantine/core";
 | 
				
			||||||
 | 
					import { useEffect } from "react";
 | 
				
			||||||
 | 
					import CyrillicToTranslit from "cyrillic-to-translit-js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = CreateEditFormProps<PositionSchema>;
 | 
					type Props = CreateEditFormProps<PositionSchema>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const PositionFormModal = ({
 | 
					const PositionFormModal = ({
 | 
				
			||||||
                               id,
 | 
					    id,
 | 
				
			||||||
                               context,
 | 
					    context,
 | 
				
			||||||
                               innerProps
 | 
					    innerProps,
 | 
				
			||||||
                           }: ContextModalProps<Props>) => {
 | 
					}: ContextModalProps<Props>) => {
 | 
				
			||||||
    const translit = CyrillicToTranslit({preset: "ru"})
 | 
					    const translit = CyrillicToTranslit({ preset: "ru" });
 | 
				
			||||||
    const isEditing = 'element' in innerProps;
 | 
					    const isEditing = "element" in innerProps;
 | 
				
			||||||
    const initialValues: PositionSchema = isEditing ? innerProps.element : {
 | 
					    const initialValues: PositionSchema = isEditing
 | 
				
			||||||
        key: "",
 | 
					        ? innerProps.element
 | 
				
			||||||
        name: ""
 | 
					        : {
 | 
				
			||||||
    }
 | 
					              key: "",
 | 
				
			||||||
 | 
					              name: "",
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
    const form = useForm<PositionSchema>({
 | 
					    const form = useForm<PositionSchema>({
 | 
				
			||||||
        initialValues: initialValues
 | 
					        initialValues: initialValues,
 | 
				
			||||||
    })
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        if (isEditing) return;
 | 
					        if (isEditing) return;
 | 
				
			||||||
        form.setFieldValue("key", translit.transform(form.values.name).toLowerCase());
 | 
					        form.setFieldValue(
 | 
				
			||||||
 | 
					            "key",
 | 
				
			||||||
 | 
					            translit.transform(form.values.name).toLowerCase()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
    }, [form.values.name]);
 | 
					    }, [form.values.name]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
@@ -33,13 +40,11 @@ const PositionFormModal = ({
 | 
				
			|||||||
            closeOnSubmit
 | 
					            closeOnSubmit
 | 
				
			||||||
            form={form}
 | 
					            form={form}
 | 
				
			||||||
            onClose={() => context.closeContextModal(id)}
 | 
					            onClose={() => context.closeContextModal(id)}
 | 
				
			||||||
            {...innerProps}
 | 
					            {...innerProps}>
 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
            <BaseFormModal.Body>
 | 
					            <BaseFormModal.Body>
 | 
				
			||||||
                <Flex
 | 
					                <Flex
 | 
				
			||||||
                    direction={"column"}
 | 
					                    direction={"column"}
 | 
				
			||||||
                    gap={rem(10)}
 | 
					                    gap={rem(10)}>
 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                    <TextInput
 | 
					                    <TextInput
 | 
				
			||||||
                        label={"Название"}
 | 
					                        label={"Название"}
 | 
				
			||||||
                        placeholder={"Введите название должности"}
 | 
					                        placeholder={"Введите название должности"}
 | 
				
			||||||
@@ -53,7 +58,7 @@ const PositionFormModal = ({
 | 
				
			|||||||
                </Flex>
 | 
					                </Flex>
 | 
				
			||||||
            </BaseFormModal.Body>
 | 
					            </BaseFormModal.Body>
 | 
				
			||||||
        </BaseFormModal>
 | 
					        </BaseFormModal>
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default PositionFormModal;
 | 
					export default PositionFormModal;
 | 
				
			||||||
@@ -1,72 +1,88 @@
 | 
				
			|||||||
import {BarcodeAttributeSchema} from "../../client";
 | 
					import { BarcodeAttributeSchema } from "../../client";
 | 
				
			||||||
import {forwardRef} from "react";
 | 
					import { forwardRef } from "react";
 | 
				
			||||||
import styles from "./PrintBarcodeModal.module.css";
 | 
					import styles from "./PrintBarcodeModal.module.css";
 | 
				
			||||||
import {Flex, Text} from "@mantine/core";
 | 
					import { Flex, Text } from "@mantine/core";
 | 
				
			||||||
import Barcode from "react-barcode";
 | 
					import Barcode from "react-barcode";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
    attributes: BarcodeAttributeSchema[]
 | 
					    attributes: BarcodeAttributeSchema[];
 | 
				
			||||||
    barcode?: string;
 | 
					    barcode?: string;
 | 
				
			||||||
    quantity: number;
 | 
					    quantity: number;
 | 
				
			||||||
    additionalField?: string | null;
 | 
					    additionalField?: string | null;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
type Ref = HTMLDivElement;
 | 
					type Ref = HTMLDivElement;
 | 
				
			||||||
const PrintBarcodeContainer = forwardRef<Ref, Props>(function PrintBarcodeContainer(props: Props, ref) {
 | 
					const PrintBarcodeContainer = forwardRef<Ref, Props>(
 | 
				
			||||||
    const {attributes, barcode, quantity, additionalField} = props;
 | 
					    function PrintBarcodeContainer(props: Props, ref) {
 | 
				
			||||||
 | 
					        const { attributes, barcode, quantity, additionalField } = props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const MAX_ATTRIBUTES = additionalField && additionalField.length > 0 ? 5 : 6;
 | 
					        const MAX_ATTRIBUTES =
 | 
				
			||||||
    const MIN_BARCODE_SIZE = 30;
 | 
					            additionalField && additionalField.length > 0 ? 5 : 6;
 | 
				
			||||||
    const MAX_BARCODE_SIZE = 100;
 | 
					        const MIN_BARCODE_SIZE = 30;
 | 
				
			||||||
    const STEP = (MAX_BARCODE_SIZE - MIN_BARCODE_SIZE) / MAX_ATTRIBUTES;
 | 
					        const MAX_BARCODE_SIZE = 100;
 | 
				
			||||||
    const MAX_ATTRIBUTE_LENGTH = 35;
 | 
					        const STEP = (MAX_BARCODE_SIZE - MIN_BARCODE_SIZE) / MAX_ATTRIBUTES;
 | 
				
			||||||
 | 
					        const MAX_ATTRIBUTE_LENGTH = 35;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const getBarcodeHeight = () => {
 | 
					        const getBarcodeHeight = () => {
 | 
				
			||||||
        return MIN_BARCODE_SIZE + (MAX_ATTRIBUTES - attributes.length) * STEP;
 | 
					            return (
 | 
				
			||||||
 | 
					                MIN_BARCODE_SIZE + (MAX_ATTRIBUTES - attributes.length) * STEP
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        const getAttributeText = (attribute: BarcodeAttributeSchema) => {
 | 
				
			||||||
 | 
					            let result = `${attribute.name}: ${attribute.value}`;
 | 
				
			||||||
 | 
					            if (result.length > MAX_ATTRIBUTE_LENGTH) {
 | 
				
			||||||
 | 
					                result = result.slice(0, MAX_ATTRIBUTE_LENGTH - 1) + ".";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return result;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return (
 | 
				
			||||||
 | 
					            <div
 | 
				
			||||||
 | 
					                className={styles["print-only"]}
 | 
				
			||||||
 | 
					                ref={ref}>
 | 
				
			||||||
 | 
					                {barcode &&
 | 
				
			||||||
 | 
					                    Array.from({ length: quantity }).map((_, index) => (
 | 
				
			||||||
 | 
					                        <>
 | 
				
			||||||
 | 
					                            <Flex
 | 
				
			||||||
 | 
					                                className={styles["barcode-container"]}
 | 
				
			||||||
 | 
					                                key={index}
 | 
				
			||||||
 | 
					                                justify={"center"}
 | 
				
			||||||
 | 
					                                direction={"column"}>
 | 
				
			||||||
 | 
					                                <Flex
 | 
				
			||||||
 | 
					                                    align={"center"}
 | 
				
			||||||
 | 
					                                    justify={"center"}>
 | 
				
			||||||
 | 
					                                    <Barcode
 | 
				
			||||||
 | 
					                                        margin={0}
 | 
				
			||||||
 | 
					                                        height={getBarcodeHeight()}
 | 
				
			||||||
 | 
					                                        value={barcode}
 | 
				
			||||||
 | 
					                                    />
 | 
				
			||||||
 | 
					                                </Flex>
 | 
				
			||||||
 | 
					                                {attributes
 | 
				
			||||||
 | 
					                                    .slice(0, MAX_ATTRIBUTES)
 | 
				
			||||||
 | 
					                                    .map(attr => (
 | 
				
			||||||
 | 
					                                        <Text
 | 
				
			||||||
 | 
					                                            key={attr.name}
 | 
				
			||||||
 | 
					                                            className={
 | 
				
			||||||
 | 
					                                                styles["barcode-attribute-text"]
 | 
				
			||||||
 | 
					                                            }
 | 
				
			||||||
 | 
					                                            size={"xs"}>
 | 
				
			||||||
 | 
					                                            {getAttributeText(attr)}
 | 
				
			||||||
 | 
					                                        </Text>
 | 
				
			||||||
 | 
					                                    ))}
 | 
				
			||||||
 | 
					                                {props.additionalField && (
 | 
				
			||||||
 | 
					                                    <Text
 | 
				
			||||||
 | 
					                                        className={
 | 
				
			||||||
 | 
					                                            styles["barcode-attribute-text"]
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                        size={"xs"}>
 | 
				
			||||||
 | 
					                                        {props.additionalField}
 | 
				
			||||||
 | 
					                                    </Text>
 | 
				
			||||||
 | 
					                                )}
 | 
				
			||||||
 | 
					                            </Flex>
 | 
				
			||||||
 | 
					                        </>
 | 
				
			||||||
 | 
					                    ))}
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const getAttributeText = (attribute: BarcodeAttributeSchema) => {
 | 
					);
 | 
				
			||||||
        let result = `${attribute.name}: ${attribute.value}`;
 | 
					 | 
				
			||||||
        if (result.length > MAX_ATTRIBUTE_LENGTH) {
 | 
					 | 
				
			||||||
            result = result.slice(0, MAX_ATTRIBUTE_LENGTH - 1) + ".";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return result;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return (
 | 
					 | 
				
			||||||
        <div className={styles['print-only']} ref={ref}>
 | 
					 | 
				
			||||||
            {barcode && Array.from({length: quantity}).map((_, index) => (
 | 
					 | 
				
			||||||
                <>
 | 
					 | 
				
			||||||
                    <Flex
 | 
					 | 
				
			||||||
                        className={styles['barcode-container']}
 | 
					 | 
				
			||||||
                        key={index}
 | 
					 | 
				
			||||||
                        justify={"center"}
 | 
					 | 
				
			||||||
                        direction={"column"}
 | 
					 | 
				
			||||||
                    >
 | 
					 | 
				
			||||||
                        <Flex align={"center"} justify={"center"}>
 | 
					 | 
				
			||||||
                            <Barcode margin={0} height={getBarcodeHeight()} value={barcode}/>
 | 
					 | 
				
			||||||
                        </Flex>
 | 
					 | 
				
			||||||
                        {attributes.slice(0, MAX_ATTRIBUTES).map((attr) => (
 | 
					 | 
				
			||||||
                            <Text
 | 
					 | 
				
			||||||
                                key={attr.name}
 | 
					 | 
				
			||||||
                                className={styles['barcode-attribute-text']}
 | 
					 | 
				
			||||||
                                size={"xs"}
 | 
					 | 
				
			||||||
                            >
 | 
					 | 
				
			||||||
                                {getAttributeText(attr)}
 | 
					 | 
				
			||||||
                            </Text>
 | 
					 | 
				
			||||||
                        ))}
 | 
					 | 
				
			||||||
                        {props.additionalField && (
 | 
					 | 
				
			||||||
                            <Text
 | 
					 | 
				
			||||||
                                className={styles['barcode-attribute-text']}
 | 
					 | 
				
			||||||
                                size={"xs"}
 | 
					 | 
				
			||||||
                            >
 | 
					 | 
				
			||||||
                                {props.additionalField}
 | 
					 | 
				
			||||||
                            </Text>
 | 
					 | 
				
			||||||
                        )}
 | 
					 | 
				
			||||||
                    </Flex>
 | 
					 | 
				
			||||||
                </>
 | 
					 | 
				
			||||||
            ))}
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default PrintBarcodeContainer;
 | 
					export default PrintBarcodeContainer;
 | 
				
			||||||
@@ -7,7 +7,6 @@
 | 
				
			|||||||
        display: block;
 | 
					        display: block;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    .barcode-container {
 | 
					    .barcode-container {
 | 
				
			||||||
        max-height: 1.45in;
 | 
					        max-height: 1.45in;
 | 
				
			||||||
        text-align: left;
 | 
					        text-align: left;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,25 +1,23 @@
 | 
				
			|||||||
import {ContextModalProps, modals} from "@mantine/modals";
 | 
					import { ContextModalProps, modals } from "@mantine/modals";
 | 
				
			||||||
import {Button, Divider, Flex, NumberInput, rem, Select} from "@mantine/core";
 | 
					import { Button, Divider, Flex, NumberInput, rem, Select } from "@mantine/core";
 | 
				
			||||||
import {useEffect, useRef, useState} from "react";
 | 
					import { useEffect, useRef, useState } from "react";
 | 
				
			||||||
import {BarcodeSchema, ProductService} from "../../client";
 | 
					import { BarcodeSchema, ProductService } from "../../client";
 | 
				
			||||||
import {useGetProductById} from "../../api/product/useGetProductById.tsx";
 | 
					import { useGetProductById } from "../../api/product/useGetProductById.tsx";
 | 
				
			||||||
import {notifications} from "../../shared/lib/notifications.ts";
 | 
					import { notifications } from "../../shared/lib/notifications.ts";
 | 
				
			||||||
import PrintBarcodeContainer from "./PrintBarcodeContainer.tsx";
 | 
					import PrintBarcodeContainer from "./PrintBarcodeContainer.tsx";
 | 
				
			||||||
import {base64ToBlob} from "../../shared/lib/utils.ts";
 | 
					import { base64ToBlob } from "../../shared/lib/utils.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
    productId: number;
 | 
					    productId: number;
 | 
				
			||||||
    defaultQuantity?: number;
 | 
					    defaultQuantity?: number;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
const PrintBarcodeModal = ({
 | 
					const PrintBarcodeModal = ({ innerProps }: ContextModalProps<Props>) => {
 | 
				
			||||||
                               innerProps
 | 
					    const { productId, defaultQuantity = 1 } = innerProps;
 | 
				
			||||||
                           }: ContextModalProps<Props>) => {
 | 
					 | 
				
			||||||
    const {productId, defaultQuantity = 1} = innerProps;
 | 
					 | 
				
			||||||
    const [quantity, setQuantity] = useState(defaultQuantity);
 | 
					    const [quantity, setQuantity] = useState(defaultQuantity);
 | 
				
			||||||
    const [barcode, setBarcode] = useState<string | undefined>()
 | 
					    const [barcode, setBarcode] = useState<string | undefined>();
 | 
				
			||||||
    const [barcodeData, setBarcodeData] = useState<BarcodeSchema | undefined>();
 | 
					    const [barcodeData, setBarcodeData] = useState<BarcodeSchema | undefined>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const {product, refetch} = useGetProductById(productId);
 | 
					    const { product, refetch } = useGetProductById(productId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const barcodeRef = useRef(null);
 | 
					    const barcodeRef = useRef(null);
 | 
				
			||||||
    // const handlePrint = useReactToPrint({
 | 
					    // const handlePrint = useReactToPrint({
 | 
				
			||||||
@@ -27,47 +25,47 @@ const PrintBarcodeModal = ({
 | 
				
			|||||||
    // });
 | 
					    // });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const onAdd = (newBarcode: string) => {
 | 
					    const onAdd = (newBarcode: string) => {
 | 
				
			||||||
        ProductService.addProductBarcode({requestBody: {productId, barcode: newBarcode}})
 | 
					        ProductService.addProductBarcode({
 | 
				
			||||||
            .then(async ({ok, message}) => {
 | 
					            requestBody: { productId, barcode: newBarcode },
 | 
				
			||||||
                notifications.guess(ok, {message});
 | 
					        }).then(async ({ ok, message }) => {
 | 
				
			||||||
            })
 | 
					            notifications.guess(ok, { message });
 | 
				
			||||||
    }
 | 
					        });
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
    const onAddClick = () => {
 | 
					    const onAddClick = () => {
 | 
				
			||||||
        if (!product) return;
 | 
					        if (!product) return;
 | 
				
			||||||
        modals.openContextModal({
 | 
					        modals.openContextModal({
 | 
				
			||||||
            modal: "addBarcode",
 | 
					            modal: "addBarcode",
 | 
				
			||||||
            title: 'Добавление штрихкода',
 | 
					            title: "Добавление штрихкода",
 | 
				
			||||||
            withCloseButton: true,
 | 
					            withCloseButton: true,
 | 
				
			||||||
            innerProps: {
 | 
					            innerProps: {
 | 
				
			||||||
                productId: product.id,
 | 
					                productId: product.id,
 | 
				
			||||||
                onSubmit: onAdd
 | 
					                onSubmit: onAdd,
 | 
				
			||||||
            }
 | 
					            },
 | 
				
			||||||
        })
 | 
					        });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const onGenerateClick = () => {
 | 
					    const onGenerateClick = () => {
 | 
				
			||||||
        if (!product) return;
 | 
					        if (!product) return;
 | 
				
			||||||
        ProductService.generateProductBarcode({requestBody: {productId}})
 | 
					        ProductService.generateProductBarcode({
 | 
				
			||||||
            .then(async ({ok, message, barcode}) => {
 | 
					            requestBody: { productId },
 | 
				
			||||||
                notifications.guess(ok, {message});
 | 
					        }).then(async ({ ok, message, barcode }) => {
 | 
				
			||||||
                if (!ok) return;
 | 
					            notifications.guess(ok, { message });
 | 
				
			||||||
                await refetch();
 | 
					            if (!ok) return;
 | 
				
			||||||
                setBarcode(barcode);
 | 
					            await refetch();
 | 
				
			||||||
            })
 | 
					            setBarcode(barcode);
 | 
				
			||||||
    }
 | 
					        });
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
    const fetchBarcodeData = () => {
 | 
					    const fetchBarcodeData = () => {
 | 
				
			||||||
        if (!barcode) return;
 | 
					        if (!barcode) return;
 | 
				
			||||||
        ProductService.getProductBarcode({
 | 
					        ProductService.getProductBarcode({
 | 
				
			||||||
            requestBody:
 | 
					            requestBody: { barcode, productId },
 | 
				
			||||||
                {barcode, productId}
 | 
					        }).then(({ barcode }) => {
 | 
				
			||||||
        }).then(({barcode}) => {
 | 
					 | 
				
			||||||
            setBarcodeData(barcode);
 | 
					            setBarcodeData(barcode);
 | 
				
			||||||
        })
 | 
					        });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        if (!product) return;
 | 
					        if (!product) return;
 | 
				
			||||||
        if (product.barcodes.length === 1)
 | 
					        if (product.barcodes.length === 1) setBarcode(product.barcodes[0]);
 | 
				
			||||||
            setBarcode(product.barcodes[0]);
 | 
					 | 
				
			||||||
    }, [product]);
 | 
					    }, [product]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
@@ -79,11 +77,10 @@ const PrintBarcodeModal = ({
 | 
				
			|||||||
        <>
 | 
					        <>
 | 
				
			||||||
            <Flex
 | 
					            <Flex
 | 
				
			||||||
                gap={rem(10)}
 | 
					                gap={rem(10)}
 | 
				
			||||||
                direction={"column"}
 | 
					                direction={"column"}>
 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
                <Select
 | 
					                <Select
 | 
				
			||||||
                    value={barcode}
 | 
					                    value={barcode}
 | 
				
			||||||
                    onChange={(value) => setBarcode(value || undefined)}
 | 
					                    onChange={value => setBarcode(value || undefined)}
 | 
				
			||||||
                    data={product?.barcodes}
 | 
					                    data={product?.barcodes}
 | 
				
			||||||
                    label={"Штрихкод"}
 | 
					                    label={"Штрихкод"}
 | 
				
			||||||
                    placeholder={"Выберите штрихкод"}
 | 
					                    placeholder={"Выберите штрихкод"}
 | 
				
			||||||
@@ -92,14 +89,16 @@ const PrintBarcodeModal = ({
 | 
				
			|||||||
                    label={"Количество копий"}
 | 
					                    label={"Количество копий"}
 | 
				
			||||||
                    placeholder={"Введите количество копий"}
 | 
					                    placeholder={"Введите количество копий"}
 | 
				
			||||||
                    value={quantity}
 | 
					                    value={quantity}
 | 
				
			||||||
                    onChange={(value) => typeof value === "number" && setQuantity(value)}
 | 
					                    onChange={value =>
 | 
				
			||||||
 | 
					                        typeof value === "number" && setQuantity(value)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                    min={1}
 | 
					                    min={1}
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <Divider
 | 
					                <Divider my={rem(10)} />
 | 
				
			||||||
                    my={rem(10)}
 | 
					                <Flex
 | 
				
			||||||
                />
 | 
					                    direction={"column"}
 | 
				
			||||||
                <Flex direction={"column"} gap={rem(10)}>
 | 
					                    gap={rem(10)}>
 | 
				
			||||||
                    <Flex gap={rem(10)}>
 | 
					                    <Flex gap={rem(10)}>
 | 
				
			||||||
                        <Button
 | 
					                        <Button
 | 
				
			||||||
                            onClick={() => onAddClick()}
 | 
					                            onClick={() => onAddClick()}
 | 
				
			||||||
@@ -119,25 +118,30 @@ const PrintBarcodeModal = ({
 | 
				
			|||||||
                        disabled={!barcode}
 | 
					                        disabled={!barcode}
 | 
				
			||||||
                        onClick={async () => {
 | 
					                        onClick={async () => {
 | 
				
			||||||
                            if (!barcode) return;
 | 
					                            if (!barcode) return;
 | 
				
			||||||
                            const response = await ProductService.getProductBarcodePdf({
 | 
					                            const response =
 | 
				
			||||||
                                requestBody: {
 | 
					                                await ProductService.getProductBarcodePdf({
 | 
				
			||||||
                                    productId,
 | 
					                                    requestBody: {
 | 
				
			||||||
                                    barcode,
 | 
					                                        productId,
 | 
				
			||||||
                                    quantity
 | 
					                                        barcode,
 | 
				
			||||||
                                }
 | 
					                                        quantity,
 | 
				
			||||||
                            });
 | 
					                                    },
 | 
				
			||||||
                            const pdfBlob = base64ToBlob(response.base64String, response.mimeType);
 | 
					                                });
 | 
				
			||||||
 | 
					                            const pdfBlob = base64ToBlob(
 | 
				
			||||||
 | 
					                                response.base64String,
 | 
				
			||||||
 | 
					                                response.mimeType
 | 
				
			||||||
 | 
					                            );
 | 
				
			||||||
                            const pdfUrl = URL.createObjectURL(pdfBlob);
 | 
					                            const pdfUrl = URL.createObjectURL(pdfBlob);
 | 
				
			||||||
                            const pdfWindow = window.open(pdfUrl);
 | 
					                            const pdfWindow = window.open(pdfUrl);
 | 
				
			||||||
                            if (!pdfWindow) {
 | 
					                            if (!pdfWindow) {
 | 
				
			||||||
                                notifications.error({message: "Ошибка"});
 | 
					                                notifications.error({ message: "Ошибка" });
 | 
				
			||||||
                                return
 | 
					                                return;
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            pdfWindow.onload = () => {
 | 
					                            pdfWindow.onload = () => {
 | 
				
			||||||
                                pdfWindow.print();
 | 
					                                pdfWindow.print();
 | 
				
			||||||
                            }
 | 
					                            };
 | 
				
			||||||
                        }}
 | 
					                        }}>
 | 
				
			||||||
                    >Печать</Button>
 | 
					                        Печать
 | 
				
			||||||
 | 
					                    </Button>
 | 
				
			||||||
                </Flex>
 | 
					                </Flex>
 | 
				
			||||||
            </Flex>
 | 
					            </Flex>
 | 
				
			||||||
            <PrintBarcodeContainer
 | 
					            <PrintBarcodeContainer
 | 
				
			||||||
@@ -148,7 +152,7 @@ const PrintBarcodeModal = ({
 | 
				
			|||||||
                additionalField={barcodeData?.additionalField}
 | 
					                additionalField={barcodeData?.additionalField}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
        </>
 | 
					        </>
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default PrintBarcodeModal;
 | 
					export default PrintBarcodeModal;
 | 
				
			||||||
@@ -1,29 +1,29 @@
 | 
				
			|||||||
import {GetServiceKitSchema} from "../../client";
 | 
					import { GetServiceKitSchema } from "../../client";
 | 
				
			||||||
import {ContextModalProps} from "@mantine/modals";
 | 
					import { ContextModalProps } from "@mantine/modals";
 | 
				
			||||||
import {useState} from "react";
 | 
					import { useState } from "react";
 | 
				
			||||||
import {Button, Flex, rem} from "@mantine/core";
 | 
					import { Button, Flex, rem } from "@mantine/core";
 | 
				
			||||||
import ServicesKitSelect from "../../components/Selects/ServicesKitSelect/ServicesKitSelect.tsx";
 | 
					import ServicesKitSelect from "../../components/Selects/ServicesKitSelect/ServicesKitSelect.tsx";
 | 
				
			||||||
import {notifications} from "../../shared/lib/notifications.ts";
 | 
					import { notifications } from "../../shared/lib/notifications.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
    onSelect: (kit: GetServiceKitSchema) => void;
 | 
					    onSelect: (kit: GetServiceKitSchema) => void;
 | 
				
			||||||
    serviceType: number;
 | 
					    serviceType: number;
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ServicesKitSelectModal = ({
 | 
					const ServicesKitSelectModal = ({
 | 
				
			||||||
                                    context,
 | 
					    context,
 | 
				
			||||||
                                    id,
 | 
					    id,
 | 
				
			||||||
                                    innerProps
 | 
					    innerProps,
 | 
				
			||||||
                                }: ContextModalProps<Props>) => {
 | 
					}: ContextModalProps<Props>) => {
 | 
				
			||||||
    const [kit, setKit] = useState<GetServiceKitSchema | undefined>();
 | 
					    const [kit, setKit] = useState<GetServiceKitSchema | undefined>();
 | 
				
			||||||
    const onSelectClick = () => {
 | 
					    const onSelectClick = () => {
 | 
				
			||||||
        if (!kit) {
 | 
					        if (!kit) {
 | 
				
			||||||
            notifications.error({message: "Выберите набор услуг"});
 | 
					            notifications.error({ message: "Выберите набор услуг" });
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        innerProps.onSelect(kit);
 | 
					        innerProps.onSelect(kit);
 | 
				
			||||||
        context.closeContextModal(id);
 | 
					        context.closeContextModal(id);
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Flex
 | 
					        <Flex
 | 
				
			||||||
            gap={rem(10)}
 | 
					            gap={rem(10)}
 | 
				
			||||||
@@ -35,26 +35,27 @@ const ServicesKitSelectModal = ({
 | 
				
			|||||||
                    placeholder={"Выберите набор услуг"}
 | 
					                    placeholder={"Выберите набор услуг"}
 | 
				
			||||||
                    value={kit}
 | 
					                    value={kit}
 | 
				
			||||||
                    onChange={setKit}
 | 
					                    onChange={setKit}
 | 
				
			||||||
                    filterBy={item => item.serviceType === innerProps.serviceType}
 | 
					                    filterBy={item =>
 | 
				
			||||||
 | 
					                        item.serviceType === innerProps.serviceType
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
            </Flex>
 | 
					            </Flex>
 | 
				
			||||||
            <Flex justify={"flex-end"} gap={rem(10)}>
 | 
					            <Flex
 | 
				
			||||||
 | 
					                justify={"flex-end"}
 | 
				
			||||||
 | 
					                gap={rem(10)}>
 | 
				
			||||||
                <Button
 | 
					                <Button
 | 
				
			||||||
                    variant={"subtle"}
 | 
					                    variant={"subtle"}
 | 
				
			||||||
                    onClick={() => context.closeContextModal(id)}
 | 
					                    onClick={() => context.closeContextModal(id)}>
 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                    Отменить
 | 
					                    Отменить
 | 
				
			||||||
                </Button>
 | 
					                </Button>
 | 
				
			||||||
                <Button
 | 
					                <Button
 | 
				
			||||||
                    onClick={onSelectClick}
 | 
					                    onClick={onSelectClick}
 | 
				
			||||||
                    variant={"default"}
 | 
					                    variant={"default"}>
 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                    Добавить
 | 
					                    Добавить
 | 
				
			||||||
                </Button>
 | 
					                </Button>
 | 
				
			||||||
 | 
					 | 
				
			||||||
            </Flex>
 | 
					            </Flex>
 | 
				
			||||||
        </Flex>
 | 
					        </Flex>
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default ServicesKitSelectModal;
 | 
					export default ServicesKitSelectModal;
 | 
				
			||||||
@@ -7,8 +7,7 @@ import AddDealServiceModal from "../pages/LeadsPage/modals/AddDealServiceModal.t
 | 
				
			|||||||
import AddDealProductModal from "../pages/LeadsPage/modals/AddDealProductModal.tsx";
 | 
					import AddDealProductModal from "../pages/LeadsPage/modals/AddDealProductModal.tsx";
 | 
				
			||||||
import PrintBarcodeModal from "./PrintBarcodeModal/PrintBarcodeModal.tsx";
 | 
					import PrintBarcodeModal from "./PrintBarcodeModal/PrintBarcodeModal.tsx";
 | 
				
			||||||
import AddBarcodeModal from "./AddBarcodeModal/AddBarcodeModal.tsx";
 | 
					import AddBarcodeModal from "./AddBarcodeModal/AddBarcodeModal.tsx";
 | 
				
			||||||
import BarcodeTemplateFormModal
 | 
					import BarcodeTemplateFormModal from "../pages/BarcodePage/modals/BarcodeTemplateFormModal/BarcodeTemplateFormModal.tsx";
 | 
				
			||||||
    from "../pages/BarcodePage/modals/BarcodeTemplateFormModal/BarcodeTemplateFormModal.tsx";
 | 
					 | 
				
			||||||
import ProductServiceFormModal from "../pages/LeadsPage/modals/ProductServiceFormModal.tsx";
 | 
					import ProductServiceFormModal from "../pages/LeadsPage/modals/ProductServiceFormModal.tsx";
 | 
				
			||||||
import UserFormModal from "../pages/AdminPage/modals/UserFormModal/UserFormModal.tsx";
 | 
					import UserFormModal from "../pages/AdminPage/modals/UserFormModal/UserFormModal.tsx";
 | 
				
			||||||
import EmployeeSelectModal from "./EmployeeSelectModal/EmployeeSelectModal.tsx";
 | 
					import EmployeeSelectModal from "./EmployeeSelectModal/EmployeeSelectModal.tsx";
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,75 +1,84 @@
 | 
				
			|||||||
import styles from './AdminPage.module.css';
 | 
					import styles from "./AdminPage.module.css";
 | 
				
			||||||
import {Tabs} from "@mantine/core";
 | 
					import { Tabs } from "@mantine/core";
 | 
				
			||||||
import PageBlock from "../../components/PageBlock/PageBlock.tsx";
 | 
					import PageBlock from "../../components/PageBlock/PageBlock.tsx";
 | 
				
			||||||
import {IconBriefcase, IconCalendarUser, IconCurrencyDollar, IconUser} from "@tabler/icons-react";
 | 
					import {
 | 
				
			||||||
 | 
					    IconBriefcase,
 | 
				
			||||||
 | 
					    IconCalendarUser,
 | 
				
			||||||
 | 
					    IconCurrencyDollar,
 | 
				
			||||||
 | 
					    IconUser,
 | 
				
			||||||
 | 
					} from "@tabler/icons-react";
 | 
				
			||||||
import RolesAndPositionsTab from "./tabs/RolesAndPositions/RolesAndPositionsTab.tsx";
 | 
					import RolesAndPositionsTab from "./tabs/RolesAndPositions/RolesAndPositionsTab.tsx";
 | 
				
			||||||
import UsersTab from "./tabs/Users/UsersTab.tsx";
 | 
					import UsersTab from "./tabs/Users/UsersTab.tsx";
 | 
				
			||||||
import {motion} from "framer-motion";
 | 
					import { motion } from "framer-motion";
 | 
				
			||||||
import FinancesTab from "./tabs/Finances/FinancesTab.tsx";
 | 
					import FinancesTab from "./tabs/Finances/FinancesTab.tsx";
 | 
				
			||||||
import WorkTimeTable from "./tabs/WorkTimeTable/ui/WorkTimeTable.tsx";
 | 
					import WorkTimeTable from "./tabs/WorkTimeTable/ui/WorkTimeTable.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const AdminPage = () => {
 | 
					const AdminPage = () => {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <div className={styles['container']}>
 | 
					        <div className={styles["container"]}>
 | 
				
			||||||
            <PageBlock fullHeight>
 | 
					            <PageBlock fullHeight>
 | 
				
			||||||
                <Tabs variant={"outline"} keepMounted={false} defaultValue={"users"}>
 | 
					                <Tabs
 | 
				
			||||||
 | 
					                    variant={"outline"}
 | 
				
			||||||
 | 
					                    keepMounted={false}
 | 
				
			||||||
 | 
					                    defaultValue={"users"}>
 | 
				
			||||||
                    <Tabs.List>
 | 
					                    <Tabs.List>
 | 
				
			||||||
                        <Tabs.Tab value={"users"} leftSection={<IconUser/>}>
 | 
					                        <Tabs.Tab
 | 
				
			||||||
 | 
					                            value={"users"}
 | 
				
			||||||
 | 
					                            leftSection={<IconUser />}>
 | 
				
			||||||
                            Пользователи
 | 
					                            Пользователи
 | 
				
			||||||
                        </Tabs.Tab>
 | 
					                        </Tabs.Tab>
 | 
				
			||||||
                        <Tabs.Tab value={"finances"} leftSection={<IconCurrencyDollar/>}>
 | 
					                        <Tabs.Tab
 | 
				
			||||||
 | 
					                            value={"finances"}
 | 
				
			||||||
 | 
					                            leftSection={<IconCurrencyDollar />}>
 | 
				
			||||||
                            Финансы
 | 
					                            Финансы
 | 
				
			||||||
                        </Tabs.Tab>
 | 
					                        </Tabs.Tab>
 | 
				
			||||||
                        <Tabs.Tab value={"rolesAndPositions"} leftSection={<IconBriefcase/>}>
 | 
					                        <Tabs.Tab
 | 
				
			||||||
 | 
					                            value={"rolesAndPositions"}
 | 
				
			||||||
 | 
					                            leftSection={<IconBriefcase />}>
 | 
				
			||||||
                            Должности
 | 
					                            Должности
 | 
				
			||||||
                        </Tabs.Tab>
 | 
					                        </Tabs.Tab>
 | 
				
			||||||
                        <Tabs.Tab value={"workTimeTable"} leftSection={<IconCalendarUser/>}>
 | 
					                        <Tabs.Tab
 | 
				
			||||||
 | 
					                            value={"workTimeTable"}
 | 
				
			||||||
 | 
					                            leftSection={<IconCalendarUser />}>
 | 
				
			||||||
                            Рабочее время
 | 
					                            Рабочее время
 | 
				
			||||||
                        </Tabs.Tab>
 | 
					                        </Tabs.Tab>
 | 
				
			||||||
                    </Tabs.List>
 | 
					                    </Tabs.List>
 | 
				
			||||||
                    <Tabs.Panel value={"users"}>
 | 
					                    <Tabs.Panel value={"users"}>
 | 
				
			||||||
                        <motion.div
 | 
					                        <motion.div
 | 
				
			||||||
                            initial={{opacity: 0}}
 | 
					                            initial={{ opacity: 0 }}
 | 
				
			||||||
                            animate={{opacity: 1}}
 | 
					                            animate={{ opacity: 1 }}
 | 
				
			||||||
                            transition={{duration: 0.2}}
 | 
					                            transition={{ duration: 0.2 }}>
 | 
				
			||||||
                        >
 | 
					                            <UsersTab />
 | 
				
			||||||
                            <UsersTab/>
 | 
					 | 
				
			||||||
                        </motion.div>
 | 
					                        </motion.div>
 | 
				
			||||||
                    </Tabs.Panel>
 | 
					                    </Tabs.Panel>
 | 
				
			||||||
                    <Tabs.Panel value={"rolesAndPositions"}>
 | 
					                    <Tabs.Panel value={"rolesAndPositions"}>
 | 
				
			||||||
                        <motion.div
 | 
					                        <motion.div
 | 
				
			||||||
                            initial={{opacity: 0}}
 | 
					                            initial={{ opacity: 0 }}
 | 
				
			||||||
                            animate={{opacity: 1}}
 | 
					                            animate={{ opacity: 1 }}
 | 
				
			||||||
                            transition={{duration: 0.2}}
 | 
					                            transition={{ duration: 0.2 }}>
 | 
				
			||||||
                        >
 | 
					                            <RolesAndPositionsTab />
 | 
				
			||||||
                            <RolesAndPositionsTab/>
 | 
					 | 
				
			||||||
                        </motion.div>
 | 
					                        </motion.div>
 | 
				
			||||||
                    </Tabs.Panel>
 | 
					                    </Tabs.Panel>
 | 
				
			||||||
                    <Tabs.Panel value={"finances"}>
 | 
					                    <Tabs.Panel value={"finances"}>
 | 
				
			||||||
                        <motion.div
 | 
					                        <motion.div
 | 
				
			||||||
                            initial={{opacity: 0}}
 | 
					                            initial={{ opacity: 0 }}
 | 
				
			||||||
                            animate={{opacity: 1}}
 | 
					                            animate={{ opacity: 1 }}
 | 
				
			||||||
                            transition={{duration: 0.2}}
 | 
					                            transition={{ duration: 0.2 }}>
 | 
				
			||||||
                        >
 | 
					                            <FinancesTab />
 | 
				
			||||||
                            <FinancesTab/>
 | 
					 | 
				
			||||||
                        </motion.div>
 | 
					                        </motion.div>
 | 
				
			||||||
                    </Tabs.Panel>
 | 
					                    </Tabs.Panel>
 | 
				
			||||||
                    <Tabs.Panel value={"workTimeTable"}>
 | 
					                    <Tabs.Panel value={"workTimeTable"}>
 | 
				
			||||||
                        <motion.div
 | 
					                        <motion.div
 | 
				
			||||||
                            initial={{opacity: 0}}
 | 
					                            initial={{ opacity: 0 }}
 | 
				
			||||||
                            animate={{opacity: 1}}
 | 
					                            animate={{ opacity: 1 }}
 | 
				
			||||||
                            transition={{duration: 0.2}}
 | 
					                            transition={{ duration: 0.2 }}>
 | 
				
			||||||
                        >
 | 
					                            <WorkTimeTable />
 | 
				
			||||||
                            <WorkTimeTable/>
 | 
					 | 
				
			||||||
                        </motion.div>
 | 
					                        </motion.div>
 | 
				
			||||||
                    </Tabs.Panel>
 | 
					                    </Tabs.Panel>
 | 
				
			||||||
 | 
					 | 
				
			||||||
                </Tabs>
 | 
					                </Tabs>
 | 
				
			||||||
            </PageBlock>
 | 
					            </PageBlock>
 | 
				
			||||||
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default AdminPage;
 | 
					export default AdminPage;
 | 
				
			||||||
@@ -1,16 +1,16 @@
 | 
				
			|||||||
import {CRUDTableProps} from "../../../../types/CRUDTable.tsx";
 | 
					import { CRUDTableProps } from "../../../../types/CRUDTable.tsx";
 | 
				
			||||||
import {PayRateSchema} from "../../../../client";
 | 
					import { PayRateSchema } from "../../../../client";
 | 
				
			||||||
import {FC} from "react";
 | 
					import { FC } from "react";
 | 
				
			||||||
import {BaseTable} from "../../../../components/BaseTable/BaseTable.tsx";
 | 
					import { BaseTable } from "../../../../components/BaseTable/BaseTable.tsx";
 | 
				
			||||||
import {usePayRatesTableColumns} from "./columns.tsx";
 | 
					import { usePayRatesTableColumns } from "./columns.tsx";
 | 
				
			||||||
import {ActionIcon, Button, Flex, rem, Text, Tooltip} from "@mantine/core";
 | 
					import { ActionIcon, Button, Flex, rem, Text, Tooltip } from "@mantine/core";
 | 
				
			||||||
import {modals} from "@mantine/modals";
 | 
					import { modals } from "@mantine/modals";
 | 
				
			||||||
import {IconEdit, IconTrash} from "@tabler/icons-react";
 | 
					import { IconEdit, IconTrash } from "@tabler/icons-react";
 | 
				
			||||||
import {MRT_TableOptions} from "mantine-react-table";
 | 
					import { MRT_TableOptions } from "mantine-react-table";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = CRUDTableProps<PayRateSchema>;
 | 
					type Props = CRUDTableProps<PayRateSchema>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const PayRateTable: FC<Props> = ({items, onCreate, onChange, onDelete}) => {
 | 
					const PayRateTable: FC<Props> = ({ items, onCreate, onChange, onDelete }) => {
 | 
				
			||||||
    const columns = usePayRatesTableColumns();
 | 
					    const columns = usePayRatesTableColumns();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const onCreateClick = () => {
 | 
					    const onCreateClick = () => {
 | 
				
			||||||
@@ -19,76 +19,77 @@ const PayRateTable: FC<Props> = ({items, onCreate, onChange, onDelete}) => {
 | 
				
			|||||||
            modal: "payRateForm",
 | 
					            modal: "payRateForm",
 | 
				
			||||||
            withCloseButton: false,
 | 
					            withCloseButton: false,
 | 
				
			||||||
            innerProps: {
 | 
					            innerProps: {
 | 
				
			||||||
                onCreate: onCreate
 | 
					                onCreate: onCreate,
 | 
				
			||||||
            }
 | 
					            },
 | 
				
			||||||
        })
 | 
					        });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    const onEditClick = (payRate: PayRateSchema) => {
 | 
					    const onEditClick = (payRate: PayRateSchema) => {
 | 
				
			||||||
        if (!onChange) return;
 | 
					        if (!onChange) return;
 | 
				
			||||||
        modals.openContextModal({
 | 
					        modals.openContextModal({
 | 
				
			||||||
            modal: "payRateForm",
 | 
					            modal: "payRateForm",
 | 
				
			||||||
            withCloseButton: false,
 | 
					            withCloseButton: false,
 | 
				
			||||||
            innerProps: {
 | 
					            innerProps: {
 | 
				
			||||||
                onChange: (event) => onChange({...event, id: payRate.id}),
 | 
					                onChange: event => onChange({ ...event, id: payRate.id }),
 | 
				
			||||||
                element: payRate
 | 
					                element: payRate,
 | 
				
			||||||
            }
 | 
					            },
 | 
				
			||||||
        })
 | 
					        });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    const onDeleteClick = (payRate: PayRateSchema) => {
 | 
					    const onDeleteClick = (payRate: PayRateSchema) => {
 | 
				
			||||||
        if (!onDelete) return;
 | 
					        if (!onDelete) return;
 | 
				
			||||||
        modals.openConfirmModal({
 | 
					        modals.openConfirmModal({
 | 
				
			||||||
            title: 'Удаление тарифа',
 | 
					            title: "Удаление тарифа",
 | 
				
			||||||
            children: (
 | 
					            children: (
 | 
				
			||||||
                <Text size="sm">
 | 
					                <Text size="sm">
 | 
				
			||||||
                    Вы уверены что хотите удалить тариф {payRate.name}
 | 
					                    Вы уверены что хотите удалить тариф {payRate.name}
 | 
				
			||||||
                </Text>
 | 
					                </Text>
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            labels: {confirm: 'Да', cancel: "Нет"},
 | 
					            labels: { confirm: "Да", cancel: "Нет" },
 | 
				
			||||||
            confirmProps: {color: 'red'},
 | 
					            confirmProps: { color: "red" },
 | 
				
			||||||
            onConfirm: () => onDelete(payRate)
 | 
					            onConfirm: () => onDelete(payRate),
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <BaseTable
 | 
					        <BaseTable
 | 
				
			||||||
            data={items}
 | 
					            data={items}
 | 
				
			||||||
            columns={columns}
 | 
					            columns={columns}
 | 
				
			||||||
            restProps={{
 | 
					            restProps={
 | 
				
			||||||
                enableSorting: false,
 | 
					                {
 | 
				
			||||||
                enableColumnActions: false,
 | 
					                    enableSorting: false,
 | 
				
			||||||
                enableTopToolbar: true,
 | 
					                    enableColumnActions: false,
 | 
				
			||||||
                renderTopToolbar: (
 | 
					                    enableTopToolbar: true,
 | 
				
			||||||
                    <Flex p={rem(10)}>
 | 
					                    renderTopToolbar: (
 | 
				
			||||||
 | 
					                        <Flex p={rem(10)}>
 | 
				
			||||||
                        <Button
 | 
					                            <Button
 | 
				
			||||||
                            variant={"default"}
 | 
					                                variant={"default"}
 | 
				
			||||||
                            onClick={() => onCreateClick()}
 | 
					                                onClick={() => onCreateClick()}>
 | 
				
			||||||
                        >
 | 
					                                Создать тариф
 | 
				
			||||||
                            Создать тариф
 | 
					                            </Button>
 | 
				
			||||||
                        </Button>
 | 
					                        </Flex>
 | 
				
			||||||
                    </Flex>
 | 
					                    ),
 | 
				
			||||||
 | 
					                    enableRowActions: true,
 | 
				
			||||||
                ),
 | 
					                    renderRowActions: ({ row }) => (
 | 
				
			||||||
                enableRowActions: true,
 | 
					                        <Flex gap="md">
 | 
				
			||||||
                renderRowActions: ({row}) => (
 | 
					                            <Tooltip label="Редактировать">
 | 
				
			||||||
                    <Flex gap="md">
 | 
					                                <ActionIcon
 | 
				
			||||||
                        <Tooltip label="Редактировать">
 | 
					                                    onClick={() => onEditClick(row.original)}
 | 
				
			||||||
                            <ActionIcon
 | 
					                                    variant={"default"}>
 | 
				
			||||||
                                onClick={() => onEditClick(row.original)}
 | 
					                                    <IconEdit />
 | 
				
			||||||
                                variant={"default"}>
 | 
					                                </ActionIcon>
 | 
				
			||||||
                                <IconEdit/>
 | 
					                            </Tooltip>
 | 
				
			||||||
                            </ActionIcon>
 | 
					                            <Tooltip label="Удалить">
 | 
				
			||||||
                        </Tooltip>
 | 
					                                <ActionIcon
 | 
				
			||||||
                        <Tooltip label="Удалить">
 | 
					                                    onClick={() => onDeleteClick(row.original)}
 | 
				
			||||||
                            <ActionIcon onClick={() => onDeleteClick(row.original)} variant={"default"}>
 | 
					                                    variant={"default"}>
 | 
				
			||||||
                                <IconTrash/>
 | 
					                                    <IconTrash />
 | 
				
			||||||
                            </ActionIcon>
 | 
					                                </ActionIcon>
 | 
				
			||||||
                        </Tooltip>
 | 
					                            </Tooltip>
 | 
				
			||||||
                    </Flex>
 | 
					                        </Flex>
 | 
				
			||||||
                )
 | 
					                    ),
 | 
				
			||||||
            } as MRT_TableOptions<PayRateSchema>}
 | 
					                } as MRT_TableOptions<PayRateSchema>
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default PayRateTable;
 | 
					export default PayRateTable;
 | 
				
			||||||
@@ -1,32 +1,36 @@
 | 
				
			|||||||
import {useMemo} from "react";
 | 
					import { useMemo } from "react";
 | 
				
			||||||
import {MRT_ColumnDef} from "mantine-react-table";
 | 
					import { MRT_ColumnDef } from "mantine-react-table";
 | 
				
			||||||
import {PayRateSchema} from "../../../../client";
 | 
					import { PayRateSchema } from "../../../../client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const usePayRatesTableColumns = () => {
 | 
					export const usePayRatesTableColumns = () => {
 | 
				
			||||||
    return useMemo<MRT_ColumnDef<PayRateSchema>[]>(() => [
 | 
					    return useMemo<MRT_ColumnDef<PayRateSchema>[]>(
 | 
				
			||||||
        {
 | 
					        () => [
 | 
				
			||||||
            accessorKey: "name",
 | 
					            {
 | 
				
			||||||
            header: "Название тарифа"
 | 
					                accessorKey: "name",
 | 
				
			||||||
        },
 | 
					                header: "Название тарифа",
 | 
				
			||||||
        {
 | 
					            },
 | 
				
			||||||
            accessorKey: "payrollScheme.name",
 | 
					            {
 | 
				
			||||||
            header: "Система оплаты"
 | 
					                accessorKey: "payrollScheme.name",
 | 
				
			||||||
        },
 | 
					                header: "Система оплаты",
 | 
				
			||||||
        {
 | 
					            },
 | 
				
			||||||
            accessorKey: "baseRate",
 | 
					            {
 | 
				
			||||||
            header: "Базовая ставка",
 | 
					                accessorKey: "baseRate",
 | 
				
			||||||
            Cell: ({row}) => `${row.original.baseRate.toLocaleString("ru")}₽`
 | 
					                header: "Базовая ставка",
 | 
				
			||||||
 | 
					                Cell: ({ row }) =>
 | 
				
			||||||
        },
 | 
					                    `${row.original.baseRate.toLocaleString("ru")}₽`,
 | 
				
			||||||
        {
 | 
					            },
 | 
				
			||||||
            accessorKey: "overtimeThreshold",
 | 
					            {
 | 
				
			||||||
            header: "Порог сверхурочных"
 | 
					                accessorKey: "overtimeThreshold",
 | 
				
			||||||
        },
 | 
					                header: "Порог сверхурочных",
 | 
				
			||||||
        {
 | 
					            },
 | 
				
			||||||
            accessorKey: "overtimeRate",
 | 
					            {
 | 
				
			||||||
            header: "Сверхурочная ставка",
 | 
					                accessorKey: "overtimeRate",
 | 
				
			||||||
            Cell: ({row}) => row.original.overtimeRate && `${row.original.overtimeRate.toLocaleString("ru")}₽`
 | 
					                header: "Сверхурочная ставка",
 | 
				
			||||||
 | 
					                Cell: ({ row }) =>
 | 
				
			||||||
        }
 | 
					                    row.original.overtimeRate &&
 | 
				
			||||||
    ], []);
 | 
					                    `${row.original.overtimeRate.toLocaleString("ru")}₽`,
 | 
				
			||||||
}
 | 
					            },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        []
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,14 +1,26 @@
 | 
				
			|||||||
import {FC, useEffect, useState} from "react";
 | 
					import { FC, useEffect, useState } from "react";
 | 
				
			||||||
import {ActionIcon, Button, Flex, Pagination, rem, Text, Tooltip} from "@mantine/core";
 | 
					import {
 | 
				
			||||||
import {usePaymentRecordsList} from "../../../../hooks/usePaymentRecordsList.tsx";
 | 
					    ActionIcon,
 | 
				
			||||||
import {BaseTable} from "../../../../components/BaseTable/BaseTable.tsx";
 | 
					    Button,
 | 
				
			||||||
import {usePaymentRecordsTableColumns} from "./columns.tsx";
 | 
					    Flex,
 | 
				
			||||||
import {modals} from "@mantine/modals";
 | 
					    Pagination,
 | 
				
			||||||
import {PaymentRecordCreateSchema, PaymentRecordGetSchema, PayrollService} from "../../../../client";
 | 
					    rem,
 | 
				
			||||||
import {notifications} from "../../../../shared/lib/notifications.ts";
 | 
					    Text,
 | 
				
			||||||
import {IconTrash} from "@tabler/icons-react";
 | 
					    Tooltip,
 | 
				
			||||||
import {MRT_TableOptions} from "mantine-react-table";
 | 
					} from "@mantine/core";
 | 
				
			||||||
import {formatDate} from "../../../../types/utils.ts";
 | 
					import { usePaymentRecordsList } from "../../../../hooks/usePaymentRecordsList.tsx";
 | 
				
			||||||
 | 
					import { BaseTable } from "../../../../components/BaseTable/BaseTable.tsx";
 | 
				
			||||||
 | 
					import { usePaymentRecordsTableColumns } from "./columns.tsx";
 | 
				
			||||||
 | 
					import { modals } from "@mantine/modals";
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    PaymentRecordCreateSchema,
 | 
				
			||||||
 | 
					    PaymentRecordGetSchema,
 | 
				
			||||||
 | 
					    PayrollService,
 | 
				
			||||||
 | 
					} from "../../../../client";
 | 
				
			||||||
 | 
					import { notifications } from "../../../../shared/lib/notifications.ts";
 | 
				
			||||||
 | 
					import { IconTrash } from "@tabler/icons-react";
 | 
				
			||||||
 | 
					import { MRT_TableOptions } from "mantine-react-table";
 | 
				
			||||||
 | 
					import { formatDate } from "../../../../types/utils.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const PaymentRecordsTable: FC = () => {
 | 
					const PaymentRecordsTable: FC = () => {
 | 
				
			||||||
    const [totalPages, setTotalPages] = useState(10);
 | 
					    const [totalPages, setTotalPages] = useState(10);
 | 
				
			||||||
@@ -16,8 +28,8 @@ const PaymentRecordsTable: FC = () => {
 | 
				
			|||||||
    const {
 | 
					    const {
 | 
				
			||||||
        pagination: paginationInfo,
 | 
					        pagination: paginationInfo,
 | 
				
			||||||
        objects: paymentRecords,
 | 
					        objects: paymentRecords,
 | 
				
			||||||
        refetch
 | 
					        refetch,
 | 
				
			||||||
    } = usePaymentRecordsList({page: page, itemsPerPage: 10});
 | 
					    } = usePaymentRecordsList({ page: page, itemsPerPage: 10 });
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        if (!paginationInfo) return;
 | 
					        if (!paginationInfo) return;
 | 
				
			||||||
        setTotalPages(paginationInfo.totalPages);
 | 
					        setTotalPages(paginationInfo.totalPages);
 | 
				
			||||||
@@ -26,98 +38,99 @@ const PaymentRecordsTable: FC = () => {
 | 
				
			|||||||
    const onCreate = (request: PaymentRecordCreateSchema) => {
 | 
					    const onCreate = (request: PaymentRecordCreateSchema) => {
 | 
				
			||||||
        PayrollService.createPaymentRecord({
 | 
					        PayrollService.createPaymentRecord({
 | 
				
			||||||
            requestBody: {
 | 
					            requestBody: {
 | 
				
			||||||
                data: request
 | 
					                data: request,
 | 
				
			||||||
            }
 | 
					            },
 | 
				
			||||||
        }).then(async ({ok, message}) => {
 | 
					        }).then(async ({ ok, message }) => {
 | 
				
			||||||
            notifications.guess(ok, {message});
 | 
					            notifications.guess(ok, { message });
 | 
				
			||||||
            if (!ok) return;
 | 
					            if (!ok) return;
 | 
				
			||||||
            await refetch();
 | 
					            await refetch();
 | 
				
			||||||
        })
 | 
					        });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    const onCreateClick = () => {
 | 
					    const onCreateClick = () => {
 | 
				
			||||||
        modals.openContextModal({
 | 
					        modals.openContextModal({
 | 
				
			||||||
            modal: "createPaymentRecord",
 | 
					            modal: "createPaymentRecord",
 | 
				
			||||||
            title: "Создание начисления",
 | 
					            title: "Создание начисления",
 | 
				
			||||||
            innerProps: {
 | 
					            innerProps: {
 | 
				
			||||||
                onCreate: onCreate
 | 
					                onCreate: onCreate,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        })
 | 
					        });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    const onDelete = (record: PaymentRecordGetSchema) => {
 | 
					    const onDelete = (record: PaymentRecordGetSchema) => {
 | 
				
			||||||
        PayrollService.deletePaymentRecord({
 | 
					        PayrollService.deletePaymentRecord({
 | 
				
			||||||
            requestBody: {
 | 
					            requestBody: {
 | 
				
			||||||
                paymentRecordId: record.id
 | 
					                paymentRecordId: record.id,
 | 
				
			||||||
            }
 | 
					            },
 | 
				
			||||||
        }).then(async ({ok, message}) => {
 | 
					        }).then(async ({ ok, message }) => {
 | 
				
			||||||
            notifications.guess(ok, {message});
 | 
					            notifications.guess(ok, { message });
 | 
				
			||||||
            if (!ok) return;
 | 
					            if (!ok) return;
 | 
				
			||||||
            await refetch();
 | 
					            await refetch();
 | 
				
			||||||
        })
 | 
					        });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    const onDeleteClick = (record: PaymentRecordGetSchema) => {
 | 
					    const onDeleteClick = (record: PaymentRecordGetSchema) => {
 | 
				
			||||||
        modals.openConfirmModal({
 | 
					        modals.openConfirmModal({
 | 
				
			||||||
            title: 'Удаление начисления',
 | 
					            title: "Удаление начисления",
 | 
				
			||||||
            children: (
 | 
					            children: (
 | 
				
			||||||
                <Text size="sm">
 | 
					                <Text size="sm">
 | 
				
			||||||
                    Вы уверены что хотите удалить начисление
 | 
					                    Вы уверены что хотите удалить начисление пользователю{" "}
 | 
				
			||||||
                    пользователю {record.user.firstName} {record.user.secondName} от {formatDate(record.createdAt)}
 | 
					                    {record.user.firstName} {record.user.secondName} от{" "}
 | 
				
			||||||
 | 
					                    {formatDate(record.createdAt)}
 | 
				
			||||||
                </Text>
 | 
					                </Text>
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            labels: {confirm: 'Да', cancel: "Нет"},
 | 
					            labels: { confirm: "Да", cancel: "Нет" },
 | 
				
			||||||
            confirmProps: {color: 'red'},
 | 
					            confirmProps: { color: "red" },
 | 
				
			||||||
            onConfirm: () => onDelete(record)
 | 
					            onConfirm: () => onDelete(record),
 | 
				
			||||||
        })
 | 
					        });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    const columns = usePaymentRecordsTableColumns();
 | 
					    const columns = usePaymentRecordsTableColumns();
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Flex
 | 
					        <Flex
 | 
				
			||||||
            direction={"column"}
 | 
					            direction={"column"}
 | 
				
			||||||
            h={"100%"}
 | 
					            h={"100%"}
 | 
				
			||||||
            gap={rem(10)}
 | 
					            gap={rem(10)}>
 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
            <BaseTable
 | 
					            <BaseTable
 | 
				
			||||||
                data={paymentRecords}
 | 
					                data={paymentRecords}
 | 
				
			||||||
                columns={columns}
 | 
					                columns={columns}
 | 
				
			||||||
                restProps={{
 | 
					                restProps={
 | 
				
			||||||
                    enableSorting: false,
 | 
					                    {
 | 
				
			||||||
                    enableColumnActions: false,
 | 
					                        enableSorting: false,
 | 
				
			||||||
                    enableTopToolbar: true,
 | 
					                        enableColumnActions: false,
 | 
				
			||||||
                    renderTopToolbar: (
 | 
					                        enableTopToolbar: true,
 | 
				
			||||||
                        <Flex p={rem(10)}>
 | 
					                        renderTopToolbar: (
 | 
				
			||||||
 | 
					                            <Flex p={rem(10)}>
 | 
				
			||||||
                            <Button
 | 
					                                <Button
 | 
				
			||||||
                                variant={"default"}
 | 
					                                    variant={"default"}
 | 
				
			||||||
                                onClick={() => onCreateClick()}
 | 
					                                    onClick={() => onCreateClick()}>
 | 
				
			||||||
                            >
 | 
					                                    Создать начисление
 | 
				
			||||||
                                Создать начисление
 | 
					                                </Button>
 | 
				
			||||||
                            </Button>
 | 
					                            </Flex>
 | 
				
			||||||
                        </Flex>
 | 
					                        ),
 | 
				
			||||||
                    ),
 | 
					                        enableRowActions: true,
 | 
				
			||||||
                    enableRowActions: true,
 | 
					                        renderRowActions: ({ row }) => (
 | 
				
			||||||
                    renderRowActions: ({row}) => (
 | 
					                            <Flex gap="md">
 | 
				
			||||||
                        <Flex gap="md">
 | 
					                                <Tooltip label="Удалить">
 | 
				
			||||||
 | 
					                                    <ActionIcon
 | 
				
			||||||
                            <Tooltip label="Удалить">
 | 
					                                        onClick={() =>
 | 
				
			||||||
                                <ActionIcon onClick={() => onDeleteClick(row.original)} variant={"default"}>
 | 
					                                            onDeleteClick(row.original)
 | 
				
			||||||
                                    <IconTrash/>
 | 
					                                        }
 | 
				
			||||||
                                </ActionIcon>
 | 
					                                        variant={"default"}>
 | 
				
			||||||
                            </Tooltip>
 | 
					                                        <IconTrash />
 | 
				
			||||||
                        </Flex>
 | 
					                                    </ActionIcon>
 | 
				
			||||||
                    )
 | 
					                                </Tooltip>
 | 
				
			||||||
                } as MRT_TableOptions<PaymentRecordGetSchema>}
 | 
					                            </Flex>
 | 
				
			||||||
 | 
					                        ),
 | 
				
			||||||
 | 
					                    } as MRT_TableOptions<PaymentRecordGetSchema>
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
            {totalPages > 1 &&
 | 
					            {totalPages > 1 && (
 | 
				
			||||||
 | 
					 | 
				
			||||||
                <Pagination
 | 
					                <Pagination
 | 
				
			||||||
                    style={{alignSelf: "flex-end"}}
 | 
					                    style={{ alignSelf: "flex-end" }}
 | 
				
			||||||
                    withEdges
 | 
					                    withEdges
 | 
				
			||||||
                    onChange={event => setPage(event)}
 | 
					                    onChange={event => setPage(event)}
 | 
				
			||||||
                    value={page}
 | 
					                    value={page}
 | 
				
			||||||
                    total={totalPages}
 | 
					                    total={totalPages}
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
            }
 | 
					            )}
 | 
				
			||||||
 | 
					 | 
				
			||||||
        </Flex>
 | 
					        </Flex>
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
export default PaymentRecordsTable;
 | 
					export default PaymentRecordsTable;
 | 
				
			||||||
@@ -1,58 +1,79 @@
 | 
				
			|||||||
import {useMemo} from "react";
 | 
					import { useMemo } from "react";
 | 
				
			||||||
import {MRT_ColumnDef} from "mantine-react-table";
 | 
					import { MRT_ColumnDef } from "mantine-react-table";
 | 
				
			||||||
import {PaymentRecordGetSchema} from "../../../../client";
 | 
					import { PaymentRecordGetSchema } from "../../../../client";
 | 
				
			||||||
import {PaySchemeType} from "../../../../shared/enums/PaySchemeType.ts";
 | 
					import { PaySchemeType } from "../../../../shared/enums/PaySchemeType.ts";
 | 
				
			||||||
import {getPluralForm} from "../../../../shared/lib/utils.ts";
 | 
					import { getPluralForm } from "../../../../shared/lib/utils.ts";
 | 
				
			||||||
import {formatDate} from "../../../../types/utils.ts";
 | 
					import { formatDate } from "../../../../types/utils.ts";
 | 
				
			||||||
import {isEqual} from "lodash";
 | 
					import { isEqual } from "lodash";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const usePaymentRecordsTableColumns = () => {
 | 
					export const usePaymentRecordsTableColumns = () => {
 | 
				
			||||||
    const getWorkUnitsText = (paymentRecord: PaymentRecordGetSchema) => {
 | 
					    const getWorkUnitsText = (paymentRecord: PaymentRecordGetSchema) => {
 | 
				
			||||||
        const payrollScheme = paymentRecord.payrollScheme;
 | 
					        const payrollScheme = paymentRecord.payrollScheme;
 | 
				
			||||||
        if (payrollScheme.key === PaySchemeType.HOURLY) {
 | 
					        if (payrollScheme.key === PaySchemeType.HOURLY) {
 | 
				
			||||||
            return getPluralForm(paymentRecord.workUnits, "час", "часа", "часов")
 | 
					            return getPluralForm(
 | 
				
			||||||
        } else if (
 | 
					                paymentRecord.workUnits,
 | 
				
			||||||
            payrollScheme.key === PaySchemeType.DAILY
 | 
					                "час",
 | 
				
			||||||
        ) {
 | 
					                "часа",
 | 
				
			||||||
            return getPluralForm(paymentRecord.workUnits, "день", "дня", "дней")
 | 
					                "часов"
 | 
				
			||||||
        } else if (
 | 
					            );
 | 
				
			||||||
            payrollScheme.key === PaySchemeType.MONTHLY
 | 
					        } else if (payrollScheme.key === PaySchemeType.DAILY) {
 | 
				
			||||||
        ) {
 | 
					            return getPluralForm(
 | 
				
			||||||
            return getPluralForm(paymentRecord.workUnits, "месяц", "месяца", "месяцев");
 | 
					                paymentRecord.workUnits,
 | 
				
			||||||
 | 
					                "день",
 | 
				
			||||||
 | 
					                "дня",
 | 
				
			||||||
 | 
					                "дней"
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        } else if (payrollScheme.key === PaySchemeType.MONTHLY) {
 | 
				
			||||||
 | 
					            return getPluralForm(
 | 
				
			||||||
 | 
					                paymentRecord.workUnits,
 | 
				
			||||||
 | 
					                "месяц",
 | 
				
			||||||
 | 
					                "месяца",
 | 
				
			||||||
 | 
					                "месяцев"
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return "";
 | 
					        return "";
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    const getDateRangesText = (paymentRecord: PaymentRecordGetSchema) => {
 | 
					    const getDateRangesText = (paymentRecord: PaymentRecordGetSchema) => {
 | 
				
			||||||
        if (paymentRecord.endDate && !isEqual(paymentRecord.startDate, paymentRecord.endDate)) {
 | 
					        if (
 | 
				
			||||||
            return `${formatDate(paymentRecord.startDate)} - ${formatDate(paymentRecord.endDate)}`
 | 
					            paymentRecord.endDate &&
 | 
				
			||||||
 | 
					            !isEqual(paymentRecord.startDate, paymentRecord.endDate)
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					            return `${formatDate(paymentRecord.startDate)} - ${formatDate(paymentRecord.endDate)}`;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return `${formatDate(paymentRecord.startDate)}`;
 | 
					        return `${formatDate(paymentRecord.startDate)}`;
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return useMemo<MRT_ColumnDef<PaymentRecordGetSchema>[]>(() => [
 | 
					    return useMemo<MRT_ColumnDef<PaymentRecordGetSchema>[]>(
 | 
				
			||||||
        {
 | 
					        () => [
 | 
				
			||||||
            header: "Дата начисления",
 | 
					            {
 | 
				
			||||||
            Cell: ({row}) => new Date(row.original.createdAt).toLocaleString('ru-RU')
 | 
					                header: "Дата начисления",
 | 
				
			||||||
        },
 | 
					                Cell: ({ row }) =>
 | 
				
			||||||
        {
 | 
					                    new Date(row.original.createdAt).toLocaleString("ru-RU"),
 | 
				
			||||||
            header: "Получил начисление",
 | 
					            },
 | 
				
			||||||
            Cell: ({row}) => `${row.original.user.firstName} ${row.original.user.secondName}`
 | 
					            {
 | 
				
			||||||
        },
 | 
					                header: "Получил начисление",
 | 
				
			||||||
        {
 | 
					                Cell: ({ row }) =>
 | 
				
			||||||
            header: "Создал начисление",
 | 
					                    `${row.original.user.firstName} ${row.original.user.secondName}`,
 | 
				
			||||||
            Cell: ({row}) => `${row.original.createdByUser.firstName} ${row.original.createdByUser.secondName}`
 | 
					            },
 | 
				
			||||||
        },
 | 
					            {
 | 
				
			||||||
        {
 | 
					                header: "Создал начисление",
 | 
				
			||||||
            header: "Количество",
 | 
					                Cell: ({ row }) =>
 | 
				
			||||||
            Cell: ({row}) => `${row.original.workUnits} ${getWorkUnitsText(row.original)}`
 | 
					                    `${row.original.createdByUser.firstName} ${row.original.createdByUser.secondName}`,
 | 
				
			||||||
        },
 | 
					            },
 | 
				
			||||||
        {
 | 
					            {
 | 
				
			||||||
            header: "Сумма начисления",
 | 
					                header: "Количество",
 | 
				
			||||||
            Cell: ({row}) => row.original.amount.toLocaleString("ru-RU")
 | 
					                Cell: ({ row }) =>
 | 
				
			||||||
        },
 | 
					                    `${row.original.workUnits} ${getWorkUnitsText(row.original)}`,
 | 
				
			||||||
        {
 | 
					            },
 | 
				
			||||||
            header: "Временной промежуток",
 | 
					            {
 | 
				
			||||||
            Cell: ({row}) => getDateRangesText(row.original)
 | 
					                header: "Сумма начисления",
 | 
				
			||||||
        }
 | 
					                Cell: ({ row }) => row.original.amount.toLocaleString("ru-RU"),
 | 
				
			||||||
    ], [])
 | 
					            },
 | 
				
			||||||
}
 | 
					            {
 | 
				
			||||||
 | 
					                header: "Временной промежуток",
 | 
				
			||||||
 | 
					                Cell: ({ row }) => getDateRangesText(row.original),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        []
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,19 +1,24 @@
 | 
				
			|||||||
import ObjectSelect, {ObjectSelectProps} from "../../../../components/ObjectSelect/ObjectSelect.tsx";
 | 
					import ObjectSelect, {
 | 
				
			||||||
import {PositionSchema} from "../../../../client";
 | 
					    ObjectSelectProps,
 | 
				
			||||||
import {FC} from "react";
 | 
					} from "../../../../components/ObjectSelect/ObjectSelect.tsx";
 | 
				
			||||||
 | 
					import { PositionSchema } from "../../../../client";
 | 
				
			||||||
 | 
					import { FC } from "react";
 | 
				
			||||||
import usePositionsList from "../../hooks/usePositionsList.tsx";
 | 
					import usePositionsList from "../../hooks/usePositionsList.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = Omit<ObjectSelectProps<PositionSchema>, 'data' | 'getLabelFn' | 'getValueFn'>;
 | 
					type Props = Omit<
 | 
				
			||||||
 | 
					    ObjectSelectProps<PositionSchema>,
 | 
				
			||||||
 | 
					    "data" | "getLabelFn" | "getValueFn"
 | 
				
			||||||
 | 
					>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const PositionSelect: FC<Props> = (props) => {
 | 
					const PositionSelect: FC<Props> = props => {
 | 
				
			||||||
    const {objects: positions} = usePositionsList();
 | 
					    const { objects: positions } = usePositionsList();
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <ObjectSelect
 | 
					        <ObjectSelect
 | 
				
			||||||
            getLabelFn={(position) => position.name}
 | 
					            getLabelFn={position => position.name}
 | 
				
			||||||
            getValueFn={(position) => position.key}
 | 
					            getValueFn={position => position.key}
 | 
				
			||||||
            data={positions}
 | 
					            data={positions}
 | 
				
			||||||
            {...props}
 | 
					            {...props}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
export default PositionSelect;
 | 
					export default PositionSelect;
 | 
				
			||||||
@@ -1,16 +1,16 @@
 | 
				
			|||||||
import {CRUDTableProps} from "../../../../types/CRUDTable.tsx";
 | 
					import { CRUDTableProps } from "../../../../types/CRUDTable.tsx";
 | 
				
			||||||
import {PositionSchema} from "../../../../client";
 | 
					import { PositionSchema } from "../../../../client";
 | 
				
			||||||
import {BaseTable} from "../../../../components/BaseTable/BaseTable.tsx";
 | 
					import { BaseTable } from "../../../../components/BaseTable/BaseTable.tsx";
 | 
				
			||||||
import {usePositionsTableColumns} from "./columns.tsx";
 | 
					import { usePositionsTableColumns } from "./columns.tsx";
 | 
				
			||||||
import {FC} from "react";
 | 
					import { FC } from "react";
 | 
				
			||||||
import {ActionIcon, Button, Flex, rem, Text, Tooltip} from "@mantine/core";
 | 
					import { ActionIcon, Button, Flex, rem, Text, Tooltip } from "@mantine/core";
 | 
				
			||||||
import {modals} from "@mantine/modals";
 | 
					import { modals } from "@mantine/modals";
 | 
				
			||||||
import {IconTrash} from "@tabler/icons-react";
 | 
					import { IconTrash } from "@tabler/icons-react";
 | 
				
			||||||
import {MRT_TableOptions} from "mantine-react-table";
 | 
					import { MRT_TableOptions } from "mantine-react-table";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = CRUDTableProps<PositionSchema>;
 | 
					type Props = CRUDTableProps<PositionSchema>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const PositionsTable: FC<Props> = ({items, onCreate, onDelete}) => {
 | 
					const PositionsTable: FC<Props> = ({ items, onCreate, onDelete }) => {
 | 
				
			||||||
    const columns = usePositionsTableColumns();
 | 
					    const columns = usePositionsTableColumns();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const onCreateClick = () => {
 | 
					    const onCreateClick = () => {
 | 
				
			||||||
@@ -19,63 +19,60 @@ const PositionsTable: FC<Props> = ({items, onCreate, onDelete}) => {
 | 
				
			|||||||
            modal: "positionForm",
 | 
					            modal: "positionForm",
 | 
				
			||||||
            withCloseButton: false,
 | 
					            withCloseButton: false,
 | 
				
			||||||
            innerProps: {
 | 
					            innerProps: {
 | 
				
			||||||
                onCreate: onCreate
 | 
					                onCreate: onCreate,
 | 
				
			||||||
            }
 | 
					            },
 | 
				
			||||||
        })
 | 
					        });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const onDeleteClick = (position: PositionSchema) => {
 | 
					    const onDeleteClick = (position: PositionSchema) => {
 | 
				
			||||||
        if (!onDelete) return;
 | 
					        if (!onDelete) return;
 | 
				
			||||||
        modals.openConfirmModal({
 | 
					        modals.openConfirmModal({
 | 
				
			||||||
            title: 'Удаление должности',
 | 
					            title: "Удаление должности",
 | 
				
			||||||
            children: (
 | 
					            children: (
 | 
				
			||||||
                <Text size="sm">
 | 
					                <Text size="sm">
 | 
				
			||||||
                    Вы уверены что хотите удалить должность {position.name}
 | 
					                    Вы уверены что хотите удалить должность {position.name}
 | 
				
			||||||
                </Text>
 | 
					                </Text>
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            labels: {confirm: 'Да', cancel: "Нет"},
 | 
					            labels: { confirm: "Да", cancel: "Нет" },
 | 
				
			||||||
            confirmProps: {color: 'red'},
 | 
					            confirmProps: { color: "red" },
 | 
				
			||||||
            onConfirm: () => onDelete(position)
 | 
					            onConfirm: () => onDelete(position),
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
 | 
					 | 
				
			||||||
        <BaseTable
 | 
					        <BaseTable
 | 
				
			||||||
            data={items}
 | 
					            data={items}
 | 
				
			||||||
            columns={columns}
 | 
					            columns={columns}
 | 
				
			||||||
            restProps={{
 | 
					            restProps={
 | 
				
			||||||
                enableTopToolbar: true,
 | 
					                {
 | 
				
			||||||
                enableSorting: false,
 | 
					                    enableTopToolbar: true,
 | 
				
			||||||
                enableColumnActions: false,
 | 
					                    enableSorting: false,
 | 
				
			||||||
                enableRowActions: true,
 | 
					                    enableColumnActions: false,
 | 
				
			||||||
                renderTopToolbar: (
 | 
					                    enableRowActions: true,
 | 
				
			||||||
                    <Flex p={rem(10)}>
 | 
					                    renderTopToolbar: (
 | 
				
			||||||
 | 
					                        <Flex p={rem(10)}>
 | 
				
			||||||
                        <Button
 | 
					                            <Button
 | 
				
			||||||
                            variant={"default"}
 | 
					                                variant={"default"}
 | 
				
			||||||
                            onClick={() => onCreateClick()}
 | 
					                                onClick={() => onCreateClick()}>
 | 
				
			||||||
                        >
 | 
					                                Создать должность
 | 
				
			||||||
                            Создать должность
 | 
					                            </Button>
 | 
				
			||||||
                        </Button>
 | 
					                        </Flex>
 | 
				
			||||||
                    </Flex>
 | 
					                    ),
 | 
				
			||||||
 | 
					                    renderRowActions: ({ row }) => (
 | 
				
			||||||
                ),
 | 
					                        <Flex gap="md">
 | 
				
			||||||
                renderRowActions: ({row}) => (
 | 
					                            <Tooltip label="Удалить">
 | 
				
			||||||
                    <Flex gap="md">
 | 
					                                <ActionIcon
 | 
				
			||||||
 | 
					                                    onClick={() => onDeleteClick(row.original)}
 | 
				
			||||||
                        <Tooltip label="Удалить">
 | 
					                                    variant={"default"}>
 | 
				
			||||||
                            <ActionIcon onClick={() => onDeleteClick(row.original)} variant={"default"}>
 | 
					                                    <IconTrash />
 | 
				
			||||||
                                <IconTrash/>
 | 
					                                </ActionIcon>
 | 
				
			||||||
                            </ActionIcon>
 | 
					                            </Tooltip>
 | 
				
			||||||
                        </Tooltip>
 | 
					                        </Flex>
 | 
				
			||||||
                    </Flex>
 | 
					                    ),
 | 
				
			||||||
                )
 | 
					                } as MRT_TableOptions<PositionSchema>
 | 
				
			||||||
            } as MRT_TableOptions<PositionSchema>}
 | 
					            }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
    )
 | 
					};
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default PositionsTable;
 | 
					export default PositionsTable;
 | 
				
			||||||
@@ -1,17 +1,19 @@
 | 
				
			|||||||
import {useMemo} from "react";
 | 
					import { useMemo } from "react";
 | 
				
			||||||
import {MRT_ColumnDef} from "mantine-react-table";
 | 
					import { MRT_ColumnDef } from "mantine-react-table";
 | 
				
			||||||
import {PositionSchema} from "../../../../client";
 | 
					import { PositionSchema } from "../../../../client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const usePositionsTableColumns = () => {
 | 
					export const usePositionsTableColumns = () => {
 | 
				
			||||||
    return useMemo<MRT_ColumnDef<PositionSchema>[]>(() => [
 | 
					    return useMemo<MRT_ColumnDef<PositionSchema>[]>(
 | 
				
			||||||
        {
 | 
					        () => [
 | 
				
			||||||
            accessorKey: "name",
 | 
					            {
 | 
				
			||||||
            header: "Название должности"
 | 
					                accessorKey: "name",
 | 
				
			||||||
        },
 | 
					                header: "Название должности",
 | 
				
			||||||
        {
 | 
					            },
 | 
				
			||||||
            accessorKey: "key",
 | 
					            {
 | 
				
			||||||
            header: "Ключ"
 | 
					                accessorKey: "key",
 | 
				
			||||||
        },
 | 
					                header: "Ключ",
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
    ], []);
 | 
					        ],
 | 
				
			||||||
}
 | 
					        []
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,19 +1,24 @@
 | 
				
			|||||||
import ObjectSelect, {ObjectSelectProps} from "../../../../components/ObjectSelect/ObjectSelect.tsx";
 | 
					import ObjectSelect, {
 | 
				
			||||||
import {RoleSchema} from "../../../../client";
 | 
					    ObjectSelectProps,
 | 
				
			||||||
import {FC} from "react";
 | 
					} from "../../../../components/ObjectSelect/ObjectSelect.tsx";
 | 
				
			||||||
 | 
					import { RoleSchema } from "../../../../client";
 | 
				
			||||||
 | 
					import { FC } from "react";
 | 
				
			||||||
import useRolesList from "../../hooks/useRolesList.tsx";
 | 
					import useRolesList from "../../hooks/useRolesList.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = Omit<ObjectSelectProps<RoleSchema>, 'data' | 'getLabelFn' | 'getValueFn'>;
 | 
					type Props = Omit<
 | 
				
			||||||
 | 
					    ObjectSelectProps<RoleSchema>,
 | 
				
			||||||
 | 
					    "data" | "getLabelFn" | "getValueFn"
 | 
				
			||||||
 | 
					>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const RolesSelect: FC<Props> = (props) => {
 | 
					const RolesSelect: FC<Props> = props => {
 | 
				
			||||||
    const {objects: roles} = useRolesList();
 | 
					    const { objects: roles } = useRolesList();
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <ObjectSelect
 | 
					        <ObjectSelect
 | 
				
			||||||
            getLabelFn={(position) => position.name}
 | 
					            getLabelFn={position => position.name}
 | 
				
			||||||
            getValueFn={(position) => position.key}
 | 
					            getValueFn={position => position.key}
 | 
				
			||||||
            data={roles}
 | 
					            data={roles}
 | 
				
			||||||
            {...props}
 | 
					            {...props}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
export default RolesSelect;
 | 
					export default RolesSelect;
 | 
				
			||||||
@@ -1,102 +1,103 @@
 | 
				
			|||||||
import {CRUDTableProps} from "../../../../types/CRUDTable.tsx";
 | 
					import { CRUDTableProps } from "../../../../types/CRUDTable.tsx";
 | 
				
			||||||
import {UserSchema} from "../../../../client";
 | 
					import { UserSchema } from "../../../../client";
 | 
				
			||||||
import {BaseTable} from "../../../../components/BaseTable/BaseTable.tsx";
 | 
					import { BaseTable } from "../../../../components/BaseTable/BaseTable.tsx";
 | 
				
			||||||
import {FC} from "react";
 | 
					import { FC } from "react";
 | 
				
			||||||
import {ActionIcon, Button, Flex, rem, Text, Tooltip} from "@mantine/core";
 | 
					import { ActionIcon, Button, Flex, rem, Text, Tooltip } from "@mantine/core";
 | 
				
			||||||
import {useUsersTableColumns} from "./columns.tsx";
 | 
					import { useUsersTableColumns } from "./columns.tsx";
 | 
				
			||||||
import {IconEdit, IconTrash} from "@tabler/icons-react";
 | 
					import { IconEdit, IconTrash } from "@tabler/icons-react";
 | 
				
			||||||
import {modals} from "@mantine/modals";
 | 
					import { modals } from "@mantine/modals";
 | 
				
			||||||
import {MRT_TableOptions} from "mantine-react-table";
 | 
					import { MRT_TableOptions } from "mantine-react-table";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = CRUDTableProps<UserSchema>;
 | 
					type Props = CRUDTableProps<UserSchema>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const UsersTable: FC<Props> = ({items, onChange, onDelete, onCreate}) => {
 | 
					const UsersTable: FC<Props> = ({ items, onChange, onDelete, onCreate }) => {
 | 
				
			||||||
    const columns = useUsersTableColumns();
 | 
					    const columns = useUsersTableColumns();
 | 
				
			||||||
    const onEditClick = (user: UserSchema) => {
 | 
					    const onEditClick = (user: UserSchema) => {
 | 
				
			||||||
        if (!onChange) return;
 | 
					        if (!onChange) return;
 | 
				
			||||||
        modals.openContextModal({
 | 
					        modals.openContextModal({
 | 
				
			||||||
            modal: "userFormModal",
 | 
					            modal: "userFormModal",
 | 
				
			||||||
            title: 'Редактирование пользователя',
 | 
					            title: "Редактирование пользователя",
 | 
				
			||||||
            withCloseButton: false,
 | 
					            withCloseButton: false,
 | 
				
			||||||
            innerProps: {
 | 
					            innerProps: {
 | 
				
			||||||
                onChange: onChange,
 | 
					                onChange: onChange,
 | 
				
			||||||
                element: user,
 | 
					                element: user,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            size: "md"
 | 
					            size: "md",
 | 
				
			||||||
        })
 | 
					        });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    const onDeleteClick = (user: UserSchema) => {
 | 
					    const onDeleteClick = (user: UserSchema) => {
 | 
				
			||||||
        if (!onDelete) return;
 | 
					        if (!onDelete) return;
 | 
				
			||||||
        modals.openConfirmModal({
 | 
					        modals.openConfirmModal({
 | 
				
			||||||
            title: 'Удаление пользователя',
 | 
					            title: "Удаление пользователя",
 | 
				
			||||||
            // centered: true,
 | 
					            // centered: true,
 | 
				
			||||||
            children: (
 | 
					            children: (
 | 
				
			||||||
                <Text size="sm">
 | 
					                <Text size="sm">
 | 
				
			||||||
                    Вы уверены что хотите удалить пользователя {user.firstName} {user.secondName}
 | 
					                    Вы уверены что хотите удалить пользователя {user.firstName}{" "}
 | 
				
			||||||
 | 
					                    {user.secondName}
 | 
				
			||||||
                </Text>
 | 
					                </Text>
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            labels: {confirm: 'Да', cancel: "Нет"},
 | 
					            labels: { confirm: "Да", cancel: "Нет" },
 | 
				
			||||||
            confirmProps: {color: 'red'},
 | 
					            confirmProps: { color: "red" },
 | 
				
			||||||
            onConfirm: () => onDelete(user)
 | 
					            onConfirm: () => onDelete(user),
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    const onCreateClick = () => {
 | 
					    const onCreateClick = () => {
 | 
				
			||||||
        if (!onCreate) return;
 | 
					        if (!onCreate) return;
 | 
				
			||||||
        modals.openContextModal({
 | 
					        modals.openContextModal({
 | 
				
			||||||
            modal: "userFormModal",
 | 
					            modal: "userFormModal",
 | 
				
			||||||
            title: 'Редактирование пользователя',
 | 
					            title: "Редактирование пользователя",
 | 
				
			||||||
            withCloseButton: false,
 | 
					            withCloseButton: false,
 | 
				
			||||||
            innerProps: {
 | 
					            innerProps: {
 | 
				
			||||||
                onCreate: onCreate
 | 
					                onCreate: onCreate,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            size: "md"
 | 
					            size: "md",
 | 
				
			||||||
        })
 | 
					        });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <BaseTable
 | 
					        <BaseTable
 | 
				
			||||||
            data={items}
 | 
					            data={items}
 | 
				
			||||||
            columns={columns}
 | 
					            columns={columns}
 | 
				
			||||||
            restProps={{
 | 
					            restProps={
 | 
				
			||||||
                enableSorting: false,
 | 
					                {
 | 
				
			||||||
                enableColumnActions: false,
 | 
					                    enableSorting: false,
 | 
				
			||||||
                enableTopToolbar: true,
 | 
					                    enableColumnActions: false,
 | 
				
			||||||
                renderTopToolbar: (
 | 
					                    enableTopToolbar: true,
 | 
				
			||||||
                    <Flex p={rem(10)}>
 | 
					                    renderTopToolbar: (
 | 
				
			||||||
                        <Button
 | 
					                        <Flex p={rem(10)}>
 | 
				
			||||||
                            variant={"default"}
 | 
					                            <Button
 | 
				
			||||||
                            onClick={() => onCreateClick()}
 | 
					                                variant={"default"}
 | 
				
			||||||
                        >
 | 
					                                onClick={() => onCreateClick()}>
 | 
				
			||||||
                            Создать пользователя
 | 
					                                Создать пользователя
 | 
				
			||||||
                        </Button>
 | 
					                            </Button>
 | 
				
			||||||
                    </Flex>
 | 
					                        </Flex>
 | 
				
			||||||
                ),
 | 
					                    ),
 | 
				
			||||||
                enableRowActions: true,
 | 
					                    enableRowActions: true,
 | 
				
			||||||
                renderRowActions: ({row}) => (
 | 
					                    renderRowActions: ({ row }) => (
 | 
				
			||||||
                    <Flex gap="md">
 | 
					                        <Flex gap="md">
 | 
				
			||||||
                        <Tooltip
 | 
					                            <Tooltip
 | 
				
			||||||
                            onClick={() => {
 | 
					                                onClick={() => {
 | 
				
			||||||
                                onEditClick(row.original)
 | 
					                                    onEditClick(row.original);
 | 
				
			||||||
                            }}
 | 
					                                }}
 | 
				
			||||||
                            label="Редактировать">
 | 
					                                label="Редактировать">
 | 
				
			||||||
                            <ActionIcon
 | 
					                                <ActionIcon variant={"default"}>
 | 
				
			||||||
                                variant={"default"}>
 | 
					                                    <IconEdit />
 | 
				
			||||||
                                <IconEdit/>
 | 
					                                </ActionIcon>
 | 
				
			||||||
                            </ActionIcon>
 | 
					                            </Tooltip>
 | 
				
			||||||
                        </Tooltip>
 | 
					                            <Tooltip
 | 
				
			||||||
                        <Tooltip onClick={() => {
 | 
					                                onClick={() => {
 | 
				
			||||||
                            onDeleteClick(row.original);
 | 
					                                    onDeleteClick(row.original);
 | 
				
			||||||
                        }} label="Удалить">
 | 
					                                }}
 | 
				
			||||||
                            <ActionIcon variant={"default"}>
 | 
					                                label="Удалить">
 | 
				
			||||||
                                <IconTrash/>
 | 
					                                <ActionIcon variant={"default"}>
 | 
				
			||||||
                            </ActionIcon>
 | 
					                                    <IconTrash />
 | 
				
			||||||
                        </Tooltip>
 | 
					                                </ActionIcon>
 | 
				
			||||||
                    </Flex>
 | 
					                            </Tooltip>
 | 
				
			||||||
                ),
 | 
					                        </Flex>
 | 
				
			||||||
            } as MRT_TableOptions<UserSchema>}
 | 
					                    ),
 | 
				
			||||||
 | 
					                } as MRT_TableOptions<UserSchema>
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
    )
 | 
					};
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default UsersTable;
 | 
					export default UsersTable;
 | 
				
			||||||
@@ -1,45 +1,49 @@
 | 
				
			|||||||
import {useMemo} from "react";
 | 
					import { useMemo } from "react";
 | 
				
			||||||
import {MRT_ColumnDef} from "mantine-react-table";
 | 
					import { MRT_ColumnDef } from "mantine-react-table";
 | 
				
			||||||
import {UserSchema} from "../../../../client";
 | 
					import { UserSchema } from "../../../../client";
 | 
				
			||||||
import {IconCheck, IconX} from "@tabler/icons-react";
 | 
					import { IconCheck, IconX } from "@tabler/icons-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const useUsersTableColumns = () => {
 | 
					export const useUsersTableColumns = () => {
 | 
				
			||||||
    return useMemo<MRT_ColumnDef<UserSchema>[]>(() => [
 | 
					    return useMemo<MRT_ColumnDef<UserSchema>[]>(
 | 
				
			||||||
 | 
					        () => [
 | 
				
			||||||
        {
 | 
					            {
 | 
				
			||||||
            header: "ФИО",
 | 
					                header: "ФИО",
 | 
				
			||||||
            Cell: ({row}) => `${row.original.firstName} ${row.original.secondName}`
 | 
					                Cell: ({ row }) =>
 | 
				
			||||||
        },
 | 
					                    `${row.original.firstName} ${row.original.secondName}`,
 | 
				
			||||||
        {
 | 
					            },
 | 
				
			||||||
            accessorKey: "phoneNumber",
 | 
					            {
 | 
				
			||||||
            header: "Номер телефона"
 | 
					                accessorKey: "phoneNumber",
 | 
				
			||||||
        },
 | 
					                header: "Номер телефона",
 | 
				
			||||||
        {
 | 
					            },
 | 
				
			||||||
            accessorKey: "role.name",
 | 
					            {
 | 
				
			||||||
            header: "Роль"
 | 
					                accessorKey: "role.name",
 | 
				
			||||||
        },
 | 
					                header: "Роль",
 | 
				
			||||||
        {
 | 
					            },
 | 
				
			||||||
            accessorKey: "position.name",
 | 
					            {
 | 
				
			||||||
            header: "Должность"
 | 
					                accessorKey: "position.name",
 | 
				
			||||||
        },
 | 
					                header: "Должность",
 | 
				
			||||||
        {
 | 
					            },
 | 
				
			||||||
            accessorKey: "payRate.name",
 | 
					            {
 | 
				
			||||||
            header: "Тариф"
 | 
					                accessorKey: "payRate.name",
 | 
				
			||||||
        },
 | 
					                header: "Тариф",
 | 
				
			||||||
        {
 | 
					            },
 | 
				
			||||||
            accessorKey: "comment",
 | 
					            {
 | 
				
			||||||
            header: "Дополнительная информация"
 | 
					                accessorKey: "comment",
 | 
				
			||||||
        },
 | 
					                header: "Дополнительная информация",
 | 
				
			||||||
        {
 | 
					            },
 | 
				
			||||||
            accessorKey: "isAdmin",
 | 
					            {
 | 
				
			||||||
            header: "Администратор",
 | 
					                accessorKey: "isAdmin",
 | 
				
			||||||
            Cell: ({row}) => row.original.isAdmin ? <IconCheck/> : <IconX/>
 | 
					                header: "Администратор",
 | 
				
			||||||
        },
 | 
					                Cell: ({ row }) =>
 | 
				
			||||||
        {
 | 
					                    row.original.isAdmin ? <IconCheck /> : <IconX />,
 | 
				
			||||||
            accessorKey: "isBlocked",
 | 
					            },
 | 
				
			||||||
            header: "Заблокирован",
 | 
					            {
 | 
				
			||||||
            Cell: ({row}) => row.original.isBlocked ? <IconCheck/> : <IconX/>
 | 
					                accessorKey: "isBlocked",
 | 
				
			||||||
        },
 | 
					                header: "Заблокирован",
 | 
				
			||||||
    ], []);
 | 
					                Cell: ({ row }) =>
 | 
				
			||||||
}
 | 
					                    row.original.isBlocked ? <IconCheck /> : <IconX />,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        []
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,10 @@
 | 
				
			|||||||
import {PayrollService} from "../../../client";
 | 
					import { PayrollService } from "../../../client";
 | 
				
			||||||
import ObjectList from "../../../hooks/objectList.tsx";
 | 
					import ObjectList from "../../../hooks/objectList.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const usePayRatesList = () => ObjectList({
 | 
					const usePayRatesList = () =>
 | 
				
			||||||
    queryFn: PayrollService.getAllPayRates,
 | 
					    ObjectList({
 | 
				
			||||||
    getObjectsFn: response => response.payRates,
 | 
					        queryFn: PayrollService.getAllPayRates,
 | 
				
			||||||
    queryKey: "getAllPayRates"
 | 
					        getObjectsFn: response => response.payRates,
 | 
				
			||||||
})
 | 
					        queryKey: "getAllPayRates",
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
export default usePayRatesList;
 | 
					export default usePayRatesList;
 | 
				
			||||||
@@ -1,9 +1,10 @@
 | 
				
			|||||||
import {PositionService} from "../../../client";
 | 
					import { PositionService } from "../../../client";
 | 
				
			||||||
import ObjectList from "../../../hooks/objectList.tsx";
 | 
					import ObjectList from "../../../hooks/objectList.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const usePositionsList = () => ObjectList({
 | 
					const usePositionsList = () =>
 | 
				
			||||||
    queryFn: PositionService.getAllPositions,
 | 
					    ObjectList({
 | 
				
			||||||
    getObjectsFn: response => response.positions,
 | 
					        queryFn: PositionService.getAllPositions,
 | 
				
			||||||
    queryKey: "getAllPositions"
 | 
					        getObjectsFn: response => response.positions,
 | 
				
			||||||
})
 | 
					        queryKey: "getAllPositions",
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
export default usePositionsList;
 | 
					export default usePositionsList;
 | 
				
			||||||
@@ -1,9 +1,10 @@
 | 
				
			|||||||
import ObjectList from "../../../hooks/objectList.tsx";
 | 
					import ObjectList from "../../../hooks/objectList.tsx";
 | 
				
			||||||
import {RoleService} from "../../../client";
 | 
					import { RoleService } from "../../../client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const useRolesList = () => ObjectList({
 | 
					const useRolesList = () =>
 | 
				
			||||||
    queryFn: RoleService.getAllRoles,
 | 
					    ObjectList({
 | 
				
			||||||
    getObjectsFn: response => response.roles,
 | 
					        queryFn: RoleService.getAllRoles,
 | 
				
			||||||
    queryKey: "getAllRoles"
 | 
					        getObjectsFn: response => response.roles,
 | 
				
			||||||
})
 | 
					        queryKey: "getAllRoles",
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
export default useRolesList;
 | 
					export default useRolesList;
 | 
				
			||||||
@@ -1,11 +1,11 @@
 | 
				
			|||||||
import ObjectList from "../../../hooks/objectList.tsx";
 | 
					import ObjectList from "../../../hooks/objectList.tsx";
 | 
				
			||||||
import {UserService} from "../../../client";
 | 
					import { UserService } from "../../../client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const useUsersList = () =>
 | 
				
			||||||
const useUsersList = () => ObjectList({
 | 
					    ObjectList({
 | 
				
			||||||
    queryFn: UserService.getAllUsers,
 | 
					        queryFn: UserService.getAllUsers,
 | 
				
			||||||
    getObjectsFn: (response) => response.users,
 | 
					        getObjectsFn: response => response.users,
 | 
				
			||||||
    queryKey: "getAllUsers"
 | 
					        queryKey: "getAllUsers",
 | 
				
			||||||
});
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default useUsersList;
 | 
					export default useUsersList;
 | 
				
			||||||
@@ -1,41 +1,51 @@
 | 
				
			|||||||
import BaseFormModal, {CreateProps} from "../../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
 | 
					import BaseFormModal, {
 | 
				
			||||||
import {PaymentRecordCreateSchema} from "../../../../client";
 | 
					    CreateProps,
 | 
				
			||||||
import {ContextModalProps} from "@mantine/modals";
 | 
					} from "../../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
 | 
				
			||||||
import {useForm} from "@mantine/form";
 | 
					import { PaymentRecordCreateSchema } from "../../../../client";
 | 
				
			||||||
import {Flex, NumberInput, rem} from "@mantine/core";
 | 
					import { ContextModalProps } from "@mantine/modals";
 | 
				
			||||||
import {DatePickerInput, MonthPickerInput} from "@mantine/dates";
 | 
					import { useForm } from "@mantine/form";
 | 
				
			||||||
import {useEffect, useState} from "react";
 | 
					import { Flex, NumberInput, rem } from "@mantine/core";
 | 
				
			||||||
 | 
					import { DatePickerInput, MonthPickerInput } from "@mantine/dates";
 | 
				
			||||||
 | 
					import { useEffect, useState } from "react";
 | 
				
			||||||
import UserSelect from "../../../../components/Selects/UserSelect/UserSelect.tsx";
 | 
					import UserSelect from "../../../../components/Selects/UserSelect/UserSelect.tsx";
 | 
				
			||||||
import {PaySchemeType} from "../../../../shared/enums/PaySchemeType.ts";
 | 
					import { PaySchemeType } from "../../../../shared/enums/PaySchemeType.ts";
 | 
				
			||||||
import {motion} from "framer-motion";
 | 
					import { motion } from "framer-motion";
 | 
				
			||||||
import {dateWithoutTimezone} from "../../../../shared/lib/date.ts";
 | 
					import { dateWithoutTimezone } from "../../../../shared/lib/date.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = CreateProps<PaymentRecordCreateSchema>;
 | 
					type Props = CreateProps<PaymentRecordCreateSchema>;
 | 
				
			||||||
const CreatePaymentRecordModal = ({
 | 
					const CreatePaymentRecordModal = ({
 | 
				
			||||||
                                      context,
 | 
					    context,
 | 
				
			||||||
                                      id,
 | 
					    id,
 | 
				
			||||||
                                      innerProps
 | 
					    innerProps,
 | 
				
			||||||
                                  }: ContextModalProps<Props>) => {
 | 
					}: ContextModalProps<Props>) => {
 | 
				
			||||||
    const form = useForm<Partial<PaymentRecordCreateSchema>>({
 | 
					    const form = useForm<Partial<PaymentRecordCreateSchema>>({
 | 
				
			||||||
        validate: {
 | 
					        validate: {
 | 
				
			||||||
            user: (user) => !user && "Необходимо выбрать сотрудника",
 | 
					            user: user => !user && "Необходимо выбрать сотрудника",
 | 
				
			||||||
            startDate: (startDate) => !startDate && "Необходимо указать временной промежуток",
 | 
					            startDate: startDate =>
 | 
				
			||||||
            workUnits: (workUnits) => !workUnits && "Укажите количество"
 | 
					                !startDate && "Необходимо указать временной промежуток",
 | 
				
			||||||
        }
 | 
					            workUnits: workUnits => !workUnits && "Укажите количество",
 | 
				
			||||||
    })
 | 
					        },
 | 
				
			||||||
    const [dateRange, setDateRange] = useState<[Date | null, Date | null]>([null, null]);
 | 
					    });
 | 
				
			||||||
 | 
					    const [dateRange, setDateRange] = useState<[Date | null, Date | null]>([
 | 
				
			||||||
 | 
					        null,
 | 
				
			||||||
 | 
					        null,
 | 
				
			||||||
 | 
					    ]);
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
 | 
					        const setDates = (
 | 
				
			||||||
        const setDates = (start: string | undefined, end: string | undefined) => {
 | 
					            start: string | undefined,
 | 
				
			||||||
 | 
					            end: string | undefined
 | 
				
			||||||
 | 
					        ) => {
 | 
				
			||||||
            form.setFieldValue("startDate", start);
 | 
					            form.setFieldValue("startDate", start);
 | 
				
			||||||
            form.setFieldValue("endDate", end);
 | 
					            form.setFieldValue("endDate", end);
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (dateRange.every(dr => dr == null)) {
 | 
					        if (dateRange.every(dr => dr == null)) {
 | 
				
			||||||
            setDates(undefined, undefined);
 | 
					            setDates(undefined, undefined);
 | 
				
			||||||
            return
 | 
					            return;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            const notNullValues = dateRange.filter((dr): dr is Date => dr !== null).map(dateWithoutTimezone);
 | 
					            const notNullValues = dateRange
 | 
				
			||||||
 | 
					                .filter((dr): dr is Date => dr !== null)
 | 
				
			||||||
 | 
					                .map(dateWithoutTimezone);
 | 
				
			||||||
            const startDate = notNullValues[0];
 | 
					            const startDate = notNullValues[0];
 | 
				
			||||||
            const endDate = notNullValues[1] || startDate;
 | 
					            const endDate = notNullValues[1] || startDate;
 | 
				
			||||||
            setDates(startDate, endDate);
 | 
					            setDates(startDate, endDate);
 | 
				
			||||||
@@ -43,14 +53,13 @@ const CreatePaymentRecordModal = ({
 | 
				
			|||||||
    }, [dateRange]);
 | 
					    }, [dateRange]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const getDateRangeInput = () => {
 | 
					    const getDateRangeInput = () => {
 | 
				
			||||||
        if (!form.values.user) return <></>
 | 
					        if (!form.values.user) return <></>;
 | 
				
			||||||
        const payRate = form.values.user.payRate;
 | 
					        const payRate = form.values.user.payRate;
 | 
				
			||||||
        if (!payRate) return <></>;
 | 
					        if (!payRate) return <></>;
 | 
				
			||||||
        if (payRate.payrollScheme.key == PaySchemeType.MONTHLY)
 | 
					        if (payRate.payrollScheme.key == PaySchemeType.MONTHLY)
 | 
				
			||||||
            return (
 | 
					            return (
 | 
				
			||||||
                <MonthPickerInput
 | 
					                <MonthPickerInput
 | 
				
			||||||
                    error={form.getInputProps("startDate").error}
 | 
					                    error={form.getInputProps("startDate").error}
 | 
				
			||||||
 | 
					 | 
				
			||||||
                    label={"Временной промежуток"}
 | 
					                    label={"Временной промежуток"}
 | 
				
			||||||
                    placeholder={"Выберите временной промежуток"}
 | 
					                    placeholder={"Выберите временной промежуток"}
 | 
				
			||||||
                    type={"range"}
 | 
					                    type={"range"}
 | 
				
			||||||
@@ -58,73 +67,78 @@ const CreatePaymentRecordModal = ({
 | 
				
			|||||||
                    onChange={setDateRange}
 | 
					                    onChange={setDateRange}
 | 
				
			||||||
                    allowSingleDateInRange
 | 
					                    allowSingleDateInRange
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
            )
 | 
					            );
 | 
				
			||||||
        return (<DatePickerInput
 | 
					        return (
 | 
				
			||||||
            error={form.getInputProps("startDate").error}
 | 
					            <DatePickerInput
 | 
				
			||||||
            label={"Временной промежуток"}
 | 
					                error={form.getInputProps("startDate").error}
 | 
				
			||||||
            placeholder={"Выберите временной промежуток"}
 | 
					                label={"Временной промежуток"}
 | 
				
			||||||
            type={"range"}
 | 
					                placeholder={"Выберите временной промежуток"}
 | 
				
			||||||
            allowSingleDateInRange
 | 
					                type={"range"}
 | 
				
			||||||
            value={dateRange}
 | 
					                allowSingleDateInRange
 | 
				
			||||||
            onChange={setDateRange}
 | 
					                value={dateRange}
 | 
				
			||||||
        />);
 | 
					                onChange={setDateRange}
 | 
				
			||||||
    }
 | 
					            />
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
    const getAmountLabel = () => {
 | 
					    const getAmountLabel = () => {
 | 
				
			||||||
        const user = form.values.user;
 | 
					        const user = form.values.user;
 | 
				
			||||||
        if (!user) return "";
 | 
					        if (!user) return "";
 | 
				
			||||||
        const payRate = user?.payRate;
 | 
					        const payRate = user?.payRate;
 | 
				
			||||||
        if (!payRate) return "";
 | 
					        if (!payRate) return "";
 | 
				
			||||||
        if (payRate.payrollScheme.key == PaySchemeType.HOURLY) return "Количество часов";
 | 
					        if (payRate.payrollScheme.key == PaySchemeType.HOURLY)
 | 
				
			||||||
        if (payRate.payrollScheme.key == PaySchemeType.MONTHLY) return "Количество месяцев";
 | 
					            return "Количество часов";
 | 
				
			||||||
        if (payRate.payrollScheme.key == PaySchemeType.DAILY) return "Количество дней";
 | 
					        if (payRate.payrollScheme.key == PaySchemeType.MONTHLY)
 | 
				
			||||||
 | 
					            return "Количество месяцев";
 | 
				
			||||||
 | 
					        if (payRate.payrollScheme.key == PaySchemeType.DAILY)
 | 
				
			||||||
 | 
					            return "Количество дней";
 | 
				
			||||||
        return "";
 | 
					        return "";
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    const getAmountPlaceholder = () => {
 | 
					    const getAmountPlaceholder = () => {
 | 
				
			||||||
        return "Укажите " + getAmountLabel().toLowerCase();
 | 
					        return "Укажите " + getAmountLabel().toLowerCase();
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    return (<BaseFormModal
 | 
					    return (
 | 
				
			||||||
        form={form}
 | 
					        <BaseFormModal
 | 
				
			||||||
        closeOnSubmit
 | 
					            form={form}
 | 
				
			||||||
        onClose={() => context.closeContextModal(id)}
 | 
					            closeOnSubmit
 | 
				
			||||||
        {...innerProps}
 | 
					            onClose={() => context.closeContextModal(id)}
 | 
				
			||||||
    >
 | 
					            {...innerProps}>
 | 
				
			||||||
        <BaseFormModal.Body>
 | 
					            <BaseFormModal.Body>
 | 
				
			||||||
            <>
 | 
					                <>
 | 
				
			||||||
                <Flex direction={"column"} gap={rem(10)}>
 | 
					                    <Flex
 | 
				
			||||||
                    <UserSelect
 | 
					                        direction={"column"}
 | 
				
			||||||
                        label={"Сотрудник"}
 | 
					                        gap={rem(10)}>
 | 
				
			||||||
                        placeholder={"Выберите сотрудника"}
 | 
					                        <UserSelect
 | 
				
			||||||
                        searchable
 | 
					                            label={"Сотрудник"}
 | 
				
			||||||
                        filterBy={(user) => !!user.payRate}
 | 
					                            placeholder={"Выберите сотрудника"}
 | 
				
			||||||
                        {...form.getInputProps("user")}
 | 
					                            searchable
 | 
				
			||||||
                    />
 | 
					                            filterBy={user => !!user.payRate}
 | 
				
			||||||
 | 
					                            {...form.getInputProps("user")}
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    {form.values.user &&
 | 
					                        {form.values.user && (
 | 
				
			||||||
                        <>
 | 
					                            <>
 | 
				
			||||||
                            <motion.div
 | 
					                                <motion.div
 | 
				
			||||||
 | 
					                                    initial={{ opacity: 0 }}
 | 
				
			||||||
                                initial={{opacity: 0}}
 | 
					                                    animate={{ opacity: 1 }}
 | 
				
			||||||
                                animate={{opacity: 1}}
 | 
					                                    transition={{ duration: 0.3 }}>
 | 
				
			||||||
                                transition={{duration: 0.3}}
 | 
					                                    <Flex
 | 
				
			||||||
                            >
 | 
					                                        direction={"column"}
 | 
				
			||||||
                                <Flex
 | 
					                                        gap={rem(10)}>
 | 
				
			||||||
                                    direction={"column"}
 | 
					                                        {getDateRangeInput()}
 | 
				
			||||||
                                    gap={rem(10)}>
 | 
					                                        <NumberInput
 | 
				
			||||||
 | 
					                                            label={getAmountLabel()}
 | 
				
			||||||
                                    {getDateRangeInput()}
 | 
					                                            placeholder={getAmountPlaceholder()}
 | 
				
			||||||
                                    <NumberInput
 | 
					                                            hideControls
 | 
				
			||||||
                                        label={getAmountLabel()}
 | 
					                                            {...form.getInputProps("workUnits")}
 | 
				
			||||||
                                        placeholder={getAmountPlaceholder()}
 | 
					                                        />
 | 
				
			||||||
                                        hideControls
 | 
					                                    </Flex>
 | 
				
			||||||
                                        {...form.getInputProps("workUnits")}
 | 
					                                </motion.div>
 | 
				
			||||||
                                    />
 | 
					                            </>
 | 
				
			||||||
                                </Flex>
 | 
					                        )}
 | 
				
			||||||
                            </motion.div>
 | 
					                    </Flex>
 | 
				
			||||||
                        </>
 | 
					                </>
 | 
				
			||||||
                    }
 | 
					            </BaseFormModal.Body>
 | 
				
			||||||
                </Flex>
 | 
					        </BaseFormModal>
 | 
				
			||||||
            </>
 | 
					    );
 | 
				
			||||||
        </BaseFormModal.Body>
 | 
					};
 | 
				
			||||||
    </BaseFormModal>)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
export default CreatePaymentRecordModal;
 | 
					export default CreatePaymentRecordModal;
 | 
				
			||||||
@@ -1,41 +1,47 @@
 | 
				
			|||||||
import BaseFormModal, {CreateEditFormProps} from "../../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
 | 
					import BaseFormModal, {
 | 
				
			||||||
import {PayRateSchemaBase} from "../../../../client";
 | 
					    CreateEditFormProps,
 | 
				
			||||||
import {ContextModalProps} from "@mantine/modals";
 | 
					} from "../../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
 | 
				
			||||||
import {useForm} from "@mantine/form";
 | 
					import { PayRateSchemaBase } from "../../../../client";
 | 
				
			||||||
import {Fieldset, Flex, NumberInput, rem, TextInput} from "@mantine/core";
 | 
					import { ContextModalProps } from "@mantine/modals";
 | 
				
			||||||
 | 
					import { useForm } from "@mantine/form";
 | 
				
			||||||
 | 
					import { Fieldset, Flex, NumberInput, rem, TextInput } from "@mantine/core";
 | 
				
			||||||
import PayrollSchemeSelect from "../../../../components/Selects/PayrollSchemeSelect/PayrollSchemeSelect.tsx";
 | 
					import PayrollSchemeSelect from "../../../../components/Selects/PayrollSchemeSelect/PayrollSchemeSelect.tsx";
 | 
				
			||||||
import {PaySchemeType} from "../../../../shared/enums/PaySchemeType.ts";
 | 
					import { PaySchemeType } from "../../../../shared/enums/PaySchemeType.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = CreateEditFormProps<PayRateSchemaBase>
 | 
					type Props = CreateEditFormProps<PayRateSchemaBase>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const PayRateFormModal = ({
 | 
					const PayRateFormModal = ({
 | 
				
			||||||
                              context,
 | 
					    context,
 | 
				
			||||||
                              id,
 | 
					    id,
 | 
				
			||||||
                              innerProps
 | 
					    innerProps,
 | 
				
			||||||
                          }: ContextModalProps<Props>) => {
 | 
					}: ContextModalProps<Props>) => {
 | 
				
			||||||
    const isEditing = 'element' in innerProps;
 | 
					    const isEditing = "element" in innerProps;
 | 
				
			||||||
    const initialValue: Partial<PayRateSchemaBase> = isEditing ? innerProps.element : {};
 | 
					    const initialValue: Partial<PayRateSchemaBase> = isEditing
 | 
				
			||||||
 | 
					        ? innerProps.element
 | 
				
			||||||
 | 
					        : {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const form = useForm<Partial<PayRateSchemaBase>>({
 | 
					    const form = useForm<Partial<PayRateSchemaBase>>({
 | 
				
			||||||
        initialValues: initialValue,
 | 
					        initialValues: initialValue,
 | 
				
			||||||
        validate: {
 | 
					        validate: {
 | 
				
			||||||
            name: (name) => !name && "Необходимо указать название тарифа",
 | 
					            name: name => !name && "Необходимо указать название тарифа",
 | 
				
			||||||
            payrollScheme: (scheme) => !scheme && "Необходимо выбрать систему оплаты",
 | 
					            payrollScheme: scheme =>
 | 
				
			||||||
            baseRate: (baseRate) => !baseRate && "Небходимо указать базовую ставку"
 | 
					                !scheme && "Необходимо выбрать систему оплаты",
 | 
				
			||||||
        }
 | 
					            baseRate: baseRate =>
 | 
				
			||||||
 | 
					                !baseRate && "Небходимо указать базовую ставку",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <BaseFormModal
 | 
					        <BaseFormModal
 | 
				
			||||||
            form={form}
 | 
					            form={form}
 | 
				
			||||||
            closeOnSubmit
 | 
					            closeOnSubmit
 | 
				
			||||||
            onClose={() => context.closeContextModal(id)}
 | 
					            onClose={() => context.closeContextModal(id)}
 | 
				
			||||||
            {...innerProps}
 | 
					            {...innerProps}>
 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
            <BaseFormModal.Body>
 | 
					            <BaseFormModal.Body>
 | 
				
			||||||
                <>
 | 
					                <>
 | 
				
			||||||
                    <Fieldset legend={"Общие параметры"}>
 | 
					                    <Fieldset legend={"Общие параметры"}>
 | 
				
			||||||
                        <Flex direction={"column"} gap={rem(10)}>
 | 
					                        <Flex
 | 
				
			||||||
 | 
					                            direction={"column"}
 | 
				
			||||||
 | 
					                            gap={rem(10)}>
 | 
				
			||||||
                            <TextInput
 | 
					                            <TextInput
 | 
				
			||||||
                                label={"Название"}
 | 
					                                label={"Название"}
 | 
				
			||||||
                                placeholder={"Введите название тарифа"}
 | 
					                                placeholder={"Введите название тарифа"}
 | 
				
			||||||
@@ -47,11 +53,11 @@ const PayRateFormModal = ({
 | 
				
			|||||||
                                {...form.getInputProps("payrollScheme")}
 | 
					                                {...form.getInputProps("payrollScheme")}
 | 
				
			||||||
                            />
 | 
					                            />
 | 
				
			||||||
                        </Flex>
 | 
					                        </Flex>
 | 
				
			||||||
 | 
					 | 
				
			||||||
                    </Fieldset>
 | 
					                    </Fieldset>
 | 
				
			||||||
                    <Fieldset>
 | 
					                    <Fieldset>
 | 
				
			||||||
                        <Flex direction={"column"} gap={rem(10)}>
 | 
					                        <Flex
 | 
				
			||||||
 | 
					                            direction={"column"}
 | 
				
			||||||
 | 
					                            gap={rem(10)}>
 | 
				
			||||||
                            <NumberInput
 | 
					                            <NumberInput
 | 
				
			||||||
                                allowNegative={false}
 | 
					                                allowNegative={false}
 | 
				
			||||||
                                hideControls
 | 
					                                hideControls
 | 
				
			||||||
@@ -61,38 +67,42 @@ const PayRateFormModal = ({
 | 
				
			|||||||
                                thousandSeparator={" "}
 | 
					                                thousandSeparator={" "}
 | 
				
			||||||
                                suffix={"₽"}
 | 
					                                suffix={"₽"}
 | 
				
			||||||
                                {...form.getInputProps("baseRate")}
 | 
					                                {...form.getInputProps("baseRate")}
 | 
				
			||||||
 | 
					 | 
				
			||||||
                            />
 | 
					                            />
 | 
				
			||||||
                            {form.values.payrollScheme?.key === PaySchemeType.HOURLY &&
 | 
					                            {form.values.payrollScheme?.key ===
 | 
				
			||||||
 | 
					                                PaySchemeType.HOURLY && (
 | 
				
			||||||
                                <>
 | 
					                                <>
 | 
				
			||||||
                                    <NumberInput
 | 
					                                    <NumberInput
 | 
				
			||||||
                                        allowNegative={false}
 | 
					                                        allowNegative={false}
 | 
				
			||||||
                                        hideControls
 | 
					                                        hideControls
 | 
				
			||||||
                                        allowDecimal={false}
 | 
					                                        allowDecimal={false}
 | 
				
			||||||
                                        label={"Порог сверхурочных"}
 | 
					                                        label={"Порог сверхурочных"}
 | 
				
			||||||
                                        placeholder={"Введите порог сверхурочных"}
 | 
					                                        placeholder={
 | 
				
			||||||
                                        {...form.getInputProps("overtimeThreshold")}
 | 
					                                            "Введите порог сверхурочных"
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                        {...form.getInputProps(
 | 
				
			||||||
 | 
					                                            "overtimeThreshold"
 | 
				
			||||||
 | 
					                                        )}
 | 
				
			||||||
                                    />
 | 
					                                    />
 | 
				
			||||||
                                    <NumberInput
 | 
					                                    <NumberInput
 | 
				
			||||||
                                        allowNegative={false}
 | 
					                                        allowNegative={false}
 | 
				
			||||||
                                        hideControls
 | 
					                                        hideControls
 | 
				
			||||||
                                        decimalScale={2}
 | 
					                                        decimalScale={2}
 | 
				
			||||||
                                        label={"Сверхурочная ставка"}
 | 
					                                        label={"Сверхурочная ставка"}
 | 
				
			||||||
                                        placeholder={"Выберите сверхурочную ставку"}
 | 
					                                        placeholder={
 | 
				
			||||||
 | 
					                                            "Выберите сверхурочную ставку"
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
                                        thousandSeparator={" "}
 | 
					                                        thousandSeparator={" "}
 | 
				
			||||||
                                        suffix={"₽"}
 | 
					                                        suffix={"₽"}
 | 
				
			||||||
                                        {...form.getInputProps("overtimeRate")}
 | 
					                                        {...form.getInputProps("overtimeRate")}
 | 
				
			||||||
                                    />
 | 
					                                    />
 | 
				
			||||||
 | 
					 | 
				
			||||||
                                </>
 | 
					                                </>
 | 
				
			||||||
                            }
 | 
					                            )}
 | 
				
			||||||
                        </Flex>
 | 
					                        </Flex>
 | 
				
			||||||
 | 
					 | 
				
			||||||
                    </Fieldset>
 | 
					                    </Fieldset>
 | 
				
			||||||
                </>
 | 
					                </>
 | 
				
			||||||
            </BaseFormModal.Body>
 | 
					            </BaseFormModal.Body>
 | 
				
			||||||
        </BaseFormModal>
 | 
					        </BaseFormModal>
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default PayRateFormModal;
 | 
					export default PayRateFormModal;
 | 
				
			||||||
@@ -1,46 +1,65 @@
 | 
				
			|||||||
import {ContextModalProps} from "@mantine/modals";
 | 
					import { ContextModalProps } from "@mantine/modals";
 | 
				
			||||||
import BaseFormModal, {CreateEditFormProps} from "../../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
 | 
					import BaseFormModal, {
 | 
				
			||||||
import {UserSchema} from "../../../../client";
 | 
					    CreateEditFormProps,
 | 
				
			||||||
import {useForm} from "@mantine/form";
 | 
					} from "../../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
 | 
				
			||||||
import {Checkbox, Fieldset, Input, Stack, Textarea, TextInput} from "@mantine/core";
 | 
					import { UserSchema } from "../../../../client";
 | 
				
			||||||
 | 
					import { useForm } from "@mantine/form";
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    Checkbox,
 | 
				
			||||||
 | 
					    Fieldset,
 | 
				
			||||||
 | 
					    Input,
 | 
				
			||||||
 | 
					    Stack,
 | 
				
			||||||
 | 
					    Textarea,
 | 
				
			||||||
 | 
					    TextInput,
 | 
				
			||||||
 | 
					} from "@mantine/core";
 | 
				
			||||||
import RoleSelect from "../../components/RoleSelect/RoleSelect.tsx";
 | 
					import RoleSelect from "../../components/RoleSelect/RoleSelect.tsx";
 | 
				
			||||||
import PositionSelect from "../../components/PositionSelect/PositionSelect.tsx";
 | 
					import PositionSelect from "../../components/PositionSelect/PositionSelect.tsx";
 | 
				
			||||||
import {UserRoleEnum} from "../../../../shared/enums/UserRole.ts";
 | 
					import { UserRoleEnum } from "../../../../shared/enums/UserRole.ts";
 | 
				
			||||||
import {capitalize} from "lodash";
 | 
					import { capitalize } from "lodash";
 | 
				
			||||||
import {IMaskInput} from "react-imask";
 | 
					import { IMaskInput } from "react-imask";
 | 
				
			||||||
import phone from "phone";
 | 
					import phone from "phone";
 | 
				
			||||||
import PayRateSelect from "../../../../components/Selects/PayRateSelect/PayRateSelect.tsx";
 | 
					import PayRateSelect from "../../../../components/Selects/PayRateSelect/PayRateSelect.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = CreateEditFormProps<UserSchema>;
 | 
					type Props = CreateEditFormProps<UserSchema>;
 | 
				
			||||||
const UserFormModal = ({context, id, innerProps}: ContextModalProps<Props>) => {
 | 
					const UserFormModal = ({
 | 
				
			||||||
    const isEditing = 'element' in innerProps;
 | 
					    context,
 | 
				
			||||||
    const initialValues = isEditing ? innerProps.element : {
 | 
					    id,
 | 
				
			||||||
        isAdmin: false,
 | 
					    innerProps,
 | 
				
			||||||
        isBlocked: false,
 | 
					}: ContextModalProps<Props>) => {
 | 
				
			||||||
        isDeleted: false,
 | 
					    const isEditing = "element" in innerProps;
 | 
				
			||||||
        comment: "",
 | 
					    const initialValues = isEditing
 | 
				
			||||||
        roleKey: UserRoleEnum.USER
 | 
					        ? innerProps.element
 | 
				
			||||||
    };
 | 
					        : {
 | 
				
			||||||
 | 
					              isAdmin: false,
 | 
				
			||||||
 | 
					              isBlocked: false,
 | 
				
			||||||
 | 
					              isDeleted: false,
 | 
				
			||||||
 | 
					              comment: "",
 | 
				
			||||||
 | 
					              roleKey: UserRoleEnum.USER,
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const form = useForm<Partial<UserSchema>>({
 | 
					    const form = useForm<Partial<UserSchema>>({
 | 
				
			||||||
        initialValues: initialValues,
 | 
					        initialValues: initialValues,
 | 
				
			||||||
        validate: {
 | 
					        validate: {
 | 
				
			||||||
            firstName: value => !value?.trim() && "Укажите имя пользователя",
 | 
					            firstName: value => !value?.trim() && "Укажите имя пользователя",
 | 
				
			||||||
            secondName: value => !value?.trim() && "Укажите фамилию",
 | 
					            secondName: value => !value?.trim() && "Укажите фамилию",
 | 
				
			||||||
            position: (value, values) => ((values.role?.key === UserRoleEnum.EMPLOYEE) && (!value)) && 'Необходимо указать должность сотрудника',
 | 
					            position: (value, values) =>
 | 
				
			||||||
            phoneNumber: value => !phone(value || '', {
 | 
					                values.role?.key === UserRoleEnum.EMPLOYEE &&
 | 
				
			||||||
                country: "",
 | 
					                !value &&
 | 
				
			||||||
                strictDetection: false,
 | 
					                "Необходимо указать должность сотрудника",
 | 
				
			||||||
                validateMobilePrefix: false
 | 
					            phoneNumber: value =>
 | 
				
			||||||
            }).isValid && 'Неверно указан номер телефона',
 | 
					                !phone(value || "", {
 | 
				
			||||||
        }
 | 
					                    country: "",
 | 
				
			||||||
 | 
					                    strictDetection: false,
 | 
				
			||||||
 | 
					                    validateMobilePrefix: false,
 | 
				
			||||||
 | 
					                }).isValid && "Неверно указан номер телефона",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    return (<BaseFormModal
 | 
					    return (
 | 
				
			||||||
 | 
					        <BaseFormModal
 | 
				
			||||||
            form={form}
 | 
					            form={form}
 | 
				
			||||||
            closeOnSubmit
 | 
					            closeOnSubmit
 | 
				
			||||||
            onClose={() => context.closeContextModal(id)}
 | 
					            onClose={() => context.closeContextModal(id)}
 | 
				
			||||||
            {...innerProps}
 | 
					            {...innerProps}>
 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
            <BaseFormModal.Body>
 | 
					            <BaseFormModal.Body>
 | 
				
			||||||
                <>
 | 
					                <>
 | 
				
			||||||
                    <Fieldset legend={"Общая информация"}>
 | 
					                    <Fieldset legend={"Общая информация"}>
 | 
				
			||||||
@@ -49,19 +68,33 @@ const UserFormModal = ({context, id, innerProps}: ContextModalProps<Props>) => {
 | 
				
			|||||||
                                label={"Имя"}
 | 
					                                label={"Имя"}
 | 
				
			||||||
                                placeholder={"Введите имя пользователя"}
 | 
					                                placeholder={"Введите имя пользователя"}
 | 
				
			||||||
                                {...form.getInputProps("firstName")}
 | 
					                                {...form.getInputProps("firstName")}
 | 
				
			||||||
                                onChange={event => form.getInputProps('firstName').onChange(capitalize(event.target.value).trim())}
 | 
					                                onChange={event =>
 | 
				
			||||||
 | 
					                                    form
 | 
				
			||||||
 | 
					                                        .getInputProps("firstName")
 | 
				
			||||||
 | 
					                                        .onChange(
 | 
				
			||||||
 | 
					                                            capitalize(
 | 
				
			||||||
 | 
					                                                event.target.value
 | 
				
			||||||
 | 
					                                            ).trim()
 | 
				
			||||||
 | 
					                                        )
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
                            />
 | 
					                            />
 | 
				
			||||||
                            <TextInput
 | 
					                            <TextInput
 | 
				
			||||||
                                {...form.getInputProps("secondName")}
 | 
					                                {...form.getInputProps("secondName")}
 | 
				
			||||||
                                label={"Фамилия"}
 | 
					                                label={"Фамилия"}
 | 
				
			||||||
                                placeholder={"Введите фамилию пользователя"}
 | 
					                                placeholder={"Введите фамилию пользователя"}
 | 
				
			||||||
                                onChange={event => form.getInputProps('secondName').onChange(capitalize(event.target.value).trim())}
 | 
					                                onChange={event =>
 | 
				
			||||||
 | 
					                                    form
 | 
				
			||||||
 | 
					                                        .getInputProps("secondName")
 | 
				
			||||||
 | 
					                                        .onChange(
 | 
				
			||||||
 | 
					                                            capitalize(
 | 
				
			||||||
 | 
					                                                event.target.value
 | 
				
			||||||
 | 
					                                            ).trim()
 | 
				
			||||||
 | 
					                                        )
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
                            />
 | 
					                            />
 | 
				
			||||||
                            <Input.Wrapper
 | 
					                            <Input.Wrapper
 | 
				
			||||||
                                label={"Номер телефона"}
 | 
					                                label={"Номер телефона"}
 | 
				
			||||||
                                error={form.getInputProps("phoneNumber").error}
 | 
					                                error={form.getInputProps("phoneNumber").error}>
 | 
				
			||||||
                            >
 | 
					 | 
				
			||||||
                                <Input
 | 
					                                <Input
 | 
				
			||||||
                                    component={IMaskInput}
 | 
					                                    component={IMaskInput}
 | 
				
			||||||
                                    mask="+7 000 000-00-00"
 | 
					                                    mask="+7 000 000-00-00"
 | 
				
			||||||
@@ -76,46 +109,53 @@ const UserFormModal = ({context, id, innerProps}: ContextModalProps<Props>) => {
 | 
				
			|||||||
                            <RoleSelect
 | 
					                            <RoleSelect
 | 
				
			||||||
                                label={"Роль пользователя"}
 | 
					                                label={"Роль пользователя"}
 | 
				
			||||||
                                placeholder={"Выберите роль пользователя"}
 | 
					                                placeholder={"Выберите роль пользователя"}
 | 
				
			||||||
                                {...form.getInputProps('role')}
 | 
					                                {...form.getInputProps("role")}
 | 
				
			||||||
                            />
 | 
					                            />
 | 
				
			||||||
                            {form.values.role?.key === UserRoleEnum.EMPLOYEE &&
 | 
					                            {form.values.role?.key ===
 | 
				
			||||||
 | 
					                                UserRoleEnum.EMPLOYEE && (
 | 
				
			||||||
                                <>
 | 
					                                <>
 | 
				
			||||||
                                    <PositionSelect
 | 
					                                    <PositionSelect
 | 
				
			||||||
                                        label={"Должность сотрудника"}
 | 
					                                        label={"Должность сотрудника"}
 | 
				
			||||||
                                        placeholder={"Выберите должность сотрудника"}
 | 
					                                        placeholder={
 | 
				
			||||||
                                        {...form.getInputProps('position')}
 | 
					                                            "Выберите должность сотрудника"
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                        {...form.getInputProps("position")}
 | 
				
			||||||
                                    />
 | 
					                                    />
 | 
				
			||||||
                                    <PayRateSelect
 | 
					                                    <PayRateSelect
 | 
				
			||||||
                                        label={"Тариф"}
 | 
					                                        label={"Тариф"}
 | 
				
			||||||
                                        placeholder={"Выберите тариф сотрудника"}
 | 
					                                        placeholder={
 | 
				
			||||||
 | 
					                                            "Выберите тариф сотрудника"
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
                                        {...form.getInputProps("payRate")}
 | 
					                                        {...form.getInputProps("payRate")}
 | 
				
			||||||
                                    />
 | 
					                                    />
 | 
				
			||||||
                                </>
 | 
					                                </>
 | 
				
			||||||
 | 
					                            )}
 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        </Stack>
 | 
					                        </Stack>
 | 
				
			||||||
                    </Fieldset>
 | 
					                    </Fieldset>
 | 
				
			||||||
                    <Fieldset legend={"Дополнительные параметры"}>
 | 
					                    <Fieldset legend={"Дополнительные параметры"}>
 | 
				
			||||||
                        <Stack>
 | 
					                        <Stack>
 | 
				
			||||||
                            <Checkbox
 | 
					                            <Checkbox
 | 
				
			||||||
                                label={"Права администратора"}
 | 
					                                label={"Права администратора"}
 | 
				
			||||||
 | 
					                                {...form.getInputProps("isAdmin", {
 | 
				
			||||||
                                {...form.getInputProps('isAdmin', {type: "checkbox"})}
 | 
					                                    type: "checkbox",
 | 
				
			||||||
 | 
					                                })}
 | 
				
			||||||
                            />
 | 
					                            />
 | 
				
			||||||
                            <Checkbox
 | 
					                            <Checkbox
 | 
				
			||||||
                                label={"Заблокирован"}
 | 
					                                label={"Заблокирован"}
 | 
				
			||||||
                                {...form.getInputProps('isBlocked', {type: "checkbox"})}
 | 
					                                {...form.getInputProps("isBlocked", {
 | 
				
			||||||
 | 
					                                    type: "checkbox",
 | 
				
			||||||
 | 
					                                })}
 | 
				
			||||||
                            />
 | 
					                            />
 | 
				
			||||||
                            <Textarea
 | 
					                            <Textarea
 | 
				
			||||||
                                label={"Дополнительная информация"}
 | 
					                                label={"Дополнительная информация"}
 | 
				
			||||||
                                {...form.getInputProps('comment')}
 | 
					                                {...form.getInputProps("comment")}
 | 
				
			||||||
                            />
 | 
					                            />
 | 
				
			||||||
                        </Stack>
 | 
					                        </Stack>
 | 
				
			||||||
                    </Fieldset>
 | 
					                    </Fieldset>
 | 
				
			||||||
                </>
 | 
					                </>
 | 
				
			||||||
            </BaseFormModal.Body>
 | 
					            </BaseFormModal.Body>
 | 
				
			||||||
        </BaseFormModal>
 | 
					        </BaseFormModal>
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default UserFormModal;
 | 
					export default UserFormModal;
 | 
				
			||||||
@@ -1,50 +1,54 @@
 | 
				
			|||||||
import {Tabs} from "@mantine/core";
 | 
					import { Tabs } from "@mantine/core";
 | 
				
			||||||
import {IconBusinessplan, IconHistory} from "@tabler/icons-react";
 | 
					import { IconBusinessplan, IconHistory } from "@tabler/icons-react";
 | 
				
			||||||
import {motion} from "framer-motion";
 | 
					import { motion } from "framer-motion";
 | 
				
			||||||
import PayRateTable from "../../components/PayRateTable/PayRateTable.tsx";
 | 
					import PayRateTable from "../../components/PayRateTable/PayRateTable.tsx";
 | 
				
			||||||
import usePayRatesList from "../../hooks/usePayRatesList.tsx";
 | 
					import usePayRatesList from "../../hooks/usePayRatesList.tsx";
 | 
				
			||||||
import {CRUDTableProps} from "../../../../types/CRUDTable.tsx";
 | 
					import { CRUDTableProps } from "../../../../types/CRUDTable.tsx";
 | 
				
			||||||
import {PayRateSchema, PayRateSchemaBase, PayrollService} from "../../../../client";
 | 
					import {
 | 
				
			||||||
import {notifications} from "../../../../shared/lib/notifications.ts";
 | 
					    PayRateSchema,
 | 
				
			||||||
 | 
					    PayRateSchemaBase,
 | 
				
			||||||
 | 
					    PayrollService,
 | 
				
			||||||
 | 
					} from "../../../../client";
 | 
				
			||||||
 | 
					import { notifications } from "../../../../shared/lib/notifications.ts";
 | 
				
			||||||
import PaymentRecordsTable from "../../components/PaymentRecordsTable/PaymentRecordsTable.tsx";
 | 
					import PaymentRecordsTable from "../../components/PaymentRecordsTable/PaymentRecordsTable.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const payRateTableState = (): CRUDTableProps<PayRateSchema> => {
 | 
					const payRateTableState = (): CRUDTableProps<PayRateSchema> => {
 | 
				
			||||||
    const {objects: items, refetch} = usePayRatesList();
 | 
					    const { objects: items, refetch } = usePayRatesList();
 | 
				
			||||||
    const onCreate = (item: PayRateSchemaBase) => {
 | 
					    const onCreate = (item: PayRateSchemaBase) => {
 | 
				
			||||||
        PayrollService.createPayRate({
 | 
					        PayrollService.createPayRate({
 | 
				
			||||||
            requestBody: {
 | 
					            requestBody: {
 | 
				
			||||||
                data: item
 | 
					                data: item,
 | 
				
			||||||
            }
 | 
					            },
 | 
				
			||||||
        }).then(async ({ok, message}) => {
 | 
					        }).then(async ({ ok, message }) => {
 | 
				
			||||||
            notifications.guess(ok, {message});
 | 
					            notifications.guess(ok, { message });
 | 
				
			||||||
            if (!ok) return;
 | 
					            if (!ok) return;
 | 
				
			||||||
            await refetch();
 | 
					            await refetch();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    const onChange = (item: PayRateSchema) => {
 | 
					    const onChange = (item: PayRateSchema) => {
 | 
				
			||||||
        PayrollService.updatePayRate({
 | 
					        PayrollService.updatePayRate({
 | 
				
			||||||
            requestBody: {
 | 
					            requestBody: {
 | 
				
			||||||
                data: item
 | 
					                data: item,
 | 
				
			||||||
            }
 | 
					            },
 | 
				
			||||||
        }).then(async ({ok, message}) => {
 | 
					        }).then(async ({ ok, message }) => {
 | 
				
			||||||
            notifications.guess(ok, {message});
 | 
					            notifications.guess(ok, { message });
 | 
				
			||||||
            if (!ok) return;
 | 
					            if (!ok) return;
 | 
				
			||||||
            await refetch();
 | 
					            await refetch();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    const onDelete = (item: PayRateSchema) => {
 | 
					    const onDelete = (item: PayRateSchema) => {
 | 
				
			||||||
        PayrollService.deletePayRate({
 | 
					        PayrollService.deletePayRate({
 | 
				
			||||||
            requestBody: {
 | 
					            requestBody: {
 | 
				
			||||||
                payRateId: item.id
 | 
					                payRateId: item.id,
 | 
				
			||||||
            }
 | 
					            },
 | 
				
			||||||
        }).then(async ({ok, message}) => {
 | 
					        }).then(async ({ ok, message }) => {
 | 
				
			||||||
            notifications.guess(ok, {message});
 | 
					            notifications.guess(ok, { message });
 | 
				
			||||||
            if (!ok) return;
 | 
					            if (!ok) return;
 | 
				
			||||||
            await refetch();
 | 
					            await refetch();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    return {items, onCreate, onChange, onDelete};
 | 
					    return { items, onCreate, onChange, onDelete };
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const FinancesTab = () => {
 | 
					const FinancesTab = () => {
 | 
				
			||||||
    const payRateState = payRateTableState();
 | 
					    const payRateState = payRateTableState();
 | 
				
			||||||
@@ -53,45 +57,40 @@ const FinancesTab = () => {
 | 
				
			|||||||
            <Tabs
 | 
					            <Tabs
 | 
				
			||||||
                keepMounted={false}
 | 
					                keepMounted={false}
 | 
				
			||||||
                defaultValue={"paymentRecords"}
 | 
					                defaultValue={"paymentRecords"}
 | 
				
			||||||
                color={"gray.6"}
 | 
					                color={"gray.6"}>
 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
                <Tabs.List
 | 
					                <Tabs.List
 | 
				
			||||||
                    justify={"center"}
 | 
					                    justify={"center"}
 | 
				
			||||||
                    grow
 | 
					                    grow>
 | 
				
			||||||
                >
 | 
					                    <Tabs.Tab
 | 
				
			||||||
                    <Tabs.Tab value={"paymentRecords"} leftSection={<IconHistory/>}>
 | 
					                        value={"paymentRecords"}
 | 
				
			||||||
 | 
					                        leftSection={<IconHistory />}>
 | 
				
			||||||
                        Начисления
 | 
					                        Начисления
 | 
				
			||||||
                    </Tabs.Tab>
 | 
					                    </Tabs.Tab>
 | 
				
			||||||
                    <Tabs.Tab value={"tariffs"} leftSection={<IconBusinessplan/>}>
 | 
					                    <Tabs.Tab
 | 
				
			||||||
 | 
					                        value={"tariffs"}
 | 
				
			||||||
 | 
					                        leftSection={<IconBusinessplan />}>
 | 
				
			||||||
                        Тарифы
 | 
					                        Тарифы
 | 
				
			||||||
                    </Tabs.Tab>
 | 
					                    </Tabs.Tab>
 | 
				
			||||||
                </Tabs.List>
 | 
					                </Tabs.List>
 | 
				
			||||||
                <Tabs.Panel value={"tariffs"}>
 | 
					                <Tabs.Panel value={"tariffs"}>
 | 
				
			||||||
                    <motion.div
 | 
					                    <motion.div
 | 
				
			||||||
                        initial={{opacity: 0}}
 | 
					                        initial={{ opacity: 0 }}
 | 
				
			||||||
                        animate={{opacity: 1}}
 | 
					                        animate={{ opacity: 1 }}
 | 
				
			||||||
                        transition={{duration: 0.2}}
 | 
					                        transition={{ duration: 0.2 }}>
 | 
				
			||||||
 | 
					                        <PayRateTable {...payRateState} />
 | 
				
			||||||
                    >
 | 
					 | 
				
			||||||
                        <PayRateTable
 | 
					 | 
				
			||||||
                            {...payRateState}
 | 
					 | 
				
			||||||
                        />
 | 
					 | 
				
			||||||
                    </motion.div>
 | 
					                    </motion.div>
 | 
				
			||||||
                </Tabs.Panel>
 | 
					                </Tabs.Panel>
 | 
				
			||||||
                <Tabs.Panel value={"paymentRecords"}>
 | 
					                <Tabs.Panel value={"paymentRecords"}>
 | 
				
			||||||
                    <motion.div
 | 
					                    <motion.div
 | 
				
			||||||
                        initial={{opacity: 0}}
 | 
					                        initial={{ opacity: 0 }}
 | 
				
			||||||
                        animate={{opacity: 1}}
 | 
					                        animate={{ opacity: 1 }}
 | 
				
			||||||
                        transition={{duration: 0.2}}
 | 
					                        transition={{ duration: 0.2 }}>
 | 
				
			||||||
                    >
 | 
					                        <PaymentRecordsTable />
 | 
				
			||||||
                        <PaymentRecordsTable/>
 | 
					 | 
				
			||||||
                    </motion.div>
 | 
					                    </motion.div>
 | 
				
			||||||
                </Tabs.Panel>
 | 
					                </Tabs.Panel>
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            </Tabs>
 | 
					            </Tabs>
 | 
				
			||||||
        </>
 | 
					        </>
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default FinancesTab
 | 
					export default FinancesTab;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,32 +1,32 @@
 | 
				
			|||||||
import PositionsTable from "../../components/PositionsTable/PositionsTable.tsx";
 | 
					import PositionsTable from "../../components/PositionsTable/PositionsTable.tsx";
 | 
				
			||||||
import usePositionsList from "../../hooks/usePositionsList.tsx";
 | 
					import usePositionsList from "../../hooks/usePositionsList.tsx";
 | 
				
			||||||
import {PositionSchema, PositionService} from "../../../../client";
 | 
					import { PositionSchema, PositionService } from "../../../../client";
 | 
				
			||||||
import {notifications} from "../../../../shared/lib/notifications.ts";
 | 
					import { notifications } from "../../../../shared/lib/notifications.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const RolesAndPositionsTab = () => {
 | 
					const RolesAndPositionsTab = () => {
 | 
				
			||||||
    const {objects: positions, refetch} = usePositionsList();
 | 
					    const { objects: positions, refetch } = usePositionsList();
 | 
				
			||||||
    const onCreate = (position: PositionSchema) => {
 | 
					    const onCreate = (position: PositionSchema) => {
 | 
				
			||||||
        PositionService.createPosition({
 | 
					        PositionService.createPosition({
 | 
				
			||||||
            requestBody: {
 | 
					            requestBody: {
 | 
				
			||||||
                data: position
 | 
					                data: position,
 | 
				
			||||||
            }
 | 
					            },
 | 
				
			||||||
        }).then(async ({ok, message}) => {
 | 
					        }).then(async ({ ok, message }) => {
 | 
				
			||||||
            notifications.guess(ok, {message});
 | 
					            notifications.guess(ok, { message });
 | 
				
			||||||
            if (!ok) return;
 | 
					            if (!ok) return;
 | 
				
			||||||
            await refetch();
 | 
					            await refetch();
 | 
				
			||||||
        })
 | 
					        });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    const onDelete = (position: PositionSchema) => {
 | 
					    const onDelete = (position: PositionSchema) => {
 | 
				
			||||||
        PositionService.deletePosition({
 | 
					        PositionService.deletePosition({
 | 
				
			||||||
            requestBody: {
 | 
					            requestBody: {
 | 
				
			||||||
                positionKey: position.key
 | 
					                positionKey: position.key,
 | 
				
			||||||
            }
 | 
					            },
 | 
				
			||||||
        }).then(async ({ok, message}) => {
 | 
					        }).then(async ({ ok, message }) => {
 | 
				
			||||||
            notifications.guess(ok, {message});
 | 
					            notifications.guess(ok, { message });
 | 
				
			||||||
            if (!ok) return;
 | 
					            if (!ok) return;
 | 
				
			||||||
            await refetch();
 | 
					            await refetch();
 | 
				
			||||||
        })
 | 
					        });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <PositionsTable
 | 
					        <PositionsTable
 | 
				
			||||||
@@ -34,6 +34,6 @@ const RolesAndPositionsTab = () => {
 | 
				
			|||||||
            onCreate={onCreate}
 | 
					            onCreate={onCreate}
 | 
				
			||||||
            onDelete={onDelete}
 | 
					            onDelete={onDelete}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
export default RolesAndPositionsTab;
 | 
					export default RolesAndPositionsTab;
 | 
				
			||||||
@@ -1,10 +1,10 @@
 | 
				
			|||||||
import useUsersList from "../../hooks/useUsersList.tsx";
 | 
					import useUsersList from "../../hooks/useUsersList.tsx";
 | 
				
			||||||
import UsersTable from "../../components/UsersTable/UsersTable.tsx";
 | 
					import UsersTable from "../../components/UsersTable/UsersTable.tsx";
 | 
				
			||||||
import {UserSchema, UserService} from "../../../../client";
 | 
					import { UserSchema, UserService } from "../../../../client";
 | 
				
			||||||
import {notifications} from "../../../../shared/lib/notifications.ts";
 | 
					import { notifications } from "../../../../shared/lib/notifications.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const UsersTab = () => {
 | 
					const UsersTab = () => {
 | 
				
			||||||
    const {objects: users, refetch} = useUsersList();
 | 
					    const { objects: users, refetch } = useUsersList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const onChange = (user: UserSchema) => {
 | 
					    const onChange = (user: UserSchema) => {
 | 
				
			||||||
        UserService.updateUser({
 | 
					        UserService.updateUser({
 | 
				
			||||||
@@ -12,32 +12,32 @@ const UsersTab = () => {
 | 
				
			|||||||
                data: {
 | 
					                data: {
 | 
				
			||||||
                    ...user,
 | 
					                    ...user,
 | 
				
			||||||
                    positionKey: user.position?.key,
 | 
					                    positionKey: user.position?.key,
 | 
				
			||||||
                    roleKey: user.role.key
 | 
					                    roleKey: user.role.key,
 | 
				
			||||||
                }
 | 
					                },
 | 
				
			||||||
            }
 | 
					            },
 | 
				
			||||||
        }).then(async ({ok, message}) => {
 | 
					        }).then(async ({ ok, message }) => {
 | 
				
			||||||
            notifications.guess(ok, {message});
 | 
					            notifications.guess(ok, { message });
 | 
				
			||||||
            if (!ok) return;
 | 
					            if (!ok) return;
 | 
				
			||||||
            await refetch();
 | 
					            await refetch();
 | 
				
			||||||
        })
 | 
					        });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    const onDelete = async (user: UserSchema) => {
 | 
					    const onDelete = async (user: UserSchema) => {
 | 
				
			||||||
        onChange({...user, isDeleted: true});
 | 
					        onChange({ ...user, isDeleted: true });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    const onCreate = (user: UserSchema) => {
 | 
					    const onCreate = (user: UserSchema) => {
 | 
				
			||||||
        UserService.createUser({
 | 
					        UserService.createUser({
 | 
				
			||||||
            requestBody: {
 | 
					            requestBody: {
 | 
				
			||||||
                data: {
 | 
					                data: {
 | 
				
			||||||
                    ...user,
 | 
					                    ...user,
 | 
				
			||||||
                    telegramId: -1
 | 
					                    telegramId: -1,
 | 
				
			||||||
                }
 | 
					                },
 | 
				
			||||||
            }
 | 
					            },
 | 
				
			||||||
        }).then(async ({ok, message}) => {
 | 
					        }).then(async ({ ok, message }) => {
 | 
				
			||||||
            notifications.guess(ok, {message});
 | 
					            notifications.guess(ok, { message });
 | 
				
			||||||
            if (!ok) return;
 | 
					            if (!ok) return;
 | 
				
			||||||
            await refetch();
 | 
					            await refetch();
 | 
				
			||||||
        })
 | 
					        });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <UsersTable
 | 
					        <UsersTable
 | 
				
			||||||
            items={users}
 | 
					            items={users}
 | 
				
			||||||
@@ -45,7 +45,7 @@ const UsersTab = () => {
 | 
				
			|||||||
            onDelete={onDelete}
 | 
					            onDelete={onDelete}
 | 
				
			||||||
            onCreate={onCreate}
 | 
					            onCreate={onCreate}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default UsersTab;
 | 
					export default UsersTab;
 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user