feat: total services amount and recalculating

This commit is contained in:
2024-10-22 19:31:51 +03:00
parent aa6f0364b5
commit bb34f12274
10 changed files with 153 additions and 43 deletions

View File

@@ -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';

View File

@@ -8,5 +8,6 @@ export type DealProductServiceSchema = {
service: ServiceSchema;
price: number;
employees: Array<UserSchema>;
isFixedPrice: boolean;
};

View 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;
};

View 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;
};

View File

@@ -9,5 +9,6 @@ export type DealServiceSchema = {
quantity: number;
price: number;
employees: Array<UserSchema>;
isFixedPrice: boolean;
};

View File

@@ -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

View 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;

View File

@@ -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";
@@ -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>

View File

@@ -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;
@@ -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>

View File

@@ -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 {