diff --git a/src/client/index.ts b/src/client/index.ts index dd07988..7a7232e 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -19,16 +19,26 @@ export type { ClientSchema } from './models/ClientSchema'; export type { ClientUpdateDetailsRequest } from './models/ClientUpdateDetailsRequest'; export type { ClientUpdateRequest } from './models/ClientUpdateRequest'; export type { ClientUpdateResponse } from './models/ClientUpdateResponse'; +export type { DealAddServiceRequest } from './models/DealAddServiceRequest'; +export type { DealAddServiceResponse } from './models/DealAddServiceResponse'; export type { DealAddServicesRequest } from './models/DealAddServicesRequest'; export type { DealAddServicesResponse } from './models/DealAddServicesResponse'; export type { DealChangeStatusRequest } from './models/DealChangeStatusRequest'; export type { DealChangeStatusResponse } from './models/DealChangeStatusResponse'; export type { DealCreateRequest } from './models/DealCreateRequest'; +export type { DealDeleteServiceRequest } from './models/DealDeleteServiceRequest'; +export type { DealDeleteServiceResponse } from './models/DealDeleteServiceResponse'; +export type { DealDeleteServicesRequest } from './models/DealDeleteServicesRequest'; +export type { DealDeleteServicesResponse } from './models/DealDeleteServicesResponse'; +export type { DealGetAllResponse } from './models/DealGetAllResponse'; export type { DealQuickCreateRequest } from './models/DealQuickCreateRequest'; export type { DealQuickCreateResponse } from './models/DealQuickCreateResponse'; +export type { DealSchema } from './models/DealSchema'; export type { DealServiceSchema } from './models/DealServiceSchema'; export type { DealSummary } from './models/DealSummary'; export type { DealSummaryResponse } from './models/DealSummaryResponse'; +export type { DealUpdateServiceQuantityRequest } from './models/DealUpdateServiceQuantityRequest'; +export type { DealUpdateServiceQuantityResponse } from './models/DealUpdateServiceQuantityResponse'; export type { HTTPValidationError } from './models/HTTPValidationError'; export type { PaginationInfoSchema } from './models/PaginationInfoSchema'; export type { ProductCreateRequest } from './models/ProductCreateRequest'; diff --git a/src/client/models/DealAddServiceRequest.ts b/src/client/models/DealAddServiceRequest.ts new file mode 100644 index 0000000..c580abb --- /dev/null +++ b/src/client/models/DealAddServiceRequest.ts @@ -0,0 +1,10 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type DealAddServiceRequest = { + dealId: number; + serviceId: number; + quantity: number; +}; + diff --git a/src/client/models/DealAddServiceResponse.ts b/src/client/models/DealAddServiceResponse.ts new file mode 100644 index 0000000..eba29d5 --- /dev/null +++ b/src/client/models/DealAddServiceResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type DealAddServiceResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/models/DealDeleteServiceRequest.ts b/src/client/models/DealDeleteServiceRequest.ts new file mode 100644 index 0000000..ba5ecad --- /dev/null +++ b/src/client/models/DealDeleteServiceRequest.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type DealDeleteServiceRequest = { + dealId: number; + serviceId: number; +}; + diff --git a/src/client/models/DealDeleteServiceResponse.ts b/src/client/models/DealDeleteServiceResponse.ts new file mode 100644 index 0000000..acd1596 --- /dev/null +++ b/src/client/models/DealDeleteServiceResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type DealDeleteServiceResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/models/DealDeleteServicesRequest.ts b/src/client/models/DealDeleteServicesRequest.ts new file mode 100644 index 0000000..f34a1e9 --- /dev/null +++ b/src/client/models/DealDeleteServicesRequest.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type DealDeleteServicesRequest = { + dealId: number; + serviceIds: Array; +}; + diff --git a/src/client/models/DealDeleteServicesResponse.ts b/src/client/models/DealDeleteServicesResponse.ts new file mode 100644 index 0000000..c9064ba --- /dev/null +++ b/src/client/models/DealDeleteServicesResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type DealDeleteServicesResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/models/DealGetAllResponse.ts b/src/client/models/DealGetAllResponse.ts new file mode 100644 index 0000000..c948c30 --- /dev/null +++ b/src/client/models/DealGetAllResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { DealSchema } from './DealSchema'; +export type DealGetAllResponse = { + deals: Array; +}; + diff --git a/src/client/models/DealSchema.ts b/src/client/models/DealSchema.ts new file mode 100644 index 0000000..677e9d6 --- /dev/null +++ b/src/client/models/DealSchema.ts @@ -0,0 +1,14 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { DealServiceSchema } from './DealServiceSchema'; +export type DealSchema = { + id: number; + name: string; + clientId: number; + createdAt: string; + currentStatus: number; + services: Array; +}; + diff --git a/src/client/models/DealServiceSchema.ts b/src/client/models/DealServiceSchema.ts index e3b354e..de738f9 100644 --- a/src/client/models/DealServiceSchema.ts +++ b/src/client/models/DealServiceSchema.ts @@ -2,8 +2,9 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { ServiceSchema } from './ServiceSchema'; export type DealServiceSchema = { - id: number; + service: ServiceSchema; quantity: number; }; diff --git a/src/client/models/DealUpdateServiceQuantityRequest.ts b/src/client/models/DealUpdateServiceQuantityRequest.ts new file mode 100644 index 0000000..22c0b9f --- /dev/null +++ b/src/client/models/DealUpdateServiceQuantityRequest.ts @@ -0,0 +1,10 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type DealUpdateServiceQuantityRequest = { + dealId: number; + serviceId: number; + quantity: number; +}; + diff --git a/src/client/models/DealUpdateServiceQuantityResponse.ts b/src/client/models/DealUpdateServiceQuantityResponse.ts new file mode 100644 index 0000000..10ab921 --- /dev/null +++ b/src/client/models/DealUpdateServiceQuantityResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type DealUpdateServiceQuantityResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/services/DealService.ts b/src/client/services/DealService.ts index 3ad156c..7f9e8df 100644 --- a/src/client/services/DealService.ts +++ b/src/client/services/DealService.ts @@ -2,14 +2,24 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { DealAddServiceRequest } from '../models/DealAddServiceRequest'; +import type { DealAddServiceResponse } from '../models/DealAddServiceResponse'; import type { DealAddServicesRequest } from '../models/DealAddServicesRequest'; import type { DealAddServicesResponse } from '../models/DealAddServicesResponse'; import type { DealChangeStatusRequest } from '../models/DealChangeStatusRequest'; import type { DealChangeStatusResponse } from '../models/DealChangeStatusResponse'; import type { DealCreateRequest } from '../models/DealCreateRequest'; +import type { DealDeleteServiceRequest } from '../models/DealDeleteServiceRequest'; +import type { DealDeleteServiceResponse } from '../models/DealDeleteServiceResponse'; +import type { DealDeleteServicesRequest } from '../models/DealDeleteServicesRequest'; +import type { DealDeleteServicesResponse } from '../models/DealDeleteServicesResponse'; +import type { DealGetAllResponse } from '../models/DealGetAllResponse'; import type { DealQuickCreateRequest } from '../models/DealQuickCreateRequest'; import type { DealQuickCreateResponse } from '../models/DealQuickCreateResponse'; +import type { DealSchema } from '../models/DealSchema'; import type { DealSummaryResponse } from '../models/DealSummaryResponse'; +import type { DealUpdateServiceQuantityRequest } from '../models/DealUpdateServiceQuantityRequest'; +import type { DealUpdateServiceQuantityResponse } from '../models/DealUpdateServiceQuantityResponse'; import type { CancelablePromise } from '../core/CancelablePromise'; import { OpenAPI } from '../core/OpenAPI'; import { request as __request } from '../core/request'; @@ -90,11 +100,31 @@ export class DealService { * @returns DealAddServicesResponse Successful Response * @throws ApiError */ - public static servicesAddDealServicesAddPost({ + public static addMultipleDealServices({ requestBody, }: { requestBody: DealAddServicesRequest, }): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/deal/services/add/multiple', + body: requestBody, + mediaType: 'application/json', + errors: { + 422: `Validation Error`, + }, + }); + } + /** + * Services Add + * @returns DealAddServiceResponse Successful Response + * @throws ApiError + */ + public static addDealService({ + requestBody, + }: { + requestBody: DealAddServiceRequest, + }): CancelablePromise { return __request(OpenAPI, { method: 'POST', url: '/deal/services/add', @@ -105,4 +135,96 @@ export class DealService { }, }); } + /** + * Services Update + * @returns DealUpdateServiceQuantityResponse Successful Response + * @throws ApiError + */ + public static updateDealServiceQuantity({ + requestBody, + }: { + requestBody: DealUpdateServiceQuantityRequest, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/deal/services/update-quantity', + body: requestBody, + mediaType: 'application/json', + errors: { + 422: `Validation Error`, + }, + }); + } + /** + * Services Delete + * @returns DealDeleteServiceResponse Successful Response + * @throws ApiError + */ + public static deleteDealService({ + requestBody, + }: { + requestBody: DealDeleteServiceRequest, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/deal/services/delete', + body: requestBody, + mediaType: 'application/json', + errors: { + 422: `Validation Error`, + }, + }); + } + /** + * Services Delete + * @returns DealDeleteServicesResponse Successful Response + * @throws ApiError + */ + public static deleteMultipleDealServices({ + requestBody, + }: { + requestBody: DealDeleteServicesRequest, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/deal/services/delete/multiple', + body: requestBody, + mediaType: 'application/json', + errors: { + 422: `Validation Error`, + }, + }); + } + /** + * Get All + * @returns DealGetAllResponse Successful Response + * @throws ApiError + */ + public static getAllDeals(): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/deal/get-all', + }); + } + /** + * Get Deal By Id + * @returns DealSchema Successful Response + * @throws ApiError + */ + public static getDealById({ + dealId, + }: { + dealId: number, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/deal/get/{deal_id}', + path: { + 'deal_id': dealId, + }, + errors: { + 422: `Validation Error`, + }, + }); + } } diff --git a/src/components/BaseTable/BaseTable.tsx b/src/components/BaseTable/BaseTable.tsx index 0baeb13..d8b8ce4 100644 --- a/src/components/BaseTable/BaseTable.tsx +++ b/src/components/BaseTable/BaseTable.tsx @@ -7,10 +7,11 @@ import { useMantineReactTable } from "mantine-react-table"; import {MRT_Localization_RU} from "mantine-react-table/locales/ru/index.cjs"; -import {forwardRef, useImperativeHandle} from 'react'; +import {forwardRef, useEffect, useImperativeHandle} from 'react'; type Props, K extends keyof T> = { data: T[], + onSelectionChange?: (selectedRows: T[]) => void, columns: MRT_ColumnDef[], restProps?: MRT_TableOptions, striped?: boolean @@ -23,7 +24,7 @@ export type BaseTableRef = { }; export const BaseTable = forwardRef, Props>((props, ref) => { - const {data, columns, restProps, striped} = props; + const {data, columns, restProps, striped, onSelectionChange} = props; const table = useMantineReactTable({ localization: MRT_Localization_RU, @@ -34,8 +35,14 @@ export const BaseTable = forwardRef, Props>((props, ref) striped: striped }, enableTopToolbar: false, + enableBottomToolbar: false, + enableRowSelection: onSelectionChange !== undefined, ...restProps, }); + useEffect(() => { + if (!onSelectionChange) return; + onSelectionChange(table.getSelectedRowModel().rows.map(row => row.original)) + }, [table.getState().rowSelection]); // Используем useImperativeHandle для определения, что будет доступно через ref useImperativeHandle(ref, () => ({ diff --git a/src/components/Dnd/DealSummaryCard/DealSummaryCard.tsx b/src/components/Dnd/DealSummaryCard/DealSummaryCard.tsx index 8dba636..3890482 100644 --- a/src/components/Dnd/DealSummaryCard/DealSummaryCard.tsx +++ b/src/components/Dnd/DealSummaryCard/DealSummaryCard.tsx @@ -1,17 +1,25 @@ import {FC} from "react"; -import {DealSummary} from "../../../client"; +import {DealService, DealSummary} from "../../../client"; import styles from './DealSummaryCard.module.css'; import {Text} from '@mantine/core'; import classNames from "classnames"; +import {useDealPageContext} from "../../../pages/LeadsPage/contexts/DealPageContext.tsx"; type Props = { dealSummary: DealSummary } const DealSummaryCard: FC = ({dealSummary}) => { + const {setSelectedDeal} = useDealPageContext(); + const onDealSummaryClick = () => { + DealService.getDealById({dealId: dealSummary.id}) + .then((deal) => { + setSelectedDeal(deal); + }) + } return ( -
+
onDealSummaryClick()} className={styles['container']}>
diff --git a/src/components/PlusMinusInput/PlusMinusInput.module.css b/src/components/PlusMinusInput/PlusMinusInput.module.css new file mode 100644 index 0000000..d7f1c60 --- /dev/null +++ b/src/components/PlusMinusInput/PlusMinusInput.module.css @@ -0,0 +1,3 @@ +.number-input { + width: rem(50); +} \ No newline at end of file diff --git a/src/components/PlusMinusInput/PlusMinusInput.tsx b/src/components/PlusMinusInput/PlusMinusInput.tsx new file mode 100644 index 0000000..c0c76fd --- /dev/null +++ b/src/components/PlusMinusInput/PlusMinusInput.tsx @@ -0,0 +1,97 @@ +import {ActionIcon, Flex, NumberInput, rem} from "@mantine/core"; +import {IconMinus, IconPlus} from "@tabler/icons-react"; +import styles from './PlusMinusInput.module.css'; +import {FC, useEffect, useState} from "react"; + +type ControlledValueProps = { + value: number; + onChange: (value: number) => void; +} + +type RestProps = { + defaultValue?: number; + onChange: (value: number) => void; +} + +type Props = RestProps & Partial; + +const PlusMinusInput: FC = (props: Props) => { + const isControlled = props.value !== undefined; + const [internalValue, setInternalValue] = useState(props.defaultValue || 0); + + const value = isControlled ? props.value : internalValue; + + const onMinusClick = () => { + + const newValue = (value || 0) - 1; + if (newValue < 0) { + return; + } + if (isControlled) { + props.onChange(newValue); + } else { + setInternalValue(newValue); + } + } + + const onPlusClick = () => { + const newValue = (value || 0) + 1; + if (isControlled) { + props.onChange(newValue); + } else { + setInternalValue(newValue); + } + } + + const handleInputChange = (event: number | string) => { + let newValue = typeof event === "string" ? 0 : event; + if (isNaN(newValue) || newValue < 0) { + newValue = 0; + } + if (isControlled) { + props.onChange(newValue); + } else { + setInternalValue(newValue); + } + } + + useEffect(() => { + if (!isControlled) { + props.onChange(internalValue); + } + }, [internalValue]); + + return ( + + + + + handleInputChange(event)} + /> + + + + + ) +} + +export default PlusMinusInput; \ No newline at end of file diff --git a/src/components/ServiceSelect/ServiceSelect.tsx b/src/components/ServiceSelect/ServiceSelect.tsx new file mode 100644 index 0000000..de72565 --- /dev/null +++ b/src/components/ServiceSelect/ServiceSelect.tsx @@ -0,0 +1,63 @@ +import {ServiceSchema} from "../../client"; +import {FC, useEffect, useMemo, useState} from "react"; +import {Select, SelectProps} from "@mantine/core"; +import useServicesList from "../../pages/ServicesPage/hooks/useServicesList.tsx"; + +type ControlledValueProps = { + value: ServiceSchema; + onChange: (value: ServiceSchema) => void; +} +type RestProps = { + defaultValue?: ServiceSchema; + onChange: (value: ServiceSchema) => void; +} +type Props = (RestProps & Partial) & Omit; + + +const ServiceSelect: FC = (props) => { + const isControlled = props.value !== undefined; + const [internalValue, setInternalValue] = useState(props.defaultValue); + const value = isControlled ? props.value : internalValue; + const {services} = useServicesList(); + const categories = useMemo(() => services.reduce((acc, service) => { + if (!acc.includes(service.category.name)) { + acc.push(service.category.name); + } + return acc; + }, [] as string[]), [services]); + + 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) => { + if (isControlled) { + props.onChange(services.find(service => service.id.toString() === value) as ServiceSchema); + return; + } + setInternalValue(services.find(service => service.id.toString() === value) as ServiceSchema); + } + useEffect(() => { + if (!isControlled) { + props.onChange(internalValue as ServiceSchema); + } + }, [internalValue]); + return ( +