crap
This commit is contained in:
@@ -27,8 +27,12 @@ export type { HTTPValidationError } from './models/HTTPValidationError';
|
|||||||
export type { PaginationInfoSchema } from './models/PaginationInfoSchema';
|
export type { PaginationInfoSchema } from './models/PaginationInfoSchema';
|
||||||
export type { ProductCreateRequest } from './models/ProductCreateRequest';
|
export type { ProductCreateRequest } from './models/ProductCreateRequest';
|
||||||
export type { ProductCreateResponse } from './models/ProductCreateResponse';
|
export type { ProductCreateResponse } from './models/ProductCreateResponse';
|
||||||
|
export type { ProductDeleteRequest } from './models/ProductDeleteRequest';
|
||||||
|
export type { ProductDeleteResponse } from './models/ProductDeleteResponse';
|
||||||
export type { ProductGetResponse } from './models/ProductGetResponse';
|
export type { ProductGetResponse } from './models/ProductGetResponse';
|
||||||
export type { ProductSchema } from './models/ProductSchema';
|
export type { ProductSchema } from './models/ProductSchema';
|
||||||
|
export type { ProductUpdateRequest } from './models/ProductUpdateRequest';
|
||||||
|
export type { ProductUpdateResponse } from './models/ProductUpdateResponse';
|
||||||
export type { ServiceCategorySchema } from './models/ServiceCategorySchema';
|
export type { ServiceCategorySchema } from './models/ServiceCategorySchema';
|
||||||
export type { ServiceCreateCategoryRequest } from './models/ServiceCreateCategoryRequest';
|
export type { ServiceCreateCategoryRequest } from './models/ServiceCreateCategoryRequest';
|
||||||
export type { ServiceCreateCategoryResponse } from './models/ServiceCreateCategoryResponse';
|
export type { ServiceCreateCategoryResponse } from './models/ServiceCreateCategoryResponse';
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
export type AuthLoginResponse = {
|
export type AuthLoginResponse = {
|
||||||
access_token: string;
|
accessToken: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
export type ClientDetailsSchema = {
|
export type ClientDetailsSchema = {
|
||||||
address?: (string | null);
|
address?: (string | null);
|
||||||
phone_number?: (string | null);
|
phoneNumber?: (string | null);
|
||||||
inn?: (number | null);
|
inn?: (number | null);
|
||||||
email?: (string | null);
|
email?: (string | null);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import type { ClientDetailsSchema } from './ClientDetailsSchema';
|
import type { ClientDetailsSchema } from './ClientDetailsSchema';
|
||||||
export type ClientUpdateDetailsRequest = {
|
export type ClientUpdateDetailsRequest = {
|
||||||
client_id: number;
|
clientId: number;
|
||||||
details: ClientDetailsSchema;
|
details: ClientDetailsSchema;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import type { DealServiceSchema } from './DealServiceSchema';
|
import type { DealServiceSchema } from './DealServiceSchema';
|
||||||
export type DealAddServicesRequest = {
|
export type DealAddServicesRequest = {
|
||||||
deal_id: number;
|
dealId: number;
|
||||||
services: Array<DealServiceSchema>;
|
services: Array<DealServiceSchema>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
export type DealChangeStatusRequest = {
|
export type DealChangeStatusRequest = {
|
||||||
deal_id: number;
|
dealId: number;
|
||||||
new_status: number;
|
newStatus: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
export type DealQuickCreateRequest = {
|
export type DealQuickCreateRequest = {
|
||||||
name: string;
|
name: string;
|
||||||
client_name: string;
|
clientName: string;
|
||||||
client_address: string;
|
clientAddress: string;
|
||||||
comment: string;
|
comment: string;
|
||||||
acceptance_date: string;
|
acceptanceDate: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
export type DealQuickCreateResponse = {
|
export type DealQuickCreateResponse = {
|
||||||
deal_id: number;
|
dealId: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
export type DealSummary = {
|
export type DealSummary = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
client_name: string;
|
clientName: string;
|
||||||
changed_at: string;
|
changedAt: string;
|
||||||
status: number;
|
status: number;
|
||||||
total_price: number;
|
totalPrice: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
export type PaginationInfoSchema = {
|
export type PaginationInfoSchema = {
|
||||||
total_pages: number;
|
totalPages: number;
|
||||||
total_items: number;
|
totalItems: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
export type ProductCreateRequest = {
|
export type ProductCreateRequest = {
|
||||||
name: string;
|
name: string;
|
||||||
article: string;
|
article: string;
|
||||||
client_id: number;
|
clientId: number;
|
||||||
|
barcodes: Array<string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
export type ProductCreateResponse = {
|
export type ProductCreateResponse = {
|
||||||
product_id: number;
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
productId?: (number | null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
8
src/client/models/ProductDeleteRequest.ts
Normal file
8
src/client/models/ProductDeleteRequest.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type ProductDeleteRequest = {
|
||||||
|
productId: number;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/ProductDeleteResponse.ts
Normal file
9
src/client/models/ProductDeleteResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type ProductDeleteResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -6,6 +6,6 @@ import type { PaginationInfoSchema } from './PaginationInfoSchema';
|
|||||||
import type { ProductSchema } from './ProductSchema';
|
import type { ProductSchema } from './ProductSchema';
|
||||||
export type ProductGetResponse = {
|
export type ProductGetResponse = {
|
||||||
products: Array<ProductSchema>;
|
products: Array<ProductSchema>;
|
||||||
pagination_info: PaginationInfoSchema;
|
paginationInfo: PaginationInfoSchema;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export type ProductSchema = {
|
|||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
article: string;
|
article: string;
|
||||||
client_id: number;
|
clientId: number;
|
||||||
|
barcodes: Array<string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
9
src/client/models/ProductUpdateRequest.ts
Normal file
9
src/client/models/ProductUpdateRequest.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { ProductSchema } from './ProductSchema';
|
||||||
|
export type ProductUpdateRequest = {
|
||||||
|
product: ProductSchema;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/ProductUpdateResponse.ts
Normal file
9
src/client/models/ProductUpdateResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type ProductUpdateResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -4,7 +4,11 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import type { ProductCreateRequest } from '../models/ProductCreateRequest';
|
import type { ProductCreateRequest } from '../models/ProductCreateRequest';
|
||||||
import type { ProductCreateResponse } from '../models/ProductCreateResponse';
|
import type { ProductCreateResponse } from '../models/ProductCreateResponse';
|
||||||
|
import type { ProductDeleteRequest } from '../models/ProductDeleteRequest';
|
||||||
|
import type { ProductDeleteResponse } from '../models/ProductDeleteResponse';
|
||||||
import type { ProductGetResponse } from '../models/ProductGetResponse';
|
import type { ProductGetResponse } from '../models/ProductGetResponse';
|
||||||
|
import type { ProductUpdateRequest } from '../models/ProductUpdateRequest';
|
||||||
|
import type { ProductUpdateResponse } from '../models/ProductUpdateResponse';
|
||||||
import type { CancelablePromise } from '../core/CancelablePromise';
|
import type { CancelablePromise } from '../core/CancelablePromise';
|
||||||
import { OpenAPI } from '../core/OpenAPI';
|
import { OpenAPI } from '../core/OpenAPI';
|
||||||
import { request as __request } from '../core/request';
|
import { request as __request } from '../core/request';
|
||||||
@@ -14,7 +18,7 @@ export class ProductService {
|
|||||||
* @returns ProductCreateResponse Successful Response
|
* @returns ProductCreateResponse Successful Response
|
||||||
* @throws ApiError
|
* @throws ApiError
|
||||||
*/
|
*/
|
||||||
public static createProductProductCreatePost({
|
public static createProduct({
|
||||||
requestBody,
|
requestBody,
|
||||||
}: {
|
}: {
|
||||||
requestBody: ProductCreateRequest,
|
requestBody: ProductCreateRequest,
|
||||||
@@ -29,6 +33,46 @@ export class ProductService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Delete Product
|
||||||
|
* @returns ProductDeleteResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static deleteProduct({
|
||||||
|
requestBody,
|
||||||
|
}: {
|
||||||
|
requestBody: ProductDeleteRequest,
|
||||||
|
}): CancelablePromise<ProductDeleteResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/product/delete',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Delete Product
|
||||||
|
* @returns ProductUpdateResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static updateProduct({
|
||||||
|
requestBody,
|
||||||
|
}: {
|
||||||
|
requestBody: ProductUpdateRequest,
|
||||||
|
}): CancelablePromise<ProductUpdateResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/product/update',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Get Product
|
* Get Product
|
||||||
* @returns ProductGetResponse Successful Response
|
* @returns ProductGetResponse Successful Response
|
||||||
|
|||||||
@@ -2,13 +2,12 @@ import {
|
|||||||
MantineReactTable,
|
MantineReactTable,
|
||||||
MRT_ColumnDef,
|
MRT_ColumnDef,
|
||||||
MRT_RowData,
|
MRT_RowData,
|
||||||
MRT_Table,
|
|
||||||
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, useImperativeHandle} from 'react';
|
||||||
|
|
||||||
type Props<T extends Record<string, any>, K extends keyof T> = {
|
type Props<T extends Record<string, any>, K extends keyof T> = {
|
||||||
data: T[],
|
data: T[],
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ const CreateDealButton: FC<Props> = () => {
|
|||||||
DealService.quickCreateDealQuickCreatePost({
|
DealService.quickCreateDealQuickCreatePost({
|
||||||
requestBody: {
|
requestBody: {
|
||||||
...quickDeal,
|
...quickDeal,
|
||||||
acceptance_date: dateWithoutTimezone(quickDeal.acceptance_date)
|
acceptanceDate: dateWithoutTimezone(quickDeal.acceptanceDate)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ const CreateDealFrom: FC<Props> = ({onSubmit, onCancel}) => {
|
|||||||
const form = useForm({
|
const form = useForm({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
name: '',
|
name: '',
|
||||||
client_name: '',
|
clientName: '',
|
||||||
client_address: '',
|
clientAddress: '',
|
||||||
comment: '',
|
comment: '',
|
||||||
acceptance_date: new Date()
|
acceptanceDate: new Date()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
@@ -43,8 +43,8 @@ const CreateDealFrom: FC<Props> = ({onSubmit, onCancel}) => {
|
|||||||
<div className={styles['inputs']}>
|
<div className={styles['inputs']}>
|
||||||
<ClientAutocomplete
|
<ClientAutocomplete
|
||||||
withAddress
|
withAddress
|
||||||
nameRestProps={form.getInputProps('client_name')}
|
nameRestProps={form.getInputProps('clientName')}
|
||||||
addressRestProps={form.getInputProps('client_address')}
|
addressRestProps={form.getInputProps('clientAddress')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ const CreateDealFrom: FC<Props> = ({onSubmit, onCancel}) => {
|
|||||||
<div className={styles['inputs']}>
|
<div className={styles['inputs']}>
|
||||||
<DateTimePicker
|
<DateTimePicker
|
||||||
placeholder={'Дата приемки'}
|
placeholder={'Дата приемки'}
|
||||||
{...form.getInputProps('acceptance_date')}
|
{...form.getInputProps('acceptanceDate')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const DealSummaryCard: FC<Props> = ({dealSummary}) => {
|
|||||||
<div className={styles['flex-row']}>
|
<div className={styles['flex-row']}>
|
||||||
<div className={styles['flex-item']}>
|
<div className={styles['flex-item']}>
|
||||||
<Text size={"sm"} c={"gray.6"}>
|
<Text size={"sm"} c={"gray.6"}>
|
||||||
{dealSummary.client_name}
|
{dealSummary.clientName}
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles['flex-item']}>
|
<div className={styles['flex-item']}>
|
||||||
@@ -23,14 +23,14 @@ const DealSummaryCard: FC<Props> = ({dealSummary}) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className={styles['flex-item']}>
|
<div className={styles['flex-item']}>
|
||||||
<Text size={"sm"} c={"gray.6"}>
|
<Text size={"sm"} c={"gray.6"}>
|
||||||
{dealSummary.total_price.toLocaleString('ru-RU')} руб
|
{dealSummary.totalPrice.toLocaleString('ru-RU')} руб
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={classNames(styles['flex-row'], styles['flex-row-right'])}>
|
<div className={classNames(styles['flex-row'], styles['flex-row-right'])}>
|
||||||
<div className={styles['flex-item']}>
|
<div className={styles['flex-item']}>
|
||||||
<Text size={"sm"} c={"gray.6"}>
|
<Text size={"sm"} c={"gray.6"}>
|
||||||
{new Date(dealSummary.changed_at).toLocaleString('ru-RU')}
|
{new Date(dealSummary.changedAt).toLocaleString('ru-RU')}
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles['flex-item']}>
|
<div className={styles['flex-item']}>
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
import {ContextModalProps} from "@mantine/modals";
|
|
||||||
import {Button, Text} from "@mantine/core";
|
|
||||||
import {useForm} from "@mantine/form";
|
|
||||||
|
|
||||||
|
|
||||||
const CreateProductModal = ({
|
|
||||||
context,
|
|
||||||
id,
|
|
||||||
innerProps,
|
|
||||||
}: ContextModalProps<{ clientId: number }>) => {
|
|
||||||
const form = useForm({
|
|
||||||
initialValues: {
|
|
||||||
name: '',
|
|
||||||
article: '',
|
|
||||||
barcode: ''
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Button fullWidth mt="md" onClick={() => context.closeModal(id)}>
|
|
||||||
Close modal
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
export default CreateProductModal;
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import EnterDeadlineModal from "./EnterDeadlineModal/EnterDeadlineModal.tsx";
|
import EnterDeadlineModal from "./EnterDeadlineModal/EnterDeadlineModal.tsx";
|
||||||
import CreateServiceCategoryModal from "../pages/ServicesPage/modals/CreateServiceCategoryModal.tsx";
|
import CreateServiceCategoryModal from "../pages/ServicesPage/modals/CreateServiceCategoryModal.tsx";
|
||||||
import CreateServiceModal from "../pages/ServicesPage/modals/CreateServiceModal.tsx";
|
import CreateServiceModal from "../pages/ServicesPage/modals/CreateServiceModal.tsx";
|
||||||
import createProductModal from "./CreateProductModal/CreateProductModal.tsx";
|
import createProductModal from "../pages/ProductsPage/modals/CreateProductModal/CreateProductModal.tsx";
|
||||||
|
|
||||||
export const modals = {
|
export const modals = {
|
||||||
enterDeadline: EnterDeadlineModal,
|
enterDeadline: EnterDeadlineModal,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, {FC, useEffect, useState} from "react";
|
import {FC, useEffect, useState} from "react";
|
||||||
import styles from './LeadsPage.module.css';
|
import styles from './LeadsPage.module.css';
|
||||||
import Board from "../../../components/Dnd/Board/Board.tsx";
|
import Board from "../../../components/Dnd/Board/Board.tsx";
|
||||||
import {DragDropContext} from "@hello-pangea/dnd";
|
import {DragDropContext} from "@hello-pangea/dnd";
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ const LoginPage = () => {
|
|||||||
},
|
},
|
||||||
(data: TelegramUser) => {
|
(data: TelegramUser) => {
|
||||||
AuthService.loginAuthLoginPost({requestBody: data})
|
AuthService.loginAuthLoginPost({requestBody: data})
|
||||||
.then(({access_token}) => {
|
.then(({accessToken}) => {
|
||||||
dispatch(login({accessToken: access_token}));
|
dispatch(login({accessToken: accessToken}));
|
||||||
navigate({to: "/"}).then(() => {
|
navigate({to: "/"}).then(() => {
|
||||||
notifications.success({message: "Вы успешно вошли!"})
|
notifications.success({message: "Вы успешно вошли!"})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -10,5 +10,4 @@
|
|||||||
.container {
|
.container {
|
||||||
padding: rem(20);
|
padding: rem(20);
|
||||||
display: flex;
|
display: flex;
|
||||||
//min-height: 100vh;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,20 +14,17 @@ const PageWrapper: FC<Props> = ({children}) => {
|
|||||||
<AppShell
|
<AppShell
|
||||||
layout={"alt"}
|
layout={"alt"}
|
||||||
navbar={{width: rem('80px'), breakpoint: "sm"}}
|
navbar={{width: rem('80px'), breakpoint: "sm"}}
|
||||||
// header={{height:rem(60)}}
|
|
||||||
>
|
>
|
||||||
{/*<AppShell.Header>*/}
|
|
||||||
{/* <Header/>*/}
|
|
||||||
{/*</AppShell.Header>*/}
|
|
||||||
<AppShell.Navbar>
|
<AppShell.Navbar>
|
||||||
<Navbar/>
|
{authState.isAuthorized &&
|
||||||
|
<Navbar/>
|
||||||
|
}
|
||||||
</AppShell.Navbar>
|
</AppShell.Navbar>
|
||||||
<AppShell.Main className={styles['main-container']}>
|
<AppShell.Main className={styles['main-container']}>
|
||||||
<div className={styles['container']}>
|
<div className={styles['container']}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</AppShell.Main>
|
</AppShell.Main>
|
||||||
</AppShell>
|
</AppShell>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,24 +1,56 @@
|
|||||||
import {ProductSchema} from "../../../../client";
|
import {ProductSchema} from "../../../../client";
|
||||||
import {FC, RefObject} from "react";
|
import {FC} from "react";
|
||||||
import {BaseTable, BaseTableRef} from "../../../../components/BaseTable/BaseTable.tsx";
|
import {BaseTable} from "../../../../components/BaseTable/BaseTable.tsx";
|
||||||
import {MRT_TableOptions} from "mantine-react-table";
|
import {MRT_TableOptions} from "mantine-react-table";
|
||||||
import {useProductsTableColumns} from "./columns.tsx";
|
import {useProductsTableColumns} from "./columns.tsx";
|
||||||
|
import {ActionIcon, Flex, Tooltip} from "@mantine/core";
|
||||||
|
import {IconEdit, IconTrash} from "@tabler/icons-react";
|
||||||
|
import {CRUDTableProps} from "../../../../types/CRUDTable.tsx";
|
||||||
|
import {modals} from "@mantine/modals";
|
||||||
|
|
||||||
type Props = {
|
|
||||||
products: ProductSchema[];
|
const ProductsTable: FC<CRUDTableProps<ProductSchema>> = ({items, onDelete, onChange, tableRef}) => {
|
||||||
tableRef?: RefObject<BaseTableRef<ProductSchema>>
|
|
||||||
}
|
|
||||||
const ProductsTable: FC<Props> = ({products, tableRef}) => {
|
|
||||||
const columns = useProductsTableColumns();
|
const columns = useProductsTableColumns();
|
||||||
|
const onEditClick = (product: ProductSchema) => {
|
||||||
|
if (!onChange) return;
|
||||||
|
modals.openContextModal({
|
||||||
|
modal: "createProduct",
|
||||||
|
title: 'Создание товара',
|
||||||
|
withCloseButton: false,
|
||||||
|
innerProps: {
|
||||||
|
onChange: (newProduct) => onChange(newProduct),
|
||||||
|
product: product,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<BaseTable
|
<BaseTable
|
||||||
ref={tableRef}
|
ref={tableRef}
|
||||||
data={products}
|
data={items}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
restProps={{
|
restProps={{
|
||||||
enableColumnActions: false,
|
enableColumnActions: false,
|
||||||
} as MRT_TableOptions<ProductSchema>}
|
enableRowActions: true,
|
||||||
/>
|
renderRowActions: ({row}) => (
|
||||||
|
<Flex gap="md">
|
||||||
|
<Tooltip label="Редактировать">
|
||||||
|
<ActionIcon
|
||||||
|
onClick={() => onEditClick(row.original)}
|
||||||
|
variant={"default"}>
|
||||||
|
<IconEdit/>
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip label="Удалить">
|
||||||
|
<ActionIcon onClick={() => {
|
||||||
|
if (onDelete) onDelete(row.original);
|
||||||
|
}} variant={"default"}>
|
||||||
|
<IconTrash/>
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
} as MRT_TableOptions<ProductSchema>}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import {useMemo} from "react";
|
import {useMemo} from "react";
|
||||||
import {MRT_ColumnDef} from "mantine-react-table";
|
import {MRT_ColumnDef} from "mantine-react-table";
|
||||||
import {ProductSchema} from "../../../../client";
|
import {ProductSchema} from "../../../../client";
|
||||||
|
import {List, Spoiler, useMantineTheme} from "@mantine/core";
|
||||||
|
|
||||||
export const useProductsTableColumns = () => {
|
export const useProductsTableColumns = () => {
|
||||||
|
const theme = useMantineTheme();
|
||||||
return useMemo<MRT_ColumnDef<ProductSchema>[]>(() => [
|
return useMemo<MRT_ColumnDef<ProductSchema>[]>(() => [
|
||||||
{
|
{
|
||||||
accessorKey: "article",
|
accessorKey: "article",
|
||||||
@@ -13,8 +15,27 @@ export const useProductsTableColumns = () => {
|
|||||||
accessorKey: "name",
|
accessorKey: "name",
|
||||||
header: "Название",
|
header: "Название",
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
|
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "barcodes",
|
||||||
|
header: "Штрихкоды",
|
||||||
|
Cell: ({cell}) => {
|
||||||
|
return (
|
||||||
|
<List size={"sm"}>
|
||||||
|
<Spoiler maxHeight={parseFloat(theme.lineHeights.sm) * 25}
|
||||||
|
showLabel={"Показать все"}
|
||||||
|
hideLabel={"Скрыть"}>
|
||||||
|
{cell.getValue<string[]>().map(barcode => (
|
||||||
|
<List.Item key={barcode}>
|
||||||
|
{barcode}
|
||||||
|
</List.Item>
|
||||||
|
))}
|
||||||
|
</Spoiler>
|
||||||
|
|
||||||
|
</List>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
], []);
|
], []);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -8,12 +8,12 @@ type Props = {
|
|||||||
}
|
}
|
||||||
const useServicesList = (props: Props) => {
|
const useServicesList = (props: Props) => {
|
||||||
const {clientId, page, itemsPerPage} = props;
|
const {clientId, page, itemsPerPage} = props;
|
||||||
const {isPending, error, data, refetch} = useQuery({
|
const {data, refetch} = useQuery({
|
||||||
queryKey: ['getAllServices', clientId, page, itemsPerPage],
|
queryKey: ['getAllServices', clientId, page, itemsPerPage],
|
||||||
queryFn: () => ProductService.getProductsByClientId({clientId, page, itemsPerPage})
|
queryFn: () => ProductService.getProductsByClientId({clientId, page, itemsPerPage})
|
||||||
});
|
});
|
||||||
const products = isPending || error || !data ? [] : data.products;
|
const products = !data ? [] : data.products;
|
||||||
const paginationInfo = data?.pagination_info;
|
const paginationInfo = data?.paginationInfo;
|
||||||
return {products, paginationInfo, refetch}
|
return {products, paginationInfo, refetch}
|
||||||
}
|
}
|
||||||
export default useServicesList;
|
export default useServicesList;
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import {ContextModalProps} from "@mantine/modals";
|
||||||
|
import {Button, Flex, rem, TagsInput, TextInput} from "@mantine/core";
|
||||||
|
import {useForm} from "@mantine/form";
|
||||||
|
import {BaseProduct, CreateProductRequest} from "../../types.ts";
|
||||||
|
import {ProductSchema} from "../../../../client";
|
||||||
|
|
||||||
|
type CreateProps = {
|
||||||
|
clientId: number;
|
||||||
|
onCreate: (values: CreateProductRequest) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
type EditProps = {
|
||||||
|
product: ProductSchema,
|
||||||
|
onChange: (values: ProductSchema) => void
|
||||||
|
}
|
||||||
|
type Props = CreateProps | EditProps;
|
||||||
|
|
||||||
|
|
||||||
|
const CreateProductModal = ({
|
||||||
|
context,
|
||||||
|
id,
|
||||||
|
innerProps,
|
||||||
|
}: ContextModalProps<Props>) => {
|
||||||
|
const isEditProps = 'product' in innerProps;
|
||||||
|
const isCreatingProps = 'clientId' in innerProps;
|
||||||
|
const initialValues = isEditProps ? {
|
||||||
|
name: innerProps.product.name,
|
||||||
|
article: innerProps.product.article,
|
||||||
|
barcodes: innerProps.product.barcodes
|
||||||
|
} : {
|
||||||
|
name: '',
|
||||||
|
article: '',
|
||||||
|
barcodes: []
|
||||||
|
};
|
||||||
|
const form = useForm({
|
||||||
|
initialValues: initialValues,
|
||||||
|
validate: {
|
||||||
|
name: (name) => name.trim() !== '' ? null : "Необходимо ввести название товара",
|
||||||
|
article: (article) => article.trim() !== '' ? null : "Необходимо ввести артикул",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const onCancelClick = () => {
|
||||||
|
context.closeContextModal(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSubmit = (values: BaseProduct) => {
|
||||||
|
if (isEditProps) innerProps.onChange({...innerProps.product, ...values})
|
||||||
|
if (isCreatingProps) {
|
||||||
|
innerProps.onCreate({...values, clientId: innerProps.clientId});
|
||||||
|
form.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<form onSubmit={form.onSubmit((values) => onSubmit(values))}>
|
||||||
|
<Flex gap={rem(10)} direction={"column"}>
|
||||||
|
<TextInput
|
||||||
|
placeholder={"Введите название товара"}
|
||||||
|
label={"Название товара"}
|
||||||
|
{...form.getInputProps('name')}
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
placeholder={"Введите артикул"}
|
||||||
|
label={"Артикул"}
|
||||||
|
{...form.getInputProps('article')}
|
||||||
|
/>
|
||||||
|
<TagsInput
|
||||||
|
placeholder={!form.values.barcodes.length ? "Добавьте штрихкоды к товару" : ""}
|
||||||
|
label={"Штрихкоды"}
|
||||||
|
{...form.getInputProps('barcodes')}
|
||||||
|
/>
|
||||||
|
<Flex justify={"flex-end"} mt={rem(5)} gap={rem(10)}>
|
||||||
|
<Button onClick={() => onCancelClick()} variant={"subtle"}>Отменить</Button>
|
||||||
|
{isEditProps ?
|
||||||
|
<Button
|
||||||
|
onClick={() => context.closeContextModal(id)}
|
||||||
|
type={"submit"}
|
||||||
|
variant={"default"}
|
||||||
|
>Сохранить и закрыть</Button> :
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
onClick={() => context.closeContextModal(id)}
|
||||||
|
type={"submit"}
|
||||||
|
variant={"default"}
|
||||||
|
>Создать и закрыть</Button>
|
||||||
|
<Button
|
||||||
|
type={"submit"}
|
||||||
|
variant={"default"}
|
||||||
|
>Создать</Button>
|
||||||
|
</>}
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</form>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
export default CreateProductModal;
|
||||||
6
src/pages/ProductsPage/types.ts
Normal file
6
src/pages/ProductsPage/types.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export type BaseProduct = {
|
||||||
|
name: string;
|
||||||
|
article: string;
|
||||||
|
barcodes: string[];
|
||||||
|
}
|
||||||
|
export type CreateProductRequest = BaseProduct & { clientId: number }
|
||||||
@@ -1,37 +1,83 @@
|
|||||||
import PageBlock from "../../../components/PageBlock/PageBlock.tsx";
|
import PageBlock from "../../../components/PageBlock/PageBlock.tsx";
|
||||||
import {FC, useEffect, useState} from "react";
|
import {FC, useEffect, useState} from "react";
|
||||||
import styles from './ProductsPage.module.css';
|
import styles from './ProductsPage.module.css';
|
||||||
import {Button, Drawer, Pagination} from "@mantine/core";
|
import {Button, Text, Pagination} from "@mantine/core";
|
||||||
import {useDisclosure, usePagination} from "@mantine/hooks";
|
|
||||||
import ClientSelect from "../../../components/Selects/ClientSelect/ClientSelect.tsx";
|
import ClientSelect from "../../../components/Selects/ClientSelect/ClientSelect.tsx";
|
||||||
import ProductsTable from "../components/ProductsTable/ProductsTable.tsx";
|
import ProductsTable from "../components/ProductsTable/ProductsTable.tsx";
|
||||||
import useProductsList from "../hooks/useProductsList.tsx";
|
import useProductsList from "../hooks/useProductsList.tsx";
|
||||||
import {notifications} from "../../../shared/lib/notifications.ts";
|
|
||||||
import {modals} from "@mantine/modals";
|
import {modals} from "@mantine/modals";
|
||||||
|
import {notifications} from "../../../shared/lib/notifications.ts";
|
||||||
|
import {CreateProductRequest} from "../types.ts";
|
||||||
|
import {ProductSchema, ProductService} from "../../../client";
|
||||||
|
|
||||||
export const ProductsPage: FC = () => {
|
export const ProductsPage: FC = () => {
|
||||||
// const [opened, {open, close}] = useDisclosure(false);
|
|
||||||
const [clientId, setClientId] = useState(-1);
|
const [clientId, setClientId] = useState(-1);
|
||||||
const [totalPages, setTotalPages] = useState(1);
|
const [totalPages, setTotalPages] = useState(1);
|
||||||
const pagination = usePagination({total: totalPages});
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const {products, paginationInfo} = useProductsList({
|
|
||||||
|
const {products, paginationInfo, refetch} = useProductsList({
|
||||||
clientId,
|
clientId,
|
||||||
page: pagination.active - 1,
|
page: currentPage - 1,
|
||||||
itemsPerPage: 10
|
itemsPerPage: 10
|
||||||
});
|
});
|
||||||
|
const onProductCreate = (request: CreateProductRequest) => {
|
||||||
|
ProductService.createProduct({requestBody: request})
|
||||||
|
.then(async ({ok, message}) => {
|
||||||
|
notifications.guess(ok, {message: message});
|
||||||
|
if (!ok) return;
|
||||||
|
await refetch();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const onProductChange = (product: ProductSchema) => {
|
||||||
|
ProductService.updateProduct({requestBody: {product}})
|
||||||
|
.then(async ({ok, message}) => {
|
||||||
|
notifications.guess(ok, {message});
|
||||||
|
if (!ok) return;
|
||||||
|
await refetch();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const onCreateProductClick = () => {
|
const onCreateProductClick = () => {
|
||||||
|
if (clientId < 0) {
|
||||||
|
notifications.error({message: "Необходимо выбрать клиента"});
|
||||||
|
return
|
||||||
|
}
|
||||||
modals.openContextModal({
|
modals.openContextModal({
|
||||||
modal: "createProduct",
|
modal: "createProduct",
|
||||||
title: 'Создание категории',
|
title: 'Создание товара',
|
||||||
withCloseButton: false,
|
withCloseButton: false,
|
||||||
innerProps: {
|
innerProps: {
|
||||||
clientId
|
clientId,
|
||||||
|
onCreate: onProductCreate
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onDeleteClick = (product: ProductSchema) => {
|
||||||
|
modals.openConfirmModal({
|
||||||
|
title: 'Удаление товара',
|
||||||
|
centered: true,
|
||||||
|
children: (
|
||||||
|
<Text size="sm">
|
||||||
|
Вы уверены что хотите удалить товар {product.name}
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
labels: {confirm: 'Да', cancel: "Нет"},
|
||||||
|
confirmProps: {color: 'red'},
|
||||||
|
onConfirm: () =>
|
||||||
|
ProductService.deleteProduct({requestBody: {productId: product.id}})
|
||||||
|
.then(async ({ok, message}) => {
|
||||||
|
notifications.guess(ok, {message: message});
|
||||||
|
if (!ok) return;
|
||||||
|
await refetch();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!paginationInfo) return;
|
if (!paginationInfo) return;
|
||||||
setTotalPages(paginationInfo.total_pages);
|
setTotalPages(paginationInfo.totalPages);
|
||||||
}, [paginationInfo]);
|
}, [paginationInfo]);
|
||||||
|
|
||||||
|
|
||||||
@@ -50,8 +96,18 @@ export const ProductsPage: FC = () => {
|
|||||||
<PageBlock>
|
<PageBlock>
|
||||||
<div className={styles['body-container']}>
|
<div className={styles['body-container']}>
|
||||||
<div className={styles['table-container']}>
|
<div className={styles['table-container']}>
|
||||||
<ProductsTable products={products}/>
|
<ProductsTable
|
||||||
<Pagination className={styles['table-pagination']} total={100}/>
|
onChange={onProductChange}
|
||||||
|
onDelete={onDeleteClick}
|
||||||
|
items={products}
|
||||||
|
/>
|
||||||
|
<Pagination
|
||||||
|
className={styles['table-pagination']}
|
||||||
|
withEdges
|
||||||
|
onChange={event => setCurrentPage(event)}
|
||||||
|
total={totalPages}
|
||||||
|
value={currentPage}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</PageBlock>
|
</PageBlock>
|
||||||
|
|||||||
@@ -26,10 +26,10 @@ export const ServicesPage: FC = () => {
|
|||||||
}
|
}
|
||||||
const onCreate = (values: ServiceSchema) => {
|
const onCreate = (values: ServiceSchema) => {
|
||||||
ServiceService.createService({requestBody: {service: values}})
|
ServiceService.createService({requestBody: {service: values}})
|
||||||
.then(({ok, message}) => {
|
.then(async ({ok, message}) => {
|
||||||
notifications.guess(ok, {message: message});
|
notifications.guess(ok, {message: message});
|
||||||
if (!ok) return;
|
if (!ok) return;
|
||||||
refetch();
|
await refetch();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const onCreateCategoryClick = () => {
|
const onCreateCategoryClick = () => {
|
||||||
|
|||||||
11
src/types/CRUDTable.tsx
Normal file
11
src/types/CRUDTable.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import {RefObject} from "react";
|
||||||
|
import {BaseTableRef} from "../components/BaseTable/BaseTable.tsx";
|
||||||
|
import {MRT_RowData} from "mantine-react-table";
|
||||||
|
|
||||||
|
export interface CRUDTableProps<T extends MRT_RowData> {
|
||||||
|
items: T[];
|
||||||
|
onCreate?: (item: T) => void;
|
||||||
|
onDelete?: (item: T) => void;
|
||||||
|
onChange?: (item: T) => void;
|
||||||
|
tableRef?: RefObject<BaseTableRef<T>>
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
export type QuickDeal = {
|
export type QuickDeal = {
|
||||||
name: string
|
name: string
|
||||||
client_name: string
|
clientName: string
|
||||||
client_address: string
|
clientAddress: string
|
||||||
comment:string
|
comment:string
|
||||||
acceptance_date: Date
|
acceptanceDate: Date
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user