feat: deal prefilling

This commit is contained in:
2024-10-15 21:34:39 +04:00
parent f1566cdf44
commit 2bd4fddfa7
22 changed files with 736 additions and 7 deletions

View File

@@ -25,6 +25,7 @@ export type { BaseEnumListSchema } from './models/BaseEnumListSchema';
export type { BaseEnumSchema } from './models/BaseEnumSchema';
export type { BaseMarketplaceSchema } from './models/BaseMarketplaceSchema';
export type { BaseShippingWarehouseSchema } from './models/BaseShippingWarehouseSchema';
export type { BillPaymentInfo } from './models/BillPaymentInfo';
export type { BillPaymentStatus } from './models/BillPaymentStatus';
export type { BillStatusUpdateRequest } from './models/BillStatusUpdateRequest';
export type { Body_upload_product_image } from './models/Body_upload_product_image';
@@ -90,6 +91,8 @@ export type { DealDeleteServicesRequest } from './models/DealDeleteServicesReque
export type { DealDeleteServicesResponse } from './models/DealDeleteServicesResponse';
export type { DealGeneralInfoSchema } from './models/DealGeneralInfoSchema';
export type { DealGetAllResponse } from './models/DealGetAllResponse';
export type { DealPrefillRequest } from './models/DealPrefillRequest';
export type { DealPrefillResponse } from './models/DealPrefillResponse';
export type { DealProductAddKitRequest } from './models/DealProductAddKitRequest';
export type { DealProductAddKitResponse } from './models/DealProductAddKitResponse';
export type { DealProductSchema } from './models/DealProductSchema';

View File

@@ -2,11 +2,12 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { BillPaymentInfo } from './BillPaymentInfo';
import type { BillPaymentStatus } from './BillPaymentStatus';
import type { NotificationChannel } from './NotificationChannel';
export type BillStatusUpdateRequest = {
listenerTransactionId: number;
channel: NotificationChannel;
info: BillPaymentStatus;
info: (BillPaymentInfo | BillPaymentStatus);
};

View File

@@ -0,0 +1,9 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type DealPrefillRequest = {
oldDealId: number;
newDealId: number;
};

View File

