othr
This commit is contained in:
@@ -31,6 +31,7 @@ export type { DealDeleteServiceResponse } from './models/DealDeleteServiceRespon
|
|||||||
export type { DealDeleteServicesRequest } from './models/DealDeleteServicesRequest';
|
export type { DealDeleteServicesRequest } from './models/DealDeleteServicesRequest';
|
||||||
export type { DealDeleteServicesResponse } from './models/DealDeleteServicesResponse';
|
export type { DealDeleteServicesResponse } from './models/DealDeleteServicesResponse';
|
||||||
export type { DealGetAllResponse } from './models/DealGetAllResponse';
|
export type { DealGetAllResponse } from './models/DealGetAllResponse';
|
||||||
|
export type { DealProductSchema } from './models/DealProductSchema';
|
||||||
export type { DealQuickCreateRequest } from './models/DealQuickCreateRequest';
|
export type { DealQuickCreateRequest } from './models/DealQuickCreateRequest';
|
||||||
export type { DealQuickCreateResponse } from './models/DealQuickCreateResponse';
|
export type { DealQuickCreateResponse } from './models/DealQuickCreateResponse';
|
||||||
export type { DealSchema } from './models/DealSchema';
|
export type { DealSchema } from './models/DealSchema';
|
||||||
|
|||||||
10
src/client/models/DealProductSchema.ts
Normal file
10
src/client/models/DealProductSchema.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { ProductSchema } from './ProductSchema';
|
||||||
|
export type DealProductSchema = {
|
||||||
|
product: ProductSchema;
|
||||||
|
quantity: number;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
/* istanbul ignore file */
|
/* istanbul ignore file */
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
|
import type { DealProductSchema } from './DealProductSchema';
|
||||||
import type { DealServiceSchema } from './DealServiceSchema';
|
import type { DealServiceSchema } from './DealServiceSchema';
|
||||||
export type DealSchema = {
|
export type DealSchema = {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -10,5 +11,6 @@ export type DealSchema = {
|
|||||||
createdAt: string;
|
createdAt: string;
|
||||||
currentStatus: number;
|
currentStatus: number;
|
||||||
services: Array<DealServiceSchema>;
|
services: Array<DealServiceSchema>;
|
||||||
|
products: Array<DealProductSchema>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -84,8 +84,8 @@ export class ProductService {
|
|||||||
itemsPerPage,
|
itemsPerPage,
|
||||||
}: {
|
}: {
|
||||||
clientId: number,
|
clientId: number,
|
||||||
page: number,
|
page?: (number | null),
|
||||||
itemsPerPage: number,
|
itemsPerPage?: (number | null),
|
||||||
}): CancelablePromise<ProductGetResponse> {
|
}): CancelablePromise<ProductGetResponse> {
|
||||||
return __request(OpenAPI, {
|
return __request(OpenAPI, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
|||||||
58
src/components/ProductSelect/ProductSelect.tsx
Normal file
58
src/components/ProductSelect/ProductSelect.tsx
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import {ProductSchema} from "../../client";
|
||||||
|
import {Select, SelectProps} from "@mantine/core";
|
||||||
|
import {FC, useEffect, useMemo, useState} from "react";
|
||||||
|
import useProductsList from "../../pages/ProductsPage/hooks/useProductsList.tsx";
|
||||||
|
|
||||||
|
type ControlledValueProps = {
|
||||||
|
value: ProductSchema;
|
||||||
|
onChange: (value: ProductSchema) => void;
|
||||||
|
}
|
||||||
|
type RestProps = {
|
||||||
|
defaultValue?: ProductSchema;
|
||||||
|
onChange: (value: ProductSchema) => void;
|
||||||
|
clientId: number;
|
||||||
|
}
|
||||||
|
type Props = (RestProps & Partial<ControlledValueProps>) & Omit<SelectProps, 'value' | 'onChange'>;
|
||||||
|
|
||||||
|
const ProductSelect: FC<Props> = (props) => {
|
||||||
|
const isControlled = 'value' in props;
|
||||||
|
const [intertalValue, setInternalValue] = useState<ProductSchema | undefined>(props.defaultValue);
|
||||||
|
const value = isControlled ? props.value : intertalValue
|
||||||
|
|
||||||
|
const {products} = useProductsList({clientId: props.clientId});
|
||||||
|
|
||||||
|
|
||||||
|
const data = useMemo(() => products.reduce((acc, product) => {
|
||||||
|
acc.push({
|
||||||
|
label: product.name,
|
||||||
|
value: product.id.toString()
|
||||||
|
});
|
||||||
|
return acc;
|
||||||
|
}, [] as { label: string, value: string }[]), [products]);
|
||||||
|
|
||||||
|
const handleOnChange = (event: string | null) => {
|
||||||
|
if (!event) return;
|
||||||
|
const product = products.find(product => parseInt(event) == product.id);
|
||||||
|
if (!product) return;
|
||||||
|
if (isControlled) {
|
||||||
|
props.onChange(product);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setInternalValue(product);
|
||||||
|
}
|
||||||
|
useEffect(() => {
|
||||||
|
if (isControlled || !intertalValue) return;
|
||||||
|
props.onChange(intertalValue);
|
||||||
|
}, [intertalValue]);
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
{...props}
|
||||||
|
withCheckIcon={false}
|
||||||
|
searchable
|
||||||
|
value={value?.id.toString()}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
data={data}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default ProductSelect;
|
||||||
@@ -4,6 +4,7 @@ import CreateServiceModal from "../pages/ServicesPage/modals/CreateServiceModal.
|
|||||||
import createProductModal from "../pages/ProductsPage/modals/CreateProductModal/CreateProductModal.tsx";
|
import createProductModal from "../pages/ProductsPage/modals/CreateProductModal/CreateProductModal.tsx";
|
||||||
import ProductFormModal from "../pages/ClientsPage/modals/ClientFormModal/ClientFormModal.tsx";
|
import ProductFormModal from "../pages/ClientsPage/modals/ClientFormModal/ClientFormModal.tsx";
|
||||||
import AddDealServiceModal from "../pages/LeadsPage/modals/AddDealServiceModal.tsx";
|
import AddDealServiceModal from "../pages/LeadsPage/modals/AddDealServiceModal.tsx";
|
||||||
|
import AddDealProductModal from "../pages/LeadsPage/modals/AddDealProductModal.tsx";
|
||||||
|
|
||||||
export const modals = {
|
export const modals = {
|
||||||
enterDeadline: EnterDeadlineModal,
|
enterDeadline: EnterDeadlineModal,
|
||||||
@@ -11,5 +12,6 @@ export const modals = {
|
|||||||
createService: CreateServiceModal,
|
createService: CreateServiceModal,
|
||||||
createProduct: createProductModal,
|
createProduct: createProductModal,
|
||||||
productFormModal: ProductFormModal,
|
productFormModal: ProductFormModal,
|
||||||
addDealService: AddDealServiceModal
|
addDealService: AddDealServiceModal,
|
||||||
|
addDealProduct: AddDealProductModal
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import {BaseTable} from "../../../../components/BaseTable/BaseTable.tsx";
|
||||||
|
import useDealProductsTableColumns from "./columns.tsx";
|
||||||
|
import {FC} from "react";
|
||||||
|
import {CRUDTableProps} from "../../../../types/CRUDTable.tsx";
|
||||||
|
import {DealProductSchema} from "../../../../client";
|
||||||
|
import {Button, Flex, rem} from "@mantine/core";
|
||||||
|
import {MRT_TableOptions} from "mantine-react-table";
|
||||||
|
import {modals} from "@mantine/modals";
|
||||||
|
|
||||||
|
type RestProps = {
|
||||||
|
clientId: number;
|
||||||
|
}
|
||||||
|
type Props = CRUDTableProps<DealProductSchema> & RestProps;
|
||||||
|
const DealProductsTable: FC<Props> = ({items, clientId}) => {
|
||||||
|
const columns = useDealProductsTableColumns();
|
||||||
|
const onCreateClick = () => {
|
||||||
|
modals.openContextModal({
|
||||||
|
modal: "addDealProduct",
|
||||||
|
title: "Добавление товара",
|
||||||
|
innerProps: {
|
||||||
|
onCreate: event => console.log(event),
|
||||||
|
clientId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseTable
|
||||||
|
data={items}
|
||||||
|
columns={columns}
|
||||||
|
restProps={{
|
||||||
|
renderBottomToolbar: ({}) => (
|
||||||
|
<Flex justify={"flex-end"} gap={rem(10)} p={rem(10)}>
|
||||||
|
{/*{(onMultipleDelete && table.getSelectedRowModel().rows.length > 0) && (*/}
|
||||||
|
{/* <Button*/}
|
||||||
|
{/* onClick={() => {*/}
|
||||||
|
{/* onMultipleDelete(table.getSelectedRowModel().rows.map(row => row.original))*/}
|
||||||
|
{/* }}*/}
|
||||||
|
{/* variant={"filled"}*/}
|
||||||
|
{/* color={"red"}*/}
|
||||||
|
{/* >*/}
|
||||||
|
{/* Удалить выбранные*/}
|
||||||
|
{/* </Button>*/}
|
||||||
|
{/*)}*/}
|
||||||
|
<Button onClick={onCreateClick} variant={"default"}>
|
||||||
|
Добавить услугу
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
</Flex>
|
||||||
|
),
|
||||||
|
} as MRT_TableOptions<DealProductSchema>}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DealProductsTable;
|
||||||
22
src/pages/LeadsPage/components/DealProductsTable/columns.tsx
Normal file
22
src/pages/LeadsPage/components/DealProductsTable/columns.tsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import {useMemo} from "react";
|
||||||
|
import {MRT_ColumnDef} from "mantine-react-table";
|
||||||
|
import {DealProductSchema} from "../../../../client";
|
||||||
|
|
||||||
|
const useDealProductsTableColumns = () => {
|
||||||
|
return useMemo<MRT_ColumnDef<DealProductSchema>[]>(() => [
|
||||||
|
{
|
||||||
|
accessorKey: "products.name",
|
||||||
|
header: "Название"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "products.article",
|
||||||
|
header: "Артикул"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "quantity",
|
||||||
|
header: "Количество"
|
||||||
|
}
|
||||||
|
], [])
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useDealProductsTableColumns;
|
||||||
@@ -4,7 +4,7 @@ import {BaseTable} from "../../../../components/BaseTable/BaseTable.tsx";
|
|||||||
import {DealServiceSchema} from "../../../../client";
|
import {DealServiceSchema} from "../../../../client";
|
||||||
import {CRUDTableProps} from "../../../../types/CRUDTable.tsx";
|
import {CRUDTableProps} from "../../../../types/CRUDTable.tsx";
|
||||||
import {MRT_TableOptions} from "mantine-react-table";
|
import {MRT_TableOptions} from "mantine-react-table";
|
||||||
import {ActionIcon, Box, Button, Flex, rem, Tooltip} from "@mantine/core";
|
import {ActionIcon, Button, Flex, rem, Tooltip} from "@mantine/core";
|
||||||
import {openContextModal} from "@mantine/modals";
|
import {openContextModal} from "@mantine/modals";
|
||||||
import {IconTrash} from "@tabler/icons-react";
|
import {IconTrash} from "@tabler/icons-react";
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {DealService, DealServiceSchema} from "../../../../client";
|
|||||||
import {notifications} from "../../../../shared/lib/notifications.ts";
|
import {notifications} from "../../../../shared/lib/notifications.ts";
|
||||||
import {modals} from "@mantine/modals";
|
import {modals} from "@mantine/modals";
|
||||||
import {BaseTableRef} from "../../../../components/BaseTable/BaseTable.tsx";
|
import {BaseTableRef} from "../../../../components/BaseTable/BaseTable.tsx";
|
||||||
|
import DealProductsTable from "../../components/DealProductsTable/DealProductsTable.tsx";
|
||||||
|
|
||||||
const useDealServicesTableState = () => {
|
const useDealServicesTableState = () => {
|
||||||
const {selectedDeal, setSelectedDeal} = useDealPageContext();
|
const {selectedDeal, setSelectedDeal} = useDealPageContext();
|
||||||
@@ -130,7 +131,6 @@ const useDealServicesTableState = () => {
|
|||||||
services: selectedDeal?.services || []
|
services: selectedDeal?.services || []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const DealEditDrawerServicesTable = () => {
|
const DealEditDrawerServicesTable = () => {
|
||||||
const {
|
const {
|
||||||
services,
|
services,
|
||||||
@@ -150,6 +150,29 @@ const DealEditDrawerServicesTable = () => {
|
|||||||
onMultipleDelete={onsServiceMultipleDelete}
|
onMultipleDelete={onsServiceMultipleDelete}
|
||||||
/>)
|
/>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const useDealProductTableState = () => {
|
||||||
|
const {selectedDeal} = useDealPageContext();
|
||||||
|
|
||||||
|
return {
|
||||||
|
clientId: selectedDeal?.clientId || -1,
|
||||||
|
products: selectedDeal?.products || []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const DealEditDrawerProductsTable = () => {
|
||||||
|
const {
|
||||||
|
products,
|
||||||
|
clientId
|
||||||
|
} = useDealProductTableState();
|
||||||
|
return (
|
||||||
|
<DealProductsTable
|
||||||
|
clientId={clientId}
|
||||||
|
items={products}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const useDealEditDrawerState = () => {
|
const useDealEditDrawerState = () => {
|
||||||
const {selectedDeal, setSelectedDeal} = useDealPageContext();
|
const {selectedDeal, setSelectedDeal} = useDealPageContext();
|
||||||
return {
|
return {
|
||||||
@@ -168,6 +191,7 @@ const DealEditDrawer: FC = () => {
|
|||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
opened={isVisible}>
|
opened={isVisible}>
|
||||||
<DealEditDrawerServicesTable/>
|
<DealEditDrawerServicesTable/>
|
||||||
|
<DealEditDrawerProductsTable/>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
60
src/pages/LeadsPage/modals/AddDealProductModal.tsx
Normal file
60
src/pages/LeadsPage/modals/AddDealProductModal.tsx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import {ContextModalProps} from "@mantine/modals";
|
||||||
|
import BaseFormModal, {CreateEditFormProps} from "../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
|
||||||
|
import {DealProductSchema} from "../../../client";
|
||||||
|
import {useForm} from "@mantine/form";
|
||||||
|
import {NumberInput} from "@mantine/core";
|
||||||
|
import ProductSelect from "../../../components/ProductSelect/ProductSelect.tsx";
|
||||||
|
|
||||||
|
type RestProps = {
|
||||||
|
clientId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = CreateEditFormProps<Partial<DealProductSchema>> & RestProps;
|
||||||
|
const AddDealProductModal = ({
|
||||||
|
context,
|
||||||
|
id,
|
||||||
|
innerProps
|
||||||
|
}: ContextModalProps<Props>) => {
|
||||||
|
const form = useForm<Partial<DealProductSchema>>({
|
||||||
|
initialValues: {
|
||||||
|
product: undefined,
|
||||||
|
quantity: 0
|
||||||
|
},
|
||||||
|
validate: {
|
||||||
|
product: (product?: DealProductSchema['product']) => product !== undefined ? null : "Необходимо выбрать товар",
|
||||||
|
quantity: (quantity?: number) => (quantity && quantity > 0) ? null : "Количество должно быть больше 0"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const onClose = () => {
|
||||||
|
context.closeContextModal(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseFormModal
|
||||||
|
{...innerProps}
|
||||||
|
form={form}
|
||||||
|
closeOnSubmit
|
||||||
|
onClose={onClose}>
|
||||||
|
<BaseFormModal.Body>
|
||||||
|
<>
|
||||||
|
<ProductSelect
|
||||||
|
placeholder={"Выберите услугу"}
|
||||||
|
label={"Услуга"}
|
||||||
|
clientId={innerProps.clientId}
|
||||||
|
{...form.getInputProps('service')}
|
||||||
|
/>
|
||||||
|
<NumberInput
|
||||||
|
placeholder={"Введите количество"}
|
||||||
|
label={"Количество"}
|
||||||
|
min={1}
|
||||||
|
{...form.getInputProps('quantity')}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
|
||||||
|
</BaseFormModal.Body>
|
||||||
|
</BaseFormModal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AddDealProductModal;
|
||||||
@@ -13,7 +13,10 @@ const PageWrapper: FC<Props> = ({children}) => {
|
|||||||
return (
|
return (
|
||||||
<AppShell
|
<AppShell
|
||||||
layout={"alt"}
|
layout={"alt"}
|
||||||
navbar={{width: '5%', breakpoint: "sm"}}
|
navbar={{
|
||||||
|
width: '5%',
|
||||||
|
breakpoint: "sm"
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
|
|
||||||
<AppShell.Navbar>
|
<AppShell.Navbar>
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import {ProductService} from "../../../client";
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
clientId: number,
|
clientId: number,
|
||||||
page: number,
|
page?: number,
|
||||||
itemsPerPage: number,
|
itemsPerPage?: number,
|
||||||
}
|
}
|
||||||
const useProductsList = (props: Props) => {
|
const useProductsList = (props: Props) => {
|
||||||
const {clientId, page, itemsPerPage} = props;
|
const {clientId, page, itemsPerPage} = props;
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
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, Text, Pagination} from "@mantine/core";
|
import {Button, Pagination, Text} from "@mantine/core";
|
||||||
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 {modals} from "@mantine/modals";
|
import {modals} from "@mantine/modals";
|
||||||
import {notifications} from "../../../shared/lib/notifications.ts";
|
import {notifications} from "../../../shared/lib/notifications.ts";
|
||||||
import {CreateProductRequest} from "../types.ts";
|
import {CreateProductRequest} from "../types.ts";
|
||||||
import {ProductSchema, ProductService} from "../../../client";
|
import {ProductSchema, ProductService} from "../../../client";
|
||||||
import ServiceSelect from "../../../components/ServiceSelect/ServiceSelect.tsx";
|
|
||||||
import useProductsList from "../hooks/useProductsList.tsx";
|
import useProductsList from "../hooks/useProductsList.tsx";
|
||||||
import useServicesList from "../../ServicesPage/hooks/useServicesList.tsx";
|
import ProductSelect from "../../../components/ProductSelect/ProductSelect.tsx";
|
||||||
|
|
||||||
export const ProductsPage: FC = () => {
|
export const ProductsPage: FC = () => {
|
||||||
const [clientId, setClientId] = useState(-1);
|
const [clientId, setClientId] = useState(-1);
|
||||||
@@ -91,9 +90,11 @@ export const ProductsPage: FC = () => {
|
|||||||
onClick={() => onCreateProductClick()}
|
onClick={() => onCreateProductClick()}
|
||||||
variant={"default"}
|
variant={"default"}
|
||||||
>Создать</Button>
|
>Создать</Button>
|
||||||
{/*<ServiceSelect*/}
|
<ProductSelect
|
||||||
{/* value={selectedService}*/}
|
onChange={event => console.log(event)}
|
||||||
{/* onChange={setSelectedService}/>*/}
|
clientId={8}
|
||||||
|
limit={10}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</PageBlock>
|
</PageBlock>
|
||||||
<PageBlock>
|
<PageBlock>
|
||||||
|
|||||||
Reference in New Issue
Block a user