feat: total services amount and recalculating
This commit is contained in:
@@ -98,6 +98,8 @@ export type { DealProductSchema } from './models/DealProductSchema';
|
||||
export type { DealProductServiceSchema } from './models/DealProductServiceSchema';
|
||||
export type { DealQuickCreateRequest } from './models/DealQuickCreateRequest';
|
||||
export type { DealQuickCreateResponse } from './models/DealQuickCreateResponse';
|
||||
export type { DealRecalculatePriceRequest } from './models/DealRecalculatePriceRequest';
|
||||
export type { DealRecalculatePriceResponse } from './models/DealRecalculatePriceResponse';
|
||||
export type { DealSchema } from './models/DealSchema';
|
||||
export type { DealServiceSchema } from './models/DealServiceSchema';
|
||||
export type { DealServicesCopyRequest } from './models/DealServicesCopyRequest';
|
||||
|
||||
@@ -8,5 +8,6 @@ export type DealProductServiceSchema = {
|
||||
service: ServiceSchema;
|
||||
price: number;
|
||||
employees: Array<UserSchema>;
|
||||
isFixedPrice: boolean;
|
||||
};
|
||||
|
||||
|
||||
8
src/client/models/DealRecalculatePriceRequest.ts
Normal file
8
src/client/models/DealRecalculatePriceRequest.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type DealRecalculatePriceRequest = {
|
||||
dealId: number;
|
||||
};
|
||||
|
||||
9
src/client/models/DealRecalculatePriceResponse.ts
Normal file
9
src/client/models/DealRecalculatePriceResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type DealRecalculatePriceResponse = {
|
||||
ok: boolean;
|
||||
message: string;
|
||||
};
|
||||
|
||||
@@ -9,5 +9,6 @@ export type DealServiceSchema = {
|
||||
quantity: number;
|
||||
price: number;
|
||||
employees: Array<UserSchema>;
|
||||
isFixedPrice: boolean;
|
||||
};
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ import type { DealProductAddKitRequest } from '../models/DealProductAddKitReques
|
||||
import type { DealProductAddKitResponse } from '../models/DealProductAddKitResponse';
|
||||
import type { DealQuickCreateRequest } from '../models/DealQuickCreateRequest';
|
||||
import type { DealQuickCreateResponse } from '../models/DealQuickCreateResponse';
|
||||
import type { DealRecalculatePriceRequest } from '../models/DealRecalculatePriceRequest';
|
||||
import type { DealRecalculatePriceResponse } from '../models/DealRecalculatePriceResponse';
|
||||
import type { DealSchema } from '../models/DealSchema';
|
||||
import type { DealServicesCopyRequest } from '../models/DealServicesCopyRequest';
|
||||
import type { DealServicesCopyResponse } from '../models/DealServicesCopyResponse';
|
||||
@@ -350,6 +352,26 @@ export class DealService {
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Recalculate Deal Price
|
||||
* @returns DealRecalculatePriceResponse Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static recalculateDealPrice({
|
||||
requestBody,
|
||||
}: {
|
||||
requestBody: DealRecalculatePriceRequest,
|
||||
}): CancelablePromise<DealRecalculatePriceResponse> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/deal/recalculate-price',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Services Add
|
||||
* @returns DealAddServicesResponse Successful Response
|
||||
|
||||
34
src/components/LockCheckbox/LockCheckbox.tsx
Normal file
34
src/components/LockCheckbox/LockCheckbox.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { ActionIcon, ActionIconVariant, CheckboxProps, Tooltip } from "@mantine/core";
|
||||
import { FC } from "react";
|
||||
import { IconLock, IconLockOpen } from "@tabler/icons-react";
|
||||
|
||||
type RestProps = {
|
||||
value?: boolean;
|
||||
onChange?: (value: boolean) => void;
|
||||
variant: ActionIconVariant
|
||||
}
|
||||
type Props = Omit<CheckboxProps, "onChange" | "value" | "variant"> & RestProps;
|
||||
|
||||
const LockCheckbox: FC<Props> = (props) => {
|
||||
const { value = false } = props;
|
||||
const getIcon = () => {
|
||||
return value ? <IconLock /> : <IconLockOpen />;
|
||||
};
|
||||
|
||||
const handleChange = () => {
|
||||
if (props.onChange) {
|
||||
props.onChange(!value);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Tooltip label={props.label}>
|
||||
<ActionIcon
|
||||
onClick={handleChange}
|
||||
variant={props.variant}>
|
||||
{getIcon()}
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
export default LockCheckbox;
|
||||
@@ -10,7 +10,7 @@ import { ContextModalProps } from "@mantine/modals";
|
||||
import { useForm, UseFormReturnType } from "@mantine/form";
|
||||
import { isNil, isNumber } from "lodash";
|
||||
import ServiceWithPriceInput from "../../../components/ServiceWithPriceInput/ServiceWithPriceInput.tsx";
|
||||
import { Flex } from "@mantine/core";
|
||||
import { Checkbox, Flex, rem } from "@mantine/core";
|
||||
import { ServiceType } from "../../../shared/enums/ServiceType.ts";
|
||||
import { useSelector } from "react-redux";
|
||||
import { RootState } from "../../../redux/store.ts";
|
||||
@@ -26,7 +26,7 @@ const ProductServiceFormModal = ({
|
||||
context,
|
||||
id,
|
||||
innerProps,
|
||||
}: ContextModalProps<Props>) => {
|
||||
}: ContextModalProps<Props>) => {
|
||||
const authState = useSelector((state: RootState) => state.auth);
|
||||
|
||||
const isEditing = "onChange" in innerProps;
|
||||
@@ -36,6 +36,7 @@ const ProductServiceFormModal = ({
|
||||
service: undefined,
|
||||
price: undefined,
|
||||
employees: [],
|
||||
isFixedPrice: false,
|
||||
};
|
||||
const form = useForm<Partial<DealProductServiceSchema>>({
|
||||
initialValues,
|
||||
@@ -57,7 +58,7 @@ const ProductServiceFormModal = ({
|
||||
closeOnSubmit>
|
||||
<BaseFormModal.Body>
|
||||
<>
|
||||
<Flex w={"100%"}>
|
||||
<Flex w={"100%"} direction={"column"} gap={rem(10)}>
|
||||
<ServiceWithPriceInput
|
||||
category={innerProps.category}
|
||||
serviceProps={{
|
||||
@@ -85,6 +86,11 @@ const ProductServiceFormModal = ({
|
||||
lockOnEdit={isEditing}
|
||||
quantity={innerProps.quantity}
|
||||
/>
|
||||
<Checkbox
|
||||
{...form.getInputProps("isFixedPrice", { type: "checkbox" })}
|
||||
label={"Зафиксировать цену"}
|
||||
placeholder={"Зафиксировать цену"}
|
||||
/>
|
||||
</Flex>
|
||||
</>
|
||||
</BaseFormModal.Body>
|
||||
|
||||
@@ -1,21 +1,7 @@
|
||||
import { CRUDTableProps } from "../../../../../../types/CRUDTable.tsx";
|
||||
import {
|
||||
DealServiceSchema,
|
||||
GetServiceKitSchema,
|
||||
UserSchema,
|
||||
} from "../../../../../../client";
|
||||
import { DealServiceSchema, GetServiceKitSchema, UserSchema } from "../../../../../../client";
|
||||
import { FC, useState } from "react";
|
||||
import {
|
||||
ActionIcon,
|
||||
Button,
|
||||
Flex,
|
||||
Modal,
|
||||
NumberInput,
|
||||
rem,
|
||||
Text,
|
||||
Title,
|
||||
Tooltip,
|
||||
} from "@mantine/core";
|
||||
import { ActionIcon, Button, Flex, Modal, NumberInput, rem, Text, Title, Tooltip } from "@mantine/core";
|
||||
import { IconTrash, IconUsersGroup } from "@tabler/icons-react";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { isNumber } from "lodash";
|
||||
@@ -24,6 +10,7 @@ import { ServiceType } from "../../../../../../shared/enums/ServiceType.ts";
|
||||
import { useSelector } from "react-redux";
|
||||
import { RootState } from "../../../../../../redux/store.ts";
|
||||
import useDealProductAndServiceTabState from "../../hooks/useProductAndServiceTabState.tsx";
|
||||
import LockCheckbox from "../../../../../../components/LockCheckbox/LockCheckbox.tsx";
|
||||
|
||||
type RestProps = {
|
||||
onKitAdd?: (kit: GetServiceKitSchema) => void;
|
||||
@@ -35,7 +22,7 @@ const DealServicesTable: FC<Props> = ({
|
||||
onCreate,
|
||||
onChange,
|
||||
onKitAdd,
|
||||
}) => {
|
||||
}) => {
|
||||
const authState = useSelector((state: RootState) => state.auth);
|
||||
|
||||
const { dealState } = useDealProductAndServiceTabState();
|
||||
@@ -78,7 +65,13 @@ const DealServicesTable: FC<Props> = ({
|
||||
price,
|
||||
});
|
||||
};
|
||||
|
||||
const onLockChange = (item: DealServiceSchema, isLocked: boolean) => {
|
||||
if (!onChange) return;
|
||||
onChange({
|
||||
...item,
|
||||
isFixedPrice: isLocked,
|
||||
});
|
||||
};
|
||||
const onEmployeeClick = (item: DealServiceSchema) => {
|
||||
if (!onChange) return;
|
||||
setCurrentService(item);
|
||||
@@ -91,7 +84,7 @@ const DealServicesTable: FC<Props> = ({
|
||||
const getCurrentEmployees = (): UserSchema[] => {
|
||||
if (!currentService) return [];
|
||||
const item = items.find(
|
||||
i => i.service.id === currentService.service.id
|
||||
i => i.service.id === currentService.service.id,
|
||||
);
|
||||
if (!item) return [];
|
||||
return item.employees;
|
||||
@@ -169,7 +162,9 @@ const DealServicesTable: FC<Props> = ({
|
||||
isNumber(event) &&
|
||||
onQuantityChange(service, event)
|
||||
}
|
||||
|
||||
value={service.quantity}
|
||||
|
||||
/>
|
||||
<NumberInput
|
||||
flex={1}
|
||||
@@ -179,7 +174,22 @@ const DealServicesTable: FC<Props> = ({
|
||||
}
|
||||
suffix={"₽"}
|
||||
value={service.price}
|
||||
disabled={authState.isGuest || isLocked}
|
||||
disabled={authState.isGuest || isLocked || service.isFixedPrice}
|
||||
rightSectionProps={{
|
||||
style: {
|
||||
display: "flex",
|
||||
cursor: "pointer",
|
||||
pointerEvents: "auto",
|
||||
},
|
||||
}}
|
||||
rightSection={
|
||||
<LockCheckbox
|
||||
label={"Зафиксировать цену"}
|
||||
variant={"default"}
|
||||
value={service.isFixedPrice}
|
||||
onChange={value => onLockChange(service, value)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Flex>
|
||||
))}
|
||||
@@ -192,7 +202,7 @@ const DealServicesTable: FC<Props> = ({
|
||||
Итог:{" "}
|
||||
{items.reduce(
|
||||
(acc, item) => acc + item.price * item.quantity,
|
||||
0
|
||||
0,
|
||||
)}
|
||||
₽
|
||||
</Title>
|
||||
|
||||
@@ -8,13 +8,24 @@ import { useDealPageContext } from "../../../contexts/DealPageContext.tsx";
|
||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||
|
||||
const useDealState = () => {
|
||||
|
||||
const { selectedDeal, setSelectedDeal } = useDealPageContext();
|
||||
const recalculate = async () => {
|
||||
|
||||
return DealService.recalculateDealPrice({
|
||||
requestBody: {
|
||||
dealId: selectedDeal?.id || -1,
|
||||
},
|
||||
});
|
||||
};
|
||||
const refetch = async () => {
|
||||
if (!selectedDeal) return;
|
||||
const { ok, message } = await recalculate();
|
||||
if (!ok) notifications.guess(ok, { message });
|
||||
return DealService.getDealById({ dealId: selectedDeal.id }).then(
|
||||
deal => {
|
||||
setSelectedDeal(deal);
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
return {
|
||||
@@ -25,6 +36,9 @@ const useDealState = () => {
|
||||
|
||||
const useDealServicesState = (): CRUDTableProps<DealServiceSchema> => {
|
||||
const { deal, refetch } = useDealState();
|
||||
const refetchAndRecalculate = async () => {
|
||||
await refetch();
|
||||
};
|
||||
const onCreate = (item: DealServiceSchema) => {
|
||||
if (!deal) return;
|
||||
DealService.addDealService({
|
||||
@@ -36,7 +50,7 @@ const useDealServicesState = (): CRUDTableProps<DealServiceSchema> => {
|
||||
},
|
||||
}).then(async ({ ok, message }) => {
|
||||
if (!ok) notifications.guess(ok, { message });
|
||||
if (ok) await refetch();
|
||||
if (ok) await refetchAndRecalculate();
|
||||
});
|
||||
};
|
||||
const onDelete = (item: DealServiceSchema) => {
|
||||
@@ -48,7 +62,7 @@ const useDealServicesState = (): CRUDTableProps<DealServiceSchema> => {
|
||||
},
|
||||
}).then(async ({ ok, message }) => {
|
||||
if (!ok) notifications.guess(ok, { message });
|
||||
if (ok) await refetch();
|
||||
if (ok) await refetchAndRecalculate();
|
||||
});
|
||||
};
|
||||
const onChange = (item: DealServiceSchema) => {
|
||||
@@ -60,7 +74,7 @@ const useDealServicesState = (): CRUDTableProps<DealServiceSchema> => {
|
||||
},
|
||||
}).then(async ({ ok, message }) => {
|
||||
if (!ok) notifications.guess(ok, { message });
|
||||
if (ok) await refetch();
|
||||
if (ok) await refetchAndRecalculate();
|
||||
});
|
||||
};
|
||||
return {
|
||||
@@ -73,6 +87,9 @@ const useDealServicesState = (): CRUDTableProps<DealServiceSchema> => {
|
||||
|
||||
const useDealProductsState = (): CRUDTableProps<DealProductSchema> => {
|
||||
const { deal, refetch } = useDealState();
|
||||
const refetchAndRecalculate = async () => {
|
||||
await refetch();
|
||||
};
|
||||
const onCreate = (item: DealProductSchema) => {
|
||||
if (!deal) return;
|
||||
DealService.addDealProduct({
|
||||
@@ -82,7 +99,7 @@ const useDealProductsState = (): CRUDTableProps<DealProductSchema> => {
|
||||
},
|
||||
}).then(async ({ ok, message }) => {
|
||||
if (!ok) notifications.guess(ok, { message });
|
||||
if (ok) await refetch();
|
||||
if (ok) await refetchAndRecalculate();
|
||||
});
|
||||
};
|
||||
const onDelete = (item: DealProductSchema) => {
|
||||
@@ -94,7 +111,7 @@ const useDealProductsState = (): CRUDTableProps<DealProductSchema> => {
|
||||
},
|
||||
}).then(async ({ ok, message }) => {
|
||||
if (!ok) notifications.guess(ok, { message });
|
||||
if (ok) await refetch();
|
||||
if (ok) await refetchAndRecalculate();
|
||||
});
|
||||
};
|
||||
const onChange = (item: DealProductSchema) => {
|
||||
@@ -106,7 +123,7 @@ const useDealProductsState = (): CRUDTableProps<DealProductSchema> => {
|
||||
},
|
||||
}).then(async ({ ok, message }) => {
|
||||
if (!ok) notifications.guess(ok, { message });
|
||||
if (ok) await refetch();
|
||||
if (ok) await refetchAndRecalculate();
|
||||
});
|
||||
};
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user