@@ -28,6 +28,8 @@ import type { DealDeleteServiceResponse } from '../models/DealDeleteServiceRespo
import type { DealDeleteServicesRequest } from '../models/DealDeleteServicesRequest';
import type { DealDeleteServicesResponse } from '../models/DealDeleteServicesResponse';
import type { DealGetAllResponse } from '../models/DealGetAllResponse';
import type { DealPrefillRequest } from '../models/DealPrefillRequest';
import type { DealPrefillResponse } from '../models/DealPrefillResponse';
import type { DealProductAddKitRequest } from '../models/DealProductAddKitRequest';
import type { DealProductAddKitResponse } from '../models/DealProductAddKitResponse';
import type { DealQuickCreateRequest } from '../models/DealQuickCreateRequest';
@@ -328,6 +330,26 @@ export class DealService {
},
});
}
/**
* Post Prefill Deal
* @returns DealPrefillResponse Successful Response
* @throws ApiError
*/
public static prefillDeal({
requestBody,
}: {
requestBody: DealPrefillRequest,
}): CancelablePromise<DealPrefillResponse> {
return __request(OpenAPI, {
method: 'POST',
url: '/deal/prefill',
body: requestBody,
mediaType: 'application/json',
errors: {
422: `Validation Error`,
},
});
}
/**
* Services Add
* @returns DealAddServicesResponse Successful Response

View File

@@ -6,6 +6,7 @@ import CreateDealFrom from "../CreateDealForm/CreateDealFrom.tsx";
import { DealService } from "../../../client";
import { useQueryClient } from "@tanstack/react-query";
import { dateWithoutTimezone } from "../../../shared/lib/date.ts";
import { useDealPageContext } from "../../../pages/LeadsPage/contexts/DealPageContext.tsx";
type Props = {
onClick: () => void;
@@ -14,6 +15,7 @@ const CreateDealButton: FC<Props> = () => {
const [isCreating, setIsCreating] = useState(false);
const [isTransitionEnded, setIsTransitionEnded] = useState(true);
const queryClient = useQueryClient();
const { prefillDeal, setPrefillDeal } = useDealPageContext();
return (
<div
@@ -34,6 +36,7 @@ const CreateDealButton: FC<Props> = () => {
<div style={styles}>
<CreateDealFrom
onCancel={() => {
setPrefillDeal(undefined);
setIsCreating(false);
}}
onSubmit={quickDeal => {
@@ -41,10 +44,18 @@ const CreateDealButton: FC<Props> = () => {
requestBody: {
...quickDeal,
acceptanceDate: dateWithoutTimezone(
quickDeal.acceptanceDate
quickDeal.acceptanceDate,
),
},
}).then(async () => {
}).then(async (result) => {
if (prefillDeal) {
DealService.prefillDeal({
requestBody: {
oldDealId: prefillDeal.id,
newDealId: result.dealId,
},
});
}
await queryClient.invalidateQueries({
queryKey: ["getDealSummaries"],
});

View File

@@ -12,3 +12,8 @@
display: flex;
gap: rem(10);
}
.button-prefill {
display: grid;
width: 100%;
}

View File

@@ -8,12 +8,14 @@ import { DateTimePicker } from "@mantine/dates";
import ShippingWarehouseAutocomplete from "../../Selects/ShippingWarehouseAutocomplete/ShippingWarehouseAutocomplete.tsx";
import BaseMarketplaceSelect from "../../Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
import ServicePriceCategorySelect from "../../Selects/ServicePriceCategorySelect/ServicePriceCategorySelect.tsx";
import { useDealPageContext } from "../../../pages/LeadsPage/contexts/DealPageContext.tsx";
type Props = {
onSubmit: (quickDeal: QuickDeal) => void;
onCancel: () => void;
};
const CreateDealFrom: FC<Props> = ({ onSubmit, onCancel }) => {
const { prefillOnOpen, prefillDeal } = useDealPageContext();
const form = useForm<QuickDeal>({
initialValues: {
name: "",
@@ -29,6 +31,9 @@ const CreateDealFrom: FC<Props> = ({ onSubmit, onCancel }) => {
},
},
});
const prefillButtonLabel = prefillDeal ? `Предзаполнено [ID: ${prefillDeal.id}]` : "Предзаполнить";
return (
<form
style={{ width: "100%" }}
@@ -86,7 +91,14 @@ const CreateDealFrom: FC<Props> = ({ onSubmit, onCancel }) => {
{...form.getInputProps("acceptanceDate")}
/>
</div>
<div className={styles["button-prefill"]}>
<Button
style={{ whiteSpace: "wrap" }}
variant={"outline"}
onClick={() => prefillOnOpen()}>
{prefillButtonLabel}
</Button>
</div>
<div className={styles["buttons"]}>
<Button type={"submit"}>Добавить</Button>
<Button

View File

@@ -1,9 +1,19 @@
import { createContext, FC, useContext, useState } from "react";
import { DealSchema } from "../../../client";
import { createContext, Dispatch, FC, SetStateAction, useContext, useState } from "react";
import { DealSchema, DealService } from "../../../client";
import { useDisclosure } from "@mantine/hooks";
type DealPageContextState = {
selectedDeal?: DealSchema;
setSelectedDeal: (deal: DealSchema | undefined) => void;
prefillOpened: boolean;
prefillOnClose: () => void;
prefillOnOpen: () => void;
selectedPrefillDeal?: DealSchema;
selectPrefillDeal: (dealId: number) => void;
prefillDeal?: DealSchema;
setPrefillDeal: Dispatch<SetStateAction<DealSchema | undefined>>;
};
const DealPageContext = createContext<DealPageContextState | undefined>(
@@ -13,7 +23,35 @@ const useDealPageContextState = () => {
const [selectedDeal, setSelectedDeal] = useState<DealSchema | undefined>(
undefined
);
return { selectedDeal, setSelectedDeal };
const [selectedPrefillDeal, setSelectedPrefillDeal] = useState<DealSchema | undefined>(
undefined,
);
const [prefillDeal, setPrefillDeal] = useState<DealSchema | undefined>(
undefined,
);
const [prefillOpened, { open, close }] = useDisclosure(false);
const prefillOnClose = close;
const prefillOnOpen = open;
const selectPrefillDeal = (dealId: number) => {
DealService.getDealById({ dealId }).then(deal => {
setSelectedPrefillDeal(deal);
});
}
return {
selectedDeal,
setSelectedDeal,
prefillOpened,
prefillOnClose,
prefillOnOpen,
selectedPrefillDeal,
selectPrefillDeal,
prefillDeal,
setPrefillDeal,
};
};
type DealPageContextProviderProps = {

View File

@@ -0,0 +1,33 @@
.container {
display: flex;
flex-direction: row;
gap: rem(10);
max-height: 95vh;
}
.deal-container {
display: flex;
flex-direction: column;
gap: rem(10);
flex: 1;
}
.deal-container-wrapper {
border: dashed var(--item-border-size) var(--mantine-color-default-border);
border-radius: var(--item-border-radius);
padding: rem(10);
}
.deal-container-buttons {
gap: rem(10);
display: flex;
flex-direction: column;
margin-top: auto;
width: 100%;
}
.top-panel {
padding-bottom: rem(9);
gap: rem(10);
display: flex;
}

View File

@@ -0,0 +1,82 @@
import { FC, useEffect } from "react";
import { Button, Drawer, Flex, rem, TextInput } from "@mantine/core";
import { useDealPageContext } from "../../contexts/DealPageContext.tsx";
import DealsTable from "./components/tables/DealsTable/DealsTable.tsx";
import Preview from "./components/Preview/Preview.tsx";
import styles from "./DealPrefillDrawer.module.css";
import BaseMarketplaceSelect from "../../../../components/Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
import usePrefillDeal from "./hooks/usePrefillDeal.tsx";
import { notifications } from "../../../../shared/lib/notifications.ts";
const DealPrefillDrawer: FC = () => {
const { prefillOpened, prefillOnClose, selectedPrefillDeal, setPrefillDeal, prefillDeal } = useDealPageContext();
const { data, form } = usePrefillDeal();
useEffect(() => {
if (prefillOpened) return;
}, [prefillOpened]);
return (
<Drawer
size={"calc(77vw)"}
position={"right"}
onClose={prefillOnClose}
removeScrollProps={{ allowPinchZoom: true }}
withCloseButton={false}
opened={prefillOpened}
styles={{
body: {
height: "100%",
display: "flex",
flexDirection: "row",
gap: rem(20),
},
}}
>
<div className={styles["deal-container"]}>
<div className={styles["deal-container-wrapper"]}>
<div className={styles["top-panel"]}>
<TextInput
placeholder={"Введите название / id"}
{...form.getInputProps("idOrName")}
/>
<BaseMarketplaceSelect
onClear={() =>
form.setFieldValue("marketplace", null)
}
clearable
placeholder={"Выберите маркетплейс"}
{...form.getInputProps("marketplace")}
/>
</div>
<DealsTable items={data} />
<Flex direction={"row"} gap="sm">
<Button mt={10} w={"100%"} onClick={() => {
if (!selectedPrefillDeal) {
notifications.error({ message: "Сделка не выбрана." });
return;
}
setPrefillDeal(selectedPrefillDeal);
prefillOnClose();
}}>
Предзаполнить
</Button>
{
prefillDeal &&
<Button mt={10} w={"100%"} variant={"outline"} onClick={() => {
setPrefillDeal(undefined);
notifications.success({ message: "Предзаполнение отменено." });
prefillOnClose();
}}>
Отменить предзаполнение
</Button>
}
</Flex>
</div>
</div>
<Preview />
</Drawer>
);
};
export default DealPrefillDrawer;

View File

@@ -0,0 +1,34 @@
.container {
display: flex;
gap: rem(10);
max-height: 95vh;
width: 50%;
}
.products-list {
display: flex;
flex-direction: column;
gap: rem(10);
flex: 2;
}
.deal-container {
display: flex;
flex-direction: column;
gap: rem(10);
flex: 1;
}
.deal-container-wrapper {
border: dashed var(--item-border-size) var(--mantine-color-default-border);
border-radius: var(--item-border-radius);
padding: rem(10);
}
.deal-container-buttons {
gap: rem(10);
display: flex;
flex-direction: column;
margin-top: auto;
width: 100%;
}

View File

@@ -0,0 +1,56 @@
import { FC } from "react";
import styles from "./Preview.module.css";
import { ScrollArea, Skeleton, Title } from "@mantine/core";
import { useDealPageContext } from "../../../../contexts/DealPageContext.tsx";
import DealServicesTable from "../tables/DealServicesTable/DealServicesTable.tsx";
import ProductPreview from "../ProductPreview/ProductPreview.tsx";
const Preview: FC = () => {
const { selectedPrefillDeal } = useDealPageContext();
const getTotalPrice = () => {
if (!selectedPrefillDeal) return 0;
const productServicesPrice = selectedPrefillDeal.products.reduce(
(acc, row) =>
acc +
row.services.reduce(
(acc2, row2) => acc2 + row2.price * row.quantity,
0,
),
0,
);
const dealServicesPrice = selectedPrefillDeal.services.reduce(
(acc, row) => acc + row.price * row.quantity,
0,
);
return dealServicesPrice + productServicesPrice;
};
return (
<div className={styles["container"]}>
<ScrollArea offsetScrollbars={"y"} w={"100%"}>
<Skeleton visible={!selectedPrefillDeal}>
<div className={styles["deal-container-wrapper"]}>
<Title order={4} mb={18}>
Общая стоимость всех услуг:{" "}
{getTotalPrice().toLocaleString("ru")}
</Title>
<DealServicesTable items={selectedPrefillDeal?.services} />
<div className={styles["products-list"]}>
{selectedPrefillDeal?.products.map(product => (
<ProductPreview
key={product.product.id}
product={product}
/>
))}
</div>
</div>
</Skeleton>
</ScrollArea>
</div>
);
};
export default Preview;

View File

@@ -0,0 +1,33 @@
.container {
display: flex;
gap: rem(20);
margin-bottom: rem(10);
flex: 1;
}
.image-container {
display: flex;
max-height: rem(250);
max-width: rem(250);
height: 100%;
}
.services-container {
width: 100%;
display: flex;
flex-direction: column;
gap: rem(10);
flex: 1;
}
.data-container {
max-width: rem(250);
display: flex;
flex-direction: column;
gap: rem(10);
flex: 1;
}
.attributes-container {
overflow-wrap: break-word;
}

View File

@@ -0,0 +1,55 @@
import { FC } from "react";
import { DealProductSchema, ProductSchema } from "../../../../../../client";
import { Image, rem, Text, Title } from "@mantine/core";
import { isNil } from "lodash";
import { ProductFieldNames } from "../../../../tabs/ProductAndServiceTab/components/ProductView/ProductView.tsx";
import ProductServicesTable from "../tables/ProductServicesTable/ProductServicesTable.tsx";
import styles from "./ProductPreview.module.css";
type Props = {
product: DealProductSchema;
};
const ProductPreview: FC<Props> = ({ product }) => {
return (
<div className={styles["container"]}>
<div className={styles["data-container"]}>
<div className={styles["image-container"]}>
<Image
flex={1}
radius={rem(10)}
fit={"cover"}
src={product.product.imageUrl}
/>
</div>
<div className={styles["attributes-container"]}>
<Title order={3}>{product.product.name}</Title>
{Object.entries(product.product).map(([key, value]) => {
const fieldName =
ProductFieldNames[key as keyof ProductSchema];
if (!fieldName || isNil(value) || value === "") return;
return (
<Text key={fieldName}>
{fieldName}: {value.toString()}{" "}
</Text>
);
})}
<Text>
Штрихкоды: {product.product.barcodes.join(", ")}
</Text>
<Text>Количество товара: {product.quantity}</Text>
</div>
</div>
<div className={styles["services-container"]}>
<ProductServicesTable
items={product.services}
quantity={product.quantity}
/>
</div>
</div>
);
};
export default ProductPreview;

View File

@@ -0,0 +1,59 @@
import { FC } from "react";
import { Flex, rem, Title } from "@mantine/core";
import { DealServiceSchema, DealSummary } from "../../../../../../../client";
import useDealServicesTableColumns from "./columns.tsx";
import { BaseTable } from "../../../../../../../components/BaseTable/BaseTable.tsx";
import { MRT_TableOptions } from "mantine-react-table";
type Props = {
items?: DealServiceSchema[];
};
const DealServicesTable: FC<Props> = ({ items }) => {
const columns = useDealServicesTableColumns();
return (
<Flex
direction={"column"}
gap={rem(10)}
h={"100%"}
mb={10}>
<Flex
h={"100%"}
direction={"column"}>
{
items && items.length > 0 &&
<>
<BaseTable
data={items}
columns={columns}
restProps={
{
enableSorting: false,
enableColumnActions: false,
enablePagination: false,
enableBottomToolbar: false,
} as MRT_TableOptions<DealSummary>
}
/>
<Title
style={{ textAlign: "end" }}
mt={rem(10)}
mb={rem(6)}
order={4}>
Итог:{" "}
{items.reduce(
(acc, item) => acc + item.price * item.quantity,
0,
)}
</Title>
</>
}
</Flex>
</Flex>
);
};
export default DealServicesTable;

View File

@@ -0,0 +1,30 @@
import { useMemo } from "react";
import { MRT_ColumnDef } from "mantine-react-table";
import { DealServiceSchema } from "../../../../../../../client";
const useDealServicesTableColumns = () => {
return useMemo<MRT_ColumnDef<DealServiceSchema>[]>(
() => [
{
header: "Название",
accessorKey: "service.name",
size: 450,
},
{
header: "Количество",
accessorKey: "quantity",
size: 50,
Cell: ({ cell }) => cell.getValue() + " шт.",
},
{
accessorKey: "price",
header: "Цена",
size: 50,
Cell: ({ cell }) => cell.getValue() + " ₽",
},
],
[]
);
};
export default useDealServicesTableColumns;

View File

@@ -0,0 +1,45 @@
import { FC, useEffect } from "react";
import useDealsTableColumns from "./columns.tsx";
import { DealSummary } from "../../../../../../../client";
import { useDealPageContext } from "../../../../../contexts/DealPageContext.tsx";
import { BaseTable } from "../../../../../../../components/BaseTable/BaseTable.tsx";
type Props = {
items: DealSummary[];
};
const DealsTable: FC<Props> = ({ items }) => {
const { selectPrefillDeal } = useDealPageContext();
const columns = useDealsTableColumns();
const defaultSorting = [{ id: "createdAt", desc: false }];
useEffect(() => {
if (items.length < 1) return;
selectPrefillDeal(items[0].id);
}, []);
return (
<BaseTable
data={items}
columns={columns}
restProps={
{
enableSorting: true,
enableColumnActions: false,
enablePagination: true,
enableBottomToolbar: true,
paginationDisplayMode: "pages",
initialState: {
sorting: defaultSorting,
},
mantinePaginationProps: {
showRowsPerPage: false,
},
}
}
/>
);
};
export default DealsTable;

View File

@@ -0,0 +1,72 @@
import { useMemo } from "react";
import { MRT_ColumnDef } from "mantine-react-table";
import { ActionIcon, Image, Radio } from "@mantine/core";
import { DealSummary } from "../../../../../../../client";
import { useDealPageContext } from "../../../../../contexts/DealPageContext.tsx";
const useDealsTableColumns = () => {
return useMemo<MRT_ColumnDef<DealSummary>[]>(
() => [
{
accessorKey: "select",
header: "",
size: 5,
enableSorting: false,
Cell: ({ row }) => {
const { selectPrefillDeal, selectedPrefillDeal } = useDealPageContext();
const checked = row.original.id === selectedPrefillDeal?.id;
return (
<Radio
checked={checked}
onChange={() => {
selectPrefillDeal(row.original.id);
}}
/>
);
},
},
{
accessorKey: "id",
header: "ID",
size: 20,
},
{
accessorKey: "clientName",
header: "Клиент",
size: 60,
enableSorting: false,
},
{
accessorKey: "name",
header: "Название",
enableSorting: false,
size: 60,
},
{
header: "Дата создания",
accessorKey: "createdAt",
size: 10,
Cell: ({ row }) =>
new Date(row.original.createdAt).toLocaleString("ru-RU").substring(0, 17),
enableSorting: true,
sortingFn: (rowA, rowB) =>
new Date(rowB.original.createdAt).getTime() -
new Date(rowA.original.createdAt).getTime(),
},
{
header: "МП",
size: 5,
Cell: ({ row }) => (
<ActionIcon variant={"transparent"}>
<Image
src={row.original.baseMarketplace?.iconUrl || ""}
/>
</ActionIcon>
),
},
],
[],
);
};
export default useDealsTableColumns;

View File

@@ -0,0 +1,31 @@
import { FC } from "react";
import { MRT_TableOptions } from "mantine-react-table";
import { DealProductServiceSchema } from "../../../../../../../client";
import { BaseTable } from "../../../../../../../components/BaseTable/BaseTable.tsx";
import useProductServicesTableColumns from "./columns.tsx";
type Props = {
items: DealProductServiceSchema[];
quantity: number;
};
const ProductServicesTable: FC<Props> = ({ items, quantity }) => {
const columns = useProductServicesTableColumns({ data: items, quantity });
return (
<BaseTable
data={items}
columns={columns}
restProps={
{
enableColumnActions: false,
enableSorting: false,
enableRowActions: false,
enableBottomToolbar: false,
} as MRT_TableOptions<DealProductServiceSchema>
}
/>
);
};
export default ProductServicesTable;

View File

@@ -0,0 +1,44 @@
import { useMemo } from "react";
import { MRT_ColumnDef } from "mantine-react-table";
import { DealProductServiceSchema } from "../../../../../../../client";
import { useSelector } from "react-redux";
import { RootState } from "../../../../../../../redux/store.ts";
type Props = {
data: DealProductServiceSchema[];
quantity: number;
};
const useProductServicesTableColumns = (props: Props) => {
const { data, quantity } = props;
const authState = useSelector((state: RootState) => state.auth);
const totalPrice = useMemo(
() => data.reduce((acc, row) => acc + row.price * quantity, 0),
[data, quantity]
);
const hideGuestColumns = ["service.cost"];
return useMemo<MRT_ColumnDef<DealProductServiceSchema>[]>(
() => [
{
accessorKey: "service.name",
header: "Услуга",
},
{
accessorKey: "price",
header: "Цена",
size: 5,
Cell: ({ cell }) => cell.getValue() + " ₽",
Footer: () => <>Итог: {totalPrice.toLocaleString("ru")}</>,
},
],
[totalPrice]
).filter(
columnDef =>
!(
hideGuestColumns.includes(columnDef.accessorKey || "") &&
authState.isGuest
)
);
};
export default useProductServicesTableColumns;

View File

@@ -0,0 +1,52 @@
import { useForm } from "@mantine/form";
import { useEffect, useState } from "react";
import { BaseMarketplaceSchema } from "../../../../../client";
import { useDealSummariesFull } from "../../../hooks/useDealSummaries.tsx";
type State = {
idOrName: string | null;
marketplace: BaseMarketplaceSchema | null;
};
const usePrefillDeal = () => {
const { objects } = useDealSummariesFull();
const form = useForm<State>({
initialValues: {
idOrName: null,
marketplace: null,
},
});
const [data, setData] = useState(objects);
const applyFilters = () => {
let result = objects;
if (form.values.idOrName) {
if (isNaN(parseInt(form.values.idOrName))) {
const name: string = form.values.idOrName.toLowerCase();
result = result.filter(
obj => obj.name.toLowerCase().search(name) !== -1,
);
}
else {
const id = parseInt(form.values.idOrName);
result = result.filter(
obj => obj.id === id,
);
}
}
if (form.values.marketplace) {
result = result.filter(
obj => obj.baseMarketplace?.key === form.values.marketplace?.key,
);
}
setData(result);
};
useEffect(() => {
applyFilters();
}, [form.values, objects]);
return { data, form };
};
export default usePrefillDeal;

View File

@@ -20,6 +20,7 @@ import ClientSelectNew from "../../../components/Selects/ClientSelectNew/ClientS
import DealsTable from "../../DealsPage/components/DealsTable/DealsTable.tsx";
import { motion } from "framer-motion";
import { dateWithoutTimezone } from "../../../shared/lib/date.ts";
import DealPrefillDrawer from "../drawers/DealPrefillDrawer/DealPrefillDrawer.tsx";
enum DisplayMode {
BOARD,
@@ -410,6 +411,7 @@ export const LeadsPage: FC = () => {
{getBody()}
</PageBlock>
<DealEditDrawer />
<DealPrefillDrawer />
</DealPageContextProvider>
</PageBlock>
);